unix-options (C) 2009-2010 Andrew Stine

This software is distributed under the terms of the Lisp Lesser GNU 
Public License (http://opensource.franz.com/preamble.html), also 
known as the LLGPL. 

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

----------------------------------------------------------------------

Unix-options is a small Common Lisp library for parsing unix-style 
command line options. 

This library is very new and not widely tested so suggestions, 
bug-reports and patches (including changes to this document) are
welcome.

Please send any feedback to: unix dot options (at) librelist dot com

----------------------------------------------------------------------
Usage:
 
The library can be loaded through ASDF and used through the package 
'unix-options.'

Exported symbols are:

=| cli-options      - variable
=| ¶meters        - symbol
=| &free              - symbol
=| free               - symbol
=| map-parsed-options - function
=| getopt             - function
=| with-cli-options   - macro

An example of usage:

(with-cli-options ()
    (print ¶meters in-file out-file)
  (when print
    (with-open-file (in in-file :direction :input)
      (with-open-file (out out-file :direction :output)
        (while (peek-char in)
          (write-char (read-char in) out))))))

$ sample-program -p -i input.txt -o output.txt
=> write input.txt to output.txt

---------------------------------------------------------------------
API:

getopt - An imitation of the Unix 'getopt' command line program

    prototype: (getopt cli-options shortopts longopts)

    details: Getopt accepts three arguments: a list of tokens passed
             in on the CLI, a string detailing available short-form
             options and a list detailing long-form options. Getopt
             breaks the tokens up and sorts them so that they can be
             handled by the calling program more easily.
 
             Cli-options can be any list of strings. Each string
             begging with a single '-' is interpreted as a series of
             short options and any beginning with '--' is interpreted
             as a long option. Others are either free tokens or
             parameters. Any tokens appearing after a token that is
             soley a '--', are interpreted as free tokens.

             Shortopts takes the form: "abf:" where each letter is the
             name of a permissable short option and a ':' following a
             letter signifies that it takes a parameter.

             Longopts takes the form: '("option1" "file=") where each
             string is the name of a long option and a '=' signifies
             that the option takes a parameter.

	     Return three values which are all lists: (1) parsed command
	     line arguments with "--" separating valid options and free
	     arguments, (2) only the valid options and (3) only the free
	     arguments.

    example: (getopt '("-ab" "--file" "file.txt" "free")
                     "ab"
                     '("file="))
             => ("a" "b" "file" "file.txt" "--" "free")
                ("a" "b" "file" "file.txt")
                ("free")

make-option-spec - A function which create an option-spec object

    prototype: (make-option-spec tokens &optional parameter description)
    
    details: Make-option-spec generates an option-spec object from the
             the details provided. Tokens can either be a symbol, or a
             list of characters, strings, and symbols. Parameter is either
             nil, t, or a string. Description is always a string.

             An option spec contains a list of short-tokens, as characters
             and a list of long-tokens as strings. Short-tokens are drawn
             from any characters passed under tokens and the first
             letter of the name of any symbols passed. Long-tokens are
             drawn from any strings passed likewise and from the complete
             names of any symbols passed.

             Parameter signifies whether an option takes a parameter. If
             parameter is nil, then the option does not. Otherwise, the
             option does. The parameter can be described briefly by passing
             a string as parameter rather than simply t. 

             Description is a description of the option and it's purpose in
             general.
     
             Option-specs are used as parameters to several functions in
             unix-options. Generally, anywhere an option-spec is required,
             one may substitute a list of the parameters one would pass to 
             make-option-spec. Thus one might be able to type:
             (function '(foo nil "foo option")) instead of:
             (function (make-option-spec 'foo nil "foo option"))
    
    example: (make-option-spec '(#\A foo) "integer" "A number to be passed in")
             => <option-spec {12f9sj39}>
             

with-cli-options - A macro that binds values passed in from the command
                   line to local variables

    prototype: (with-cli-options (&optional (cli-options '(cli-options))
                                             enable-usage-summary)
                                  option-variables &body body)
                                  
    details: With-cli-options takes a list of symbols and attempts to
             bind values passed in on the command line to them according
             to a set of rules. For every symbol named in 
             option-variables, with-cli-options creates a variable to 
             which it binds any long option of the same name. 
             With-cli-options also take the first letter of any symbol
             and bind short options of that letter to that symbol. If
             multiple symbols begin with the same letter, only the first
             in option-variables is bound the short option of the same
             letter; the second is bound to the uppercase version and any
             more must use a long option.
             
             Optionally, each entry in option-variables can be a short
             list. The first item is symbol to be used. The second is
             either a documentation string, or a full option-spec allowing 
             greater detail as to how that particular entry is to be
             formed.

             In addition, symbols in option-variables are boolean by 
             default; that is, they bound to either t or nil depending on
             whether they are passed in on the cli. Symbols listed after
             ¶meters, if it is present, are are considered parameter
             options, that is, they are bound to the next token on the cli
             if the are long options or the same or the rest of the
             current short option group, if they are short options. If
             nothing is passed for this options (ie. if it is the last
             token) it is bound to nil.
             
             Any tokens that aren't options or parameters to options are
             saved into a list of free tokens. This list is bound to 'free'
             unless a different symbol is provided in option-variables
             after &free.
             
             If enable-usage-summary is set, then with-cli-options will be
             able to print out a usage summary (using print-usage-summary)
             based on the options bound in option-varibles. The summary
             will be printed either when an invalid option is specified or
             the user uses '-h' or '--help' as an option. If
             enable-usage-summary is set to a string, that string is passed
             as control string to print-usage-summary. Else, a generic
             string is used.

   example: (with-cli-options ('("-pi" "in" "--out-file" "out" "another"))
                (print ¶meters in-file out-file)
              (print print)
              (print in-file)
              (print out-file)
              (print free))
            T
            "in"
            "out"
            ("another")
            => NIL
            
cli-options - returns the list of whitespace separated tokens passed
                on the command line.
                
print-usage-summary - Prints a usage description of the current program

    prototype: (print-usage-summary description option-specs)

    details: Print-usage-summary print a usage summary of options specified
             by options-specs.

             The function takes each option-spec and generates a string
             describing in detail each option. The resulting strings are
             passed as arguments to a call to format. Description is used
             as the control string. The option-spec descriptions are auto-
             aligned.

    example: (print-usage-summary "Usage:~%~@{~A~%~}~%end summary"
                                  '(((#\a "alpha") nil "A simple option")
                                    ((#\f "file") "FILENAME" "A filename")
                                    ((#\b #\d "beta") nil "Another option")))
             Usage:
               -a, --alpha          A simple option
               -f, --file=FILENAME  A filename
               -b, -d, --beta       Another option

             end summary
             =>NIL

map-parsed-options - Function that does the actual parsing of cli tokens

    prototype: (map-parsed-options cli-options bool-options param-options
                                   opt-val-func free-val-func)

    details: Map-parsed-options is the backend function that does most
             of the actual work in parsing cli functions. It is exposed
             to allow more front ends in addition to getopt and
             with-cli-options. Map-parsed-options receives a list of cli
             tokens, as well as two list of options, boolean options and
             options with parameters, respectively. The final two
             arguments are functions, one to handle found options and
             there values, and one to handle free tokens.

    example: (map-parsed-options '("-ab" "value" "free") '("a") '("b")
                                 (lambda (option value)
                                    (format t "option: ~A; value: ~A~%"
                                            option value))
                                 (lambda (free-val)
                                    (format t "free: ~A~%" free-val)))
             option: a; value T
             option: b; value value 
             free: free
             => NIL

With thanks to folks who've reported bugs and provided patches.