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