# HG changeset patch # User Steve Losh # Date 1718298328 14400 # Node ID 0f6bab39c0f403e01634445886b3ee25c57c17b8 # Parent 15d3b832fdc5c7c7c4c0e9eff7ab45da00ba538a adopt: Update site. diff -r 15d3b832fdc5 -r 0f6bab39c0f4 adopt/_dmedia/style.less --- a/adopt/_dmedia/style.less Wed Jun 21 15:21:12 2023 -0400 +++ b/adopt/_dmedia/style.less Thu Jun 13 13:05:28 2024 -0400 @@ -8,7 +8,7 @@ body { font-size: 16px; line-height: 1.4; - font-family: Georgia, Palatino, "Palatino Linotype", serif; + font-family: serif; color: #111111; } a { @@ -16,7 +16,7 @@ color: #0069d6; } h1, h2, h3, h4, h5 { - font-family: Palatino, "Palatino Linotype", serif; + font-family: serif; a { color: #111111; @@ -99,6 +99,12 @@ padding: 1px 4px; font-family: Menlo, Monaco, Consolas, monospace; } + +pre code { + border: none; + padding: 0px; + background: none; +} ol, ul { margin-bottom: 9px; } diff -r 15d3b832fdc5 -r 0f6bab39c0f4 adopt/changelog/index.html --- a/adopt/changelog/index.html Wed Jun 21 15:21:12 2023 -0400 +++ b/adopt/changelog/index.html Thu Jun 13 13:05:28 2024 -0400 @@ -1,10 +1,10 @@ - + Changelog / Adopt - - + + @@ -12,15 +12,20 @@

Adopt

-

Changelog

Here's the list of changes in each released version.

+

Changelog

+

Here's the list of changes in each released version.

+ +
+

1.3.0

+

Add ECL support to exit (thanks Dmitry Solomennnikov).

1.2.0

Add Lispworks support to argv and exit.

1.1.1

@@ -32,8 +37,7 @@

1.0.0

Initial release.

- + \ No newline at end of file diff -r 15d3b832fdc5 -r 0f6bab39c0f4 adopt/index.html --- a/adopt/index.html Wed Jun 21 15:21:12 2023 -0400 +++ b/adopt/index.html Thu Jun 13 13:05:28 2024 -0400 @@ -31,8 +31,7 @@ The tests pass on SBCL, CCL, ECL, and ABCL on Ubuntu 18.04. Further testing is welcome.

Table of Contents

  1. Usage
  2. API Reference
  3. Changelog
- + diff -r 15d3b832fdc5 -r 0f6bab39c0f4 adopt/reference/index.html --- a/adopt/reference/index.html Wed Jun 21 15:21:12 2023 -0400 +++ b/adopt/reference/index.html Thu Jun 13 13:05:28 2024 -0400 @@ -1,10 +1,10 @@ - + API Reference / Adopt - - + + @@ -12,7 +12,8 @@

Adopt

-

API Reference

The following is a list of all user-facing parts of adopt.

+

API Reference

+

The following is a list of all user-facing parts of adopt.

If there are backwards-incompatible changes to anything listed here, they will be noted in the changelog and the author will feel bad.

Anything not listed here is subject to change at any time with no warning, so @@ -47,38 +48,31 @@ -

+ +

Package ADOPT

ARGV (function)

-
(ARGV)
-
- - +
(ARGV)
+

Return a list of the program name and command line arguments.

This is not implemented for every Common Lisp implementation. You can pass your own values to parse-options and print-help if it's not implemented for your particular Lisp.

COLLECT (function)

-
(COLLECT LIST EL)
-
- - +
(COLLECT LIST EL)
+

Append element el to the end of list.

It is useful as a :reduce function when you want to collect all values given for an option.

This is implemented as (append list (list el)). It is not particularly fast. If you can live with reversed output consider (flip #'cons) instead.

DEFINE-STRING (macro)

-
(DEFINE-STRING VAR STRING &REST ARGS)
-
- - +
(DEFINE-STRING VAR STRING &REST ARGS)
+

Convenience macro for (defparameter ,var (format nil ,string ,@args)).

DEFPARAMETERS (macro)

-
(DEFPARAMETERS PARAMETERS VALUES-FORM)
-
- - +
(DEFPARAMETERS PARAMETERS VALUES-FORM)
+

Convenience macro for defparametering multiple variables at once.

parameters must be a list of special variable names suitable for giving to defparameter.

@@ -87,7 +81,7 @@

This can be handy when using make-boolean-options to create two options at once and assign them to special variables.

Examples:

-
(defparameters (*a* *b*) (truncate 100 3))
+
(defparameters (*a* *b*) (truncate 100 3))
 (list *a* *b*)
 ; =>
 ; (33 1)
@@ -98,72 +92,54 @@
     :help-no "Do not foo the widgets during the run (the default)."
     :long "foo"
     :short #f))
-
- - +

DISCARD-OPTION (function)

-
(DISCARD-OPTION CONDITION)
-
- - +
(DISCARD-OPTION CONDITION)
+

Invoke the discard-option restart properly.

Example:

-
(handler-bind ((unrecognized-option 'discard-option))
+
(handler-bind ((unrecognized-option 'discard-option))
   (multiple-value-bind (arguments options) (parse-options *ui*)
     (run arguments options)))
-
- - +

EXIT (function)

-
(EXIT &OPTIONAL (CODE 0))
-
- - +
(EXIT &OPTIONAL (CODE 0))
+

Exit the program with status code.

This is not implemented for every Common Lisp implementation. You can write your own version of it and pass it to print-help-and-exit and print-error-and-exit if it's not implemented for your particular Lisp.

FIRST (function)

-
(FIRST OLD NEW)
-
- - +
(FIRST OLD NEW)
+

Return new if old is nil, otherwise return old.

It is useful as a :reduce function when you want to just keep the first-given value for an option.

FLIP (function)

-
(FLIP FUNCTION)
-
- - +
(FLIP FUNCTION)
+

Return a function of two arguments X and Y that calls function with Y and X.

Useful for wrapping existing functions that expect their arguments in the opposite order.

Examples:

-
(funcall #'cons 1 2)        ; => (1 . 2)
+
(funcall #'cons 1 2)        ; => (1 . 2)
 (funcall (flip #'cons) 1 2) ; => (2 . 1)
 (reduce (flip #'cons) '(1 2 3) :initial-value nil)
 ; => (3 2 1)
-
- - +

LAST (function)

-
(LAST OLD NEW)
-
- - +
(LAST OLD NEW)
+

Return new.

It is useful as a :reduce function when you want to just keep the last-given value for an option.

MAKE-BOOLEAN-OPTIONS (function)

-
(MAKE-BOOLEAN-OPTIONS NAME &KEY
+
(MAKE-BOOLEAN-OPTIONS NAME &KEY
                       (NAME-NO (INTERN (CONCATENATE 'STRING (STRING 'NO-) (STRING NAME)))) LONG
                       (LONG-NO (WHEN LONG (FORMAT NIL no-~A LONG))) SHORT
                       (SHORT-NO (WHEN SHORT (CHAR-UPCASE SHORT))) (RESULT-KEY NAME) HELP
                       HELP-NO MANUAL MANUAL-NO INITIAL-VALUE)
-
- - +

Create and return a pair of boolean options, suitable for use in an interface.

This function reduces some of the boilerplate when creating two options for boolean values, e.g. --foo and --no-foo. It will try to guess at an @@ -173,7 +149,7 @@ defparameters to conveniently bind them to two separate variables if desired.

Example:

-
(defparameters (*option-debug* *option-no-debug*)
+
(defparameters (*option-debug* *option-no-debug*)
   (make-boolean-options 'debug
     :long "debug"
     :short #d
@@ -196,14 +172,10 @@
     :short #D
     :help "Disable the Lisp debugger (the default)."
     :reduce (constantly nil))
-
- - +

MAKE-GROUP (function)

-
(MAKE-GROUP NAME &KEY TITLE HELP MANUAL OPTIONS)
-
- - +
(MAKE-GROUP NAME &KEY TITLE HELP MANUAL OPTIONS)
+

Create and return an option group, suitable for use in an interface.

This function takes a number of arguments that define how the group is presented to the user:

@@ -216,10 +188,8 @@

See the full documentation for more information.

MAKE-INTERFACE (function)

-
(MAKE-INTERFACE &KEY NAME SUMMARY USAGE HELP MANUAL EXAMPLES CONTENTS)
-
- - +
(MAKE-INTERFACE &KEY NAME SUMMARY USAGE HELP MANUAL EXAMPLES CONTENTS)
+

Create and return a command line interface.

This function takes a number of arguments that define how the interface is presented to the user:

@@ -234,12 +204,10 @@

See the full documentation for more information.

MAKE-OPTION (function)

-
(MAKE-OPTION NAME &KEY LONG SHORT HELP MANUAL PARAMETER REDUCE
+
(MAKE-OPTION NAME &KEY LONG SHORT HELP MANUAL PARAMETER REDUCE
              (INITIAL-VALUE NIL INITIAL-VALUE?) (RESULT-KEY NAME) (KEY #'IDENTITY)
              (FINALLY #'IDENTITY))
-
- - +

Create and return an option, suitable for use in an interface.

This function takes a number of arguments, some required, that define how the option interacts with the user.

@@ -264,10 +232,8 @@

See the full documentation for more information.

PARSE-OPTIONS (function)

-
(PARSE-OPTIONS INTERFACE &OPTIONAL (ARGUMENTS (REST (ARGV))))
-
- - +
(PARSE-OPTIONS INTERFACE &OPTIONAL (ARGUMENTS (REST (ARGV))))
+

Parse arguments according to interface.

Two values are returned:

    @@ -277,10 +243,8 @@

See the full documentation for more information.

PARSE-OPTIONS-OR-EXIT (function)

-
(PARSE-OPTIONS-OR-EXIT INTERFACE &OPTIONAL (ARGUMENTS (REST (ARGV))))
-
- - +
(PARSE-OPTIONS-OR-EXIT INTERFACE &OPTIONAL (ARGUMENTS (REST (ARGV))))
+

Parse arguments according to interface, exiting if any error occurs.

Two values are returned:

    @@ -292,27 +256,21 @@ adopt:print-error-and-exit.

    See the full documentation for more information.

    -
    (PRINT-ERROR-AND-EXIT ERROR &KEY (STREAM *ERROR-OUTPUT*) (EXIT-FUNCTION #'EXIT) (EXIT-CODE 1)
    +
    (PRINT-ERROR-AND-EXIT ERROR &KEY (STREAM *ERROR-OUTPUT*) (EXIT-FUNCTION #'EXIT) (EXIT-CODE 1)
                           (PREFIX error: ))
    -
    - - +

    Print prefix and error to stream and exit.

    Example:

    -
    (handler-case
    +
    (handler-case
         (multiple-value-bind (arguments options) (parse-options *ui*)
           (run arguments options))
       (unrecognized-option (c)
         (print-error-and-exit c)))
    -
    - - + -
    (PRINT-HELP INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (PROGRAM-NAME (CAR (ARGV))) (WIDTH 80)
    +
    (PRINT-HELP INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (PROGRAM-NAME (CAR (ARGV))) (WIDTH 80)
                 (OPTION-WIDTH 20) (INCLUDE-EXAMPLES T))
    -
    - - +

    Print a pretty help document for interface to stream.

    width should be the total width (in characters) for line-wrapping purposes. Care will be taken to ensure lines are no longer than this, though some edge @@ -323,7 +281,7 @@ help string will start on the same line. Otherwise the option's help string will start on the next line.

    The result will look something like:

    -
    (print-help *program-interface* :width 60 :option-width 15)
    +
    (print-help *program-interface* :width 60 :option-width 15)
     ; =>
     ; foo - do some things and meow
     ;
    @@ -343,66 +301,48 @@
     ;   -m, --meow       Meow.
     ;   0.........1.... option-width
     ; 0........1.........2.........3.........4.........5.........6
    -
    - - + -
    (PRINT-HELP-AND-EXIT INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (PROGRAM-NAME (CAR (ARGV)))
    +
    (PRINT-HELP-AND-EXIT INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (PROGRAM-NAME (CAR (ARGV)))
                          (WIDTH 80) (OPTION-WIDTH 20) (INCLUDE-EXAMPLES T) (EXIT-FUNCTION #'EXIT)
                          (EXIT-CODE 0))
    -
    - - +

    Print a pretty help document for interface to stream and exit.

    Handy for easily providing --help:

    -
    (multiple-value-bind (arguments options) (parse-options *ui*)
    +
    (multiple-value-bind (arguments options) (parse-options *ui*)
       (when (gethash 'help options)
         (print-help-and-exit *ui*))
       (run arguments options))
    -
    - - + -
    (PRINT-MANUAL INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (MANUAL-SECTION 1))
    -
    - - +
    (PRINT-MANUAL INTERFACE &KEY (STREAM *STANDARD-OUTPUT*) (MANUAL-SECTION 1))
    +

    Print a troff-formatted man page for interface to stream.

    Example:

    -
    (with-open-file (manual "man/man1/foo.1"
    +
    (with-open-file (manual "man/man1/foo.1"
                             :direction :output
                             :if-exists :supersede)
       (print-manual *ui* manual))
    -
    - - +

    SUPPLY-NEW-VALUE (function)

    -
    (SUPPLY-NEW-VALUE CONDITION VALUE)
    -
    - - +
    (SUPPLY-NEW-VALUE CONDITION VALUE)
    +

    Invoke the supply-new-value restart properly.

    Example:

    -
    (handler-bind
    +
    (handler-bind
         ((unrecognized-option (alexandria:rcurry 'supply-new-value "--foo"))
       (multiple-value-bind (arguments options) (parse-options *ui*)
         (run arguments options)))
    -
    - - +

    TREAT-AS-ARGUMENT (function)

    -
    (TREAT-AS-ARGUMENT CONDITION)
    -
    - - +
    (TREAT-AS-ARGUMENT CONDITION)
    +

    Invoke the treat-as-argument restart properly.

    Example:

    -
    (handler-bind ((unrecognized-option 'treat-as-argument))
    +
    (handler-bind ((unrecognized-option 'treat-as-argument))
       (multiple-value-bind (arguments options) (parse-options *ui*)
         (run arguments options)))
    -
    - - +

    UNRECOGNIZED-OPTION (class)

    Slot PROBLEMATIC-OPTION

    - + \ No newline at end of file diff -r 15d3b832fdc5 -r 0f6bab39c0f4 adopt/usage/index.html --- a/adopt/usage/index.html Wed Jun 21 15:21:12 2023 -0400 +++ b/adopt/usage/index.html Thu Jun 13 13:05:28 2024 -0400 @@ -1,10 +1,10 @@ - + Usage / Adopt - - + + @@ -12,7 +12,8 @@

    Adopt

    -

    Usage

    Adopt is a library for parsing UNIX-style command line arguments in Common Lisp. +

    Usage

    +

    Adopt is a library for parsing UNIX-style command line arguments in Common Lisp. It was made because none of the other libraries did what I needed.

    -
    + +

    Package

    All core Adopt functions are in the adopt package. Several of the symbols in adopt shadow those in the common-lisp package, so you should probably use @@ -58,15 +60,14 @@

    Let's say you're developing a program to search the contents of files (because the world certainly needs another grep replacement). You might start with something like:

    -
    (defparameter *ui*
    -  (adopt:make-interface
    -    :name "search"
    -    :summary "search files for a regular expression"
    -    :usage "[OPTIONS] PATTERN [FILE]..."
    -    :help "Search the contents of each FILE for the regular expression PATTERN.  If no files are specified, searches standard input instead."))
    -
    - - +
    :::lisp
    +(defparameter *ui*
    +  (adopt:make-interface
    +    :name "search"
    +    :summary "search files for a regular expression"
    +    :usage "[OPTIONS] PATTERN [FILE]..."
    +    :help "Search the contents of each FILE for the regular expression PATTERN.  If no files are specified, searches standard input instead."))
    +

    make-interface takes several required arguments:

    • :name is the name of the program.
    • @@ -76,17 +77,16 @@
    • :help is a longer description of the program.

    You can now print some pretty help text for the CLI with adopt:print-help:

    -
    (adopt:print-help *ui*)
    -; =>
    -; search - search files for a regular expression
    -;
    -; USAGE: /path/to/binary [OPTIONS] PATTERN [FILE]...
    -;
    -; Search the contents of each FILE for the regular expression PATTERN.  If no
    -; files are specified, searches standard input instead.
    -
    - - +
    :::lisp
    +(adopt:print-help *ui*)
    +; =>
    +; search - search files for a regular expression
    +;
    +; USAGE: /path/to/binary [OPTIONS] PATTERN [FILE]...
    +;
    +; Search the contents of each FILE for the regular expression PATTERN.  If no
    +; files are specified, searches standard input instead.
    +

    Line Wrapping

    Adopt will handle line-wrapping your help text, so you don't need to (and shouldn't) add extra line breaks when creating your interface.

    @@ -94,138 +94,132 @@ text editor, remember that adopt:make-interface is just a function — you can use format (possibly with its ~Newline directive) to preprocess the help text argument:

    -
    (defparameter *ui*
    -  (adopt:make-interface
    -    :name "search"
    -    :summary "search files for a regular expression"
    -    :usage "[OPTIONS] PATTERN [FILE]..."
    -    :help (format nil "Search the contents of each FILE for ~
    -                       the regular expression PATTERN.  If ~
    -                       no files are specified, searches ~
    -                       standard input instead.")))
    -
    - - +
    :::lisp
    +(defparameter *ui*
    +  (adopt:make-interface
    +    :name "search"
    +    :summary "search files for a regular expression"
    +    :usage "[OPTIONS] PATTERN [FILE]..."
    +    :help (format nil "Search the contents of each FILE for ~
    +                       the regular expression PATTERN.  If ~
    +                       no files are specified, searches ~
    +                       standard input instead.")))
    +

    If you want to pull out the documentation string into its own variable to keep that make-interface call from getting too unwieldy, you can certainly do that:

    -
    (defparameter *help-text*
    -  (format nil "Search the contents of each FILE for the ~
    -               regular expression PATTERN.  If no files ~
    -               are specified, searches standard input ~
    -               instead."))
    +
    :::lisp
    +(defparameter *help-text*
    +  (format nil "Search the contents of each FILE for the ~
    +               regular expression PATTERN.  If no files ~
    +               are specified, searches standard input ~
    +               instead."))
     
    -(defparameter *ui*
    -  (adopt:make-interface
    -    :name "search"
    -    :summary "search files for a regular expression"
    -    :usage "[OPTIONS] PATTERN [FILE]..."
    -    :help *help-text*))
    -
    - - +(defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help *help-text*)) +

    The (defparameter … (format nil …)) pattern can be tedious to write, so Adopt provides a helper macro define-string that does exactly that:

    -
    (adopt:define-string *help-text*
    -  "Search the contents of each FILE for the regular ~
    -   expression PATTERN.  If no files are specified, ~
    -   searches standard input instead.")
    +
    :::lisp
    +(adopt:define-string *help-text*
    +  "Search the contents of each FILE for the regular ~
    +   expression PATTERN.  If no files are specified, ~
    +   searches standard input instead.")
     
    -(defparameter *ui*
    -  (adopt:make-interface
    -    :name "search"
    -    :summary "search files for a regular expression"
    -    :usage "[OPTIONS] PATTERN [FILE]..."
    -    :help *help-text*))
    -
    - - +(defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help *help-text*)) +

    Adopt's line-wrapping library Bobbin will only ever add line breaks, never remove them, which means you can include breaks in the output if you want to have multiple paragraphs in your help text. Once again, format is your friend:

    -
    (adopt:define-string *help-text*
    -  "Search the contents of each FILE for the regular ~
    -   expression PATTERN.~@
    -   ~@
    -   If no files are specified (or if - is given as a ~
    -   file name), standard input will be searched instead.")
    +
    :::lisp
    +(adopt:define-string *help-text*
    +  "Search the contents of each FILE for the regular ~
    +   expression PATTERN.~@
    +   ~@
    +   If no files are specified (or if - is given as a ~
    +   file name), standard input will be searched instead.")
     
    -(defparameter *ui*
    -  (adopt:make-interface
    -    :name "search"
    -    :summary "search files for a regular expression"
    -    :usage "[OPTIONS] PATTERN [FILE]..."
    -    :help *help-text*))
    -
    - - +(defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help *help-text*)) +

    If you want to control the width of the help text lines when they are printed, adopt:print-help takes a :width argument:

    -
    (adopt:print-help *ui* :width 50)
    -; =>
    -; search - search files for a regular expression
    -;
    -; USAGE: … [OPTIONS] PATTERN [FILE]...
    -;
    -; Search the contents of each FILE for the regular
    -; expression PATTERN.
    -;
    -; If no files are specified (or if - is given as a
    -; file name), standard input will be searched
    -; instead.
    -
    - - +
    :::lisp
    +(adopt:print-help *ui* :width 50)
    +; =>
    +; search - search files for a regular expression
    +;
    +; USAGE: … [OPTIONS] PATTERN [FILE]...
    +;
    +; Search the contents of each FILE for the regular
    +; expression PATTERN.
    +;
    +; If no files are specified (or if - is given as a
    +; file name), standard input will be searched
    +; instead.
    +

    adopt:print-help takes a number of other options — see the API Reference for more information.

    Adding Examples

    Describing the CLI in detail is helpful, but users can often learn a lot more by seeing a few examples of its usage. make-interface can take an :examples argument, which should be an alist of (description . example) conses:

    -
    (defparameter *ui*
    -  (adopt:make-interface
    -    :name "search"
    -    :summary "search files for a regular expression"
    -    :usage "[OPTIONS] PATTERN [FILE]..."
    -    :help *help-text*
    -    :examples
    -    '(("Search foo.txt for the string 'hello':"
    -       . "search hello foo.txt")
    -      ("Search standard input for lines starting with x:"
    -       . "search '^x' -")
    -      ("Watch the file log.txt for lines containing the username steve.losh:"
    -       . "tail foo/bar/baz/log.txt | search --literal steve.losh -"))))
    +
    :::lisp
    +(defparameter *ui*
    +  (adopt:make-interface
    +    :name "search"
    +    :summary "search files for a regular expression"
    +    :usage "[OPTIONS] PATTERN [FILE]..."
    +    :help *help-text*
    +    :examples
    +    '(("Search foo.txt for the string 'hello':"
    +       . "search hello foo.txt")
    +      ("Search standard input for lines starting with x:"
    +       . "search '^x' -")
    +      ("Watch the file log.txt for lines containing the username steve.losh:"
    +       . "tail foo/bar/baz/log.txt | search --literal steve.losh -"))))
     
    -(adopt:print-help *ui* :width 50)
    -; =>
    -; search - search files for a regular expression
    -;
    -; USAGE: … [OPTIONS] PATTERN [FILE]...
    -;
    -; Search the contents of each FILE for the regular
    -; expression PATTERN.
    -;
    -; If no files are specified (or if - is given as a
    -; file name) standard input will be searched
    -; instead.
    -;
    -; Examples:
    -;
    -;   Search foo.txt for the string 'hello':
    -;
    -;       search hello foo.txt
    -;
    -;   Search standard input for lines starting with x:
    -;
    -;       search '^x' -
    -;
    -;   Watch the file log.txt for lines containing the
    -;   username steve.losh:
    -;
    -;       tail foo/bar/baz/log.txt | search --literal steve.losh -
    -
    - - +(adopt:print-help *ui* :width 50) +; => +; search - search files for a regular expression +; +; USAGE: … [OPTIONS] PATTERN [FILE]... +; +; Search the contents of each FILE for the regular +; expression PATTERN. +; +; If no files are specified (or if - is given as a +; file name) standard input will be searched +; instead. +; +; Examples: +; +; Search foo.txt for the string 'hello': +; +; search hello foo.txt +; +; Search standard input for lines starting with x: +; +; search '^x' - +; +; Watch the file log.txt for lines containing the +; username steve.losh: +; +; tail foo/bar/baz/log.txt | search --literal steve.losh - +

    Notice how Adopt line wraps the prose explaining each example, but leaves the example itself untouched for easier copying and pasting. In general Adopt tries to do the right thing for your users (even when that means making a little more @@ -233,78 +227,75 @@

    Exiting

    Adopt provides some helpful utility functions to exit out of your program with a UNIX exit code. These do what you think they do:

    -
    (adopt:exit)
    +
    :::lisp
    +(adopt:exit)
     
    -(adopt:exit 1)
    +(adopt:exit 1)
     
    -(adopt:print-help-and-exit *ui*)
    +(adopt:print-help-and-exit *ui*)
     
    -(adopt:print-help-and-exit *ui*
    -  :stream *error-output*
    -  :exit-code 1)
    +(adopt:print-help-and-exit *ui*
    +  :stream *error-output*
    +  :exit-code 1)
     
    -(handler-case (assert (= 1 0))
    -  (error (err)
    -    (adopt:print-error-and-exit err)))
    -
    - - +(handler-case (assert (= 1 0)) + (error (err) + (adopt:print-error-and-exit err))) +

    These functions are not implemented for every Lisp implementation. PRs are welcome, or you can just write the implementation-specific calls in your program yourself if you prefer.

    Options

    Now that you know how to create an interface, you can create some options to use inside it with adopt:make-option:

    -
    (defparameter *option-version*
    -  (adopt:make-option 'version
    -    :long "version"
    -    :help "Display version and exit."
    -    :reduce (constantly t)))
    +
    :::lisp
    +(defparameter *option-version*
    +  (adopt:make-option 'version
    +    :long "version"
    +    :help "Display version and exit."
    +    :reduce (constantly t)))
     
    -(defparameter *option-help*
    -  (adopt:make-option 'help
    -    :long "help"
    -    :short #\h
    -    :help "Display help and exit."
    -    :reduce (constantly t)))
    +(defparameter *option-help*
    +  (adopt:make-option 'help
    +    :long "help"
    +    :short #\h
    +    :help "Display help and exit."
    +    :reduce (constantly t)))
     
    -(defparameter *option-literal*
    -  (adopt:make-option 'literal
    -    :long "literal"
    -    :short #\l
    -    :help "Treat PATTERN as a literal string instead of a regular expression."
    -    :reduce (constantly t)))
    +(defparameter *option-literal*
    +  (adopt:make-option 'literal
    +    :long "literal"
    +    :short #\l
    +    :help "Treat PATTERN as a literal string instead of a regular expression."
    +    :reduce (constantly t)))
     
    -(defparameter *ui*
    -  (adopt:make-interface
    -    :name "search"
    -    :summary "search files for a regular expression"
    -    :usage "[OPTIONS] PATTERN [FILE]..."
    -    :help "Search the contents of …"
    -    :contents (list
    -                *option-version*
    -                *option-help*
    -                *option-literal*)))
    -
    - - +(defparameter *ui* + (adopt:make-interface + :name "search" + :summary "search files for a regular expression" + :usage "[OPTIONS] PATTERN [FILE]..." + :help "Search the contents of …" + :contents (list + *option-version* + *option-help* + *option-literal*))) +

    Adopt will automatically add the options to the help text:

    -
    (adopt:print-help *ui*)
    -; =>
    -; search - search files for a regular expression
    -;
    -; USAGE: /usr/local/bin/sbcl [OPTIONS] PATTERN [FILE]...
    -;
    -; Search the contents of …
    -;
    -; Options:
    -;   --version             Display version and exit.
    -;   -h, --help            Display help and exit.
    -;   -l, --literal         Treat PATTERN as a literal string instead of a regular
    -;                         expression.
    -
    - - +
    :::lisp
    +(adopt:print-help *ui*)
    +; =>
    +; search - search files for a regular expression
    +;
    +; USAGE: /usr/local/bin/sbcl [OPTIONS] PATTERN [FILE]...
    +;
    +; Search the contents of …
    +;
    +; Options:
    +;   --version             Display version and exit.
    +;   -h, --help            Display help and exit.
    +;   -l, --literal         Treat PATTERN as a literal string instead of a regular
    +;                         expression.
    +

    The first argument to make-option is the name of the option, which we'll see put to use shortly. At least one of :short and :long is required, and :help text must be specified. We'll talk more about :reduce in a little @@ -316,36 +307,33 @@

    I prefer to define each option as its own global variable to keep the call to make-interface from getting too large and unwieldy, but feel free to do something like this if you prefer to avoid cluttering your package:

    -
    (defparameter *ui*
    -  (adopt:make-interface
    -    
    -    :contents
    -    (list (adopt:make-option 'foo )
    -          (adopt:make-option 'bar )
    -          )))
    -
    - - +
    :::lisp
    +(defparameter *ui*
    +  (adopt:make-interface
    +    …
    +    :contents
    +    (list (adopt:make-option 'foo …)
    +          (adopt:make-option 'bar …)
    +          …)))
    +

    Parsing

    At this point we've got an interface with some options, so we can use it to parse a list of strings we've received as command line arguments with adopt:parse-options:

    -
    (adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt"))
    -; =>
    -; ("foo.*" "a.txt" "b.txt")
    -; #<HASH-TABLE :TEST EQL :COUNT 3 {10103142A3}>
    -
    - - +
    :::lisp
    +(adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt"))
    +; =>
    +; ("foo.*" "a.txt" "b.txt")
    +; #<HASH-TABLE :TEST EQL :COUNT 3 {10103142A3}>
    +

    From now on I'll use a special pretty printer for hash tables to make it easier to see what's inside them:

    -
    (adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt"))
    -; =>
    -; ("foo.*" "a.txt" "b.txt")
    -; {LITERAL: T, VERSION: NIL, HELP: NIL}
    -
    - - +
    :::lisp
    +(adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt"))
    +; =>
    +; ("foo.*" "a.txt" "b.txt")
    +; {LITERAL: T, VERSION: NIL, HELP: NIL}
    +

    parse-options returns two values:

    1. A list of non-option arguments.
    2. @@ -355,23 +343,22 @@ hash table are (by default) the option names given as the first argument to make-option. You can specify a different key for a particular option with the :result-key argument to make-option:

      -
      (defparameter *option-literal*
      -  (adopt:make-option 'literal
      -    :result-key 'pattern-is-literal
      -    :long "literal"
      -    :short #\l
      -    :help "Treat PATTERN as a literal string instead of a regular expression."
      -    :reduce (constantly t)))
      +
      :::lisp
      +(defparameter *option-literal*
      +  (adopt:make-option 'literal
      +    :result-key 'pattern-is-literal
      +    :long "literal"
      +    :short #\l
      +    :help "Treat PATTERN as a literal string instead of a regular expression."
      +    :reduce (constantly t)))
       
      -;; …
      +;; …
       
      -(adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt"))
      -; =>
      -; ("foo.*" "a.txt" "b.txt")
      -; {PATTERN-IS-LITERAL: T, VERSION: NIL, HELP: NIL}
      -
      - - +(adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt")) +; => +; ("foo.*" "a.txt" "b.txt") +; {PATTERN-IS-LITERAL: T, VERSION: NIL, HELP: NIL} +

      This can come in useful if you want multiple options that affect the same result (e.g. --verbose and --silent flags that toggle extra log output on and off).

      Option Formats

      @@ -388,30 +375,29 @@

      Top-Level Structure

      We'll look at how the option values are computed shortly, but first let's see the overall structure of the programs you'll typically create with Adopt:

      -
      (defun run (pattern files &key literal)
      -  ;; Actually do something here.
      -  )
      +
      :::lisp
      +(defun run (pattern files &key literal)
      +  ;; Actually do something here.
      +  )
       
      -(defun toplevel ()
      -  (handler-case
      -      (multiple-value-bind (arguments options) (adopt:parse-options *ui*)
      -        (when (gethash 'help options)
      -          (adopt:print-help-and-exit *ui*))
      -        (when (gethash 'version options)
      -          (format t "1.0.0~%")
      -          (adopt:exit))
      -        (destructuring-bind (pattern . files) arguments
      -          (run pattern
      -               files
      -               :literal (gethash 'literal options))))
      -    (error (c)
      -      (adopt:print-error-and-exit c))))
      +(defun toplevel ()
      +  (handler-case
      +      (multiple-value-bind (arguments options) (adopt:parse-options *ui*)
      +        (when (gethash 'help options)
      +          (adopt:print-help-and-exit *ui*))
      +        (when (gethash 'version options)
      +          (format t "1.0.0~%")
      +          (adopt:exit))
      +        (destructuring-bind (pattern . files) arguments
      +          (run pattern
      +               files
      +               :literal (gethash 'literal options))))
      +    (error (c)
      +      (adopt:print-error-and-exit c))))
       
      -(defun build ()
      -  (sb-ext:save-lisp-and-die "search" :executable t :toplevel #'toplevel))
      -
      - - +(defun build () + (sb-ext:save-lisp-and-die "search" :executable t :toplevel #'toplevel)) +

      This is a typical way to use Adopt. There are three important functions here:

      • The toplevel function takes care of parsing arguments and exiting with an @@ -474,80 +460,74 @@

        Simple Options

        To define an option that just tracks whether it's ever been given, you can do something like:

        -
        (defparameter *option-help*
        -  (adopt:make-option 'help
        -    :long "help"
        -    :short #\h
        -    :help "Display help and exit."
        -    :initial-value nil
        -    :reduce (lambda (current-value)
        -              (declare (ignore current-value))
        -              t)))
        -
        - - +
        :::lisp
        +(defparameter *option-help*
        +  (adopt:make-option 'help
        +    :long "help"
        +    :short #\h
        +    :help "Display help and exit."
        +    :initial-value nil
        +    :reduce (lambda (current-value)
        +              (declare (ignore current-value))
        +              t)))
        +

        But since nil is the default initial value and Common Lisp provides the handy constantly function, you can do this more concisely:

        -
        (defparameter *option-help*
        -  (adopt:make-option 'help
        -    :long "help"
        -    :short #\h
        -    :help "Display help and exit."
        -    :reduce (constantly t)))
        -
        - - +
        :::lisp
        +(defparameter *option-help*
        +  (adopt:make-option 'help
        +    :long "help"
        +    :short #\h
        +    :help "Display help and exit."
        +    :reduce (constantly t)))
        +

        Boolean Options

        If you want to have multiple options that both affect the same key in the results, you can use :result-key to do this:

        -
        (defparameter *option-paginate*
        -  (adopt:make-option 'paginate
        -    :long "paginate"
        -    :short #\p
        -    :help "Turn pagination on."
        -    :reduce (constantly t)))
        +
        :::lisp
        +(defparameter *option-paginate*
        +  (adopt:make-option 'paginate
        +    :long "paginate"
        +    :short #\p
        +    :help "Turn pagination on."
        +    :reduce (constantly t)))
         
        -(defparameter *option-no-paginate*
        -  (adopt:make-option 'no-paginate
        -    :result-key 'paginate
        -    :long "no-paginate"
        -    :short #\P
        -    :help "Turn pagination off (the default)."
        -    :reduce (constantly nil)))
        -
        - - +(defparameter *option-no-paginate* + (adopt:make-option 'no-paginate + :result-key 'paginate + :long "no-paginate" + :short #\P + :help "Turn pagination off (the default)." + :reduce (constantly nil))) +

        The way we've written this, if the user gives multiple options the last-given one will take precedence. This is generally what you want, because it allows someone to add a shell alias with these options like this:

        -
        alias g='git --paginate --color=always'
        -
        - - +
        :::bash
        +alias g='git --paginate --color=always'
        +

        but still lets them override an option at runtime for a single invocation:

        -
        g --no-paginate log
        -# expands to: git --paginate --color=always --no-paginate log
        -
        - - +
        :::bash
        +g --no-paginate log
        +# expands to: git --paginate --color=always --no-paginate log
        +

        If the last-given option didn't take precedence, they'd have to fall back to the non-alias version of the command, and type out all the options they do want by hand. This is annoying, so it's usually better to let the last one win.

        Making two separate options by hand can be tedious if you have a lot of boolean options, so Adopt provides a make-boolean-options function that will do some of the boilerplate for you:

        -
        (adopt:make-boolean-options 'paginate
        -  :long "paginate"
        -  :short #\p
        -  :help "Turn pagination on."
        -  :help-no "Turn pagination off (the default).")
        -;; =>
        -#<ADOPT::OPTION PAGINATE p/paginate>
        -#<ADOPT::OPTION NO-PAGINATE P/no-paginate>
        -
        - - +
        :::lisp
        +(adopt:make-boolean-options 'paginate
        +  :long "paginate"
        +  :short #\p
        +  :help "Turn pagination on."
        +  :help-no "Turn pagination off (the default).")
        +;; =>
        +#<ADOPT::OPTION PAGINATE p/paginate>
        +#<ADOPT::OPTION NO-PAGINATE P/no-paginate>
        +

        make-boolean-options will try to guess at sensible values to reduce the boilerplate you need to type:

          @@ -563,130 +543,121 @@

          The two options are returned as separate values. Adopt also provides a defparameters convenience macro to create special variables for them more easily:

          -
          (defparameters (*option-paginate* *option-no-paginate*)
          -  (adopt:make-boolean-options 'paginate
          -    :long "paginate"
          -    :short #\p
          -    :help "Turn pagination on."
          -    :help-no "Turn pagination off (the default)."))
          -
          - - +
          :::lisp
          +(defparameters (*option-paginate* *option-no-paginate*)
          +  (adopt:make-boolean-options 'paginate
          +    :long "paginate"
          +    :short #\p
          +    :help "Turn pagination on."
          +    :help-no "Turn pagination off (the default)."))
          +

          Counting Options

          To define an option that counts how many times it's been given, like SSH's -v, you can use something like this:

          -
          (defparameter *option-verbosity*
          -  (adopt:make-option 'verbosity
          -    :short #\v
          -    :help "Output more verbose logs."
          -    :initial-value 0
          -    :reduce #'1+))
          -
          - - +
          :::lisp
          +(defparameter *option-verbosity*
          +  (adopt:make-option 'verbosity
          +    :short #\v
          +    :help "Output more verbose logs."
          +    :initial-value 0
          +    :reduce #'1+))
          +

          Single-Parameter Options

          To define an option that takes a parameter and only keeps the last one given, you can do something like:

          -
          (defparameter *option-repository*
          -  (adopt:make-option 'repository
          -    :parameter "PATTERN"
          -    :long "repository"
          -    :short #\R
          -    :help "Path to the repository (default .)."
          -    :initial-value "."
          -    :reduce (lambda (prev new)
          -              (declare (ignore prev))
          -              new)))
          -
          - - +
          :::lisp
          +(defparameter *option-repository*
          +  (adopt:make-option 'repository
          +    :parameter "PATTERN"
          +    :long "repository"
          +    :short #\R
          +    :help "Path to the repository (default .)."
          +    :initial-value "."
          +    :reduce (lambda (prev new)
          +              (declare (ignore prev))
          +              new)))
          +

          Specifying the :parameter argument makes this option a parameter-taking option, which means the :reduce function will be called with the current value and the given parameter each time.

          Writing that lambda out by hand every time would be tedious. Adopt provides a function called last (as in "keep the last parameter given") that does exactly that:

          -
          (defparameter *option-repository*
          -  (adopt:make-option 'repository
          -    :long "repository"
          -    :short #\R
          -    :help "Path to the repository (default .)."
          -    :initial-value "."
          -    :reduce #'adopt:last))
          -
          - - +
          :::lisp
          +(defparameter *option-repository*
          +  (adopt:make-option 'repository
          +    :long "repository"
          +    :short #\R
          +    :help "Path to the repository (default .)."
          +    :initial-value "."
          +    :reduce #'adopt:last))
          +

          Multiple-Parameter Options

          Collecting every parameter given can be done in a number of different ways. One strategy could be:

          -
          (defparameter *option-exclude*
          -  (adopt:make-option 'exclude
          -    :long "exclude"
          -    :parameter "PATTERN"
          -    :help "Exclude PATTERN (may be given multiple times)."
          -    :initial-value nil
          -    :reduce (lambda (patterns new)
          -              (cons new patterns))))
          -
          - - +
          :::lisp
          +(defparameter *option-exclude*
          +  (adopt:make-option 'exclude
          +    :long "exclude"
          +    :parameter "PATTERN"
          +    :help "Exclude PATTERN (may be given multiple times)."
          +    :initial-value nil
          +    :reduce (lambda (patterns new)
          +              (cons new patterns))))
          +

          You might notice that the :reduce function here is just cons with its arguments flipped. Common Lisp doesn't have a function like Haskell's flip, so Adopt provides it:

          -
          (defparameter *option-exclude*
          -  (adopt:make-option 'exclude
          -    :long "exclude"
          -    :parameter "PATTERN"
          -    :help "Exclude PATTERN (may be given multiple times)."
          -    :initial-value nil
          -    :reduce (adopt:flip #'cons)))
          -
          - - +
          :::lisp
          +(defparameter *option-exclude*
          +  (adopt:make-option 'exclude
          +    :long "exclude"
          +    :parameter "PATTERN"
          +    :help "Exclude PATTERN (may be given multiple times)."
          +    :initial-value nil
          +    :reduce (adopt:flip #'cons)))
          +

          Note that the result of this will be a fresh list of all the given parameters, but their order will be reversed because cons adds each new parameter to the front of the list. If the order doesn't matter for what you're going to do with it, you're all set. Otherwise, there are several ways to get around this problem. The first is to add the parameter to the end of the list in the :reduce function:

          -
          (defparameter *option-exclude*
          -  (adopt:make-option 'exclude
          -    :long "exclude"
          -    :parameter "PATTERN"
          -    :help "Exclude PATTERN (may be given multiple times)."
          -    :initial-value nil
          -    :reduce (lambda (patterns new)
          -              (append patterns (list new)))))
          -
          - - +
          :::lisp
          +(defparameter *option-exclude*
          +  (adopt:make-option 'exclude
          +    :long "exclude"
          +    :parameter "PATTERN"
          +    :help "Exclude PATTERN (may be given multiple times)."
          +    :initial-value nil
          +    :reduce (lambda (patterns new)
          +              (append patterns (list new)))))
          +

          This is tedious and inefficient if you have a lot of arguments. If you don't care much about argument parsing speed, Adopt provides a function called collect that does exactly this, so you don't have to type out that lambda yourself (and nil is the default initial value, so you don't need that either):

          -
          (defparameter *option-exclude*
          -  (adopt:make-option 'exclude
          -    :long "exclude"
          -    :parameter "PATTERN"
          -    :help "Exclude PATTERN (may be given multiple times)."
          -    :reduce #'adopt:collect))
          -
          - - +
          :::lisp
          +(defparameter *option-exclude*
          +  (adopt:make-option 'exclude
          +    :long "exclude"
          +    :parameter "PATTERN"
          +    :help "Exclude PATTERN (may be given multiple times)."
          +    :reduce #'adopt:collect))
          +

          A more efficient (though slightly uglier) solution would be to use nreverse at the end:

          -
          (defparameter *option-exclude*
          -  (adopt:make-option 'exclude
          -    :long "exclude"
          -    :parameter "PATTERN"
          -    :help "Exclude PATTERN (may be given multiple times)."
          -    :reduce (adopt:flip #'cons)
          -    :finally #'nreverse))
          -
          - - +
          :::lisp
          +(defparameter *option-exclude*
          +  (adopt:make-option 'exclude
          +    :long "exclude"
          +    :parameter "PATTERN"
          +    :help "Exclude PATTERN (may be given multiple times)."
          +    :reduce (adopt:flip #'cons)
          +    :finally #'nreverse))
          +

          If you really need maximum efficiency when parsing command line options (you probably don't) you could use a queue library, or use a vector and vector-push-extend, or anything else you might dream up. The combination of @@ -697,252 +668,245 @@ foo --help they shouldn't get an error about a missing required option.

          In cases where you really do need to require an option (perhaps only if some other one is also given) you can check it yourself:

          -
          (defun toplevel ()
          -  (handler-case
          -      (multiple-value-bind (arguments options) (adopt:parse-options *ui*)
          -        (when (gethash 'help options)
          -          (adopt:print-help-and-exit *ui*))
          -        (unless (gethash 'some-required-option options)
          -          (error "Required option foo is missing."))
          -        (run ))
          -    (error (c)
          -      (adopt:print-error-and-exit c))))
          -
          - - +
          :::lisp
          +(defun toplevel ()
          +  (handler-case
          +      (multiple-value-bind (arguments options) (adopt:parse-options *ui*)
          +        (when (gethash 'help options)
          +          (adopt:print-help-and-exit *ui*))
          +        (unless (gethash 'some-required-option options)
          +          (error "Required option foo is missing."))
          +        (run …))
          +    (error (c)
          +      (adopt:print-error-and-exit c))))
          +

          Option Groups

          Related options can be grouped together in the help text to make them easier for users to understand. Groups can have their own name, title, and help text.

          Here's a example of how this works. It's fairly long, but shows how Adopt can help you make a command line interface with all the fixins:

          -
          (defparameter *option-help*
          -  (adopt:make-option 'help
          -    :help "Display help and exit."
          -    :long "help"
          -    :short #\h
          -    :reduce (constantly t)))
          +
          :::lisp
          +(defparameter *option-help*
          +  (adopt:make-option 'help
          +    :help "Display help and exit."
          +    :long "help"
          +    :short #\h
          +    :reduce (constantly t)))
           
          -(defparameter *option-literal*
          -  (adopt:make-option 'literal
          -    :help "Treat PATTERN as a literal string instead of a regex."
          -    :long "literal"
          -    :short #\l
          -    :reduce (constantly t)))
          +(defparameter *option-literal*
          +  (adopt:make-option 'literal
          +    :help "Treat PATTERN as a literal string instead of a regex."
          +    :long "literal"
          +    :short #\l
          +    :reduce (constantly t)))
           
          -(defparameter *option-no-literal*
          -  (adopt:make-option 'no-literal
          -    :result-key 'literal
          -    :help "Treat PATTERN as a regex (the default)."
          -    :long "no-literal"
          -    :short #\L
          -    :reduce (constantly nil)))
          +(defparameter *option-no-literal*
          +  (adopt:make-option 'no-literal
          +    :result-key 'literal
          +    :help "Treat PATTERN as a regex (the default)."
          +    :long "no-literal"
          +    :short #\L
          +    :reduce (constantly nil)))
           
          -(defparameter *option-case-sensitive*
          -  (adopt:make-option 'case-sensitive
          -    :help "Match case-sensitively (the default)."
          -    :long "case-sensitive"
          -    :short #\c
          -    :initial-value t
          -    :reduce (constantly t)))
          +(defparameter *option-case-sensitive*
          +  (adopt:make-option 'case-sensitive
          +    :help "Match case-sensitively (the default)."
          +    :long "case-sensitive"
          +    :short #\c
          +    :initial-value t
          +    :reduce (constantly t)))
           
          -(defparameter *option-case-insensitive*
          -  (adopt:make-option 'case-insensitive
          -    :help "Ignore case when matching."
          -    :long "case-insensitive"
          -    :short #\C
          -    :result-key 'case-sensitive
          -    :reduce (constantly nil)))
          +(defparameter *option-case-insensitive*
          +  (adopt:make-option 'case-insensitive
          +    :help "Ignore case when matching."
          +    :long "case-insensitive"
          +    :short #\C
          +    :result-key 'case-sensitive
          +    :reduce (constantly nil)))
           
          -(defparameter *option-color*
          -  (adopt:make-option 'color
          -    :help "Highlight matches with color."
          -    :long "color"
          -    :reduce (constantly t)))
          +(defparameter *option-color*
          +  (adopt:make-option 'color
          +    :help "Highlight matches with color."
          +    :long "color"
          +    :reduce (constantly t)))
           
          -(defparameter *option-no-color*
          -  (adopt:make-option 'no-color
          -    :help "Don't highlight matches (the default)."
          -    :long "no-color"
          -    :result-key 'color
          -    :reduce (constantly nil)))
          +(defparameter *option-no-color*
          +  (adopt:make-option 'no-color
          +    :help "Don't highlight matches (the default)."
          +    :long "no-color"
          +    :result-key 'color
          +    :reduce (constantly nil)))
           
          -(defparameter *option-context*
          -  (adopt:make-option 'context
          -    :parameter "N"
          -    :help "Show N lines of context (default 0)."
          -    :long "context"
          -    :short #\U
          -    :initial-value 0
          -    :reduce #'adopt:last
          -    :key #'parse-integer))
          +(defparameter *option-context*
          +  (adopt:make-option 'context
          +    :parameter "N"
          +    :help "Show N lines of context (default 0)."
          +    :long "context"
          +    :short #\U
          +    :initial-value 0
          +    :reduce #'adopt:last
          +    :key #'parse-integer))
           
           
          -(defparameter *group-matching*
          -  (adopt:make-group 'matching-options
          -    :title "Matching Options"
          -    :options (list *option-literal*
          -                   *option-no-literal*
          -                   *option-case-sensitive*
          -                   *option-case-insensitive*)))
          -
          -(defparameter *group-output*
          -  (adopt:make-group 'output-options
          -    :title "Output Options"
          -    :help "These options affect how matching lines are printed.  The defaults are ideal for piping into other programs."
          -    :options (list *option-color*
          -                   *option-no-color*
          -                   *option-context*)))
          -
          +(defparameter *group-matching*
          +  (adopt:make-group 'matching-options
          +    :title "Matching Options"
          +    :options (list *option-literal*
          +                   *option-no-literal*
          +                   *option-case-sensitive*
          +                   *option-case-insensitive*)))
           
          -(adopt:define-string *help-text*
          -  "Search FILEs for lines that match the regular expression ~
          -   PATTERN and print them to standard out.  Several options ~
          -   are available to control how the matching lines are printed.~@
          -   ~@
          -   If no files are given (or if - is given as a filename) ~
          -   standard input will be searched.")
          -
          -(defparameter *ui*
          -  (adopt:make-interface
          -    :name "search"
          -    :usage "PATTERN [FILE...]"
          -    :summary "print lines that match a regular expression"
          -    :help *help-text*
          -    :contents (list *option-help*
          -                    *group-matching*
          -                    *group-output*)))
          -
          +(defparameter *group-output* + (adopt:make-group 'output-options + :title "Output Options" + :help "These options affect how matching lines are printed. The defaults are ideal for piping into other programs." + :options (list *option-color* + *option-no-color* + *option-context*))) +(adopt:define-string *help-text* + "Search FILEs for lines that match the regular expression ~ + PATTERN and print them to standard out. Several options ~ + are available to control how the matching lines are printed.~@ + ~@ + If no files are given (or if - is given as a filename) ~ + standard input will be searched.") + +(defparameter *ui* + (adopt:make-interface + :name "search" + :usage "PATTERN [FILE...]" + :summary "print lines that match a regular expression" + :help *help-text* + :contents (list *option-help* + *group-matching* + *group-output*))) +

          And with all that out of the way, you've got some nicely-organized help text for your users:

          -
          (adopt:print-help *ui* :width 60 :option-width 16)
          -; =>
          -; search - print lines that match a regular expression
          -;
          -; USAGE: /usr/local/bin/sbcl PATTERN [FILE...]
          -;
          -; Search FILEs for lines that match the regular expression
          -; PATTERN and print them to standard out.  Several options are
          -; available to control how the matching lines are printed.
          -;
          -; If no files are given (or if - is given as a filename)
          -; standard input will be searched.
          -;
          -; Options:
          -;   -h, --help        display help and exit
          -;
          -; Matching Options:
          -;   -l, --literal     treat PATTERN as a literal string
          -;                     instead of a regex
          -;   -L, --no-literal  treat PATTERN as a regex (the default)
          -;   -c, --case-sensitive
          -;                     match case-sensitively (the default)
          -;   -C, --case-insensitive
          -;                     ignore case when matching
          -;
          -; Output Options:
          -;
          -;   These options affect how matching lines are printed.  The
          -;   defaults are ideal for piping into other programs.
          -;
          -;   --color           highlight matches with color
          -;   --no-color        don't highlight matches (the default)
          -;   -u N, --context N show N lines of context (default 0)
          -
          - - +
          :::lisp
          +(adopt:print-help *ui* :width 60 :option-width 16)
          +; =>
          +; search - print lines that match a regular expression
          +;
          +; USAGE: /usr/local/bin/sbcl PATTERN [FILE...]
          +;
          +; Search FILEs for lines that match the regular expression
          +; PATTERN and print them to standard out.  Several options are
          +; available to control how the matching lines are printed.
          +;
          +; If no files are given (or if - is given as a filename)
          +; standard input will be searched.
          +;
          +; Options:
          +;   -h, --help        display help and exit
          +;
          +; Matching Options:
          +;   -l, --literal     treat PATTERN as a literal string
          +;                     instead of a regex
          +;   -L, --no-literal  treat PATTERN as a regex (the default)
          +;   -c, --case-sensitive
          +;                     match case-sensitively (the default)
          +;   -C, --case-insensitive
          +;                     ignore case when matching
          +;
          +; Output Options:
          +;
          +;   These options affect how matching lines are printed.  The
          +;   defaults are ideal for piping into other programs.
          +;
          +;   --color           highlight matches with color
          +;   --no-color        don't highlight matches (the default)
          +;   -u N, --context N show N lines of context (default 0)
          +

          Error Handling

          For the most part Adopt doesn't try to be too smart about error handling and leaves it up to you.

          However, when Adopt is parsing the command line options it will signal an error of type adopt:unrecognized-option if the user passes a command line option that wasn't defined in the interface:

          -
          (defparameter *ui*
          -  (adopt:make-interface
          -    :name "meow"
          -    :summary "say meow"
          -    :usage "[OPTIONS]"
          -    :help "Say meow.  Like a cat."
          -    :contents (list (make-option 'times
          -                      :parameter "N"
          -                      :long "times"
          -                      :initial-value 1
          -                      :help "Say meow N times (default 1)."
          -                      :reduce #'adopt:last
          -                      :key #'parse-integer))))
          +
          :::lisp
          +(defparameter *ui*
          +  (adopt:make-interface
          +    :name "meow"
          +    :summary "say meow"
          +    :usage "[OPTIONS]"
          +    :help "Say meow.  Like a cat."
          +    :contents (list (make-option 'times
          +                      :parameter "N"
          +                      :long "times"
          +                      :initial-value 1
          +                      :help "Say meow N times (default 1)."
          +                      :reduce #'adopt:last
          +                      :key #'parse-integer))))
           
          -(adopt:parse-options *ui* '("--times" "5"))
          -; =>
          -; NIL
          -; {TIMES: 5}
          +(adopt:parse-options *ui* '("--times" "5"))
          +; =>
          +; NIL
          +; {TIMES: 5}
           
          -(adopt:parse-options *ui* '("--bark"))
          -; =>
          -; No such option "--bark".
          -;    [Condition of type UNRECOGNIZED-OPTION]
          -;
          -; Restarts:
          -;   R 0.  DISCARD-OPTION    - Discard the unrecognized option.
          -;   R 1.  TREAT-AS-ARGUMENT - Treat the unrecognized option as a plain argument.
          -;   R 2.  SUPPLY-NEW-VALUE  - Supply a new value to parse.
          -;   R 3.  RETRY             - Retry SLIME REPL evaluation request.
          -;   R 4. *ABORT             - Return to SLIME's top level.
          -;   R 5.  ABORT             - abort thread (#<THREAD "repl-thread" RUNNING {100AF48413}>)
          -
          - - +(adopt:parse-options *ui* '("--bark")) +; => +; No such option "--bark". +; [Condition of type UNRECOGNIZED-OPTION] +; +; Restarts: +; R 0. DISCARD-OPTION - Discard the unrecognized option. +; R 1. TREAT-AS-ARGUMENT - Treat the unrecognized option as a plain argument. +; R 2. SUPPLY-NEW-VALUE - Supply a new value to parse. +; R 3. RETRY - Retry SLIME REPL evaluation request. +; R 4. *ABORT - Return to SLIME's top level. +; R 5. ABORT - abort thread (#<THREAD "repl-thread" RUNNING {100AF48413}>) +

          Adopt provides three possible restarts for this condition as seen above. Adopt also provides functions with the same names that invoke the restarts properly, to make it easier to use them programatically with handler-bind. For example:

          -
          (handler-bind
          -    ((adopt:unrecognized-option 'adopt:discard-option))
          -  (adopt:parse-options *ui* '("--bark")))
          -; =>
          -; NIL
          -; {TIMES: 1}
          +
          :::lisp
          +(handler-bind
          +    ((adopt:unrecognized-option 'adopt:discard-option))
          +  (adopt:parse-options *ui* '("--bark")))
          +; =>
          +; NIL
          +; {TIMES: 1}
           
          -(handler-bind
          -    ((adopt:unrecognized-option 'adopt:treat-as-argument))
          -  (adopt:parse-options *ui* '("--bark")))
          -; =>
          -; ("--bark")
          -; {TIMES: 1}
          +(handler-bind
          +    ((adopt:unrecognized-option 'adopt:treat-as-argument))
          +  (adopt:parse-options *ui* '("--bark")))
          +; =>
          +; ("--bark")
          +; {TIMES: 1}
           
          -(handler-bind
          -    ((adopt:unrecognized-option
          -       (alexandria:rcurry 'adopt:supply-new-value "--times")))
          -  (adopt:parse-options *ui* '("--bark" "5")))
          -; =>
          -; NIL
          -; {TIMES: 5}
          -
          - - +(handler-bind + ((adopt:unrecognized-option + (alexandria:rcurry 'adopt:supply-new-value "--times"))) + (adopt:parse-options *ui* '("--bark" "5"))) +; => +; NIL +; {TIMES: 5} +

          Generating Man Pages

          We've already seen that Adopt can print a pretty help document, but it can also render man pages for you:

          -
          (with-open-file (out "man/man1/search.1"
          -                  :direction :output
          -                  :if-exists :supersede)
          -  (adopt:print-manual *ui* :stream out))
          -
          - - +
          :::lisp
          +(with-open-file (out "man/man1/search.1"
          +                  :direction :output
          +                  :if-exists :supersede)
          +  (adopt:print-manual *ui* :stream out))
          +

          The generated man page will contain the same information as the help text by default. If you want to override this (e.g. to provide a short summary of an option in the help text, but elaborate more in the manual), you can use the :manual argument to make-interface and make-option:

          -
          (defparameter *option-exclude*
          -  (adopt:make-option 'exclude
          -    :long "exclude"
          -    :parameter "PATTERN"
          -    :help "Exclude PATTERN."
          -    :manual "Exclude lines that match PATTERN (a PERL-compatible regular expression) from the search results.  Multiple PATTERNs can be specified by giving this option multiple times."
          -    :reduce (adopt:flip #'cons)))
          -
          - - +
          :::lisp
          +(defparameter *option-exclude*
          +  (adopt:make-option 'exclude
          +    :long "exclude"
          +    :parameter "PATTERN"
          +    :help "Exclude PATTERN."
          +    :manual "Exclude lines that match PATTERN (a PERL-compatible regular expression) from the search results.  Multiple PATTERNs can be specified by giving this option multiple times."
          +    :reduce (adopt:flip #'cons)))
          +

          In order for man to find the pages, they need to be in the correct place. By default man is usually smart enough to look next to every directory in your $PATH to find a directory called man. So if you put your binaries in @@ -956,8 +920,7 @@

          ClozureCL

          See https://github.com/Clozure/ccl/issues/177.

    - + \ No newline at end of file