base: Fix bug in `while'.
[lisp] / optparse.lisp
CommitLineData
861345b4 1;;; -*-lisp-*-
2;;;
3;;; $Id$
4;;;
5;;; Option parser, standard issue
6;;;
7;;; (c) 2005 Straylight/Edgeware
8;;;
9
10;;;----- Licensing notice ---------------------------------------------------
11;;;
12;;; This program is free software; you can redistribute it and/or modify
13;;; it under the terms of the GNU General Public License as published by
14;;; the Free Software Foundation; either version 2 of the License, or
15;;; (at your option) any later version.
16;;;
17;;; This program is distributed in the hope that it will be useful,
18;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;;; GNU General Public License for more details.
21;;;
22;;; You should have received a copy of the GNU General Public License
23;;; along with this program; if not, write to the Free Software Foundation,
24;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
02866e07
MW
26;;;--------------------------------------------------------------------------
27;;; Packages.
28
0b3651e5 29(defpackage #:optparse
861345b4 30 (:use #:common-lisp #:mdw.base #:mdw.sys-base #:mdw.str)
31 (:export #:exit #:*program-name* #:*command-line-strings*
32 #:moan #:die
33 #:option #:optionp #:make-option
34 #:opt-short-name #:opt-long-name #:opt-tag #:opt-negated-tag
35 #:opt-arg-name #:opt-arg-optional-p #:opt-documentation
36 #:option-parser #:make-option-parser #:option-parser-p
37 #:op-options #:op-non-option #:op-long-only-p #:op-numeric-p
38 #:op-negated-numeric-p #:op-negated-p
39 #:option-parse-error
40 #:option-parse-remainder #:option-parse-next #:option-parse-try
41 #:with-unix-error-reporting
42 #:defopthandler #:invoke-option-handler
43 #:set #:clear #:inc #:dec #:read #:int #:string
44 #:keyword #:list
45 #:parse-option-form #:options
46 #:simple-usage #:show-usage #:show-version #:show-help
5fa07b7a
MW
47 #:sanity-check-option-list
48 #:*help* #:*version* #:*usage* #:*options*
49 #:do-options #:help-opts #:define-program #:do-usage #:die-usage))
861345b4 50
0b3651e5 51(in-package #:optparse)
861345b4 52
02866e07 53;;;--------------------------------------------------------------------------
861345b4 54;;; Standard error-reporting functions.
55
56(defun moan (msg &rest args)
57 "Report an error message in the usual way."
58 (format *error-output* "~&~A: ~?~%" *program-name* msg args))
5fa07b7a 59
861345b4 60(defun die (&rest args)
61 "Report an error message and exit."
62 (apply #'moan args)
63 (exit 1))
64
02866e07 65;;;--------------------------------------------------------------------------
861345b4 66;;; The main option parser.
67
5fa07b7a
MW
68(defvar *options*)
69
861345b4 70(defstruct (option (:predicate optionp)
71 (:conc-name opt-)
72 (:print-function
73 (lambda (o s k)
74 (declare (ignore k))
75 (format s
76 "#<option~@[ -~C,~]~@[ --~A~]~:[~2*~;~:[=~A~;[=~A]~]~]~@[ ~S~]>"
77 (opt-short-name o)
78 (opt-long-name o)
79 (opt-arg-name o)
80 (opt-arg-optional-p o)
81 (opt-arg-name o)
82 (opt-documentation o))))
83 (:constructor %make-option)
84 (:constructor make-option
85 (long-name
86 short-name
87 &optional
88 arg-name
89 &key
90 (tag (intern (string-upcase long-name)
91 :keyword))
92 negated-tag
93 arg-optional-p
94 doc
95 (documentation doc))))
96 "Describes a command-line option. Slots:
97
98LONG-NAME The option's long name. If this is null, the `option' is
99 just a banner to be printed in the program's help text.
100
101TAG The value to be returned if this option is encountered. If
102 this is a function, instead, the function is called with the
103 option's argument or nil.
104
105NEGATED-TAG As for TAG, but used if the negated form of the option is
106 found. If this is nil (the default), the option cannot be
107 negated.
108
109SHORT-NAME The option's short name. This must be a single character, or
110 nil if the option has no short name.
111
112ARG-NAME The name of the option's argument, a string. If this is nil,
113 the option doesn't accept an argument. The name is shown in
114 the help text.
115
116ARG-OPTIONAL-P If non-nil, the option's argument is optional. This is
117 ignored unless ARG-NAME is non-null.
118
119DOCUMENTATION The help text for this option. It is automatically
120 line-wrapped. If nil, the option is omitted from the help
121 text.
122
123Usually, one won't use make-option, but use the option macro instead."
124 (long-name nil :type (or null string))
125 (tag nil :type t)
126 (negated-tag nil :type t)
127 (short-name nil :type (or null character))
128 (arg-name nil :type (or null string))
129 (arg-optional-p nil :type t)
130 (documentation nil :type (or null string)))
131
132(defstruct (option-parser (:conc-name op-)
133 (:constructor make-option-parser
5fa07b7a
MW
134 (&key
135 ((:args argstmp)
136 (cdr *command-line-strings*))
137 (options *options*)
861345b4 138 (non-option :skip)
139 ((:numericp numeric-p))
140 negated-numeric-p
141 long-only-p
142 &aux
143 (args (cons nil argstmp))
144 (next args)
145 (negated-p (or negated-numeric-p
146 (some
147 #'opt-negated-tag
148 options))))))
149 "An option parser object. Slots:
150
151ARGS The arguments to be parsed. Usually this will be
152 *command-line-strings*.
153
154OPTIONS List of option structures describing the acceptable options.
155
156NON-OPTION Behaviour when encountering a non-option argument. The
157 default is :skip. Allowable values are:
158 :skip -- pretend that it appeared after the option
159 arguments; this is the default behaviour of GNU getopt
160 :stop -- stop parsing options, leaving the remaining
161 command line unparsed
162 :return -- return :non-option and the argument word
163
164NUMERIC-P Non-nil tag (as for options) if numeric options (e.g., -43)
165 are to be allowed. The default is nil. (Anomaly: the
166 keyword for this argument is :numericp.)
167
168NEGATED-NUMERIC-P
169 Non-nil tag (as for options) if numeric options (e.g., -43)
170 can be negated. This is not the same thing as a negative
171 numeric option!
172
173LONG-ONLY-P A misnomer inherited from GNU getopt. Whether to allow
174 long options to begin with a single dash. Short options are
175 still allowed, and may be cuddled as usual. The default is
176 nil."
177 (args nil :type list)
178 (options nil :type list)
179 (non-option :skip :type (or function (member :skip :stop :return)))
180 (next nil :type list)
181 (short-opt nil :type (or null string))
182 (short-opt-index 0 :type fixnum)
183 (short-opt-neg-p nil :type t)
184 (long-only-p nil :type t)
185 (numeric-p nil :type t)
186 (negated-numeric-p nil :type t)
187 (negated-p nil :type t))
188
189(define-condition option-parse-error (error simple-condition)
190 ()
191 (:documentation "Indicates an error found while parsing options. Probably
192not that useful."))
193
194(defun option-parse-error (msg &rest args)
195 "Signal an option-parse-error with the given message and arguments."
196 (error (make-condition 'option-parse-error
197 :format-control msg
198 :format-arguments args)))
199
200(defun option-parse-remainder (op)
201 "Returns the unparsed remainder of the command line."
202 (cdr (op-args op)))
203
204(defun option-parse-next (op)
205 "The main option-parsing function. OP is an option-parser object,
206initialized appropriately. Returns two values, OPT and ARG: OPT is the tag
207of the next option read, and ARG is the argument attached to it, or nil if
208there was no argument. If there are no more options, returns nil twice.
209Options whose TAG is a function aren't returned; instead, the tag function is
210called, with the option argument (or nil) as the only argument. It is safe
211for tag functions to throw out of option-parse-next, if they desparately need
212to. (This is the only way to actually get option-parse-next to return a
213function value, should that be what you want.)
214
215While option-parse-next is running, there is a restart `skip-option' which
216moves on to the next option. Error handlers should use this to resume after
217parsing errors."
218 (loop
219 (labels ((ret (opt &optional arg)
220 (return-from option-parse-next (values opt arg)))
221 (finished ()
222 (setf (op-next op) nil)
223 (ret nil nil))
224 (peek-arg ()
225 (cadr (op-next op)))
226 (more-args-p ()
227 (and (op-next op)
228 (cdr (op-next op))))
229 (skip-arg ()
230 (setf (op-next op) (cdr (op-next op))))
231 (eat-arg ()
232 (setf (cdr (op-next op)) (cddr (op-next op))))
233 (get-arg ()
234 (prog1 (peek-arg) (eat-arg)))
235 (process-option (o name negp &key arg argfunc)
236 (cond ((not (opt-arg-name o))
237 (when arg
238 (option-parse-error
239 "Option `~A' does not accept arguments"
240 name)))
241 (arg)
242 (argfunc
243 (setf arg (funcall argfunc)))
244 ((opt-arg-optional-p o))
245 ((more-args-p)
246 (setf arg (get-arg)))
247 (t
248 (option-parse-error "Option `~A' requires an argument"
249 name)))
250 (let ((how (if negp (opt-negated-tag o) (opt-tag o))))
251 (if (functionp how)
252 (funcall how arg)
253 (ret how arg))))
254 (process-long-option (arg start negp)
255 (when (and (not negp)
256 (op-negated-p op)
257 (> (length arg) (+ start 3))
258 (string= arg "no-"
259 :start1 start :end1 (+ start 3)))
260 (incf start 3)
261 (setf negp t))
262 (let* ((matches nil)
263 (eqpos (position #\= arg :start start))
264 (len (or eqpos (length arg)))
265 (optname (subseq arg 0 len))
266 (len-2 (- len start)))
267 (dolist (o (op-options op))
268 (cond ((or (not (stringp (opt-long-name o)))
269 (and negp (not (opt-negated-tag o)))
270 (< (length (opt-long-name o)) len-2)
271 (string/= optname (opt-long-name o)
272 :start1 start :end2 len-2)))
273 ((= (length (opt-long-name o)) len-2)
274 (setf matches (list o))
275 (return))
276 (t
277 (push o matches))))
278 (cond ((null matches)
279 (option-parse-error "Unknown option `~A'" optname))
280 ((cdr matches)
281 (option-parse-error
282 "~
283Ambiguous long option `~A' -- could be any of:~{~% --~A~}"
284 optname
285 (mapcar #'opt-long-name matches))))
286 (process-option (car matches)
287 optname
288 negp
289 :arg (and eqpos
290 (subseq arg (1+ eqpos)))))))
291 (with-simple-restart (skip-option "Skip this bogus option.")
292 (cond
293 ;;
294 ;; We're embroiled in short options: handle them.
295 ((op-short-opt op)
296 (if (>= (op-short-opt-index op) (length (op-short-opt op)))
297 (setf (op-short-opt op) nil)
298 (let* ((str (op-short-opt op))
299 (i (op-short-opt-index op))
300 (ch (char str i))
301 (negp (op-short-opt-neg-p op))
302 (name (format nil "~C~A" (if negp #\+ #\-) ch))
303 (o (find ch (op-options op) :key #'opt-short-name)))
304 (incf i)
305 (setf (op-short-opt-index op) i)
306 (when (or (not o)
307 (and negp (not (opt-negated-tag o))))
308 (option-parse-error "Unknown option `~A'" name))
309 (process-option o
310 name
311 negp
312 :argfunc
313 (and (< i (length str))
314 (lambda ()
315 (prog1
316 (subseq str i)
317 (setf (op-short-opt op)
318 nil))))))))
319 ;;
320 ;; End of the list. Say we've finished.
321 ((not (more-args-p))
322 (finished))
323 ;;
324 ;; Process the next option.
325 (t
326 (let ((arg (peek-arg)))
327 (cond
328 ;;
329 ;; Non-option. Decide what to do.
330 ((or (<= (length arg) 1)
331 (and (char/= (char arg 0) #\-)
332 (or (char/= (char arg 0) #\+)
333 (not (op-negated-p op)))))
334 (case (op-non-option op)
335 (:skip (skip-arg))
336 (:stop (finished))
337 (:return (eat-arg)
338 (ret :non-option arg))
339 (t (eat-arg)
340 (funcall (op-non-option op) arg))))
341 ;;
342 ;; Double-hyphen. Stop right now.
343 ((string= arg "--")
344 (eat-arg)
345 (finished))
346 ;;
347 ;; Numbers. Check these before long options, since `--43' is
348 ;; not a long option.
349 ((and (op-numeric-p op)
350 (or (char= (char arg 0) #\-)
351 (op-negated-numeric-p op))
352 (or (and (digit-char-p (char arg 1))
353 (every #'digit-char-p (subseq arg 2)))
354 (and (or (char= (char arg 1) #\-)
355 (char= (char arg 1) #\+))
356 (>= (length arg) 3)
357 (digit-char-p (char arg 2))
358 (every #'digit-char-p (subseq arg 3)))))
359 (eat-arg)
360 (let ((negp (char= (char arg 0) #\+))
361 (num (parse-integer arg :start 1)))
362 (when (and negp (eq (op-negated-numeric-p op) :-))
363 (setf num (- num))
364 (setf negp nil))
365 (let ((how (if negp
366 (op-negated-numeric-p op)
367 (op-numeric-p op))))
368 (if (functionp how)
369 (funcall how num)
370 (ret (if negp :negated-numeric :numeric) num)))))
371 ;;
372 ;; Long option. Find the matching option-spec and process
373 ;; it.
374 ((and (char= (char arg 0) #\-)
375 (char= (char arg 1) #\-))
376 (eat-arg)
377 (process-long-option arg 2 nil))
378 ;;
379 ;; Short options. All that's left.
380 (t
381 (eat-arg)
382 (let ((negp (char= (char arg 0) #\+))
383 (ch (char arg 1)))
384 (cond ((and (op-long-only-p op)
385 (not (member ch (op-options op)
386 :key #'opt-short-name)))
387 (process-long-option arg 1 negp))
388 (t
389 (setf (op-short-opt op) arg
390 (op-short-opt-index op) 1
391 (op-short-opt-neg-p op) negp)))))))))))))
392
393(defmacro option-parse-try (&body body)
394 "Report errors encountered while parsing options, and continue struggling
395along. Also establishes a restart `stop-parsing'. Returns t if parsing
396completed successfully, or nil if errors occurred."
397 (with-gensyms (retcode)
398 `(let ((,retcode t))
399 (restart-case
400 (handler-bind
401 ((option-parse-error
402 (lambda (cond)
403 (setf ,retcode nil)
404 (moan "~A" cond)
405 (dolist (rn '(skip-option stop-parsing))
406 (let ((r (find-restart rn)))
407 (when r (invoke-restart r)))))))
408 ,@body)
409 (stop-parsing ()
410 :report "Give up parsing options."
411 (setf ,retcode nil)))
412 ,retcode)))
413
414(defmacro with-unix-error-reporting ((&key) &body body)
415 "Evaluate BODY with errors reported in the standard Unix fashion."
416 (with-gensyms (cond)
417 `(handler-case
418 (progn ,@body)
419 (simple-condition (,cond)
5fa07b7a
MW
420 (apply #'die
421 (simple-condition-format-control ,cond)
422 (simple-condition-format-arguments ,cond)))
861345b4 423 (error (,cond)
424 (die "~A" ,cond)))))
425
02866e07 426;;;--------------------------------------------------------------------------
861345b4 427;;; Standard option handlers.
428
429(defmacro defopthandler (name (var &optional (arg (gensym)))
430 (&rest args)
431 &body body)
432 "Define an option handler function NAME. Option handlers update a
433generalized variable, which may be referred to as VAR in the BODY, based on
434some parameters (the ARGS) and the value of an option-argument named ARG."
435 (let ((func (intern (format nil "OPTHANDLER/~:@(~A~)" name))))
436 `(progn
437 (setf (get ',name 'opthandler) ',func)
438 (defun ,func (,var ,arg ,@args)
439 (with-locatives ,var
440 (declare (ignorable ,arg))
441 ,@body))
5fa07b7a 442 ',name)))
861345b4 443
444(defun parse-c-integer (string &key radix (start 0) end)
445 "Parse STRING, or at least the parts of it between START and END, according
446to the standard C rules. Well, almost: the 0 and 0x prefixes are accepted,
447but so too are 0o (Haskell) and 0b (original); also RADIX_DIGITS is accepted,
448for any radix between 2 and 36. Prefixes are only accepted if RADIX is nil.
449Returns two values: the integer parsed (or nil if there wasn't enough for a
450sensible parse), and the index following the characters of the integer."
451 (unless end (setf end (length string)))
202c91a3
MW
452 (labels ((simple (i r goodp sgn)
453 (multiple-value-bind
454 (a i)
455 (if (and (< i end)
456 (digit-char-p (char string i) r))
457 (parse-integer string
458 :start i :end end
459 :radix r
460 :junk-allowed t)
461 (values nil i))
462 (values (if a (* sgn a) (and goodp 0)) i)))
861345b4 463 (get-radix (i r sgn)
202c91a3 464 (cond (r (simple i r nil sgn))
861345b4 465 ((>= i end) (values nil i))
466 ((and (char= (char string i) #\0)
467 (>= (- end i) 2))
468 (case (char string (1+ i))
202c91a3
MW
469 (#\x (simple (+ i 2) 16 nil sgn))
470 (#\o (simple (+ i 2) 8 nil sgn))
471 (#\b (simple (+ i 2) 2 nil sgn))
472 (t (simple (1+ i) 8 t sgn))))
861345b4 473 (t
474 (multiple-value-bind
202c91a3
MW
475 (r i)
476 (simple i 10 nil +1)
861345b4 477 (cond ((not r) (values nil i))
478 ((and (< i end)
479 (char= (char string i) #\_)
480 (<= 2 r 36))
202c91a3 481 (simple (1+ i) r nil sgn))
861345b4 482 (t
483 (values (* r sgn) i))))))))
484 (cond ((>= start end) (values nil start))
485 ((char= (char string start) #\-)
486 (get-radix (1+ start) radix -1))
487 ((char= (char string start) #\+)
488 (get-radix (1+ start) radix +1))
489 (t
490 (get-radix start radix +1)))))
491
492(defun invoke-option-handler (handler loc arg args)
493 "Call the HANDLER function, giving it LOC to update, the option-argument
494ARG, and the remaining ARGS."
495 (apply (if (functionp handler) handler
496 (fdefinition (get handler 'opthandler)))
497 loc
498 arg
499 args))
500
5fa07b7a
MW
501;;;--------------------------------------------------------------------------
502;;; Built-in option handlers.
503
861345b4 504(defopthandler set (var) (&optional (value t))
505 "Sets VAR to VALUE; defaults to t."
506 (setf var value))
5fa07b7a 507
861345b4 508(defopthandler clear (var) (&optional (value nil))
509 "Sets VAR to VALUE; defaults to nil."
510 (setf var value))
5fa07b7a 511
861345b4 512(defopthandler inc (var) (&optional max (step 1))
513 "Increments VAR by STEP (defaults to 1), but not greater than MAX (default
514nil for no maximum). No errors are signalled."
515 (incf var step)
516 (when (>= var max)
517 (setf var max)))
5fa07b7a 518
861345b4 519(defopthandler dec (var) (&optional min (step 1))
520 "Decrements VAR by STEP (defaults to 1), but not less than MIN (default nil
521for no maximum). No errors are signalled."
522 (decf var step)
523 (when (<= var min)
524 (setf var min)))
5fa07b7a 525
861345b4 526(defopthandler read (var arg) ()
527 "Stores in VAR the Lisp object found by reading the ARG. Evaluation is
528forbidden while reading ARG. If there is an error during reading, an error
529of type option-parse-error is signalled."
530 (handler-case
531 (let ((*read-eval* nil))
532 (multiple-value-bind (x end) (read-from-string arg t)
533 (unless (>= end (length arg))
534 (option-parse-error "Junk at end of argument `~A'" arg))
535 (setf var x)))
536 (error (cond)
537 (option-parse-error (format nil "~A" cond)))))
5fa07b7a 538
861345b4 539(defopthandler int (var arg) (&key radix min max)
540 "Stores in VAR the integer read from the ARG. Integers are parsed
541according to C rules, which is normal in Unix; the RADIX may be nil to allow
542radix prefixes, or an integer between 2 and 36. An option-parse-error is
543signalled if the ARG is not a valid integer, or if it is not between MIN and
544MAX (either of which may be nil if no lower resp. upper bound is wanted)."
545 (multiple-value-bind (v end) (parse-c-integer arg :radix radix)
546 (unless (and v (>= end (length arg)))
547 (option-parse-error "Bad integer `~A'" arg))
548 (when (or (and min (< v min))
549 (and max (> v max)))
550 (option-parse-error
551 "Integer ~A out of range (must have ~@[~D <= ~]x~@[ <= ~D~])"
552 arg min max))
553 (setf var v)))
5fa07b7a 554
861345b4 555(defopthandler string (var arg) ()
556 "Stores ARG in VAR, just as it is."
557 (setf var arg))
5fa07b7a 558
861345b4 559(defopthandler keyword (var arg) (&rest valid)
560 (if (null valid)
561 (setf var (intern (string-upcase arg) :keyword))
562 (let ((matches nil)
563 (guess (string-upcase arg))
564 (len (length arg)))
565 (dolist (k valid)
566 (let* ((kn (symbol-name k))
567 (klen (length kn)))
568 (cond ((string= kn guess)
569 (setf matches (list k))
570 (return))
571 ((and (< len klen)
572 (string= guess kn :end2 len))
573 (push k matches)))))
574 (case (length matches)
575 (0 (option-parse-error "Argument `~A' invalid: must be one of:~
576 ~{~%~8T~(~A~)~}"
577 arg valid))
578 (1 (setf var (car matches)))
579 (t (option-parse-error "Argument `~A' ambiguous: may be any of:~
580 ~{~%~8T~(~A~)~}"
581 arg matches))))))
5fa07b7a 582
861345b4 583(defopthandler list (var arg) (&optional handler &rest handler-args)
584 "Collect ARGs in a list at VAR. ARGs are translated by the HANDLER first,
585if specified. If not, it's as if you asked for `string'."
586 (when handler
587 (invoke-option-handler handler (locf arg) arg handler-args))
588 (setf var (nconc var (list arg))))
589
5fa07b7a
MW
590;;;--------------------------------------------------------------------------
591;;; Option descriptions.
592
593(defmacro defoptmacro (name args &body body)
594 "Defines an option macro NAME. Option macros should produce a list of
595expressions producing one option structure each."
596 `(progn
597 (setf (get ',name 'optmacro) (lambda ,args ,@body))
598 ',name))
599
861345b4 600(compile-time-defun parse-option-form (form)
601 "Does the heavy lifting for parsing an option form. See the docstring for
602the `option' macro for details of the syntax."
603 (flet ((doc (form)
604 (cond ((stringp form) form)
605 ((null (cdr form)) (car form))
606 (t `(format nil ,@form))))
607 (docp (form)
608 (or (stringp form)
609 (and (consp form)
610 (stringp (car form))))))
5fa07b7a
MW
611 (cond ((stringp form)
612 `(%make-option :documentation ,form))
613 ((not (listp form))
614 (error "option form must be string or list"))
615 ((and (docp (car form)) (null (cdr form)))
616 `(%make-option :documentation ,(doc (car form))))
617 (t
618 (let (long-name short-name
619 arg-name arg-optional-p
620 tag negated-tag
621 doc)
622 (dolist (f form)
623 (cond ((and (or (not tag) (not negated-tag))
624 (or (keywordp f)
625 (and (consp f)
626 (member (car f) '(lambda function)))))
627 (if tag
628 (setf negated-tag f)
629 (setf tag f)))
630 ((and (not long-name)
631 (or (rationalp f)
632 (symbolp f)
633 (stringp f)))
634 (setf long-name (if (stringp f) f
635 (format nil "~(~A~)" f))))
636 ((and (not short-name)
637 (characterp f))
638 (setf short-name f))
639 ((and (not doc)
640 (docp f))
641 (setf doc (doc f)))
642 ((and (consp f) (symbolp (car f)))
643 (case (car f)
644 (:arg (setf arg-name (cadr f)))
645 (:opt-arg (setf arg-name (cadr f))
646 (setf arg-optional-p t))
647 (:doc (setf doc (doc (cdr f))))
648 (t (let ((handler (get (car f) 'opthandler)))
649 (unless handler
650 (error "No handler `~S' defined." (car f)))
651 (let* ((var (cadr f))
652 (arg (gensym))
653 (thunk `#'(lambda (,arg)
654 (,handler (locf ,var)
655 ,arg
656 ,@(cddr f)))))
657 (if tag
658 (setf negated-tag thunk)
659 (setf tag thunk)))))))
660 (t
661 (error "Unexpected thing ~S in option form." f))))
662 `(make-option ,long-name ,short-name ,arg-name
663 ,@(and arg-optional-p `(:arg-optional-p t))
664 ,@(and tag `(:tag ,tag))
665 ,@(and negated-tag `(:negated-tag ,negated-tag))
666 ,@(and doc `(:documentation ,doc))))))))
861345b4 667
668(defmacro options (&rest optlist)
669 "More convenient way of initializing options. The OPTLIST is a list of
5fa07b7a
MW
670OPTFORMS. Each OPTFORM is one of the following:
671
672 STRING
673 A banner to print.
674
675 SYMBOL or (SYMBOL STUFF...)
676 If SYMBOL is an optform macro, the result of invoking it.
677
678 (...)
679 A full option-form. See below.
680
681Full option-forms are as follows.
861345b4 682
683 KEYWORD or FUNCTION
684 If no TAG is set yet, then as a TAG; otherwise as the NEGATED-TAG.
685
686 STRING (or SYMBOL or RATIONAL)
687 If no LONG-NAME seen yet, then the LONG-NAME. For symbols and rationals,
688 the item is converted to a string and squashed to lower-case.
689
690 CHARACTER
691 The SHORT-NAME.
692
693 STRING or (STRING STUFF...)
5fa07b7a
MW
694 If no DOCUMENTATION set yet, then the DOCUMENTATION string, as for
695 (:DOC STRING STUFF...)
696
697 (:DOC STRING STUFF...)
698 The DOCUMENATION string. With no STUFF, STRING is used as is; otherwise
699 the documentation string is computed by (format nil STRING STUFF...).
861345b4 700
701 (:ARG NAME)
702 Set the ARG-NAME.
703
704 (:OPT-ARG NAME)
705 Set the ARG-NAME, and also set ARG-OPTIONAL-P.
706
707 (HANDLER VAR ARGS...)
708 If no TAG is set yet, attach the HANDLER to this option, giving it ARGS.
709 Otherwise, set the NEGATED-TAG."
5fa07b7a
MW
710 `(list ,@(mapcan (lambda (form)
711 (multiple-value-bind
712 (sym args)
713 (cond ((symbolp form) (values form nil))
714 ((and (consp form) (symbolp (car form)))
715 (values (car form) (cdr form)))
716 (t (values nil nil)))
717 (let ((macro (and sym (get sym 'optmacro))))
718 (if macro
719 (apply macro args)
720 (list (parse-option-form form))))))
861345b4 721 optlist)))
722
02866e07 723;;;--------------------------------------------------------------------------
5fa07b7a 724;;; Support stuff for help and usage messages.
861345b4 725
726(defun print-text (string
727 &optional
728 (stream *standard-output*)
729 &key
730 (start 0)
731 (end nil))
732 "Prints STRING to a pretty-printed STREAM, breaking it at whitespace and
733newlines in the obvious way. Stuff between square brackets is not broken:
734this makes usage messages work better."
735 (let ((i start)
736 (nest 0)
737 (splitp nil))
738 (flet ((emit ()
739 (write-string string stream :start start :end i)
740 (setf start i)))
741 (unless end
742 (setf end (length string)))
743 (loop
744 (unless (< i end)
745 (emit)
746 (return))
747 (let ((ch (char string i)))
748 (cond ((char= ch #\newline)
749 (emit)
750 (incf start)
751 (pprint-newline :mandatory stream))
752 ((whitespace-char-p ch)
753 (when (zerop nest)
754 (setf splitp t)))
755 (t
756 (when splitp
757 (emit)
758 (pprint-newline :fill stream))
759 (setf splitp nil)
760 (case ch
761 (#\[ (incf nest))
762 (#\] (when (plusp nest) (decf nest))))))
763 (incf i))))))
764
765(defun simple-usage (opts &optional mandatory-args)
766 "Build a simple usage list from a list of options, and (optionally)
767mandatory argument names."
768 (let (short-simple long-simple short-arg long-arg)
769 (dolist (o opts)
770 (cond ((not (and (opt-documentation o)
771 (opt-long-name o))))
772 ((and (opt-short-name o) (opt-arg-name o))
773 (push o short-arg))
774 ((opt-short-name o)
775 (push o short-simple))
776 ((opt-arg-name o)
777 (push o long-arg))
778 (t
779 (push o long-simple))))
780 (list
781 (nconc (and short-simple
782 (list (format nil "[-~{~C~}]"
783 (sort (mapcar #'opt-short-name short-simple)
784 #'char<))))
785 (and long-simple
786 (mapcar (lambda (o)
787 (format nil "[--~A]" (opt-long-name o)))
788 (sort long-simple #'string< :key #'opt-long-name)))
789 (and short-arg
790 (mapcar (lambda (o)
791 (format nil "~:[[-~C ~A]~;[-~C[~A]]~]"
792 (opt-arg-optional-p o)
793 (opt-short-name o)
794 (opt-arg-name o)))
795 (sort short-arg #'char-lessp
796 :key #'opt-short-name)))
797 (and long-arg
798 (mapcar (lambda (o)
799 (format nil "~:[[--~A ~A]~;[--~A[=~A]]~]"
800 (opt-arg-optional-p o)
801 (opt-long-name o)
802 (opt-arg-name o)))
803 (sort long-arg #'string-lessp
804 :key #'opt-long-name)))
805 (listify mandatory-args)))))
806
807(defun show-usage (prog usage &optional (stream *standard-output*))
5fa07b7a 808 "Basic usage-showing function. PROG is the program name, probably from
861345b4 809*command-line-strings*. USAGE is a list of possible usages of the program,
810each of which is a list of items to be supplied by the user. In simple
811cases, a single string is sufficient."
812 (pprint-logical-block (stream nil :prefix "Usage: ")
813 (dolist (u (listify usage))
814 (pprint-logical-block (stream nil :prefix (format nil "~A " prog))
815 (format stream "~{~A ~:_~}" (listify u)))
816 (pprint-newline :mandatory stream))))
817
818(defun show-help (prog ver usage opts &optional (stream *standard-output*))
819 "Basic help-showing function. PROG is the program name, probably from
820*command-line-strings*. VER is the program's version number. USAGE is a
821list of the possible usages of the program, each of which may be a list of
822items to be supplied. OPTS is the list of supported options, as provided to
823the options parser. STREAM is the stream to write on."
824 (format stream "~A, version ~A~2%" prog ver)
825 (show-usage prog usage stream)
826 (terpri stream)
827 (let (newlinep)
828 (dolist (o opts)
829 (let ((doc (opt-documentation o)))
830 (cond ((not o))
831 ((not (opt-long-name o))
832 (when newlinep
833 (terpri stream)
834 (setf newlinep nil))
835 (pprint-logical-block (stream nil)
836 (print-text doc stream))
837 (terpri stream))
838 (t
839 (setf newlinep t)
840 (pprint-logical-block (stream nil :prefix " ")
841 (pprint-indent :block 30 stream)
842 (format stream "~:[ ~;-~:*~C,~] --~A"
843 (opt-short-name o)
844 (opt-long-name o))
845 (when (opt-arg-name o)
846 (format stream "~:[=~A~;[=~A]~]"
847 (opt-arg-optional-p o)
848 (opt-arg-name o)))
849 (write-string " " stream)
850 (pprint-tab :line 30 1 stream)
851 (print-text doc stream))
852 (terpri stream)))))))
853
854(defun sanity-check-option-list (opts)
855 "Check the option list OPTS for basic sanity. Reused short and long option
856names are diagnosed. Maybe other problems will be reported later. Returns a
857list of warning strings."
858 (let ((problems nil)
859 (longs (make-hash-table :test #'equal))
860 (shorts (make-hash-table)))
861 (flet ((problem (msg &rest args)
862 (push (apply #'format nil msg args) problems)))
863 (dolist (o opts)
864 (push o (gethash (opt-long-name o) longs))
865 (push o (gethash (opt-short-name o) shorts)))
866 (maphash (lambda (k v)
867 (when (and k (cdr v))
868 (problem "Long name `--~A' reused in ~S" k v)))
869 longs)
870 (maphash (lambda (k v)
871 (when (and k (cdr v))
872 (problem "Short name `-~C' reused in ~S" k v)))
873 shorts)
874 problems)))
875
5fa07b7a
MW
876;;;--------------------------------------------------------------------------
877;;; Full program descriptions.
878
879(defvar *help*)
880(defvar *version*)
881(defvar *usage*)
882
883(defun opt-help (arg)
884 (declare (ignore arg))
885 (show-help *program-name* *version* *usage* *options*)
886 (typecase *help*
887 (string (terpri) (write-string *help*))
888 ((or function symbol) (terpri) (funcall *help*)))
889 (format t "~&")
890 (exit 0))
891
892(defun opt-version (arg)
893 (declare (ignore arg))
894 (format t "~A, version ~A~%" *program-name* *version*)
895 (exit 0))
896
897(defun do-usage (&optional (stream *standard-output*))
898 (show-usage *program-name* *usage* stream))
899
900(defun die-usage ()
901 (do-usage *error-output*)
902 (exit 1))
903
904(defun opt-usage (arg)
905 (declare (ignore arg))
906 (do-usage)
907 (exit 0))
908
909(defoptmacro help-opts (&key (short-help #\h)
910 (short-version #\v)
911 (short-usage #\u))
912 (mapcar #'parse-option-form
913 `("Help options"
914 (,@(and short-help (list short-help))
915 "help"
916 #'opt-help
917 "Show this help message.")
918 (,@(and short-version (list short-version))
919 "version"
920 #'opt-version
921 ("Show ~A's version number." *program-name*))
922 (,@(and short-usage (list short-usage))
923 "usage"
924 #'opt-usage
925 ("Show a very brief usage summary for ~A." *program-name*)))))
926
927(defun define-program (&key
928 program-name
929 help
930 version
931 usage full-usage
932 options)
933 "Sets up all the required things a program needs to have to parse options
934and respond to them properly."
935 (when program-name (setf *program-name* program-name))
936 (when help (setf *help* help))
937 (when version (setf *version* version))
938 (when options (setf *options* options))
939 (cond ((and usage full-usage) (error "conflicting options"))
940 (usage (setf *usage* (simple-usage *options* usage)))
941 (full-usage (setf *usage* full-usage))))
942
943(defmacro do-options ((&key (parser '(make-option-parser))) &body clauses)
944 (with-gensyms (topt targ tparser)
945 (flet ((frob (clause)
946 (destructuring-bind
947 (case (&optional arg) &rest forms)
948 clause
949 (and case
950 (list `(,case ,@(if arg
951 `(let ((,arg ,targ)) ,@forms)
952 forms)))))))
953 `(let ((,tparser ,parser))
954 (loop
955 (multiple-value-bind (,topt ,targ) (option-parse-next ,tparser)
956 (declare (ignorable ,targ))
957 (unless ,topt (return))
958 (case ,topt
959 ,@(mapcan #'frob clauses))))
960 ,@(let ((tail (find nil clauses :key #'car)))
961 (and tail
962 (destructuring-bind
963 ((&optional arg) &rest forms)
964 (cdr tail)
965 (list (if arg
966 `(let ((,arg (option-parse-remainder
967 ,tparser)))
968 ,@forms)
969 forms)))))))))
970
861345b4 971;;;----- That's all, folks --------------------------------------------------