diff options
| author | Eli Zaretskii | 2024-09-07 12:17:24 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2024-09-07 12:17:24 +0300 |
| commit | e1304e9b1bbb62ff3e3680c84bd1fad4922b41eb (patch) | |
| tree | 5e4daa49ffde16c7ddc209752ac23b1379b4d7d8 | |
| parent | 04c44405bf604380c575fa2a4d9611af0f3bc0d9 (diff) | |
| download | emacs-e1304e9b1bbb62ff3e3680c84bd1fad4922b41eb.tar.gz emacs-e1304e9b1bbb62ff3e3680c84bd1fad4922b41eb.zip | |
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)
| -rw-r--r-- | lisp/emacs-lisp/chart.el | 71 |
1 files changed, 56 insertions, 15 deletions
diff --git a/lisp/emacs-lisp/chart.el b/lisp/emacs-lisp/chart.el index da61e45213d..2ca9b64be33 100644 --- a/lisp/emacs-lisp/chart.el +++ b/lisp/emacs-lisp/chart.el | |||
| @@ -641,27 +641,68 @@ SORT-PRED if desired." | |||
| 641 | (lambda (a b) (> (cdr a) (cdr b)))) | 641 | (lambda (a b) (> (cdr a) (cdr b)))) |
| 642 | )) | 642 | )) |
| 643 | 643 | ||
| 644 | ;; This assumes 4KB blocks | ||
| 645 | (defun chart--file-size (size) | ||
| 646 | (* (/ (+ size 4095) 4096) 4096)) | ||
| 647 | |||
| 648 | (defun chart--directory-size (dir) | ||
| 649 | "Compute total size of files in directory DIR and its subdirectories. | ||
| 650 | DIR is assumed to be a directory, verified by the caller." | ||
| 651 | (let ((size 0)) | ||
| 652 | (dolist (file (directory-files-recursively dir "." t)) | ||
| 653 | (let ((fsize (nth 7 (file-attributes file)))) | ||
| 654 | (if (> fsize 0) | ||
| 655 | (setq size | ||
| 656 | (+ size (chart--file-size fsize)))))) | ||
| 657 | size)) | ||
| 658 | |||
| 644 | (defun chart-space-usage (d) | 659 | (defun chart-space-usage (d) |
| 645 | "Display a top usage chart for directory D." | 660 | "Display a top usage chart for directory D." |
| 646 | (interactive "DDirectory: ") | 661 | (interactive "DDirectory: ") |
| 647 | (message "Collecting statistics...") | 662 | (message "Collecting statistics...") |
| 648 | (let ((nmlst nil) | 663 | (let ((nmlst nil) |
| 649 | (cntlst nil) | 664 | (cntlst nil) |
| 650 | (b (get-buffer-create " *du-tmp*"))) | 665 | b) |
| 651 | (set-buffer b) | 666 | (if (executable-find "du") |
| 652 | (erase-buffer) | 667 | (progn |
| 653 | (insert "cd " d ";du -sk * \n") | 668 | (setq b (get-buffer-create " *du-tmp*")) |
| 654 | (message "Running `cd %s;du -sk *'..." d) | 669 | (set-buffer b) |
| 655 | (call-process-region (point-min) (point-max) shell-file-name t | 670 | (erase-buffer) |
| 656 | (current-buffer) nil) | 671 | (if (and (memq system-type '(windows-nt ms-dos)) |
| 657 | (goto-char (point-min)) | 672 | (fboundp 'w32-shell-dos-semantics) |
| 658 | (message "Scanning output ...") | 673 | (w32-shell-dos-semantics)) |
| 659 | (while (re-search-forward "^\\([0-9]+\\)[ \t]+\\([^ \n]+\\)$" nil t) | 674 | (progn |
| 660 | (let* ((nam (buffer-substring (match-beginning 2) (match-end 2))) | 675 | ;; With Windows shells, 'cd' does not change the drive, |
| 661 | (num (buffer-substring (match-beginning 1) (match-end 1)))) | 676 | ;; and ';' is not reliable for running multiple |
| 662 | (setq nmlst (cons nam nmlst) | 677 | ;; commands, so use alternatives. We quote the |
| 663 | ;; * 1000 to put it into bytes | 678 | ;; directory because otherwise pushd will barf on a |
| 664 | cntlst (cons (* (string-to-number num) 1000) cntlst)))) | 679 | ;; directory with forward slashes. Note that * will not |
| 680 | ;; skip dotfiles with Windows shells, unlike on Unix. | ||
| 681 | (insert "pushd \"" d "\" && du -sk * \n") | ||
| 682 | (message "Running `pushd \"%s\" && du -sk *'..." d)) | ||
| 683 | (insert "cd " d ";du -sk * \n") | ||
| 684 | (message "Running `cd %s;du -sk *'..." d)) | ||
| 685 | (call-process-region (point-min) (point-max) shell-file-name t | ||
| 686 | (current-buffer) nil) | ||
| 687 | (goto-char (point-min)) | ||
| 688 | (message "Scanning output ...") | ||
| 689 | (while (re-search-forward "^\\([0-9]+\\)[ \t]+\\([^ \n]+\\)$" nil t) | ||
| 690 | (let* ((nam (buffer-substring (match-beginning 2) (match-end 2))) | ||
| 691 | (num (buffer-substring (match-beginning 1) (match-end 1)))) | ||
| 692 | (setq nmlst (cons nam nmlst) | ||
| 693 | ;; * 1000 to put it into bytes | ||
| 694 | cntlst (cons (* (string-to-number num) 1000) cntlst))))) | ||
| 695 | (dolist (file (directory-files d t directory-files-no-dot-files-regexp)) | ||
| 696 | (let ((fbase (file-name-nondirectory file))) | ||
| 697 | ;; Typical shells exclude files and subdirectories whose names | ||
| 698 | ;; begin with a period when it expands *, so we do the same. | ||
| 699 | (unless (string-match-p "\\`\\." fbase) | ||
| 700 | (setq nmlst (cons fbase nmlst)) | ||
| 701 | (if (file-regular-p file) | ||
| 702 | (setq cntlst (cons (chart--file-size | ||
| 703 | (nth 7 (file-attributes file))) | ||
| 704 | cntlst)) | ||
| 705 | (setq cntlst (cons (chart--directory-size file) cntlst))))))) | ||
| 665 | (if (not nmlst) | 706 | (if (not nmlst) |
| 666 | (error "No files found!")) | 707 | (error "No files found!")) |
| 667 | (chart-bar-quickie 'vertical (format "Largest files in %s" d) | 708 | (chart-bar-quickie 'vertical (format "Largest files in %s" d) |