-;;; esh-cmd.el --- command invocation
+;;; esh-cmd.el --- command invocation -*- lexical-binding:t -*-
-;; Copyright (C) 1999-2011 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))
: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)
(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))))))
(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
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))
(looking-at eshell-lisp-regexp))
(let* ((here (point))
(obj
- (condition-case err
+ (condition-case nil
(read (current-buffer))
(end-of-file
(throw 'eshell-incomplete ?\()))))
`(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
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
(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)
(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
,@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
(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)))
;; `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))
;; 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))))
(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
;; 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))
(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
(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)
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.