getopt-long.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. ;;; Copyright (C) 1998, 2001, 2006, 2009, 2011 Free Software Foundation, Inc.
  2. ;;;
  3. ;;;; This library is free software; you can redistribute it and/or
  4. ;;;; modify it under the terms of the GNU Lesser General Public
  5. ;;;; License as published by the Free Software Foundation; either
  6. ;;;; version 3 of the License, or (at your option) any later version.
  7. ;;;;
  8. ;;;; This library is distributed in the hope that it will be useful,
  9. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;;;; Lesser General Public License for more details.
  12. ;;;;
  13. ;;;; You should have received a copy of the GNU Lesser General Public
  14. ;;;; License along with this library; if not, write to the Free Software
  15. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ;;; Author: Russ McManus (rewritten by Thien-Thi Nguyen)
  17. ;;; Commentary:
  18. ;;; This module implements some complex command line option parsing, in
  19. ;;; the spirit of the GNU C library function `getopt_long'. Both long
  20. ;;; and short options are supported.
  21. ;;;
  22. ;;; The theory is that people should be able to constrain the set of
  23. ;;; options they want to process using a grammar, rather than some arbitrary
  24. ;;; structure. The grammar makes the option descriptions easy to read.
  25. ;;;
  26. ;;; `getopt-long' is a procedure for parsing command-line arguments in a
  27. ;;; manner consistent with other GNU programs. `option-ref' is a procedure
  28. ;;; that facilitates processing of the `getopt-long' return value.
  29. ;;; (getopt-long ARGS GRAMMAR)
  30. ;;; Parse the arguments ARGS according to the argument list grammar GRAMMAR.
  31. ;;;
  32. ;;; ARGS should be a list of strings. Its first element should be the
  33. ;;; name of the program; subsequent elements should be the arguments
  34. ;;; that were passed to the program on the command line. The
  35. ;;; `program-arguments' procedure returns a list of this form.
  36. ;;;
  37. ;;; GRAMMAR is a list of the form:
  38. ;;; ((OPTION (PROPERTY VALUE) ...) ...)
  39. ;;;
  40. ;;; Each OPTION should be a symbol. `getopt-long' will accept a
  41. ;;; command-line option named `--OPTION'.
  42. ;;; Each option can have the following (PROPERTY VALUE) pairs:
  43. ;;;
  44. ;;; (single-char CHAR) --- Accept `-CHAR' as a single-character
  45. ;;; equivalent to `--OPTION'. This is how to specify traditional
  46. ;;; Unix-style flags.
  47. ;;; (required? BOOL) --- If BOOL is true, the option is required.
  48. ;;; getopt-long will raise an error if it is not found in ARGS.
  49. ;;; (value BOOL) --- If BOOL is #t, the option accepts a value; if
  50. ;;; it is #f, it does not; and if it is the symbol
  51. ;;; `optional', the option may appear in ARGS with or
  52. ;;; without a value.
  53. ;;; (predicate FUNC) --- If the option accepts a value (i.e. you
  54. ;;; specified `(value #t)' for this option), then getopt
  55. ;;; will apply FUNC to the value, and throw an exception
  56. ;;; if it returns #f. FUNC should be a procedure which
  57. ;;; accepts a string and returns a boolean value; you may
  58. ;;; need to use quasiquotes to get it into GRAMMAR.
  59. ;;;
  60. ;;; The (PROPERTY VALUE) pairs may occur in any order, but each
  61. ;;; property may occur only once. By default, options do not have
  62. ;;; single-character equivalents, are not required, and do not take
  63. ;;; values.
  64. ;;;
  65. ;;; In ARGS, single-character options may be combined, in the usual
  66. ;;; Unix fashion: ("-x" "-y") is equivalent to ("-xy"). If an option
  67. ;;; accepts values, then it must be the last option in the
  68. ;;; combination; the value is the next argument. So, for example, using
  69. ;;; the following grammar:
  70. ;;; ((apples (single-char #\a))
  71. ;;; (blimps (single-char #\b) (value #t))
  72. ;;; (catalexis (single-char #\c) (value #t)))
  73. ;;; the following argument lists would be acceptable:
  74. ;;; ("-a" "-b" "bang" "-c" "couth") ("bang" and "couth" are the values
  75. ;;; for "blimps" and "catalexis")
  76. ;;; ("-ab" "bang" "-c" "couth") (same)
  77. ;;; ("-ac" "couth" "-b" "bang") (same)
  78. ;;; ("-abc" "couth" "bang") (an error, since `-b' is not the
  79. ;;; last option in its combination)
  80. ;;;
  81. ;;; If an option's value is optional, then `getopt-long' decides
  82. ;;; whether it has a value by looking at what follows it in ARGS. If
  83. ;;; the next element is does not appear to be an option itself, then
  84. ;;; that element is the option's value.
  85. ;;;
  86. ;;; The value of a long option can appear as the next element in ARGS,
  87. ;;; or it can follow the option name, separated by an `=' character.
  88. ;;; Thus, using the same grammar as above, the following argument lists
  89. ;;; are equivalent:
  90. ;;; ("--apples" "Braeburn" "--blimps" "Goodyear")
  91. ;;; ("--apples=Braeburn" "--blimps" "Goodyear")
  92. ;;; ("--blimps" "Goodyear" "--apples=Braeburn")
  93. ;;;
  94. ;;; If the option "--" appears in ARGS, argument parsing stops there;
  95. ;;; subsequent arguments are returned as ordinary arguments, even if
  96. ;;; they resemble options. So, in the argument list:
  97. ;;; ("--apples" "Granny Smith" "--" "--blimp" "Goodyear")
  98. ;;; `getopt-long' will recognize the `apples' option as having the
  99. ;;; value "Granny Smith", but it will not recognize the `blimp'
  100. ;;; option; it will return the strings "--blimp" and "Goodyear" as
  101. ;;; ordinary argument strings.
  102. ;;;
  103. ;;; The `getopt-long' function returns the parsed argument list as an
  104. ;;; assocation list, mapping option names --- the symbols from GRAMMAR
  105. ;;; --- onto their values, or #t if the option does not accept a value.
  106. ;;; Unused options do not appear in the alist.
  107. ;;;
  108. ;;; All arguments that are not the value of any option are returned
  109. ;;; as a list, associated with the empty list.
  110. ;;;
  111. ;;; `getopt-long' throws an exception if:
  112. ;;; - it finds an unrecognized property in GRAMMAR
  113. ;;; - the value of the `single-char' property is not a character
  114. ;;; - it finds an unrecognized option in ARGS
  115. ;;; - a required option is omitted
  116. ;;; - an option that requires an argument doesn't get one
  117. ;;; - an option that doesn't accept an argument does get one (this can
  118. ;;; only happen using the long option `--opt=value' syntax)
  119. ;;; - an option predicate fails
  120. ;;;
  121. ;;; So, for example:
  122. ;;;
  123. ;;; (define grammar
  124. ;;; `((lockfile-dir (required? #t)
  125. ;;; (value #t)
  126. ;;; (single-char #\k)
  127. ;;; (predicate ,file-is-directory?))
  128. ;;; (verbose (required? #f)
  129. ;;; (single-char #\v)
  130. ;;; (value #f))
  131. ;;; (x-includes (single-char #\x))
  132. ;;; (rnet-server (single-char #\y)
  133. ;;; (predicate ,string?))))
  134. ;;;
  135. ;;; (getopt-long '("my-prog" "-vk" "/tmp" "foo1" "--x-includes=/usr/include"
  136. ;;; "--rnet-server=lamprod" "--" "-fred" "foo2" "foo3")
  137. ;;; grammar)
  138. ;;; => ((() "foo1" "-fred" "foo2" "foo3")
  139. ;;; (rnet-server . "lamprod")
  140. ;;; (x-includes . "/usr/include")
  141. ;;; (lockfile-dir . "/tmp")
  142. ;;; (verbose . #t))
  143. ;;; (option-ref OPTIONS KEY DEFAULT)
  144. ;;; Return value in alist OPTIONS using KEY, a symbol; or DEFAULT if not
  145. ;;; found. The value is either a string or `#t'.
  146. ;;;
  147. ;;; For example, using the `getopt-long' return value from above:
  148. ;;;
  149. ;;; (option-ref (getopt-long ...) 'x-includes 42) => "/usr/include"
  150. ;;; (option-ref (getopt-long ...) 'not-a-key! 31) => 31
  151. ;;; Code:
  152. (define-module (ice-9 getopt-long)
  153. #:use-module ((ice-9 common-list) #:select (remove-if-not))
  154. #:use-module (srfi srfi-9)
  155. #:use-module (ice-9 match)
  156. #:use-module (ice-9 regex)
  157. #:use-module (ice-9 optargs)
  158. #:export (getopt-long option-ref))
  159. (define %program-name (make-fluid "guile"))
  160. (define (program-name)
  161. (fluid-ref %program-name))
  162. (define (fatal-error fmt . args)
  163. (format (current-error-port) "~a: " (program-name))
  164. (apply format (current-error-port) fmt args)
  165. (newline (current-error-port))
  166. (exit 1))
  167. (define-record-type option-spec
  168. (%make-option-spec name required? option-spec->single-char predicate value-policy)
  169. option-spec?
  170. (name
  171. option-spec->name set-option-spec-name!)
  172. (required?
  173. option-spec->required? set-option-spec-required?!)
  174. (option-spec->single-char
  175. option-spec->single-char set-option-spec-single-char!)
  176. (predicate
  177. option-spec->predicate set-option-spec-predicate!)
  178. (value-policy
  179. option-spec->value-policy set-option-spec-value-policy!))
  180. (define (make-option-spec name)
  181. (%make-option-spec name #f #f #f #f))
  182. (define (parse-option-spec desc)
  183. (let ((spec (make-option-spec (symbol->string (car desc)))))
  184. (for-each (match-lambda
  185. (('required? val)
  186. (set-option-spec-required?! spec val))
  187. (('value val)
  188. (set-option-spec-value-policy! spec val))
  189. (('single-char val)
  190. (or (char? val)
  191. (error "`single-char' value must be a char!"))
  192. (set-option-spec-single-char! spec val))
  193. (('predicate pred)
  194. (set-option-spec-predicate!
  195. spec (lambda (name val)
  196. (or (not val)
  197. (pred val)
  198. (fatal-error "option predicate failed: --~a"
  199. name)))))
  200. ((prop val)
  201. (error "invalid getopt-long option property:" prop)))
  202. (cdr desc))
  203. spec))
  204. (define (split-arg-list argument-list)
  205. ;; Scan ARGUMENT-LIST for "--" and return (BEFORE-LS . AFTER-LS).
  206. ;; Discard the "--". If no "--" is found, AFTER-LS is empty.
  207. (let loop ((yes '()) (no argument-list))
  208. (cond ((null? no) (cons (reverse yes) no))
  209. ((string=? "--" (car no)) (cons (reverse yes) (cdr no)))
  210. (else (loop (cons (car no) yes) (cdr no))))))
  211. (define short-opt-rx (make-regexp "^-([a-zA-Z]+)(.*)"))
  212. (define long-opt-no-value-rx (make-regexp "^--([^=]+)$"))
  213. (define long-opt-with-value-rx (make-regexp "^--([^=]+)=(.*)"))
  214. (define (looks-like-an-option string)
  215. (or (regexp-exec short-opt-rx string)
  216. (regexp-exec long-opt-with-value-rx string)
  217. (regexp-exec long-opt-no-value-rx string)))
  218. (define (process-options specs argument-ls stop-at-first-non-option)
  219. ;; Use SPECS to scan ARGUMENT-LS; return (FOUND . ETC).
  220. ;; FOUND is an unordered list of option specs for found options, while ETC
  221. ;; is an order-maintained list of elements in ARGUMENT-LS that are neither
  222. ;; options nor their values.
  223. (let ((idx (map (lambda (spec)
  224. (cons (option-spec->name spec) spec))
  225. specs))
  226. (sc-idx (map (lambda (spec)
  227. (cons (make-string 1 (option-spec->single-char spec))
  228. spec))
  229. (remove-if-not option-spec->single-char specs))))
  230. (let loop ((unclumped 0) (argument-ls argument-ls) (found '()) (etc '()))
  231. (define (eat! spec ls)
  232. (cond
  233. ((eq? 'optional (option-spec->value-policy spec))
  234. (if (or (null? ls)
  235. (looks-like-an-option (car ls)))
  236. (loop (- unclumped 1) ls (acons spec #t found) etc)
  237. (loop (- unclumped 2) (cdr ls) (acons spec (car ls) found) etc)))
  238. ((eq? #t (option-spec->value-policy spec))
  239. (if (or (null? ls)
  240. (looks-like-an-option (car ls)))
  241. (fatal-error "option must be specified with argument: --~a"
  242. (option-spec->name spec))
  243. (loop (- unclumped 2) (cdr ls) (acons spec (car ls) found) etc)))
  244. (else
  245. (loop (- unclumped 1) ls (acons spec #t found) etc))))
  246. (match argument-ls
  247. (()
  248. (cons found (reverse etc)))
  249. ((opt . rest)
  250. (cond
  251. ((regexp-exec short-opt-rx opt)
  252. => (lambda (match)
  253. (if (> unclumped 0)
  254. ;; Next option is known not to be clumped.
  255. (let* ((c (match:substring match 1))
  256. (spec (or (assoc-ref sc-idx c)
  257. (fatal-error "no such option: -~a" c))))
  258. (eat! spec rest))
  259. ;; Expand a clumped group of short options.
  260. (let* ((extra (match:substring match 2))
  261. (unclumped-opts
  262. (append (map (lambda (c)
  263. (string-append "-" (make-string 1 c)))
  264. (string->list
  265. (match:substring match 1)))
  266. (if (string=? "" extra) '() (list extra)))))
  267. (loop (length unclumped-opts)
  268. (append unclumped-opts rest)
  269. found
  270. etc)))))
  271. ((regexp-exec long-opt-no-value-rx opt)
  272. => (lambda (match)
  273. (let* ((opt (match:substring match 1))
  274. (spec (or (assoc-ref idx opt)
  275. (fatal-error "no such option: --~a" opt))))
  276. (eat! spec rest))))
  277. ((regexp-exec long-opt-with-value-rx opt)
  278. => (lambda (match)
  279. (let* ((opt (match:substring match 1))
  280. (spec (or (assoc-ref idx opt)
  281. (fatal-error "no such option: --~a" opt))))
  282. (if (option-spec->value-policy spec)
  283. (eat! spec (cons (match:substring match 2) rest))
  284. (fatal-error "option does not support argument: --~a"
  285. opt)))))
  286. ((and stop-at-first-non-option
  287. (<= unclumped 0))
  288. (cons found (append (reverse etc) argument-ls)))
  289. (else
  290. (loop (- unclumped 1) rest found (cons opt etc)))))))))
  291. (define* (getopt-long program-arguments option-desc-list
  292. #:key stop-at-first-non-option)
  293. "Process options, handling both long and short options, similar to
  294. the glibc function 'getopt_long'. PROGRAM-ARGUMENTS should be a value
  295. similar to what (program-arguments) returns. OPTION-DESC-LIST is a
  296. list of option descriptions. Each option description must satisfy the
  297. following grammar:
  298. <option-spec> :: (<name> . <attribute-ls>)
  299. <attribute-ls> :: (<attribute> . <attribute-ls>)
  300. | ()
  301. <attribute> :: <required-attribute>
  302. | <arg-required-attribute>
  303. | <single-char-attribute>
  304. | <predicate-attribute>
  305. | <value-attribute>
  306. <required-attribute> :: (required? <boolean>)
  307. <single-char-attribute> :: (single-char <char>)
  308. <value-attribute> :: (value #t)
  309. (value #f)
  310. (value optional)
  311. <predicate-attribute> :: (predicate <1-ary-function>)
  312. The procedure returns an alist of option names and values. Each
  313. option name is a symbol. The option value will be '#t' if no value
  314. was specified. There is a special item in the returned alist with a
  315. key of the empty list, (): the list of arguments that are not options
  316. or option values.
  317. By default, options are not required, and option values are not
  318. required. By default, single character equivalents are not supported;
  319. if you want to allow the user to use single character options, you need
  320. to add a `single-char' clause to the option description."
  321. (with-fluids ((%program-name (car program-arguments)))
  322. (let* ((specifications (map parse-option-spec option-desc-list))
  323. (pair (split-arg-list (cdr program-arguments)))
  324. (split-ls (car pair))
  325. (non-split-ls (cdr pair))
  326. (found/etc (process-options specifications split-ls
  327. stop-at-first-non-option))
  328. (found (car found/etc))
  329. (rest-ls (append (cdr found/etc) non-split-ls)))
  330. (for-each (lambda (spec)
  331. (let ((name (option-spec->name spec))
  332. (val (assq-ref found spec)))
  333. (and (option-spec->required? spec)
  334. (or val
  335. (fatal-error "option must be specified: --~a"
  336. name)))
  337. (let ((pred (option-spec->predicate spec)))
  338. (and pred (pred name val)))))
  339. specifications)
  340. (for-each (lambda (spec+val)
  341. (set-car! spec+val
  342. (string->symbol (option-spec->name (car spec+val)))))
  343. found)
  344. (cons (cons '() rest-ls) found))))
  345. (define (option-ref options key default)
  346. "Return value in alist OPTIONS using KEY, a symbol; or DEFAULT if not found.
  347. The value is either a string or `#t'."
  348. (or (assq-ref options key) default))
  349. ;;; getopt-long.scm ends here