Subject: bug#22829: Acknowledgement (25.1.50; Display number of marked files)

From: Tino Calancha <[email-masked]>
Date: Tue, 8 Mar 2016 19:20:15 +0900 (JST)

> Thank you!  I have a feeling that code could be optimized more before
> installing to dired.  I'll check it to estimate how well it works.
Please, test the latest `my-dired-count-sizes-opt3' (at the end of 
the comments):
*) Added the tramp handlers to make this work on remote directories.
*) `du' receives all the directories at once.

> One quick comment: in defcustom my-dired-used-space-program
> you could find if an executable exists like:
>
> (defcustom my-dired-used-space-program (and (executable-find "du") du")
Thank you.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Following code requires the patch on Bug#22893

(defcustom my-dired-used-space-program
   (purecopy (let ((opts (if (string-prefix-p "gnu" (symbol-name system-type))
 							"-sb"
 						  "-sk"))) ; -k overestimate used space for files w/ size < 1024
 			  (cond ((executable-find "du") (list "du" opts))
 					((file-executable-p "/usr/sbin/du") (list "/usr/sbin/du" opts))
 					((file-executable-p "/etc/du") (list "/etc/du" opts))
 					(t (list "du" opts)))))
   "Program and its options to get recursively the total size of a directory.
We assume the output has the format of `du'.

A value of nil disables this feature."
   :type '(list (string :tag "Program")
 			   (repeat :tag "Options"
 					   :inline t
 					   (stting :format "%v")))
   :group 'dired)

(defun my-dired-count-sizes-opt3 (&optional mark ask include-dirs)
   "Count sizes of files marked by MARK mark.
When ASK non-nil user is prompted for MARK. Otherwise `dired-marker-char'
is used.
Optional arg INCLUDE-DIRS, if non-nil, run `my-dired-used-space-program'
on the markd directories. Otherwise the size of the directories is
not included."
   ;; TODO: add this info to mode-line and file count too, e.g.: F32 S64k
   ;; and make minor mode
   ;; see `dired-change-marks'
   (interactive
    (let* ((cursor-in-echo-area t)
           (default current-prefix-arg)
           (mark (or (and default (progn (message "Count files marked with mark: ")
                                                    (read-char)))
                     dired-marker-char))
           (dirs (and default (car my-dired-used-space-program) (y-or-n-p "Include directories? "))))
      (list mark t dirs)))
   (unless mark (setq mark dired-marker-char))
   ;; If `my-dired-used-space-program' not available signal an error.
   (when (and include-dirs
              (not (equal 0 (condition-case nil
                                (process-file (car my-dired-used-space-program) nil nil nil null-device)
                              (error nil)))))
     (error "Program `my-dired-used-space-program' not found"))
   (require 'cl-lib) ; for cl-remove-if and cl-nset-difference
   (if (eq mark ?\r)
       (progn
         (message "Mark cannot be \\r")
         (sit-for 1)
         (ding))
     (let* ((files          (dired-get-marked-files nil nil nil t mark))
 		   (num-files      (or (and (not (cdr files)) 0)
 							   (and (equal t (car files)) (pop files) 1)
 							   (length files)))
            (non-dirs       (cl-remove-if (lambda(x) (eq (car (file-attributes x)) t)) files))
            (total-size     (apply '+ (mapcar (lambda(x) (elt x 7))
                                              (mapcar 'file-attributes non-dirs))))
            (dirs           (cl-nset-difference files non-dirs :test 'equal))
            (num-dirs       (length dirs))
            (num-non-dirs   (- num-files num-dirs))
 		   (handler        (and dirs (find-file-name-handler (car dirs) 'call-process)))
            total-size-str)
       (when (and include-dirs (not (= num-dirs 0)))
         (let ((size 0)
 			  (scale-factor (if (string= (cadr my-dired-used-space-program) "-sk")
 								1024.0
 							  1.0)))
           (with-temp-buffer
 			(if handler
 				(apply handler 'process-file (car my-dired-used-space-program)
 					   nil t nil
 					   (cadr my-dired-used-space-program)
 					   (mapcar 'file-name-nondirectory dirs))
 			  (apply 'process-file (car my-dired-used-space-program)
 					 nil t nil
 					 (cadr my-dired-used-space-program) dirs))
             (goto-char 1)
             (while (search-forward-regexp "^[0-9]+" nil t)
               (setq size (+ size (string-to-number (match-string 0))))))
           (setq total-size (+ total-size (* scale-factor size)))))

       (setq total-size-str (if my-dired-human-readable
                                (file-size-human-readable total-size)
                              (my-dired-use-comma-separator total-size)))
       (if (= num-files 0)
           (message "No marked files with mark '%s'" (char-to-string mark))
         (message "Marked %d %s (%d non-dirs/%d dirs) with '%s' and total size %s%s%s"
                  num-files
                  (or (and (= num-files 1) "file") "files")
                  num-non-dirs
                  num-dirs
                  (char-to-string mark)
                  total-size-str
                  (or (and my-dired-human-readable "") " bytes")
                  (or (and (not include-dirs) " (dirs size excluded)") ""))))))

(define-key dired-mode-map (kbd "*N") 'my-dired-count-sizes-opt3)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;