# HG changeset patch # User John Wiegley # Date 971427759 0 # Node ID aab90b31807cb9b6bdc0d73b7d2ab1cdaed0fc3b # Parent 05513a882886bdcbee01a82e82fb9cb3c4b1e97b Added better remote directory support to Eshell, as well as a few bug fixes. See the ChangeLog. diff -r 05513a882886 -r aab90b31807c lisp/ChangeLog --- a/lisp/ChangeLog Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/ChangeLog Fri Oct 13 09:02:39 2000 +0000 @@ -1,3 +1,82 @@ +2000-10-13 John Wiegley + + * 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 + + * 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 + + * eshell/esh-mode.el (eshell-find-tag): Corrections to the + Eshell-friendly version of find-tag. + 2000-10-13 Miles Bader * image-file.el (image-file-name-extensions) @@ -347,6 +426,8 @@ * 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 + * 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. @@ -1362,9 +1443,6 @@ 2000-09-16 Andrew Innes - * 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. diff -r 05513a882886 -r aab90b31807c lisp/eshell/em-glob.el --- a/lisp/eshell/em-glob.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/em-glob.el Fri Oct 13 09:02:39 2000 +0000 @@ -243,7 +243,7 @@ (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))) diff -r 05513a882886 -r aab90b31807c lisp/eshell/em-ls.el --- a/lisp/eshell/em-ls.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/em-ls.el Fri Oct 13 09:02:39 2000 +0000 @@ -192,9 +192,15 @@ "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. @@ -265,7 +271,8 @@ (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." @@ -328,7 +335,7 @@ (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'" @@ -347,7 +354,7 @@ (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))) @@ -379,7 +386,7 @@ (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)))) @@ -425,16 +432,22 @@ "%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))) diff -r 05513a882886 -r aab90b31807c lisp/eshell/em-pred.el --- a/lisp/eshell/em-pred.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/em-pred.el Fri Oct 13 09:02:39 2000 +0000 @@ -464,7 +464,7 @@ (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 ?%) @@ -489,7 +489,7 @@ (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 ?-) '< @@ -518,7 +518,7 @@ (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 ?-) '< diff -r 05513a882886 -r aab90b31807c lisp/eshell/em-rebind.el --- a/lisp/eshell/em-rebind.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/em-rebind.el Fri Oct 13 09:02:39 2000 +0000 @@ -232,7 +232,7 @@ 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) diff -r 05513a882886 -r aab90b31807c lisp/eshell/em-unix.el --- a/lisp/eshell/em-unix.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/em-unix.el Fri Oct 13 09:02:39 2000 +0000 @@ -122,6 +122,12 @@ :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: @@ -296,7 +302,7 @@ "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) @@ -315,8 +321,10 @@ ((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))) @@ -339,10 +347,10 @@ (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) @@ -415,7 +423,7 @@ (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))) @@ -508,7 +516,7 @@ 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))) @@ -525,10 +533,24 @@ 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 @@ -772,61 +794,69 @@ (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) diff -r 05513a882886 -r aab90b31807c lisp/eshell/esh-arg.el --- a/lisp/eshell/esh-arg.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/esh-arg.el Fri Oct 13 09:02:39 2000 +0000 @@ -328,13 +328,13 @@ (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))) diff -r 05513a882886 -r aab90b31807c lisp/eshell/esh-mode.el --- a/lisp/eshell/esh-mode.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/esh-mode.el Fri Oct 13 09:02:39 2000 +0000 @@ -524,8 +524,9 @@ (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) diff -r 05513a882886 -r aab90b31807c lisp/eshell/esh-util.el --- a/lisp/eshell/esh-util.el Fri Oct 13 08:21:20 2000 +0000 +++ b/lisp/eshell/esh-util.el Fri Oct 13 09:02:39 2000 +0000 @@ -86,6 +86,15 @@ :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 @@ -558,28 +567,123 @@ (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."