Fix 'chart-space-usage' on MS-Windows

* lisp/emacs-lisp/chart.el (chart--file-size)
(chart--directory-size): New functions.
(chart-space-usage): Invoke 'du' correctly on MS-Windows.  Provide
alternative implementation in Lisp when 'du' is not installed,
using 'chart--directory-size' and 'chart--file-size'.  (Bug#72919)
This commit is contained in:
Eli Zaretskii 2024-09-07 12:17:24 +03:00
parent 04c44405bf
commit e1304e9b1b

View file

@ -641,27 +641,68 @@ SORT-PRED if desired."
(lambda (a b) (> (cdr a) (cdr b)))) (lambda (a b) (> (cdr a) (cdr b))))
)) ))
;; This assumes 4KB blocks
(defun chart--file-size (size)
(* (/ (+ size 4095) 4096) 4096))
(defun chart--directory-size (dir)
"Compute total size of files in directory DIR and its subdirectories.
DIR is assumed to be a directory, verified by the caller."
(let ((size 0))
(dolist (file (directory-files-recursively dir "." t))
(let ((fsize (nth 7 (file-attributes file))))
(if (> fsize 0)
(setq size
(+ size (chart--file-size fsize))))))
size))
(defun chart-space-usage (d) (defun chart-space-usage (d)
"Display a top usage chart for directory D." "Display a top usage chart for directory D."
(interactive "DDirectory: ") (interactive "DDirectory: ")
(message "Collecting statistics...") (message "Collecting statistics...")
(let ((nmlst nil) (let ((nmlst nil)
(cntlst nil) (cntlst nil)
(b (get-buffer-create " *du-tmp*"))) b)
(set-buffer b) (if (executable-find "du")
(erase-buffer) (progn
(insert "cd " d ";du -sk * \n") (setq b (get-buffer-create " *du-tmp*"))
(message "Running `cd %s;du -sk *'..." d) (set-buffer b)
(call-process-region (point-min) (point-max) shell-file-name t (erase-buffer)
(current-buffer) nil) (if (and (memq system-type '(windows-nt ms-dos))
(goto-char (point-min)) (fboundp 'w32-shell-dos-semantics)
(message "Scanning output ...") (w32-shell-dos-semantics))
(while (re-search-forward "^\\([0-9]+\\)[ \t]+\\([^ \n]+\\)$" nil t) (progn
(let* ((nam (buffer-substring (match-beginning 2) (match-end 2))) ;; With Windows shells, 'cd' does not change the drive,
(num (buffer-substring (match-beginning 1) (match-end 1)))) ;; and ';' is not reliable for running multiple
(setq nmlst (cons nam nmlst) ;; commands, so use alternatives. We quote the
;; * 1000 to put it into bytes ;; directory because otherwise pushd will barf on a
cntlst (cons (* (string-to-number num) 1000) cntlst)))) ;; directory with forward slashes. Note that * will not
;; skip dotfiles with Windows shells, unlike on Unix.
(insert "pushd \"" d "\" && du -sk * \n")
(message "Running `pushd \"%s\" && du -sk *'..." d))
(insert "cd " d ";du -sk * \n")
(message "Running `cd %s;du -sk *'..." d))
(call-process-region (point-min) (point-max) shell-file-name t
(current-buffer) nil)
(goto-char (point-min))
(message "Scanning output ...")
(while (re-search-forward "^\\([0-9]+\\)[ \t]+\\([^ \n]+\\)$" nil t)
(let* ((nam (buffer-substring (match-beginning 2) (match-end 2)))
(num (buffer-substring (match-beginning 1) (match-end 1))))
(setq nmlst (cons nam nmlst)
;; * 1000 to put it into bytes
cntlst (cons (* (string-to-number num) 1000) cntlst)))))
(dolist (file (directory-files d t directory-files-no-dot-files-regexp))
(let ((fbase (file-name-nondirectory file)))
;; Typical shells exclude files and subdirectories whose names
;; begin with a period when it expands *, so we do the same.
(unless (string-match-p "\\`\\." fbase)
(setq nmlst (cons fbase nmlst))
(if (file-regular-p file)
(setq cntlst (cons (chart--file-size
(nth 7 (file-attributes file)))
cntlst))
(setq cntlst (cons (chart--directory-size file) cntlst)))))))
(if (not nmlst) (if (not nmlst)
(error "No files found!")) (error "No files found!"))
(chart-bar-quickie 'vertical (format "Largest files in %s" d) (chart-bar-quickie 'vertical (format "Largest files in %s" d)