]> code.delx.au - gnu-emacs/blobdiff - lisp/eshell/em-unix.el
Merge from emacs-23; up to 2010-06-12T17:12:15Z!cyd@stupidchicken.com.
[gnu-emacs] / lisp / eshell / em-unix.el
index 10e767745beec0814a626876733ce0f3bf1b12da..707f2ebc2ced0652c2840b535cf2ea6a3e6bf682 100644 (file)
@@ -1,7 +1,6 @@
 ;;; em-unix.el --- UNIX command aliases
 
-;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-;;   2008  Free Software Foundation, Inc.
+;; Copyright (C) 1999-2011  Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 
@@ -37,6 +36,8 @@
 ;;; Code:
 
 (require 'eshell)
+(require 'esh-opt)
+(require 'pcomplete)
 
 ;;;###autoload
 (eshell-defgroup eshell-unix nil
@@ -52,85 +53,86 @@ by name)."
   :tag "UNIX commands in Lisp"
   :group 'eshell-module)
 
-(defcustom eshell-unix-load-hook '(eshell-unix-initialize)
-  "*A list of functions to run when `eshell-unix' is loaded."
+(defcustom eshell-unix-load-hook nil
+  "A list of functions to run when `eshell-unix' is loaded."
+  :version "24.1"                      ; removed eshell-unix-initialize
   :type 'hook
   :group 'eshell-unix)
 
 (defcustom eshell-plain-grep-behavior nil
-  "*If non-nil, standalone \"grep\" commands will behave normally.
+  "If non-nil, standalone \"grep\" commands will behave normally.
 Standalone in this context means not redirected, and not on the
 receiving side of a command pipeline."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-no-grep-available (not (eshell-search-path "grep"))
-  "*If non-nil, no grep is available on the current machine."
+  "If non-nil, no grep is available on the current machine."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-plain-diff-behavior nil
-  "*If non-nil, standalone \"diff\" commands will behave normally.
+  "If non-nil, standalone \"diff\" commands will behave normally.
 Standalone in this context means not redirected, and not on the
 receiving side of a command pipeline."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-plain-locate-behavior (featurep 'xemacs)
-  "*If non-nil, standalone \"locate\" commands will behave normally.
+  "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."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-rm-removes-directories nil
-  "*If non-nil, `rm' will remove directory entries.
+  "If non-nil, `rm' will remove directory entries.
 Otherwise, `rmdir' is required."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-rm-interactive-query (= (user-uid) 0)
-  "*If non-nil, `rm' will query before removing anything."
+  "If non-nil, `rm' will query before removing anything."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-mv-interactive-query (= (user-uid) 0)
-  "*If non-nil, `mv' will query before overwriting anything."
+  "If non-nil, `mv' will query before overwriting anything."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-mv-overwrite-files t
-  "*If non-nil, `mv' will overwrite files without warning."
+  "If non-nil, `mv' will overwrite files without warning."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-cp-interactive-query (= (user-uid) 0)
-  "*If non-nil, `cp' will query before overwriting anything."
+  "If non-nil, `cp' will query before overwriting anything."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-cp-overwrite-files t
-  "*If non-nil, `cp' will overwrite files without warning."
+  "If non-nil, `cp' will overwrite files without warning."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-ln-interactive-query (= (user-uid) 0)
-  "*If non-nil, `ln' will query before overwriting anything."
+  "If non-nil, `ln' will query before overwriting anything."
   :type 'boolean
   :group 'eshell-unix)
 
 (defcustom eshell-ln-overwrite-files nil
-  "*If non-nil, `ln' will overwrite files without warning."
+  "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 `.'."
+  "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.
+  "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)
@@ -145,17 +147,17 @@ Otherwise, Emacs will attempt to use rsh to invoke du on the remote machine."
   (make-local-variable 'eshell-complex-commands)
   (setq eshell-complex-commands
        (append '("grep" "egrep" "fgrep" "agrep" "glimpse" "locate"
-                 "cat" "time" "cp" "mv" "make" "du" "diff")
+                 "cat" "time" "cp" "mv" "make" "du" "diff" "su" "sudo")
                eshell-complex-commands)))
 
 (defalias 'eshell/date     'current-time-string)
 (defalias 'eshell/basename 'file-name-nondirectory)
 (defalias 'eshell/dirname  'file-name-directory)
 
-(defvar interactive)
-(defvar preview)
-(defvar recursive)
-(defvar verbose)
+(defvar em-interactive)
+(defvar em-preview)
+(defvar em-recursive)
+(defvar em-verbose)
 
 (defun eshell/man (&rest args)
   "Invoke man, flattening the arguments appropriately."
@@ -201,32 +203,26 @@ Otherwise, Emacs will attempt to use rsh to invoke du on the remote machine."
            (eshell-error "rm: cannot remove `.' or `..'\n"))
       (if (and (file-directory-p (car files))
               (not (file-symlink-p (car files))))
-         (let ((dir (file-name-as-directory (car files))))
-           (eshell-remove-entries dir
-                                  (mapcar
-                                   (function
-                                    (lambda (file)
-                                      (concat dir file)))
-                                   (directory-files dir)))
-           (if verbose
+         (progn
+           (if em-verbose
                (eshell-printn (format "rm: removing directory `%s'"
                                       (car files))))
            (unless
-               (or preview
-                   (and interactive
+               (or em-preview
+                   (and em-interactive
                         (not (y-or-n-p
                               (format "rm: remove directory `%s'? "
                                       (car files))))))
-             (eshell-funcalln 'delete-directory (car files))))
-       (if verbose
+             (eshell-funcalln 'delete-directory (car files) t t)))
+       (if em-verbose
            (eshell-printn (format "rm: removing file `%s'"
                                   (car files))))
-       (unless (or preview
-                   (and interactive
+       (unless (or em-preview
+                   (and em-interactive
                         (not (y-or-n-p
                               (format "rm: remove `%s'? "
                                       (car files))))))
-         (eshell-funcalln 'delete-file (car files)))))
+         (eshell-funcalln 'delete-file (car files) t))))
     (setq files (cdr files))))
 
 (defun eshell/rm (&rest args)
@@ -239,21 +235,21 @@ argument."
    "rm" args
    '((?h "help" nil nil "show this usage screen")
      (?f "force" nil force-removal "force removal")
-     (?i "interactive" nil interactive "prompt before any removal")
-     (?n "preview" nil preview "don't change anything on disk")
-     (?r "recursive" nil recursive
+     (?i "interactive" nil em-interactive "prompt before any removal")
+     (?n "preview" nil em-preview "don't change anything on disk")
+     (?r "recursive" nil em-recursive
         "remove the contents of directories recursively")
-     (?R nil nil recursive "(same)")
-     (?v "verbose" nil verbose "explain what is being done")
+     (?R nil nil em-recursive "(same)")
+     (?v "verbose" nil em-verbose "explain what is being done")
      :preserve-args
      :external "rm"
      :show-usage
      :usage "[OPTION]... FILE...
 Remove (unlink) the FILE(s).")
-   (unless interactive
-     (setq interactive eshell-rm-interactive-query))
-   (if (and force-removal interactive)
-       (setq interactive nil))
+   (unless em-interactive
+     (setq em-interactive eshell-rm-interactive-query))
+   (if (and force-removal em-interactive)
+       (setq em-interactive nil))
    (while args
      (let ((entry (if (stringp (car args))
                      (directory-file-name (car args))
@@ -262,37 +258,37 @@ Remove (unlink) the FILE(s).")
                      (car args)))))
        (cond
        ((bufferp entry)
-        (if verbose
+        (if em-verbose
             (eshell-printn (format "rm: removing buffer `%s'" entry)))
-        (unless (or preview
-                    (and interactive
+        (unless (or em-preview
+                    (and em-interactive
                          (not (y-or-n-p (format "rm: delete buffer `%s'? "
                                                 entry)))))
           (eshell-funcalln 'kill-buffer entry)))
        ((eshell-processp entry)
-        (if verbose
+        (if em-verbose
             (eshell-printn (format "rm: killing process `%s'" entry)))
-        (unless (or preview
-                    (and interactive
+        (unless (or em-preview
+                    (and em-interactive
                          (not (y-or-n-p (format "rm: kill process `%s'? "
                                                 entry)))))
           (eshell-funcalln 'kill-process entry)))
        ((symbolp entry)
-        (if verbose
+        (if em-verbose
             (eshell-printn (format "rm: uninterning symbol `%s'" entry)))
         (unless
-            (or preview
-                (and interactive
+            (or em-preview
+                (and em-interactive
                      (not (y-or-n-p (format "rm: unintern symbol `%s'? "
                                             entry)))))
           (eshell-funcalln 'unintern entry)))
        ((stringp entry)
         (if (and (file-directory-p entry)
                  (not (file-symlink-p entry)))
-            (if (or recursive
+            (if (or em-recursive
                     eshell-rm-removes-directories)
-                (if (or preview
-                        (not interactive)
+                (if (or em-preview
+                        (not em-interactive)
                         (y-or-n-p
                          (format "rm: descend into directory `%s'? "
                                  entry)))
@@ -337,8 +333,6 @@ Remove the DIRECTORY(ies), if they are empty.")
 (put 'eshell/rmdir 'eshell-no-numeric-conversions t)
 
 (defvar no-dereference)
-(defvar preview)
-(defvar verbose)
 
 (defvar eshell-warn-dot-directories t)
 
@@ -346,9 +340,9 @@ Remove the DIRECTORY(ies), if they are empty.")
   "Shuffle around some filesystem entries, using FUNC to do the work."
   (let ((attr-target (eshell-file-attributes target))
        (is-dir (or (file-directory-p target)
-                   (and preview (not eshell-warn-dot-directories))))
+                   (and em-preview (not eshell-warn-dot-directories))))
        attr)
-    (if (and (not preview) (not is-dir)
+    (if (and (not em-preview) (not is-dir)
             (> (length files) 1))
        (error "%s: when %s multiple files, last argument must be a directory"
               command action))
@@ -385,7 +379,7 @@ Remove the DIRECTORY(ies), if they are empty.")
                   (not (memq func '(make-symbolic-link
                                     add-name-to-file))))
              (if (and (eq func 'copy-file)
-                      (not recursive))
+                      (not em-recursive))
                  (eshell-error (format "%s: %s: omitting directory\n"
                                        command (car files)))
                (let (eshell-warn-dot-directories)
@@ -403,11 +397,11 @@ Remove the DIRECTORY(ies), if they are empty.")
                                             (expand-file-name target)))))))
                      (apply 'eshell-funcalln func source target args)
                  (unless (file-directory-p target)
-                   (if verbose
+                   (if em-verbose
                        (eshell-printn
                         (format "%s: making directory %s"
                                 command target)))
-                   (unless preview
+                   (unless em-preview
                      (eshell-funcalln 'make-directory target)))
                  (apply 'eshell-shuffle-files
                         command action
@@ -418,16 +412,16 @@ Remove the DIRECTORY(ies), if they are empty.")
                          (directory-files source))
                         target func t args)
                  (when (eq func 'rename-file)
-                   (if verbose
+                   (if em-verbose
                        (eshell-printn
                         (format "%s: deleting directory %s"
                                 command source)))
-                   (unless preview
+                   (unless em-preview
                      (eshell-funcalln 'delete-directory source))))))
-           (if verbose
+           (if em-verbose
                (eshell-printn (format "%s: %s -> %s" command
                                       source target)))
-           (unless preview
+           (unless em-preview
              (if (and no-dereference
                       (setq link (file-symlink-p source)))
                  (progn
@@ -452,7 +446,7 @@ Remove the DIRECTORY(ies), if they are empty.")
     (if (file-exists-p archive)
        (setq tar-args (concat "u" tar-args))
       (setq tar-args (concat "c" tar-args)))
-    (if verbose
+    (if em-verbose
        (setq tar-args (concat "v" tar-args)))
     (if (equal command "mv")
        (setq tar-args (concat "--remove-files -" tar-args)))
@@ -485,7 +479,7 @@ Remove the DIRECTORY(ies), if they are empty.")
         (eshell-shuffle-files
          ,command ,action args target ,func nil
          ,@(append
-            `((if (and (or interactive
+            `((if (and (or em-interactive
                            ,query-var)
                        (not force))
                   1 (or force ,force-var)))
@@ -499,11 +493,11 @@ Remove the DIRECTORY(ies), if they are empty.")
    "mv" args
    '((?f "force" nil force
         "remove existing destinations, never prompt")
-     (?i "interactive" nil interactive
+     (?i "interactive" nil em-interactive
         "request confirmation if target already exists")
-     (?n "preview" nil preview
+     (?n "preview" nil em-preview
         "don't change anything on disk")
-     (?v "verbose" nil verbose
+     (?v "verbose" nil em-verbose
         "explain what is being done")
      (nil "help" nil nil "show this usage screen")
      :preserve-args
@@ -530,15 +524,15 @@ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
         "preserve links")
      (?f "force" nil force
         "remove existing destinations, never prompt")
-     (?i "interactive" nil interactive
+     (?i "interactive" nil em-interactive
         "request confirmation if target already exists")
-     (?n "preview" nil preview
+     (?n "preview" nil em-preview
         "don't change anything on disk")
      (?p "preserve" nil preserve
         "preserve file attributes if possible")
-     (?R "recursive" nil recursive
+     (?R "recursive" nil em-recursive
         "copy directories recursively")
-     (?v "verbose" nil verbose
+     (?v "verbose" nil em-verbose
         "explain what is being done")
      (nil "help" nil nil "show this usage screen")
      :preserve-args
@@ -548,7 +542,7 @@ Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
    or:  cp [OPTION]... SOURCE... DIRECTORY
 Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.")
    (if archive
-       (setq preserve t no-dereference t recursive t))
+       (setq preserve t no-dereference t em-recursive t))
    (eshell-mvcpln-template "cp" "copying" 'copy-file
                           eshell-cp-interactive-query
                           eshell-cp-overwrite-files preserve)))
@@ -562,12 +556,12 @@ 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
+     (?i "interactive" nil em-interactive
         "request confirmation if target already exists")
      (?f "force" nil force "remove existing destinations, never prompt")
-     (?n "preview" nil preview
+     (?n "preview" nil em-preview
         "don't change anything on disk")
-     (?v "verbose" nil verbose "explain what is being done")
+     (?v "verbose" nil em-verbose "explain what is being done")
      :preserve-args
      :external "ln"
      :show-usage
@@ -594,7 +588,7 @@ 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
+           (dolist (arg args)
              (unless (or (and (stringp arg)
                               (> (length arg) 0)
                               (eq (aref arg 0) ?-))
@@ -617,12 +611,12 @@ symlink, then revert to the system's definition of cat."
        :show-usage
        :usage "[OPTION] FILE...
 Concatenate FILE(s), or standard input, to standard output.")
-     (eshell-for file args
+     (dolist (file args)
        (if (string= file "-")
           (throw 'eshell-external
                  (eshell-external-command "cat" args))))
      (let ((curbuf (current-buffer)))
-       (eshell-for file args
+       (dolist (file args)
         (with-temp-buffer
           (insert-file-contents file)
           (goto-char (point-min))
@@ -667,8 +661,7 @@ Concatenate FILE(s), or standard input, to standard output.")
   "In Occur mode, go to the occurrence whose line you click on."
   (interactive "e")
   (let (pos)
-    (save-excursion
-      (set-buffer (window-buffer (posn-window (event-end event))))
+    (with-current-buffer (window-buffer (posn-window (event-end event)))
       (save-excursion
        (goto-char (posn-point (event-end event)))
        (setq pos (occur-mode-find-occurrence))))
@@ -859,10 +852,9 @@ external command."
   (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)
+                   (dolist (arg args)
+                     (if (string-equal
+                          (file-remote-p (expand-file-name arg) 'method) "ftp")
                          (throw 'have-ange-path t))))))
        (throw 'eshell-replace-command
               (eshell-parse-command ext-du args))
@@ -920,9 +912,7 @@ Summarize disk usage of each FILE, recursively for directories.")
 (defvar eshell-time-start nil)
 
 (defun eshell-show-elapsed-time ()
-  (let ((elapsed (format "%.3f secs\n"
-                        (- (eshell-time-to-seconds (current-time))
-                           eshell-time-start))))
+  (let ((elapsed (format "%.3f secs\n" (- (float-time) eshell-time-start))))
     (set-text-properties 0 (length elapsed) '(face bold) elapsed)
     (eshell-interactive-print elapsed))
   (remove-hook 'eshell-post-command-hook 'eshell-show-elapsed-time t))
@@ -948,7 +938,7 @@ Summarize disk usage of each FILE, recursively for directories.")
        :show-usage
        :usage "COMMAND...
 Show wall-clock time elapsed during execution of COMMAND.")
-     (setq eshell-time-start (eshell-time-to-seconds (current-time)))
+     (setq eshell-time-start (float-time))
      (add-hook 'eshell-post-command-hook 'eshell-show-elapsed-time nil t)
      ;; after setting
      (throw 'eshell-replace-command
@@ -957,7 +947,9 @@ Show wall-clock time elapsed during execution of COMMAND.")
                                  (eshell-stringify-list
                                   (eshell-flatten-list (cdr time-args))))))))
 
-(defalias 'eshell/whoami 'user-login-name)
+(defun eshell/whoami (&rest args)
+  "Make \"whoami\" Tramp aware."
+  (or (file-remote-p default-directory 'user) (user-login-name)))
 
 (defvar eshell-diff-window-config nil)
 
@@ -1043,11 +1035,86 @@ Show wall-clock time elapsed during execution of COMMAND.")
 
 (put 'eshell/occur 'eshell-no-numeric-conversions t)
 
+;; Pacify the byte-compiler.
+(defvar tramp-default-proxies-alist)
+
+(defun eshell/su (&rest args)
+  "Alias \"su\" to call Tramp."
+  (require 'tramp)
+  (setq args (eshell-stringify-list (eshell-flatten-list args)))
+  (let ((orig-args (copy-tree args)))
+    (eshell-eval-using-options
+     "su" args
+     '((?h "help" nil nil "show this usage screen")
+       (?l "login" nil login "provide a login environment")
+       (?  nil nil login "provide a login environment")
+       :usage "[- | -l | --login] [USER]
+Become another USER during a login session.")
+     (throw 'eshell-replace-command
+           (let ((user "root")
+                 (host (or (file-remote-p default-directory 'host)
+                           "localhost"))
+                 (dir (or (file-remote-p default-directory 'localname)
+                          (expand-file-name default-directory))))
+             (dolist (arg args)
+               (if (string-equal arg "-") (setq login t) (setq user arg)))
+             ;; `eshell-eval-using-options' does not handle "-".
+             (if (member "-" orig-args) (setq login t))
+             (if login (setq dir "~/"))
+             (if (and (file-remote-p default-directory)
+                      (or
+                       (not (string-equal
+                             "su" (file-remote-p default-directory 'method)))
+                       (not (string-equal
+                             user (file-remote-p default-directory 'user)))))
+                 (add-to-list
+                  'tramp-default-proxies-alist
+                  (list host user (file-remote-p default-directory))))
+             (eshell-parse-command
+              "cd" (list (format "/su:%s@%s:%s" user host dir))))))))
+
+(put 'eshell/su 'eshell-no-numeric-conversions t)
+
+(defun eshell/sudo (&rest args)
+  "Alias \"sudo\" to call Tramp."
+  (require 'tramp)
+  (setq args (eshell-stringify-list (eshell-flatten-list args)))
+  (let ((orig-args (copy-tree args)))
+    (eshell-eval-using-options
+     "sudo" args
+     '((?h "help" nil nil "show this usage screen")
+       (?u "user" t user "execute a command as another USER")
+       :show-usage
+       :usage "[(-u | --user) USER] COMMAND
+Execute a COMMAND as the superuser or another USER.")
+     (throw 'eshell-external
+           (let ((user (or user "root"))
+                 (host (or (file-remote-p default-directory 'host)
+                           "localhost"))
+                 (dir (or (file-remote-p default-directory 'localname)
+                          (expand-file-name default-directory))))
+             ;; `eshell-eval-using-options' reads options of COMMAND.
+             (while (and (stringp (car orig-args))
+                         (member (car orig-args) '("-u" "--user")))
+               (setq orig-args (cddr orig-args)))
+             (if (and (file-remote-p default-directory)
+                      (or
+                       (not (string-equal
+                             "sudo" (file-remote-p default-directory 'method)))
+                       (not (string-equal
+                             user (file-remote-p default-directory 'user)))))
+                 (add-to-list
+                  'tramp-default-proxies-alist
+                  (list host user (file-remote-p default-directory))))
+             (let ((default-directory (format "/sudo:%s@%s:%s" user host dir)))
+               (eshell-named-command (car orig-args) (cdr orig-args))))))))
+
+(put 'eshell/sudo 'eshell-no-numeric-conversions t)
+
 (provide 'em-unix)
 
 ;; Local Variables:
 ;; generated-autoload-file: "esh-groups.el"
 ;; End:
 
-;; arch-tag: 2462edd2-a76a-4cf2-897d-92e9a82ac1c9
 ;;; em-unix.el ends here