aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Zaretskii2000-12-27 17:07:40 +0000
committerEli Zaretskii2000-12-27 17:07:40 +0000
commit3f51f5a958e77fba2cddc1002d34fa31babae056 (patch)
tree6f719b21386d3d4e8f82104adff35260bd7a3ced
parent5e25feeec1b3a0e1ba10a7650a6daba82083b0af (diff)
downloademacs-3f51f5a958e77fba2cddc1002d34fa31babae056.tar.gz
emacs-3f51f5a958e77fba2cddc1002d34fa31babae056.zip
From Francis J. Wright <F.J.Wright@maths.qmw.ac.uk>:
Better support for the Mac and MS-Windows. (ls-lisp): New defgroup. (ls-lisp-emulation, ls-lisp-ignore-case, ls-lisp-dirs-first) (ls-lisp-verbosity, ls-lisp-use-insert-directory-program) (ls-lisp-support-shell-wildcards): New defcustoms. (ls-lisp-parse-symlink): New function. (insert-directory): Code to convert switches to a list and set up the wildcard argument copied from ls-lisp-insert-directory. (ls-lisp-insert-directory): New argument TIME-INDEX. Add support for -C and -R switches. (ls-lisp-column-format): New function. (ls-lisp-delete-matching, ls-lisp-handle-switches) (ls-lisp-format-time): Add doc strings. (ls-lisp-handle-switches): Handle -U, -S, -X, and -F switches. Support ls-lisp-dirs-first. (ls-lisp-classify, ls-lisp-extension): New functions. (ls-lisp-format): Optionally support emulation of symlinks. Support -i, -s, and -G switches.
-rw-r--r--lisp/ls-lisp.el659
1 files changed, 443 insertions, 216 deletions
diff --git a/lisp/ls-lisp.el b/lisp/ls-lisp.el
index fa7b462b3ff..67b87c0be55 100644
--- a/lisp/ls-lisp.el
+++ b/lisp/ls-lisp.el
@@ -1,10 +1,11 @@
1;;; ls-lisp.el --- emulate insert-directory completely in Emacs Lisp 1;;; ls-lisp.el --- emulate insert-directory completely in Emacs Lisp
2 2
3;; Copyright (C) 1992, 1994 Free Software Foundation, Inc. 3;; Copyright (C) 1992, 1994, 2000 Free Software Foundation, Inc.
4 4
5;; Author: Sebastian Kremer <sk@thp.uni-koeln.de> 5;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>
6;; Maintainer: FSF 6;; Modified by: Francis J. Wright <F.J.Wright@maths.qmw.ac.uk>
7;; Keywords: unix 7;; Maintainer: FSF
8;; Keywords: unix, dired
8 9
9;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
10 11
@@ -25,55 +26,137 @@
25 26
26;;; Commentary: 27;;; Commentary:
27 28
28;; INSTALLATION ======================================================= 29;; OVERVIEW ==========================================================
29;;
30;; Put this file into your load-path. To use it, load it
31;; with (load "ls-lisp").
32 30
33;; OVERVIEW =========================================================== 31;; This file redefines the function `insert-directory' to implement it
32;; directly from Emacs lisp, without running ls in a subprocess. It
33;; is useful if you cannot afford to fork Emacs on a real memory UNIX,
34;; under VMS or other non-UNIX platforms if you don't have the ls
35;; program, or if you want a different format from what ls offers.
34 36
35;; This file overloads the function insert-directory to implement it 37;; This function can use regexps instead of shell wildcards. If you
36;; directly from Emacs lisp, without running `ls' in a subprocess. 38;; enter regexps remember to double each $ sign. For example, to
39;; include files *.el, enter `.*\.el$$', resulting in the regexp
40;; `.*\.el$'.
37 41
38;; It is useful if you cannot afford to fork Emacs on a real memory UNIX, 42;; RESTRICTIONS ======================================================
39;; under VMS, or if you don't have the ls program, or if you want
40;; different format from what ls offers.
41 43
42;; This function uses regexps instead of shell 44;; * A few obscure ls switches are still ignored: see the docstring of
43;; wildcards. If you enter regexps remember to double each $ sign. 45;; `insert-directory'.
44;; For example, to include files *.el, enter `.*\.el$$',
45;; resulting in the regexp `.*\.el$'.
46 46
47;; RESTRICTIONS ===================================================== 47;; * Generally only numeric uid/gid.
48 48
49;; * many ls switches are ignored, see docstring of `insert-directory'. 49;; TO DO =============================================================
50 50
51;; * Only numeric uid/gid 51;; Complete handling of F switch (if/when possible).
52 52
53;; TODO ============================================================== 53;; FJW: May be able to sort much faster by consing the sort key onto
54;; the front of each list element, sorting and then stripping the key
55;; off again!
54 56
55;; Recognize some more ls switches: R F 57;;; History:
56
57;;; Code:
58 58
59;;;###autoload 59;; Written originally by Sebastian Kremer <sk@thp.uni-koeln.de>
60(defvar ls-lisp-support-shell-wildcards t 60;; Revised by Andrew Innes and Geoff Volker (and maybe others).
61 "*Non-nil means file patterns are treated as shell wildcards.
62nil means they are treated as Emacs regexps (for backward compatibility).
63This variable is checked by \\[insert-directory] only when `ls-lisp.el'
64package is used.")
65 61
66(defvar ls-lisp-dired-ignore-case nil 62;; Modified by Francis J. Wright <F.J.Wright@maths.qmw.ac.uk>, mainly
67 "Non-nil causes dired buffers to sort alphabetically regardless of case.") 63;; to support many more ls options, "platform emulation", hooks for
64;; external symbolic link support and more robust sorting.
65
66;;; Code:
68 67
69(defvar ls-lisp-use-insert-directory-program nil 68;;;###autoload
70 "Non-nil causes ls-lisp to revert back to using `insert-directory-program'. 69(defgroup ls-lisp nil
70 "Emulate the ls program completely in Emacs Lisp."
71 :group 'dired)
72
73(defcustom ls-lisp-emulation
74 (cond ((eq system-type 'macos) 'MacOS)
75 ;; ((eq system-type 'windows-nt) 'MS-Windows)
76 ((memq system-type
77 '(hpux dgux usg-unix-v unisoft-unix rtu irix berkeley-unix))
78 'UNIX)) ; very similar to GNU
79 ;; Anything else defaults to nil, meaning GNU.
80 "*Platform to emulate: GNU (default), MacOS, MS-Windows, UNIX.
81Corresponding value is one of the atoms: nil, MacOS, MS-Windows, UNIX.
82Sets default values for: `ls-lisp-ignore-case', `ls-lisp-dirs-first',
83`ls-lisp-verbosity'. Need not match actual platform. Changing this
84option will have no effect until you restart Emacs."
85 :type '(choice (const :tag "GNU" nil)
86 (const MacOS)
87 (const MS-Windows)
88 (const UNIX))
89 :group 'ls-lisp)
90
91(defcustom ls-lisp-ignore-case
92 ;; Name change for consistency with other option names.
93 (or (memq ls-lisp-emulation '(MS-Windows MacOS))
94 (and (boundp 'ls-lisp-dired-ignore-case) ls-lisp-dired-ignore-case))
95 "*Non-nil causes ls-lisp alphabetic sorting to ignore case."
96 :type 'boolean
97 :group 'ls-lisp)
98
99(defcustom ls-lisp-dirs-first (eq ls-lisp-emulation 'MS-Windows)
100 "*Non-nil causes ls-lisp to sort directories first in any ordering.
101\(Or last if it is reversed.) Follows Microsoft Windows Explorer."
102 ;; Functionality suggested by Chris McMahan <cmcmahan@one.net>
103 :type 'boolean
104 :group 'ls-lisp)
105
106(defcustom ls-lisp-verbosity
107 (cond ((eq ls-lisp-emulation 'MacOS) nil)
108 ((eq ls-lisp-emulation 'MS-Windows)
109 (if (and (fboundp 'w32-using-nt) (w32-using-nt))
110 '(links))) ; distinguish NT/2K from 9x
111 ((eq ls-lisp-emulation 'UNIX) '(links uid)) ; UNIX ls
112 (t '(links uid gid))) ; GNU ls
113 "*A list of optional file attributes that ls-lisp should display.
114It should contain none or more of the symbols: links, uid, gid.
115Nil (or an empty list) means display none of them.
116
117Concepts come from UNIX: `links' means count of names associated with
118the file\; `uid' means user (owner) identifier\; `gid' means group
119identifier.
120
121If emulation is MacOS then default is nil\;
122if emulation is MS-Windows then default is `(links)' if platform is
123Windows NT/2K, nil otherwise\;
124if emulation is UNIX then default is `(links uid)'\;
125if emulation is GNU then default is `(links uid gid)'."
126 ;; Functionality suggested by Howard Melman <howard@silverstream.com>
127 :type '(set (const :tag "Show Link Count" links)
128 (const :tag "Show User" uid)
129 (const :tag "Show Group" gid))
130 :group 'ls-lisp)
131
132(defcustom ls-lisp-use-insert-directory-program nil
133 "*Non-nil causes ls-lisp to revert back to using `insert-directory-program'.
71This is useful on platforms where ls-lisp is dumped into Emacs, such as 134This is useful on platforms where ls-lisp is dumped into Emacs, such as
72Microsoft Windows, but you would still like to use a program to list 135Microsoft Windows, but you would still like to use a program to list
73the contents of a directory.") 136the contents of a directory."
137 :type 'boolean
138 :group 'ls-lisp)
139
140(defcustom ls-lisp-support-shell-wildcards t
141 "*Non-nil means ls-lisp treats file patterns as shell wildcards.
142Otherwise they are treated as Emacs regexps (for backward compatibility)."
143 :type 'boolean
144 :group 'ls-lisp)
145
146;; Remember the original insert-directory function
147(or (featurep 'ls-lisp) ; FJW: unless this file is being reloaded!
148 (fset 'original-insert-directory (symbol-function 'insert-directory)))
149
150;; This stub is to allow ls-lisp to parse symbolic links via another
151;; library such as w32-symlinks.el from
152;; http://centaur.qmw.ac.uk/Emacs/:
153(defun ls-lisp-parse-symlink (file-name)
154 "This stub may be redefined to parse FILE-NAME as a symlink.
155It should return nil or the link target as a string."
156 nil)
74 157
75;; Remember the original insert-directory function. 158
76(fset 'original-insert-directory (symbol-function 'insert-directory)) 159;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
77 160
78(defun insert-directory (file switches &optional wildcard full-directory-p) 161(defun insert-directory (file switches &optional wildcard full-directory-p)
79 "Insert directory listing for FILE, formatted according to SWITCHES. 162 "Insert directory listing for FILE, formatted according to SWITCHES.
@@ -83,216 +166,360 @@ Optional third arg WILDCARD means treat FILE as shell wildcard.
83Optional fourth arg FULL-DIRECTORY-P means file is a directory and 166Optional fourth arg FULL-DIRECTORY-P means file is a directory and
84switches do not contain `d', so that a full listing is expected. 167switches do not contain `d', so that a full listing is expected.
85 168
86This version of the function comes from `ls-lisp.el'. Depending upon 169This version of the function comes from `ls-lisp.el'.
87the value of `ls-lisp-use-insert-directory-program', it will use an 170If the value of `ls-lisp-use-insert-directory-program' is non-nil then
88external program if non-nil or the lisp function `ls-lisp-insert-directory' 171it works exactly like the version from `files.el' and runs a directory
89otherwise." 172listing program whose name is in the variable
173`insert-directory-program'; if also WILDCARD is non-nil then it runs
174the shell specified by `shell-file-name'. If the value of
175`ls-lisp-use-insert-directory-program' is nil then it runs a Lisp
176emulation.
177
178The Lisp emulation does not run any external programs or shells. It
179supports ordinary shell wildcards if `ls-lisp-support-shell-wildcards'
180is non-nil; otherwise, it interprets wildcards as regular expressions
181to match file names. It does not support all `ls' switches -- those
182that work are: A a c i r S s t u U X g G B C R and F partly."
90 (if ls-lisp-use-insert-directory-program 183 (if ls-lisp-use-insert-directory-program
91 (original-insert-directory file switches wildcard full-directory-p) 184 (original-insert-directory file switches wildcard full-directory-p)
92 (ls-lisp-insert-directory file switches wildcard full-directory-p))) 185 ;; We need the directory in order to find the right handler.
93 186 (let ((handler (find-file-name-handler (expand-file-name file)
94(defun ls-lisp-insert-directory (file switches &optional wildcard full-directory-p) 187 'insert-directory)))
188 (if handler
189 (funcall handler 'insert-directory file switches
190 wildcard full-directory-p)
191 ;; Convert SWITCHES to a list of characters.
192 (setq switches (delete ?- (append switches nil)))
193 (if wildcard
194 (setq wildcard
195 (if ls-lisp-support-shell-wildcards
196 (wildcard-to-regexp (file-name-nondirectory file))
197 (file-name-nondirectory file))
198 file (file-name-directory file))
199 (if (memq ?B switches) (setq wildcard "[^~]\\'")))
200 (ls-lisp-insert-directory
201 file switches (ls-lisp-time-index switches)
202 wildcard full-directory-p)))))
203
204(defun ls-lisp-insert-directory
205 (file switches time-index wildcard full-directory-p)
95 "Insert directory listing for FILE, formatted according to SWITCHES. 206 "Insert directory listing for FILE, formatted according to SWITCHES.
96Leaves point after the inserted text. 207Leaves point after the inserted text. This is an internal function
97Optional third arg WILDCARD means treat FILE as shell wildcard. 208optionally called by the `ls-lisp.el' version of `insert-directory'.
98Optional fourth arg FULL-DIRECTORY-P means file is a directory and 209It is called recursively if the -R switch is used.
99switches do not contain `d', so that a full listing is expected. 210SWITCHES is a *list* of characters. TIME-INDEX is the time index into
100 211file-attributes according to SWITCHES. WILDCARD is nil or an *Emacs
101This version of the function comes from `ls-lisp.el'. It does not 212regexp*. FULL-DIRECTORY-P means file is a directory and SWITCHES does
102run any external programs or shells. It supports ordinary shell 213not contain `d', so that a full listing is expected."
103wildcards if `ls-lisp-support-shell-wildcards' variable is non-nil; 214 ;; Sometimes we get ".../foo*/" as FILE. While the shell and
104otherwise, it interprets wildcards as regular expressions to match 215 ;; `ls' don't mind, we certainly do, because it makes us think
105file names. 216 ;; there is no wildcard, only a directory name.
106 217 (if (and ls-lisp-support-shell-wildcards
107Not all `ls' switches are supported. The switches that work 218 (string-match "[[?*]" file))
108are: A a c i r S s t u" 219 (progn
109 (let ((handler (find-file-name-handler file 'insert-directory)) 220 (or (not (eq (aref file (1- (length file))) ?/))
110 fattr) 221 (setq file (substring file 0 (1- (length file)))))
111 (if handler 222 (setq wildcard t)))
112 (funcall handler 'insert-directory file switches 223 (if (or wildcard full-directory-p)
113 wildcard full-directory-p) 224 (let* ((dir (file-name-as-directory file))
114 ;; Sometimes we get ".../foo*/" as FILE. While the shell and 225 (default-directory dir) ; so that file-attributes works
115 ;; `ls' don't mind, we certainly do, because it makes us think 226 (file-alist
116 ;; there is no wildcard, only a directory name. 227 (directory-files-and-attributes dir nil wildcard t))
117 (if (and ls-lisp-support-shell-wildcards 228 (now (current-time))
118 (string-match "[[?*]" file)) 229 (sum 0)
119 (progn 230 ;; do all bindings here for speed
120 (or (not (eq (aref file (1- (length file))) ?/)) 231 total-line files elt short file-size fil attr)
121 (setq file (substring file 0 (1- (length file))))) 232 (cond ((memq ?A switches)
122 (setq wildcard t))) 233 (setq file-alist
123 ;; Convert SWITCHES to a list of characters. 234 (ls-lisp-delete-matching "^\\.\\.?$" file-alist)))
124 (setq switches (append switches nil)) 235 ((not (memq ?a switches))
125 (if wildcard 236 ;; if neither -A nor -a, flush . files
126 (setq wildcard 237 (setq file-alist
127 (if ls-lisp-support-shell-wildcards 238 (ls-lisp-delete-matching "^\\." file-alist))))
128 (wildcard-to-regexp (file-name-nondirectory file)) 239 (setq file-alist
129 (file-name-nondirectory file)) 240 (ls-lisp-handle-switches file-alist switches))
130 file (file-name-directory file))) 241 (if (memq ?C switches) ; column (-C) format
131 (if (or wildcard 242 (ls-lisp-column-format file-alist)
132 full-directory-p) 243 (setq total-line (cons (point) (car-safe file-alist)))
133 (let* ((dir (file-name-as-directory file)) 244 (setq files file-alist)
134 (default-directory dir);; so that file-attributes works 245 (while files ; long (-l) format
135 (sum 0) 246 (setq elt (car files)
136 elt 247 files (cdr files)
137 short 248 short (car elt)
138 (file-alist (directory-files-and-attributes dir nil wildcard)) 249 attr (cdr elt)
139 (now (current-time)) 250 file-size (nth 7 attr))
140 ;; do all bindings here for speed 251 (and attr
141 file-size 252 (setq sum (+ file-size
142 fil attr) 253 ;; Even if neither SUM nor file's size
143 (cond ((memq ?A switches) 254 ;; overflow, their sum could.
144 (setq file-alist 255 (if (or (< sum (- 134217727 file-size))
145 (ls-lisp-delete-matching "^\\.\\.?$" file-alist))) 256 (floatp sum)
146 ((not (memq ?a switches)) 257 (floatp file-size))
147 ;; if neither -A nor -a, flush . files 258 sum
148 (setq file-alist 259 (float sum))))
149 (ls-lisp-delete-matching "^\\." file-alist)))) 260 (insert (ls-lisp-format short attr file-size
150 ;; ``Total'' line (filled in afterwards). 261 switches time-index now))))
151 (insert (if (car-safe file-alist) 262 ;; Insert total size of all files:
152 "total \007\n" 263 (save-excursion
153 ;; Shell says ``No match'' if no files match 264 (goto-char (car total-line))
154 ;; the wildcard; let's say something similar. 265 (or (cdr total-line)
155 "(No match)\ntotal \007\n")) 266 ;; Shell says ``No match'' if no files match
156 (setq file-alist 267 ;; the wildcard; let's say something similar.
157 (ls-lisp-handle-switches file-alist switches)) 268 (insert "(No match)\n"))
269 (insert (format "total %.0f\n" (fceiling (/ sum 1024.0))))))
270 (if (memq ?R switches)
271 ;; List the contents of all directories recursively.
272 ;; cadr of each element of `file-alist' is t for
273 ;; directory, string (name linked to) for symbolic
274 ;; link, or nil.
158 (while file-alist 275 (while file-alist
159 (setq elt (car file-alist) 276 (setq elt (car file-alist)
160 file-alist (cdr file-alist) 277 file-alist (cdr file-alist))
161 short (car elt) 278 (when (and (eq (cadr elt) t) ; directory
162 attr (cdr elt) 279 (not (string-match "\\`\\.\\.?\\'" (car elt))))
163 file-size (nth 7 attr)) 280 (setq elt (expand-file-name (car elt) dir))
164 (and attr 281 (insert "\n" elt ":\n")
165 (setq sum 282 (ls-lisp-insert-directory
166 ;; Even if neither SUM nor file's size 283 elt switches time-index wildcard full-directory-p)))))
167 ;; overflow, their sum could. 284 ;; If not full-directory-p, FILE *must not* end in /, as
168 (if (or (< sum (- 134217727 file-size)) 285 ;; file-attributes will not recognize a symlink to a directory,
169 (floatp sum) 286 ;; so must make it a relative filename as ls does:
170 (floatp file-size)) 287 (if (eq (aref file (1- (length file))) ?/)
171 (+ sum file-size) 288 (setq file (substring file 0 -1)))
172 (+ (float sum) file-size))) 289 (let ((fattr (file-attributes file)))
173 (insert (ls-lisp-format short attr file-size switches now)) 290 (if fattr
174 )) 291 (insert (ls-lisp-format file fattr (nth 7 fattr)
175 ;; Fill in total size of all files: 292 switches time-index (current-time)))
176 (save-excursion 293 (message "%s: doesn't exist or is inaccessible" file)
177 (search-backward "total \007") 294 (ding) (sit-for 2))))) ; to show user the message!
178 (goto-char (match-end 0)) 295
179 (delete-char -1) 296(defun ls-lisp-column-format (file-alist)
180 (insert (format "%.0f" (fceiling (/ sum 1024.0)))))) 297 "Insert the file names (only) in FILE-ALIST into the current buffer.
181 ;; if not full-directory-p, FILE *must not* end in /, as 298Format in columns, sorted vertically, following GNU ls -C.
182 ;; file-attributes will not recognize a symlink to a directory 299Responds to the window width as ls should but may not!"
183 ;; must make it a relative filename as ls does: 300 (let (files fmt ncols collen (nfiles 0) (colwid 0))
184 (if (eq (aref file (1- (length file))) ?/) 301 ;; Count number of files as `nfiles', build list of filenames as
185 (setq file (substring file 0 (1- (length file))))) 302 ;; `files', and find maximum filename length as `colwid':
186 (setq fattr (file-attributes file)) 303 (let (file len)
187 (if fattr 304 (while file-alist
188 (insert (ls-lisp-format file fattr (nth 7 fattr) 305 (setq nfiles (1+ nfiles)
189 switches (current-time))) 306 file (caar file-alist)
190 (message "%s: doesn't exist or is inaccessible" file) 307 files (cons file files)
191 (ding) 308 file-alist (cdr file-alist)
192 (sit-for 2)))))) 309 len (length file))
310 (if (> len colwid) (setq colwid len))))
311 (setq files (nreverse files)
312 colwid (+ 2 colwid) ; 2 character column gap
313 fmt (format "%%-%ds" colwid) ; print format
314 ncols (/ (window-width) colwid) ; no of columns
315 collen (/ nfiles ncols)) ; floor of column length
316 (if (> nfiles (* collen ncols)) (setq collen (1+ collen)))
317 ;; Output the file names in columns, sorted vertically:
318 (let ((i 0) j)
319 (while (< i collen)
320 (setq j i)
321 (while (< j nfiles)
322 (insert (format fmt (nth j files)))
323 (setq j (+ j collen)))
324 ;; FJW: This is completely unnecessary, but I don't like
325 ;; trailing white space...
326 (delete-region (point) (progn (skip-chars-backward " \t") (point)))
327 (insert ?\n)
328 (setq i (1+ i))))))
193 329
194(defun ls-lisp-delete-matching (regexp list) 330(defun ls-lisp-delete-matching (regexp list)
195 ;; Delete all elements matching REGEXP from LIST, return new list. 331 "Delete all elements matching REGEXP from LIST, return new list."
196 ;; Should perhaps use setcdr for efficiency. 332 ;; Should perhaps use setcdr for efficiency.
197 (let (result) 333 (let (result)
198 (while list 334 (while list
199 (or (string-match regexp (car (car list))) 335 (or (string-match regexp (caar list))
200 (setq result (cons (car list) result))) 336 (setq result (cons (car list) result)))
201 (setq list (cdr list))) 337 (setq list (cdr list)))
202 result)) 338 result))
203 339
340(defsubst ls-lisp-string-lessp (s1 s2)
341 "Return t if string S1 is less than string S2 in lexicographic order.
342Case is significant if `ls-lisp-ignore-case' is nil.
343Unibyte strings are converted to multibyte for comparison."
344 (let ((u (compare-strings s1 0 nil s2 0 nil ls-lisp-ignore-case)))
345 (and (numberp u) (< u 0))))
346
204(defun ls-lisp-handle-switches (file-alist switches) 347(defun ls-lisp-handle-switches (file-alist switches)
348 "Return new FILE-ALIST sorted according to SWITCHES.
349SWITCHES is a list of characters. Default sorting is alphabetic."
205 ;; FILE-ALIST's elements are (FILE . FILE-ATTRIBUTES). 350 ;; FILE-ALIST's elements are (FILE . FILE-ATTRIBUTES).
206 ;; Return new alist sorted according to SWITCHES which is a list of 351 (or (memq ?U switches) ; unsorted
207 ;; characters. Default sorting is alphabetically. 352 ;; Catch and ignore unexpected sorting errors
208 (let (index) 353 (condition-case err
209 (setq file-alist 354 (setq file-alist
210 (sort file-alist 355 (let (index)
211 (cond ((memq ?S switches) ; sorted on size 356 ;; Copy file-alist in case of error
212 (function 357 (sort (copy-sequence file-alist) ; modifies its argument!
213 (lambda (x y) 358 (cond ((memq ?S switches)
214 ;; 7th file attribute is file size 359 (lambda (x y) ; sorted on size
215 ;; Make largest file come first 360 ;; 7th file attribute is file size
216 (< (nth 7 (cdr y)) 361 ;; Make largest file come first
217 (nth 7 (cdr x)))))) 362 (< (nth 7 (cdr y))
218 ((memq ?t switches) ; sorted on time 363 (nth 7 (cdr x)))))
219 (setq index (ls-lisp-time-index switches)) 364 ((setq index (ls-lisp-time-index switches))
220 (function 365 (lambda (x y) ; sorted on time
221 (lambda (x y) 366 (ls-lisp-time-lessp (nth index (cdr y))
222 (ls-lisp-time-lessp (nth index (cdr y)) 367 (nth index (cdr x)))))
223 (nth index (cdr x)))))) 368 ((memq ?X switches)
224 (t ; sorted alphabetically 369 (lambda (x y) ; sorted on extension
225 (if ls-lisp-dired-ignore-case 370 (ls-lisp-string-lessp
226 (function 371 (ls-lisp-extension (car x))
227 (lambda (x y) 372 (ls-lisp-extension (car y)))))
228 (string-lessp (upcase (car x)) 373 (t
229 (upcase (car y))))) 374 (lambda (x y) ; sorted alphabetically
230 (function 375 (ls-lisp-string-lessp (car x) (car y))))))))
231 (lambda (x y) 376 (error (message "Unsorted (ls-lisp sorting error) - %s"
232 (string-lessp (car x) 377 (error-message-string err))
233 (car y)))))))))) 378 (ding) (sit-for 2)))) ; to show user the message!
234 (if (memq ?r switches) ; reverse sort order 379 (if (memq ?F switches) ; classify switch
235 (setq file-alist (nreverse file-alist))) 380 (setq file-alist (mapcar 'ls-lisp-classify file-alist)))
236 file-alist) 381 (if ls-lisp-dirs-first
382 ;; Re-sort directories first, without otherwise changing the
383 ;; ordering, and reverse whole list. cadr of each element of
384 ;; `file-alist' is t for directory, string (name linked to) for
385 ;; symbolic link, or nil.
386 (let (el dirs files)
387 (while file-alist
388 (if (eq (cadr (setq el (car file-alist))) t) ; directory
389 (setq dirs (cons el dirs))
390 (setq files (cons el files)))
391 (setq file-alist (cdr file-alist)))
392 (setq file-alist
393 (if (memq ?U switches) ; unsorted order is reversed
394 (nconc dirs files)
395 (nconc files dirs)
396 ))))
397 ;; Finally reverse file alist if necessary.
398 ;; (eq below MUST compare `(not (memq ...))' to force comparison of
399 ;; `t' or `nil', rather than list tails!)
400 (if (eq (eq (not (memq ?U switches)) ; unsorted order is reversed
401 (not (memq ?r switches))) ; reversed sort order requested
402 ls-lisp-dirs-first) ; already reversed
403 (nreverse file-alist)
404 file-alist))
405
406(defun ls-lisp-classify (filedata)
407 "Append a character to each file name indicating the file type.
408Also, for regular files that are executable, append `*'.
409The file type indicators are `/' for directories, `@' for symbolic
410links, `|' for FIFOs, `=' for sockets, and nothing for regular files.
411\[But FIFOs and sockets are not recognised.]
412FILEDATA has the form (filename . `file-attributes'). Its `cadr' is t
413for directory, string (name linked to) for symbolic link, or nil."
414 (let ((dir (cadr filedata)) (file-name (car filedata)))
415 (cond ((or dir
416 ;; Parsing .lnk files here is perhaps overkill!
417 (setq dir (ls-lisp-parse-symlink file-name)))
418 (cons
419 (concat file-name (if (eq dir t) "/" "@"))
420 (cdr filedata)))
421 ((string-match "x" (nth 9 filedata))
422 (cons
423 (concat file-name "*")
424 (cdr filedata)))
425 (t filedata))))
426
427(defun ls-lisp-extension (filename)
428 "Return extension of FILENAME (ignoring any version extension)
429FOLLOWED by null and full filename, SOLELY for full alpha sort."
430 ;; Force extension sort order: `no ext' then `null ext' then `ext'
431 ;; to agree with GNU ls.
432 (concat
433 (let* ((i (length filename)) end)
434 (if (= (aref filename (1- i)) ?.) ; null extension
435 "\0"
436 (while (and (>= (setq i (1- i)) 0)
437 (/= (aref filename i) ?.)))
438 (if (< i 0) "\0\0" ; no extension
439 (if (/= (aref filename (1+ i)) ?~)
440 (substring filename (1+ i))
441 ;; version extension found -- ignore it
442 (setq end i)
443 (while (and (>= (setq i (1- i)) 0)
444 (/= (aref filename i) ?.)))
445 (if (< i 0) "\0\0" ; no extension
446 (substring filename (1+ i) end))))
447 )) "\0" filename))
237 448
238;; From Roland McGrath. Can use this to sort on time. 449;; From Roland McGrath. Can use this to sort on time.
239(defun ls-lisp-time-lessp (time0 time1) 450(defun ls-lisp-time-lessp (time0 time1)
240 (let ((hi0 (car time0)) 451 "Return t if time TIME0 is earlier than time TIME1."
241 (hi1 (car time1)) 452 (let ((hi0 (car time0)) (hi1 (car time1)))
242 (lo0 (car (cdr time0)))
243 (lo1 (car (cdr time1))))
244 (or (< hi0 hi1) 453 (or (< hi0 hi1)
245 (and (= hi0 hi1) 454 (and (= hi0 hi1)
246 (< lo0 lo1))))) 455 (< (cadr time0) (cadr time1))))))
247 456
248 457(defun ls-lisp-format (file-name file-attr file-size switches time-index now)
249(defun ls-lisp-format (file-name file-attr file-size switches now) 458 "Format one line of long ls output for file FILE-NAME.
250 (let ((file-type (nth 0 file-attr))) 459FILE-ATTR and FILE-SIZE give the file's attributes and size.
460SWITCHES, TIME-INDEX and NOW give the full switch list and time data."
461 (let ((file-type (nth 0 file-attr))
462 ;; t for directory, string (name linked to)
463 ;; for symbolic link, or nil.
464 (drwxrwxrwx (nth 8 file-attr))) ; attribute string ("drwxrwxrwx")
465 (and (null file-type)
466 ;; Maybe no kernel support for symlinks, so...
467 (setq file-type (ls-lisp-parse-symlink file-name))
468 (aset drwxrwxrwx 0 ?l)) ; symbolic link - update attribute string
251 (concat (if (memq ?i switches) ; inode number 469 (concat (if (memq ?i switches) ; inode number
252 (format "%6d " (nth 10 file-attr))) 470 (format " %6d" (nth 10 file-attr)))
253 ;; nil is treated like "" in concat 471 ;; nil is treated like "" in concat
254 (if (memq ?s switches) ; size in K 472 (if (memq ?s switches) ; size in K
255 (format "%4.0f " (fceiling (/ file-size 1024.0)))) 473 (format " %4.0f" (fceiling (/ file-size 1024.0))))
256 (nth 8 file-attr) ; permission bits 474 drwxrwxrwx ; attribute string
257 ;; numeric uid/gid are more confusing than helpful 475 (if (memq 'links ls-lisp-verbosity)
476 (format " %3d" (nth 1 file-attr))) ; link count
477 ;; Numeric uid/gid are more confusing than helpful;
258 ;; Emacs should be able to make strings of them. 478 ;; Emacs should be able to make strings of them.
259 ;; user-login-name and user-full-name could take an 479 ;; They tend to be bogus on non-UNIX platforms anyway so
260 ;; optional arg. 480 ;; optionally hide them.
261 (format (if (floatp file-size) 481 (if (memq 'uid ls-lisp-verbosity)
262 " %3d %-8s %-8s %8.0f " 482 ;; (user-login-name uid) works on Windows NT but not
263 " %3d %-8s %-8s %8d ") 483 ;; on 9x and maybe not on some other platforms, so...
264 (nth 1 file-attr) ; no. of links 484 (let ((uid (nth 2 file-attr)))
265 (if (= (user-uid) (nth 2 file-attr)) 485 (if (= uid (user-uid))
266 (user-login-name) 486 (format " %-8s" (user-login-name))
267 (int-to-string (nth 2 file-attr))) ; uid 487 (format " %-8d" uid))))
268 (if (eq system-type 'ms-dos) 488 (if (not (memq ?G switches)) ; GNU ls -- shows group by default
269 "root" ; everything is root on MSDOS. 489 (if (or (memq ?g switches) ; UNIX ls -- no group by default
270 (int-to-string (nth 3 file-attr))) ; gid 490 (memq 'gid ls-lisp-verbosity))
271 file-size 491 (if (memq system-type '(macos windows-nt ms-dos))
272 ) 492 ;; No useful concept of group...
273 (ls-lisp-format-time file-attr switches now) 493 "root"
494 (let* ((gid (nth 3 file-attr))
495 (group (user-login-name gid)))
496 (if group
497 (format " %-8s" group)
498 (format " %-8d" gid))))))
499 (format (if (floatp file-size) " %8.0f" " %8d") file-size)
500 " "
501 (ls-lisp-format-time file-attr time-index now)
274 " " 502 " "
275 file-name 503 file-name
276 (if (stringp file-type) ; is a symbolic link 504 (if (stringp file-type) ; is a symbolic link
277 (concat " -> " file-type) 505 (concat " -> " file-type))
278 "")
279 "\n" 506 "\n"
280 ))) 507 )))
281 508
282(defun ls-lisp-time-index (switches) 509(defun ls-lisp-time-index (switches)
283 ;; Return index into file-attributes according to ls SWITCHES. 510 "Return time index into file-attributes according to ls SWITCHES list.
284 (cond 511Return nil if no time switch found."
285 ((memq ?c switches) 6) ; last mode change 512 ;; FJW: Default of nil is IMPORTANT and used in `ls-lisp-handle-switches'!
286 ((memq ?u switches) 4) ; last access 513 (cond ((memq ?c switches) 6) ; last mode change
287 ;; default is last modtime 514 ((memq ?t switches) 5) ; last modtime
288 (t 5))) 515 ((memq ?u switches) 4))) ; last access
289 516
290(defun ls-lisp-format-time (file-attr switches now) 517(defun ls-lisp-format-time (file-attr time-index now)
291 ;; Format time string for file with attributes FILE-ATTR according 518 "Format time for file with attributes FILE-ATTR according to TIME-INDEX.
292 ;; to SWITCHES (a list of ls option letters of which c and u are recognized). 519Use the same method as ls to decide whether to show time-of-day or year,
293 ;; Use the same method as `ls' to decide whether to show time-of-day or year, 520depending on distance between file date and NOW.
294 ;; depending on distance between file date and NOW. 521All ls time options, namely c, t and u, are handled."
295 (let* ((time (nth (ls-lisp-time-index switches) file-attr)) 522 (let* ((time (nth (or time-index 5) file-attr)) ; default is last modtime
296 (diff16 (- (car time) (car now))) 523 (diff16 (- (car time) (car now)))
297 (diff (+ (ash diff16 16) (- (car (cdr time)) (car (cdr now))))) 524 (diff (+ (ash diff16 16) (- (car (cdr time)) (car (cdr now)))))
298 (past-cutoff (- (* 6 30 24 60 60))) ; 6 30-day months 525 (past-cutoff (- (* 6 30 24 60 60))) ; 6 30-day months