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