--- a/docs/01-usage.markdown Tue Apr 02 17:58:03 2019 -0400
+++ b/docs/01-usage.markdown Fri May 17 22:47:35 2019 -0400
@@ -1,8 +1,8 @@
Usage
=====
-Adopt is a simple library for parsing UNIX-style command line arguments in
-Common Lisp. It was made because none of the other libraries did what I needed.
+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.
[TOC]
@@ -16,9 +16,9 @@
Interfaces
----------
-To get started with Adopt, you should create an interface with the
-`adopt:make-interface` function. This returns an object representing the
-command line interface presented to your users.
+To get started with Adopt you can create an interface object with
+`adopt:make-interface`. This returns an object representing the command line
+interface presented to your users.
### Creating an Interface
@@ -33,13 +33,20 @@
: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."))
-You can now print some help text for your CLI with `adopt:print-help`:
+`make-interface` takes several required arguments:
+
+* `:name` is the name of the program.
+* `:summary` is a concise one-line summary of what it does.
+* `:usage` is a UNIX-style the command line usage string.
+* `: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: … [OPTIONS] PATTERN [FILE]...
+ 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.
@@ -47,10 +54,14 @@
### 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. If you want to
-line break the text in your source code to fit nicely in your 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:
+shouldn't) add extra line breaks when creating your interface.
+
+If you want to line break the text in your source code to fit nicely in your
+text editor, remember that `adopt:make-interface` is just a function — you can
+use `format` (possibly with its [`~Newline` directive][tilde-newline]) to
+preprocess the help text argument:
+
+[tilde-newline]: http://www.lispworks.com/documentation/lw71/CLHS/Body/22_cic.htm
(defparameter *ui*
(adopt:make-interface
@@ -62,19 +73,57 @@
no files are specified, searches ~
standard input instead.")))
-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:
+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."))
(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 (or if - is given as a file name) standard input will be searched instead.")))
+ :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.")
+
+ (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:
+
+[Bobbin]: https://sjl.bitbucket.io/bobbin/
+
+ (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*))
If you want to control the width of the help text lines when they are printed,
`adopt:print-help` takes a `:width` argument:
@@ -89,18 +138,17 @@
expression PATTERN.
If no files are specified (or if - is given as a
- file name) standard input will be searched
+ file name), standard input will be searched
instead.
`adopt:print-help` takes a number of other options — see the API Reference for
more information.
-### Examples
+### 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. `adopt:make-interface` can take an
-`:examples` argument, which should be an alist of `(description . example)`
-conses:
+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
@@ -157,7 +205,7 @@
(adopt:exit)
- (adopt:exit 1)
+ (adopt:exit 1)
(adopt:print-help-and-exit *ui*)
@@ -171,7 +219,7 @@
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.
+yourself if you prefer.
Options
-------
@@ -226,13 +274,13 @@
-l, --literal treat PATTERN as a literal string instead of a regular
expression
-The first argument to `adopt: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 bit, but
-it too is required.
+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
+while, but it too is required.
I prefer to define each option as its own global variable to keep the call to
-`adopt:make-interface` from getting too large and unwieldy, but feel free to do
+`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*
@@ -247,20 +295,14 @@
-------
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:
+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}>
-`adopt:parse-options` returns two values: a list of non-option arguments, and
-a hash table of the option values.
-
-The keys of the hash table are (by default) the option names given as the first
-argument to `adopt:make-option`. We'll see how the option values are determined
-soon.
-
From now on I'll use a special pretty printer for hash tables to make it easier
to see what's inside them:
@@ -269,11 +311,43 @@
("foo.*" "a.txt" "b.txt")
{LITERAL: T, VERSION: NIL, HELP: NIL}
+`parse-options` returns two values:
+
+1. A list of non-option arguments.
+2. An `eql` hash table of the option keys and values.
+
+We'll talk about how the option values are determined soon. The keys of the
+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)))
+
+ ;; …
+
+ (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).
+
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 create with Adopt:
+the overall structure of the programs you'll typically create with Adopt:
+
+ (defun run (pattern files &key literal)
+ ;; Actually do something here.
+ )
(defun toplevel ()
(handler-case
@@ -291,25 +365,525 @@
(error (c)
(adopt:print-error-and-exit c))))
- (sb-ext:save-lisp-and-die "search" :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 functions important
+functions here:
-The `toplevel` function first uses a `handler-case` to trap all `error`s. If
-any error occurs it will print the error message and exit, to avoid confusing
-users by dropping them into a Lisp debugger REPL (which they probably won't
-understand). When you're developing your program yourself you'll want to omit
-this part and let yourself land in the debugger as usual.
+* The `toplevel` function takes care of parsing arguments and exiting with an
+ appropriate status code if necessary.
+* The `run` function takes parsed, Lispy arguments and actually *does*
+ something. When developing (in SLIME, VLIME, etc) you'll call `run`, because
+ you don't want the program to exit when you're developing interactively.
+* The `build` function dumps an executable binary. For more complicated
+ programs you might use something fancier, like ASDF or Shinmera's Deploy
+ library instead.
-Next we use `adopt:parse-options` to parse the command line arguments and
-options. We do some initial checks to see if the user wants `--help` or
-`--version` information. If not, we destructure the arguments into the items we
-expect and call a `run` function with all the information it needs to do its
-job.
+In this example the `toplevel` function first uses a `handler-case` to trap all
+errors. If any error occurs it will print the error message and exit, to avoid
+confusing users by dropping them into a Lisp debugger REPL (which they probably
+won't understand). If you're developing a program just for yourself, you might
+want to omit this part and let yourself land in the debugger as usual.
-If the `destructuring-bind` fails an error will be signaled, and the
-`handler-case` will print it and exit. If you want to be a nice person you
-could check that the `arguments` have the correct shape first, and return
-a friendlier error message to your users if they don't.
+Next it uses `adopt:parse-options` to parse the command line arguments and
+options. It them does some initial checks to see if the user wants `--help` or
+`--version` information. If so, it prints the requested information and exits.
+
+Otherwise it destructures the arguments into the expected items and calls `run`
+with all the information it needs to do its job. If the `destructuring-bind`
+fails an error will be signaled, and the `handler-case` will print it and exit.
+If you want to be a nice person you could check that the `arguments` have the
+correct shape first, and return a friendlier error message to your users if they
+don't.
Computing Values with Reduce
----------------------------
+So far we've talked about how to define an interface, print help text, parse
+a list of options, and the overall structure of the program you'll create with
+Adopt. Now we need to talk about how the options the user specifies are parsed
+and turned into the resulting hash table.
+
+Not all command-line options are the same. There are several common types of
+options in the UNIX world:
+
+* Simple options that are either given or not, like `--help` or `--version`.
+* Boolean options, like git's `-p/--paginate` and `--no-pager`, where both options affect a single boolean flag.
+* Counted options, where the number of times they are given has an effect, like SSH's `-v` option (more `-v`'s means more verbosity).
+* Options that take a single parameter, like Mercurial's `--repository /path/to/repo` option, which specifies the path to a repository to work on.
+* Options that collect all parameters they are given, like rsync's `--exclude PATTERN`, which you can pass multiple times to add several exclusions.
+
+An option-parsing library needs to give you the tools to handle all of these
+cases (and more). Python's [argparse][ap-actions] library, for example, has
+a number of different "actions" to account to handle these various use cases.
+Adopt works differently: it uses an interface similar to [reduce][] to let you
+do whatever you need.
+
+[ap-actions]: https://docs.python.org/3/library/argparse.html#action
+[reduce]: http://www.lispworks.com/documentation/HyperSpec/Body/f_reduce.htm
+
+First: before any options are parsed, all entries in the options hash table have
+their values set to the `:initial-value` given to `make-option` (or `nil` if
+none was specified).
+
+Next: When you create an option you must specify a `:reduce` function that takes
+the current value (and, for options that take a parameter, the given parameter)
+and produces a new value each time the option is given.
+
+You may also specify a `:finally` function that will be called on the final
+value after all parsing is done.
+
+For convenience, if an option takes a parameter you may also specify a `:key`
+function, which will be called on the given string before it is passed to the
+`:reduce` function. For example: you might use this for an option that takes
+integers as arguments with something like `:key #'parse-integer`.
+
+The combination of these four pieces will let you do just about anything you
+might want. Let's look at how to do some common option parsing tasks using
+these as our building blocks.
+
+### 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 information 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`](http://www.lispworks.com/documentation/HyperSpec/Body/f_cons_1.htm)
+function, we can do this more concisely:
+
+ (defparameter *option-help*
+ (adopt:make-option 'help
+ :long "help"
+ :short #\h
+ :help "display help information and exit"
+ :reduce (constantly t)))
+
+### Boolean Options
+
+If we want to have multiple options that both affect the same key in the
+results, we 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)))
+
+ (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'
+
+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
+
+If the last-given option didn't take precedence, they would 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.
+
+### Counting Options
+
+To define an option that counts how many times it's been given, like SSH's `-v`,
+we can say:
+
+ (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, we
+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)))
+
+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))
+
+### Multiple-Parameter Options
+
+Collecting every parameter given can be done in a number of different ways. One
+way 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))))
+
+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](https://en.wikibooks.org/wiki/Haskell/Higher-order_functions#Flipping_arguments),
+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)))
+
+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, 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)))))
+
+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:
+
+ (defparameter *option-exclude*
+ (adopt:make-option 'exclude
+ :long "exclude"
+ :parameter "PATTERN"
+ :help "exclude PATTERN (may be given multiple times)"
+ :initial-value nil
+ :reduce #'adopt:collect))
+
+A more efficient (though slightly uglier) solution could 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)"
+ :initial-value nil
+ :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
+`:reduce`, `:initial-value`, and `:finally` will let you do just about anything.
+
+
+Required Options
+----------------
+
+Adopt doesn't have a concept of a required option. Not only is "required
+option" an oxymoron, but it's almost never what you want — if a user types
+`program --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)
+ (adopt:print-error-and-exit "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)))
+
+ (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
+ :help "treat PATTERN as a regex (the default)"
+ :long "no-literal"
+ :short #\L
+ :result-key 'literal
+ :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-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-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 *group-matching*
+ (adopt:make-group 'matching-options
+ :title "Matching Options"
+ :help "These options affect how lines are matched."
+ :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."
+ :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:
+ --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))))
+
+ (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 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}
+
+ (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}
+
+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))
+
+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)))
+
+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
+`/home/me/bin/` you can put your man pages in `/home/me/man/` under the
+appropriate subdirectories and it should all Just Work™. Consult the `man`
+documentation for more information.
+
+Implementation Specifics
+------------------------
+
+### SBCL
+
+### ClozureCL