1 ;;; esh-opt.el --- command options processing
3 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
4 ;; 2008, 2009, 2010 Free Software Foundation, Inc.
6 ;; Author: John Wiegley <johnw@gnu.org>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
29 (eval-when-compile (require 'esh-ext
))
31 (defgroup eshell-opt nil
32 "The options processing code handles command argument parsing for
33 Eshell commands implemented in Lisp."
34 :tag
"Command options processing"
39 (defmacro eshell-eval-using-options
(name macro-args
40 options
&rest body-forms
)
41 "Process NAME's MACRO-ARGS using a set of command line OPTIONS.
42 After doing so, settings will be stored in local symbols as declared
43 by OPTIONS; FORMS will then be evaluated -- assuming all was OK.
45 The syntax of OPTIONS is:
47 '((?C nil nil multi-column \"multi-column display\")
48 (nil \"help\" nil nil \"show this usage display\")
49 (?r \"reverse\" nil reverse-list \"reverse order while sorting\")
51 :usage \"[OPTION]... [FILE]...
52 List information about the FILEs (the current directory by default).
53 Sort entries alphabetically across.\")
55 `eshell-eval-using-options' returns the value of the last form in
56 BODY-FORMS. If instead an external command is run, the tag
57 `eshell-external' will be thrown with the new process for its value.
59 Lastly, any remaining arguments will be available in a locally
60 interned variable `args' (created using a `let' form)."
61 (declare (debug (form form sexp body
)))
63 ,(if (memq ':preserve-args
(cadr options
))
65 (list 'eshell-stringify-list
66 (list 'eshell-flatten-list macro-args
)))))
67 (let ,(append (mapcar (function
69 (or (and (listp opt
) (nth 3 opt
))
70 'eshell-option-stub
)))
72 '(usage-msg last-value ext-command args
))
73 (eshell-do-opt ,name
,options
(quote ,body-forms
)))))
75 ;;; Internal Functions:
83 (defun eshell-do-opt (name options body-forms
)
84 "Helper function for `eshell-eval-using-options'.
85 This code doesn't really need to be macro expanded everywhere."
89 (catch 'eshell-ext-command
94 (if (and (= (length args
) 0)
95 (memq ':show-usage options
))
97 (eshell-show-usage name options
)))
98 (setq args
(eshell-process-args name args options
)
99 last-value
(eval (append (list 'progn
)
102 (error "%s" usage-msg
))))
103 (throw 'eshell-external
104 (eshell-external-command ext-command args
))
107 (defun eshell-show-usage (name options
)
108 "Display the usage message for NAME, using OPTIONS."
109 (let ((usage (format "usage: %s %s\n\n" name
110 (cadr (memq ':usage options
))))
111 (extcmd (memq ':external options
))
112 (post-usage (memq ':post-usage options
))
115 (when (listp (car options
))
116 (let ((opt (car options
)))
118 (cond ((and (nth 0 opt
)
122 (format " %-20s %s\n"
123 (format "-%c, --%s" (nth 0 opt
)
129 (format " %-20s %s\n"
130 (format "-%c" (nth 0 opt
))
135 (format " %-20s %s\n"
136 (format " --%s" (nth 1 opt
))
138 (t (setq had-option nil
)))))
139 (setq options
(cdr options
)))
141 (setq usage
(concat usage
(and had-option
"\n")
144 (setq extcmd
(eshell-search-path (cadr extcmd
)))
149 This command is implemented in Lisp. If an unrecognized option is
150 passed to this command, the external version '%s'
151 will be called instead." extcmd
)))))
152 (throw 'eshell-usage usage
)))
154 (defun eshell-set-option (name ai opt options
)
155 "Using NAME's remaining args (index AI), set the OPT within OPTIONS.
156 If the option consumes an argument for its value, the argument list
158 (if (not (nth 3 opt
))
159 (eshell-show-usage name options
)
160 (if (eq (nth 2 opt
) t
)
161 (if (> ai
(length args
))
162 (error "%s: missing option argument" name
)
163 (set (nth 3 opt
) (nth ai args
))
165 (setcdr (nthcdr (1- ai
) args
) (nthcdr (1+ ai
) args
))
166 (setq args
(cdr args
))))
167 (set (nth 3 opt
) (or (nth 2 opt
) t
)))))
169 (defun eshell-process-option (name switch kind ai options
)
170 "For NAME, process SWITCH (of type KIND), from args at index AI.
171 The SWITCH will be looked up in the set of OPTIONS.
173 SWITCH should be either a string or character. KIND should be the
174 integer 0 if it's a character, or 1 if it's a string.
176 The SWITCH is then be matched against OPTIONS. If no matching handler
177 is found, and an :external command is defined (and available), it will
178 be called; otherwise, an error will be triggered to say that the
179 switch is unrecognized."
180 (let* ((opts options
)
183 (if (and (listp (car opts
))
184 (nth kind
(car opts
))
186 (eq switch
(nth kind
(car opts
)))
187 (string= switch
(nth kind
(car opts
)))))
189 (eshell-set-option name ai
(car opts
) options
)
190 (setq found t opts nil
))
191 (setq opts
(cdr opts
))))
193 (let ((extcmd (memq ':external options
)))
195 (setq extcmd
(eshell-search-path (cadr extcmd
)))
197 (throw 'eshell-ext-command extcmd
)
198 (if (characterp switch
)
199 (error "%s: unrecognized option -%c" name switch
)
200 (error "%s: unrecognized option --%s" name switch
))))))))
202 (defun eshell-process-args (name args options
)
203 "Process the given ARGS using OPTIONS.
204 This assumes that symbols have been intern'd by `eshell-with-options'."
206 (while (< ai
(length args
))
207 (setq arg
(nth ai args
))
208 (if (not (and (stringp arg
)
209 (string-match "^-\\(-\\)?\\(.*\\)" arg
)))
211 (let* ((dash (match-string 1 arg
))
212 (switch (match-string 2 arg
)))
214 (setq args
(cdr args
))
215 (setcdr (nthcdr (1- ai
) args
) (nthcdr (1+ ai
) args
)))
217 (if (> (length switch
) 0)
218 (eshell-process-option name switch
1 ai options
)
219 (setq ai
(length args
)))
220 (let ((len (length switch
))
223 (eshell-process-option name
(aref switch index
) 0 ai options
)
224 (setq index
(1+ index
)))))))))
227 ;; arch-tag: 45c6c2d0-8091-46a1-a205-2f4bafd8230c
228 ;;; esh-opt.el ends here