MacroPackage: systemToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Object described on page has changed in 10.1.
10.0 version

with-command-line-arguments

Arguments: ( &rest option-lists) (restvar &key command-line-arguments usage posixly-correct long-abbrev) &body body

This macro was redefined in release 6.2. The change is upward-compatible so that with-command-line-arguments calls defined using the earlier macro/description should work as before.

The old-format lambda list for this macro is:

Arguments: (optstring &rest varlist) (restvar &key command-line-arguments usage) &body body

Any arguments that followed the old format are accepted in the new macro (which accepts more than one option-list and accepts more keyword arguments).

This macro makes the task of parsing command line arguments easier, in the style similar to the UNIX getopt(3) and getopt(1). During parsing, command line arguments are scanned from left to right. An argument named -- terminates option scanning. This macro works on both UNIX and Windows platforms. It works on Windows as it does on UNIX. Windows users should, like UNIX users refer to UNIX documentation on this (you can enter getopt(1) and getopt(3) into the Google (tm) search engine to find such documentation). We believe, however, that this documentation is stand-alone and should be sufficient for you to understand all the issues surrounding command line arguments and their use.

The main task of with-command-line-arguments is to bind Lisp variables to the given command line arguments.

option-lists, the first argument, is a list of (for the old form) an option-string and any number of variable names or (for the new form) a list of lists described below. Note that with-command-line-arguments will signal an error if a command line argument (option) is found without a corresponding entry in the option-lists.

The old form specification of option-lists

The old form, which preserves for compatibility with the older version of this macro, is therefore (in the lambda-list specification above, the parentheses around option-lists are the parentheses shown here):

  (option-string [variable-names]*)

option-string is a getopt(3) style string describing the expected options. variable-names are the symbols to which values are bound. The order of the options in option-string is the order in which the variables are bound. From the complete example below, we have

("ab:c" aflag bflag cflag)

The getopt(3) allows one character for each option, so the options in the example are `a', `b', and `c'. The corresponding variables are aflag, bflag, and cflag.

The new form specification of option-lists

In the first update (in March 2003), the new form specification was (option style varname [companion]). This has been superseded as follows:

In the new form, option-lists is a list of items with the following form:

  (name style varname [modifiers ...])

name is a string naming the argument, such as "a" or "foo".

style must be either :short or :long. For :short options, the string must be of length 1 (e.g., "a" for the command line option -a). For :long options, the string can be any length (e.g., "foo" for --foo). Long options are typically specified on a command line preceded with two dashes (hence --foo) while short options are preceded with one dash (hence -a). However, the long-abbrev keyword argument can allow single dashes to precede long options.

varname is a symbol to which a value is bound. This should be a Lisp symbol, for example aflag or foo.

modifiers may be unspecified or one or more of the following keyword selections:

Consider the following example which does not allow multiple options:

        ("foo" :long foo :required-companion)
        ("a" :short aflag :required-companion)   

The required-companion arguments can be specified in the following ways:

        --foo bar
        --foo=bar
        -abar
        -a bar

In each case the companion argument is "bar". Note that long form options are separated from their companions by a space or by =. Short form options are separated by a space or are not separated. The fact that no separation is allowed for short form options can lead to ambiguity. See the discussion of the long-abbrev keyword argument below for details.

Now consider these examples which do (for some options) allow multiple options:

;;  Option `o' allows multiple options, and requires companions
;;  so the `oflag' variable is bound to a list of the companion 
;;  values specified:
cl-user(13): (sys:with-command-line-arguments
                 (("o" :short oflag :required-companion 
                   :allow-multiple-options))
                 (rest :command-line-arguments '("-ofoo" "-o" "bar"))
               oflag)
("foo" "bar")

;;  Option `x' does not allow multiple options but option `v' does.
;;  Neither accepts companions. `xflag' is either T or NIL while
;;  `vflag' is a number, indicating how many times it appears
;;  (if it does not appear, `vflag' is NIL, not 0):
cl-user(14): (sys:with-command-line-arguments
                 (("x" :short xflag)
                  ("v" :short vflag :allow-multiple-options))
                 (rest :command-line-arguments '("-v" "-x" "-v"))
               (list vflag xflag))
(2 t)
cl-user(15): (sys:with-command-line-arguments
                 (("x" :short xflag)
                  ("v" :short vflag :allow-multiple-options))
                 (rest :command-line-arguments '("-x"))
               (list vflag xflag))
(nil t)

;;  The same specification as just above, but `x' is specified
;;  twice, causing an error:
cl-user(16): (sys:with-command-line-arguments
                 (("x" :short xflag)
                  ("v" :short vflag :allow-multiple-options))
                 (rest :command-line-arguments '("-v" "-x" "-v" "-x"))
               (list vflag xflag))
Error: Option `x' cannot be specified more than once.

Restart actions (select using :continue):
 0: Return to Top Level (an "abort" restart).
 1: Abort entirely from this process.
[1] cl-user(17): :reset

;;  The same specification as just above. `x' is not specified and
;;  `v' is specified three times with `-vvv':
cl-user(18): (sys:with-command-line-arguments
                 (("x" :short xflag)
                  ("v" :short vflag :allow-multiple-options))
                 (rest :command-line-arguments '("-vvv"))
               (list vflag xflag))
(3 nil)
;;  Here `v' has an optional-companion, and appears as `"-v" "2" "-vv"'.
;;  Note that `-vv' is `v' appearing once with companion "v" rather than
;;  `v' appearing twice (an unavoidable ambiguity discussed further
;;  below):

cl-user(19): (sys:with-command-line-arguments
                 (("x" :short xflag)
                  ("v" :short vflag
                       :optional-companion :allow-multiple-options))
                 (rest :command-line-arguments '("-v" "2" "-vv"))
               (list vflag xflag))
(("2" "v") nil)

;;  Again `v' has an optional-companion and is specified with
;;  and without a companion value. The unspecified value appears
;;  as the empty string in the values of v list (`x' also appears 
;;  once):
cl-user(20): (sys:with-command-line-arguments
                 (("x" :short xflag)
                  ("v" :short vflag
                       :optional-companion :allow-multiple-options))
                 (rest :command-line-arguments '("-v" "2" "-v" "-x"))
               (list vflag xflag))
(("2" "") t)
cl-user(23): 

Although either form, old or new, can be used for the option-lists, the new form is preferred since it offers long form arguments and does not require positional synchronization of the options in option-string with the variables given by [variable-names]*.

The other arguments to with-command-line-arguments

restvar should be a symbol. It is bound to all command line arguments that are not options. A command line argument of -- terminates option scanning and causes all remaining arguments to be put into restvar. The order of arguments in restvar will have the same order as the arguments on the command line, or in the value of the keyword command-line-arguments.

The command-line-arguments keyword argument is used to initialize the command line arguments. If not given, then the function command-line-arguments, called with a true value for the application keyword argument, is used to retrieve the actual command line arguments given to the invoked image. If command-line-arguments is specified nil, then the actual command line arguments are not substituted in (as they are when no value for this argument is specified). Instead, the with-command-line-arguments form is rendered a no-op and does nothing. (Obviously there is no point in calling this macro with :command-line-arguments nil, but you might call it with the value of :command-line-arguments a variable whose value might happen to be nil. If you do that, the macro will do what you want when the variable has value nil as well as when it has a non-nil value.)

The posixly-correct keyword argument, ignored in the old form, says what to do when a non-option is encountered in the left to right scan of the command line arguments. If posixly-correct is non-nil, then processing of the command line terminates when a non-option is seen, otherwise the non-option is put into restvar.

The usage keyword argument, if specified, should be a string. This string is printed when errors are found while parsing the command line arguments according to the given specification. (Possible errors would be not supplying a companion argument, supplying an unknown command line argument, etc.)

The long-abbrev keyword argument, ignored in the old form, allows long form arguments to be specified with a single -. Otherwise they must be specified with double dashes, --.

Allowing a single dash to specify a long form argument can introduce ambiguity. Consider the following case illustrated by a partial with-command-line-arguments form:

(sys:with-command-line-arguments
    (("foo" :long foo :optional-companion)
     ("f" :short aflag :required-companion))
    (:long-abbrev t) ...) 

Now suppose the argument -foo is provided. Does this mean the long form argument -foo or the short form argument -f with companion (or value) oo? Both interpretations are valid, and so the argument is ambiguous. When long-abbrev is specified true, the system will always assume that an argument is a long form argument if it can be so intrepreted. If the argument cannot be interpreted as a long form argument, the short form argument interpretation will be used. Programmers should avoid any possibility of ambiguity, of course. Again, if long-abbrev is nil, short form and long form arguments are designated differently and there can be no ambiguity in interpretation. The downside is that it may be harder for users of an application to specify arguments correctly (that is to remember which require two dashes and which require one).

Here is the example above shown in a script:

cl-user(1): (system:with-command-line-arguments
		(("foo" :long foo :optional-companion)
		 ("f" :short fflag :optional-companion))
		(rest :command-line-arguments
		      '("-foo") :long-abbrev nil)
	      (list foo fflag rest))
(nil "oo" nil)

;;  -foo is parsed as a short option since the long form "foo"
;;  must be specified --foo.

cl-user(2): (system:with-command-line-arguments
		(("foo" :long foo :optional-companion)
		 ("f" :short fflag :optional-companion))
		(rest :command-line-arguments '("-foo")
		      :long-abbrev t)
	      (list foo fflag rest))
(t nil nil)

;;  -foo is parsed as a long option.

The body argument to with-command-line-arguments

body is a list of forms, executed in a lexical environment where the variables discussed above are bound for use by the macro's body.

Examples using with-command-line-arguments

Here are some more examples of the new form:

cl-user(15): (sys:with-command-line-arguments
                 (("foo" :long foo :required-companion)
                  ("bar" :long bar :optional-companion)
                  ("baz" :long baz nil)
                  ("a" :short aflag :required-companion)
                  ("b" :short bflag :optional-companion)
                  ("c" :short cflag nil))
                 (rest :long-abbrev t
                       :command-line-arguments
                       '("--foo" "xxx" "--bar" "-afoo" "-b" "-c" "1" "2" "3"))
               (format t "foo=~a, bar=~a, baz=~a, a=~a, b=~a, c=~a, rest=~a~%"
                       foo bar baz aflag bflag cflag rest))
foo=xxx, bar=t, baz=nil, a=foo, b=t, c=t, rest=(1 2 3)
nil
cl-user(16): (sys:with-command-line-arguments
                 (("foo" :long foo :required-companion)
                  ("bar" :long bar :optional-companion)
                  ("baz" :long baz nil)
                  ("a" :short aflag :required-companion)
                  ("b" :short bflag :optional)
                  ("c" :short cflag nil))
                 (rest :long-abbrev t
                       :command-line-arguments
                       '("--foo=xxx" "--bar" "-a" "foo" "-b" "-c" "1" "2" "3"))
               (format t "foo=~a, bar=~a, baz=~a, a=~a, b=~a, c=~a, rest=~a~%"
                       foo bar baz aflag bflag cflag rest))
foo=xxx, bar=t, baz=nil, a=foo, b=t, c=t, rest=(1 2 3)
nil
cl-user(17): (sys:with-command-line-arguments
                 (("foo" :long foo :required-companion)
                  ("f" :short fflag :required-companion)
                  ("foo2" :long foo2 :required-companion))
                 (rest :command-line-arguments
                       '("--foo" "xxx" "--foo2=blah" "-foo" "foo"))
               (format t "foo=~a, foo2=~a, f=~a, rest=~a~%"
                       foo foo2 fflag rest))
foo=xxx, foo2=blah, f=oo, rest=(foo)
nil
cl-user(18): (sys:with-command-line-arguments
                 (("f" :short fflag :required-companion))
                 (rest :command-line-arguments '("-foo" "xxx" "foo"))
               (format t "f=~a, rest=~a~%"
                       fflag rest))
f=oo, rest=(xxx foo)
nil
cl-user(19): 

Note: the command-line-arguments keyword would normally not be used since the actual command line arguments would come from the command line of the running application.

Here is an example of the old form:

cl-user(18): (sys:with-command-line-arguments
                 ("ab:c" aflag bflag cflag)
                 (rest :command-line-arguments
                       '("-a" "-b" "xxx" "1" "2" "3"))
               (format t "a=~a, b=~a, c=~a, rest=~a~%"
                       aflag bflag cflag rest))
a=t, b=xxx, c=nil, rest=(1 2 3)
nil
cl-user(19): 

See Command line arguments in startup.htm for a list of command-line arguments accepted by Allegro CL.


Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
The object described on this page has been modified in the 10.1 release; see the Release Notes.
Created 2019.8.20.

ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Object described on page has changed in 10.1.
10.0 version