fixes. See the ChangeLog.
+2000-10-13 John Wiegley <johnw@gnu.org>
+
+ * eshell/esh-util.el: Added a global form which declares an
+ autoload for `parse-time-string', if that function is not already
+ defined, and if parse-time.el is available on the user's system.
+
+ * eshell/em-ls.el (eshell-ls-applicable): Extended this function
+ to be aware of ange-ftp user info.
+ (eshell-do-ls): Bind `ange-cache'. Also, use
+ `eshell-file-attributes'.
+ (eshell-ls-annotate): Use `eshell-file-attributes'.
+ (eshell-ls-file): Made the user-id printing code a bit smarter.
+
+ * eshell/esh-util.el (eshell-ange-ls-uids): Added variable, to
+ allow identification of alias user ids in remote directories.
+ It's manual, but there's no other way to know when the current
+ user on the local machine, is also the owning user on the remote
+ machine.
+ (fboundp): Bind `ange-cache'.
+ (eshell-directory-files-and-attributes): Re-organized the logic a
+ bit to use `eshell-file-attributes' instead of `file-attributes'.
+ The former is more sensitive to directories that are read via FTP,
+ and knows how to use ange-ftp to determine full attribute
+ information, instead of just the name and last modtime.
+ (eshell-current-ange-uids): Return the current user id when in a
+ remote directory.
+ (eshell-parse-ange-ls): Parse a full directory listing that has
+ been returned by ange-ftp.
+ (eshell-file-attributes): This beefed up version of
+ `file-attributes' is only special if the user is currently in a
+ remote directory, in which case it does a lot of work to find out
+ what the real attributes of a file are, as they appear on the
+ remote machine. This makes usage of remote directories (i.e.,
+ ange-ftp pathnames) much more useful. You can now use Eshell as a
+ full-fledged FTP client, with much more manipulation ability than
+ most other clients.
+
+ * eshell/em-unix.el (eshell-du-prefer-over-ange): Added a new
+ variable, which means that Eshell's du should always be preferred
+ in remote directories.
+ (eshell-shuffle-files): Use `eshell-file-attributes', rather than
+ just `file-attributes'.
+ (eshell-mvcp-template): Bind `ange-cache', to improve performance
+ when reading remote directories. This is an Eshell-specific
+ variable (not part of ange-ftp).
+ (eshell/ln): Bind `ange-cache'.
+ (eshell/du): Added some extra logic for determining when to use
+ Eshell's du (which is slow), and when to use the external version
+ (which may or may not exist).
+
+ * eshell/em-rebind.el (eshell-delchar-or-maybe-eof): Call
+ `eshell-interactive-process', rather than using
+ `get-buffer-process', since backgrounded processes don't count in
+ the context of this function's logic.
+
+ * eshell/esh-arg.el (eshell-parse-double-quote): Moved a call to
+ `forward-char', so that null strings are parsed correctly.
+
+2000-09-10 John Wiegley <johnw@gnu.org>
+
+ * eshell/em-pred.el (eshell-pred-file-type,
+ eshell-pred-file-links, eshell-pred-file-size): Use
+ `eshell-file-attributes'. This is more correct over ange-ftp.
+
+ * eshell/em-glob.el (eshell-extended-glob): Bind `ange-cache', so
+ that remote file globbing is more efficient.
+
+ * eshell/em-ls.el (eshell-ls-dir): Use `expand-file-name' when
+ gathering the files and attributes within a directory.
+
+ * eshell/em-unix.el (eshell/cat): If any of the files passed on
+ the command line is a special file (not a regular file, directory
+ or symlink), always attempt to call the external version of cat.
+
+2000-09-06 John Wiegley <johnw@gnu.org>
+
+ * eshell/esh-mode.el (eshell-find-tag): Corrections to the
+ Eshell-friendly version of find-tag.
+
2000-10-13 Miles Bader <miles@lsi.nec.co.jp>
* image-file.el (image-file-name-extensions)
* files.el (set-auto-mode): Ignore unknown -*- mode -*- rather than
raise an error. This way it can still default to a sane value.
+2000-10-06 Stefan Monnier <monnier@cs.yale.edu>
+
* startup.el (fancy-splash-screens): Use local rather than global map.
Don't use `update-menu-bindings' any more.
Get rid of assumptions about keymap representation.
2000-09-16 Andrew Innes <andrewi@gnu.org>
- * makefile.nt (compile-files): No need to make .elc files
- read-only, since they aren't under VC now.
-
* makefile.w32-in (compile-files-CMD): No need to make .elc files
read-only, since they aren't under VC now.
(INCLUDE-REGEXP EXCLUDE-REGEXP (PRED-FUNC-LIST) (MOD-FUNC-LIST))"
(let ((paths (eshell-split-path glob))
- matches message-shown)
+ matches message-shown ange-cache)
(unwind-protect
(if (and (cdr paths)
(file-name-absolute-p (car paths)))
"Test whether, for ATTRS, the user UID can do what corresponds to INDEX.
This is really just for efficiency, to avoid having to stat the file
yet again."
- `(if (= (user-uid) (nth 2 ,attrs))
- (not (eq (aref (nth 8 ,attrs) ,index) ?-))
- (,(eval func) ,file)))
+ `(if (numberp (nth 2 ,attrs))
+ (if (= (user-uid) (nth 2 ,attrs))
+ (not (eq (aref (nth 8 ,attrs) ,index) ?-))
+ (,(eval func) ,file))
+ (not (eq (aref (nth 8 ,attrs)
+ (+ ,index (if (member (nth 2 ,attrs)
+ (eshell-current-ange-uids))
+ 0 6)))
+ ?-))))
(defcustom eshell-ls-highlight-alist nil
"*This alist correlates test functions to color.
(defvar show-all)
(defvar show-recursive)
(defvar show-size)
- (defvar sort-method))
+ (defvar sort-method)
+ (defvar ange-cache))
(defun eshell-do-ls (&rest args)
"Implementation of \"ls\" in Lisp, passing ARGS."
(setq listing-style 'by-columns))
(unless args
(setq args (list ".")))
- (let ((eshell-ls-exclude-regexp eshell-ls-exclude-regexp))
+ (let ((eshell-ls-exclude-regexp eshell-ls-exclude-regexp) ange-cache)
(when ignore-pattern
(unless (eshell-using-module 'eshell-glob)
(error (concat "-I option requires that `eshell-glob'"
(file-name-absolute-p arg))
(expand-file-name arg)
arg)
- (file-attributes arg)))) args)
+ (eshell-file-attributes arg)))) args)
t (expand-file-name default-directory)))
(funcall flush-func)))
(file-name-directory
(expand-file-name (car fileinfo))))))
(setq attr
- (file-attributes
+ (eshell-file-attributes
(let ((target (if dir
(expand-file-name (cadr fileinfo) dir)
(cadr fileinfo))))
"%s%4d %-8s %-8s "
(or (nth 8 attrs) "??????????")
(or (nth 1 attrs) 0)
- (or (and (not numeric-uid-gid)
- (nth 2 attrs)
- (eshell-substring
- (user-login-name (nth 2 attrs)) 8))
+ (or (let ((user (nth 2 attrs)))
+ (and (not numeric-uid-gid)
+ user
+ (eshell-substring
+ (if (numberp user)
+ (user-login-name user)
+ user) 8)))
(nth 2 attrs)
"")
- (or (and (not numeric-uid-gid)
- (nth 3 attrs)
- (eshell-substring
- (eshell-group-name (nth 3 attrs)) 8))
+ (or (let ((group (nth 3 attrs)))
+ (and (not numeric-uid-gid)
+ group
+ (eshell-substring
+ (if (numberp group)
+ (eshell-group-name group)
+ group) 8)))
(nth 3 attrs)
""))
(let* ((str (eshell-ls-printable-size (nth 7 attrs)))
(forward-char)
(setq type ?%)))
`(lambda (file)
- (let ((attrs (file-attributes (directory-file-name file))))
+ (let ((attrs (eshell-file-attributes (directory-file-name file))))
(if attrs
(memq (aref (nth 8 attrs) 0)
,(if (eq type ?%)
(setq amount (string-to-number (match-string 0)))
(goto-char (match-end 0))
`(lambda (file)
- (let ((attrs (file-attributes file)))
+ (let ((attrs (eshell-file-attributes file)))
(if attrs
(,(if (eq qual ?-)
'<
(setq amount (* (string-to-number (match-string 0)) quantum))
(goto-char (match-end 0))
`(lambda (file)
- (let ((attrs (file-attributes file)))
+ (let ((attrs (eshell-file-attributes file)))
(if attrs
(,(if (eq qual ?-)
'<
Sends an EOF only if point is at the end of the buffer and there is no
input."
(interactive "p")
- (let ((proc (get-buffer-process (current-buffer))))
+ (let ((proc (eshell-interactive-process)))
(if (eobp)
(cond
((/= (point) eshell-last-output-end)
:type 'boolean
:group 'eshell-unix)
+(defcustom eshell-du-prefer-over-ange nil
+ "*Use Eshell's du in ange-ftp remote directories.
+Otherwise, Emacs will attempt to use rsh to invoke du the machine."
+ :type 'boolean
+ :group 'eshell-unix)
+
(require 'esh-opt)
;;; Functions:
"Shuffle around some filesystem entries, using FUNC to do the work."
(if (null target)
(error "%s: missing destination file" command))
- (let ((attr-target (file-attributes target))
+ (let ((attr-target (eshell-file-attributes target))
(is-dir (or (file-directory-p target)
(and preview (not eshell-warn-dot-directories))))
attr)
((and attr-target
(or (not (eshell-under-windows-p))
(eq system-type 'ms-dos))
- (setq attr (file-attributes (car files)))
+ (setq attr (eshell-file-attributes (car files)))
+ (nth 10 attr-target) (nth 10 attr)
(= (nth 10 attr-target) (nth 10 attr))
+ (nth 11 attr-target) (nth 11 attr)
(= (nth 11 attr-target) (nth 11 attr)))
(eshell-error (format "%s: `%s' and `%s' are the same file\n"
command (car files) target)))
(let (eshell-warn-dot-directories)
(if (and (not deep)
(eq func 'rename-file)
- (= (nth 11 (file-attributes
+ (= (nth 11 (eshell-file-attributes
(file-name-directory
(expand-file-name source))))
- (nth 11 (file-attributes
+ (nth 11 (eshell-file-attributes
(file-name-directory
(expand-file-name target))))))
(apply 'eshell-funcalln func source target args)
(or (not no-dereference)
(not (file-symlink-p (car args)))))))
(eshell-shorthand-tar-command ,command args)
- (let (target)
+ (let (target ange-cache)
(if (> (length args) 1)
(progn
(setq target (car (last args)))
more than one TARGET, the last argument must be a directory; create links
in DIRECTORY to each TARGET. Create hard links by default, symbolic links
with '--symbolic'. When creating hard links, each TARGET must exist.")
- (let (target no-dereference)
+ (let (target no-dereference ange-cache)
(if (> (length args) 1)
(progn
(setq target (car (last args)))
nil))
(defun eshell/cat (&rest args)
- "Implementation of cat in Lisp."
- (if eshell-in-pipeline-p
- (throw 'eshell-replace-command
- (eshell-parse-command "*cat" (eshell-flatten-list args)))
+ "Implementation of cat in Lisp.
+If in a pipeline, or the file is not a regular file, directory or
+symlink, then revert to the system's definition of cat."
+ (setq args (eshell-flatten-list args))
+ (if (or eshell-in-pipeline-p
+ (catch 'special
+ (eshell-for arg args
+ (unless (let ((attrs (eshell-file-attributes arg)))
+ (and attrs (memq (aref (nth 8 attrs) 0)
+ '(?d ?l ?-))))
+ (throw 'special t)))))
+ (let ((ext-cat (eshell-search-path "cat")))
+ (if ext-cat
+ (throw 'eshell-replace-command
+ (eshell-parse-command ext-cat args))
+ (if eshell-in-pipeline-p
+ (error "Eshell's `cat' does not work in pipelines")
+ (error "Eshell's `cat' cannot display one of the files given"))))
(eshell-init-print-buffer)
(eshell-eval-using-options
"cat" args
(defun eshell/du (&rest args)
"Implementation of \"du\" in Lisp, passing ARGS."
- (if (eshell-search-path "du")
- (throw 'eshell-replace-command
- (eshell-parse-command "*du" (eshell-flatten-list args)))
- (eshell-eval-using-options
- "du" args
- '((?a "all" nil show-all
- "write counts for all files, not just directories")
- (nil "block-size" t block-size
- "use SIZE-byte blocks (i.e., --block-size SIZE)")
- (?b "bytes" nil by-bytes
- "print size in bytes")
- (?c "total" nil grand-total
- "produce a grand total")
- (?d "max-depth" t max-depth
- "display data only this many levels of data")
- (?h "human-readable" 1024 human-readable
- "print sizes in human readable format")
- (?H "is" 1000 human-readable
- "likewise, but use powers of 1000 not 1024")
- (?k "kilobytes" 1024 block-size
- "like --block-size 1024")
- (?L "dereference" nil dereference-links
- "dereference all symbolic links")
- (?m "megabytes" 1048576 block-size
- "like --block-size 1048576")
- (?s "summarize" 0 max-depth
- "display only a total for each argument")
- (?x "one-file-system" nil only-one-filesystem
- "skip directories on different filesystems")
- (nil "help" nil nil
- "show this usage screen")
- :external "du"
- :usage "[OPTION]... FILE...
+ (setq args (if args
+ (eshell-flatten-list args)
+ '(".")))
+ (let ((ext-du (eshell-search-path "du")))
+ (if (and ext-du
+ (not (catch 'have-ange-path
+ (eshell-for arg args
+ (if (eq (find-file-name-handler (expand-file-name arg)
+ 'directory-files)
+ 'ange-ftp-hook-function)
+ (throw 'have-ange-path t))))))
+ (throw 'eshell-replace-command
+ (eshell-parse-command ext-du args))
+ (eshell-eval-using-options
+ "du" args
+ '((?a "all" nil show-all
+ "write counts for all files, not just directories")
+ (nil "block-size" t block-size
+ "use SIZE-byte blocks (i.e., --block-size SIZE)")
+ (?b "bytes" nil by-bytes
+ "print size in bytes")
+ (?c "total" nil grand-total
+ "produce a grand total")
+ (?d "max-depth" t max-depth
+ "display data only this many levels of data")
+ (?h "human-readable" 1024 human-readable
+ "print sizes in human readable format")
+ (?H "is" 1000 human-readable
+ "likewise, but use powers of 1000 not 1024")
+ (?k "kilobytes" 1024 block-size
+ "like --block-size 1024")
+ (?L "dereference" nil dereference-links
+ "dereference all symbolic links")
+ (?m "megabytes" 1048576 block-size
+ "like --block-size 1048576")
+ (?s "summarize" 0 max-depth
+ "display only a total for each argument")
+ (?x "one-file-system" nil only-one-filesystem
+ "skip directories on different filesystems")
+ (nil "help" nil nil
+ "show this usage screen")
+ :external "du"
+ :usage "[OPTION]... FILE...
Summarize disk usage of each FILE, recursively for directories.")
- (unless by-bytes
- (setq block-size (or block-size 1024)))
- (if (and max-depth (stringp max-depth))
- (setq max-depth (string-to-int max-depth)))
- ;; filesystem support means nothing under Windows
- (if (eshell-under-windows-p)
- (setq only-one-filesystem nil))
- (unless args
- (setq args '(".")))
- (let ((size 0.0))
- (while args
- (if only-one-filesystem
- (setq only-one-filesystem
- (nth 11 (file-attributes
- (file-name-as-directory (car args))))))
- (setq size (+ size (eshell-du-sum-directory
- (directory-file-name (car args)) 0)))
- (setq args (cdr args)))
- (if grand-total
- (eshell-print (concat (eshell-du-size-string size)
- "total\n")))))))
+ (unless by-bytes
+ (setq block-size (or block-size 1024)))
+ (if (and max-depth (stringp max-depth))
+ (setq max-depth (string-to-int max-depth)))
+ ;; filesystem support means nothing under Windows
+ (if (eshell-under-windows-p)
+ (setq only-one-filesystem nil))
+ (let ((size 0.0) ange-cache)
+ (while args
+ (if only-one-filesystem
+ (setq only-one-filesystem
+ (nth 11 (eshell-file-attributes
+ (file-name-as-directory (car args))))))
+ (setq size (+ size (eshell-du-sum-directory
+ (directory-file-name (car args)) 0)))
+ (setq args (cdr args)))
+ (if grand-total
+ (eshell-print (concat (eshell-du-size-string size)
+ "total\n"))))))))
(defvar eshell-time-start nil)
(defun eshell-parse-double-quote ()
"Parse a double quoted string, which allows for variable interpolation."
(when (eq (char-after) ?\")
- (forward-char)
(let* ((end (eshell-find-delimiter ?\" ?\" nil nil t))
(eshell-current-quoted t))
(if (not end)
(throw 'eshell-incomplete ?\")
(prog1
(save-restriction
+ (forward-char)
(narrow-to-region (point) end)
(list 'eshell-escape-arg
(eshell-parse-argument)))
(interactive)
(require 'etags)
(let ((inhibit-read-only t)
- (no-default (eobp)))
- (setq tagname (find-tag-interactive "Find tag: " no-default))
+ (no-default (eobp))
+ (find-tag-default-function 'ignore))
+ (setq tagname (car (find-tag-interactive "Find tag: ")))
(find-tag tagname next-p regexp-p)))
(defun eshell-move-argument (limit func property arg)
:type 'regexp
:group 'eshell-util)
+(defcustom eshell-ange-ls-uids nil
+ "*List of user/host/id strings, used to determine remote ownership."
+ :type '(list (cons :tag "Host/User Pair"
+ (string :tag "Hostname")
+ (repeat (cons :tag "User/UID List"
+ (string :tag "Username")
+ (repeat :tag "UIDs" string)))))
+ :group 'eshell-util)
+
;;; Internal Variables:
(defvar eshell-group-names nil
(unless (fboundp 'directory-files-and-attributes)
(defun directory-files-and-attributes (dir &optional full match nosort)
(documentation 'directory-files)
- (let* ((dir (expand-file-name dir))
- (default-directory dir))
+ (let ((dir (expand-file-name dir)) ange-cache)
(mapcar
(function
(lambda (file)
- (cons file (file-attributes file))))
+ (cons file (eshell-file-attributes (expand-file-name file dir)))))
(directory-files dir full match nosort)))))
+(eval-when-compile
+ (defvar ange-cache))
+
(defun eshell-directory-files-and-attributes (dir &optional full match nosort)
"Make sure to use the handler for `directory-file-and-attributes'."
- (let ((dfh (find-file-name-handler dir 'directory-files)))
+ (let* ((dir (expand-file-name dir))
+ (dfh (find-file-name-handler dir 'directory-files)))
(if (not dfh)
(directory-files-and-attributes dir full match nosort)
- (let* ((files (funcall dfh 'directory-files dir full match nosort))
- (fah (find-file-name-handler dir 'file-attributes))
- (default-directory (expand-file-name dir)))
+ (let ((files (funcall dfh 'directory-files dir full match nosort))
+ (fah (find-file-name-handler dir 'file-attributes)))
(mapcar
(function
(lambda (file)
- (cons file (funcall fah 'file-attributes file))))
+ (cons file (if fah
+ (eshell-file-attributes
+ (expand-file-name file dir))
+ (file-attributes (expand-file-name file dir))))))
files)))))
+(defun eshell-current-ange-uids ()
+ (if (string-match "/\\([^@]+\\)@\\([^:]+\\):" default-directory)
+ (let* ((host (match-string 2 default-directory))
+ (user (match-string 1 default-directory))
+ (host-users (assoc host eshell-ange-ls-uids)))
+ (when host-users
+ (setq host-users (cdr host-users))
+ (cdr (assoc user host-users))))))
+
+;; Add an autoload for parse-time-string
+(if (and (not (fboundp 'parse-time-string))
+ (locate-library "parse-time"))
+ (autoload 'parse-time-string "parse-time"))
+
+(defun eshell-parse-ange-ls (dir)
+ (let (entry)
+ (with-temp-buffer
+ (insert (ange-ftp-ls dir "-la" nil))
+ (goto-char (point-min))
+ (if (looking-at "^total [0-9]+$")
+ (forward-line 1))
+ ;; Some systems put in a blank line here.
+ (if (eolp) (forward-line 1))
+ (while (looking-at
+ `,(concat "\\([dlscb-][rwxst-]+\\)"
+ "\\s-*" "\\([0-9]+\\)" "\\s-+"
+ "\\(\\S-+\\)" "\\s-+"
+ "\\(\\S-+\\)" "\\s-+"
+ "\\([0-9]+\\)" "\\s-+" "\\(.*\\)"))
+ (let* ((perms (match-string 1))
+ (links (string-to-number (match-string 2)))
+ (user (match-string 3))
+ (group (match-string 4))
+ (size (string-to-number (match-string 5)))
+ (mtime
+ (if (fboundp 'parse-time-string)
+ (let ((moment (parse-time-string
+ (match-string 6))))
+ (if (nth 0 moment)
+ (setcar (nthcdr 5 moment)
+ (nth 5 (decode-time (current-time))))
+ (setcar (nthcdr 0 moment) 0)
+ (setcar (nthcdr 1 moment) 0)
+ (setcar (nthcdr 2 moment) 0))
+ (apply 'encode-time moment))
+ (ange-ftp-file-modtime (expand-file-name name dir))))
+ (name (ange-ftp-parse-filename))
+ symlink)
+ (if (string-match "\\(.+\\) -> \\(.+\\)" name)
+ (setq symlink (match-string 2 name)
+ name (match-string 1 name)))
+ (setq entry
+ (cons
+ (cons name
+ (list (if (eq (aref perms 0) ?d)
+ t
+ symlink)
+ links user group
+ nil mtime nil
+ size perms nil nil)) entry)))
+ (forward-line)))
+ entry))
+
+(defun eshell-file-attributes (file)
+ "Return the attributes of FILE, playing tricks if it's over ange-ftp."
+ (let* ((file (expand-file-name file))
+ (handler (find-file-name-handler file 'file-attributes))
+ entry)
+ (if (not handler)
+ (file-attributes file)
+ (if (eq (find-file-name-handler (file-name-directory file)
+ 'directory-files)
+ 'ange-ftp-hook-function)
+ (let ((base (file-name-nondirectory file))
+ (dir (file-name-directory file)))
+ (if (boundp 'ange-cache)
+ (setq entry (cdr (assoc base (cdr (assoc dir ange-cache))))))
+ (unless entry
+ (setq entry (eshell-parse-ange-ls dir))
+ (if (boundp 'ange-cache)
+ (setq ange-cache
+ (cons (cons dir entry)
+ ange-cache)))
+ (if entry
+ (let ((fentry (assoc base (cdr entry))))
+ (if fentry
+ (setq entry (cdr fentry))
+ (setq entry nil)))))))
+ (or entry (funcall handler 'file-attributes file)))))
+
(defun eshell-copy-list (list)
"Return a copy of a list, which may be a dotted list.
The elements of the list are not copied, just the list structure itself."