* eshell/esh-ext.el (eshell-external-command): Pass args to
[bpt/emacs.git] / lisp / eshell / esh-ext.el
index 9f77f1e..474e536 100644 (file)
@@ -1,16 +1,15 @@
 ;;; esh-ext.el --- commands external to Eshell
 
-;; Copyright (C) 1999, 2000, 2002, 2003, 2004,
-;;   2005, 2006 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2013 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
-
-(provide 'esh-ext)
-
-(eval-when-compile (require 'esh-maint))
-(require 'esh-util)
-
-(defgroup eshell-ext nil
-  "External commands are invoked when operating system executables are
-loaded into memory, thus beginning a new process."
-  :tag "External commands"
-  :group 'eshell)
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -43,20 +29,39 @@ loaded into memory, thus beginning a new process."
 ;;   /bin/grep   ; will definitely invoke /bin/grep
 ;;   *grep        ; will also invoke /bin/grep
 
+;;; Code:
+
+(provide 'esh-ext)
+
+(require 'esh-util)
+
+(eval-when-compile
+  (require 'cl-lib)
+  (require 'esh-io)
+  (require 'esh-cmd))
+(require 'esh-opt)
+
+(defgroup eshell-ext nil
+  "External commands are invoked when operating system executables are
+loaded into memory, thus beginning a new process."
+  :tag "External commands"
+  :group 'eshell)
+
 ;;; User Variables:
 
-(defcustom eshell-ext-load-hook '(eshell-ext-initialize)
-  "*A hook that gets run when `eshell-ext' is loaded."
+(defcustom eshell-ext-load-hook nil
+  "A hook that gets run when `eshell-ext' is loaded."
+  :version "24.1"                      ; removed eshell-ext-initialize
   :type 'hook
   :group 'eshell-ext)
 
 (defcustom eshell-binary-suffixes exec-suffixes
-  "*A list of suffixes used when searching for executable files."
+  "A list of suffixes used when searching for executable files."
   :type '(repeat string)
   :group 'eshell-ext)
 
 (defcustom eshell-force-execution nil
-  "*If non-nil, try to execute binary files regardless of permissions.
+  "If non-nil, try to execute binary files regardless of permissions.
 This can be useful on systems like Windows, where the operating system
 doesn't happen to honor the permission bits in certain cases; or in
 cases where you want to associate an interpreter with a particular
@@ -71,7 +76,7 @@ since nothing else but Eshell will be able to understand
   "Search the environment path for NAME."
   (if (file-name-absolute-p name)
       name
-    (let ((list (parse-colon-path (getenv "PATH")))
+    (let ((list (eshell-parse-colon-path eshell-path-env))
          suffixes n1 n2 file)
       (while list
        (setq n1 (concat (car list) name))
@@ -89,36 +94,41 @@ since nothing else but Eshell will be able to understand
 
 (defcustom eshell-windows-shell-file
   (if (eshell-under-windows-p)
-      (if (string-match "\\(\\`cmdproxy\\|sh\\)\\.\\(com\\|exe\\)"
+      (if (string-match "\\(cmdproxy\\|sh\\)\\.\\(com\\|exe\\)"
                        shell-file-name)
          (or (eshell-search-path "cmd.exe")
              (eshell-search-path "command.com"))
        shell-file-name))
-  "*The name of the shell command to use for DOS/Windows batch files.
+  "The name of the shell command to use for DOS/Windows batch files.
 This defaults to nil on non-Windows systems, where this variable is
 wholly ignored."
   :type '(choice file (const nil))
   :group 'eshell-ext)
 
+(autoload 'eshell-parse-command "esh-cmd")
+
 (defsubst eshell-invoke-batch-file (&rest args)
   "Invoke a .BAT or .CMD file on DOS/Windows systems."
   ;; since CMD.EXE can't handle forward slashes in the initial
   ;; argument...
   (setcar args (subst-char-in-string ?/ ?\\ (car args)))
   (throw 'eshell-replace-command
-        (eshell-parse-command eshell-windows-shell-file (cons "/c" args))))
+        (eshell-parse-command
+         (eshell-quote-argument eshell-windows-shell-file)
+         (cons "/c" args))))
 
 (defcustom eshell-interpreter-alist
   (if (eshell-under-windows-p)
       '(("\\.\\(bat\\|cmd\\)\\'" . eshell-invoke-batch-file)))
-  "*An alist defining interpreter substitutions.
+  "An alist defining interpreter substitutions.
 Each member is a cons cell of the form:
 
   (MATCH . INTERPRETER)
 
-MATCH should be a regexp, which is matched against the command name,
-or a function.  If either returns a non-nil value, then INTERPRETER
-will be used for that command.
+MATCH should be a regexp, which is matched against the command
+name, or a function of arity 2 receiving the COMMAND and its
+ARGS (a list).  If either returns a non-nil value, then
+INTERPRETER will be used for that command.
 
 If INTERPRETER is a string, it will be called as the command name,
 with the original command name passed as the first argument, with all
@@ -132,7 +142,7 @@ possible return values of `eshell-external-command', which see."
   :group 'eshell-ext)
 
 (defcustom eshell-alternate-command-hook nil
-  "*A hook run whenever external command lookup fails.
+  "A hook run whenever external command lookup fails.
 If a functions wishes to provide an alternate command, they must throw
 it using the tag `eshell-replace-command'.  This is done because the
 substituted command need not be external at all, and therefore must be
@@ -145,12 +155,12 @@ by the user on the command line."
   :group 'eshell-ext)
 
 (defcustom eshell-command-interpreter-max-length 256
-  "*The maximum length of any command interpreter string, plus args."
+  "The maximum length of any command interpreter string, plus args."
   :type 'integer
   :group 'eshell-ext)
 
 (defcustom eshell-explicit-command-char ?*
-  "*If this char occurs before a command name, call it externally.
+  "If this char occurs before a command name, call it externally.
 That is, although `vi' may be an alias, `\vi' will always call the
 external version."
   :type 'character
@@ -174,7 +184,9 @@ This bypasses all Lisp functions and aliases."
        (error "%s: external command not found"
               (substring command 1))))))
 
-(defun eshell-remote-command (handler command args)
+(autoload 'eshell-close-handles "esh-io")
+
+(defun eshell-remote-command (command args)
   "Insert output from a remote COMMAND, using ARGS.
 A remote command is something that executes on a different machine.
 An external command simply means external to Emacs.
@@ -184,18 +196,17 @@ all the output from the remote command, and sends it all at once,
 causing the user to wonder if anything's really going on..."
   (let ((outbuf (generate-new-buffer " *eshell remote output*"))
        (errbuf (generate-new-buffer " *eshell remote error*"))
+       (command (or (file-remote-p command 'localname) command))
        (exitcode 1))
     (unwind-protect
        (progn
          (setq exitcode
-               (funcall handler 'shell-command
-                        (mapconcat 'shell-quote-argument
-                                   (append (list command) args) " ")
-                        outbuf errbuf))
-         (eshell-print (save-excursion (set-buffer outbuf)
-                                       (buffer-string)))
-         (eshell-error (save-excursion (set-buffer errbuf)
-                                       (buffer-string))))
+               (shell-command
+                (mapconcat 'shell-quote-argument
+                           (append (list command) args) " ")
+                outbuf errbuf))
+         (eshell-print (with-current-buffer outbuf (buffer-string)))
+         (eshell-error (with-current-buffer errbuf (buffer-string))))
       (eshell-close-handles exitcode 'nil)
       (kill-buffer outbuf)
       (kill-buffer errbuf))))
@@ -203,23 +214,20 @@ causing the user to wonder if anything's really going on..."
 (defun eshell-external-command (command args)
   "Insert output from an external COMMAND, using ARGS."
   (setq args (eshell-stringify-list (eshell-flatten-list args)))
-  (let ((handler
-        (unless (or (equal default-directory "/")
-                    (and (eshell-under-windows-p)
-                         (string-match "\\`[A-Za-z]:[/\\\\]\\'"
-                                       default-directory)))
-          (find-file-name-handler default-directory
-                                  'shell-command))))
-    (if (and handler
-            (not (and (eshell-under-xemacs-p)
-                      (eq handler 'dired-handler-fn))))
-       (eshell-remote-command handler command args))
-    (let ((interp (eshell-find-interpreter command)))
-      (assert interp)
-      (if (functionp (car interp))
-         (apply (car interp) (append (cdr interp) args))
-       (eshell-gather-process-output
-        (car interp) (append (cdr interp) args))))))
+  (let ((interp (eshell-find-interpreter
+                command
+                args
+                ;; `eshell-find-interpreter' does not work correctly
+                ;; for Tramp file name syntax.  But we don't need to
+                ;; know the interpreter in that case, therefore the
+                ;; check is suppressed.
+                (or (and (stringp command) (file-remote-p command))
+                    (file-remote-p default-directory)))))
+    (cl-assert interp)
+    (if (functionp (car interp))
+       (apply (car interp) (append (cdr interp) args))
+      (eshell-gather-process-output
+       (car interp) (append (cdr interp) args)))))
 
 (defun eshell/addpath (&rest args)
   "Add a set of paths to PATH."
@@ -231,20 +239,15 @@ causing the user to wonder if anything's really going on..."
 Adds the given PATH to $PATH.")
    (if args
        (progn
-        (if prepend
-            (setq args (nreverse args)))
-        (while args
-          (setenv "PATH"
-                  (if prepend
-                      (concat (car args) path-separator
-                              (getenv "PATH"))
-                    (concat (getenv "PATH") path-separator
-                            (car args))))
-          (setq args (cdr args))))
-     (let ((paths (parse-colon-path (getenv "PATH"))))
-       (while paths
-        (eshell-printn (car paths))
-        (setq paths (cdr paths)))))))
+        (setq eshell-path-env (getenv "PATH")
+              args (mapconcat 'identity args path-separator)
+              eshell-path-env
+              (if prepend
+                  (concat args path-separator eshell-path-env)
+                (concat eshell-path-env path-separator args)))
+        (setenv "PATH" eshell-path-env))
+     (dolist (dir (parse-colon-path (getenv "PATH")))
+       (eshell-printn dir)))))
 
 (put 'eshell/addpath 'eshell-no-numeric-conversions t)
 
@@ -266,18 +269,19 @@ Return nil, or a list of the form:
                (list (match-string 1)
                      file)))))))
 
-(defun eshell-find-interpreter (file &optional no-examine-p)
+(defun eshell-find-interpreter (file args &optional no-examine-p)
   "Find the command interpreter with which to execute FILE.
 If NO-EXAMINE-P is non-nil, FILE will not be inspected for a script
 line of the form #!<interp>."
   (let ((finterp
         (catch 'found
           (ignore
-           (eshell-for possible eshell-interpreter-alist
+           (dolist (possible eshell-interpreter-alist)
              (cond
               ((functionp (car possible))
-               (and (funcall (car possible) file)
-                    (throw 'found (cdr possible))))
+               (let ((fn (car possible)))
+                 (and (funcall fn file args)
+                      (throw 'found (cdr possible)))))
               ((stringp (car possible))
                (and (string-match (car possible) file)
                     (throw 'found (cdr possible))))
@@ -311,11 +315,8 @@ line of the form #!<interp>."
            (setq interp (eshell-script-interpreter fullname))
            (if interp
                (setq interp
-                     (cons (car (eshell-find-interpreter (car interp) t))
+                     (cons (car (eshell-find-interpreter (car interp) args t))
                            (cdr interp)))))
          (or interp (list fullname)))))))
 
-;;; Code:
-
-;;; arch-tag: 178d4064-7e60-4745-b81f-bab5d8d7c40f
 ;;; esh-ext.el ends here