1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-19 10:10:23 +02:00
Touch up docstrings.
Augment commentary.
This commit is contained in:
Thien-Thi Nguyen 2001-08-12 19:21:59 +00:00
parent 24bf1e2f6b
commit b817e7ef1c

View file

@ -1,6 +1,3 @@
;;; Author: Russ McManus
;;; $Id: getopt-long.scm,v 1.4.2.1 2001-08-02 10:52:37 ttn Exp $
;;;
;;; Copyright (C) 1998, 2001 Free Software Foundation, Inc. ;;; Copyright (C) 1998, 2001 Free Software Foundation, Inc.
;;; ;;;
;;; This program is free software; you can redistribute it and/or modify ;;; This program is free software; you can redistribute it and/or modify
@ -41,19 +38,21 @@
;;; whether to permit this exception to apply to your modifications. ;;; whether to permit this exception to apply to your modifications.
;;; If you do not wish that, delete this exception notice. ;;; If you do not wish that, delete this exception notice.
;;; Author: Russ McManus (rewritten by Thien-Thi Nguyen)
;;; Commentary: ;;; Commentary:
;;; This module implements some complex command line option parsing, in ;;; This module implements some complex command line option parsing, in
;;; the spirit of the GNU C library function 'getopt_long'. Both long ;;; the spirit of the GNU C library function `getopt_long'. Both long
;;; and short options are supported. ;;; and short options are supported.
;;; ;;;
;;; The theory is that people should be able to constrain the set of ;;; The theory is that people should be able to constrain the set of
;;; options they want to process using a grammar, rather than some arbitrary ;;; options they want to process using a grammar, rather than some arbitrary
;;; structure. The grammar makes the option descriptions easy to read. ;;; structure. The grammar makes the option descriptions easy to read.
;;; ;;;
;;; `getopt-long' is a procedure for parsing command-line arguments in a
;;; getopt-long is a function for parsing command-line arguments in a ;;; manner consistent with other GNU programs. `option-ref' is a procedure
;;; manner consistent with other GNU programs. ;;; that facilitates processing of the `getopt-long' return value.
;;; (getopt-long ARGS GRAMMAR) ;;; (getopt-long ARGS GRAMMAR)
;;; Parse the arguments ARGS according to the argument list grammar GRAMMAR. ;;; Parse the arguments ARGS according to the argument list grammar GRAMMAR.
@ -109,8 +108,8 @@
;;; ;;;
;;; If an option's value is optional, then `getopt-long' decides ;;; If an option's value is optional, then `getopt-long' decides
;;; whether it has a value by looking at what follows it in ARGS. If ;;; whether it has a value by looking at what follows it in ARGS. If
;;; the next element is a string, and it does not appear to be an ;;; the next element is does not appear to be an option itself, then
;;; option itself, then that string is the option's value. ;;; that element is the option's value.
;;; ;;;
;;; The value of a long option can appear as the next element in ARGS, ;;; The value of a long option can appear as the next element in ARGS,
;;; or it can follow the option name, separated by an `=' character. ;;; or it can follow the option name, separated by an `=' character.
@ -138,6 +137,8 @@
;;; as a list, associated with the empty list. ;;; as a list, associated with the empty list.
;;; ;;;
;;; `getopt-long' throws an exception if: ;;; `getopt-long' throws an exception if:
;;; - it finds an unrecognized property in GRAMMAR
;;; - the value of the `single-char' property is not a character
;;; - it finds an unrecognized option in ARGS ;;; - it finds an unrecognized option in ARGS
;;; - a required option is omitted ;;; - a required option is omitted
;;; - an option that requires an argument doesn't get one ;;; - an option that requires an argument doesn't get one
@ -168,515 +169,200 @@
;;; (lockfile-dir . "/tmp") ;;; (lockfile-dir . "/tmp")
;;; (verbose . #t)) ;;; (verbose . #t))
;;; (option-ref OPTIONS KEY DEFAULT)
;;; Return value in alist OPTIONS using KEY, a symbol; or DEFAULT if not
;;; found. The value is either a string or `#t'.
;;;
;;; For example, using the `getopt-long' return value from above:
;;;
;;; (option-ref (getopt-long ...) 'x-includes 42) => "/usr/include"
;;; (option-ref (getopt-long ...) 'not-a-key! 31) => 31
;;; Code: ;;; Code:
(define-module (ice-9 getopt-long) (define-module (ice-9 getopt-long)
:use-module (ice-9 common-list)) :use-module ((ice-9 common-list) :select (some remove-if-not))
:export (getopt-long option-ref))
(define option-spec-fields '(name
;;; The code on this page was expanded by hand using the following code: value
;;; (pretty-print required?
;;; (macroexpand single-char
;;; '(define-record option-spec predicate
;;; (name value-policy))
;;; value
;;; value-required?
;;; single-char
;;; predicate-ls
;;; parse-ls))))
;;;
;;; This avoids the need to load slib for records.
(define slib:error error)
(begin (define
option-spec->name
(lambda
(obj)
(if (option-spec? obj)
(vector-ref obj 1)
(slib:error
(quote option-spec->name)
": bad record"
obj))))
(define
option-spec->value
(lambda
(obj)
(if (option-spec? obj)
(vector-ref obj 2)
(slib:error
(quote option-spec->value)
": bad record"
obj))))
(define
option-spec->value-required?
(lambda
(obj)
(if (option-spec? obj)
(vector-ref obj 3)
(slib:error
(quote option-spec->value-required?)
": bad record"
obj))))
(define
option-spec->single-char
(lambda
(obj)
(if (option-spec? obj)
(vector-ref obj 4)
(slib:error
(quote option-spec->single-char)
": bad record"
obj))))
(define
option-spec->predicate-ls
(lambda
(obj)
(if (option-spec? obj)
(vector-ref obj 5)
(slib:error
(quote option-spec->predicate-ls)
": bad record"
obj))))
(define
option-spec->parse-ls
(lambda
(obj)
(if (option-spec? obj)
(vector-ref obj 6)
(slib:error
(quote option-spec->parse-ls)
": bad record"
obj))))
(define
set-option-spec-name!
(lambda
(obj val)
(if (option-spec? obj)
(vector-set! obj 1 val)
(slib:error
(quote set-option-spec-name!)
": bad record"
obj))))
(define
set-option-spec-value!
(lambda
(obj val)
(if (option-spec? obj)
(vector-set! obj 2 val)
(slib:error
(quote set-option-spec-value!)
": bad record"
obj))))
(define
set-option-spec-value-required?!
(lambda
(obj val)
(if (option-spec? obj)
(vector-set! obj 3 val)
(slib:error
(quote set-option-spec-value-required?!)
": bad record"
obj))))
(define
set-option-spec-single-char!
(lambda
(obj val)
(if (option-spec? obj)
(vector-set! obj 4 val)
(slib:error
(quote set-option-spec-single-char!)
": bad record"
obj))))
(define
set-option-spec-predicate-ls!
(lambda
(obj val)
(if (option-spec? obj)
(vector-set! obj 5 val)
(slib:error
(quote set-option-spec-predicate-ls!)
": bad record"
obj))))
(define
set-option-spec-parse-ls!
(lambda
(obj val)
(if (option-spec? obj)
(vector-set! obj 6 val)
(slib:error
(quote set-option-spec-parse-ls!)
": bad record"
obj))))
(define
option-spec?
(lambda
(obj)
(and (vector? obj)
(= (vector-length obj) 7)
(eq? (vector-ref obj 0) (quote option-spec)))))
(define
make-option-spec
(lambda
(option-spec->name
option-spec->value
option-spec->value-required?
option-spec->single-char
option-spec->predicate-ls
option-spec->parse-ls)
(vector
(quote option-spec)
option-spec->name
option-spec->value
option-spec->value-required?
option-spec->single-char
option-spec->predicate-ls
option-spec->parse-ls))))
(define option-spec (make-record-type 'option-spec option-spec-fields))
;;; (define make-option-spec (record-constructor option-spec option-spec-fields))
;;; parse functions go on this page.
;;;
(define make-user-predicate
(lambda (pred)
(lambda (spec)
(let ((val (option-spec->value spec)))
(if (and val
(pred val)) #t
(error "option predicate failed:" (option-spec->name spec)))))))
(define make-not-allowed-value-fn (define (define-one-option-spec-field-accessor field)
(lambda () `(define ,(symbol-append 'option-spec-> field) ;;; name slib-compat
(lambda (spec) (record-accessor option-spec ',field)))
(let ((val (option-spec->value spec)))
(if (not (or (eq? val #t)
(eq? val #f)))
(let ((name (option-spec->name spec)))
(error "option does not support argument:" name)))))))
(define make-option-required-predicate (define (define-one-option-spec-field-modifier field)
(lambda () `(define ,(symbol-append 'set-option-spec- field '!) ;;; name slib-compat
(lambda (spec) (record-modifier option-spec ',field)))
(let ((val (option-spec->value spec)))
(if (not val)
(let ((name (option-spec->name spec)))
(error "option must be specified:" name)))))))
(define make-option-value-predicate (defmacro define-all-option-spec-accessors/modifiers ()
(lambda (predicate) `(begin
(lambda (spec) ,@(map define-one-option-spec-field-accessor option-spec-fields)
(let ((val (option-spec->value spec))) ,@(map define-one-option-spec-field-modifier option-spec-fields)))
(if (not (predicate val))
(let ((name (option-spec->name spec)))
(error "Bad option value:" name val)))))))
(define make-required-value-fn (define-all-option-spec-accessors/modifiers)
(lambda ()
(lambda (spec)
(let ((val (option-spec->value spec)))
(if (eq? val #t)
(let ((name (option-spec->name spec)))
(error "option must be specified with argument:" name)))))))
(define single-char-value? (define make-option-spec
(lambda (val) (let ((ctor (record-constructor option-spec '(name))))
(char? val))) (lambda (name)
(ctor name))))
(define (parse-option-spec desc) (define (parse-option-spec desc)
(letrec ((parse-iter (let ((spec (make-option-spec (symbol->string (car desc)))))
(lambda (spec) (for-each (lambda (desc-elem)
(let ((parse-ls (option-spec->parse-ls spec))) (let ((given (lambda () (cadr desc-elem))))
(if (null? parse-ls) (case (car desc-elem)
spec ((required?)
(let ((ls (car parse-ls))) (set-option-spec-required?! spec (given)))
(if (or (not (list? ls)) ((value)
(not (= (length ls) 2))) (set-option-spec-value-policy! spec (given)))
(error "Bad option specification:" ls)) ((single-char)
(let ((key (car ls)) (or (char? (given))
(val (cadr ls))) (error "`single-char' value must be a char!"))
(cond ((and (eq? key 'required?) val) (set-option-spec-single-char! spec (given)))
;; required values implemented as a predicate ((predicate)
(parse-iter (set-option-spec-predicate!
(make-option-spec spec ((lambda (pred)
(option-spec->name spec) (lambda (name val)
(option-spec->value spec) (or (not val)
(option-spec->value-required? spec) (pred val)
(option-spec->single-char spec) (error "option predicate failed:" name))))
(cons (make-option-required-predicate) (given))))
(option-spec->predicate-ls spec)) (else
(cdr parse-ls)))) (error "invalid getopt-long option property:"
;; if value not required, don't add predicate, (car desc-elem))))))
((eq? key 'required?) (cdr desc))
(parse-iter spec))
(make-option-spec
(option-spec->name spec)
(option-spec->value spec)
(option-spec->value-required? spec)
(option-spec->single-char spec)
(option-spec->predicate-ls spec)
(cdr parse-ls))))
;; handle value specification
((eq? key 'value)
(cond ((eq? val #t)
;; when value is required, add a
;; predicate to that effect and record
;; the fact in value-required? field.
(parse-iter
(make-option-spec
(option-spec->name spec)
(option-spec->value spec)
#t
(option-spec->single-char spec)
(cons (make-required-value-fn)
(option-spec->predicate-ls spec))
(cdr parse-ls))))
((eq? val #f)
;; when the value is not allowed, add a
;; predicate to that effect. one can
;; detect that a value is not supplied
;; by checking the option value against
;; #f.
(parse-iter
(make-option-spec
(option-spec->name spec)
(option-spec->value spec)
#f
(option-spec->single-char spec)
(cons (make-not-allowed-value-fn)
(option-spec->predicate-ls spec))
(cdr parse-ls))))
((eq? val 'optional)
;; for optional values, don't add a
;; predicate. do, however put the value
;; 'optional in the value-required?
;; field. this setting checks whether
;; optional values are 'greedy'. set to
;; #f to make optional value clauses
;; 'non-greedy'.
(parse-iter
(make-option-spec
(option-spec->name spec)
(option-spec->value spec)
'optional
(option-spec->single-char spec)
(option-spec->predicate-ls spec)
(cdr parse-ls))))
(#t
;; error case
(error "Bad value specification for option:"
(cons key val)))))
;; specify single char defined for this option.
((eq? key 'single-char)
(if (not (single-char-value? val))
(error "Not a single-char-value:"
val " for option:" key)
(parse-iter
(make-option-spec
(option-spec->name spec)
(option-spec->value spec)
(option-spec->value-required? spec)
val
(option-spec->predicate-ls spec)
(cdr parse-ls)))))
((eq? key 'predicate)
(if (procedure? val)
(parse-iter
(make-option-spec
(option-spec->name spec)
(option-spec->value spec)
(option-spec->value-required? spec)
(option-spec->single-char spec)
(cons (make-user-predicate val)
(option-spec->predicate-ls spec))
(cdr parse-ls)))
(error "Bad predicate specified for option:"
(cons key val))))))))))))
(if (or (not (pair? desc))
(string? (car desc)))
(error "Bad option specification:" desc))
(parse-iter (make-option-spec (car desc)
#f
#f
#f
'()
(cdr desc)))))
;;;
;;;
;;;
(define (split-arg-list argument-list) (define (split-arg-list argument-list)
"Given an ARGUMENT-LIST, decide which part to process for options. ;; Scan ARGUMENT-LIST for "--" and return (BEFORE-LS . AFTER-LS).
Everything before an arg of \"--\" is fair game, everything after it ;; Discard the "--". If no "--" is found, AFTER-LS is empty.
should not be processed. The \"--\" is discarded. A cons pair is (let loop ((yes '()) (no argument-list))
returned whose car is the list to process for options, and whose cdr (cond ((null? no) (cons (reverse yes) no))
is the list to not process." ((string=? "--" (car no)) (cons (reverse yes) (cdr no)))
(let loop ((process-ls '()) (else (loop (cons (car no) yes) (cdr no))))))
(not-process-ls argument-list))
(cond ((null? not-process-ls)
(cons (reverse process-ls) '()))
((string=? "--" (car not-process-ls))
(cons (reverse process-ls) (cdr not-process-ls)))
(#t
(loop (cons (car not-process-ls) process-ls)
(cdr not-process-ls))))))
(define short-opt-rx (make-regexp "^-([a-zA-Z]+)")) (define short-opt-rx (make-regexp "^-([a-zA-Z]+)(.*)"))
(define long-opt-no-value-rx (make-regexp "^--([^=]+)$")) (define long-opt-no-value-rx (make-regexp "^--([^=]+)$"))
(define long-opt-with-value-rx (make-regexp "^--([^=]+)=(.*)")) (define long-opt-with-value-rx (make-regexp "^--([^=]+)=(.*)"))
(define (single-char-expander specifications opt-ls) (define (match-substring match which)
"Expand single letter options that are mushed together." ;; condensed from (ice-9 regex) `match:{substring,start,end}'
(let ((response #f)) (let ((sel (vector-ref match (1+ which))))
(define (is-short-opt? str) (substring (vector-ref match 0) (car sel) (cdr sel))))
(set! response (regexp-exec short-opt-rx str))
response) (define (expand-clumped-singles opt-ls)
(define (iter opt-ls ret-ls) ;; example: ("--xyz" "-abc5d") => ("--xyz" "-a" "-b" "-c" "5d")
(let loop ((opt-ls opt-ls) (ret-ls '()))
(cond ((null? opt-ls) (cond ((null? opt-ls)
(reverse ret-ls)) (reverse ret-ls)) ;;; retval
((is-short-opt? (car opt-ls)) ((regexp-exec short-opt-rx (car opt-ls))
(let* ((orig-str (car opt-ls)) => (lambda (match)
(match-pair (vector-ref response 2)) (let ((singles (reverse
(match-str (substring orig-str (car match-pair) (map (lambda (c)
(cdr match-pair)))) (string-append "-" (make-string 1 c)))
(if (= (string-length match-str) 1) (string->list
(iter (cdr opt-ls) (match-substring match 1)))))
(cons (string-append "-" match-str) ret-ls)) (extra (match-substring match 2)))
(iter (cons (string-append "-" (substring match-str 1)) (loop (cdr opt-ls)
(cdr opt-ls)) (append (if (string=? "" extra)
(cons (string-append "-" (substring match-str 0 1)) singles
(cons extra singles))
ret-ls))))) ret-ls)))))
(#t (iter (cdr opt-ls) (else (loop (cdr opt-ls)
(cons (car opt-ls) ret-ls))))) (cons (car opt-ls) ret-ls))))))
(iter opt-ls '())))
(define (process-short-option specifications argument-ls alist) (define (looks-like-an-option string)
"Process a single short option that appears at the front of the ARGUMENT-LS, (some (lambda (rx)
according to SPECIFICATIONS. Returns #f is there is no such argument. (regexp-exec rx string))
Otherwise returns a pair whose car is the list of remaining arguments, and `(,short-opt-rx
whose cdr is a new association list, constructed by adding a pair to the ,long-opt-with-value-rx
supplied ALIST. The pair on the front of the returned association list ,long-opt-no-value-rx)))
describes the option found at the head of ARGUMENT-LS. The way this routine
currently works, an option that never takes a value that is followed by a non
option will cause an error, which is probably a bug. To fix the bug the
option specification needs to record whether the option ever can take a
value."
(define (short-option->char option)
(string-ref option 1))
(define (is-short-option? option)
(regexp-exec short-opt-rx option))
(define (is-long-option? option)
(or (regexp-exec long-opt-with-value-rx option)
(regexp-exec long-opt-no-value-rx option)))
(define (find-matching-spec option)
(let ((key (short-option->char option)))
(find-if (lambda (spec)
(eq? key (option-spec->single-char spec))) specifications)))
(let ((option (car argument-ls)))
(if (is-short-option? option)
(let ((spec (find-matching-spec option)))
(if spec
(let* ((next-value (if (null? (cdr argument-ls))
#f
(cadr argument-ls)))
(option-value (if (and next-value
(not (is-short-option? next-value))
(not (is-long-option? next-value))
(option-spec->value-required? spec))
next-value
#t))
(new-alist (cons (cons (option-spec->name spec)
option-value)
alist)))
(cons (if (eq? option-value #t)
(cdr argument-ls) ; one value, skip just one
(cddr argument-ls)) ; must be a value, skip two
new-alist))
(error "No such option:" option)))
#f)))
(define (process-long-option specifications argument-ls alist) (define (process-options specs argument-ls)
(define (find-matching-spec key) ;; Use SPECS to scan ARGUMENT-LS; return (FOUND . ETC).
(find-if (lambda (spec) ;; FOUND is an unordered list of option specs for found options, while ETC
(eq? key (option-spec->name spec))) ;; is an order-maintained list of elements in ARGUMENT-LS that are neither
specifications)) ;; options nor their values.
(define (split-long-option option) (let ((idx (map (lambda (spec)
;; returns a pair whose car is a symbol naming the option, cdr is (cons (option-spec->name spec) spec))
;; the option value. as a special case, if the option value is specs))
;; #f, then the caller should use the next item in argument-ls as (sc-idx (map (lambda (spec)
;; the option value. (cons (make-string 1 (option-spec->single-char spec))
(let ((resp (regexp-exec long-opt-no-value-rx option))) spec))
(if resp (remove-if-not option-spec->single-char specs))))
;; Aha, we've found a long option without an equal sign. (let loop ((argument-ls argument-ls) (found '()) (etc '()))
;; Maybe we need to grab a value from argument-ls. To find (let ((eat! (lambda (spec ls)
;; out we need to refer to the option-spec. (let ((val!loop (lambda (val n-ls n-found n-etc)
(let* ((key-pair (vector-ref resp 2)) (set-option-spec-value! spec val)
(key (string->symbol (loop n-ls n-found n-etc)))
(substring option (car key-pair) (cdr key-pair)))) (ERR:no-arg (lambda ()
(spec (find-matching-spec key))) (error (string-append
(let* ((req (option-spec->value-required? spec)) "option must be specified"
(retval (cons key (if req #f #t)))) " with argument:")
;; this is a fucking kludge, i hate it. it's necessary because (option-spec->name spec)))))
;; the protocol (return #f to indicate next element is an option (cond
;; arg) is insufficient. needs redesign. why am i checking in ((eq? 'optional (option-spec->value-policy spec))
;; such ugliness? read moby dick! -ttn (if (or (null? (cdr ls))
(and (eq? 'optional req) (looks-like-an-option (cadr ls)))
(set-object-property! retval 'optional #t)) (val!loop #t
retval)) (cdr ls)
(let ((resp (regexp-exec long-opt-with-value-rx option))) (cons spec found)
;; Aha, we've found a long option with an equal sign. The etc)
;; option value is simply the value to the right of the (val!loop (cadr ls)
;; equal sign. (cddr ls)
(if resp (cons spec found)
(let* ((key-pair (vector-ref resp 2)) etc)))
(key (string->symbol ((eq? #t (option-spec->value-policy spec))
(substring option (if (or (null? (cdr ls))
(car key-pair) (cdr key-pair)))) (looks-like-an-option (cadr ls)))
(value-pair (vector-ref resp 3)) (ERR:no-arg)
(value (substring option (val!loop (cadr ls)
(car value-pair) (cdr value-pair)))) (cddr ls)
(cons key value)) (cons spec found)
#f))))) etc)))
(let* ((option (car argument-ls))
(pair (split-long-option option)))
(cond ((and pair (eq? (cdr pair) #f))
(cond ((and (null? (cdr argument-ls))
(not (object-property pair 'optional)))
(error "Not enough options."))
((null? (cdr argument-ls))
(cons '() (cons (cons (car pair) #t) alist)))
((let* ((next (cadr argument-ls))
(m (or (regexp-exec short-opt-rx next)
(regexp-exec long-opt-with-value-rx next)
(regexp-exec long-opt-no-value-rx next))))
(and m (object-property pair 'optional)))
(cons (cdr argument-ls)
(cons (cons (car pair) #t) alist)))
(else (else
(cons (cddr argument-ls) (val!loop #t
(cons (cons (car pair) (cadr argument-ls)) alist))))) (cdr ls)
(pair (cons spec found)
(cons (cdr argument-ls) (cons pair alist))) etc)))))))
(else #f))))
(define (process-options specifications argument-ls)
(define (iter argument-ls alist rest-ls)
(if (null? argument-ls) (if (null? argument-ls)
(cons alist (reverse rest-ls)) (cons found (reverse etc)) ;;; retval
(let ((pair (process-short-option specifications argument-ls alist))) (cond ((regexp-exec short-opt-rx (car argument-ls))
(if pair => (lambda (match)
(let ((argument-ls (car pair)) (let* ((c (match-substring match 1))
(alist (cdr pair))) (spec (or (assoc-ref sc-idx c)
(iter argument-ls alist rest-ls)) (error "no such option:" c))))
(let ((pair (process-long-option (eat! spec argument-ls))))
specifications argument-ls alist))) ((regexp-exec long-opt-no-value-rx (car argument-ls))
(if pair => (lambda (match)
(let ((argument-ls (car pair)) (let* ((opt (match-substring match 1))
(alist (cdr pair))) (spec (or (assoc-ref idx opt)
(iter argument-ls alist rest-ls)) (error "no such option:" opt))))
(iter (cdr argument-ls) (eat! spec argument-ls))))
alist ((regexp-exec long-opt-with-value-rx (car argument-ls))
(cons (car argument-ls) rest-ls)))))))) => (lambda (match)
(iter argument-ls '() '())) (let* ((opt (match-substring match 1))
(spec (or (assoc-ref idx opt)
(error "no such option:" opt))))
(if (option-spec->value-policy spec)
(eat! spec (append
(list 'ignored
(match-substring match 2))
(cdr argument-ls)))
(error "option does not support argument:"
opt)))))
(else
(loop (cdr argument-ls)
found
(cons (car argument-ls) etc)))))))))
(define (getopt-long program-arguments option-desc-list) (define (getopt-long program-arguments option-desc-list)
"Process options, handling both long and short options, similar to "Process options, handling both long and short options, similar to
@ -708,41 +394,37 @@ or option values.
By default, options are not required, and option values are not By default, options are not required, and option values are not
required. By default, single character equivalents are not supported; required. By default, single character equivalents are not supported;
if you want to allow the user to use single character options, you need if you want to allow the user to use single character options, you need
to add a 'single-char' clause to the option description." to add a `single-char' clause to the option description."
(let* ((specifications (map parse-option-spec option-desc-list)) (let* ((specifications (map parse-option-spec option-desc-list))
(pair (split-arg-list (cdr program-arguments))) (pair (split-arg-list (cdr program-arguments)))
(split-ls (single-char-expander specifications (car pair))) (split-ls (expand-clumped-singles (car pair)))
(non-split-ls (cdr pair))) (non-split-ls (cdr pair))
(let* ((opt-pair (process-options specifications split-ls)) (found/etc (process-options specifications split-ls))
(alist (car opt-pair)) (found (car found/etc))
(rest-ls (append (cdr opt-pair) non-split-ls))) (rest-ls (append (cdr found/etc) non-split-ls)))
;; loop through returned alist, set values into specifications
(for-each (lambda (pair)
(let* ((key (car pair))
(val (cdr pair))
(spec (find-if (lambda (spec)
(eq? key (option-spec->name spec)))
specifications)))
(if spec (set-option-spec-value! spec val))))
alist)
;; now fire all the predicates
(for-each (lambda (spec) (for-each (lambda (spec)
(let ((predicate-ls (option-spec->predicate-ls spec))) (let ((name (option-spec->name spec))
(for-each (lambda (predicate) (val (option-spec->value spec)))
(predicate spec)) (and (option-spec->required? spec)
predicate-ls))) (or (memq spec found)
(error "option must be specified:" name)))
(and (memq spec found)
(eq? #t (option-spec->value-policy spec))
(or val
(error "option must be specified with argument:"
name)))
(let ((pred (option-spec->predicate spec)))
(and pred (pred name val)))))
specifications) specifications)
(cons (cons '() rest-ls) alist)))) (cons (cons '() rest-ls)
(map (lambda (spec)
(cons (string->symbol (option-spec->name spec))
(option-spec->value spec)))
found))))
(define (option-ref options key default) (define (option-ref options key default)
"Look for an option value in OPTIONS using KEY. If no such value is "Return value in alist OPTIONS using KEY, a symbol; or DEFAULT if not found.
found, return DEFAULT." The value is either a string or `#t'."
(let ((pair (assq key options))) (or (assq-ref options key) default))
(if pair
(cdr pair)
default)))
(export option-ref)
(export getopt-long)
;;; getopt-long.scm ends here ;;; getopt-long.scm ends here