]> code.delx.au - gnu-emacs/blobdiff - lisp/eshell/esh-ext.el
Prevent bar cursor overwriting next glyph (bug#16856)
[gnu-emacs] / lisp / eshell / esh-ext.el
index 3acbeac0b891d3d85de6e4cb71e026bf1e9f157f..ca62d0cf8b02277f34b2a5db82dcb596d5775b8d 100644 (file)
@@ -1,6 +1,6 @@
-;;; esh-ext.el --- commands external to Eshell
+;;; esh-ext.el --- commands external to Eshell  -*- lexical-binding:t -*-
 
 
-;; Copyright (C) 1999-201 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 
 
 ;; Author: John Wiegley <johnw@gnu.org>
 
 
 (provide 'esh-ext)
 
 
 (provide 'esh-ext)
 
+(require 'esh-util)
+
 (eval-when-compile
 (eval-when-compile
-  (require 'cl)
+  (require 'cl-lib)
+  (require 'esh-io)
   (require 'esh-cmd))
   (require 'esh-cmd))
-(require 'esh-util)
+(require 'esh-opt)
 
 (defgroup eshell-ext nil
   "External commands are invoked when operating system executables are
 
 (defgroup eshell-ext nil
   "External commands are invoked when operating system executables are
@@ -57,14 +60,15 @@ loaded into memory, thus beginning a new process."
   :type '(repeat string)
   :group 'eshell-ext)
 
   :type '(repeat string)
   :group 'eshell-ext)
 
-(defcustom eshell-force-execution nil
-  "If non-nil, try to execute binary files regardless of permissions.
+(defcustom eshell-force-execution
+  (not (null (memq system-type '(windows-nt ms-dos))))
+  "If non-nil, try to execute files regardless of execute permissions.
 This can be useful on systems like Windows, where the operating system
 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
-kind of script file, but the language won't let you but a '#!'
-interpreter line in the file, and you don't want to make it executable
-since nothing else but Eshell will be able to understand
+doesn't support the execution bit for shell scripts; or in cases where
+you want to associate an interpreter with a particular kind of script
+file, but the language won't let you but a `#!' interpreter line in
+the file, and you don't want to make it executable since nothing else
+but Eshell will be able to understand
 `eshell-interpreter-alist'."
   :type 'boolean
   :group 'eshell-ext)
 `eshell-interpreter-alist'."
   :type 'boolean
   :group 'eshell-ext)
@@ -75,6 +79,8 @@ since nothing else but Eshell will be able to understand
       name
     (let ((list (eshell-parse-colon-path eshell-path-env))
          suffixes n1 n2 file)
       name
     (let ((list (eshell-parse-colon-path eshell-path-env))
          suffixes n1 n2 file)
+      (if (eshell-under-windows-p)
+          (push "." list))
       (while list
        (setq n1 (concat (car list) name))
        (setq suffixes eshell-binary-suffixes)
       (while list
        (setq n1 (concat (car list) name))
        (setq suffixes eshell-binary-suffixes)
@@ -89,9 +95,13 @@ since nothing else but Eshell will be able to understand
        (setq list (cdr list)))
       file)))
 
        (setq list (cdr list)))
       file)))
 
+;; This file provides itself then eval-when-compile loads files that require it.
+;; This causes spurious "might not be defined at runtime" warnings.
+(declare-function eshell-search-path "esh-ext" (name))
+
 (defcustom eshell-windows-shell-file
   (if (eshell-under-windows-p)
 (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)
          (or (eshell-search-path "cmd.exe")
              (eshell-search-path "command.com"))
@@ -102,13 +112,17 @@ wholly ignored."
   :type '(choice file (const nil))
   :group 'eshell-ext)
 
   :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
 (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)
 
 (defcustom eshell-interpreter-alist
   (if (eshell-under-windows-p)
@@ -118,9 +132,10 @@ Each member is a cons cell of the form:
 
   (MATCH . INTERPRETER)
 
 
   (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
 
 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
@@ -176,6 +191,8 @@ This bypasses all Lisp functions and aliases."
        (error "%s: external command not found"
               (substring command 1))))))
 
        (error "%s: external command not found"
               (substring command 1))))))
 
+(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.
 (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.
@@ -186,6 +203,7 @@ 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*"))
 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
        (exitcode 1))
     (unwind-protect
        (progn
@@ -203,10 +221,16 @@ 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)))
 (defun eshell-external-command (command args)
   "Insert output from an external COMMAND, using ARGS."
   (setq args (eshell-stringify-list (eshell-flatten-list args)))
-  (if (file-remote-p default-directory)
-      (eshell-remote-command command args))
-  (let ((interp (eshell-find-interpreter command)))
-    (assert interp)
+  (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
     (if (functionp (car interp))
        (apply (car interp) (append (cdr interp) args))
       (eshell-gather-process-output
@@ -222,20 +246,15 @@ causing the user to wonder if anything's really going on..."
 Adds the given PATH to $PATH.")
    (if args
        (progn
 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)
 
 
 (put 'eshell/addpath 'eshell-no-numeric-conversions t)
 
@@ -257,7 +276,7 @@ Return nil, or a list of the form:
                (list (match-string 1)
                      file)))))))
 
                (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>."
   "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>."
@@ -267,8 +286,9 @@ line of the form #!<interp>."
            (dolist (possible eshell-interpreter-alist)
              (cond
               ((functionp (car possible))
            (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))))
               ((stringp (car possible))
                (and (string-match (car possible) file)
                     (throw 'found (cdr possible))))
@@ -279,6 +299,11 @@ line of the form #!<interp>."
       (let ((fullname (if (file-name-directory file) file
                        (eshell-search-path file)))
            (suffixes eshell-binary-suffixes))
       (let ((fullname (if (file-name-directory file) file
                        (eshell-search-path file)))
            (suffixes eshell-binary-suffixes))
+       (if (and fullname
+                (not (file-remote-p fullname))
+                (file-remote-p default-directory))
+           (setq fullname (expand-file-name
+                           (concat "./" fullname) default-directory)))
        (if (and fullname (not (or eshell-force-execution
                                   (file-executable-p fullname))))
            (while suffixes
        (if (and fullname (not (or eshell-force-execution
                                   (file-executable-p fullname))))
            (while suffixes
@@ -302,7 +327,7 @@ line of the form #!<interp>."
            (setq interp (eshell-script-interpreter fullname))
            (if interp
                (setq 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)))))))
 
                            (cdr interp)))))
          (or interp (list fullname)))))))