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

From: Juri Linkov <[email-masked]>
Date: Wed, 02 Mar 2016 02:32:39 +0200

>> And I'm not sure I'm feeling the utility of this function, either.
>> What's the use case?
>
> For those marking files in several directories quite often, and using
> dired-change-marks to separate files in different categories: those
> people may be interested in counting number of marked files. I need
> this every day, but I agree not too many people would find it useful.

I confirm this is very useful.  I'm using the same for a long time,
but additionally also displaying a total sum of sizes on every mark
(this emulates the behavior of File Commanders on marking files by INS).

The code I'm using in ~/.emacs is very very old, and nowadays
you could implement the same with less code.

;; 2 new functions:
(defun dired-get-file-info ()
  "Get file info files for which PREDICATE returns non-nil."
  ;; code for this function is borrowed from dired-x.el::dired-mark-sexp
  (let (inode s mode nlink uid gid size time name sym)
    (save-excursion
      (if (dired-move-to-filename)
          (let (pos
                (mode-len 10)
                (dired-re-inode-size "\\s *\\([0-9]*\\)\\s *\\([0-9]*\\) ?"))
            (beginning-of-line)
            (forward-char 2)
            (if (looking-at dired-re-inode-size)
                (progn
                  (goto-char (match-end 0))
                  (setq inode (string-to-int (buffer-substring (match-beginning 1)
                                                               (match-end 1)))
                        s (string-to-int (buffer-substring (match-beginning 2)
                                                           (match-end 2)))))
              (setq inode nil
                    s nil))
            (setq mode (buffer-substring (point) (+ mode-len (point))))
            (forward-char mode-len)
            (setq nlink (read (current-buffer)))
            (setq uid (buffer-substring (+ (point) 1) (progn (forward-word 1) (point))))
            ;; works only with ls patch
            ;; patched in dired.el:dired-move-to-filename-regexp
            ;; (re-search-forward "\\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\\)")
	    ;; try standard expression
            (re-search-forward directory-listing-before-filename-regexp)
            (goto-char (match-beginning 2))
            (forward-char -1)
            (setq size (string-to-int (replace-regexp-in-string
				       ;; handle thousand separators in sizes
				       "," ""
				       (buffer-substring (save-excursion
							   ;; (backward-word 1)
							   (skip-chars-backward "[0-9,.]")
							   (setq pos (point)))
							 (point)))))
            (goto-char pos)
            (backward-word 1)
            (setq gid (buffer-substring (save-excursion (forward-word 1) (point))
                                        (point))
                  time (buffer-substring (match-beginning 1)
                                         (1- (dired-move-to-filename)))
                  name (buffer-substring (point)
                                         (or (dired-move-to-end-of-filename t)
                                             (point)))
                  sym  (progn
                         (if (looking-at " -> ")
                             (buffer-substring (progn (forward-char 4) (point))
                                               (progn (end-of-line) (point)))
                           "")))
            (list
             (cons 'inode inode)
             (cons 's s)
             (cons 'mode mode)
             (cons 'nlink nlink)
             (cons 'uid uid)
             (cons 'gid gid)
             (cons 'size size)
             (cons 'time time)
             (cons 'name name)
             (cons 'sym sym)))
        nil))))

;; TODO: use `pint2hrstr' in Lisp
(defun dired-count-sizes (&optional mark)
  "Count sizes of files marked by MARK mark."
  ;; 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)
          (mark (progn (message "Count files marked by mark: ")
                       (read-char))))
     (list mark)))
  (if (or (eq mark ?\r))
      (ding)
    (let ((string (format "\n%c" mark))
          (buffer-read-only)
          (total-size 0)
          total-size-str
          (total-count 0))
      (save-excursion
        (goto-char (point-min))
        (while (search-forward string nil t)
          (if (if (= mark ?\ )
                  (save-match-data
                    (dired-get-filename 'no-dir t))
                t)
              (if (equal (buffer-substring-no-properties
                          (match-beginning 0) (match-end 0))
                         string)
                  (setq total-size
                        (+ total-size
                           (*;;(/
                            (cdr (assoc 'size (dired-get-file-info)))
                            1.0);;1024)
                           )
                        total-count (+ total-count 1))))))
      (setq total-size-str (replace-regexp-in-string
                            "^," ""
                            (apply 'string
                                   (reverse
                                    (string-to-list
                                     (replace-regexp-in-string
                                      "\\([0-9]\\{3\\}\\)" "\\1,"
                                      (apply 'string
                                             (reverse
                                              (string-to-list
                                               (replace-regexp-in-string
                                                "\.0$" ""
                                                (number-to-string
                                                 total-size)))))))))))
      (message "Marked %s files with %s bytes" total-count total-size-str))))

(define-key dired-mode-map [(shift f5)] 'dired-count-sizes)

(defun my-dired-mark (arg)
  "Mark ARG files and print the total size of marked files."
  (interactive "P")
  (dired-mark arg)
  (dired-count-sizes dired-marker-char))
(define-key dired-mode-map [insert] 'my-dired-mark)

(defun my-dired-unmark-backward (arg)
  "Move up lines, remove deletion flag there and print size of marked files."
  (interactive "p")
  (dired-unmark-backward arg)
  (dired-count-sizes dired-marker-char))
(define-key dired-mode-map [backspace] 'my-dired-unmark-backward)