adopt/usage/index.html @ 8058918d4932

adopt: Update site.
author Steve Losh <steve@stevelosh.com>
date Mon, 23 Dec 2019 15:15:01 -0500
parents de312656c9d5
children 957b6c9390f8
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Usage / Adopt</title>
        <link rel="stylesheet" href="../_dmedia/tango.css"/>
        <link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/>
        <script src="../_dmedia/less.js" type="text/javascript">
        </script>
    </head>
    <body class="content">
        <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 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="#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="#option-formats">Option Formats</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><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 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/><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><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">; =&gt;</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.</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>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://docs.stevelosh.com/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.  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/><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">; =&gt;</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="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>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>

<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">; =&gt;</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>


<p>Notice how Adopt line wraps the prose explaining each example, but leaves the
example itself untouched for easier copying and pasting.  In general Adopt tries
to do the right thing for your users (even when that means making a little more
work for <em>you</em> in certain places).</p>
<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/><span class="p">(</span><span class="nv">adopt:exit</span><span class="p">)</span>

<span class="p">(</span><span class="nv">adopt:exit</span> <span class="mi">1</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="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>

<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 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/><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>

<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>

<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>

<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/><span class="p">(</span><span class="nv">adopt:print-help</span> <span class="vg">*ui*</span><span class="p">)</span>
<span class="c1">; =&gt;</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>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>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/><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 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">; =&gt;</span>
<span class="c1">; ("foo.*" "a.txt" "b.txt")</span>
<span class="c1">; #&lt;HASH-TABLE :TEST EQL :COUNT 3 {10103142A3}&gt;</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">; =&gt;</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">; =&gt;</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="option-formats">Option Formats</h2>
<p>Adopt tries to support the most common styles of long and short UNIX options.</p>
<p>Long options must be given with two dashes (<code>--foo</code>).  If a long option takes
a parameter it can be given as the next argument (<code>--foo meow</code>) or mashed
together into the same argument using an equals sign (<code>--foo=meow</code>).</p>
<p>Short options must be given with a single dash (<code>-f</code>).  If several short options
do not take any parameters, they can be mashed together and given all at once
(<code>-xzvf</code>).  If a short option takes a parameter it can be given as the next
argument (<code>-n foo</code>) or mashed together with the option <code>-nfoo</code>.</p>
<p>The special string <code>--</code> signals that all remaining arguments are normal text
arguments, and should not be parsed as options.</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">&amp;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>This is a typical way to use Adopt.  There are three 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>


<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>


<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>


<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">:result-key</span> <span class="ss">'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">: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">: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.  The defaults are ideal for piping into other programs."</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">; =&gt;</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">;</span>
<span class="c1">;   These options affect how matching lines are printed.  The</span>
<span class="c1">;   defaults are ideal for piping into other programs.</span>
<span class="c1">;</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">; =&gt;</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">; =&gt;</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 (#&lt;THREAD "repl-thread" RUNNING {100AF48413}&gt;)</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">; =&gt;</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">; =&gt;</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">; =&gt;</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 <code>man</code> 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>
<p>TODO: talk about Lisp runtime options vs program options.</p>
<h3 id="sbcl">SBCL</h3>
<p>You'll want to use <code>:save-runtime-options t</code> in the call to <code>sb-ext:save-lisp-and-die</code>.</p>
<h3 id="clozurecl">ClozureCL</h3>
<p>See <a href="https://github.com/Clozure/ccl/issues/177">https://github.com/Clozure/ccl/issues/177</a>.</p>
                </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>

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-15328874-3', 'auto');
  ga('send', 'pageview');

</script></footer>
        </div>
    </body>
</html>