(eshell-preinput-scroll-to-bottom): Fix spelling in docstrings.
[bpt/emacs.git] / lisp / eshell / em-unix.el
index 33a99cb..62296dd 100644 (file)
@@ -1,6 +1,8 @@
-;;; em-unix --- UNIX command aliases
+;;; em-unix.el --- UNIX command aliases
 
-;; Copyright (C) 1999, 2000 Free Software Foundation
+;; Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation
+
+;; Author: John Wiegley <johnw@gnu.org>
 
 ;; This file is part of GNU Emacs.
 
@@ -22,6 +24,7 @@
 (provide 'em-unix)
 
 (eval-when-compile (require 'esh-maint))
+(require 'eshell)
 
 (defgroup eshell-unix nil
   "This module defines many of the more common UNIX utilities as
@@ -74,7 +77,7 @@ receiving side of a command pipeline."
   :type 'boolean
   :group 'eshell-unix)
 
-(defcustom eshell-plain-locate-behavior nil
+(defcustom eshell-plain-locate-behavior (eshell-under-xemacs-p)
   "*If non-nil, standalone \"locate\" commands will behave normally.
 Standalone in this context means not redirected, and not on the
 receiving side of a command pipeline."
@@ -117,22 +120,36 @@ Otherwise, `rmdir' is required."
   :type 'boolean
   :group 'eshell-unix)
 
-(defcustom eshell-ln-overwrite-files t
+(defcustom eshell-ln-overwrite-files nil
   "*If non-nil, `ln' will overwrite files without warning."
   :type 'boolean
   :group 'eshell-unix)
 
+(defcustom eshell-default-target-is-dot nil
+  "*If non-nil, the default destination for cp, mv or ln is `.'."
+  :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 on the remote machine."
+  :type 'boolean
+  :group 'eshell-unix)
+
 (require 'esh-opt)
 
 ;;; Functions:
 
 (defun eshell-unix-initialize ()
   "Initialize the UNIX support/emulation code."
-  (make-local-hook 'eshell-post-command-hook)
   (when (eshell-using-module 'eshell-cmpl)
-    (make-local-hook 'pcomplete-try-first-hook)
     (add-hook 'pcomplete-try-first-hook
-             'eshell-complete-host-reference nil t)))
+             'eshell-complete-host-reference nil t))
+  (make-local-variable 'eshell-complex-commands)
+  (setq eshell-complex-commands
+       (append '("grep" "egrep" "fgrep" "agrep" "glimpse" "locate"
+                 "cat" "time" "cp" "mv" "make" "du" "diff")
+               eshell-complex-commands)))
 
 (defalias 'eshell/date     'current-time-string)
 (defalias 'eshell/basename 'file-name-nondirectory)
@@ -148,7 +165,10 @@ Otherwise, `rmdir' is required."
   "Invoke man, flattening the arguments appropriately."
   (funcall 'man (apply 'eshell-flatten-and-stringify args)))
 
+(put 'eshell/man 'eshell-no-numeric-conversions t)
+
 (defun eshell-remove-entries (path files &optional top-level)
+  "From PATH, remove all of the given FILES, perhaps interactively."
   (while files
     (if (string-match "\\`\\.\\.?\\'"
                      (file-name-nondirectory (car files)))
@@ -224,7 +244,7 @@ Remove (unlink) the FILE(s).")
                          (not (y-or-n-p (format "rm: delete buffer `%s'? "
                                                 entry)))))
           (eshell-funcalln 'kill-buffer entry)))
-       ((processp entry)
+       ((eshell-processp entry)
         (if verbose
             (eshell-printn (format "rm: killing process `%s'" entry)))
         (unless (or preview
@@ -257,6 +277,8 @@ Remove (unlink) the FILE(s).")
      (setq args (cdr args)))
    nil))
 
+(put 'eshell/rm 'eshell-no-numeric-conversions t)
+
 (defun eshell/mkdir (&rest args)
   "Implementation of mkdir in Lisp."
   (eshell-eval-using-options
@@ -271,6 +293,8 @@ Create the DIRECTORY(ies), if they do not already exist.")
      (setq args (cdr args)))
    nil))
 
+(put 'eshell/mkdir 'eshell-no-numeric-conversions t)
+
 (defun eshell/rmdir (&rest args)
   "Implementation of rmdir in Lisp."
   (eshell-eval-using-options
@@ -285,6 +309,8 @@ Remove the DIRECTORY(ies), if they are empty.")
      (setq args (cdr args)))
    nil))
 
+(put 'eshell/rmdir 'eshell-no-numeric-conversions t)
+
 (eval-when-compile
   (defvar no-dereference)
   (defvar preview)
@@ -294,9 +320,7 @@ Remove the DIRECTORY(ies), if they are empty.")
 
 (defun eshell-shuffle-files (command action files target func deep &rest args)
   "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,9 +339,13 @@ Remove the DIRECTORY(ies), if they are empty.")
        ((and attr-target
             (or (not (eshell-under-windows-p))
                 (eq system-type 'ms-dos))
-            (setq attr (file-attributes (car files)))
-            (= (nth 10 attr-target) (nth 10 attr))
-            (= (nth 11 attr-target) (nth 11 attr)))
+            (setq attr (eshell-file-attributes (car files)))
+            (nth 10 attr-target) (nth 10 attr)
+            ;; Use equal, not -, since the inode and the device could
+            ;; cons cells.
+            (equal (nth 10 attr-target) (nth 10 attr))
+            (nth 11 attr-target) (nth 11 attr)
+            (equal (nth 11 attr-target) (nth 11 attr)))
        (eshell-error (format "%s: `%s' and `%s' are the same file\n"
                              command (car files) target)))
        (t
@@ -339,12 +367,16 @@ Remove the DIRECTORY(ies), if they are empty.")
                (let (eshell-warn-dot-directories)
                  (if (and (not deep)
                           (eq func 'rename-file)
-                          (= (nth 11 (file-attributes
-                                      (file-name-directory
-                                       (expand-file-name source))))
-                             (nth 11 (file-attributes
-                                      (file-name-directory
-                                       (expand-file-name target))))))
+                          ;; Use equal, since the device might be a
+                          ;; cons cell.
+                          (equal (nth 11 (eshell-file-attributes
+                                          (file-name-directory
+                                           (directory-file-name
+                                            (expand-file-name source)))))
+                                 (nth 11 (eshell-file-attributes
+                                          (file-name-directory
+                                           (directory-file-name
+                                            (expand-file-name target)))))))
                      (apply 'eshell-funcalln func source target args)
                  (unless (file-directory-p target)
                    (if verbose
@@ -353,13 +385,14 @@ Remove the DIRECTORY(ies), if they are empty.")
                                 command target)))
                    (unless preview
                      (eshell-funcalln 'make-directory target)))
-                 (eshell-shuffle-files command action
-                                       (mapcar
-                                        (function
-                                         (lambda (file)
-                                           (concat source "/" file)))
-                                        (directory-files source))
-                                       target func t args)
+                 (apply 'eshell-shuffle-files
+                        command action
+                        (mapcar
+                         (function
+                          (lambda (file)
+                            (concat source "/" file)))
+                         (directory-files source))
+                        target func t args)
                  (when (eq func 'rename-file)
                    (if verbose
                        (eshell-printn
@@ -406,30 +439,35 @@ Remove the DIRECTORY(ies), if they are empty.")
            (format "tar %s %s" tar-args archive) args))))
 
 ;; this is to avoid duplicating code...
-(defmacro eshell-mvcp-template
-  (command action func query-var force-var &optional preserve)
-  `(if (and (string-match eshell-tar-regexp (car (last args)))
-           (or (> (length args) 2)
-               (and (file-directory-p (car args))
-                    (or (not no-dereference)
-                        (not (file-symlink-p (car args)))))))
-       (eshell-shorthand-tar-command ,command args)
-     (let (target)
-       (if (> (length args) 1)
-          (progn
-            (setq target (car (last args)))
-            (setcdr (last args 2) nil))
-        (setq args nil))
-       (eshell-shuffle-files
-       ,command ,action args target ,func nil
-       ,@(append
-          `((if (and (or interactive
-                         ,query-var)
-                     (not force))
-                1 (or force ,force-var)))
-          (if preserve
-              (list preserve)))))
-     nil))
+(defmacro eshell-mvcpln-template (command action func query-var
+                                         force-var &optional preserve)
+  `(let ((len (length args)))
+     (if (or (= len 0)
+            (and (= len 1) (null eshell-default-target-is-dot)))
+        (error "%s: missing destination file or directory" ,command))
+     (if (= len 1)
+        (nconc args '(".")))
+     (setq args (eshell-stringify-list (eshell-flatten-list args)))
+     (if (and ,(not (equal command "ln"))
+             (string-match eshell-tar-regexp (car (last args)))
+             (or (> (length args) 2)
+                 (and (file-directory-p (car args))
+                      (or (not no-dereference)
+                          (not (file-symlink-p (car args)))))))
+        (eshell-shorthand-tar-command ,command args)
+       (let ((target (car (last args)))
+            ange-cache)
+        (setcdr (last args 2) nil)
+        (eshell-shuffle-files
+         ,command ,action args target ,func nil
+         ,@(append
+            `((if (and (or interactive
+                           ,query-var)
+                       (not force))
+                  1 (or force ,force-var)))
+            (if preserve
+                (list preserve)))))
+       nil)))
 
 (defun eshell/mv (&rest args)
   "Implementation of mv in Lisp."
@@ -444,6 +482,7 @@ Remove the DIRECTORY(ies), if they are empty.")
      (?v "verbose" nil verbose
         "explain what is being done")
      (nil "help" nil nil "show this usage screen")
+     :preserve-args
      :external "mv"
      :show-usage
      :usage "[OPTION]... SOURCE DEST
@@ -451,9 +490,11 @@ Remove the DIRECTORY(ies), if they are empty.")
 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
 \[OPTION] DIRECTORY...")
    (let ((no-dereference t))
-     (eshell-mvcp-template "mv" "moving" 'rename-file
-                          eshell-mv-interactive-query
-                          eshell-mv-overwrite-files))))
+     (eshell-mvcpln-template "mv" "moving" 'rename-file
+                            eshell-mv-interactive-query
+                            eshell-mv-overwrite-files))))
+
+(put 'eshell/mv 'eshell-no-numeric-conversions t)
 
 (defun eshell/cp (&rest args)
   "Implementation of cp in Lisp."
@@ -476,6 +517,7 @@ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
      (?v "verbose" nil verbose
         "explain what is being done")
      (nil "help" nil nil "show this usage screen")
+     :preserve-args
      :external "cp"
      :show-usage
      :usage "[OPTION]... SOURCE DEST
@@ -483,9 +525,11 @@ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
 Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.")
    (if archive
        (setq preserve t no-dereference t recursive t))
-   (eshell-mvcp-template "cp" "copying" 'copy-file
-                        eshell-cp-interactive-query
-                        eshell-cp-overwrite-files preserve)))
+   (eshell-mvcpln-template "cp" "copying" 'copy-file
+                          eshell-cp-interactive-query
+                          eshell-cp-overwrite-files preserve)))
+
+(put 'eshell/cp 'eshell-no-numeric-conversions t)
 
 (defun eshell/ln (&rest args)
   "Implementation of ln in Lisp."
@@ -494,11 +538,13 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.")
    '((?h "help" nil nil "show this usage screen")
      (?s "symbolic" nil symbolic
         "make symbolic links instead of hard links")
-     (?i "interactive" nil interactive "request confirmation if target already exists")
+     (?i "interactive" nil interactive
+        "request confirmation if target already exists")
      (?f "force" nil force "remove existing destinations, never prompt")
      (?n "preview" nil preview
         "don't change anything on disk")
      (?v "verbose" nil verbose "explain what is being done")
+     :preserve-args
      :external "ln"
      :show-usage
      :usage "[OPTION]... TARGET [LINK_NAME]
@@ -507,27 +553,38 @@ Create a link to the specified TARGET with optional LINK_NAME.  If there is
 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)
-     (if (> (length args) 1)
-        (progn
-          (setq target (car (last args)))
-          (setcdr (last args 2) nil))
-       (setq args nil))
-     (eshell-shuffle-files "ln" "linking" args target
-                          (if symbolic
-                              'make-symbolic-link
-                            'add-name-to-file) nil
-                          (if (and (or interactive
-                                       eshell-ln-interactive-query)
-                                   (not force))
-                              1 (or force eshell-ln-overwrite-files))))
-   nil))
+   (let ((no-dereference t))
+     (eshell-mvcpln-template "ln" "linking"
+                            (if symbolic
+                                'make-symbolic-link
+                              'add-name-to-file)
+                            eshell-ln-interactive-query
+                            eshell-ln-overwrite-files))))
+
+(put 'eshell/ln 'eshell-no-numeric-conversions t)
 
 (defun eshell/cat (&rest args)
-  "Implementation of cat in Lisp."
-  (if eshell-in-pipeline-p
-      (throw 'eshell-replace-command
-            (eshell-parse-command "*cat" 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-stringify-list (eshell-flatten-list args)))
+  (if (or eshell-in-pipeline-p
+         (catch 'special
+           (eshell-for arg args
+             (unless (or (and (stringp arg)
+                              (> (length arg) 0)
+                              (eq (aref arg 0) ?-))
+                         (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
@@ -556,6 +613,8 @@ Concatenate FILE(s), or standard input, to standard output.")
      ;; if the file does not end in a newline, do not emit one
      (setq eshell-ensure-newline-p nil))))
 
+(put 'eshell/cat 'eshell-no-numeric-conversions t)
+
 ;; special front-end functions for compilation-mode buffers
 
 (defun eshell/make (&rest args)
@@ -568,7 +627,10 @@ Concatenate FILE(s), or standard input, to standard output.")
                         (list 'quote (eshell-copy-environment))))))
        (compile (concat "make " (eshell-flatten-and-stringify args))))
     (throw 'eshell-replace-command
-          (eshell-parse-command "*make" args))))
+          (eshell-parse-command "*make" (eshell-stringify-list
+                                         (eshell-flatten-list args))))))
+
+(put 'eshell/make 'eshell-no-numeric-conversions t)
 
 (defun eshell-occur-mode-goto-occurrence ()
   "Go to the occurrence the current line describes."
@@ -580,13 +642,12 @@ Concatenate FILE(s), or standard input, to standard output.")
 (defun eshell-occur-mode-mouse-goto (event)
   "In Occur mode, go to the occurrence whose line you click on."
   (interactive "e")
-  (let (buffer pos)
+  (let (pos)
     (save-excursion
       (set-buffer (window-buffer (posn-window (event-end event))))
       (save-excursion
        (goto-char (posn-point (event-end event)))
-       (setq pos (occur-mode-find-occurrence))
-       (setq buffer occur-buffer)))
+       (setq pos (occur-mode-find-occurrence))))
     (pop-to-buffer (marker-buffer pos))
     (goto-char (marker-position pos))))
 
@@ -602,7 +663,8 @@ available..."
              (default-directory default-dir))
          (erase-buffer)
          (occur-mode)
-         (let ((files (eshell-flatten-list (cdr args)))
+         (let ((files (eshell-stringify-list
+                       (eshell-flatten-list (cdr args))))
                (inhibit-redisplay t)
                string)
            (when (car args)
@@ -621,7 +683,6 @@ available..."
                (if string (insert string))
                (setq string nil
                      files (cdr files)))))
-         (setq occur-buffer (current-buffer))
          (local-set-key [mouse-2] 'eshell-occur-mode-mouse-goto)
          (local-set-key [(control ?c) (control ?c)]
                         'eshell-occur-mode-goto-occurrence)
@@ -644,14 +705,13 @@ external command."
                      (not eshell-in-pipeline-p)
                      (not eshell-in-subcommand-p))))
        (throw 'eshell-replace-command
-              (eshell-parse-command (concat "*" command) args))
-      (let* ((compilation-process-setup-function
-             (list 'lambda nil
-                   (list 'setq 'process-environment
-                         (list 'quote (eshell-copy-environment)))))
-            (args (mapconcat 'identity
+              (eshell-parse-command (concat "*" command)
+                                    (eshell-stringify-list
+                                     (eshell-flatten-list args))))
+      (let* ((args (mapconcat 'identity
                              (mapcar 'shell-quote-argument
-                                     (eshell-flatten-list args))
+                                     (eshell-stringify-list
+                                      (eshell-flatten-list args)))
                              " "))
             (cmd (progn
                    (set-text-properties 0 (length args)
@@ -739,14 +799,14 @@ external command."
        (size 0.0))
     (while entries
       (unless (string-match "\\`\\.\\.?\\'" (caar entries))
-       (let* ((entry (concat path (char-to-string directory-sep-char)
+       (let* ((entry (concat path "/"
                              (caar entries)))
               (symlink (and (stringp (cadr (car entries)))
                             (cadr (car entries)))))
          (unless (or (and symlink (not dereference-links))
                      (and only-one-filesystem
-                          (not (= only-one-filesystem
-                                  (nth 12 (car entries))))))
+                          (/= only-one-filesystem
+                              (nth 12 (car entries)))))
            (if symlink
                (setq entry symlink))
            (setq size
@@ -769,62 +829,70 @@ external command."
     size))
 
 (defun eshell/du (&rest args)
-  "Implementation of \"du\" in Lisp, passing RAGS."
-  (if (eshell-search-path "du")
-      (throw 'eshell-replace-command
-            (eshell-parse-command "*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...
+  "Implementation of \"du\" in Lisp, passing ARGS."
+  (setq args (if args
+                (eshell-stringify-list (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-number 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)
 
@@ -875,32 +943,46 @@ Show wall-clock time elapsed during execution of COMMAND.")
 
 (defun eshell/diff (&rest args)
   "Alias \"diff\" to call Emacs `diff' function."
-  (if (or eshell-plain-diff-behavior
-         (not (and (eshell-interactive-output-p)
-                   (not eshell-in-pipeline-p)
-                   (not eshell-in-subcommand-p))))
-      (throw 'eshell-replace-command
-            (eshell-parse-command "*diff" args))
-    (setq args (eshell-flatten-list args))
-    (if (< (length args) 2)
-       (error "diff: missing operand"))
-    (let ((old (car (last args 2)))
-         (new (car (last args)))
-         (config (current-window-configuration)))
-      (if (= (length args) 2)
-         (setq args nil)
-       (setcdr (last args 3) nil))
-      (with-current-buffer
-         (diff old new (eshell-flatten-and-stringify args))
-       (when (fboundp 'diff-mode)
-         (diff-mode)
-         (set (make-local-variable 'eshell-diff-window-config) config)
-         (local-set-key [?q] 'eshell-diff-quit)
-         (if (fboundp 'turn-on-font-lock-if-enabled)
-             (turn-on-font-lock-if-enabled))))
-      (other-window 1)
-      (goto-char (point-min))
-      nil)))
+  (let ((orig-args (eshell-stringify-list (eshell-flatten-list args))))
+    (if (or eshell-plain-diff-behavior
+           (not (and (eshell-interactive-output-p)
+                     (not eshell-in-pipeline-p)
+                     (not eshell-in-subcommand-p))))
+       (throw 'eshell-replace-command
+              (eshell-parse-command "*diff" orig-args))
+      (setq args (copy-sequence orig-args))
+      (if (< (length args) 2)
+         (throw 'eshell-replace-command
+                (eshell-parse-command "*diff" orig-args)))
+      (let ((old (car (last args 2)))
+           (new (car (last args)))
+           (config (current-window-configuration)))
+       (if (= (length args) 2)
+           (setq args nil)
+         (setcdr (last args 3) nil))
+       (with-current-buffer
+           (condition-case err
+               (diff old new (eshell-flatten-and-stringify args))
+             (error
+              (throw 'eshell-replace-command
+                     (eshell-parse-command "*diff" orig-args))))
+         (when (fboundp 'diff-mode)
+           (make-local-variable 'compilation-finish-functions)
+           (add-hook
+            'compilation-finish-functions
+            `(lambda (buff msg)
+               (with-current-buffer buff
+                 (diff-mode)
+                 (set (make-local-variable 'eshell-diff-window-config)
+                      ,config)
+                 (local-set-key [?q] 'eshell-diff-quit)
+                 (if (fboundp 'turn-on-font-lock-if-enabled)
+                     (turn-on-font-lock-if-enabled))
+                 (goto-char (point-min))))))
+         (pop-to-buffer (current-buffer))))))
+  nil)
+
+(put 'eshell/diff 'eshell-no-numeric-conversions t)
 
 (defun eshell/locate (&rest args)
   "Alias \"locate\" to call Emacs `locate' function."
@@ -911,18 +993,24 @@ Show wall-clock time elapsed during execution of COMMAND.")
          (and (stringp (car args))
               (string-match "^-" (car args))))
       (throw 'eshell-replace-command
-            (eshell-parse-command "*locate" args))
+            (eshell-parse-command "*locate" (eshell-stringify-list
+                                             (eshell-flatten-list args))))
     (save-selected-window
       (let ((locate-history-list (list (car args))))
        (locate-with-filter (car args) (cadr args))))))
 
+(put 'eshell/locate 'eshell-no-numeric-conversions t)
+
 (defun eshell/occur (&rest args)
   "Alias \"occur\" to call Emacs `occur' function."
   (let ((inhibit-read-only t))
-    (if args
-       (error "usage: occur: (REGEXP)")
-      (occur (car args)))))
+    (if (> (length args) 2)
+       (error "usage: occur: (REGEXP &optional NLINES)")
+      (apply 'occur args))))
+
+(put 'eshell/occur 'eshell-no-numeric-conversions t)
 
 ;;; Code:
 
+;;; arch-tag: 2462edd2-a76a-4cf2-897d-92e9a82ac1c9
 ;;; em-unix.el ends here