% vim: et ts=4

\section{Module Preface}
%--------------------------------------------------------------------------

  The module preface contains the module declaration, the export and import
lists, and in this case, the pragma to enable required language
extensions.

\begin{code}
{-# LANGUAGE ExistentialQuantification, RankNTypes #-}
module Cmdline
    ( Optspec (..)
    , Args (..)
    , flag, parameter, optional
    , option_synopsis
    , parse_args
    , get_args
    , get_progname
    , stdargs
    )
    where

import Data.Typeable    
import Data.Dynamic     
import Data.List        (unfoldr, sortBy, find, intersperse, partition)
import Data.Char        (isUpper)
import Data.Maybe       (fromMaybe, fromJust, listToMaybe)
import Control.Monad    (liftM, when)
import System           (getArgs, getProgName)
import System.Exit
\end{code}


\section{Interface Design}
%--------------------------------------------------------------------------

  We consider command line options as a means for specifying
default-valued parameters to a program. Conceptually, the program uses a
fixed set of parameters during its run-time, each of which has a
name, a type, and a default value. To specify a value for a program parameter,
a corresponding command line option is passed to the program. The kinds and
formats of command line options recognized by this module closely follow
customary practice on Unix systems: They are of the form
@-x@ (short options), or @--name@
(long options) and may take (mandatory or optional) arguments. The argument
is given as the next word on the command line. Long options also support the
form @--name=argument@. Several flags (options without
arguments) may be condensed into a single argument so, for example,
@-Naur@ is equivalent to @-N -a -u -r@.

  Command line options map to different types of program parameters 
depending on whether they take an argument or not:
\begin{itemize}
\item Options taking no argument represent Boolean parameters. They are
  traditionally called \emph{flags}. If given, the parameter's value is
  |True|, otherwise |False|.
\item Options taking a mandatory argument represent program parameters of
  any suitable type (i.e. one that is in the |Typeable|,
  |Show|, and |Read| classes; see below). If given, the parameter
  gets its value from the argument.
\item Options with an optional argument represent program parameters of
  type |Maybe a| where |a| is any suitable type as above. Their
  value is |Nothing| if not given, |Just d| if given without an
  argument (where |d| is a default value), or |Just x| where
  |x| is the argument value.
\end{itemize}

  The options accepted by a program are represented by values of the type
|Optspec| ("option specifier"):

\begin{code}
data Optspec  =   Flag       [String] String
              |  forall a. (Typeable a, Read a) =>
                  Parameter  [String] String String a String
              |  forall a. (Typeable a, Read a) =>
                  Optional   [String] String String a String
\end{code}

Notice the existentially quantified type variable |a| in
the |Parameter| and |Optional| constructors. The
|Flag| constructor is for options that take no argument,
|Parameter| for options with a mandatory argument, and
|Optional| is for options with an optional argument.
The first field in each constructor is the list of names for that option.
For example
|["v","verbose","chatty-output"]| will result in the options
@-v@, @--verbose@, and
@--chatty-output@ being recognized (corresponding to
the same parameter). The second field in each
constructor is the documentation string for that option. The rest of the
fields in |Parameter| and |Optional| are, in that order, a name for the
argument (as it will appear in the option synopsis), the default
value, and a textual representation of that default (also for the synopsis).

  The parameter types must be in class |Typeable| because the argument type
has to be checked at run-time, and in class |Read| to read parameter
values from the string arguments.
Consequently, the formats expected for the option arguments are those expected
by |read|. There is one convenient exception: If the parameter type is
|String|, the option argument does not have to be surrounded by quotation
marks. If they are there, they are interpreted as part of the string. Notice
that this exception does not apply to |String|s within other data types,
so an argument of type |[String]| still needs to be written in the usual form
@["foo","bar",...]@

  We provide convenience functions for creating
|Optspec|s. They perform some sanity checks on the parameter names, guess the
argument name from the docstring, and supply the default text via |show|.
The argument name guesser uses the first all-uppercase word of
the docstring or a default name if there is no such word. The name sanity
check rejects empty parameter names and names starting with the flag character
(like |"-n"| which was most probably meant to be just |"n"|).

\begin{code}
flag       ::    [String]  ->  String  ->         Optspec
parameter  ::  (Typeable a, Show a, Read a) =>
                 [String]  ->  String  ->  a  ->  Optspec
optional   ::  (Typeable a, Show a, Read a) =>
                 [String]  ->  String  ->  a  ->  Optspec
\end{code}

  The arguments correspond to the names, docstring, and default fields of the
corresponding data constructors. We suggest these functions be used by default,
only falling back on the ``raw'' constructors for problematic cases
like, for example, a docstring of ``use ELF header from FILE''.

  Finally, to parse the command line according to a set of |Optspec|s,
call |get_args|.
\begin{code}
get_args :: [Optspec] -> IO Args
\end{code}

Also exported is it's cousin
\begin{code}
parse_args :: [Optspec] -> [String] -> Args
\end{code}
which takes a command line to be parsed as an explicit argument.\footnote{
Naturally, |get_args| is implemented in terms of |parse_args|.}

  The resulting data type |Args| contains two fields, giving access
to the program parameter values and the non-option command line arguments.
\begin{code}
data Args = Args (forall a. (Typeable a) => String -> a) [String]
\end{code}

The first field of the type
|(Typeable a) => String -> a|
represents the set of program parameters as a function mapping
parameter names to their respective values (known to mathematicians as a
family). The function is (naturally) polymorphic in its result type which is
checked at run-time to match the actual type of the requested parameter.
Failure of that check throws an exception, as do requests for unrecognized
parameters.

  For the sake of completeness of interface (this module is named |Cmdline|
after all), we export a procedure for querying the program name, as given
on the command line. It is simply a re-export of |System.getProgName| under
a different name (to be consistent with the rest of the interface).
\begin{code}
get_progname :: IO String
\end{code}

You can generate a textual synopsis of command line options from a list of
|Optspec|s, as usually output by @--help@ or similar.
\begin{code}
option_synopsis :: [Optspec] -> String
\end{code}
 The output will be
laid-out in two columns. On the left are the forms in which command line
options may be given, and the right column contains the corresponding
documentation strings and default values.

Also, in the majority of cases,
a program's command line just takes the standard form
\begin{verbatim}
[options] arg1 arg2 ...
\end{verbatim}
where the accepted options include @--version@ and @--help@.
We provide a standard handler for this situation,
|stdargs|,
which we first introduce by means of an example.

Let ``Foo'', version 1.0, be an accounting program.
It takes a number of |Optspec|s,
declared in a list |opts|,
and three numbers, $i$, $j$, and $n$ as mandatory,
non-option arguments.
You can then declare

\begin{verbatim}
main = do  Args parm nonopts <- stdargs  name version desc
                                         opts argnames
           let [i,j,n] = map read nonopts
           {- ... -}
    where
    name      = "Foo"
    version   = "1.0"
    desc      = "Sail the wide accountant-sea."
    argnames  = ["I", "J", "N"]
\end{verbatim}

and the program will accept both @--help@ and @--version@
automatically,
their output completely synthesized from the information provided.
The number of non-option arguments will be checked
against the number of argnames given,
an automatic usage hint printed in case of mismatch.


\section{Implementation}
%--------------------------------------------------------------------------

\paragraph{Module parameters}
  We define some constants for values used throughout the module which could
in principle be configured. Since Haskell does not support parameterization
of modules, however, they cannot be changed (except by modifying the module
itself, of course).

\savecolumns
\begin{code}
parm_defargname  = "X"
\end{code}
Default argument name to use if guessing fails.

\restorecolumns
\begin{code}
parm_flagchar    = '-'
\end{code}
Character that introduces a command line flag.

\restorecolumns
\begin{code}
parm_leftwidth   = 27
\end{code}
Width in characters of the left column in the command synopsis.

\restorecolumns
\begin{code}
parm_seperators  = [' ','=']
\end{code}
List of characters that can be used to seperate option name and argument
if the argument is ``cuddled'' to the name in the same command line argument.
Example: @--outfile=a.out@

We introduce a shortcut for the flag character, because the long name
|parm_flagchar| would really blow up the code to come.
\begin{code}
fchr = parm_flagchar
\end{code}


\subsection{|Optspec| accessors and constructors}

  The following are accessor functions to common fields of the |Optspec|
data type. Notice how the |opt_default| accessor wraps the default value
it returns in a |Dynamic|. That is not only for convenience but necessary
because the type of the default value (which is ``hidden'' in the type
|Optspec|) must not ``escape''. Apart from that, these functions are
pretty trivial. Pattern match on one of the fields and return that value.

\begin{code}
opt_names          :: Optspec -> [String]
opt_names (Flag       names _)        = names
opt_names (Parameter  names _ _ _ _)  = names
opt_names (Optional   names _ _ _ _)  = names

opt_default        :: Optspec -> Dynamic
opt_default (Flag       _ _)               = toDyn False
opt_default (Parameter  _ _ _ def      _)  = toDyn def
opt_default (Optional   _ _ _ x        _)  = toDyn (Nothing `asTypeOf` Just x)

opt_documentation  :: Optspec -> String
opt_documentation (Flag       _ doc)        = doc
opt_documentation (Parameter  _ doc _ _ _)  = doc
opt_documentation (Optional   _ doc _ _ _)  = doc
\end{code}

  Next in line are constructors for |Optspec|s. These are currently
just convenience wrappers around the corresponding data constructors except
they guess the argument name for |Parameter|s and |Optional|s.

\begin{code}
flag       ns doc      = Flag       (check_names ns) doc
parameter  ns doc def  = Parameter  (check_names ns) doc
                                    (guess_argname doc) def (cshow def)
optional   ns doc def  = Optional   (check_names ns) doc
                                    (guess_argname doc) def (cshow def)
\end{code}

  The argument name guessed is the first all-uppercase word in the
given documentation string, or a default value if there is no such word.
The default argument name is one of the module parameters (see above).

\begin{code}
guess_argname      ::  String -> String
guess_argname doc  =   fromMaybe  parm_defargname
                                  (find (all isUpper) (words doc))
\end{code}

  The parameter name sanity check replaces ``insane'' names with a call to
|error|.
\begin{code}
check_names = map check_name
check_name name
    | null name          = error msg_empty
    | head name == fchr  = error msg_flagchar
    | otherwise          = name
    where
    msg_empty     =  "BUG: Empty parameter name specified"
    msg_flagchar  =  "BUG: Parameter name starting with "
                     ++ [fchr] ++ " specified"
\end{code}

  The |cshow| function used above is like |show| but handles the special case
of |String|s, which are shown literally, without the quotes. Her twin
|cread| is also in this module, used later in the parsing of the options.
The implementation of both functions is given together in a later subsection.


\subsection{Parsing the Command Line}

As already stated, |get_progname| is a re-export of |System.getProgName|.
\begin{code}
get_progname = getProgName
\end{code}

The |get_args| procedure calls |System.getArgs| and passes the result through
|parse_args| which does the real work.
\begin{code}
get_args specs = liftM (parse_args specs) getArgs
\end{code}

In |parse_args|, the raw command line arguments, before going through
the parser proper, are preprocessed. If the argument @--@ (|stop_token|)
appears on the command line, it is dropped and any arguments after it bypass 
the option parser. Still before entering the parser proper, condensed flags
are expanded. The parser itself is an instance of
|unfoldr|, at each step removing one or two arguments from the line and
yielding either a command line option or a
non-option argument. The parameter family calls on the function
|lookup_parameter| (see below) to find the requested parameter values from
either the options on the command line or the defaults given in the specs.

\begin{code}
parse_args specs args = Args parameters (nonopts ++ bypass)
    where
    bypass           = drop 1 cutoff
    (args', cutoff)  = span (/=stop_token) args
    (opts, nonopts)  = unzip_eithers (parser_proper (expand args'))
    parser_proper    = unfoldr (parse_arg specs)

    parameters  ::  (Typeable a) => String -> a
    parameters  =   lookup_parameter specs opts

stop_token = [fchr,fchr]

unzip_eithers :: [Either a b] -> ([a], [b])
unzip_eithers []      = ([], [])
unzip_eithers (x:xs)  = case x of  (Left l)   -> (l:ls,rs)
                                   (Right r)  -> (ls,r:rs)
    where
    (ls,rs) = unzip_eithers xs
\end{code}

A command line argument is a condensed flag if it is more than two characters
long and starts with exactly one dash (flag character). If it is of that form,
its expansion is the list of all characters after the dash, each prefixed by
its ``own'' dash. The expansion of any argument which is not a condensed flag
is the argument unchanged. The expansion of a command line is the
concatenation of the expansions of its elements.

\begin{code}
expand  ::  [String] -> [String]
expand  =   concatMap expand_argument
    where
    expand_argument xs
        | length xs < 3  = [xs]
        | xs!!0 /= fchr  = [xs]
        | xs!!1 == fchr  = [xs]
        | otherwise      = map (\x -> [fchr,x]) (tail xs)
\end{code}

To find a parameter's value, |lookup_parameter| tries to find a corresponding
option from the command line or returns the default value from the specs.
There are two errors that can happen here: If the parameter requested is
not known, or does not match the result type, this indicates a bug in the
program logic and is reported as such.

The command line options are given as a list of |Optspec|s each paired with the
corresponding parameter value.

\begin{code}
type Option = (Optspec, Dynamic)

lookup_parameter ::  [Optspec] -> [Option]
                     -> (forall a. (Typeable a) => String -> a)
lookup_parameter specs opts name
    = case  lkup_opt of
            Just (_,dyn)  -> fromMaybe  (error msg_type)
                                        (fromDynamic dyn)
            Nothing       -> case  lkup_spec of
                                   Just spec  -> default_val spec
                                   Nothing    -> error msg_unknown
    where
    lkup_opt   = find (elem name . opt_names . fst)  opts
    lkup_spec  = find (elem name . opt_names)        specs
    default_val spec = maybe  (error msg_type) id
                              (fromDynamic (opt_default spec))

    msg_type =  "BUG: Wrong type requested for parameter \""
                ++ name ++ "\""
    msg_unknown =  "BUG: Unknown parameter \"" ++ name
                   ++ "\" requested"
\end{code}

Parsing a single command line argument/option is trivial if the command line
is empty, in which case a return value of |Nothing| terminates the unfold.
If the first argument is not an option, it is returned as a
non-option argument. If the first argument \emph{is} an option, the
|parse_arg| routine calls upon its next relative |parse_opt| to parse
the first element of the command line as that specific option.

\begin{code}
parse_arg ::  [Optspec] -> [String]
              -> Maybe (Either Option String, [String])
parse_arg specs line
    | null line              = Nothing
    | is_option (head line)  = Just (Left  (spec, val), line')
    | otherwise              = Just (Right (head line), tail line)
    where
    (val, line')  = parse_opt spec line
    spec          = lookup_spec optname specs
    optname       = option_to_name (head line)
\end{code}

The above code makes use of the following simple helper functions.
|option_to_name| extracts the parameter name from a command line option
which starts with one or more flag characters and possibly contains a
cuddled argument.

\begin{code}
option_to_name :: String -> String
option_to_name = takeWhile not_seperator . dropWhile (== fchr)

not_seperator x = not (x `elem` parm_seperators)
\end{code}

Given a parameter name, the |lookup_spec| function
finds the corresponding |Optspec| in the list of |Optspec|s.

\begin{code}
lookup_spec :: String -> [Optspec] -> Optspec
lookup_spec name specs = fromMaybe (error msg_unrec) lkup
    where
    lkup       = find (elem name . opt_names) specs
    msg_unrec  =  "Unrecognized command line option \""
                  ++ name ++ "\""
\end{code}

The |parse_opt| function examines a given |Optspec|, accordingly extracts an
option argument from the command line if required/possible, and returns
the resulting parameter value along with the remaining command line. The
hard work is again delegated to the next function, |parse_optarg|, which
tries to read an option argument (cuddled or not) from the command line.

\begin{code}
parse_opt ::  Optspec -> [String] -> (Dynamic, [String])
parse_opt spec line =
    case  spec of
          Flag       _ _              -> (toDyn True, tail line)
          Parameter  _ _ _ d _  -> (toDyn (val `asTypeOf` d), line')
              where (val, line') = fromMaybe  (error msg_missarg)
                                              (parse_optarg line)
          Optional   _ _ _ d _  -> (toDyn (Just (val `asTypeOf` d)), line')
              where (val, line') = fromMaybe  (d, tail line)
                                              (parse_optarg line)
    where
    msg_missarg =
       "Argument missing for command line option \""
       ++ option_to_name (head line) ++ "\""
\end{code}

An option argument may be cuddled to its option or appear in the next
command line argument. If the next command line argument is also an option,
there is no option argument. Notice that the only place this function
is called is in |parse_opt| above, where we know |line| to contain at least
one element.

\begin{code}
parse_optarg ::  (Read a, Typeable a) =>
                 [String] -> Maybe (a, [String])
parse_optarg line
    | null line     = error "BUG: This shouldn't happen"
    | null cuddled  = liftM  (\(x, line') -> (read_optarg x, line'))
                             (parse_nonopt (tail line))
    | otherwise     = Just (read_optarg cuddled, tail line)
    where
    cuddled = cuddled_optarg (head line)

cuddled_optarg arg = drop 1 (dropWhile not_seperator arg)
\end{code}

|parse_optarg| calls |parse_nonopt| to try to get the next non-option command
line argument. This function is very simple, and the last one
in the chain of parse functions.

\begin{code}
parse_nonopt :: [String] -> Maybe (String, [String])
parse_nonopt []      = Nothing
parse_nonopt (x:xs)  = if (is_option x)  then  Nothing
                                         else  Just (x,xs)
\end{code}
\begin{quotation}\it
I call this \emph{splatter programming} --
keep chopping pieces off until there's nothing left\dots
\end{quotation}
\begin{flushright}-- Sven Moritz\end{flushright}

So let's pick up the remains. |is_option| takes a command line argument and
determines whether it is an option, i.e. whether it starts with one or two
flag characters. Notice that \emph{exactly} one or two flag characters are
not interpreted as options. Remember that a single dash is usually used by
Unix utilities to mean standard in- or output and that the double flag
character is the |stop_token|.

\begin{code}
is_option x
   | null x            = False
   | x == [fchr]       = False
   | x == [fchr,fchr]  = False
   | otherwise         = (head x == fchr)
\end{code}

|read_optarg| is just like |read| but throws
a different error message if it is unable to parse.

\begin{code}
read_optarg :: (Read a, Typeable a) => String -> a
read_optarg str = result  -- We want to reference the result type.
    where
    result     =  maybe (error msg_parse) id (cread str)
    msg_parse  =  "Failed to parse option argument \"" ++ str
                 ++ "\" as " ++ show (typeOf result)
\end{code}

Note that |cread| (described in a later subsection) returns a |Maybe|,
indicating success or failure of the parse. This way, a better error message
can be generated.


\subsection{External Representation of Option Arguments: |cread| and |cshow|}

The functions |cread| and |cshow| (``command line read'' and
``command line show'') are variants of |read| and |show| that treat
|String|s specially, as has already been described at the sites of their
usage.
The special behaviour on |String| is accomplished by comparing the type of
their argument/result, represented by a |TypeRep|, to the |TypeRep| of
|String|. This explains the |Typeable| constraint on both functions.

The |String| |TypeRep| is obtained by applying |typeOf| to |undefined| of
type |String|, which is luckily not evaluated \dots

\begin{code}
type_String = typeOf (undefined :: String)
\end{code}

\begin{code}
cshow :: (Typeable a, Show a) => a -> String
cshow x
    | typeOf x == type_String  = fromJust (cast x)
    | otherwise                = show x
\end{code}

The |cread| function looks (!) less simple, because we need a pattern to
bind a type variable to the result type, and the parse may fail in which case
we want |Nothing| returned.

\begin{code}
cread :: (Typeable a, Read a) => String -> Maybe a
cread str
    | result_type == type_String  = cast str
    | otherwise                   = listToMaybe parses
    where
    result_type  = typeOf (head parses)
    parses       = map fst (readsPrec 0 str)
\end{code}


\subsection{Autogenerating an Option Synopsis}

Each synopsis line consists of left (option form) and right (doc) column.
The columns are generated seperately by |synopsis_line| and then combined
with suitable blank filling by |show_synline|. The result is concatenated by
|unlines|.

\begin{code}
option_synopsis = unlines . map (show_synline . synopsis_line)
\end{code}

To combine the two columns, enough blanks are inserted after the left to
start the right column on a fixed position (|parm_leftwidth| characters into
the line). If the left string is longer than |parm_leftwidth| characters, the
docstring is broken onto the next line and starts after |parm_leftwidth|
blanks.

\begin{code}
show_synline :: (String,String) -> String
show_synline (left,right) = left ++ filling ++ right
    where
    n = parm_leftwidth - length left
    filling
        | n > 0      = replicate n ' '
        | otherwise  = '\n' : replicate parm_leftwidth ' '
\end{code}

The |synopsis_line| function calls some helpers to create the different
pars of the command line, assembles the left and right columns and returns
them in a pair.

\begin{code}
synopsis_line :: Optspec -> (String, String)
synopsis_line spec
    = ( " " ++ optforms ++ argform, docstring ++ defstring )
    where
    optforms  = concat (intersperse ", " (option_forms spec))
    argform   = argument_form spec
    docstring = opt_documentation spec
    defstring = default_synopsis spec
\end{code}

The |option_forms| helper builds the list of forms in which a specific
option may be given, for example |["-h","--help"]|. The options are reordered
so that any short options appear before the long forms. 

\begin{code}
option_forms :: Optspec -> [String]
option_forms spec = map ("-"++) shorts ++ map ("--"++) longs
    where
    (shorts, longs)  = partition (null . tail) (opt_names spec)
\end{code}

The |argument_form| helper returns the text which should be output in the
synopsis to describe the argument to an option, if any. For flags, it returns
the empty string. The code should be rather self-explanatory:

\begin{code}
argument_form :: Optspec -> String
argument_form spec
    = case  spec of
            Flag _ _         -> ""
            Parameter  _ _ argname _ _
                | long       -> '=':argname
                | otherwise  -> ' ':argname
            Optional   _ _ argname _ _
                | long       -> "[="  ++ argname ++ "]"
                | otherwise  -> " ["  ++ argname ++ "]"
    where
    long = any ((>1) . length) (opt_names spec)
\end{code}

The |default_synopsis| helper returns the text which should be output in the
synopsis to describe the default value of an option. For flags, again, it
just returns an empty string, because their default parameter value is always
|False|. For |Parameter|s and |Optional|s, the default value is printed
in square brackets.

\begin{code}
default_synopsis (Flag _ _)             = ""
default_synopsis (Parameter _ _ _ _ d)  = " [" ++ d ++ "]"
default_synopsis (Optional  _ _ _ _ d)  = " [" ++ d ++ "]"
\end{code}


\subsection{Standard Command Line Handling}

Recall the earlier example for the |stdargs| function.
\begin{verbatim}
main = do  Args p a <- stdargs  name version description
                                optspecs argnames
           {- ... -}
\end{verbatim}
The (admittedly lengthy) type signature of |stdargs| is as follows.
\begin{code}
stdargs ::  String        -- program name
            -> String     -- program version, e.g. |"2.0"|
            -> String     -- program copyright, e.g. |"(c) 1957 K.H. Janke"|
            -> String     -- one-line description
            -> [Optspec]  -- additional options to accept
            -> [String]   -- names for the non-option args, in order
            -> IO Args
\end{code}

The function is, as one can imagine, straightforwardly implemented
in terms of the general API.
Generic |Optspec|s are defined for
@--help@ and @--version@ and |get_args| is called with them
in addition to the user-supplied list of |Optspec|s.
\savecolumns
\begin{code}
stdargs name version copyright description specs argnames =
    do
    Args parm nonopts <- get_args allspecs
\end{code}
Before any further checks, if @--help@ or @--version@ is given,
the corresponding text is printed and the program terminated.
\restorecolumns
\begin{code}
    when (parm "help") $ do
        pn <- get_progname
        putStr (helptext pn)
        exitWith ExitSuccess
    when (parm "version") $ do
        putStrLn versiontext
        exitWith ExitSuccess
\end{code}
Next, the actual number of non-option command line arguments
is checked to match at least the length of the specified name list.
If there are too little, a ``usage'' message is printed and the
program terminated with error status.
\restorecolumns
\begin{code}
    when (length nonopts < length argnames) $ do
        pn <- get_progname
        putStrLn (usagetext pn)
        exitWith (ExitFailure 1)
\end{code}
If the test succeeded, everything is well and the |Args| result
can be returned.
\restorecolumns
\begin{code}
    return (Args parm nonopts)
\end{code}
Finally, the definitions of |allspecs|, |helptext|, etc. used above
are provided in the following where clause.
\restorecolumns
\begin{code}
    where
    allspecs  = helpspec:verspec:specs
    helpspec  = flag ["help"]     "Print command synopsis"
    verspec   = flag ["version"]  "Print program version"

    helptext pn =
        unlines  [  versiontext
                 ,  description
                 ,  ""
                 ,  usagetext pn
                 ,  ""
                 ,  "available options:"
                 ]
        ++ option_synopsis allspecs

    versiontext = name ++ " v" ++ version ++ " " ++ copyright

    usagetext pn =
        "usage: " ++ pn ++ " [options]... " ++ unwords argnames
\end{code}
Thus concludes the module.
