emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: find-name-dired exits when s is pressed (for sorting)


From: Ehud Karni
Subject: Re: find-name-dired exits when s is pressed (for sorting)
Date: Wed, 16 Jan 2002 23:57:28 +0200

On Sun, 13 Jan 2002 21:29:09 -0700 (MST), Richard Stallman <address@hidden> 
wrote:
>
> Writing code to sort the Dired buffer by time or by name internally,
> handling subdirectories properly, is not trivial, but it should not be
> terribly hard either.  Does someone want to try?

I wrote code for that exact purpose. It works in Emacs 20 & 21. It
supports sorting modes not supported by the OS (e.g by extension or
owner, also sorting directories and symbolic links before regular
files). This code also works with MULE (it support Hebrew names).

However, it is written in Emacs-lisp and for really large directories
(more than 1500 files) it is VERY noticeable. I'm sure it can be
improved both in structure and performance. I'm open to any suggestion
or if anybody want s/he can use it and rewrite it's own code.

Ehud.

;; -------------------- sort dired buffer code --------------------

(defvar dired-sort-number 0 "sort requested for this buffer (see help for 
'dired-sort-set)")
(make-variable-buffer-local 'dired-sort-number)
(setq-default dired-sort-number 0)             ;; initial value

(add-hook 'dired-after-readin-hook 'dired-sort-after-revert)   ;check for sort 
after revert

(defun dired-get-file-extension (file-name)
  "In dired, return extension of FILE-NAME. Extension is defined as the
substring from the rightmost `.' to the end, if no dot return \"\"."
       (if (string-match "\\." file-name)
           (let* ((lng (length file-name))
                  (ix lng)
                  (ch 0)
                  (ext ""))
               (while (not (= ch ?.))
                  (setq ix (1- ix))
                  (setq ch (aref file-name ix))
                  (setq ext (concat (list ch) ext)))
               ext)
           ""))

(defun dired-get-file-length ()
  "In dired, return length of file mentioned on this line.
Return 0 if there is no file on this line."
       (string-to-int (dired-get-file-length-string)))

(defun dired-get-file-length-string ()
  "In dired, return length of file mentioned on this line.
Return "0" if there is no file on this line."
       (let ((length "0") bol pos)
           (save-excursion
               (beginning-of-line)
               (setq bol (point))
               (end-of-line)
               (if (re-search-backward
                       (concat "[ ]" dired-months-regexp) bol t)
                   (progn
                       (setq pos (point))
                       (skip-chars-backward "^ " bol)
                       (setq length (buffer-substring (point) pos))))
               length)))



(defun dired-get-file-owner ()
  "In dired, return owner (1st field after number of links)
Return "" if there is no file on this line."
       (let ((owner " ")
             (pos (point))
             (eol (progn (end-of-line) (point))))
           (beginning-of-line)
           (and (re-search-forward "[ ][^ ]*[-stx][ ]" eol t)
                (re-search-forward "[0-9][ ]" eol t)
                (setq owner (buffer-substring (point) (search-forward " "))))
           (goto-char pos)
           owner))

(defun dired-get-file-time (&optional arg)
  "In dired, return file time as exactly 10 character string
optional ARG specify what time: 1-modify (def), 2-access, 3-control."
       (or arg (setq arg 1))                   ; def value
       (let ((ftst "          ")               ;10 spaces
             (tp (cond
                   ((= arg 2)  4)
                   ((= arg 3)  6)
                   (t          5)))
             (fn (dired-get-filename t t)))    ;file name/time
           (and fn
               (if (or (member system-type '(windows-nt dos))
                       (and (boundp 'ftp-on) ftp-on))
                   (save-excursion
                       (beginning-of-line)
                       (re-search-forward
                           (concat dired-months-regexp "[ ]+[0-9]+") nil t)
                       (let* ((beg (match-beginning 0))
                              (end (match-end 0))
                              (ymf (+ 0.032786885 (/ (float (car 
(current-time))) 40.12756)))  ; Year & month in float
                              (YR  (format "%3d" (+ 170 (truncate (/ ymf 
12)))))           ; curent year 1xx/2xx
                              (HM   "0000")                                     
           ; def hou.minute
                              (MNT (cdr-safe (assoc (upcase (buffer-substring 
beg (+ 3 beg)))
                                          timezone-months-assoc)))              
           ; month number or nil (unknown)
                              (DAY (string-to-number (buffer-substring (- end 
2) end) 10)) ; day (number)
                             )
                           (or MNT (setq MNT 0))           ; convert unknown 
month to 0
                           (setq ymf (1+ (% (truncate ymf) 12)))   ; current 
month 1-12
                           (skip-chars-forward " ")        ; start of YEAR or 
HH:MM
                           (setq beg (point))
                           (skip-chars-forward "^ ")       ; end of YEAR or 
HH:MM
                           (setq end (point))
                           (if (= (- end beg) 4)           ; 4 digits - YEAR 
19xx/20xx
                               (setq YR (concat
                                           (buffer-substring beg (1+ beg))
                                           (buffer-substring (- end 2) end)))
                               (setq HM (concat
                                           (buffer-substring beg (+ beg 2))
                                           (buffer-substring (- end 2) end))
                                     YR (if (<= MNT ymf) YR                
;leave year as is if month <= current
                                            (format "%d" (1- (string-to-number 
YR))))));decrease 1 otherwise
                           (setq DAY (format "%d" (+ 100 (* MNT 50) DAY))) ; 
month*50 + day + 100 (3 digits)
                           (setq ftst (concat YR DAY HM))))                ; 
3d-3d-4d (10 chars)
               (progn
                   (setq fn (nth tp (file-attributes fn)))                 ; 
get file time as (hi16bit lo16bit)
                   (setq ftst (format "%d%d" (+ 10000 (or (car fn) 0))     ; 
convert each to exactly 5
                                       (+ 10000 (or (nth 1 fn) 0)))))))    ; 
decimal digits
           ftst))


(defun dired-restore-8bit-chars ()
"Replace \[23]xx (4 chars) with their real value (1 char) in the whole buffer"
       (let ((pos (point-marker))
             buffer-read-only
             rplc
             )
           (goto-char (point-min))
           (while (re-search-forward "\\\\[23][0-7][0-7]" nil t)
               (setq rplc (read (concat "\""
                                        (buffer-substring (match-beginning 0) 
(match-end 0))
                                        "\"")))
               (and (eq 'w32 window-system)       ;; Windows NT/95 system (20.x)
                    (string-lessp rplc "\233")
                    (aset rplc 0 (+ 96 (aref rplc 0))))    ;;windows erorr in 
handling Hebrew names
               (replace-match rplc t t))
           (goto-char (point-min))
           (while (search-forward "\\ " nil t)
               (replace-match " " t t))
           (goto-char pos)))

(defun dired-sort-after-revert () "sort the buffer each time its re-read or 
inserted"
       (let* ((bdir (< dired-sort-number 0))                   ;begin with dirs
              (rvrs (>= (mod (abs dired-sort-number) 100) 10)) ;revers switch
              (o-s  (mod (abs dired-sort-number) 10))          ;0-6 sort 
requested
              (ftp-on (string-match "@.*:"                     ;ange-ftp on 
switch
                           (if (listp dired-directory)
                               (car dired-directory)
                               dired-directory))))
           (dired-restore-8bit-chars)          ; convert Hebrew to 8bit
           (if bdir                            ; special dirs
               (dired-sort-ek rvrs o-s bdir)   ; Yes, use my sort
               (and (or
                       (and (member system-type '(windows-nt dos))
                            (>= o-s 1))                ; sort of any type in 
MSDOS
;;;;   U N I X  -  (with ange ftp) ==============================
                       (>= o-s 4)              ; sort by owner / size / 
extension (unix)
                       ftp-on)                 ; any (ange-ftp)
                    (dired-sort-ek rvrs o-s)))); sort by any needed sort (0-6)
       (set-buffer-modified-p nil))            ; ignore modification done by 
sort

(defun dired-sort-ek (reverse sub-sort &optional bdir)
  "sort the buffer (REVERSED) according to SUB-SORT:
0-name, 1,2,3-sort by time/name, 4-size/name,  5-owner/name,  6-extension/name.
Optional BDIR - sort directories at beging by name."
       (let ((sv-fn (dired-get-filename 'no-dir t))
             (sv-fn-ful (dired-get-filename t t))
             (sv-pos (point-marker))
             (sv-col (column-no))
             (sbul buffer-undo-list)       ;; undo list
             buffer-read-only
             (fsdch "531")                 ;; file/symbolic/dir char
             (diron 0)                     ;; sub type - file/symbolic/dir - 
0/1/2
             ds-chk                        ;; dir/symbolic check
             fn beg end sv-end)
           (and (> sub-sort 0)
                (< sub-sort 4)
                (setq reverse (not reverse)))
           (and bdir
                reverse
                (setq fsdch "579"))
           (goto-char (point-min))
           (while (not (eobp))
               (while (and (not (eobp)) (not (dired-get-filename 'no-dir t)))
                   (forward-line 1))                   ;;search 1st line of 
directory to sort
               (beginning-of-line)
               (setq beg (point))
               (while (and (not (eobp))
                           (setq fn (dired-get-filename 'no-dir t)))
                   (beginning-of-line)
                   (and bdir fn
                       (setq diron 0
                             ds-chk (if (member system-type '(windows-nt dos)) 
;; on W9x/DOS use the bash attributes
                                       (cond
                                           ((looking-at ". d") t)          
;;directory
                                           ((looking-at ". l") "symlink")  
;;symbolic link
                                           (t                  nil))
                                       (nth 0 (file-attributes fn))))     ; 
exit from and for regular files
                       (setq diron
                             (if (or (eq ds-chk t)
                                     (string-equal fn "..")
                                     (string-equal fn "."))
                                 2 1)))
                   (insert (aref fsdch diron)
                           (cond
                               ((> diron 0)
                                   (and reverse
                                       (setq diron (length fn))
                                       (setq fn (concat fn "��"))
                                       (while (> diron 0)
                                           (setq diron (1- diron))
                                           (setq ds-chk (- 287 (% (aref fn 
diron) 256)))
                                           (if (> ds-chk 255) (setq ds-chk 255))
                                           (aset fn diron ds-chk)))
                                                       "")         ;; ensure 
add nothing
                               ((= sub-sort 0)         "")         ;; we realy 
need only the name
                               ((< sub-sort 4)                     ;; 1, 2 or 3 
(all the same on pc)
                                       (dired-get-file-time sub-sort))
                               ((= sub-sort 4)
                                       (dired-get-file-owner))
                               ((= sub-sort 5)
                                       (substring (concat "         " 
(dired-get-file-length-string)) -10))
                               ((= sub-sort 6)
                                       (dired-get-file-extension fn))
                               (t      ""))
                           "     "
                           fn
                           "            ��‗")
                   (forward-line 1))
               (setq sv-end (point-marker))
               (setq end (point))
               (sort-lines reverse beg end)
               (goto-char beg)
               (while (< (point-marker) sv-end)
                   (delete-region (point)
                                  (search-forward " ��‗"))
                   (forward-line 1))
               (goto-char sv-end))
           (setq buffer-undo-list sbul)    ;; restore undo
           (goto-char sv-pos)
           (if sv-fn
               (progn
                   (goto-char (point-min))
                   (while (not (string-equal sv-fn-ful (dired-get-filename t 
t)))
                       (search-forward sv-fn)
                       (end-of-line))
                   (goto-col sv-col)))))


(defun dired-sort-set (&optional arg)
  "Sort current dired buffer (and reread),
 and change dired-listing-switches according to argument:
0/9 -sort by name (normal)             - al
 1 - sort by modification time         - alt   PC:-al
 2 - sort by access time               - altu  PC:-al
 3 - sort by control (chmod ..) time   - altc  PC:-al
 4 - sort by owner / name              - al
 5 - sort by size / name               - al
 6 - sort by extension / name          - al
 Add   10 for reverse sorting        (r switch to ls)
 Add  100 for subdirectory inclusion (R switch to ls)
 Use negative value (e.g. -101) for listing directories
     and symbolic links at the beginning by name"
    (interactive "P")
       (or arg
           (save-window-excursion
               (describe-function 'dired-sort-set)
               (setq arg (string-to-int (read-string
                   "Enter new sort value for this Dired: " (format "%s" 
dired-sort-number))))
               (kill-buffer "*Help*")))
       (let* ((a-arg (abs arg))
              (Subdir (if (< a-arg 100) "" "R"))
              (revers (if (< (mod a-arg 100)  10) "" "r"))
              (switch (mod a-arg 10))
              (d-l-s ""))
           (cond
               ((member system-type '(windows-nt dos))
                   (setq d-l-s "-al"))             ;; it always "-al" in 
MSDOS/windows
               ((or (= switch 0)                   ;; by name
                    (= switch 9))                  ;; 0 / 9
                   (setq d-l-s "-al"))
               ((= switch 1)
                   (setq d-l-s "-alt"))
               ((= switch 2)
                   (setq d-l-s "-altu"))
               ((= switch 3)
                   (setq d-l-s "-altc"))
               ((<= switch 6)
                   (setq d-l-s "-al"))
               (t
                   (error "arg to dired-sort-set is %d - must be [01][01][0-6]" 
arg)))
           (setq-default dired-sort-number arg)    ;; make it default for new 
dirs
           (setq dired-sort-number arg)
           (setq dired-actual-switches (setq dired-listing-switches (concat 
d-l-s revers Subdir)))
           (dired-sort-set-modeline)
           (revert-buffer)))

(define-key dired-mode-map  "z"   'dired-sort-set)




(defun dired-sort-set-modeline () "Dired sub mode (sort & Subdirectory options)"
       (let ((modec "Dired by ")
             (dsn (abs dired-sort-number))
             (case-fold-search))
           (if (string-match "t" dired-actual-switches)
               (progn
                   (cond
                       ((string-match "u" dired-actual-switches)
                               (setq modec (concat modec "access")))
                       ((string-match "c" dired-actual-switches)
                               (setq modec (concat modec "control")))
                       (t      (setq modec (concat modec "modify"))))
                   (setq modec (concat modec " time")))
               (progn
                   (cond
                       ((= (mod dsn 10) 4)
                               (setq modec (concat modec "owner/")))
                       ((= (mod dsn 10) 5)
                               (setq modec (concat modec "size/")))
                       ((= (mod dsn 10) 6)
                               (setq modec (concat modec "ext/"))))
                   (setq modec (concat modec "name"))))
           (and (string-match "r" dired-actual-switches)
                (setq modec (concat modec " reversed")))
           (and (string-match "R" dired-actual-switches)
                (setq modec (concat modec " (SubDir)")))
           (setq mode-name modec))
           (force-mode-line-update))

(define-key dired-mode-map [menu-bar sort] (cons "Sort" (make-sparse-keymap 
"Sort")))
(let (typ kot nnn lsw
      (srt-opts '(
               (ext   "6" "extens/name" "-alb")
               (size  "5" "size/name  " "-alb")
               (owner "4" "owner/name " "-alb")
               (ctrl  "3" "chmod time " "-albtc")
               (acct  "2" "access time" "-albtu")
               (modt  "1" "mod time   " "-albt")
               (name  "0" "name       " "-alb"))))
       (while srt-opts
           (setq kot (car srt-opts))
           (setq srt-opts (cdr srt-opts))
           (setq typ (vector 'menu-bar 'sort (car kot)))
           (setq nnn (nth 1 kot))
           (setq lsw (nth 3 kot))
           (setq kot (concat "sort by " (nth 2 kot)))
           (define-key dired-mode-map typ (cons (concat kot " " lsw) 
(make-sparse-keymap kot)))
           (setq typ (vector (aref typ 0) (aref typ 1) (aref typ 2) 'no-rvrs))
           (define-key dired-mode-map typ (cons "  -NO-  subdirs/reverse" 
(concat "1" nnn "z")))
           (aset typ 3 'no-nrml)
           (define-key dired-mode-map typ (cons "  -NO-  subdirs/normal" 
(concat nnn "z")))
           (aset typ 3 'inc-rvrs)
           (define-key dired-mode-map typ (cons "include subdirs/reverse" 
(concat "11" nnn "z")))
           (aset typ 3 'inc-nrml)
           (define-key dired-mode-map typ (cons "include subdirs/normal" 
(concat "10" nnn "z")))))


(defun dired-sort-set-menu (event)
  "Pop up a menu of sort-options for this dired buffer
This switches buffers in the window that you clicked on,
and selects that window."
  (interactive "e")
       (let (rsb                                           ; reverse & sub dir 
option
             (srt (x-popup-menu t '("type select"
                   ("dired sort options"
                       ("normal (sort by name)          " . 0)
                       ("sort by modification time      " . 1)
                       ("sort by access time            " . 2)
                       ("sort by control (chmod ..) time" . 3)
                       ("sort by owner / name           " . 4)
                       ("sort by size / name            " . 5)
                       ("sort by extension / name       " . 6))))))    ;;major 
sort option
           (and srt
               (setq rsb (x-popup-menu t '("rsb"
                   ("sort order / subdirectory"
                       ("ls order, NO sub directories" . 0)
                       ("-r order, NO sub directories" . 10)
                       ("ls order,  + sub directories " . 100)
                       ("-r order,  + sub directories " . 110)))))
               (setq srt (+ srt rsb))
               (dired-sort-set srt))))

(define-key dired-mode-map [drag-mouse-3]  'dired-sort-set-menu) ;; sort dired

;; --------------- sort dired buffer code ends here ---------------

--
 Ehud Karni           Tel: +972-3-7966-561  /"\
 Mivtach - Simon      Fax: +972-3-7966-667  \ /  ASCII Ribbon Campaign
 Insurance agencies   (USA) voice mail and   X   Against   HTML   Mail
 http://www.mvs.co.il  FAX:  1-815-5509341  / \
 mailto:address@hidden          Better  Safe  Than  Sorry



reply via email to

[Prev in Thread] Current Thread [Next in Thread]