--- a/adopt/_dmedia/style.less Wed Apr 17 10:30:37 2019 -0400
+++ b/adopt/_dmedia/style.less Fri May 17 23:12:54 2019 -0400
@@ -2,11 +2,11 @@
overflow-y: scroll;
}
.wrap {
- width: 600px;
+ width: 640px;
margin: 40px auto 180px;
}
body {
- font-size: 18px;
+ font-size: 16px;
line-height: 1.4;
font-family: Georgia, Palatino, "Palatino Linotype", serif;
color: #111111;
@@ -27,11 +27,11 @@
}
}
h1 {
- font-size: 48px;
+ font-size: 40px;
line-height: 1;
}
h2 {
- font-size: 34px;
+ font-size: 28px;
line-height: 1;
margin-top: 40px;
margin-bottom: 0;
@@ -44,7 +44,7 @@
}
}
h3 {
- font-size: 26px;
+ font-size: 22px;
line-height: 1;
margin-top: 40px;
margin-bottom: 0;
@@ -57,7 +57,7 @@
}
}
h4 {
- font-size: 20px;
+ font-size: 18px;
line-height: 1;
margin-top: 30px;
margin-bottom: 0;
@@ -83,7 +83,7 @@
}
}
pre {
- font-size: 15px;
+ font-size: 14px;
line-height: 1.3;
white-space: pre;
overflow-x: auto;
@@ -93,7 +93,7 @@
font-family: Menlo, Monaco, Consolas, monospace;
}
code {
- font-size: 15px;
+ font-size: 14px;
border: 1px solid #fdd;
background: #fffafa;
padding: 1px 4px;
--- a/adopt/usage/index.html Wed Apr 17 10:30:37 2019 -0400
+++ b/adopt/usage/index.html Fri May 17 23:12:54 2019 -0400
@@ -12,154 +12,215 @@
<div class="wrap">
<header><h1><a href="..">Adopt</a></h1></header>
<div class="markdown">
-<h1 id="usage"><a href="">Usage</a></h1><p>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.</p>
+<h1 id="usage"><a href="">Usage</a></h1><p>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.</p>
<div class="toc">
<ul>
<li><a href="#package">Package</a></li>
<li><a href="#interfaces">Interfaces</a><ul>
<li><a href="#creating-an-interface">Creating an Interface</a></li>
<li><a href="#line-wrapping">Line Wrapping</a></li>
-<li><a href="#examples">Examples</a></li>
+<li><a href="#adding-examples">Adding Examples</a></li>
</ul>
</li>
<li><a href="#exiting">Exiting</a></li>
<li><a href="#options">Options</a></li>
<li><a href="#parsing">Parsing</a></li>
<li><a href="#top-level-structure">Top-Level Structure</a></li>
-<li><a href="#computing-values-with-reduce">Computing Values with Reduce</a></li>
+<li><a href="#computing-values-with-reduce">Computing Values with Reduce</a><ul>
+<li><a href="#simple-options">Simple Options</a></li>
+<li><a href="#boolean-options">Boolean Options</a></li>
+<li><a href="#counting-options">Counting Options</a></li>
+<li><a href="#single-parameter-options">Single-Parameter Options</a></li>
+<li><a href="#multiple-parameter-options">Multiple-Parameter Options</a></li>
+</ul>
+</li>
+<li><a href="#required-options">Required Options</a></li>
+<li><a href="#option-groups">Option Groups</a></li>
+<li><a href="#error-handling">Error Handling</a></li>
+<li><a href="#generating-man-pages">Generating Man Pages</a></li>
+<li><a href="#implementation-specifics">Implementation Specifics</a><ul>
+<li><a href="#sbcl">SBCL</a></li>
+<li><a href="#clozurecl">ClozureCL</a></li>
+</ul>
+</li>
</ul></div>
<h2 id="package">Package</h2>
<p>All core Adopt functions are in the <code>adopt</code> package. Several of the symbols in
adopt shadow those in the <code>common-lisp</code> package, so you should probably use
namespaced <code>adopt:…</code> symbols instead of <code>USE</code>ing the package.</p>
<h2 id="interfaces">Interfaces</h2>
-<p>To get started with Adopt, you should create an interface with the
-<code>adopt:make-interface</code> function. This returns an object representing the
-command line interface presented to your users.</p>
+<p>To get started with Adopt you can create an interface object with
+<code>adopt:make-interface</code>. This returns an object representing the command line
+interface presented to your users.</p>
<h3 id="creating-an-interface">Creating an Interface</h3>
<p>Let's say you're developing a program to search the contents of files (because
the world certainly needs <em>another</em> <code>grep</code> replacement). You might start with
something like:</p>
-<div class="codehilite"><pre><span/>(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."))
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:summary</span> <span class="s">"search files for a regular expression"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS] PATTERN [FILE]..."</span>
+ <span class="ss">:help</span> <span class="s">"Search the contents of each FILE for the regular expression PATTERN. If no files are specified, searches standard input instead."</span><span class="p">))</span>
</pre></div>
-<p>You can now print some help text for your CLI with <code>adopt:print-help</code>:</p>
-<div class="codehilite"><pre><span/>(adopt:print-help *ui*)
-; =>
-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, searches standard input instead.
+<p><code>make-interface</code> takes several required arguments:</p>
+<ul>
+<li><code>:name</code> is the name of the program.</li>
+<li><code>:summary</code> is a concise one-line summary of what it does.</li>
+<li><code>:usage</code> is a UNIX-style the command line usage string.</li>
+<li><code>:help</code> is a longer description of the program.</li>
+</ul>
+<p>You can now print some pretty help text for the CLI with <code>adopt:print-help</code>:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:print-help</span> <span class="vg">*ui*</span><span class="p">)</span>
+<span class="c1">; =></span>
+<span class="c1">; search - search files for a regular expression</span>
+<span class="c1">;</span>
+<span class="c1">; USAGE: /path/to/binary [OPTIONS] PATTERN [FILE]...</span>
+<span class="c1">;</span>
+<span class="c1">; Search the contents of each FILE for the regular expression PATTERN. If no</span>
+<span class="c1">; files are specified, searches standard input instead.</span>
</pre></div>
<h3 id="line-wrapping">Line Wrapping</h3>
<p>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 <code>adopt:make-interface</code> is just a function — you can use <code>format</code> (possibly
-with its <code>~Newline</code> directive) to preprocess the help text argument:</p>
-<div class="codehilite"><pre><span/>(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.")))
+shouldn't) add extra line breaks when creating your interface.</p>
+<p>If you want to line break the text in your source code to fit nicely in your
+text editor, remember that <code>adopt:make-interface</code> is just a function — you can
+use <code>format</code> (possibly with its <a href="http://www.lispworks.com/documentation/lw71/CLHS/Body/22_cic.htm"><code>~Newline</code> directive</a>) to
+preprocess the help text argument:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:summary</span> <span class="s">"search files for a regular expression"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS] PATTERN [FILE]..."</span>
+ <span class="ss">:help</span> <span class="p">(</span><span class="nb">format</span> <span class="no">nil</span> <span class="s">"Search the contents of each FILE for ~</span>
+<span class="s"> the regular expression PATTERN. If ~</span>
+<span class="s"> no files are specified, searches ~</span>
+<span class="s"> standard input instead."</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>If you want to pull out the documentation string into its own variable to keep
+that <code>make-interface</code> call from getting too unwieldy, you can certainly do that:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*help-text*</span>
+ <span class="p">(</span><span class="nb">format</span> <span class="no">nil</span> <span class="s">"Search the contents of each FILE for the ~</span>
+<span class="s"> regular expression PATTERN. If no files ~</span>
+<span class="s"> are specified, searches standard input ~</span>
+<span class="s"> instead."</span><span class="p">))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:summary</span> <span class="s">"search files for a regular expression"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS] PATTERN [FILE]..."</span>
+ <span class="ss">:help</span> <span class="vg">*help-text*</span><span class="p">))</span>
</pre></div>
-<p>Adopt's line-wrapping library [Bobbin][] will only ever <em>add</em> line breaks, never
+<p>The <code>(defparameter … (format nil …))</code> pattern can be tedious to write, so Adopt
+provides a helper macro <code>define-string</code> that does exactly that:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:define-string</span> <span class="vg">*help-text*</span>
+ <span class="s">"Search the contents of each FILE for the regular ~</span>
+<span class="s"> expression PATTERN. If no files are specified, ~</span>
+<span class="s"> searches standard input instead."</span><span class="p">)</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:summary</span> <span class="s">"search files for a regular expression"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS] PATTERN [FILE]..."</span>
+ <span class="ss">:help</span> <span class="vg">*help-text*</span><span class="p">))</span>
+</pre></div>
+
+
+<p>Adopt's line-wrapping library <a href="https://sjl.bitbucket.io/bobbin/">Bobbin</a> will only ever <em>add</em> 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:</p>
-<div class="codehilite"><pre><span/>(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.")))
+have multiple paragraphs in your help text. Once again, <code>format</code> is your
+friend:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:define-string</span> <span class="vg">*help-text*</span>
+ <span class="s">"Search the contents of each FILE for the regular ~</span>
+<span class="s"> expression PATTERN.~@</span>
+<span class="s"> ~@</span>
+<span class="s"> If no files are specified (or if - is given as a ~</span>
+<span class="s"> file name), standard input will be searched instead."</span><span class="p">)</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:summary</span> <span class="s">"search files for a regular expression"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS] PATTERN [FILE]..."</span>
+ <span class="ss">:help</span> <span class="vg">*help-text*</span><span class="p">))</span>
</pre></div>
<p>If you want to control the width of the help text lines when they are printed,
<code>adopt:print-help</code> takes a <code>:width</code> argument:</p>
-<div class="codehilite"><pre><span/>(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.
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:print-help</span> <span class="vg">*ui*</span> <span class="ss">:width</span> <span class="mi">50</span><span class="p">)</span>
+<span class="c1">; =></span>
+<span class="c1">; search - search files for a regular expression</span>
+<span class="c1">;</span>
+<span class="c1">; USAGE: … [OPTIONS] PATTERN [FILE]...</span>
+<span class="c1">;</span>
+<span class="c1">; Search the contents of each FILE for the regular</span>
+<span class="c1">; expression PATTERN.</span>
+<span class="c1">;</span>
+<span class="c1">; If no files are specified (or if - is given as a</span>
+<span class="c1">; file name), standard input will be searched</span>
+<span class="c1">; instead.</span>
</pre></div>
<p><code>adopt:print-help</code> takes a number of other options — see the API Reference for
more information.</p>
-<h3 id="examples">Examples</h3>
+<h3 id="adding-examples">Adding Examples</h3>
<p>Describing the CLI in detail is helpful, but users can often learn a lot more by
-seeing a few examples of its usage. <code>adopt:make-interface</code> can take an
-<code>:examples</code> argument, which should be an alist of <code>(description . example)</code>
-conses:</p>
-<div class="codehilite"><pre><span/>(defparameter *ui*
- (adopt:make-interface
- :name "search"
- :summary "search files for a regular expression"
- :usage "[OPTIONS] PATTERN [FILE]..."
- :help …
- :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
+seeing a few examples of its usage. <code>make-interface</code> can take an <code>:examples</code>
+argument, which should be an alist of <code>(description . example)</code> conses:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:summary</span> <span class="s">"search files for a regular expression"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS] PATTERN [FILE]..."</span>
+ <span class="ss">:help</span> <span class="vg">*help-text*</span>
+ <span class="ss">:examples</span>
+ <span class="o">'</span><span class="p">((</span><span class="s">"Search foo.txt for the string 'hello':"</span>
+ <span class="o">.</span> <span class="s">"search hello foo.txt"</span><span class="p">)</span>
+ <span class="p">(</span><span class="s">"Search standard input for lines starting with x:"</span>
+ <span class="o">.</span> <span class="s">"search '^x' -"</span><span class="p">)</span>
+ <span class="p">(</span><span class="s">"Watch the file log.txt for lines containing the username steve.losh:"</span>
+ <span class="o">.</span> <span class="s">"tail foo/bar/baz/log.txt | search --literal steve.losh -"</span><span class="p">))))</span>
-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 -
+<span class="p">(</span><span class="nv">adopt:print-help</span> <span class="vg">*ui*</span> <span class="ss">:width</span> <span class="mi">50</span><span class="p">)</span>
+<span class="c1">; =></span>
+<span class="c1">; search - search files for a regular expression</span>
+<span class="c1">;</span>
+<span class="c1">; USAGE: … [OPTIONS] PATTERN [FILE]...</span>
+<span class="c1">;</span>
+<span class="c1">; Search the contents of each FILE for the regular</span>
+<span class="c1">; expression PATTERN.</span>
+<span class="c1">;</span>
+<span class="c1">; If no files are specified (or if - is given as a</span>
+<span class="c1">; file name) standard input will be searched</span>
+<span class="c1">; instead.</span>
+<span class="c1">;</span>
+<span class="c1">; Examples:</span>
+<span class="c1">;</span>
+<span class="c1">; Search foo.txt for the string 'hello':</span>
+<span class="c1">;</span>
+<span class="c1">; search hello foo.txt</span>
+<span class="c1">;</span>
+<span class="c1">; Search standard input for lines starting with x:</span>
+<span class="c1">;</span>
+<span class="c1">; search '^x' -</span>
+<span class="c1">;</span>
+<span class="c1">; Watch the file log.txt for lines containing the</span>
+<span class="c1">; username steve.losh:</span>
+<span class="c1">;</span>
+<span class="c1">; tail foo/bar/baz/log.txt | search --literal steve.losh -</span>
</pre></div>
@@ -170,157 +231,668 @@
<h2 id="exiting">Exiting</h2>
<p>Adopt provides some helpful utility functions to exit out of your program with
a UNIX exit code. These do what you think they do:</p>
-<div class="codehilite"><pre><span/>(adopt:exit)
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:exit</span><span class="p">)</span>
-(adopt:exit 1)
+<span class="p">(</span><span class="nv">adopt:exit</span> <span class="mi">1</span><span class="p">)</span>
-(adopt:print-help-and-exit *ui*)
+<span class="p">(</span><span class="nv">adopt:print-help-and-exit</span> <span class="vg">*ui*</span><span class="p">)</span>
-(adopt:print-help-and-exit *ui*
- :stream *error-output*
- :exit-code 1)
+<span class="p">(</span><span class="nv">adopt:print-help-and-exit</span> <span class="vg">*ui*</span>
+ <span class="ss">:stream</span> <span class="vg">*error-output*</span>
+ <span class="ss">:exit-code</span> <span class="mi">1</span><span class="p">)</span>
-(handler-case (assert (= 1 0))
- (error (err)
- (adopt:print-error-and-exit err)))
+<span class="p">(</span><span class="nb">handler-case</span> <span class="p">(</span><span class="nb">assert</span> <span class="p">(</span><span class="nb">=</span> <span class="mi">1</span> <span class="mi">0</span><span class="p">))</span>
+ <span class="p">(</span><span class="nb">error</span> <span class="p">(</span><span class="nv">err</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:print-error-and-exit</span> <span class="nv">err</span><span class="p">)))</span>
</pre></div>
<p>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.</p>
+yourself if you prefer.</p>
<h2 id="options">Options</h2>
<p>Now that you know how to create an interface, you can create some options to use
inside it with <code>adopt:make-option</code>:</p>
-<div class="codehilite"><pre><span/>(defparameter *option-version*
- (adopt:make-option 'version
- :long "version"
- :help "display version information and exit"
- :reduce (constantly t)))
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-version*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'version</span>
+ <span class="ss">:long</span> <span class="s">"version"</span>
+ <span class="ss">:help</span> <span class="s">"display version information and exit"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
-(defparameter *option-help*
- (adopt:make-option 'help
- :long "help"
- :short #\h
- :help "display help information and exit"
- :reduce (constantly t)))
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-help*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'help</span>
+ <span class="ss">:long</span> <span class="s">"help"</span>
+ <span class="ss">:short</span> <span class="sc">#\h</span>
+ <span class="ss">:help</span> <span class="s">"display help information and exit"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
-(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)))
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-literal*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'literal</span>
+ <span class="ss">:long</span> <span class="s">"literal"</span>
+ <span class="ss">:short</span> <span class="sc">#\l</span>
+ <span class="ss">:help</span> <span class="s">"treat PATTERN as a literal string instead of a regular expression"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
-(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*)))
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:summary</span> <span class="s">"search files for a regular expression"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS] PATTERN [FILE]..."</span>
+ <span class="ss">:help</span> <span class="s">"Search the contents of …"</span>
+ <span class="ss">:contents</span> <span class="p">(</span><span class="nb">list</span>
+ <span class="vg">*option-version*</span>
+ <span class="vg">*option-help*</span>
+ <span class="vg">*option-literal*</span><span class="p">)))</span>
</pre></div>
<p>Adopt will automatically add the options to the help text:</p>
-<div class="codehilite"><pre><span/>(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 information and exit
- -h, --help display help information and exit
- -l, --literal treat PATTERN as a literal string instead of a regular
- expression
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:print-help</span> <span class="vg">*ui*</span><span class="p">)</span>
+<span class="c1">; =></span>
+<span class="c1">; search - search files for a regular expression</span>
+<span class="c1">;</span>
+<span class="c1">; USAGE: /usr/local/bin/sbcl [OPTIONS] PATTERN [FILE]...</span>
+<span class="c1">;</span>
+<span class="c1">; Search the contents of …</span>
+<span class="c1">;</span>
+<span class="c1">; Options:</span>
+<span class="c1">; --version display version information and exit</span>
+<span class="c1">; -h, --help display help information and exit</span>
+<span class="c1">; -l, --literal treat PATTERN as a literal string instead of a regular</span>
+<span class="c1">; expression</span>
</pre></div>
-<p>The first argument to <code>adopt:make-option</code> is the name of the option, which we'll
-see put to use shortly. At least one of <code>:short</code> and <code>:long</code> is required, and
-<code>:help</code> text must be specified. We'll talk more about <code>:reduce</code> in a bit, but
-it too is required.</p>
+<p>The first argument to <code>make-option</code> is the name of the option, which we'll see
+put to use shortly. At least one of <code>:short</code> and <code>:long</code> is required, and
+<code>:help</code> text must be specified. We'll talk more about <code>:reduce</code> in a little
+while, but it too is required.</p>
<p>I prefer to define each option as its own global variable to keep the call to
-<code>adopt:make-interface</code> from getting too large and unwieldy, but feel free to do
+<code>make-interface</code> from getting too large and unwieldy, but feel free to do
something like this if you prefer to avoid cluttering your package:</p>
-<div class="codehilite"><pre><span/>(defparameter *ui*
- (adopt:make-interface
- …
- :contents
- (list (adopt:make-option 'foo …)
- (adopt:make-option 'bar …)
- …)))
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="err">…</span>
+ <span class="ss">:contents</span>
+ <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'foo</span> <span class="err">…</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'bar</span> <span class="err">…</span><span class="p">)</span>
+ <span class="err">…</span><span class="p">)))</span>
</pre></div>
<h2 id="parsing">Parsing</h2>
<p>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:</p>
-<div class="codehilite"><pre><span/>(adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt"))
-; =>
-("foo.*" "a.txt" "b.txt")
-#<HASH-TABLE :TEST EQL :COUNT 3 {10103142A3}>
+parse a list of strings we've received as command line arguments with
+<code>adopt:parse-options</code>:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"foo.*"</span> <span class="s">"--literal"</span> <span class="s">"a.txt"</span> <span class="s">"b.txt"</span><span class="p">))</span>
+<span class="c1">; =></span>
+<span class="c1">; ("foo.*" "a.txt" "b.txt")</span>
+<span class="c1">; #<HASH-TABLE :TEST EQL :COUNT 3 {10103142A3}></span>
+</pre></div>
+
+
+<p>From now on I'll use a special pretty printer for hash tables to make it easier
+to see what's inside them:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"foo.*"</span> <span class="s">"--literal"</span> <span class="s">"a.txt"</span> <span class="s">"b.txt"</span><span class="p">))</span>
+<span class="c1">; =></span>
+<span class="c1">; ("foo.*" "a.txt" "b.txt")</span>
+<span class="c1">; {LITERAL: T, VERSION: NIL, HELP: NIL}</span>
+</pre></div>
+
+
+<p><code>parse-options</code> returns two values:</p>
+<ol>
+<li>A list of non-option arguments.</li>
+<li>An <code>eql</code> hash table of the option keys and values.</li>
+</ol>
+<p>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
+<code>make-option</code>. You can specify a different key for a particular option with the
+<code>:result-key</code> argument to <code>make-option</code>:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-literal*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'literal</span>
+ <span class="ss">:result-key</span> <span class="ss">'pattern-is-literal</span>
+ <span class="ss">:long</span> <span class="s">"literal"</span>
+ <span class="ss">:short</span> <span class="sc">#\l</span>
+ <span class="ss">:help</span> <span class="s">"treat PATTERN as a literal string instead of a regular expression"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
+
+<span class="c1">;; …</span>
+
+<span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"foo.*"</span> <span class="s">"--literal"</span> <span class="s">"a.txt"</span> <span class="s">"b.txt"</span><span class="p">))</span>
+<span class="c1">; =></span>
+<span class="c1">; ("foo.*" "a.txt" "b.txt")</span>
+<span class="c1">; {PATTERN-IS-LITERAL: T, VERSION: NIL, HELP: NIL}</span>
+</pre></div>
+
+
+<p>This can come in useful if you want multiple options that affect the same result
+(e.g. <code>--verbose</code> and <code>--silent</code> flags that toggle extra log output on and off).</p>
+<h2 id="top-level-structure">Top-Level Structure</h2>
+<p>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:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defun</span> <span class="nv">run</span> <span class="p">(</span><span class="nv">pattern</span> <span class="nv">files</span> <span class="k">&key</span> <span class="nv">literal</span><span class="p">)</span>
+ <span class="c1">;; Actually do something here.</span>
+ <span class="p">)</span>
+
+<span class="p">(</span><span class="nb">defun</span> <span class="nv">toplevel</span> <span class="p">()</span>
+ <span class="p">(</span><span class="nb">handler-case</span>
+ <span class="p">(</span><span class="nb">multiple-value-bind</span> <span class="p">(</span><span class="nv">arguments</span> <span class="nv">options</span><span class="p">)</span> <span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span><span class="p">)</span>
+ <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nb">gethash</span> <span class="ss">'help</span> <span class="nv">options</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:print-help-and-exit</span> <span class="vg">*ui*</span><span class="p">))</span>
+ <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nb">gethash</span> <span class="ss">'version</span> <span class="nv">options</span><span class="p">)</span>
+ <span class="p">(</span><span class="nb">format</span> <span class="no">t</span> <span class="s">"1.0.0~%"</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:exit</span><span class="p">))</span>
+ <span class="p">(</span><span class="nb">destructuring-bind</span> <span class="p">(</span><span class="nv">pattern</span> <span class="o">.</span> <span class="nv">files</span><span class="p">)</span> <span class="nv">arguments</span>
+ <span class="p">(</span><span class="nv">run</span> <span class="nv">pattern</span>
+ <span class="nv">files</span>
+ <span class="ss">:literal</span> <span class="p">(</span><span class="nb">gethash</span> <span class="ss">'literal</span> <span class="nv">options</span><span class="p">))))</span>
+ <span class="p">(</span><span class="nb">error</span> <span class="p">(</span><span class="nv">c</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:print-error-and-exit</span> <span class="nv">c</span><span class="p">))))</span>
+
+<span class="p">(</span><span class="nb">defun</span> <span class="nv">build</span> <span class="p">()</span>
+ <span class="p">(</span><span class="nv">sb-ext:save-lisp-and-die</span> <span class="s">"search"</span> <span class="ss">:executable</span> <span class="no">t</span> <span class="ss">:toplevel</span> <span class="nf">#'</span><span class="nv">toplevel</span><span class="p">))</span>
</pre></div>
-<p><code>adopt:parse-options</code> returns two values: a list of non-option arguments, and
-a hash table of the option values.</p>
-<p>The keys of the hash table are (by default) the option names given as the first
-argument to <code>adopt:make-option</code>. We'll see how the option values are determined
-soon.</p>
-<p>From now on I'll use a special pretty printer for hash tables to make it easier
-to see what's inside them:</p>
-<div class="codehilite"><pre><span/>(adopt:parse-options *ui* '("foo.*" "--literal" "a.txt" "b.txt"))
-; =>
-("foo.*" "a.txt" "b.txt")
-{LITERAL: T, VERSION: NIL, HELP: NIL}
+<p>This is a typical way to use Adopt. There are three functions important
+functions here:</p>
+<ul>
+<li>The <code>toplevel</code> function takes care of parsing arguments and exiting with an
+ appropriate status code if necessary.</li>
+<li>The <code>run</code> function takes parsed, Lispy arguments and actually <em>does</em>
+ something. When developing (in SLIME, VLIME, etc) you'll call <code>run</code>, because
+ you don't want the program to exit when you're developing interactively.</li>
+<li>The <code>build</code> function dumps an executable binary. For more complicated
+ programs you might use something fancier, like ASDF or Shinmera's Deploy
+ library instead.</li>
+</ul>
+<p>In this example the <code>toplevel</code> function first uses a <code>handler-case</code> 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.</p>
+<p>Next it uses <code>adopt:parse-options</code> to parse the command line arguments and
+options. It them does some initial checks to see if the user wants <code>--help</code> or
+<code>--version</code> information. If so, it prints the requested information and exits.</p>
+<p>Otherwise it destructures the arguments into the expected items and calls <code>run</code>
+with all the information it needs to do its job. If the <code>destructuring-bind</code>
+fails an error will be signaled, and the <code>handler-case</code> will print it and exit.
+If you want to be a nice person you could check that the <code>arguments</code> have the
+correct shape first, and return a friendlier error message to your users if they
+don't.</p>
+<h2 id="computing-values-with-reduce">Computing Values with Reduce</h2>
+<p>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.</p>
+<p>Not all command-line options are the same. There are several common types of
+options in the UNIX world:</p>
+<ul>
+<li>Simple options that are either given or not, like <code>--help</code> or <code>--version</code>.</li>
+<li>Boolean options, like git's <code>-p/--paginate</code> and <code>--no-pager</code>, where both options affect a single boolean flag.</li>
+<li>Counted options, where the number of times they are given has an effect, like SSH's <code>-v</code> option (more <code>-v</code>'s means more verbosity).</li>
+<li>Options that take a single parameter, like Mercurial's <code>--repository /path/to/repo</code> option, which specifies the path to a repository to work on.</li>
+<li>Options that collect all parameters they are given, like rsync's <code>--exclude PATTERN</code>, which you can pass multiple times to add several exclusions.</li>
+</ul>
+<p>An option-parsing library needs to give you the tools to handle all of these
+cases (and more). Python's <a href="https://docs.python.org/3/library/argparse.html#action">argparse</a> 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 <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_reduce.htm">reduce</a> to let you
+do whatever you need.</p>
+<p>First: before any options are parsed, all entries in the options hash table have
+their values set to the <code>:initial-value</code> given to <code>make-option</code> (or <code>nil</code> if
+none was specified).</p>
+<p>Next: When you create an option you must specify a <code>:reduce</code> 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.</p>
+<p>You may also specify a <code>:finally</code> function that will be called on the final
+value after all parsing is done.</p>
+<p>For convenience, if an option takes a parameter you may also specify a <code>:key</code>
+function, which will be called on the given string before it is passed to the
+<code>:reduce</code> function. For example: you might use this for an option that takes
+integers as arguments with something like <code>:key #'parse-integer</code>.</p>
+<p>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.</p>
+<h3 id="simple-options">Simple Options</h3>
+<p>To define an option that just tracks whether it's ever been given, you can do
+something like:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-help*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'help</span>
+ <span class="ss">:long</span> <span class="s">"help"</span>
+ <span class="ss">:short</span> <span class="sc">#\h</span>
+ <span class="ss">:help</span> <span class="s">"display help information and exit"</span>
+ <span class="ss">:initial-value</span> <span class="no">nil</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nv">current-value</span><span class="p">)</span>
+ <span class="p">(</span><span class="k">declare</span> <span class="p">(</span><span class="k">ignore</span> <span class="nv">current-value</span><span class="p">))</span>
+ <span class="no">t</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>But since <code>nil</code> is the default initial value and Common Lisp provides the handy
+<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_cons_1.htm"><code>constantly</code></a>
+function, you can do this more concisely:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-help*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'help</span>
+ <span class="ss">:long</span> <span class="s">"help"</span>
+ <span class="ss">:short</span> <span class="sc">#\h</span>
+ <span class="ss">:help</span> <span class="s">"display help information and exit"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
+</pre></div>
+
+
+<h3 id="boolean-options">Boolean Options</h3>
+<p>If you want to have multiple options that both affect the same key in the
+results, you can use <code>:result-key</code> to do this:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-paginate*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'paginate</span>
+ <span class="ss">:long</span> <span class="s">"paginate"</span>
+ <span class="ss">:short</span> <span class="sc">#\p</span>
+ <span class="ss">:help</span> <span class="s">"turn pagination on"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-no-paginate*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'no-paginate</span>
+ <span class="ss">:result-key</span> <span class="ss">'paginate</span>
+ <span class="ss">:long</span> <span class="s">"no-paginate"</span>
+ <span class="ss">:short</span> <span class="sc">#\P</span>
+ <span class="ss">:help</span> <span class="s">"turn pagination off (the default)"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">nil</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>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:</p>
+<div class="codehilite"><pre><span/><span class="nb">alias</span> <span class="nv">g</span><span class="o">=</span><span class="s1">'git --paginate --color=always'</span>
+</pre></div>
+
+
+<p>but still lets them override an option at runtime for a single invocation:</p>
+<div class="codehilite"><pre><span/>g --no-paginate log
+<span class="c1"># expands to: git --paginate --color=always --no-paginate log</span>
+</pre></div>
+
+
+<p>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 <em>do</em> want by
+hand. This is annoying, so it's usually better to let the last one win.</p>
+<h3 id="counting-options">Counting Options</h3>
+<p>To define an option that counts how many times it's been given, like SSH's <code>-v</code>,
+you can use something like this:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-verbosity*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'verbosity</span>
+ <span class="ss">:short</span> <span class="sc">#\v</span>
+ <span class="ss">:help</span> <span class="s">"output more verbose logs"</span>
+ <span class="ss">:initial-value</span> <span class="mi">0</span>
+ <span class="ss">:reduce</span> <span class="nf">#'</span><span class="nb">1+</span><span class="p">))</span>
+</pre></div>
+
+
+<h3 id="single-parameter-options">Single-Parameter Options</h3>
+<p>To define an option that takes a parameter and only keeps the last one given,
+you can do something like:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-repository*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'repository</span>
+ <span class="ss">:parameter</span> <span class="s">"PATTERN"</span>
+ <span class="ss">:long</span> <span class="s">"repository"</span>
+ <span class="ss">:short</span> <span class="sc">#\R</span>
+ <span class="ss">:help</span> <span class="s">"path to the repository (default .)"</span>
+ <span class="ss">:initial-value</span> <span class="s">"."</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nv">prev</span> <span class="nv">new</span><span class="p">)</span>
+ <span class="p">(</span><span class="k">declare</span> <span class="p">(</span><span class="k">ignore</span> <span class="nv">prev</span><span class="p">))</span>
+ <span class="nv">new</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>Specifying the <code>:parameter</code> argument makes this option a parameter-taking
+option, which means the <code>:reduce</code> function will be called with the current value
+and the given parameter each time.</p>
+<p>Writing that <code>lambda</code> out by hand every time would be tedious. Adopt provides
+a function called <code>last</code> (as in "keep the <em>last</em> parameter given") that does
+exactly that:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-repository*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'repository</span>
+ <span class="ss">:long</span> <span class="s">"repository"</span>
+ <span class="ss">:short</span> <span class="sc">#\R</span>
+ <span class="ss">:help</span> <span class="s">"path to the repository (default .)"</span>
+ <span class="ss">:initial-value</span> <span class="s">"."</span>
+ <span class="ss">:reduce</span> <span class="nf">#'</span><span class="nv">adopt:last</span><span class="p">))</span>
+</pre></div>
+
+
+<h3 id="multiple-parameter-options">Multiple-Parameter Options</h3>
+<p>Collecting every parameter given can be done in a number of different ways. One
+strategy could be:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-exclude*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'exclude</span>
+ <span class="ss">:long</span> <span class="s">"exclude"</span>
+ <span class="ss">:parameter</span> <span class="s">"PATTERN"</span>
+ <span class="ss">:help</span> <span class="s">"exclude PATTERN (may be given multiple times)"</span>
+ <span class="ss">:initial-value</span> <span class="no">nil</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nv">patterns</span> <span class="nv">new</span><span class="p">)</span>
+ <span class="p">(</span><span class="nb">cons</span> <span class="nv">new</span> <span class="nv">patterns</span><span class="p">))))</span>
+</pre></div>
+
+
+<p>You might notice that the <code>:reduce</code> function here is just <code>cons</code> with its
+arguments flipped. Common Lisp doesn't have a function like Haskell's
+<a href="https://en.wikibooks.org/wiki/Haskell/Higher-order_functions#Flipping_arguments">flip</a>,
+so Adopt provides it:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-exclude*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'exclude</span>
+ <span class="ss">:long</span> <span class="s">"exclude"</span>
+ <span class="ss">:parameter</span> <span class="s">"PATTERN"</span>
+ <span class="ss">:help</span> <span class="s">"exclude PATTERN (may be given multiple times)"</span>
+ <span class="ss">:initial-value</span> <span class="no">nil</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nv">adopt:flip</span> <span class="nf">#'</span><span class="nb">cons</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>Note that the result of this will be a fresh list of all the given parameters,
+but their order will be reversed because <code>cons</code> 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
+<code>:reduce</code> function:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-exclude*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'exclude</span>
+ <span class="ss">:long</span> <span class="s">"exclude"</span>
+ <span class="ss">:parameter</span> <span class="s">"PATTERN"</span>
+ <span class="ss">:help</span> <span class="s">"exclude PATTERN (may be given multiple times)"</span>
+ <span class="ss">:initial-value</span> <span class="no">nil</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nv">patterns</span> <span class="nv">new</span><span class="p">)</span>
+ <span class="p">(</span><span class="nb">append</span> <span class="nv">patterns</span> <span class="p">(</span><span class="nb">list</span> <span class="nv">new</span><span class="p">)))))</span>
</pre></div>
-<h2 id="top-level-structure">Top-Level Structure</h2>
-<p>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:</p>
-<div class="codehilite"><pre><span/>(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))))
+<p>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
+<code>collect</code> that does exactly this, so you don't have to type out that <code>lambda</code>
+yourself (and <code>nil</code> is the default initial value, so you don't need that
+either):</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-exclude*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'exclude</span>
+ <span class="ss">:long</span> <span class="s">"exclude"</span>
+ <span class="ss">:parameter</span> <span class="s">"PATTERN"</span>
+ <span class="ss">:help</span> <span class="s">"exclude PATTERN (may be given multiple times)"</span>
+ <span class="ss">:reduce</span> <span class="nf">#'</span><span class="nv">adopt:collect</span><span class="p">))</span>
+</pre></div>
+
-(sb-ext:save-lisp-and-die "search" :toplevel #'toplevel)
+<p>A more efficient (though slightly uglier) solution would be to use <code>nreverse</code> at
+the end:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-exclude*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'exclude</span>
+ <span class="ss">:long</span> <span class="s">"exclude"</span>
+ <span class="ss">:parameter</span> <span class="s">"PATTERN"</span>
+ <span class="ss">:help</span> <span class="s">"exclude PATTERN (may be given multiple times)"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nv">adopt:flip</span> <span class="nf">#'</span><span class="nb">cons</span><span class="p">)</span>
+ <span class="ss">:finally</span> <span class="nf">#'</span><span class="nb">nreverse</span><span class="p">))</span>
+</pre></div>
+
+
+<p>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
+<code>vector-push-extend</code>, or anything else you might dream up. The combination of
+<code>:reduce</code>, <code>:initial-value</code>, and <code>:finally</code> will let you do just about anything.</p>
+<h2 id="required-options">Required Options</h2>
+<p>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
+<code>foo --help</code> they shouldn't get an error about a missing required option.</p>
+<p>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:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defun</span> <span class="nv">toplevel</span> <span class="p">()</span>
+ <span class="p">(</span><span class="nb">handler-case</span>
+ <span class="p">(</span><span class="nb">multiple-value-bind</span> <span class="p">(</span><span class="nv">arguments</span> <span class="nv">options</span><span class="p">)</span> <span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span><span class="p">)</span>
+ <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nb">gethash</span> <span class="ss">'help</span> <span class="nv">options</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:print-help-and-exit</span> <span class="vg">*ui*</span><span class="p">))</span>
+ <span class="p">(</span><span class="nb">unless</span> <span class="p">(</span><span class="nb">gethash</span> <span class="ss">'some-required-option</span> <span class="nv">options</span><span class="p">)</span>
+ <span class="p">(</span><span class="nb">error</span> <span class="s">"Required option foo is missing."</span><span class="p">))</span>
+ <span class="p">(</span><span class="nv">run</span> <span class="err">…</span><span class="p">))</span>
+ <span class="p">(</span><span class="nb">error</span> <span class="p">(</span><span class="nv">c</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:print-error-and-exit</span> <span class="nv">c</span><span class="p">))))</span>
</pre></div>
-<p>The <code>toplevel</code> function first uses a <code>handler-case</code> to trap all <code>error</code>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.</p>
-<p>Next we use <code>adopt:parse-options</code> to parse the command line arguments and
-options. We do some initial checks to see if the user wants <code>--help</code> or
-<code>--version</code> information. If not, we destructure the arguments into the items we
-expect and call a <code>run</code> function with all the information it needs to do its
-job.</p>
-<p>If the <code>destructuring-bind</code> fails an error will be signaled, and the
-<code>handler-case</code> will print it and exit. If you want to be a nice person you
-could check that the <code>arguments</code> have the correct shape first, and return
-a friendlier error message to your users if they don't.</p>
-<h2 id="computing-values-with-reduce">Computing Values with Reduce</h2>
+<h2 id="option-groups">Option Groups</h2>
+<p>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.</p>
+<p>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:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-help*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'help</span>
+ <span class="ss">:help</span> <span class="s">"display help and exit"</span>
+ <span class="ss">:long</span> <span class="s">"help"</span>
+ <span class="ss">:short</span> <span class="sc">#\h</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-literal*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'literal</span>
+ <span class="ss">:help</span> <span class="s">"treat PATTERN as a literal string instead of a regex"</span>
+ <span class="ss">:long</span> <span class="s">"literal"</span>
+ <span class="ss">:short</span> <span class="sc">#\l</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-no-literal*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'no-literal</span>
+ <span class="ss">:help</span> <span class="s">"treat PATTERN as a regex (the default)"</span>
+ <span class="ss">:long</span> <span class="s">"no-literal"</span>
+ <span class="ss">:short</span> <span class="sc">#\L</span>
+ <span class="ss">:result-key</span> <span class="ss">'literal</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">nil</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-case-sensitive*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'case-sensitive</span>
+ <span class="ss">:help</span> <span class="s">"match case-sensitively (the default)"</span>
+ <span class="ss">:long</span> <span class="s">"case-sensitive"</span>
+ <span class="ss">:short</span> <span class="sc">#\c</span>
+ <span class="ss">:initial-value</span> <span class="no">t</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-case-insensitive*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'case-insensitive</span>
+ <span class="ss">:help</span> <span class="s">"ignore case when matching"</span>
+ <span class="ss">:long</span> <span class="s">"case-insensitive"</span>
+ <span class="ss">:short</span> <span class="sc">#\C</span>
+ <span class="ss">:result-key</span> <span class="ss">'case-sensitive</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">nil</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-color*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'color</span>
+ <span class="ss">:help</span> <span class="s">"highlight matches with color"</span>
+ <span class="ss">:long</span> <span class="s">"color"</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">t</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-no-color*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'no-color</span>
+ <span class="ss">:help</span> <span class="s">"don't highlight matches (the default)"</span>
+ <span class="ss">:long</span> <span class="s">"no-color"</span>
+ <span class="ss">:result-key</span> <span class="ss">'color</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nb">constantly</span> <span class="no">nil</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-context*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'context</span>
+ <span class="ss">:parameter</span> <span class="s">"N"</span>
+ <span class="ss">:help</span> <span class="s">"show N lines of context (default 0)"</span>
+ <span class="ss">:long</span> <span class="s">"context"</span>
+ <span class="ss">:short</span> <span class="sc">#\U</span>
+ <span class="ss">:initial-value</span> <span class="mi">0</span>
+ <span class="ss">:reduce</span> <span class="nf">#'</span><span class="nv">adopt:last</span>
+ <span class="ss">:key</span> <span class="nf">#'</span><span class="nb">parse-integer</span><span class="p">))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*group-matching*</span>
+ <span class="p">(</span><span class="nv">adopt:make-group</span> <span class="ss">'matching-options</span>
+ <span class="ss">:title</span> <span class="s">"Matching Options"</span>
+ <span class="ss">:help</span> <span class="s">"These options affect how lines are matched."</span>
+ <span class="ss">:options</span> <span class="p">(</span><span class="nb">list</span> <span class="vg">*option-literal*</span>
+ <span class="vg">*option-no-literal*</span>
+ <span class="vg">*option-case-sensitive*</span>
+ <span class="vg">*option-case-insensitive*</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*group-output*</span>
+ <span class="p">(</span><span class="nv">adopt:make-group</span> <span class="ss">'output-options</span>
+ <span class="ss">:title</span> <span class="s">"Output Options"</span>
+ <span class="ss">:help</span> <span class="s">"These options affect how matching lines are printed."</span>
+ <span class="ss">:options</span> <span class="p">(</span><span class="nb">list</span> <span class="vg">*option-color*</span>
+ <span class="vg">*option-no-color*</span>
+ <span class="vg">*option-context*</span><span class="p">)))</span>
+
+<span class="p">(</span><span class="nv">adopt:define-string</span> <span class="vg">*help-text*</span>
+ <span class="s">"Search FILEs for lines that match the regular expression ~</span>
+<span class="s"> PATTERN and print them to standard out. Several options ~</span>
+<span class="s"> are available to control how the matching lines are printed.~@</span>
+<span class="s"> ~@</span>
+<span class="s"> If no files are given (or if - is given as a filename) ~</span>
+<span class="s"> standard input will be searched."</span><span class="p">)</span>
+
+<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"search"</span>
+ <span class="ss">:usage</span> <span class="s">"PATTERN [FILE...]"</span>
+ <span class="ss">:summary</span> <span class="s">"print lines that match a regular expression"</span>
+ <span class="ss">:help</span> <span class="vg">*help-text*</span>
+ <span class="ss">:contents</span> <span class="p">(</span><span class="nb">list</span> <span class="vg">*option-help*</span>
+ <span class="vg">*group-matching*</span>
+ <span class="vg">*group-output*</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>And with all that out of the way, you've got some nicely-organized help text
+for your users:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">adopt:print-help</span> <span class="vg">*ui*</span> <span class="ss">:width</span> <span class="mi">60</span> <span class="ss">:option-width</span> <span class="mi">16</span><span class="p">)</span>
+<span class="c1">; =></span>
+<span class="c1">; search - print lines that match a regular expression</span>
+<span class="c1">;</span>
+<span class="c1">; USAGE: /usr/local/bin/sbcl PATTERN [FILE...]</span>
+<span class="c1">;</span>
+<span class="c1">; Search FILEs for lines that match the regular expression</span>
+<span class="c1">; PATTERN and print them to standard out. Several options are</span>
+<span class="c1">; available to control how the matching lines are printed.</span>
+<span class="c1">;</span>
+<span class="c1">; If no files are given (or if - is given as a filename)</span>
+<span class="c1">; standard input will be searched.</span>
+<span class="c1">;</span>
+<span class="c1">; Options:</span>
+<span class="c1">; -h, --help display help and exit</span>
+<span class="c1">;</span>
+<span class="c1">; Matching Options:</span>
+<span class="c1">; -l, --literal treat PATTERN as a literal string</span>
+<span class="c1">; instead of a regex</span>
+<span class="c1">; -L, --no-literal treat PATTERN as a regex (the default)</span>
+<span class="c1">; -c, --case-sensitive</span>
+<span class="c1">; match case-sensitively (the default)</span>
+<span class="c1">; -C, --case-insensitive</span>
+<span class="c1">; ignore case when matching</span>
+<span class="c1">;</span>
+<span class="c1">; Output Options:</span>
+<span class="c1">; --color highlight matches with color</span>
+<span class="c1">; --no-color don't highlight matches (the default)</span>
+<span class="c1">; -U N, --context N show N lines of context (default 0)</span>
+</pre></div>
+
+
+<h2 id="error-handling">Error Handling</h2>
+<p>For the most part Adopt doesn't try to be too smart about error handling and
+leaves it up to you.</p>
+<p>However, when Adopt is parsing the command line options it <em>will</em> signal an
+error of type <code>adopt:unrecognized-option</code> if the user passes a command line
+option that wasn't defined in the interface:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*ui*</span>
+ <span class="p">(</span><span class="nv">adopt:make-interface</span>
+ <span class="ss">:name</span> <span class="s">"meow"</span>
+ <span class="ss">:summary</span> <span class="s">"say meow"</span>
+ <span class="ss">:usage</span> <span class="s">"[OPTIONS]"</span>
+ <span class="ss">:help</span> <span class="s">"Say meow. Like a cat."</span>
+ <span class="ss">:contents</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nv">make-option</span> <span class="ss">'times</span>
+ <span class="ss">:parameter</span> <span class="s">"N"</span>
+ <span class="ss">:long</span> <span class="s">"times"</span>
+ <span class="ss">:initial-value</span> <span class="mi">1</span>
+ <span class="ss">:help</span> <span class="s">"say meow N times (default 1)"</span>
+ <span class="ss">:reduce</span> <span class="nf">#'</span><span class="nv">adopt:last</span>
+ <span class="ss">:key</span> <span class="nf">#'</span><span class="nb">parse-integer</span><span class="p">))))</span>
+
+<span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"--times"</span> <span class="s">"5"</span><span class="p">))</span>
+<span class="c1">; =></span>
+<span class="c1">; NIL</span>
+<span class="c1">; {TIMES: 5}</span>
+
+<span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"--bark"</span><span class="p">))</span>
+<span class="c1">; =></span>
+<span class="c1">; No such option "--bark".</span>
+<span class="c1">; [Condition of type UNRECOGNIZED-OPTION]</span>
+<span class="c1">;</span>
+<span class="c1">; Restarts:</span>
+<span class="c1">; R 0. DISCARD-OPTION - Discard the unrecognized option.</span>
+<span class="c1">; R 1. TREAT-AS-ARGUMENT - Treat the unrecognized option as a plain argument.</span>
+<span class="c1">; R 2. SUPPLY-NEW-VALUE - Supply a new value to parse.</span>
+<span class="c1">; R 3. RETRY - Retry SLIME REPL evaluation request.</span>
+<span class="c1">; R 4. *ABORT - Return to SLIME's top level.</span>
+<span class="c1">; R 5. ABORT - abort thread (#<THREAD "repl-thread" RUNNING {100AF48413}>)</span>
+</pre></div>
+
+
+<p>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 <code>handler-bind</code>. For example:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">handler-bind</span>
+ <span class="p">((</span><span class="nv">adopt:unrecognized-option</span> <span class="ss">'adopt:discard-option</span><span class="p">))</span>
+ <span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"--bark"</span><span class="p">)))</span>
+<span class="c1">; =></span>
+<span class="c1">; NIL</span>
+<span class="c1">; {TIMES: 1}</span>
+
+<span class="p">(</span><span class="nb">handler-bind</span>
+ <span class="p">((</span><span class="nv">adopt:unrecognized-option</span> <span class="ss">'adopt:treat-as-argument</span><span class="p">))</span>
+ <span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"--bark"</span><span class="p">)))</span>
+<span class="c1">; =></span>
+<span class="c1">; ("--bark")</span>
+<span class="c1">; {TIMES: 1}</span>
+
+<span class="p">(</span><span class="nb">handler-bind</span>
+ <span class="p">((</span><span class="nv">adopt:unrecognized-option</span>
+ <span class="p">(</span><span class="nv">alexandria:rcurry</span> <span class="ss">'adopt:supply-new-value</span> <span class="s">"--times"</span><span class="p">)))</span>
+ <span class="p">(</span><span class="nv">adopt:parse-options</span> <span class="vg">*ui*</span> <span class="o">'</span><span class="p">(</span><span class="s">"--bark"</span> <span class="s">"5"</span><span class="p">)))</span>
+<span class="c1">; =></span>
+<span class="c1">; NIL</span>
+<span class="c1">; {TIMES: 5}</span>
+</pre></div>
+
+
+<h2 id="generating-man-pages">Generating Man Pages</h2>
+<p>We've already seen that Adopt can print a pretty help document, but it can also
+render <code>man</code> pages for you:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">with-open-file</span> <span class="p">(</span><span class="nv">out</span> <span class="s">"man/man1/search.1"</span>
+ <span class="ss">:direction</span> <span class="ss">:output</span>
+ <span class="ss">:if-exists</span> <span class="ss">:supersede</span><span class="p">)</span>
+ <span class="p">(</span><span class="nv">adopt:print-manual</span> <span class="vg">*ui*</span> <span class="ss">:stream</span> <span class="nv">out</span><span class="p">))</span>
+</pre></div>
+
+
+<p>The generated <code>man</code> 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
+<code>:manual</code> argument to <code>make-interface</code> and <code>make-option</code>:</p>
+<div class="codehilite"><pre><span/><span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*option-exclude*</span>
+ <span class="p">(</span><span class="nv">adopt:make-option</span> <span class="ss">'exclude</span>
+ <span class="ss">:long</span> <span class="s">"exclude"</span>
+ <span class="ss">:parameter</span> <span class="s">"PATTERN"</span>
+ <span class="ss">:help</span> <span class="s">"exclude PATTERN"</span>
+ <span class="ss">:manual</span> <span class="s">"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."</span>
+ <span class="ss">:reduce</span> <span class="p">(</span><span class="nv">adopt:flip</span> <span class="nf">#'</span><span class="nb">cons</span><span class="p">)))</span>
+</pre></div>
+
+
+<p>In order for <code>man</code> to find the pages, they need to be in the correct place. By
+default <code>man</code> is usually smart enough to look next to every directory in your
+<code>$PATH</code> to find a directory called <code>man</code>. So if you put your binaries in
+<code>/home/me/bin/</code> you can put your man pages in <code>/home/me/man/</code> under the
+appropriate subdirectories and it should all Just Work™. Consult the <code>man</code>
+documentation for more information.</p>
+<h2 id="implementation-specifics">Implementation Specifics</h2>
+<h3 id="sbcl">SBCL</h3>
+<h3 id="clozurecl">ClozureCL</h3>
</div>
<footer><p><i>Made with Lisp and love by <a href="http://stevelosh.com/">Steve Losh</a>.</i></p>
<p><a href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p>