* eshell/esh-var.el (eshell-variable-aliases-list): Fix doc typo.
[bpt/emacs.git] / lisp / eshell / esh-cmd.el
index a7c9208..cc4fdd1 100644 (file)
@@ -1,6 +1,6 @@
-;;; esh-cmd.el --- command invocation
+;;; esh-cmd.el --- command invocation  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1999-201 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2013 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 
 (require 'esh-ext)
 
 (eval-when-compile
-  (require 'cl)
+  (require 'cl-lib)
   (require 'pcomplete))
 
 
@@ -236,10 +236,14 @@ return non-nil if the command is complex."
   :group 'eshell-cmd)
 
 (defcustom eshell-debug-command nil
-  "If non-nil, enable debugging code.  SSLLOOWW.
-This option is only useful for reporting bugs.  If you enable it, you
-will have to visit the file 'eshell-cmd.el' and run the command
-\\[eval-buffer]."
+  "If non-nil, enable Eshell debugging code.
+This is slow, and only useful for debugging problems with Eshell.
+If you change this without using customize after Eshell has loaded,
+you must re-load 'esh-cmd.el'."
+  :initialize 'custom-initialize-default
+  :set (lambda (symbol value)
+        (set symbol value)
+        (load-library "esh-cmd"))
   :type 'boolean
   :group 'eshell-cmd)
 
@@ -480,25 +484,22 @@ implemented via rewriting, rather than as a function."
       (let ((body (car (last terms))))
        (setcdr (last terms 2) nil)
        `(let ((for-items
-                ;; Apparently, eshell-do-eval only works for immutable
-                ;; let-bindings, i.e. we cannot use `setq' on `for-items'.
-                ;; Instead we store the list in the car of a cons-cell (which
-                ;; acts as a ref-cell) so we can setcar instead of setq.
-                (list
-                 (append
-                  ,@(mapcar
-                     (lambda (elem)
-                       (if (listp elem)
-                           elem
-                         `(list ,elem)))
-                     (cdr (cddr terms))))))
-               (eshell-command-body '(nil))
+               (copy-tree
+                (append
+                 ,@(mapcar
+                    (lambda (elem)
+                      (if (listp elem)
+                          elem
+                        `(list ,elem)))
+                    (cdr (cddr terms))))))
+              (eshell-command-body '(nil))
                (eshell-test-body '(nil)))
-           (while (consp (car for-items))
-             (let ((,(intern (cadr terms)) (caar for-items)))
-               (eshell-protect
-                ,(eshell-invokify-arg body t)))
-             (setcar for-items (cdar for-items)))
+          (while (car for-items)
+            (let ((,(intern (cadr terms)) (car for-items)))
+              (eshell-protect
+               ,(eshell-invokify-arg body t)))
+            (setcar for-items (cadr for-items))
+            (setcdr for-items (cddr for-items)))
            (eshell-close-handles
             eshell-last-command-status
             (list 'quote eshell-last-command-result))))))
@@ -605,7 +606,7 @@ For an external command, it means an exit code of 0."
                 (list
                  (if (<= (length pieces) 1)
                      (car pieces)
-                   (assert (not eshell-in-pipeline-p))
+                   (cl-assert (not eshell-in-pipeline-p))
                    `(eshell-execute-pipeline (quote ,pieces))))))
        (setq bp (cdr bp))))
     ;; `results' might be empty; this happens in the case of
@@ -616,7 +617,7 @@ For an external command, it means an exit code of 0."
          results (cdr results)
          sep-terms (nreverse sep-terms))
     (while results
-      (assert (car sep-terms))
+      (cl-assert (car sep-terms))
       (setq final (eshell-structure-basic-command
                   'if (string= (car sep-terms) "&&") "if"
                   `(eshell-protect ,(car results))
@@ -649,7 +650,7 @@ For an external command, it means an exit code of 0."
           (looking-at eshell-lisp-regexp))
       (let* ((here (point))
             (obj
-             (condition-case err
+             (condition-case nil
                  (read (current-buffer))
                (end-of-file
                 (throw 'eshell-incomplete ?\()))))
@@ -766,9 +767,8 @@ This macro calls itself recursively, with NOTFIRST non-nil."
     `(eshell-copy-handles
       (progn
        ,(when (cdr pipeline)
-          `(let (nextproc)
-              (setq nextproc
-                    (eshell-do-pipelines (quote ,(cdr pipeline)) t))
+          `(let ((nextproc
+                  (eshell-do-pipelines (quote ,(cdr pipeline)) t)))
               (eshell-set-output-handle ,eshell-output-handle
                                         'append nextproc)
               (eshell-set-output-handle ,eshell-error-handle
@@ -796,10 +796,9 @@ This macro calls itself recursively, with NOTFIRST non-nil."
 Output of each command is passed as input to the next one in the pipeline.
 This is used on systems where `start-process' is not supported."
   (when (setq pipeline (cadr pipeline))
-    `(let (result)
+    `(progn
        ,(when (cdr pipeline)
-          `(let (output-marker)
-             (setq output-marker ,(point-marker))
+          `(let ((output-marker ,(point-marker)))
              (eshell-set-output-handle ,eshell-output-handle
                                        'append output-marker)
              (eshell-set-output-handle ,eshell-error-handle
@@ -811,21 +810,21 @@ This is used on systems where `start-process' is not supported."
           (when (memq (car head) eshell-deferrable-commands)
             (ignore
              (setcar head
-                      (intern-soft
-                       (concat (symbol-name (car head)) "*"))))))
-        ;; The last process in the pipe should get its handles
+                     (intern-soft
+                      (concat (symbol-name (car head)) "*"))))))
+       ;; The last process in the pipe should get its handles
        ;; redirected as we found them before running the pipe.
        ,(if (null (cdr pipeline))
             `(progn
                (setq eshell-current-handles tail-handles)
                (setq eshell-in-pipeline-p nil)))
-       (setq result ,(car pipeline))
-       ;; tailproc gets the result of the last successful process in
-       ;; the pipeline.
-       (setq tailproc (or result tailproc))
-       ,(if (cdr pipeline)
-            `(eshell-do-pipelines-synchronously (quote ,(cdr pipeline))))
-       result)))
+       (let ((result ,(car pipeline)))
+         ;; tailproc gets the result of the last successful process in
+         ;; the pipeline.
+         (setq tailproc (or result tailproc))
+         ,(if (cdr pipeline)
+              `(eshell-do-pipelines-synchronously (quote ,(cdr pipeline))))
+         result))))
 
 (defalias 'eshell-process-identity 'identity)
 
@@ -890,8 +889,7 @@ Returns a string comprising the output from the command."
         (eshell-print "errors\n"))
      (if eshell-debug-command
         (eshell-print "commands\n")))
-    ((or (string= (car args) "-h")
-        (string= (car args) "--help"))
+    ((member (car args) '("-h" "--help"))
      (eshell-print "usage: eshell-debug [kinds]
 
 This command is used to aid in debugging problems related to Eshell
@@ -995,14 +993,6 @@ at the moment are:
        ,@commands
        (eshell-debug-command ,(concat "done " (eval tag)) form))))
 
-(defsubst eshell-macrop (object)
-  "Return t if OBJECT is a macro or nil otherwise."
-  (and (symbolp object) (fboundp object)
-       (setq object (indirect-function object))
-       (listp object)
-       (eq 'macro (car object))
-       (functionp (cdr object))))
-
 (defun eshell-do-eval (form &optional synchronous-p)
   "Evaluate form, simplifying it as we go.
 Unless SYNCHRONOUS-P is non-nil, throws `eshell-defer' if it needs to
@@ -1018,7 +1008,7 @@ be finished later after the completion of an asynchronous subprocess."
       (setq form (cadr (cadr form))))
     ;; expand any macros directly into the form.  This is done so that
     ;; we can modify any `let' forms to evaluate only once.
-    (if (eshell-macrop (car form))
+    (if (macrop (car form))
        (let ((exp (eshell-copy-tree (macroexpand form))))
          (eshell-manipulate (format "expanding macro `%s'"
                                     (symbol-name (car form)))
@@ -1030,7 +1020,7 @@ be finished later after the completion of an asynchronous subprocess."
        ;; `eshell-copy-tree' is needed here so that the test argument
        ;; doesn't get modified and thus always yield the same result.
        (when (car eshell-command-body)
-         (assert (not synchronous-p))
+         (cl-assert (not synchronous-p))
          (eshell-do-eval (car eshell-command-body))
          (setcar eshell-command-body nil)
          (setcar eshell-test-body nil))
@@ -1050,7 +1040,7 @@ be finished later after the completion of an asynchronous subprocess."
        ;; doesn't get modified and thus always yield the same result.
        (if (car eshell-command-body)
            (progn
-             (assert (not synchronous-p))
+             (cl-assert (not synchronous-p))
              (eshell-do-eval (car eshell-command-body)))
          (unless (car eshell-test-body)
            (setcar eshell-test-body (eshell-copy-tree (car args))))
@@ -1091,6 +1081,11 @@ be finished later after the completion of an asynchronous subprocess."
          (eshell-manipulate "handling special form"
            (setcar args `(eshell-do-eval ',(car args) ,synchronous-p))))
        (eval form))
+       ((eq (car form) 'setq)
+       (if (cddr args) (error "Unsupported form (setq X1 E1 X2 E2..)"))
+        (eshell-manipulate "evaluating arguments to setq"
+          (setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p)))
+       (list 'quote (eval form)))
        (t
        (if (and args (not (memq (car form) '(run-hooks))))
            (eshell-manipulate
@@ -1127,11 +1122,12 @@ be finished later after the completion of an asynchronous subprocess."
          ;; Thus, aliases can even contain references to asynchronous
          ;; sub-commands, and things will still work out as they
          ;; should.
-         (let (result new-form)
-           (if (setq new-form
-                     (catch 'eshell-replace-command
-                       (ignore
-                        (setq result (eval form)))))
+         (let* (result
+                 (new-form
+                  (catch 'eshell-replace-command
+                    (ignore
+                     (setq result (eval form))))))
+           (if new-form
                (progn
                  (eshell-manipulate "substituting replacement form"
                    (setcar form (car new-form))
@@ -1199,7 +1195,7 @@ COMMAND may result in an alias being executed, or a plain command."
   (setq eshell-last-arguments args
        eshell-last-command-name (eshell-stringify command))
   (run-hook-with-args 'eshell-prepare-command-hook)
-  (assert (stringp eshell-last-command-name))
+  (cl-assert (stringp eshell-last-command-name))
   (if eshell-last-command-name
       (or (run-hook-with-args-until-success
           'eshell-named-command-hook eshell-last-command-name
@@ -1214,13 +1210,12 @@ COMMAND may result in an alias being executed, or a plain command."
   (let* ((sym (intern-soft (concat "eshell/" name)))
         (file (symbol-file sym 'defun)))
     ;; If the function exists, but is defined in an eshell module
-    ;; that's not currently enabled, don't report it as found
+    ;; that's not currently enabled, don't report it as found.
     (if (and file
-            (string-match "\\(em\\|esh\\)-\\(.*\\)\\(\\.el\\)?\\'" file))
+            (setq file (file-name-base file))
+            (string-match "\\`\\(em\\|esh\\)-\\([[:alnum:]]+\\)\\'" file))
        (let ((module-sym
-              (intern (file-name-sans-extension
-                       (file-name-nondirectory
-                        (concat "eshell-" (match-string 2 file)))))))
+              (intern (concat "eshell-" (match-string 2 file)))))
          (if (and (functionp sym)
                   (or (null module-sym)
                       (eshell-using-module module-sym)
@@ -1247,25 +1242,23 @@ or an external command."
 PRINTER and ERRPRINT are functions to use for printing regular
 messages, and errors.  FORM-P should be non-nil if FUNC-OR-FORM
 represent a lisp form; ARGS will be ignored in that case."
-  (let (result)
-    (eshell-condition-case err
-       (progn
-         (setq result
-               (save-current-buffer
-                 (if form-p
-                     (eval func-or-form)
-                   (apply func-or-form args))))
-         (and result (funcall printer result))
-         result)
-      (error
-       (let ((msg (error-message-string err)))
-        (if (and (not form-p)
-                 (string-match "^Wrong number of arguments" msg)
-                 (fboundp 'eldoc-get-fnsym-args-string))
-            (let ((func-doc (eldoc-get-fnsym-args-string func-or-form)))
-              (setq msg (format "usage: %s" func-doc))))
-        (funcall errprint msg))
-       nil))))
+  (eshell-condition-case err
+      (let ((result
+             (save-current-buffer
+               (if form-p
+                   (eval func-or-form)
+                 (apply func-or-form args)))))
+        (and result (funcall printer result))
+        result)
+    (error
+     (let ((msg (error-message-string err)))
+       (if (and (not form-p)
+                (string-match "^Wrong number of arguments" msg)
+                (fboundp 'eldoc-get-fnsym-args-string))
+           (let ((func-doc (eldoc-get-fnsym-args-string func-or-form)))
+             (setq msg (format "usage: %s" func-doc))))
+       (funcall errprint msg))
+     nil)))
 
 (defsubst eshell-apply* (printer errprint func args)
   "Call FUNC, with ARGS, trapping errors and return them as output.