X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/ecae6af979abcbb5b45c33ee05ceb297678ec9a0..0e963201d03d9229bb8ac4323291d2b0119526ed:/lisp/eshell/esh-opt.el diff --git a/lisp/eshell/esh-opt.el b/lisp/eshell/esh-opt.el index 8f8fb9b8a7..d0929db546 100644 --- a/lisp/eshell/esh-opt.el +++ b/lisp/eshell/esh-opt.el @@ -1,7 +1,6 @@ -;;; esh-opt.el --- command options processing +;;; esh-opt.el --- command options processing -*- lexical-binding:t -*- -;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, -;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +;; Copyright (C) 1999-2016 Free Software Foundation, Inc. ;; Author: John Wiegley @@ -22,27 +21,68 @@ ;;; Commentary: +;;; Code: + (provide 'esh-opt) -(eval-when-compile (require 'esh-ext)) +(require 'esh-ext) -(defgroup eshell-opt nil - "The options processing code handles command argument parsing for -Eshell commands implemented in Lisp." - :tag "Command options processing" - :group 'eshell) +;; Unused. +;; (defgroup eshell-opt nil +;; "The options processing code handles command argument parsing for +;; Eshell commands implemented in Lisp." +;; :tag "Command options processing" +;; :group 'eshell) ;;; User Functions: -(defmacro eshell-eval-using-options (name macro-args - options &rest body-forms) +(defmacro eshell-eval-using-options (name macro-args options &rest body-forms) "Process NAME's MACRO-ARGS using a set of command line OPTIONS. -After doing so, settings will be stored in local symbols as declared -by OPTIONS; FORMS will then be evaluated -- assuming all was OK. +After doing so, stores settings in local symbols as declared by OPTIONS; +then evaluates BODY-FORMS -- assuming all was OK. + +OPTIONS is a list, beginning with one or more elements of the form: +\(SHORT LONG VALUE SYMBOL HELP-STRING) +Each of these elements represents a particular command-line switch. + +SHORT is either nil, or a character that can be used as a switch -SHORT. +LONG is either nil, or a string that can be used as a switch --LONG. +At least one of SHORT and LONG must be non-nil. +VALUE is the value associated with the option. It can be either: + t - the option needs a value to be specified after the switch; + nil - the option is given the value t; + anything else - specifies the actual value for the option. +SYMBOL is either nil, or the name of the Lisp symbol that will be bound +to VALUE. A nil SYMBOL calls `eshell-show-usage', and so is appropriate +for a \"--help\" type option. +HELP-STRING is a documentation string for the option. + +Any remaining elements of OPTIONS are :KEYWORD arguments. Some take +arguments, some do not. The recognized :KEYWORDS are: + +:external STRING + STRING is an external command to run if there are unknown switches. + +:usage STRING + STRING is the initial part of the command's documentation string. + It appears before the options are listed. -The syntax of OPTIONS is: +:post-usage STRING + STRING is an optional trailing part of the command's documentation string. + It appears after the options, but before the final part of the + documentation about the associated external command (if there is one). - '((?C nil nil multi-column \"multi-column display\") +:show-usage + If present, then show the usage message if the command is called with no + arguments. + +:preserve-args + If present, do not pass MACRO-ARGS through `eshell-flatten-list' +and `eshell-stringify-list'. + +For example, OPTIONS might look like: + + ((?C nil nil multi-column \"multi-column display\") (nil \"help\" nil nil \"show this usage display\") (?r \"reverse\" nil reverse-list \"reverse order while sorting\") :external \"ls\" @@ -51,57 +91,51 @@ The syntax of OPTIONS is: Sort entries alphabetically across.\") `eshell-eval-using-options' returns the value of the last form in -BODY-FORMS. If instead an external command is run, the tag -`eshell-external' will be thrown with the new process for its value. +BODY-FORMS. If instead an external command is run (because of +an unknown option), the tag `eshell-external' will be thrown with +the new process for its value. Lastly, any remaining arguments will be available in a locally interned variable `args' (created using a `let' form)." (declare (debug (form form sexp body))) - `(let ((temp-args - ,(if (memq ':preserve-args (cadr options)) - macro-args - (list 'eshell-stringify-list - (list 'eshell-flatten-list macro-args))))) - (let ,(append (mapcar (function - (lambda (opt) - (or (and (listp opt) (nth 3 opt)) - 'eshell-option-stub))) - (cadr options)) - '(usage-msg last-value ext-command args)) - (eshell-do-opt ,name ,options (quote ,body-forms))))) + `(let* ((temp-args + ,(if (memq ':preserve-args (cadr options)) + macro-args + (list 'eshell-stringify-list + (list 'eshell-flatten-list macro-args)))) + (processed-args (eshell--do-opts ,name ,options temp-args)) + ,@(delete-dups + (delq nil (mapcar (lambda (opt) + (and (listp opt) (nth 3 opt) + `(,(nth 3 opt) (pop processed-args)))) + ;; `options' is of the form (quote OPTS). + (cadr options)))) + (args processed-args)) + ,@body-forms)) ;;; Internal Functions: -(eval-when-compile - (defvar temp-args) - (defvar last-value) - (defvar usage-msg) - (defvar ext-command) - (defvar args)) +;; Documented part of the interface; see eshell-eval-using-options. +(defvar eshell--args) -(defun eshell-do-opt (name options body-forms) +(defun eshell--do-opts (name options args) "Helper function for `eshell-eval-using-options'. This code doesn't really need to be macro expanded everywhere." - (setq args temp-args) - (if (setq - ext-command - (catch 'eshell-ext-command - (when (setq - usage-msg - (catch 'eshell-usage - (setq last-value nil) - (if (and (= (length args) 0) - (memq ':show-usage options)) - (throw 'eshell-usage - (eshell-show-usage name options))) - (setq args (eshell-process-args name args options) - last-value (eval (append (list 'progn) - body-forms))) - nil)) - (error "%s" usage-msg)))) - (throw 'eshell-external - (eshell-external-command ext-command args)) - last-value)) + (let ((ext-command + (catch 'eshell-ext-command + (let ((usage-msg + (catch 'eshell-usage + (if (and (= (length args) 0) + (memq ':show-usage options)) + (eshell-show-usage name options) + (setq args (eshell--process-args name args options)) + nil)))) + (when usage-msg + (error "%s" usage-msg)))))) + (if ext-command + (throw 'eshell-external + (eshell-external-command ext-command args)) + args))) (defun eshell-show-usage (name options) "Display the usage message for NAME, using OPTIONS." @@ -144,28 +178,30 @@ This code doesn't really need to be macro expanded everywhere." (if extcmd (setq usage (concat usage - (format " + (format-message " This command is implemented in Lisp. If an unrecognized option is -passed to this command, the external version '%s' +passed to this command, the external version `%s' will be called instead." extcmd))))) (throw 'eshell-usage usage))) -(defun eshell-set-option (name ai opt options) +(defun eshell--set-option (name ai opt options opt-vals) "Using NAME's remaining args (index AI), set the OPT within OPTIONS. If the option consumes an argument for its value, the argument list will be modified." (if (not (nth 3 opt)) (eshell-show-usage name options) - (if (eq (nth 2 opt) t) - (if (> ai (length args)) - (error "%s: missing option argument" name) - (set (nth 3 opt) (nth ai args)) - (if (> ai 0) - (setcdr (nthcdr (1- ai) args) (nthcdr (1+ ai) args)) - (setq args (cdr args)))) - (set (nth 3 opt) (or (nth 2 opt) t))))) - -(defun eshell-process-option (name switch kind ai options) + (setcdr (assq (nth 3 opt) opt-vals) + (if (eq (nth 2 opt) t) + (if (> ai (length eshell--args)) + (error "%s: missing option argument" name) + (prog1 (nth ai eshell--args) + (if (> ai 0) + (setcdr (nthcdr (1- ai) eshell--args) + (nthcdr (1+ ai) eshell--args)) + (setq eshell--args (cdr eshell--args))))) + (or (nth 2 opt) t))))) + +(defun eshell--process-option (name switch kind ai options opt-vals) "For NAME, process SWITCH (of type KIND), from args at index AI. The SWITCH will be looked up in the set of OPTIONS. @@ -180,12 +216,10 @@ switch is unrecognized." found) (while opts (if (and (listp (car opts)) - (nth kind (car opts)) - (if (= kind 0) - (eq switch (nth kind (car opts))) - (string= switch (nth kind (car opts))))) + (nth kind (car opts)) + (equal switch (nth kind (car opts)))) (progn - (eshell-set-option name ai (car opts) options) + (eshell--set-option name ai (car opts) options opt-vals) (setq found t opts nil)) (setq opts (cdr opts)))) (unless found @@ -194,14 +228,22 @@ switch is unrecognized." (setq extcmd (eshell-search-path (cadr extcmd))) (if extcmd (throw 'eshell-ext-command extcmd) - (if (characterp switch) - (error "%s: unrecognized option -%c" name switch) - (error "%s: unrecognized option --%s" name switch)))))))) - -(defun eshell-process-args (name args options) - "Process the given ARGS using OPTIONS. -This assumes that symbols have been intern'd by `eshell-with-options'." - (let ((ai 0) arg) + (error (if (characterp switch) "%s: unrecognized option -%c" + "%s: unrecognized option --%s") + name switch))))))) + +(defun eshell--process-args (name args options) + "Process the given ARGS using OPTIONS." + (let* ((seen ()) + (opt-vals (delq nil (mapcar (lambda (opt) + (when (listp opt) + (let ((sym (nth 3 opt))) + (when (and sym (not (memq sym seen))) + (push sym seen) + (list sym))))) + options))) + (ai 0) arg + (eshell--args args)) (while (< ai (length args)) (setq arg (nth ai args)) (if (not (and (stringp arg) @@ -214,16 +256,14 @@ This assumes that symbols have been intern'd by `eshell-with-options'." (setcdr (nthcdr (1- ai) args) (nthcdr (1+ ai) args))) (if dash (if (> (length switch) 0) - (eshell-process-option name switch 1 ai options) + (eshell--process-option name switch 1 ai options opt-vals) (setq ai (length args))) (let ((len (length switch)) (index 0)) (while (< index len) - (eshell-process-option name (aref switch index) 0 ai options) - (setq index (1+ index))))))))) - args) - -;;; Code: + (eshell--process-option name (aref switch index) + 0 ai options opt-vals) + (setq index (1+ index)))))))) + (nconc (mapcar #'cdr opt-vals) args))) -;; arch-tag: 45c6c2d0-8091-46a1-a205-2f4bafd8230c ;;; esh-opt.el ends here