diff --git a/CHANGES.txt b/CHANGES.txt
new file mode 100644
--- /dev/null
+++ b/CHANGES.txt
@@ -0,0 +1,152 @@
+Changelog for CmdArgs
+
+0.10.22, released 2023-03-13
+    #68, support GHC 9.6
+0.10.21, released 2021-02-14
+    Handle GHC 9.0 optimisations
+    Remove support for GHC 7.4 to 7.8
+0.10.20, released 2018-01-22
+    #54, use the getopt data types from base
+0.10.19, released 2018-01-01
+    #47, ensure Semigroup instance on all GHC versions
+0.10.18, released 2017-09-24
+    #47, GHC 8.4 compatibility
+0.10.17, released 2017-03-31
+    Add processValueIO for more controlled error messages
+    #529, don't include the stack trace in processValue
+0.10.16, released 2017-03-22
+    Minor improvement to error messages
+0.10.15, released 2017-03-06
+    #43, GHC 8.2 compatibility
+0.10.14, released 2016-02-16
+    #39, ensure correct line breaks in HTML help output
+    #18, preserve manual \n in help messages
+    #25, reformat the README
+0.10.13, released 2015-05-22
+    #24, support Ratio in some places
+0.10.12, released 2014-10-27
+    GHC 7.2 compatibility
+0.10.11, released 2014-10-12
+    #15, never put [brackets] around optional args in Explicit
+0.10.10, released 2014-09-18
+    #14, fix @ file arguments
+0.10.9, released 2014-07-22
+    #10, fix versionArgs (broken in 0.10.8)
+0.10.8, released 2014-07-21
+    Avoid compilation warnings on GHC 7.8
+    #9, add --numeric-version flag
+    Update the copyright year
+    Change GetOpt.usageInfo to be more like GetOpt
+0.10.7, released 2013-12-09
+    #1, fix timestamps in .tar.gz dist file
+0.10.6, released 2013-12-05
+    #625, more documentation about args/argPos
+    #626, ensure initial lists don't get reversed (fix after #610)
+0.10.5, released 2013-07-29
+    #615, support lists inside a newtype
+0.10.4, released 2013-06-26
+    #610, make sure it is O(n) to append arguments, not O(n^2)
+0.10.3, released 2013-04-05
+    Append list items under an enum
+    Support &= ignore on enum fields
+0.10.2, released 2013-02-28
+    Relax upper bounds to be GHC 7.7 compatible
+0.10.1, released 2012-11-17
+    #569, set the test program to off by default
+	Complete revamp of cmdargs-browser, far better Javascript
+    Add a missing case for Helper marshalling FlagNone
+0.10, released 2012-08-09
+    Revert to 0.9.6, including modeExpandAt
+0.9.7, released 2012-08-09
+    Revert to 0.9.5, to fix up PVP breakage
+0.9.6, released 2012-07-29
+    #539, hopefully more fixes to compiling in profile mode
+    #522, add modeExpandAt and noAtExpand annotation
+    #522, don't @expand after --
+0.9.5, released 2012-03-25
+    Don't specify TH extension unless quotation is true
+0.9.4, released 2012-03-25
+    #539, specify the TH extension in the Cabal file
+    Allow transformers 0.3.*
+    Correct copyright in license and cabal file
+0.9.3, released 2012-02-10
+    Add expandArgsAt and support for @ flag file directives
+0.9.2, released 2012-01-07
+    Don't build the test program if quotation is turned off
+0.9.1, released 2012-01-05
+    Improve the documentation for the Explicit module
+    #433, propagate groupname on modes in the Implicit code
+0.9, released 2011-11-05
+    #467, add completions for people running bash
+    #334, add a Quote module, to write pure in the impure syntax
+    #482, fix the sample in Explicit, don't use def
+    #461, fix the translation for enum/enum_
+    Make showHelp take an argument for the prefix bits
+    Add Helper interface, and initial cmdargs-browser code
+    Add splitArgs/joinArgs
+0.8, released 2011-08-13
+    #450, redo the manual generator so Maker example is not cut off
+    Support all the types in Data.Int/Data.Word
+    Make modeArgs take a list of arguments as well
+0.7, released 2011-05-07
+    No changes, just a version bump to allow requiring the GHC fix
+0.6.10, released 2011-05-07
+    Change the annotate module to cope better with GHC's CSE
+0.6.9, released 2011-04-03
+    #422, support newtype value as the underlying type
+0.6.8, released 2011-02-13
+    Allow versionArgs [summary] to override --version
+    Improve the documentation surrounding opt
+    Add modeReform to Mode
+    Add modeEmpty, to construct blank Mode values
+    Improve the documentation surrounding pure annotations.
+0.6.7, released 2011-01-15
+    #395, don't put two newlines after --help or --version
+0.6.6, released 2010-12-30
+    #392, support helpArgs [groupname "something"]
+0.6.5, released 2010-12-15
+    Don't fail with ambiguous enum if you exactly match a value
+    Put errors on stderr
+0.6.4, released 2010-11-20
+    Eliminate the filepath dependence
+0.6.3, released 2010-11-10
+    Switch mtl for transformers
+0.6.2, released 2010-11-10
+    Build on GHC 7.0 RC2, add an extra type signature
+    Add verbosityArgs to customise the verbose/quiet flags
+    Add helpArg/versionArg flags to customise those flags
+    Support multiline summary using \n escape codes
+0.6.1, released 2010-10-04
+    Build on GHC 6.10, don't rely on record name disambiguation
+0.6, released 2010-09-18
+    Add ignore annotation for modes and flags
+    #350, make top-level help appear properly
+0.5, released 2010-09-15
+    #351, name/explicit attributes on mode were broken (regression)
+0.4, released 2010-09-05
+    #342, display common fields only once
+    Raise errors if annotations are placed in invalid places
+    Rewrite the translation of annotation to explicit modes
+    Treat anything after -- as an argument
+    Add a pure annotation mechanism
+    Introduce System.Console.CmdArgs.Annotate
+0.3, released 2010-08-23
+    Add a documentation example for the Explicit mode
+    Improve the purity and annotations a bit, try disabling CSE
+    Change the help format
+    Rename groupHiden to groupHidden, patch from Matthew Cox
+    Bug, missing fields and explicit enums didn't work together
+0.2, released 2010-08-14
+    #252, add support for grouped flags/modes
+    #333, support missing fields
+    Add support for reading tuple values (including nested)
+    #292, add support for automatic enumerations
+    #221, make argpos work with non-string fields
+    #222, support opt and args together
+    #230, different modes can share short flags
+    #295, make verbosity flags explicit
+    #231, add support for Maybe
+    #256, add --option=false support
+    Complete rewrite to introduce Explicit module
+0.1, released 2009-09-12
+    Start of changelog
diff --git a/Data/Generics/Any.hs b/Data/Generics/Any.hs
--- a/Data/Generics/Any.hs
+++ b/Data/Generics/Any.hs
@@ -2,11 +2,13 @@
 
 module Data.Generics.Any where
 
+import Control.Exception
 import Control.Monad.Trans.State
 import qualified Data.Data as D
-import Data.Data hiding (toConstr, typeOf, dataTypeOf, isAlgType)
+import Data.Data hiding (toConstr, typeOf, dataTypeOf)
 import Data.List
 import Data.Maybe
+import System.IO.Unsafe
 
 
 type CtorName = String
@@ -18,6 +20,8 @@
                 | otherwise = Nothing
     where y = init $ tail x
 
+try1 :: a -> Either SomeException a
+try1 = unsafePerformIO . try . evaluate
 
 ---------------------------------------------------------------------
 -- BASIC TYPES
@@ -62,7 +66,7 @@
 typeShell = tyconUQname . typeShellFull
 
 typeShellFull :: Any -> String
-typeShellFull = tyConString . typeRepTyCon . typeOf
+typeShellFull = tyConName . typeRepTyCon . typeOf
 
 typeName :: Any -> String
 typeName = show . typeOf
@@ -81,6 +85,7 @@
 
 
 compose0 :: Any -> CtorName -> Any
+compose0 x c | either (const False) (== c) $ try1 $ ctor x = x
 compose0 (Any x) c = Any $ fromConstrB err y `asTypeOf` x
     where Just y = readConstr (D.dataTypeOf x) c
           err = error $ "Data.Generics.Any: Undefined field inside compose0, " ++ c ++ " :: " ++ show (Any x)
diff --git a/Data/Generics/Any/Prelude.hs b/Data/Generics/Any/Prelude.hs
--- a/Data/Generics/Any/Prelude.hs
+++ b/Data/Generics/Any/Prelude.hs
@@ -2,7 +2,6 @@
 
 module Data.Generics.Any.Prelude where
 
-import Prelude hiding (head,tail,null)
 import Data.Generics.Any
 import Data.Maybe
 
@@ -16,6 +15,11 @@
 cons :: AnyT a -> AnyT [a] -> AnyT [a]
 cons x y = compose y "(:)" [x,y]
 
+uncons :: AnyT [a] -> Maybe (AnyT a, AnyT [a])
+uncons x = case decompose x of
+    ("[]",[]) -> Nothing
+    ("(:)",[a,b]) -> Just (a,b)
+
 null :: AnyT [a] -> Bool
 null x | isList x = ctor x == "[]"
 
@@ -30,12 +34,21 @@
 
 append :: AnyT [a] -> AnyT [a] -> AnyT [a]
 append x y | typeOf x == typeOf y = f x y
-    where f x y | null x = y
-                | otherwise = cons (head x) (f (tail x) y)
+    where f x y = case uncons x of
+                       Nothing -> y
+                       Just (a,b) -> cons a $ f b y
 
+reverse :: AnyT [a] -> AnyT [a]
+reverse xs | isList xs = rev xs (nil_ xs)
+    where rev xs acc = case uncons xs of
+                           Nothing -> acc
+                           Just (x,xs) -> rev xs (cons x acc)
 
+
 isString x = typeName x == "[Char]"
-isList x = typeShell x == "[]"
+-- GHC 9.6 changes from [] to List, so accept either
+isList x = ts == "[]" || ts == "List"
+    where ts = typeShell x
 isMaybe x = typeShell x == "Maybe"
 isTuple x = isJust $ readTupleType $ typeShell x
 
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Neil Mitchell 2006-2007.
+Copyright Neil Mitchell 2009-2023.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/Main.hs b/Main.hs
--- a/Main.hs
+++ b/Main.hs
@@ -1,5 +1,5 @@
 
-module Main where
+module Main(main) where
 
 import System.Console.CmdArgs.Test.All
 import qualified System.Console.CmdArgs.Test.Implicit.Diffy as D
@@ -10,6 +10,7 @@
 import System.Console.CmdArgs.Text
 import System.Console.CmdArgs.Default
 
+import Control.Monad
 import Data.List
 import Data.Maybe
 import System.IO
@@ -25,8 +26,8 @@
     where
         flags = [flagHelpFormat $ \a b _ -> Help a b
                 ,flagVersion $ const Version
-                ,flagNone ["t","test"] (const Test) "Run the tests"
-                ,flagNone ["g","generate"] (const Generate) "Generate the manual"]
+                ,flagNone ["test","t"] (const Test) "Run the tests"
+                ,flagNone ["generate","g"] (const Generate) "Generate the manual"]
 
         ms = map (remap Demo (\(Demo x) -> (x,Demo))) demo
 
@@ -36,12 +37,24 @@
     let ver = "CmdArgs demo program, (C) Neil Mitchell"
     case x of
         Version -> putStrLn ver
-        Help hlp txt -> putStrLn $ showText txt $ Line ver : Line "" : helpText hlp args
+        Help hlp txt -> do
+            let xs = showText txt $ helpText [ver] hlp args
+            putStrLn xs
+            when (hlp == HelpFormatBash) $ do
+                writeFileBinary "cmdargs.bash_comp" xs
+                putStrLn "# Output written to cmdargs.bash_comp"
         Test -> test
         Generate -> generateManual
         Demo x -> runDemo x
 
 
+writeFileBinary :: FilePath -> String -> IO ()
+writeFileBinary file x = do
+    h <- openBinaryFile file WriteMode
+    hPutStr h x
+    hClose h
+
+
 ---------------------------------------------------------------------
 -- GENERATE MANUAL
 
@@ -75,7 +88,7 @@
 
 
 recode :: [String] -> [String]
-recode = concatMap f . blanks . takeWhile (not . isPrefixOf "test")
+recode = concatMap f . blanks . takeWhile (/= "-- STOP MANUAL")
     where
         blanks ("":"":xs) = blanks ("":xs)
         blanks [""] = []
@@ -83,6 +96,7 @@
         blanks (x:xs) = x : blanks xs
 
         f x | x == "import System.Console.CmdArgs.Test.Implicit.Util" = []
+            | "{-# OPTIONS_GHC " `isPrefixOf` x = []
             | "{-# LANGUAGE " `isPrefixOf` x = ["{-# LANGUAGE DeriveDataTypeable #-}"]
             | "module System.Console.CmdArgs.Test.Implicit." `isPrefixOf` x = ["module " ++ drop 44 x]
         f x = [x]
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,364 @@
+# CmdArgs: Easy Command Line Processing [![Hackage version](https://img.shields.io/hackage/v/cmdargs.svg?label=Hackage)](https://hackage.haskell.org/package/cmdargs) [![Stackage version](https://www.stackage.org/package/cmdargs/badge/nightly?label=Stackage)](https://www.stackage.org/package/cmdargs) [![Build status](https://img.shields.io/github/workflow/status/ndmitchell/cmdargs/ci/master.svg)](https://github.com/ndmitchell/cmdargs/actions)
+
+CmdArgs is a Haskell library for defining command line parsers. The two features that make it a better choice than the standard [getopt library](http://haskell.org/ghc/docs/latest/html/libraries/base/System-Console-GetOpt.html) are:
+
+* It's very concise to use. The HLint command line handling is three times shorter with CmdArgs.
+* It supports programs with multiple modes, such as [darcs](http://darcs.net) or [Cabal](http://haskell.org/cabal/).
+
+A very simple example of a command line processor is:
+```haskell
+data Sample = Sample {hello :: String} deriving (Show, Data, Typeable)
+
+sample = Sample{hello = def &= help "World argument" &= opt "world"}
+         &= summary "Sample v1"
+
+main = print =<< cmdArgs sample
+```
+Despite being very concise, this processor is already fairly well featured:
+
+    $ runghc Sample.hs --hello=world
+    Sample {hello = "world"}
+
+    $ runghc Sample.hs --help
+    Sample v1, (C) Neil Mitchell 2009
+
+    sample [FLAG]
+
+      -? --help[=FORMAT]  Show usage information (optional format)
+      -V --version        Show version information
+      -v --verbose        Higher verbosity
+      -q --quiet          Lower verbosity
+      -h --hello=VALUE    World argument (default=world)
+
+## User Manual
+
+The rest of this document explains how to write the "hello world" of command line processors, then how to extend it with features into a complex command line processor. Finally this document gives three samples, which the `cmdargs` program can run. The three samples are:
+
+* `hlint` - the [HLint](https://github.com/ndmitchell/hlint#readme) program.
+* `diffy` - a program to compare the differences between directories.
+* `maker` - a make style program.
+
+For each example you are encouraged to look at it's source (in the [repo](https://github.com/ndmitchell/cmdargs/tree/master/System/Console/CmdArgs/Test/Implicit)) and run it (try `cmdargs hlint --help`). The HLint program is fairly standard in terms of it's argument processing, and previously used the [System.Console.GetOpt](http://haskell.org/ghc/docs/latest/html/libraries/base/System-Console-GetOpt.html) library. Using GetOpt required 90 lines and a reasonable amount of duplication. Using CmdArgs the code requires 30 lines, and the logic is much simpler.
+
+**Acknowledgements** Thanks to Kevin Quick for substantial patches, and additional code contributions from Sebastian Fischer and Daniel Schoepe.
+
+
+## Hello World Example
+
+The following code defines a complete command line argument processor:
+```haskell
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# OPTIONS_GHC -fno-cse #-}
+module Sample where
+import System.Console.CmdArgs
+
+data Sample = Sample {hello :: String}
+              deriving (Show, Data, Typeable)
+
+sample = Sample{hello = def}
+
+main = print =<< cmdArgs sample
+```
+To use the CmdArgs library there are three steps:
+
+* Define a record data type (`Sample`) that contains a field for each argument. This type needs to have instances for `Show`, `Data` and `Typeable`.
+* Give a value of that type (`sample`) with default values (`def` is a default value of any type, but I could also have written `""`). This value is turned into a command line by calling the `cmdArgs` function.
+* To ensure GHC evalutes attributes the right number of times we disable the CSE optimisation on this module.
+
+Now we have a reasonably functional command line argument processor. Some sample interactions are:
+
+    $ runghc Sample.hs --hello=world
+    Sample {hello = "world"}
+
+    $ runghc Sample.hs --version
+    The sample program
+
+    $ runghc Sample.hs --help
+    The sample program
+
+    sample [OPTIONS]
+
+      -? --help        Display help message
+      -V --version     Print version information
+      -h --hello=ITEM
+
+CmdArgs uses defaults to automatically infer a command line parser for a value, and provides annotations to override any of the the defaults. CmdArgs automatically supports `--help` and `--version` flags, and optionally supports verbosity flags.
+
+## Specifying Attributes
+
+In order to control the behaviour we can add attributes. For example to add an attribute specifying the help text for the `--hello` argument we can write:
+```haskell
+sample = Sample{hello = def &= help "Who to say hello to"}
+```
+We can add additional attributes, for example to specify the type of the value expected by hello:
+```haskell
+sample = Sample {hello = def &= help "Who to say hello to" &= typ "WORLD"}
+```
+Now when running `--help` the final line is:
+
+      -h --hello=WORLD  Who to say hello to
+
+There are many more attributes, detailed in the [Haddock documentation](http://hackage.haskell.org/packages/archive/cmdargs/latest/doc/html/System-Console-CmdArgs.html#2).
+
+
+## Multiple Modes
+
+To specify a program with multiple modes, similar to [darcs](http://darcs.net/), we can supply a data type with multiple constructors, for example:
+```haskell
+data Sample = Hello {whom :: String}
+            | Goodbye
+              deriving (Show, Data, Typeable)
+
+hello = Hello{whom = def}
+goodbye = Goodbye
+
+main = print =<< cmdArgs (modes [hello,goodbye])
+```
+Compared to the first example, we now have multiple constructors, and a sample value for each constructor is passed to `cmdArgs`. Some sample interactions with this command line are:
+
+    $ runghc Sample.hs hello --whom=world
+    Hello {whom = "world"}
+
+    $ runghc Sample.hs goodbye
+    Goodbye
+
+    $ runghc Sample.hs --help
+    The sample program
+
+    sample [OPTIONS]
+
+     Common flags
+      -? --help       Display help message
+      -V --version    Print version information
+
+    sample hello [OPTIONS]
+
+      -w --whom=ITEM
+
+    sample goodbye [OPTIONS]
+
+As before, the behaviour can be customised using attributes.
+
+
+## Larger Examples
+
+For each of the following examples we first explain the purpose of the program, then give the source code, and finally the output of `--help`. The programs are intended to show sample uses of CmdArgs, and are available to experiment with through `cmdargs progname`.
+
+### HLint
+
+The [HLint](https://github.com/ndmitchell/hlint#readme) program analyses a list of files, using various options to control the analysis. The command line processing is simple, but a few interesting points are:
+
+* The `--report` flag can be used to output a report in a standard location, but giving the flag a value changes where the file is output.
+* The `color` field is assigned two flag aliases, `--colour` and `-c`. Assigning the `-c` short flag explicitly stops either of the CPP fields using it.
+* The `show_` field would clash with `show` if given the expected name, but CmdArgs automatically strips the trailing underscore.
+* The `cpp_define` field has an underscore in it's name, which is transformed into a hyphen for the flag name.
+
+The code is:
+```haskell
+{-# LANGUAGE DeriveDataTypeable #-}
+module HLint where
+import System.Console.CmdArgs
+
+data HLint = HLint
+    {report :: [FilePath]
+    ,hint :: [FilePath]
+    ,color :: Bool
+    ,ignore_ :: [String]
+    ,show_ :: Bool
+    ,extension :: [String]
+    ,language :: [String]
+    ,utf8 :: Bool
+    ,encoding :: String
+    ,find :: [FilePath]
+    ,test_ :: Bool
+    ,datadir :: [FilePath]
+    ,cpp_define :: [String]
+    ,cpp_include :: [FilePath]
+    ,files :: [FilePath]
+    }
+    deriving (Data,Typeable,Show,Eq)
+
+hlint = HLint
+    {report = def &= opt "report.html" &= typFile &= help "Generate a report in HTML"
+    ,hint = def &= typFile &= help "Hint/ignore file to use"
+    ,color = def &= name "c" &= name "colour" &= help "Color the output (requires ANSI terminal)"
+    ,ignore_ = def &= typ "MESSAGE" &= help "Ignore a particular hint"
+    ,show_ = def &= help "Show all ignored ideas"
+    ,extension = def &= typ "EXT" &= help "File extensions to search (defaults to hs and lhs)"
+    ,language = def &= name "X" &= typ "LANG" &= help "Language extension (Arrows, NoCPP)"
+    ,utf8 = def &= help "Use UTF-8 text encoding"
+    ,encoding = def &= typ "ENC" &= help "Choose the text encoding"
+    ,find = def &= typFile &= help "Find hints in a Haskell file"
+    ,test_ = def &= help "Run in test mode"
+    ,datadir = def &= typDir &= help "Override the data directory"
+    ,cpp_define = def &= typ "NAME[=VALUE]" &= help "CPP #define"
+    ,cpp_include = def &= typDir &= help "CPP include path"
+    ,files = def &= args &= typ "FILES/DIRS"
+    } &=
+    verbosity &=
+    help "Suggest improvements to Haskell source code" &=
+    summary "HLint v0.0.0, (C) Neil Mitchell" &=
+    details ["Hlint gives hints on how to improve Haskell code",""
+            ,"To check all Haskell files in 'src' and generate a report type:","  hlint src --report"]
+
+mode = cmdArgsMode hlint
+```
+Produces the `--help` output:
+
+    HLint v0.0.0, (C) Neil Mitchell
+
+    hlint [OPTIONS] [FILES/DIRS]
+    Suggest improvements to Haskell source code
+
+    Common flags:
+      -r --report[=FILE]            Generate a report in HTML
+      -h --hint=FILE                Hint/ignore file to use
+      -c --colour --color            Color the output (requires ANSI terminal)
+      -i --ignore=MESSAGE            Ignore a particular hint
+      -s --show                     Show all ignored ideas
+         --extension=EXT            File extensions to search (defaults to hs and lhs)
+      -X --language=LANG            Language extension (Arrows, NoCPP)
+      -u --utf8                        Use UTF-8 text encoding
+         --encoding=ENC                Choose the text encoding
+      -f --find=FILE                Find hints in a Haskell file
+      -t --test                        Run in test mode
+      -d --datadir=DIR                Override the data directory
+         --cpp-define=NAME[=VALUE]  CPP #define
+         --cpp-include=DIR            CPP include path
+      -? --help                        Display help message
+      -V --version                    Print version information
+      -v --verbose                    Loud verbosity
+      -q --quiet                    Quiet verbosity
+
+    Hlint gives hints on how to improve Haskell code
+
+    To check all Haskell files in 'src' and generate a report type:
+      hlint src --report
+
+
+### Diffy
+
+The Diffy sample is a based on the idea of creating directory listings and comparing them. The tool can operate in two separate modes, `create` or `diff`. This sample is fictional, but the ideas are drawn from a real program. A few notable features:
+
+* There are multiple modes of execution, creating and diffing.
+* The diff mode takes exactly two arguments, the old file and the new file.
+* Default values are given for the `out` field, which are different in both modes.
+
+The code is:
+```haskell
+{-# LANGUAGE DeriveDataTypeable #-}
+module Diffy where
+import System.Console.CmdArgs
+
+data Diffy = Create {src :: Maybe FilePath, out :: FilePath}
+           | Diff {old :: FilePath, new :: FilePath, out :: FilePath}
+             deriving (Data,Typeable,Show,Eq)
+
+outFlags x = x &= help "Output file" &= typFile
+
+create = Create
+    {src = def &= help "Source directory" &= typDir
+    ,out = outFlags "ls.txt"
+    } &= help "Create a fingerprint"
+
+diff = Diff
+    {old = def &= typ "OLDFILE" &= argPos 0
+    ,new = def &= typ "NEWFILE" &= argPos 1
+    ,out = outFlags "diff.txt"
+    } &= help "Perform a diff"
+
+mode = cmdArgsMode $ modes [create,diff] &= help "Create and compare differences" &= program "diffy" &= summary "Diffy v1.0"
+```
+And `--help` produces:
+
+    Diffy v1.0
+
+    diffy [COMMAND] ... [OPTIONS]
+      Create and compare differences
+
+    Common flags:
+      -o --out=FILE     Output file
+      -? --help         Display help message
+      -V --version     Print version information
+
+    diffy create [OPTIONS]
+      Create a fingerprint
+
+      -s  --src=DIR  Source directory
+
+    diffy diff [OPTIONS] OLDFILE NEWFILE
+      Perform a diff
+
+### Maker
+
+The Maker sample is based around a build system, where we can either build a project, clean the temporary files, or run a test. Some interesting features are:
+
+* The build mode is the default, so `maker` on it's own will be interpreted as a build command.
+* The build method is an enumeration.
+* The `threads` field is in two of the constructors, but not all three. It is given the short flag `-j`, rather than the default `-t`.
+
+The code is:
+```haskell
+{-# LANGUAGE DeriveDataTypeable #-}
+module Maker where
+import System.Console.CmdArgs
+
+data Method = Debug | Release | Profile
+              deriving (Data,Typeable,Show,Eq)
+
+data Maker
+    = Wipe
+    | Test {threads :: Int, extra :: [String]}
+    | Build {threads :: Int, method :: Method, files :: [FilePath]}
+      deriving (Data,Typeable,Show,Eq)
+
+threadsMsg x = x &= help "Number of threads to use" &= name "j" &= typ "NUM"
+
+wipe = Wipe &= help "Clean all build objects"
+
+test_ = Test
+    {threads = threadsMsg def
+    ,extra = def &= typ "ANY" &= args
+    } &= help "Run the test suite"
+
+build = Build
+    {threads = threadsMsg def
+    ,method = enum
+        [Release &= help "Release build"
+        ,Debug &= help "Debug build"
+        ,Profile &= help "Profile build"]
+    ,files = def &= args
+    } &= help "Build the project" &= auto
+
+mode = cmdArgsMode $ modes [build,wipe,test_]
+     &= help "Build helper program"
+     &= program "maker"
+     &= summary "Maker v1.0\nMake it"
+```
+And `--help` produces:
+
+    Maker v1.0
+      Make it
+
+    maker [COMMAND] ... [OPTIONS]
+      Build helper program
+
+    Common flags:
+      -? --help     Display help message
+      -V --version  Print version information
+
+    maker [build] [OPTIONS] [ITEM]
+      Build the project
+
+      -j --threads=NUM  Number of threads to use
+      -r --release      Release build
+      -d --debug        Debug build
+      -p --profile      Profile build
+
+    maker wipe [OPTIONS]
+      Clean all build objects
+
+    maker test [OPTIONS] [ANY]
+      Run the test suite
+
+      -j --threads=NUM  Number of threads to use
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,4 +1,2 @@
-#! /usr/bin/runhaskell
-
 import Distribution.Simple
 main = defaultMain
diff --git a/System/Console/CmdArgs/Annotate.hs b/System/Console/CmdArgs/Annotate.hs
--- a/System/Console/CmdArgs/Annotate.hs
+++ b/System/Console/CmdArgs/Annotate.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE PatternGuards, ScopedTypeVariables, ExistentialQuantification, DeriveDataTypeable #-}
+{-# OPTIONS_GHC -O0 #-}
 
 -- | This module captures annotations on a value, and builds a 'Capture' value.
 --   This module has two ways of writing annotations:
@@ -189,14 +190,10 @@
 (&=) :: (Data val, Data ann) => val -> ann -> val
 (&=) x y = addAnn (id_ x) (id_ y)
 
-
-{-# NOINLINE const_ #-}
-const_ :: a -> b -> b
-const_ f x = x
-
 {-# INLINE id_ #-}
 id_ :: a -> a
-id_ x = const_ (\() -> ()) x
+id_ x = case unit of () -> x
+    where unit = reverse "" `seq` ()
 
 
 ---------------------------------------------------------------------
@@ -210,6 +207,7 @@
     | AMany [Annotate ann]
     | AAtom Any
     | ACtor Any [Annotate ann]
+      deriving Typeable
       -- specifically DOES NOT derive Data, to avoid people accidentally including it
 
 
@@ -231,7 +229,7 @@
 --
 --   This operation is not type safe, and may raise an exception at runtime
 --   if any field has the wrong type or label.
-record :: Data a => a -> [Annotate b] -> Annotate b
+record :: Data a => a -> [Annotate ann] -> Annotate ann
 record a b = ACtor (Any a) b
 
 -- | Capture the annotations from an annotated value.
diff --git a/System/Console/CmdArgs/Default.hs b/System/Console/CmdArgs/Default.hs
--- a/System/Console/CmdArgs/Default.hs
+++ b/System/Console/CmdArgs/Default.hs
@@ -3,6 +3,8 @@
 --   To use the default value simply write 'def'.
 module System.Console.CmdArgs.Default where
 
+import Data.Int
+import Data.Word
 
 -- | Class for default values.
 class Default a where
@@ -17,6 +19,16 @@
 instance Default Double where def = 0
 instance Default [a] where def = []
 instance Default (Maybe a) where def = Nothing
+
+instance Default Int8 where def = 0
+instance Default Int16 where def = 0
+instance Default Int32 where def = 0
+instance Default Int64 where def = 0
+instance Default Word where def = 0
+instance Default Word8 where def = 0
+instance Default Word16 where def = 0
+instance Default Word32 where def = 0
+instance Default Word64 where def = 0
 
 -- EXPANDY: $(2\10 instance ($(1,$ Default a$)) => Default ($(1,$ a$)) where def = ($(1,$ def)))
 instance (Default a1,Default a2) => Default (a1,a2) where def = (def,def)
diff --git a/System/Console/CmdArgs/Explicit.hs b/System/Console/CmdArgs/Explicit.hs
--- a/System/Console/CmdArgs/Explicit.hs
+++ b/System/Console/CmdArgs/Explicit.hs
@@ -1,4 +1,4 @@
-
+{-# LANGUAGE ScopedTypeVariables, CPP #-}
 {-|
     This module constructs command lines. You may either use the helper functions
     ('flagNone', 'flagOpt', 'mode' etc.) or construct the type directly. These
@@ -10,21 +10,25 @@
 
     As an example of a parser:
 
-  > arguments :: Mode [(String,String)]
-  > arguments = mode "explicit" [] "Explicit sample program" (flagArg (upd "file") "FILE")
-  >     [flagOpt "world" ["hello","h"] (upd "world") "WHO" "World argument"
-  >     ,flagReq ["greeting","g"] (upd "greeting") "MSG" "Greeting to give"
-  >     ,flagHelpSimple (("help",""):)]
-  >     where upd msg x v = Right $ (msg,x):v
+    @
+    arguments :: 'Mode' [(String,String)]
+    arguments = 'mode' \"explicit\" [] \"Explicit sample program\" ('flagArg' (upd \"file\") \"FILE\")
+        ['flagOpt' \"world\" [\"hello\",\"h\"] (upd \"world\") \"WHO\" \"World argument\"
+        ,'flagReq' [\"greeting\",\"g\"] (upd \"greeting\") \"MSG\" \"Greeting to give\"
+        ,'flagHelpSimple' ((\"help\",\"\"):)]
+        where upd msg x v = Right $ (msg,x):v
+    @
 
     And this can be invoked by:
 
-  > main = do
-  >     x <- processArgs arguments
-  >     if ("help","") `elem` xs then
-  >         print $ helpText def arguments
-  >      else
-  >         print x
+    @
+    main = do
+        xs <- 'processArgs' arguments
+        if (\"help\",\"\") \`elem\` xs then
+            print $ 'helpText' [] 'HelpFormatDefault' arguments
+         else
+            print xs
+    @
 
     /Groups/: The 'Group' structure allows flags/modes to be grouped for the purpose of
     displaying help. When processing command lines, the group structure is ignored.
@@ -37,27 +41,118 @@
     enter a mode, simply give sub-modes, and leave 'modeArgs' as @Nothing@. Alternatively, if
     you want one sub-mode to be selected by default, place all it's flags both in the sub-mode
     and the outer mode.
+
+    /Parsing rules/: Command lines are parsed as per most GNU programs. Short arguments single
+    letter flags start with @-@, longer flags start with @--@, and everything else is considered
+    an argument. Anything after @--@ alone is considered to be an argument. For example:
+
+  > -f --flag argument1 -- --argument2
+
+    This command line passes one single letter flag (@f@), one longer flag (@flag@) and two arguments
+    (@argument1@ and @--argument2@).
 -}
 module System.Console.CmdArgs.Explicit(
     -- * Running command lines
-    module System.Console.CmdArgs.Explicit.Process,
+    process, processArgs, processValue, processValueIO,
     -- * Constructing command lines
     module System.Console.CmdArgs.Explicit.Type,
-    module System.Console.CmdArgs.Explicit,
+    flagHelpSimple, flagHelpFormat, flagVersion, flagNumericVersion, flagsVerbosity,
     -- * Displaying help
-    module System.Console.CmdArgs.Explicit.Help
+    module System.Console.CmdArgs.Explicit.Help,
+    -- * Utilities for working with command lines
+    module System.Console.CmdArgs.Explicit.ExpandArgsAt,
+    module System.Console.CmdArgs.Explicit.SplitJoin,
+    Complete(..), complete
     ) where
 
-import System.Console.CmdArgs.Explicit.Type hiding (showRecord, (*=))
+import System.Console.CmdArgs.Explicit.Type
 import System.Console.CmdArgs.Explicit.Process
 import System.Console.CmdArgs.Explicit.Help
+import System.Console.CmdArgs.Explicit.ExpandArgsAt
+import System.Console.CmdArgs.Explicit.SplitJoin
+import System.Console.CmdArgs.Explicit.Complete
 import System.Console.CmdArgs.Default
+import System.Console.CmdArgs.Helper
 import System.Console.CmdArgs.Text
 import System.Console.CmdArgs.Verbosity
 
+import Control.Monad
 import Data.Char
+import Data.Maybe
+import System.Environment
+import System.Exit
+import System.IO
 
 
+-- | Process the flags obtained by @'getArgs'@ and @'expandArgsAt'@ with a mode. Displays
+--   an error and exits with failure if the command line fails to parse, or returns
+--   the associated value. Implemented in terms of 'process'. This function makes
+--   use of the following environment variables:
+--
+-- * @$CMDARGS_COMPLETE@ - causes the program to produce completions using 'complete', then exit.
+--   Completions are based on the result of 'getArgs', the index of the current argument is taken
+--   from @$CMDARGS_COMPLETE@ (set it to @-@ to complete the last argument), and the index within
+--   that argument is taken from @$CMDARGS_COMPLETE_POS@ (if set).
+--
+-- * @$CMDARGS_HELPER@\/@$CMDARGS_HELPER_/PROG/@ - uses the helper mechanism for entering command
+--   line programs as described in "System.Console.CmdArgs.Helper".
+processArgs :: Mode a -> IO a
+processArgs m = do
+    env <- getEnvironment
+    case lookup "CMDARGS_COMPLETE" env of
+        Just x -> do
+            args <- getArgs
+            let argInd = fromMaybe (length args - 1) $ readMay x
+                argPos = fromMaybe (if argInd >= 0 && argInd < length args then length (args !! argInd) else 0) $
+                         readMay =<< lookup "CMDARGS_COMPLETE_POS" env
+            print $ complete m (concatMap words args) (argInd,argPos)
+            exitWith ExitSuccess
+        Nothing -> do
+            nam <- getProgName
+            let var = mplus (lookup ("CMDARGS_HELPER_" ++ show (map toUpper $ head $ modeNames m ++ [nam])) env)
+                            (lookup "CMDARGS_HELPER" env)
+            case var of
+                Nothing -> processValueIO m =<< (if modeExpandAt m then expandArgsAt else return) =<< getArgs
+                Just cmd -> do
+                    res <- execute cmd m []
+                    case res of
+                        Left err -> do
+                            hPutStrLn stderr $ "Error when running helper " ++ cmd
+                            hPutStrLn stderr err
+                            exitFailure
+                        Right args -> processValueIO m args
+
+
+readMay :: Read a => String -> Maybe a
+readMay s = case [x | (x,t) <- reads s, ("","") <- lex t] of
+                [x] -> Just x
+                _ -> Nothing
+
+
+#if __GLASGOW_HASKELL__ < 800
+errorWithoutStackTrace :: String -> a
+errorWithoutStackTrace = error
+#endif
+
+-- | Process a list of flags (usually obtained from @'getArgs'@ and @'expandArgsAt'@) with a mode.
+--   Throws an error if the command line fails to parse, or returns
+--   the associated value. Implemeneted in terms of 'process'. This function
+--   does not take account of any environment variables that may be set
+--   (see 'processArgs').
+--
+--   If you are in 'IO' you will probably get a better user experience by calling 'processValueIO'.
+processValue :: Mode a -> [String] -> a
+processValue m xs = case process m xs of
+    Left x -> errorWithoutStackTrace x
+    Right x -> x
+
+-- | Like 'processValue' but on failure prints to stderr and exits the program.
+processValueIO :: Mode a -> [String] -> IO a
+processValueIO m xs = case process m xs of
+    Left x -> do hPutStrLn stderr x; exitFailure
+    Right x -> return x
+
+
 -- | Create a help flag triggered by @-?@/@--help@.
 flagHelpSimple :: (a -> a) -> Flag a
 flagHelpSimple f = flagNone ["help","?"] f "Display help message"
@@ -78,7 +173,7 @@
             Right (a,b) -> Right $ f a b v
 
         format :: String -> Either String (HelpFormat,TextFormat)
-        format xs = foldl (\acc x -> either Left (f x) acc) (Right def) (sep xs)
+        format xs = foldl (\acc x -> f x =<< acc) (Right def) (sep xs)
             where
                 sep = words . map (\x -> if x `elem` ":," then ' ' else toLower x)
                 f x (a,b) = case x of
@@ -87,6 +182,8 @@
                     "def" -> Right (HelpFormatDefault,b)
                     "html" -> Right (a,HTML)
                     "text" -> Right (a,defaultWrap)
+                    "bash" -> Right (HelpFormatBash,Wrap 1000000)
+                    "zsh"  -> Right (HelpFormatZsh ,Wrap 1000000)
                     _ | all isDigit x -> Right (a,Wrap $ read x)
                     _ -> Left "unrecognised help format, expected one of: all one def html text <NUMBER>"
 
@@ -94,6 +191,10 @@
 -- | Create a version flag triggered by @-V@/@--version@.
 flagVersion :: (a -> a) -> Flag a
 flagVersion f = flagNone ["version","V"] f "Print version information"
+
+-- | Create a version flag triggered by @--numeric-version@.
+flagNumericVersion :: (a -> a) -> Flag a
+flagNumericVersion f = flagNone ["numeric-version"] f "Print just the version number"
 
 
 -- | Create verbosity flags triggered by @-v@/@--verbose@ and
diff --git a/System/Console/CmdArgs/Explicit/Complete.hs b/System/Console/CmdArgs/Explicit/Complete.hs
new file mode 100644
--- /dev/null
+++ b/System/Console/CmdArgs/Explicit/Complete.hs
@@ -0,0 +1,201 @@
+{-# LANGUAGE PatternGuards #-}
+
+-- | This module does command line completion
+module System.Console.CmdArgs.Explicit.Complete(
+    Complete(..), complete,
+    completeBash, completeZsh
+    ) where
+
+import System.Console.CmdArgs.Explicit.Type
+import Control.Monad
+import Data.List
+import Data.Maybe
+
+
+-- | How to complete a command line option.
+--   The 'Show' instance is suitable for parsing from shell scripts.
+data Complete
+    = CompleteValue String -- ^ Complete to a particular value
+    | CompleteFile String FilePath -- ^ Complete to a prefix, and a file
+    | CompleteDir String FilePath -- ^ Complete to a prefix, and a directory
+      deriving (Eq,Ord)
+
+instance Show Complete where
+    show (CompleteValue a) = "VALUE " ++ a
+    show (CompleteFile a b) = "FILE " ++ a ++ " " ++ b
+    show (CompleteDir a b) = "DIR " ++ a ++ " " ++ b
+
+    showList xs = showString $ unlines (map show xs)
+
+
+prepend :: String -> Complete -> Complete
+prepend a (CompleteFile b c) = CompleteFile (a++b) c
+prepend a (CompleteDir b c) = CompleteDir (a++b) c
+prepend a (CompleteValue b) = CompleteValue (a++b)
+
+
+-- | Given a current state, return the set of commands you could type now, in preference order.
+complete
+    :: Mode a -- ^ Mode specifying which arguments are allowed
+    -> [String] -- ^ Arguments the user has already typed
+    -> (Int,Int) -- ^ 0-based index of the argument they are currently on, and the position in that argument
+    -> [Complete]
+-- Roll forward looking at modes, and if you match a mode, enter it
+-- If the person just before is a flag without arg, look at how you can complete that arg
+-- If your prefix is a complete flag look how you can complete that flag
+-- If your prefix looks like a flag, look for legitimate flags
+-- Otherwise give a file/dir if they are arguments to this mode, and all flags
+-- If you haven't seen any args/flags then also autocomplete to any child modes
+complete mode_ args_ (i,_) = nub $ followArgs mode args now
+    where
+        (seen,next) = splitAt i args_
+        now = head $ next ++ [""]
+        (mode,args) = followModes mode_ seen
+
+
+-- | Given a mode and some arguments, try and drill down into the mode
+followModes :: Mode a -> [String] -> (Mode a, [String])
+followModes m (x:xs) | Just m2 <- pickBy modeNames x $ modeModes m = followModes m2 xs
+followModes m xs = (m,xs)
+
+
+pickBy :: (a -> [String]) -> String -> [a] -> Maybe a
+pickBy f name xs = find (\x -> name `elem` f x) xs `mplus`
+                   find (\x -> any (name `isPrefixOf`) (f x)) xs
+
+
+-- | Follow args deals with all seen arguments, then calls on to deal with the next one
+followArgs :: Mode a -> [String] -> (String -> [Complete])
+followArgs m = first
+    where
+        first [] = expectArgFlagMode (modeModes m) (argsPick 0) (modeFlags m)
+        first xs = norm 0 xs
+
+        -- i is the number of arguments that have gone past
+        norm i [] = expectArgFlag (argsPick i) (modeFlags m)
+        norm i ("--":xs) = expectArg $ argsPick (i + length xs)
+        norm i (('-':'-':x):xs) | null b, flagInfo flg == FlagReq = val i flg xs
+                                | otherwise = norm i xs
+            where (a,b) = break (== '=') x
+                  flg = getFlag a
+        norm i (('-':x:y):xs) = case flagInfo flg of
+            FlagReq | null y -> val i flg xs
+                    | otherwise -> norm i xs
+            FlagOpt{} -> norm i xs
+            _ | "=" `isPrefixOf` y -> norm i xs
+              | null y -> norm i xs
+              | otherwise -> norm i (('-':y):xs)
+            where flg = getFlag [x]
+        norm i (x:xs) = norm (i+1) xs
+
+        val i flg [] = expectVal flg
+        val i flg (x:xs) = norm i xs
+
+        argsPick i = let (lst,end) = modeArgs m in if i < length lst then Just $ lst !! i else end
+
+        -- if you can't find the flag, pick one that is FlagNone (has all the right fallback)
+        getFlag x = fromMaybe (flagNone [] id "") $ pickBy flagNames x $ modeFlags m
+
+
+expectArgFlagMode :: [Mode a] -> Maybe (Arg a) -> [Flag a] -> String -> [Complete]
+expectArgFlagMode mode arg flag x =
+    (if "-" `isPrefixOf` x then [] else expectMode mode x) ++
+    expectArgFlag arg flag x
+
+expectArgFlag :: Maybe (Arg a) -> [Flag a] -> String -> [Complete]
+expectArgFlag arg flag x
+    | "-" `isPrefixOf` x = expectFlag flag x ++ [CompleteValue "-" | x == "-", isJust arg]
+    | otherwise = expectArg arg x ++ expectFlag flag x
+
+expectMode :: [Mode a] -> String -> [Complete]
+expectMode mode = expectStrings (map modeNames mode)
+
+expectArg :: Maybe (Arg a) -> String -> [Complete]
+expectArg Nothing x = []
+expectArg (Just arg) x = expectFlagHelp (argType arg) x
+
+expectFlag :: [Flag a] -> String -> [Complete]
+expectFlag flag x
+    | (a,_:b) <- break (== '=') x = case pickBy (map f . flagNames) a flag of
+        Nothing -> []
+        Just flg -> map (prepend (a ++ "=")) $ expectVal flg b
+    | otherwise = expectStrings (map (map f . flagNames) flag) x
+    where f x = "-" ++ ['-' | length x > 1] ++ x
+
+expectVal :: Flag a -> String -> [Complete]
+expectVal flg = expectFlagHelp (flagType flg)
+
+expectStrings :: [[String]] -> String -> [Complete]
+expectStrings xs x = map CompleteValue $ concatMap (take 1 . filter (x `isPrefixOf`)) xs
+
+expectFlagHelp :: FlagHelp -> String -> [Complete]
+expectFlagHelp typ x = case typ of
+    "FILE" -> [CompleteFile "" x]
+    "DIR" -> [CompleteDir "" x]
+    "FILE/DIR" -> [CompleteFile "" x, CompleteDir "" x]
+    "DIR/FILE" -> [CompleteDir "" x, CompleteFile "" x]
+    '[':s | "]" `isSuffixOf` s -> expectFlagHelp (init s) x
+    _ -> []
+
+
+---------------------------------------------------------------------
+-- BASH SCRIPT
+
+completeBash :: String -> [String]
+completeBash prog =
+    ["# Completion for " ++ prog
+    ,"# Generated by CmdArgs: http://community.haskell.org/~ndm/cmdargs/"
+    ,"_" ++ prog ++ "()"
+    ,"{"
+    ,"    # local CMDARGS_DEBUG=1 # uncomment to debug this script"
+    ,""
+    ,"    COMPREPLY=()"
+    ,"    function add { COMPREPLY[((${#COMPREPLY[@]} + 1))]=$1 ; }"
+    ,"    IFS=$'\\n\\r'"
+    ,""
+    ,"    export CMDARGS_COMPLETE=$((${COMP_CWORD} - 1))"
+    ,"    result=`" ++ prog ++ " ${COMP_WORDS[@]:1}`"
+    ,""
+    ,"    if [ -n $CMDARGS_DEBUG ]; then"
+    ,"        echo Call \\(${COMP_WORDS[@]:1}, $CMDARGS_COMPLETE\\) > cmdargs.tmp"
+    ,"        echo $result >> cmdargs.tmp"
+    ,"    fi"
+    ,"    unset CMDARGS_COMPLETE"
+    ,"    unset CMDARGS_COMPLETE_POS"
+    ,""
+    ,"    for x in $result ; do"
+    ,"        case $x in"
+    ,"            VALUE\\ *)"
+    ,"                add ${x:6}"
+    ,"                ;;"
+    ,"            FILE\\ *)"
+    ,"                local prefix=`expr match \"${x:5}\" '\\([^ ]*\\)'`"
+    ,"                local match=`expr match \"${x:5}\" '[^ ]* \\(.*\\)'`"
+    ,"                for x in `compgen -f -- \"$match\"`; do"
+    ,"                    add $prefix$x"
+    ,"                done"
+    ,"                ;;"
+    ,"            DIR\\ *)"
+    ,"                local prefix=`expr match \"${x:4}\" '\\([^ ]*\\)'`"
+    ,"                local match=`expr match \"${x:4}\" '[^ ]* \\(.*\\)'`"
+    ,"                for x in `compgen -d -- \"$match\"`; do"
+    ,"                    add $prefix$x"
+    ,"                done"
+    ,"                ;;"
+    ,"        esac"
+    ,"    done"
+    ,"    unset IFS"
+    ,""
+    ,"    if [ -n $CMDARGS_DEBUG ]; then"
+    ,"        echo echo COMPREPLY: ${#COMPREPLY[@]} = ${COMPREPLY[@]} >> cmdargs.tmp"
+    ,"    fi"
+    ,"}"
+    ,"complete -o bashdefault -F _" ++ prog ++ " " ++ prog
+    ]
+
+
+---------------------------------------------------------------------
+-- ZSH SCRIPT
+
+completeZsh :: String -> [String]
+completeZsh _ = ["echo TODO: help add Zsh completions to cmdargs programs"]
diff --git a/System/Console/CmdArgs/Explicit/ExpandArgsAt.hs b/System/Console/CmdArgs/Explicit/ExpandArgsAt.hs
new file mode 100644
--- /dev/null
+++ b/System/Console/CmdArgs/Explicit/ExpandArgsAt.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE RecordWildCards #-}
+module System.Console.CmdArgs.Explicit.ExpandArgsAt(expandArgsAt) where
+
+import System.FilePath
+
+
+-- | Expand @\@@ directives in a list of arguments, usually obtained from 'getArgs'.
+--   As an example, given the file @test.txt@ with the lines @hello@ and @world@:
+--
+-- > expandArgsAt ["@test.txt","!"] == ["hello","world","!"]
+--
+--   Any @\@@ directives in the files will be recursively expanded (raising an error
+--   if there is infinite recursion).
+--
+--   To supress @\@@ expansion, pass any @\@@ arguments after @--@.
+expandArgsAt :: [String] -> IO [String]
+expandArgsAt args = do
+        ebefore <- mapM (f [] ".") before
+        return $ concat ebefore ++ after
+    where
+        (before,after) = break (== "--") args
+
+        f seen dir ('@':x)
+            | x `elem` seen = error $ unlines $
+                "System.Console.CmdArgs.Explicit.expandArgsAt, recursion in @ directives:" :
+                map ("  "++) (reverse $ x:seen)
+            | length seen > 15 = error $ unlines $
+                "System.Console.CmdArgs.Explicit.expandArgsAt, over 15 @ directives deep:" :
+                map ("  "++) (reverse seen)
+            | otherwise = do
+                src <- readFile $ dir </> x
+                fmap concat $ mapM (f (x:seen) (takeDirectory x)) $ lines src
+        f _ _ x = return [x]
diff --git a/System/Console/CmdArgs/Explicit/Help.hs b/System/Console/CmdArgs/Explicit/Help.hs
--- a/System/Console/CmdArgs/Explicit/Help.hs
+++ b/System/Console/CmdArgs/Explicit/Help.hs
@@ -1,3 +1,4 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-} -- Not good reasons, but shouldn't be too fatal
 {-
 Sample renderings:
 
@@ -6,7 +7,7 @@
 
 programname [OPTIONS] FILE1 FILE2 [FILES]
   Program to perform some action
-  
+
   -f --flag     description
 Flag grouping:
   -a --another  description
@@ -45,6 +46,7 @@
 module System.Console.CmdArgs.Explicit.Help(HelpFormat(..), helpText) where
 
 import System.Console.CmdArgs.Explicit.Type
+import System.Console.CmdArgs.Explicit.Complete
 import System.Console.CmdArgs.Text
 import System.Console.CmdArgs.Default
 import Data.List
@@ -56,6 +58,8 @@
     = HelpFormatDefault -- ^ Equivalent to 'HelpFormatAll' if there is not too much text, otherwise 'HelpFormatOne'.
     | HelpFormatOne -- ^ Display only the first mode.
     | HelpFormatAll -- ^ Display all modes.
+    | HelpFormatBash -- ^ Bash completion information
+    | HelpFormatZsh -- ^ Z shell completion information
       deriving (Read,Show,Enum,Bounded,Eq,Ord)
 
 instance Default HelpFormat where def = HelpFormatDefault
@@ -70,13 +74,20 @@
 instance Show (Arg a) where
     show = show . argType
 
--- | Generate a help message from a mode.
-helpText :: HelpFormat -> Mode a -> [Text]
-helpText HelpFormatDefault = helpTextDefault
-helpText HelpFormatOne = helpTextOne
-helpText HelpFormatAll = helpTextAll
+-- | Generate a help message from a mode.  The first argument is a prefix,
+--   which is prepended when not using 'HelpFormatBash' or 'HelpFormatZsh'.
+helpText :: [String] -> HelpFormat -> Mode a -> [Text]
+helpText pre HelpFormatDefault x = helpPrefix pre ++ helpTextDefault x
+helpText pre HelpFormatOne x = helpPrefix pre ++ helpTextOne x
+helpText pre HelpFormatAll x = helpPrefix pre ++ helpTextAll x
+helpText pre HelpFormatBash x = map Line $ completeBash $ head $ modeNames x ++ ["unknown"]
+helpText pre HelpFormatZsh x = map Line $ completeZsh $ head $ modeNames x ++ ["unknown"]
 
 
+helpPrefix :: [String] -> [Text]
+helpPrefix xs = map Line xs ++ [Line "" | not $ null xs]
+
+
 helpTextDefault x = if length all > 40 then one else all
     where all = helpTextAll x
           one = helpTextOne x
@@ -104,7 +115,7 @@
 -- > <options>
 helpTextOne :: Mode a -> [Text]
 helpTextOne m = pre ++ ms ++ suf
-    where 
+    where
         (pre,suf) = helpTextMode m
         ms = space $ [Line "Commands:" | not $ null $ groupUnnamed $ modeGroupModes m] ++ helpGroup f (modeGroupModes m)
         f m = return $ cols [concat $ take 1 $ modeNames m, ' ' : modeHelp m]
@@ -116,7 +127,7 @@
         pre = [Line $ unwords $ take 1 (modeNames x) ++
                   ["[COMMAND] ..." | notNullGroup modes] ++
                   ["[OPTIONS]" | not $ null $ fromGroup flags] ++
-                  map argType (maybeToList $ modeArgs x)] ++
+                  helpArgs (modeArgs x)] ++
               [Line $ "  " ++ modeHelp x | not $ null $ modeHelp x]
         suf = space
                   ([Line "Flags:" | mixedGroup flags] ++
@@ -127,6 +138,12 @@
 helpGroup :: (a -> [Text]) -> Group a -> [Text]
 helpGroup f xs = concatMap f (groupUnnamed xs) ++ concatMap g (groupNamed xs)
     where g (a,b) = Line (a ++ ":") : concatMap f b
+
+
+helpArgs :: ([Arg a], Maybe (Arg a)) -> [String]
+helpArgs (ys,y) = [['['|o] ++ argType x ++ [']'|o] | (i,x) <- zip [0..] xs, let o = False && req <= i]
+    where xs = ys ++ maybeToList y
+          req = maximum $ 0 : [i | (i,x) <- zip [1..] xs, argRequire x]
 
 
 helpFlag :: Flag a -> [Text]
diff --git a/System/Console/CmdArgs/Explicit/Process.hs b/System/Console/CmdArgs/Explicit/Process.hs
--- a/System/Console/CmdArgs/Explicit/Process.hs
+++ b/System/Console/CmdArgs/Explicit/Process.hs
@@ -1,36 +1,13 @@
-
-module System.Console.CmdArgs.Explicit.Process(process, processValue, processArgs) where
+{-# LANGUAGE RecordWildCards #-}
+module System.Console.CmdArgs.Explicit.Process(process) where
 
 import System.Console.CmdArgs.Explicit.Type
 import Control.Arrow
 import Data.List
 import Data.Maybe
-import System.Environment
-import System.Exit
-import System.IO
 
 
--- | Process the flags obtained by @getArgs@ with a mode. Displays
---   an error and exits with failure if the command line fails to parse, or returns
---   the associated value. Implemented in terms of 'process'.
-processArgs :: Mode a -> IO a
-processArgs m = do
-    xs <- getArgs
-    case process m xs of
-        Left x -> do hPutStrLn stderr x; exitFailure
-        Right x -> return x
-
-
--- | Process a list of flags (usually obtained from @getArgs@) with a mode. Displays
---   an error and exits with failure if the command line fails to parse, or returns
---   the associated value. Implemeneted in terms of 'process'.
-processValue :: Mode a -> [String] -> a
-processValue m xs = case process m xs of
-    Left x -> error x
-    Right x -> x
-
-
--- | Process a list of flags (usually obtained from @getArgs@) with a mode. Returns
+-- | Process a list of flags (usually obtained from @getArgs@/@expandArgsAt@) with a mode. Returns
 --   @Left@ and an error message if the command line fails to parse, or @Right@ and
 --   the associated value.
 process :: Mode a -> [String] -> Either String a
@@ -43,10 +20,10 @@
         Ambiguous xs -> Left $ ambiguous "mode" a xs
         Found x -> processMode x as
         NotFound
-            | isNothing (modeArgs m) && args /= [] &&
+            | null (fst $ modeArgs m) && isNothing (snd $ modeArgs m) && args /= [] &&
               not (null $ modeModes m) && not ("-" `isPrefixOf` concat args)
                 -> Left $ missing "mode" $ concatMap modeNames $ modeModes m
-            | otherwise -> either Left (modeCheck m) $ processFlags m (modeValue m) args
+            | otherwise -> modeCheck m =<< processFlags m (modeValue m) args
     where
         (find,a,as) = case args of
             [] -> (NotFound,"",[])
@@ -54,15 +31,19 @@
 
 
 data S a = S
-    {val :: a
-    ,args :: [String]
-    ,errs :: [String]
+    {val :: a -- The value you are accumulating
+    ,args :: [String] -- The arguments you are processing through
+    ,argsCount :: Int -- The number of unnamed arguments you have seen
+    ,errs :: [String] -- The errors you have seen
     }
 
-stop :: S a -> Maybe (Either String a)
-stop s | not $ null $ errs s = Just $ Left $ last $ errs s
-       | null $ args s = Just $ Right $ val s
-       | otherwise = Nothing
+stop :: Mode a -> S a -> Maybe (Either String a)
+stop mode S{..}
+    | not $ null errs = Just $ Left $ last errs
+    | null args = Just $ if argsCount >= mn then Right val else
+        Left $ "Expected " ++ (if Just mn == mx then "exactly" else "at least") ++ show mn ++ " unnamed arguments, but got only " ++ show argsCount
+    | otherwise = Nothing
+    where (mn, mx) = argsRange mode
 
 err :: S a -> String -> S a
 err s x = s{errs=x:errs s}
@@ -74,8 +55,8 @@
 
 
 processFlags :: Mode a -> a -> [String] -> Either String a
-processFlags mode val_ args_ = f $ S val_ args_ []
-    where f s = fromMaybe (f $ processFlag mode s) $ stop s
+processFlags mode val_ args_ = f $ S val_ args_ 0 []
+    where f s = fromMaybe (f $ processFlag mode s) $ stop mode s
 
 
 pickFlags long mode = [(filter (\x -> (length x > 1) == long) $ flagNames flag,(flagInfo flag,flag)) | flag <- modeFlags mode]
@@ -118,21 +99,32 @@
 
 
 processFlag mode s_@S{args="--":ys} = f s_{args=ys}
-    where f s | isJust $ stop s = s
+    where f s | isJust $ stop mode s = s
               | otherwise = f $ processArg mode s
 
 processFlag mode s = processArg mode s
 
-processArg mode s_@S{args=x:ys} =
-    case modeArgs mode of
-        Nothing -> err s $ "Unhandled argument, none expected: " ++ x
-        Just arg -> case argValue arg x (val s) of
+processArg mode s_@S{args=x:ys, argsCount=count} = case argsPick mode count of
+    Nothing -> err s $ "Unhandled argument, " ++ str ++ " expected: " ++ x
+        where str = if count == 0 then "none" else "at most " ++ show count
+    Just arg -> case argValue arg x (val s) of
             Left e -> err s $ "Unhandled argument, " ++ e ++ ": " ++ x
             Right v -> s{val=v}
     where
-        s = s_{args=ys}
+        s = s_{args=ys, argsCount=count+1}
 
 
+-- find the minimum and maximum allowed number of arguments (Nothing=infinite)
+argsRange :: Mode a -> (Int, Maybe Int)
+argsRange Mode{modeArgs=(lst,end)} = (mn,mx)
+    where mn = length $ dropWhile (not . argRequire) $ reverse $ lst ++ maybeToList end
+          mx = if isJust end then Nothing else Just $ length lst
+
+
+argsPick :: Mode a -> Int -> Maybe (Arg a)
+argsPick Mode{modeArgs=(lst,end)} i = if i < length lst then Just $ lst !! i else end
+
+
 ---------------------------------------------------------------------
 -- UTILITIES
 
@@ -155,4 +147,3 @@
         (xs,_) -> Ambiguous $ map fst xs
     where
         match op = [(head ys,v) | (xs,v) <- names, let ys = filter (op value) xs, ys /= []]
-    
diff --git a/System/Console/CmdArgs/Explicit/SplitJoin.hs b/System/Console/CmdArgs/Explicit/SplitJoin.hs
new file mode 100644
--- /dev/null
+++ b/System/Console/CmdArgs/Explicit/SplitJoin.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE RecordWildCards #-}
+module System.Console.CmdArgs.Explicit.SplitJoin(splitArgs, joinArgs) where
+
+import Data.Char
+import Data.Maybe
+
+
+-- | Given a sequence of arguments, join them together in a manner that could be used on
+--   the command line, giving preference to the Windows @cmd@ shell quoting conventions.
+--
+--   For an alternative version, intended for actual running the result in a shell, see "System.Process.showCommandForUser"
+joinArgs :: [String] -> String
+joinArgs = unwords . map f
+    where
+        f x = q ++ g x ++ q
+            where
+                hasSpace = any isSpace x
+                q = ['\"' | hasSpace || null x]
+
+                g ('\\':'\"':xs) = '\\':'\\':'\\':'\"': g xs
+                g "\\" | hasSpace = "\\\\"
+                g ('\"':xs) = '\\':'\"': g xs
+                g (x:xs) = x : g xs
+                g [] = []
+
+
+data State = Init -- either I just started, or just emitted something
+           | Norm -- I'm seeing characters
+           | Quot -- I've seen a quote
+
+-- | Given a string, split into the available arguments. The inverse of 'joinArgs'.
+splitArgs :: String -> [String]
+splitArgs = join . f Init
+    where
+        -- Nothing is start a new string
+        -- Just x is accumulate onto the existing string
+        join :: [Maybe Char] -> [String]
+        join [] = []
+        join xs = map fromJust a : join (drop 1 b)
+            where (a,b) = break isNothing xs
+
+        f Init (x:xs) | isSpace x = f Init xs
+        f Init "\"\"" = [Nothing]
+        f Init "\"" = [Nothing]
+        f Init xs = f Norm xs
+        f m ('\"':'\"':'\"':xs) = Just '\"' : f m xs
+        f m ('\\':'\"':xs) = Just '\"' : f m xs
+        f m ('\\':'\\':'\"':xs) = Just '\\' : f m ('\"':xs)
+        f Norm ('\"':xs) = f Quot xs
+        f Quot ('\"':'\"':xs) = Just '\"' : f Norm xs
+        f Quot ('\"':xs) = f Norm xs
+        f Norm (x:xs) | isSpace x = Nothing : f Init xs
+        f m (x:xs) = Just x : f m xs
+        f m [] = []
diff --git a/System/Console/CmdArgs/Explicit/Type.hs b/System/Console/CmdArgs/Explicit/Type.hs
--- a/System/Console/CmdArgs/Explicit/Type.hs
+++ b/System/Console/CmdArgs/Explicit/Type.hs
@@ -6,7 +6,8 @@
 import Data.Char
 import Data.List
 import Data.Maybe
-import Data.Monoid
+import Data.Semigroup hiding (Arg)
+import Prelude
 
 
 -- | A name, either the name of a flag (@--/foo/@) or the name of a mode.
@@ -22,7 +23,7 @@
 ---------------------------------------------------------------------
 -- UTILITY
 
--- | Parse a boolean, accepts as True: true yes on enabled 1. 
+-- | Parse a boolean, accepts as True: true yes on enabled 1.
 parseBool :: String -> Maybe Bool
 parseBool s | ls `elem` true  = Just True
             | ls `elem` false = Just False
@@ -42,14 +43,17 @@
     {groupUnnamed :: [a] -- ^ Normal items.
     ,groupHidden :: [a] -- ^ Items that are hidden (not displayed in the help message).
     ,groupNamed :: [(Help, [a])] -- ^ Items that have been grouped, along with a description of each group.
-    } deriving Show 
+    } deriving Show
 
 instance Functor Group where
     fmap f (Group a b c) = Group (map f a) (map f b) (map (second $ map f) c)
 
+instance Semigroup (Group a) where
+    Group x1 x2 x3 <> Group y1 y2 y3 = Group (x1++y1) (x2++y2) (x3++y3)
+
 instance Monoid (Group a) where
     mempty = Group [] [] []
-    mappend (Group x1 x2 x3) (Group y1 y2 y3) = Group (x1++y1) (x2++y2) (x3++y3)
+    mappend = (<>)
 
 -- | Convert a group into a list.
 fromGroup :: Group a -> [a]
@@ -63,26 +67,28 @@
 ---------------------------------------------------------------------
 -- TYPES
 
-showRecord x ys = x ++ " {" ++ intercalate ", " ys ++ "}"
-a *= b = a ++ " = " ++ show b
-
-
--- | A mode. Each mode has three main features:
+-- | A mode. Do not use the 'Mode' constructor directly, instead
+--   use 'mode' to construct the 'Mode' and then record updates.
+--   Each mode has three main features:
 --
 --   * A list of submodes ('modeGroupModes')
 --
 --   * A list of flags ('modeGroupFlags')
 --
 --   * Optionally an unnamed argument ('modeArgs')
+--
+--  To produce the help information for a mode, either use 'helpText' or 'show'.
 data Mode a = Mode
     {modeGroupModes :: Group (Mode a) -- ^ The available sub-modes
     ,modeNames :: [Name] -- ^ The names assigned to this mode (for the root mode, this name is used as the program name)
     ,modeValue :: a -- ^ Value to start with
     ,modeCheck :: a -> Either String a -- ^ Check the value reprsented by a mode is correct, after applying all flags
     ,modeReform :: a -> Maybe [String] -- ^ Given a value, try to generate the input arguments.
+    ,modeExpandAt :: Bool -- ^ Expand @\@@ arguments with 'expandArgsAt', defaults to 'True', only applied if using an 'IO' processing function.
+                          --   Only the root 'Mode's value will be used.
     ,modeHelp :: Help -- ^ Help text
     ,modeHelpSuffix :: [String] -- ^ A longer help suffix displayed after a mode
-    ,modeArgs :: Maybe (Arg a) -- ^ An unnamed argument
+    ,modeArgs :: ([Arg a], Maybe (Arg a)) -- ^ The unnamed arguments, a series of arguments, followed optionally by one for all remaining slots
     ,modeGroupFlags :: Group (Flag a) -- ^ Groups of flags
     }
 
@@ -98,8 +104,8 @@
 --
 --
 -- >              FlagReq     FlagOpt      FlagOptRare/FlagNone
--- > -xfoo        -x=foo      -x=foo       -x= -foo
--- > -x foo       -x=foo      -x foo       -x= foo
+-- > -xfoo        -x=foo      -x=foo       -x -foo
+-- > -x foo       -x=foo      -x foo       -x foo
 -- > -x=foo       -x=foo      -x=foo       -x=foo
 -- > --xx foo     --xx=foo    --xx foo     --xx foo
 -- > --xx=foo     --xx=foo    --xx=foo     --xx=foo
@@ -141,6 +147,7 @@
 data Arg a = Arg
     {argValue :: Update a -- ^ A way of processing the argument.
     ,argType :: FlagHelp -- ^ The type of data for the argument, i.e. FILE\/DIR\/EXT
+    ,argRequire :: Bool -- ^ Is at least one of these arguments required, the command line will fail if none are set
     }
 
 
@@ -158,11 +165,11 @@
     where
         checkGroup :: Group a -> Maybe String
         checkGroup x = msum
-            [check "Empty group name" $ all (not . null . fst) $ groupNamed x
-            ,check "Empty group contents" $ all (not . null . snd) $ groupNamed x]
+            [check "Empty group name" $ not $ any (null . fst) $ groupNamed x
+            ,check "Empty group contents" $ not $ any (null . snd) $ groupNamed x]
 
         checkNames :: String -> [Name] -> Maybe String
-        checkNames msg xs = check "Empty names" (all (not . null) xs) `mplus` do
+        checkNames msg xs = check "Empty names" (not (any null xs)) `mplus` do
             bad <- listToMaybe $ xs \\ nub xs
             let dupe = filter (== bad) xs
             return $ "Sanity check failed, multiple " ++ msg ++ ": " ++ unwords (map show dupe)
@@ -175,11 +182,14 @@
 ---------------------------------------------------------------------
 -- REMAP
 
+-- | Like functor, but where the the argument isn't just covariant.
 class Remap m where
+    -- | Convert between two values.
     remap :: (a -> b) -- ^ Embed a value
           -> (b -> (a, a -> b)) -- ^ Extract the mode and give a way of re-embedding
           -> m a -> m b
 
+-- | Restricted version of 'remap' where the values are isomorphic.
 remap2 :: Remap m => (a -> b) -> (b -> a) -> m a -> m b
 remap2 f g = remap f (\x -> (g x, f))
 
@@ -189,7 +199,7 @@
         ,modeValue = f $ modeValue x
         ,modeCheck = \v -> let (a,b) = g v in fmap b $ modeCheck x a
         ,modeReform = modeReform x . fst . g
-        ,modeArgs = fmap (remap f g) $ modeArgs x
+        ,modeArgs = (fmap (remap f g) *** fmap (remap f g)) $ modeArgs x
         ,modeGroupFlags = fmap (remap f g) $ modeGroupFlags x}
 
 instance Remap Flag where
@@ -198,6 +208,8 @@
 instance Remap Arg where
     remap f g x = x{argValue = remapUpdate f g $ argValue x}
 
+-- | Version of 'remap' for the 'Update' type alias.
+remapUpdate :: (a -> b) -> (b -> (a, a -> b)) -> Update a -> Update b
 remapUpdate f g upd = \s v -> let (a,b) = g v in fmap b $ upd s a
 
 
@@ -207,12 +219,12 @@
 -- | Create an empty mode specifying only 'modeValue'. All other fields will usually be populated
 --   using record updates.
 modeEmpty :: a -> Mode a
-modeEmpty x = Mode mempty [] x Right (const Nothing) "" [] Nothing mempty
+modeEmpty x = Mode mempty [] x Right (const Nothing) True "" [] ([],Nothing) mempty
 
 -- | Create a mode with a name, an initial value, some help text, a way of processing arguments
 --   and a list of flags.
 mode :: Name -> a -> Help -> Arg a -> [Flag a] -> Mode a
-mode name value help arg flags = (modeEmpty value){modeNames=[name], modeHelp=help, modeArgs=Just arg, modeGroupFlags=toGroup flags}
+mode name value help arg flags = (modeEmpty value){modeNames=[name], modeHelp=help, modeArgs=([],Just arg), modeGroupFlags=toGroup flags}
 
 -- | Create a list of modes, with a program name, an initial value, some help text and the child modes.
 modes :: String -> a -> Help -> [Mode a] -> Mode a
@@ -240,7 +252,7 @@
 
 -- | Create an argument flag, with an update function and the type of the argument.
 flagArg :: Update a -> FlagHelp -> Arg a
-flagArg upd typ = Arg upd typ
+flagArg upd typ = Arg upd typ False
 
 -- | Create a boolean flag, with a list of flag names, an update function and some help text.
 flagBool :: [Name] -> (Bool -> a -> a) -> Help -> Flag a
diff --git a/System/Console/CmdArgs/GetOpt.hs b/System/Console/CmdArgs/GetOpt.hs
--- a/System/Console/CmdArgs/GetOpt.hs
+++ b/System/Console/CmdArgs/GetOpt.hs
@@ -9,6 +9,7 @@
     ) where
 
 import System.Console.CmdArgs.Explicit
+import System.Console.GetOpt(OptDescr(..), ArgDescr(..))
 
 
 -- | What to do with options following non-options.
@@ -16,39 +17,15 @@
 --   /Changes:/ Only 'Permute' is allowed, both @RequireOrder@ and @ReturnInOrder@
 --   have been removed.
 data ArgOrder a = Permute
-
-
--- | Each 'OptDescr' describes a single option/flag.
---
---   The arguments to 'Option' are:
---
---   * list of short option characters
---
---   * list of long option strings (without "--", may not be 1 character long)
---
---   * argument descriptor
---
---   * explanation of option for userdata
-data OptDescr a = Option
-    [Char]
-    [String]
-    (ArgDescr a)
-    String
-
-
--- | Describes whether an option takes an argument or not, and if so
---   how the argument is injected into a value of type @a@.
-data ArgDescr a
-   = NoArg                   a         -- ^ no argument expected
-   | ReqArg (String       -> a) String -- ^ option requires argument
-   | OptArg (Maybe String -> a) String -- ^ optional argument
+instance Functor ArgOrder where
+    fmap _ Permute = Permute
 
 
 -- | Return a string describing the usage of a command, derived from
---   the header (first argument) and the options described by the 
+--   the header (first argument) and the options described by the
 --   second argument.
 usageInfo :: String -> [OptDescr a] -> String
-usageInfo desc flags = show $ convert desc flags
+usageInfo desc flags = unlines $ desc : drop 2 (lines $ show $ convert "" flags)
 
 
 -- | Process the command-line, and return the list of values that matched
@@ -58,7 +35,7 @@
 --
 --   * The option descriptions (see 'OptDescr')
 --
---   * The actual command line arguments (presumably got from 
+--   * The actual command line arguments (presumably got from
 --     'System.Environment.getArgs').
 --
 --   'getOpt' returns a triple consisting of the option arguments, a list
diff --git a/System/Console/CmdArgs/Helper.hs b/System/Console/CmdArgs/Helper.hs
new file mode 100644
--- /dev/null
+++ b/System/Console/CmdArgs/Helper.hs
@@ -0,0 +1,327 @@
+{-# LANGUAGE RecordWildCards, TypeSynonymInstances, FlexibleInstances #-}
+
+-- | Module for implementing CmdArgs helpers. A CmdArgs helper is an external program,
+--   that helps a user construct the command line arguments. To use a helper set the
+--   environment variable @$CMDARGS_HELPER@ (or @$CMDARGS_HELPER_/YOURPROGRAM/@) to
+--   one of:
+--
+-- * @echo /foo/@ will cause @/foo/@ to be used as the command arguments.
+--
+-- * @cmdargs-browser@ will cause a web browser to appear to help entering the arguments.
+--   For this command to work, you will need to install the @cmdargs-browser@ package:
+--   <http://hackage.haskell.org/package/cmdargs-browser>
+module System.Console.CmdArgs.Helper(
+    -- * Called by the main program
+    execute,
+    -- * Called by the helper program
+    Unknown, receive, reply, comment
+    ) where
+-- Should really be under Explicit, but want to export it top-level as Helper
+
+import System.Console.CmdArgs.Explicit.Type
+import System.Console.CmdArgs.Explicit.SplitJoin
+import System.Process
+import Control.Exception
+import Control.Monad
+import Data.Char
+import Data.IORef
+import Data.List
+import Data.Maybe
+import System.Exit
+import System.IO
+import System.IO.Unsafe
+
+
+hOut h x = do
+    hPutStrLn h x
+    hFlush h
+
+
+-- | Run a remote command line entry.
+execute
+    :: String -- ^ Name of the command to run, e.g. @echo argument@, @cmdargs-browser@
+    -> Mode a -- ^ Mode to run remotely
+    -> [String] -- ^ Initial set of command line flags (not supported by all helpers)
+    -> IO (Either String [String]) -- ^ Either an error message, or a list of flags to use
+execute cmd mode args
+    | "echo" == takeWhile (not . isSpace) cmd = return $ Right $ splitArgs $ drop 4 cmd
+    | otherwise = withBuffering stdout NoBuffering $ do
+        (Just hin, Just hout, _, _) <- createProcess (shell cmd){std_in=CreatePipe, std_out=CreatePipe}
+        -- none of the buffering seems necessary in practice, but better safe than sorry
+        hSetBuffering hin LineBuffering
+        hSetBuffering hout LineBuffering
+        (m, ans) <- saveMode mode
+        hOut hin m
+        loop ans hin hout
+    where
+        loop ans hin hout = do
+            x <- hGetLine hout
+            if "Result " `isPrefixOf` x then
+                return $ read $ drop 7 x
+             else if "Send " `isPrefixOf` x then do
+                hOut hin =<< ans (drop 5 x)
+                loop ans hin hout
+             else if "#" `isPrefixOf` x then do
+                hOut stdout x
+                loop ans hin hout
+             else
+                return $ Left $ "Unexpected message from program: " ++ show x
+
+
+withBuffering hndl mode act = bracket
+    (do old <- hGetBuffering hndl; hSetBuffering hndl mode; return old)
+    (hSetBuffering hndl)
+    (const act)
+
+
+-- | Unknown value, representing the values stored within the 'Mode' structure. While the values
+--   are not observable, they behave identically to the original values.
+newtype Unknown = Unknown {fromUnknown :: Value} -- wrap Value so the Pack instance doesn't leak
+
+
+-- | Receive information about the mode to display.
+receive :: IO (Mode Unknown)
+receive = do
+    m <- getLine
+    return $ remap2 Unknown fromUnknown $ loadMode m $ \msg -> unsafePerformIO $ do
+        hOut stdout $ "Send " ++ msg
+        getLine
+
+
+-- | Send a reply with either an error, or a list of flags to use. This function exits the helper program.
+reply :: Either String [String] -> IO ()
+reply x = do
+    hOut stdout $ "Result " ++ show x
+    exitWith ExitSuccess
+
+
+-- | Send a comment which will be displayed on the calling console, mainly useful for debugging.
+comment :: String -> IO ()
+comment x = hOut stdout $ "# " ++ x
+
+
+---------------------------------------------------------------------
+-- IO MAP
+
+data IOMap a = IOMap (IORef (Int,[(Int,a)]))
+
+newIOMap :: IO (IOMap a)
+newIOMap = fmap IOMap $ newIORef (0, [])
+
+addIOMap :: IOMap a -> a -> IO Int
+addIOMap (IOMap ref) x = atomicModifyIORef ref $ \(i,xs) -> let j = i+1 in ((j,(j,x):xs), j)
+
+getIOMap :: IOMap a -> Int -> IO a
+getIOMap (IOMap ref) i = do (_,xs) <- readIORef ref; return $ fromJust $ lookup i xs
+
+
+---------------------------------------------------------------------
+-- SERIALISE A MODE
+
+newtype Value = Value Int
+
+
+{-# NOINLINE toValue #-}
+toValue :: Mode a -> Mode Value
+-- fairly safe, use of a table and pointers from one process to another, but referentially transparent
+toValue x = unsafePerformIO $ do
+    -- the ref accumulates, so is a space leak
+    -- but it will all disappear after the helper goes, so not too much of an issue
+    mp <- newIOMap
+    let embed x = unsafePerformIO $ fmap Value $ addIOMap mp x
+        proj (Value x) = unsafePerformIO $ getIOMap mp x
+    return $ remap2 embed proj x
+
+
+saveMode :: Mode a -> IO (String, String -> IO String) -- (value, ask questions from stdin)
+saveMode m = do
+    mp <- newIOMap
+    res <- add mp $ pack $ toValue m
+    return $ (show res, fmap show . get mp . read)
+    where
+        add :: IOMap (Pack -> Pack) -> Pack -> IO Pack
+        add mp x = flip transformM x $ \x -> case x of
+            Func (NoShow f) -> do i <- addIOMap mp f; return $ FuncId i
+            x -> return x
+
+        get :: IOMap (Pack -> Pack) -> (Int,Pack) -> IO Pack
+        get mp (i,x) = do
+            f <- getIOMap mp i
+            add mp $ f x
+
+
+loadMode :: String -> (String -> String) -> Mode Value -- given serialised, question asker, give me a value
+loadMode x f = unpack $ rep $ read x
+    where
+        rep :: Pack -> Pack
+        rep x = flip transform x $ \x -> case x of
+            FuncId i -> Func $ NoShow $ \y -> rep $ read $ f $ show (i,y)
+            x -> x
+
+
+-- Support data types
+
+data Pack = Ctor String [(String, Pack)]
+          | List [Pack]
+          | Char Char
+          | Int Int
+          | Func (NoShow (Pack -> Pack))
+          | FuncId Int -- Never passed to pack/unpack, always transfromed away by saveMode/loadMode
+          | String String
+          | None -- ^ Never generated, only used for reading in bad cases
+            deriving (Show,Read)
+
+newtype NoShow a = NoShow a
+instance Show (NoShow a) where showsPrec = error "Cannot show value of type NoShow"
+instance Read (NoShow a) where readsPrec = error "Cannot read value of type NoShow"
+
+
+transformM, descendM :: Monad m => (Pack -> m Pack) -> Pack -> m Pack
+transformM f x = f =<< descendM (transformM f) x
+descendM f x = let (a,b) = uniplate x in liftM b $ mapM f a
+
+transform, descend :: (Pack -> Pack) -> Pack -> Pack
+transform f = f . descend (transform f)
+descend f x = let (a,b) = uniplate x in b $ map f a
+
+uniplate :: Pack -> ([Pack], [Pack] -> Pack)
+uniplate (List xs) = (xs, List)
+uniplate (Ctor x ys) = (map snd ys, Ctor x . zip (map fst ys))
+uniplate x = ([], const x)
+
+
+class Packer a where
+    pack :: a -> Pack
+    unpack :: Pack -> a
+
+add a b = (a, pack b)
+ctor x (Ctor y xs) | x == y = xs
+ctor _ _ = []
+get a b = unpack $ fromMaybe None $ lookup a b
+
+
+-- General instances
+
+instance Packer a => Packer [a] where
+    pack xs = if length ys == length zs && not (null ys) then String zs else List ys
+        where ys = map (pack) xs
+              zs = [x | Char x <- ys]
+
+    unpack (String xs) = unpack $ List $ map Char xs
+    unpack (List xs) = map (unpack) xs
+    unpack _ = []
+
+instance (Packer a, Packer b) => Packer (a -> b) where
+    pack f = Func $ NoShow $ pack . f . unpack
+    unpack (Func (NoShow f)) = unpack . f . pack
+
+instance Packer Value where
+    pack (Value x) = pack x
+    unpack x = Value $ unpack x
+
+instance Packer Char where
+    pack = Char
+    unpack (Char x) = x
+    unpack _ = ' '
+
+instance Packer Int where
+    pack = Int
+    unpack (Int x) = x
+    unpack _ = -1
+
+instance (Packer a, Packer b) => Packer (a,b) where
+    pack (a,b) = Ctor "(,)" [add "fst" a, add "snd" b]
+    unpack x = (get "fst" y, get "snd" y)
+        where y = ctor "(,)" x
+
+instance Packer a => Packer (Maybe a) where
+    pack Nothing = Ctor "Nothing" []
+    pack (Just x) = Ctor "Just" [add "fromJust" x]
+    unpack x@(Ctor "Just" _) = Just $ get "fromJust" $ ctor "Just" x
+    unpack _ = Nothing
+
+instance (Packer a, Packer b) => Packer (Either a b) where
+    pack (Left x) = Ctor "Left" [add "fromLeft" x]
+    pack (Right x) = Ctor "Right" [add "fromRight" x]
+    unpack x@(Ctor "Left" _) = Left $ get "fromLeft" $ ctor "Left" x
+    unpack x@(Ctor "Right" _) = Right $ get "fromRight" $ ctor "Right" x
+    unpack _ = Left $ unpack None
+
+instance Packer Bool where
+    pack True = Ctor "True" []
+    pack _ = Ctor "False" []
+    unpack (Ctor "True" _) = True
+    unpack _ = False
+
+
+-- CmdArgs specific
+
+instance Packer a => Packer (Group a) where
+    pack Group{..} = Ctor "Group"
+        [add "groupUnnamed" groupUnnamed
+        ,add "groupHidden" groupHidden
+        ,add "groupNamed" groupNamed]
+    unpack x = let y = ctor "Group" x in Group
+        {groupUnnamed = get "groupUnnamed" y
+        ,groupHidden = get "groupHidden" y
+        ,groupNamed = get "groupNamed" y}
+
+instance Packer a => Packer (Mode a) where
+    pack Mode{..} = Ctor "Mode"
+        [add "modeGroupModes" modeGroupModes
+        ,add "modeNames" modeNames
+        ,add "modeHelp" modeHelp
+        ,add "modeHelpSuffix" modeHelpSuffix
+        ,add "modeArgs" modeArgs
+        ,add "modeGroupFlags" modeGroupFlags
+        ,add "modeValue" modeValue
+        ,add "modeCheck" modeCheck
+        ,add "modeReform" modeReform
+        ,add "modeExpandAt" modeExpandAt]
+    unpack x = let y = ctor "Mode" x in Mode
+        {modeGroupModes = get "modeGroupModes" y
+        ,modeNames = get "modeNames" y
+        ,modeHelp = get "modeHelp" y
+        ,modeHelpSuffix = get "modeHelpSuffix" y
+        ,modeArgs = get "modeArgs" y
+        ,modeGroupFlags = get "modeGroupFlags" y
+        ,modeValue = get "modeValue" y
+        ,modeCheck = get "modeCheck" y
+        ,modeReform = get "modeReform" y
+        ,modeExpandAt = get "modeExpandAt" y}
+
+instance Packer a => Packer (Flag a) where
+    pack Flag{..} = Ctor "Flag"
+        [add "flagNames" flagNames
+        ,add "flagInfo" flagInfo
+        ,add "flagType" flagType
+        ,add "flagHelp" flagHelp
+        ,add "flagValue" flagValue]
+    unpack x = let y = ctor "Flag" x in Flag
+        {flagNames = get "flagNames" y
+        ,flagInfo = get "flagInfo" y
+        ,flagType = get "flagType" y
+        ,flagHelp = get "flagHelp" y
+        ,flagValue = get "flagValue" y}
+
+instance Packer a => Packer (Arg a) where
+    pack Arg{..} = Ctor "Arg"
+        [add "argType" argType
+        ,add "argRequire" argRequire
+        ,add "argValue" argValue]
+    unpack x = let y = ctor "Arg" x in Arg
+        {argType = get "argType" y
+        ,argRequire = get "argRequire" y
+        ,argValue = get "argValue" y}
+
+instance Packer FlagInfo where
+    pack FlagReq = Ctor "FlagReq" []
+    pack (FlagOpt x) = Ctor "FlagOpt" [add "fromFlagOpt" x]
+    pack (FlagOptRare x) = Ctor "FlagOptRare" [add "fromFlagOpt" x]
+    pack FlagNone = Ctor "FlagNone" []
+    unpack x@(Ctor name _) = case name of
+        "FlagReq" -> FlagReq
+        "FlagOpt" -> FlagOpt $ get "fromFlagOpt" $ ctor name x
+        "FlagOptRare" -> FlagOptRare $ get "fromFlagOpt" $ ctor name x
+        "FlagNone" -> FlagNone
+    unpack _ = FlagNone
diff --git a/System/Console/CmdArgs/Implicit.hs b/System/Console/CmdArgs/Implicit.hs
--- a/System/Console/CmdArgs/Implicit.hs
+++ b/System/Console/CmdArgs/Implicit.hs
@@ -14,13 +14,13 @@
     @main = print =<< 'cmdArgs' sample@
 
     Attributes are used to control a number of behaviours:
-    
+
     * The help message: 'help', 'typ', 'details', 'summary', 'program', 'groupname'
-    
+
     * Flag behaviour: 'opt', 'enum', 'verbosity', 'ignore'
-    
+
     * Flag name assignment: 'name', 'explicit'
-    
+
     * Controlling non-flag arguments: 'args', 'argPos'
 
     * multi-mode programs: 'modes', 'auto'
@@ -56,11 +56,16 @@
 
     All the examples are written using impure annotations. To convert to pure
     annotations follow the rules:
-    
+
     > Ctor {field1 = value1 &= ann1, field2 = value2} &= ann2 ==> record Ctor{} [field1 := value1 += ann1, field2 := value2] += ann2
     > Ctor (value1 &= ann1) value2 &= ann2 ==> record Ctor{} [atom value1 += ann1, atom value2] += ann2
-    > many [Ctor1{...}, Ctor2{...}] ==> many_ [record Ctor1{} [...], record Ctor2{} [...]]
-    > Ctor {field1 = enum [X &= ann, Y]} ==> record Ctor{} [field1 := enum_ [atom X += ann, atom Y]]
+    > modes [Ctor1{...}, Ctor2{...}] ==> modes_ [record Ctor1{} [...], record Ctor2{} [...]]
+    > Ctor {field1 = enum [X &= ann, Y]} ==> record Ctor{} [enum_ field1 [atom X += ann, atom Y]]
+
+    If you are willing to use TemplateHaskell, you can write in the impure syntax,
+    but have your code automatically translated to the pure style. For more details see
+    "System.Console.CmdArgs.Quote".
+
 -}
 module System.Console.CmdArgs.Implicit(
     -- * Running command lines
@@ -203,13 +208,21 @@
 -- | Flag: \"I want several different flags to set this one field to different values.\"
 --
 --   This annotation takes a type which is an enumeration, and provides multiple
---   separate flags to set the field to each value.
+--   separate flags to set the field to each value. The first element in the list
+--   is used as the value of the field.
 --
 -- > data State = On | Off deriving Data
 -- > data Mode = Mode {state :: State}
 -- > cmdArgs $ Mode {state = enum [On &= help "Turn on",Off &= help "Turn off"]}
 -- >   --on   Turn on
 -- >   --off  Turn off
+--
+--   This annotation can be used to allow multiple flags within a field:
+--
+-- > data Mode = Mode {state :: [State]}
+-- > cmdArgs $ Mode {state = enum [[] &= ignore, [On] &= help "Turn on", [Off] &= help "Turn off"]}
+--
+--   Now @--on --off@ would produce @Mode [On,Off]@.
 enum :: Data val => [val] -> val
 enum = many
 
diff --git a/System/Console/CmdArgs/Implicit/Ann.hs b/System/Console/CmdArgs/Implicit/Ann.hs
--- a/System/Console/CmdArgs/Implicit/Ann.hs
+++ b/System/Console/CmdArgs/Implicit/Ann.hs
@@ -26,4 +26,5 @@
     | ProgHelpArg [Ann]
     | ProgVersionArg [Ann]
     | ProgVerbosityArgs [Ann] [Ann]
+    | ProgNoAtExpand
       deriving (Eq,Ord,Show,Data,Typeable)
diff --git a/System/Console/CmdArgs/Implicit/Global.hs b/System/Console/CmdArgs/Implicit/Global.hs
--- a/System/Console/CmdArgs/Implicit/Global.hs
+++ b/System/Console/CmdArgs/Implicit/Global.hs
@@ -9,6 +9,7 @@
 import System.Console.CmdArgs.Text
 import System.Console.CmdArgs.Default
 
+import Control.Arrow
 import Control.Monad
 import Data.Char
 import Data.Function
@@ -18,44 +19,59 @@
 
 
 global :: Prog_ -> Mode (CmdArgs Any)
-global x = setReform (reform y) $ setHelp y $ collapse $ assignGroups y
+global x = setReform (reform y) $ setHelp y $ setProgOpts x $ collapse $ assignGroups y
     where y = assignNames $ extraFlags x
 
+
+setProgOpts :: Prog_ -> Mode a -> Mode a
+setProgOpts p m = m{modeExpandAt = not $ progNoAtExpand p
+                   ,modeGroupModes = fmap (setProgOpts p) $ modeGroupModes m}
+
+
 ---------------------------------------------------------------------
 -- COLLAPSE THE FLAGS/MODES UPWARDS
 
 collapse :: Prog_ -> Mode (CmdArgs Any)
-collapse x | length ms == 1 = (head ms){modeNames=[progProgram x]}
+collapse x | length ms == 1 = (snd $ head ms){modeNames=[progProgram x]}
            | length auto > 1 = err "prog" "Multiple automatic modes"
-           | otherwise = (head $ map zeroMode auto ++ map emptyMode ms)
-                {modeNames=[progProgram x], modeGroupModes = toGroup ms, modeHelp = progHelp x}
+           | otherwise = (head $ map zeroMode auto ++ map (emptyMode . snd) ms)
+                {modeNames=[progProgram x], modeGroupModes=grouped, modeHelp=progHelp x}
     where
-        ms = map collapseMode $ progModes x
-        auto = [m | (m,True) <- zip ms $ map modeDefault $ progModes x]
+        grouped = Group (pick Nothing) [] [(g, pick $ Just g) | g <- nub $ mapMaybe (modeGroup . fst) ms]
+        pick x = [m | (m_,m) <- ms, modeGroup m_ == x]
 
+        ms = map (id &&& collapseMode) $ progModes x
+        auto = [m | (m_,m) <- ms, modeDefault m_]
 
+
 -- | A mode devoid of all it's contents
 emptyMode :: Mode (CmdArgs Any) -> Mode (CmdArgs Any)
 emptyMode x = x
     {modeCheck = \x -> if cmdArgsHasValue x then Left "No mode given and no default mode" else Right x
     ,modeGroupFlags = groupUncommonDelete $ modeGroupFlags x
-    ,modeArgs=Nothing, modeHelpSuffix=[]}
+    ,modeArgs=([],Nothing), modeHelpSuffix=[]}
 
 -- | A mode whose help hides all it's contents
 zeroMode :: Mode (CmdArgs Any) -> Mode (CmdArgs Any)
 zeroMode x = x
     {modeGroupFlags = groupUncommonHide $ modeGroupFlags x
-    ,modeArgs=fmap (\x -> x{argType=""}) $ modeArgs x
+    ,modeArgs = let zeroArg x = x{argType=""} in map zeroArg *** fmap zeroArg $ modeArgs x
     ,modeHelpSuffix=[]}
 
 
 collapseMode :: Mode_ -> Mode (CmdArgs Any)
 collapseMode x =
+    applyFixups (map flagFixup $ modeFlags_ x) $
     collapseArgs [x | x@Arg_{} <- modeFlags_ x] $
     collapseFlags [x | x@Flag_{} <- modeFlags_ x] $
     modeMode x
 
 
+applyFixups :: [Fixup] -> Mode (CmdArgs Any) -> Mode (CmdArgs Any)
+applyFixups xs m = m{modeCheck = either Left (Right . fmap fix) . modeCheck m}
+    where fix a = foldr ($) a [x | Fixup x <- xs]
+
+
 collapseFlags :: [Flag_] -> Mode (CmdArgs Any) -> Mode (CmdArgs Any)
 collapseFlags xs x = x{modeGroupFlags = Group (pick Nothing) [] [(g, pick $ Just g) | g <- groups]}
     where
@@ -65,7 +81,7 @@
 
 collapseArgs :: [Flag_] -> Mode (CmdArgs Any) -> Mode (CmdArgs Any)
 collapseArgs [] x = x
-collapseArgs xs x = x{modeCheck=chk, modeArgs = Just $ flagArg upd hlp}
+collapseArgs xs x = x{modeCheck=chk, modeArgs = ([], Just $ flagArg upd hlp)}
     where
         argUpd = argValue . flagArg_
 
@@ -149,6 +165,8 @@
           wrap x = def{flagFlag=x, flagExplicit=True, flagGroup=grp}
           flags = changeBuiltin_ (progHelpArg p) (wrap $ flagHelpFormat $ error "flagHelpFormat undefined") ++
                   changeBuiltin_ (progVersionArg p) (wrap $ flagVersion vers) ++
+                  [wrap $ flagNumericVersion $ \x -> x{cmdArgsVersion = Just $ unlines v}
+                        | Just v <- [progNumericVersionOutput p]] ++
                   changeBuiltin_ (fst $ progVerbosityArgs p) (wrap loud) ++
                   changeBuiltin_ (snd $ progVerbosityArgs p) (wrap quiet)
           [loud,quiet] = flagsVerbosity verb
@@ -175,7 +193,7 @@
         mapModes1 f pre m = m{modeGroupModes = fmap (mapModes0 f (pre ++ head (modeNames m) ++ " ")) $ modeGroupModes m}
 
         add pre m = changeHelp p m $ \hlp txt x -> x{cmdArgsHelp=Just $ showText txt $ msg hlp}
-            where msg hlp = map Line (progHelpOutput p) ++ Line "" : helpText hlp (prepare m{modeNames = map (pre++) $ modeNames m})
+            where msg hlp = helpText (progHelpOutput p) hlp (prepare m{modeNames = map (pre++) $ modeNames m})
 
         prepare = mapModes1 (\_ m -> m{modeGroupFlags = groupCommonHide $ modeGroupFlags m}) ""
 
@@ -211,7 +229,7 @@
         isFlag_ _ = False
 
 
-asName s = map (\x -> if x == '_' then '-' else toLower x) $ if last s == '_' then init s else s 
+asName s = map (\x -> if x == '_' then '-' else toLower x) $ if last s == '_' then init s else s
 
 -- have are already assigned, want are a list of ones I might want
 data Names = Names {have :: [String], want :: [String]}
diff --git a/System/Console/CmdArgs/Implicit/Local.hs b/System/Console/CmdArgs/Implicit/Local.hs
--- a/System/Console/CmdArgs/Implicit/Local.hs
+++ b/System/Console/CmdArgs/Implicit/Local.hs
@@ -5,8 +5,8 @@
 --   constraints.
 module System.Console.CmdArgs.Implicit.Local(
     local, err,
-    Prog_(..), Builtin_(..), Mode_(..), Flag_(..), isFlag_,
-    progHelpOutput, progVersionOutput
+    Prog_(..), Builtin_(..), Mode_(..), Flag_(..), Fixup(..), isFlag_,
+    progHelpOutput, progVersionOutput, progNumericVersionOutput
     ) where
 
 import System.Console.CmdArgs.Implicit.Ann
@@ -15,11 +15,13 @@
 import System.Console.CmdArgs.Explicit
 import System.Console.CmdArgs.Annotate
 import System.Console.CmdArgs.Default
+import qualified Data.Generics.Any.Prelude as A
 
 import Control.Monad
 import Data.Char
 import Data.Generics.Any
 import Data.Maybe
+import Data.List
 
 
 data Prog_ = Prog_
@@ -30,17 +32,26 @@
     ,progVerbosityArgs :: (Maybe Builtin_, Maybe Builtin_) -- (verbose, quiet)
     ,progHelpArg :: Maybe Builtin_
     ,progVersionArg :: Maybe Builtin_
+    ,progNoAtExpand :: Bool
     } deriving Show
 instance Default Prog_ where
-    def = Prog_ def def def def def (Just def) (Just def)
+    def = Prog_ def def def def def (Just def) (Just def) def
 
 progOutput f x = fromMaybe ["The " ++ progProgram x ++ " program"] $
     (builtinSummary =<< f x) `mplus` progSummary x
 
 progHelpOutput = progOutput progHelpArg
 progVersionOutput = progOutput progVersionArg
+progNumericVersionOutput x = fmap return $ parseVersion =<< listToMaybe (progVersionOutput x)
 
+-- | Find numbers starting after space/comma, v
+parseVersion :: String -> Maybe String
+parseVersion xs = listToMaybe
+    [y | x <- words $ map (\x -> if x `elem` ",;" then ' ' else x) xs
+       , let y = fromMaybe x $ stripPrefix "v" x
+       , length (takeWhile isDigit y) >= 1]
 
+
 data Builtin_ = Builtin_
     {builtinNames :: [String]
     ,builtinExplicit :: Bool
@@ -68,16 +79,23 @@
         ,flagExplicit :: Bool
         ,flagGroup :: Maybe String
         ,flagEnum :: Maybe String -- if you are an enum, what is your string value
+        ,flagFixup :: Fixup
         }
     | Arg_
         {flagArg_ :: Arg (CmdArgs Any)
         ,flagArgPos :: Maybe Int
         ,flagArgOpt :: Maybe String
+        ,flagFixup :: Fixup
         }
       deriving Show
 instance Default Flag_ where
-    def = Flag_ "" (error "Flag_ undefined") def def def
+    def = Flag_ "" (error "Flag_ undefined") def def def def
 
+newtype Fixup = Fixup (Any -> Any)
+
+instance Default Fixup where def = Fixup id
+instance Show Fixup where show _ = "Fixup"
+
 isFlag_ Flag_{} = True
 isFlag_ _ = False
 
@@ -107,39 +125,46 @@
 mode_ :: Capture Ann -> [Mode_]
 mode_ (Ann Ignore _) = []
 mode_ (Ann a b) = map (modeAnn a) $ mode_ b
-mode_ o@(Ctor x ys) = [withMode def{modeFlags_=concat $ zipWith flag_ (fields x) ys} $ \x -> x{modeValue=embed $ fromCapture o}]
+mode_ o@(Ctor x ys) = [withMode def{modeFlags_=flgs} $ \x -> x{modeValue=embed $ fixup $ fromCapture o}]
+    where flgs = concat $ zipWith flag_ (fields x) ys
+          fixup x = foldl (\x (Fixup f) -> f x) x $ map flagFixup flgs
 mode_ x = err "mode" $ show x
 
 
 flag_ :: String -> Capture Ann -> [Flag_]
 flag_ name (Ann Ignore _) = []
 flag_ name (Ann a b) = map (flagAnn a) $ flag_ name b
-flag_ name (Value x) = [def{flagField=name, flagFlag = remap embed reembed $ value_ name x}]
+flag_ name (Value x) = let (fix,flg) = value_ name x in [def{flagField=name, flagFlag=remap embed reembed flg, flagFixup=fix}]
 flag_ name x@Ctor{} = flag_ name $ Value $ fromCapture x
-flag_ name (Many xs) = map (enum_ name) xs
+flag_ name (Many xs) = concatMap (enum_ name) xs
 flag_ name x = errFlag name $ show x
 
 
-enum_ :: String -> Capture Ann -> Flag_
-enum_ name (Ann a b) = flagAnn a $ enum_ name b
-enum_ name (Value x) = def{flagField=name, flagFlag = flagNone [] (fmap $ setField (name,x)) "", flagEnum=Just $ ctor x}
+enum_ :: String -> Capture Ann -> [Flag_]
+enum_ name (Ann Ignore _) = []
+enum_ name (Ann a b) = map (flagAnn a) $ enum_ name b
+enum_ name (Value x) = [def{flagField=name, flagFlag = flagNone [] (fmap upd) "", flagEnum=Just $ ctor x}]
+    where upd v | not (A.isString x) && A.isList x = setField (name, getField name v `A.append` x) v
+                | otherwise = setField (name,x) v
 enum_ name x@Ctor{} = enum_ name $ Value $ fromCapture x
 enum_ name x = errFlag name $ show x
 
 
-value_ :: String -> Any -> Flag Any
+-- Fixup (ends up in modeCheck) and the flag itself
+value_ :: String -> Any -> (Fixup, Flag Any)
 value_ name x
     | isNothing mty = errFlag name $ show x
     | readerBool ty =
         let f (Right x) = x
             upd b x = setField (name, f $ readerRead ty (getField name x) $ show b) x
-        in flagBool [] upd ""
+        in (fixup, flagBool [] upd "")
     | otherwise =
         let upd s x = fmap (\c -> setField (name,c) x) $ readerRead ty (getField name x) s
-        in flagReq [] upd (readerHelp ty) ""
+        in (fixup, flagReq [] upd (readerHelp ty) "")
     where
         mty = reader x
         ty = fromJust mty
+        fixup = Fixup $ \x -> setField (name,readerFixup ty $ getField name x) x
 
 
 ---------------------------------------------------------------------
@@ -153,6 +178,7 @@
 progAnn (ProgHelpArg a) x = x{progHelpArg = builtinAnns (progHelpArg x) a}
 progAnn (ProgVersionArg a) x = x{progVersionArg = builtinAnns (progVersionArg x) a}
 progAnn (ProgVerbosityArgs a b) x = x{progVerbosityArgs=(builtinAnns (Just $ fromMaybe def $ fst $ progVerbosityArgs x) a, builtinAnns (Just $ fromMaybe def $ snd $ progVerbosityArgs x) b)}
+progAnn ProgNoAtExpand x = x{progNoAtExpand=True}
 progAnn a x | length (progModes x) == 1 = x{progModes = map (modeAnn a) $ progModes x}
 progAnn a x = err "program" $ show a
 
@@ -194,9 +220,9 @@
 flagAnn a x = errFlag (head $ words $ show x) $ show a
 
 toArg :: Flag_ -> Maybe Int -> Flag_
-toArg (Flag_ fld x False Nothing Nothing) pos
+toArg (Flag_ fld x False Nothing Nothing fix) pos
     | null (flagNames x), null (flagHelp x), Just y <- opt $ flagInfo x
-    = Arg_ (Arg (flagValue x) (flagType x)) pos y
+    = Arg_ (Arg (flagValue x) (flagType x) (isNothing y)) pos y fix
     where
         opt FlagReq = Just Nothing
         opt (FlagOpt x) = Just (Just x)
diff --git a/System/Console/CmdArgs/Implicit/Reader.hs b/System/Console/CmdArgs/Implicit/Reader.hs
--- a/System/Console/CmdArgs/Implicit/Reader.hs
+++ b/System/Console/CmdArgs/Implicit/Reader.hs
@@ -6,43 +6,57 @@
 import qualified Data.Generics.Any.Prelude as A
 import System.Console.CmdArgs.Explicit
 import Data.Char
+import Data.Int
+import Data.Word
 import Data.List
+import Data.Maybe
 
 
 data Reader = Reader
     {readerHelp :: String
     ,readerBool :: Bool
     ,readerParts :: Int
+    ,readerFixup :: Any -> Any -- If a list, then 'reverse', otherwise nothing, so we can build up using cons in O(n)
     ,readerRead :: Any -> String -> Either String Any
     }
 
--- reader tries to use the first argument to readerRead, but reader_ doesn't
+-- reader has an actual value of type Any that can be inspected
+-- reader_ has a value of type _|_ instead
 readerRead_ r = readerRead r $ error "Invariant broken: reader/reader_"
 
 
 reader :: Any -> Maybe Reader
 reader x | A.isList x && not (A.isString x) = do
     r <- reader_ $ A.fromList x
-    return r{readerRead = \o s -> fmap (A.append o . A.list_ x) $ readerRead_ r s}
+    return r{readerRead = \o s -> fmap (`A.cons` o) $ readerRead_ r s, readerFixup = A.reverse}
+reader x | isAlgType x, [ctor] <- ctors x, [child] <- children x = do
+    -- newtype wrapper, just forward it
+    r <- reader child
+    let down = head . children
+    let up o c = recompose o [c]
+    return r{readerFixup = \x -> up x $ readerFixup r $ down x
+            ,readerRead = \x -> either Left (Right . up x) . readerRead r (down x)
+            }
 reader x = reader_ x
 
 
 reader_ :: Any -> Maybe Reader
-reader_ x | A.isString x = Just $ Reader "ITEM" False 1 $ const $ Right . Any
+reader_ x | A.isString x = Just $ Reader "ITEM" False 1 id $ const $ Right . Any
 
 
-reader_ x | typeName x == "Bool" = Just $ Reader "BOOL" True 1 $ const $ \s ->
+reader_ x | typeName x == "Bool" = Just $ Reader "BOOL" True 1 id $ const $ \s ->
     maybe (Left $ "Could not read as boolean, " ++ show s) (Right . Any) $ parseBool s
 
 
-reader_ x
-    | ty == "Int" = f "INT" (0::Int)
-    | ty == "Integer" = f "INT" (0::Integer)
-    | ty == "Float" = f "NUM" (0::Float)
-    | ty == "Double" = f "NUM" (0::Double)
+reader_ x | res:_ <- catMaybes
+    [f "INT" (0::Integer), f "NUM" (0::Float), f "NUM" (0::Double)
+    ,f "INT" (0::Int), f "INT" (0::Int8), f "INT" (0::Int16), f "INT" (0::Int32), f "INT" (0::Int64)
+    ,f "NAT" (0::Word), f "NAT" (0::Word8), f "NAT" (0::Word16), f "NAT" (0::Word32), f "NAT" (0::Word64)
+    ] = Just res
     where
-        ty = typeName x
-        f hlp t = Just $ Reader hlp False 1 $ const $ \s -> case reads s of
+        ty = typeOf x
+        f hlp t | typeOf (Any t) /= ty = Nothing
+                | otherwise = Just $ Reader hlp False 1 id $ const $ \s -> case reads s of
             [(x,"")] -> Right $ Any $ x `asTypeOf` t
             _ -> Left $ "Could not read as type " ++ show (typeOf $ Any t) ++ ", " ++ show s
 
@@ -58,22 +72,22 @@
 
 
 reader_ x | isAlgType x && length xs > 1 && all ((==) 0 . arity . snd) xs
-    = Just $ Reader (map toUpper $ typeShell x) (typeName x == "Bool") 1 $ const $ rd . map toLower
+    = Just $ Reader (map toUpper $ typeShell x) (typeName x == "Bool") 1 id $ const $ rd . map toLower
     where
         xs = [(map toLower c, compose0 x c) | c <- ctors x]
 
-        rd s | null ys = Left $ "Could not read, expected one of: " ++ unwords (map fst xs)
+        rd s | null ys = Left $ "Could not read " ++ show s ++ ", expected one of: " ++ unwords (map fst xs)
              | Just (_,x) <- find ((==) s . fst) ys = Right x
-             | length ys > 1 = Left $ "Ambiguous read, could be any of: " ++ unwords (map fst ys)
+             | length ys > 1 = Left $ "Ambiguous read for " ++ show s ++ ", could be any of: " ++ unwords (map fst ys)
              | otherwise = Right $ snd $ head ys
             where ys = filter (isPrefixOf s . fst) xs
 
 
-reader_ x | [c] <- ctors x, x <- compose0 x c = do
+reader_ x | isAlgType x, [c] <- ctors x, x <- compose0 x c = do
     let cs = children x
     rs <- mapM reader_ cs
     let n = sum $ map readerParts rs
-    return $ Reader (uncommas $ map readerHelp rs) (map readerBool rs == [True]) n $ const $ \s ->
+    return $ Reader (uncommas $ map readerHelp rs) (map readerBool rs == [True]) n id $ const $ \s ->
         let ss = commas s in
         if n == 1 then fmap (recompose x . return) $ readerRead_ (head $ filter ((==) 1 . readerParts) rs) s
         else if length ss /= n then Left "Incorrect number of commas for fields"
diff --git a/System/Console/CmdArgs/Implicit/Reform.hs b/System/Console/CmdArgs/Implicit/Reform.hs
--- a/System/Console/CmdArgs/Implicit/Reform.hs
+++ b/System/Console/CmdArgs/Implicit/Reform.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE PatternGuards, RecordWildCards #-}
+{-# LANGUAGE RecordWildCards #-}
 
 module System.Console.CmdArgs.Implicit.Reform(reform) where
 
diff --git a/System/Console/CmdArgs/Implicit/UI.hs b/System/Console/CmdArgs/Implicit/UI.hs
--- a/System/Console/CmdArgs/Implicit/UI.hs
+++ b/System/Console/CmdArgs/Implicit/UI.hs
@@ -90,6 +90,8 @@
 
 -- | Flag: \"Put non-flag arguments here.\"
 --
+--   All argument flags not captured by 'argPos' are returned by 'args'.
+--
 -- > {hello = def &= args}
 args :: Ann
 args = FlagArgs
@@ -127,7 +129,8 @@
 -- | Modes: \"My program name\/version\/copyright is ...\"
 --
 --   One line summary of the entire program, the first line of
---   @--help@ and the only line of @--version@.
+--   @--help@ and the only line of @--version@. If the string contains a
+--   version number component will also provide @--numeric-version@.
 --
 -- > Sample{..} &= summary "CmdArgs v0.0, (C) Neil Mitchell 1981"
 summary :: String -> Ann
@@ -208,3 +211,13 @@
 -- > Sample{..} &= verbosityArgs [ignore] [name "silent", explicit]
 verbosityArgs :: [Ann] -> [Ann] -> Ann
 verbosityArgs = ProgVerbosityArgs
+
+
+-- | Program: \"Turn off \@ expansion.\"
+--
+--   Usually arguments starting with \@ are treated as a file containing
+--   a set of arguments. This annotation turns off that behaviour.
+--
+-- > Sample{..} &= noAtExpand
+noAtExpand :: Ann
+noAtExpand = ProgNoAtExpand
diff --git a/System/Console/CmdArgs/Quote.hs b/System/Console/CmdArgs/Quote.hs
new file mode 100644
--- /dev/null
+++ b/System/Console/CmdArgs/Quote.hs
@@ -0,0 +1,213 @@
+{-# LANGUAGE TemplateHaskell, PatternGuards, MagicHash #-}
+
+-- | This module provides a quotation feature to let you write command line
+--   arguments in the impure style, but have them translated into the pure style,
+--   as per "System.Console.CmdArgs.Implicit". An example:
+--
+-- > {-# LANGUAGE TemplateHaskell, DeriveDataTypeable, MagicHash #-}
+-- > import System.Console.CmdArgs.Implicit
+-- > import System.Console.CmdArgs.Quote
+-- >
+-- > data Sample = Sample {hello :: String} deriving (Show, Data, Typeable)
+-- >
+-- > $(cmdArgsQuote [d|
+-- >     sample = Sample{hello = def &=# help "World argument" &=# opt "world"}
+-- >                    &=# summary "Sample v1"
+-- >
+-- >     run = cmdArgs# sample :: IO Sample
+-- >     |])
+-- >
+-- > main = print =<< run
+--
+--   Inside 'cmdArgsQuote' you supply the command line parser using attributes in the
+--   impure style. If you run with @-ddump-splices@ (to see the Template Haskell output),
+--   you would see:
+--
+-- > run = cmdArgs_
+-- >     (record Sample{} [hello := def += help "World argument" += opt "world"]
+-- >         += summary "Sample v1")
+-- >     :: IO Sample
+--
+--   /Stubs/
+--
+--   To define the original parser you may use either the standard impure annotations ('(&=)', 'modes'), or
+--   the stub annotations versions defined in this module ('(&=#)', 'modes'). The stub versions do not include
+--   a "Data" constraint, so can be used in situations where the Data instance is not yet available - typically
+--   when defining the parser in the same module as the data type on GHC 7.2 and above. The stub versions should
+--   never be used outside 'cmdArgsQuote' and will always raise an error.
+--
+--   /Explicit types/
+--
+--   There will be a limited number of situations where an impure parser will require additional types, typically
+--   on the result of 'cmdArgs' if the result is used without a fixed type - for example if you 'show' it. Most users
+--   will not need to add any types. In some cases you may need to remove some explicit types, where the intermediate
+--   type of the annotations has changed - but again, this change should be rare.
+--
+--   /Completeness/
+--
+--   The translation is not complete, although works for all practical instances I've tried. The translation works
+--   by first expanding out the expression (inlining every function defined within the quote, inlining let bindings),
+--   then performs the translation. This scheme leads to two consequences: 1) Any expensive computation executed inside
+--   the quotation to produce the command line flags may be duplicated (a very unlikely scenario). 2) As I do not yet
+--   have expansion rules for all possible expressions, the expansion (and subsequently the translation) may fail.
+--   I am interested in any bug reports where the feature does not work as intended.
+module System.Console.CmdArgs.Quote(
+    -- * Template Haskell quotation function
+    cmdArgsQuote,
+    -- * Stub versions of the impure annotations
+    (&=#), modes#, cmdArgsMode#, cmdArgs#, enum#
+    ) where
+
+import Language.Haskell.TH
+import Control.Arrow
+import Control.Monad
+import Data.Data
+import Data.Maybe
+import System.Console.CmdArgs.Implicit
+
+stub name = error $
+    "System.Console.CmdArgs.Quote." ++ name ++
+    ": this function is provided only for use inside cmdArgsQuote, and should never be called"
+
+-- | Version of '&=' without a 'Data' context, only to be used within 'cmdArgsQuote'.
+(&=#) :: a -> Ann -> a
+(&=#) = stub "(&=#)"
+
+-- | Version of 'modes' without a 'Data' context, only to be used within 'cmdArgsQuote'.
+modes# :: [a] -> a
+modes# = stub "modes#"
+
+-- | Version of 'cmdArgsMode' without a 'Data' context, only to be used within 'cmdArgsQuote'.
+cmdArgsMode# :: a -> Mode (CmdArgs a)
+cmdArgsMode# = stub "cmdArgsMode#"
+
+-- | Version of 'cmdArgs' without a 'Data' context, only to be used within 'cmdArgsQuote'.
+cmdArgs# :: a -> IO a
+cmdArgs# = stub "cmdArgs#"
+
+-- | Version of 'enum' without a 'Data' context, only to be used within 'cmdArgsQuote'.
+enum# :: [a] -> a
+enum# = stub "enum#"
+
+
+-- | Quotation function to turn an impure version of "System.Console.CmdArgs.Implicit" into a pure one.
+--   For details see "System.Console.CmdArgs.Quote".
+cmdArgsQuote :: Q [Dec] -> Q [Dec]
+cmdArgsQuote x = do
+    x <- x
+    translate $ rename $ simplify $ inline x
+
+
+-- | Apply the rewrite rules
+translate :: [Dec] -> Q [Dec]
+translate = descendBiM f
+    where
+        dull = ['Just, 'Left, 'Right, '(:)] -- Prelude constructors of non-zero arity
+
+        f (RecConE x xs) = return $
+            let args = [anns (InfixE (Just $ VarE lbl) (ConE '(:=)) (Just val)) as | (lbl,x) <- xs, let (val, as) = asAnns x]
+            in VarE 'record `AppE` RecConE x [] `AppE` ListE args
+
+        f x | (ConE x, xs@(_:_)) <- asApps x, x `notElem` dull = do
+            names <- forM [1..length xs] $ \i -> newName $ "_" ++ nameBase x ++ show i
+            let (vals, ass) = unzip $ map asAnns xs
+                bind = [ValD (VarP name) (NormalB val) [] | (name,val) <- zip names vals]
+                args = [anns (VarE 'atom `AppE` VarE name) as | (name,as) <- zip names ass]
+            return $ LetE bind $ VarE 'record `AppE` (ConE x `apps` map VarE names) `AppE` ListE args
+
+        f x = descendM f x
+
+        apps x [] = x
+        apps x (y:ys) = apps (x `AppE` y) ys
+
+        asApps (AppE x y) = let (a,b) = asApps x in (a,b++[y])
+        asApps x = (x,[])
+
+        anns x [] = x
+        anns x (a:as) = anns (InfixE (Just x) (VarE '(+=)) (Just a)) as
+
+        asAnns (InfixE (Just x) (VarE op) (Just y)) | op == '(+=) = let (a,b) = asAnns x in (a,b++[y])
+        asAnns (AppE (AppE (VarE op) x) y) | op == '(+=) = let (a,b) = asAnns x in (a,b++[y])
+        asAnns x = (x, [])
+
+
+-- | Move from the old names to the new names, sufficient for where that is the full translation
+rename :: [Dec] -> [Dec]
+rename = transformBi f
+    where
+        rep = let f a b c = [(a,c),(b,c)] in concat
+            [f '(&=) '(&=#) '(+=)
+            ,f 'modes 'modes# 'modes_
+            ,f 'enum 'enum# 'enum_
+            ,f 'cmdArgsMode 'cmdArgsMode# 'cmdArgsMode_
+            ,f 'cmdArgs 'cmdArgs# 'cmdArgs_]
+
+        f (VarE x) | Just x <- lookup x rep = VarE x
+        f x = x
+
+
+-- | Simplify the syntax tree - things like application of a lambda
+simplify :: [Dec] -> [Dec]
+simplify = transformBi f
+    where
+        f (AppE (LamE [VarP v] bod) x) = f $ subst v x bod
+        f x = x
+
+        subst v x bod = transform f bod
+            where f (VarE v2) | v == v2 = x
+                  f x = x
+
+
+-- | Evaluate through all locally defined functions and let expressions, at most once per defn
+inline :: [Dec] -> [Dec]
+inline xs = map (dec $ addEnv xs []) xs
+    where
+        newEnv = concatMap $ \x -> case x of
+            FunD x [Clause ps (NormalB e) ds] -> [(x, LamE ps $ let_ ds e)]
+            ValD (VarP x) (NormalB e) ds -> [(x, let_ ds e)]
+            _ -> []
+
+        addEnv xs env = without [] (newEnv xs) ++ env
+            where
+                -- create an environment where everything in ns is missing, recursively drop one thing each time
+                without ns new = [(n, exp (new2 ++ env) e) | (n,e) <- new, n `notElem` ns, let new2 = without (n:ns) new]
+
+
+        dec env (FunD n cs) = FunD n $ map (clause env) cs
+        dec env (ValD p x ds) = ValD p (body (addEnv ds env) x) ds
+
+        clause env (Clause ps x ds) = Clause ps (body (addEnv ds env) x) ds
+
+        body env (GuardedB xs) = GuardedB $ map (second $ exp env) xs
+        body env (NormalB x) = NormalB $ exp env x
+
+        -- FIXME: propagating the env ignores variables shadowed by LamE/CaseE
+        exp env (LetE ds x) = LetE ds $ exp (addEnv ds env) x
+        exp env (VarE x) | Just x <- lookup x env = x
+        exp env x = descend (exp env) x
+
+        let_ ds e = if null ds then e else LetE ds e
+
+
+---------------------------------------------------------------------
+-- MINI UNIPLATE - Avoid the dependency just for one small module
+
+descendBi :: (Data a, Data b) => (b -> b) -> a -> a
+descendBi f x | Just f <- cast f = f x
+              | otherwise = gmapT (descendBi f) x
+
+descend :: Data a => (a -> a) -> a -> a
+descend f = gmapT (descendBi f)
+
+transform :: Data a => (a -> a) -> a -> a
+transform f = f . descend (transform f)
+
+transformBi :: (Data a, Data b) => (b -> b) -> a -> a
+transformBi f = descendBi (transform f)
+
+descendBiM :: (Data a, Data b, Monad m) => (b -> m b) -> a -> m a
+descendBiM f x | Just x <- cast x = liftM (fromJust . cast) $ f x -- guaranteed safe
+               | otherwise = gmapM (descendBiM f) x
+
+descendM :: (Data a, Monad m) => (a -> m a) -> a -> m a
+descendM f = gmapM (descendBiM f)
diff --git a/System/Console/CmdArgs/Test/All.hs b/System/Console/CmdArgs/Test/All.hs
--- a/System/Console/CmdArgs/Test/All.hs
+++ b/System/Console/CmdArgs/Test/All.hs
@@ -6,12 +6,14 @@
 import qualified System.Console.CmdArgs.Test.Explicit as Explicit
 import qualified System.Console.CmdArgs.Test.Implicit as Implicit
 import qualified System.Console.CmdArgs.Test.GetOpt as GetOpt
+import qualified System.Console.CmdArgs.Test.SplitJoin as SplitJoin
 
 test :: IO ()
 test = do
     Explicit.test
     GetOpt.test
     Implicit.test
+    SplitJoin.test
     putStrLn "\nTest completed"
 
 demo :: [Mode Demo]
diff --git a/System/Console/CmdArgs/Test/Explicit.hs b/System/Console/CmdArgs/Test/Explicit.hs
--- a/System/Console/CmdArgs/Test/Explicit.hs
+++ b/System/Console/CmdArgs/Test/Explicit.hs
@@ -8,7 +8,7 @@
 
 demo = [newDemo act dem]
 
-act xs | ("help","") `elem` xs = print $ helpText def dem
+act xs | ("help","") `elem` xs = print $ helpText [] def dem
        | otherwise = print xs
 
 dem :: Mode [(String,String)]
@@ -33,6 +33,10 @@
     checkGood m ["fred","bob"] ["fred","bob"]
     checkGood m ["--","--test"] ["--test"]
     checkGood m [] []
+    checkComp m [] (0,0) []
+    checkComp m ["--"] (0,2) []
+    checkComp m ["bob"] (0,3) []
+    checkComp m ["-"] (0,1) [CompleteValue "-"]
 
 testFlags = do
     let m = name "Flags" $ mode "" [] "" (flagArg (upd "") "")
@@ -58,10 +62,18 @@
     checkGood m ["-tm"] ["test","more"]
     checkGood m ["--col=red"] ["colorred"]
     checkGood m ["--col","red","-t"] ["colorred","test"]
+    checkComp m ["--tes"] (0,5) [CompleteValue "--test"]
+    checkComp m ["--color","--tes"] (1,5) []
+    checkComp m ["--more","--tes"] (1,5) [CompleteValue "--test"]
+    checkComp m ["--moo","--tes"] (1,5) [CompleteValue "--test"]
+    checkComp m ["--col"] (0,5) [CompleteValue "--color"]
+    checkComp m ["--bob"] (0,5) [CompleteValue "--bobby",CompleteValue "--bob"]
+    checkComp m ["-"] (0,1) $ map CompleteValue $ words "--test --more --color --bob -x -"
+    checkComp m ["--"] (0,2) $ map CompleteValue $ words "--test --more --color --bob --xxx"
 
 testModes = do
     let m = name "Modes" $ modes "" [] ""
-                [(mode "test" ["test"] "" undefined [flagNone ["bob"] ("bob":) ""]){modeArgs=Nothing}
+                [(mode "test" ["test"] "" undefined [flagNone ["bob"] ("bob":) ""]){modeArgs=([],Nothing)}
                 ,mode "dist" ["dist"] "" (flagArg (upd "") "") [flagNone ["bob"] ("bob":) "", flagReq ["bill"] (upd "bill") "" ""]]
     checkGood m [] []
     checkFail m ["--bob"]
@@ -91,3 +103,9 @@
     Left err -> failure "Failed when should have succeeded" [("Name",n),("Args",show xs),("Error",err)]
     Right a | reverse a /= ys -> failure "Wrong parse" [("Name",n),("Args",show xs),("Wanted",show ys),("Got",show $ reverse a)]
     _ -> success
+
+checkComp :: (String,Mode [String]) -> [String] -> (Int,Int) -> [Complete] -> IO ()
+checkComp (n,m) xs ab want
+    | want == got = success
+    | otherwise = failure "Bad completions" [("Name",n),("Args",show xs),("Index",show ab),("Wanted",show want),("Got",show got)]
+    where got = complete m xs ab
diff --git a/System/Console/CmdArgs/Test/Implicit/Diffy.hs b/System/Console/CmdArgs/Test/Implicit/Diffy.hs
--- a/System/Console/CmdArgs/Test/Implicit/Diffy.hs
+++ b/System/Console/CmdArgs/Test/Implicit/Diffy.hs
@@ -1,6 +1,8 @@
-{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
+{-# LANGUAGE DeriveDataTypeable, RecordWildCards, TemplateHaskell, MagicHash #-}
+{-# OPTIONS_GHC -fno-warn-missing-fields #-}
 module System.Console.CmdArgs.Test.Implicit.Diffy where
 import System.Console.CmdArgs
+import System.Console.CmdArgs.Quote
 import System.Console.CmdArgs.Test.Implicit.Util
 
 data Diffy = Create {src :: Maybe FilePath, out :: FilePath}
@@ -22,14 +24,37 @@
 
 mode = cmdArgsMode $ modes [create,diff] &= help "Create and compare differences" &= program "diffy" &= summary "Diffy v1.0"
 
+
+$(cmdArgsQuote
+    [d|
+        outFlags_ x = x &=# help "Output file" &=# typFile
+
+        create_ = Create
+            {src = Nothing &=# help "Source directory" &=# typDir
+            ,out = outFlags_ "ls.txt"
+            } &=# help "Create a fingerprint"
+
+        diff_ = Diff
+            {old = "" &=# typ "OLDFILE" &=# argPos 0
+            ,new = "" &=# typ "NEWFILE" &=# argPos 1
+            ,out = outFlags_ "diff.txt"
+            } &=# help "Perform a diff"
+
+        mode_ = cmdArgsMode# $ modes# [create_,diff_] &=# help "Create and compare differences" &=# program "diffy" &=# summary "Diffy v1.0"
+    |])
+
+
+-- STOP MANUAL
+
 test = do
-    let Tester{..} = tester "Diffy" mode
+    let Tester{..} = testers "Diffy" [mode,mode_]
     fails []
-    isHelp ["--help"] []
+    isHelp ["--help"] ["diffy [COMMAND] ... [OPTIONS]"] -- FIXME: Should know that root is not valid, thus no brackets on [COMMAND]
     isHelp ["create","--help"] []
     isHelp ["diff","--help"] []
     isHelpNot ["--help"] ["diffy"]
     isVersion ["--version"] "Diffy v1.0"
+    isVersion ["--numeric-version"] "1.0"
     ["create"] === create
     fails ["create","file1"]
     fails ["create","--quiet"]
@@ -42,4 +67,6 @@
     ["diff","foo1","foo2"] === diff{old="foo1",new="foo2"}
     fails ["diff","foo1"]
     fails ["diff","foo1","foo2","foo3"]
-
+    completion [] (0,0) [CompleteValue "create",CompleteValue "diff",CompleteValue "--out",CompleteValue "--help",CompleteValue "--version",CompleteValue "--numeric-version"]
+    completion ["d"] (0,1) [CompleteValue "diff"]
+    completion ["dd"] (0,2) []
diff --git a/System/Console/CmdArgs/Test/Implicit/HLint.hs b/System/Console/CmdArgs/Test/Implicit/HLint.hs
--- a/System/Console/CmdArgs/Test/Implicit/HLint.hs
+++ b/System/Console/CmdArgs/Test/Implicit/HLint.hs
@@ -39,7 +39,7 @@
     ,datadir = def &= typDir &= help "Override the data directory"
     ,cpp_define = def &= typ "NAME[=VALUE]" &= help "CPP #define"
     ,cpp_include = def &= typDir &= help "CPP include path"
-    ,files = def &= args &= typ "FILES/DIRS"
+    ,files = def &= args &= typ "FILE/DIR"
     } &=
     verbosity &=
     help "Suggest improvements to Haskell source code" &=
@@ -49,6 +49,8 @@
 
 mode = cmdArgsMode hlint
 
+-- STOP MANUAL
+
 test = do
     let Tester{..} = tester "HLint" mode
     [] === hlint
@@ -59,6 +61,9 @@
     isVerbosity [] Normal
     isHelp ["-?"] ["HLint v0.0.0, (C) Neil Mitchell"]
     isHelp ["--help"] ["  hlint src --report"]
+    isVersion ["--version"] "HLint v0.0.0, (C) Neil Mitchell"
+    isVersion ["-V"] "HLint v0.0.0, (C) Neil Mitchell"
+    isVersion ["--numeric-version"] "0.0.0"
     ["--colo"] === hlint{color=True}
     ["--colour","--colour=false"] === hlint
     ["--colour=true"] === hlint{color=True}
@@ -70,5 +75,4 @@
     ["--cpp-define=val","x"] === hlint{cpp_define=["val"],files=["x"]}
     fails ["--cpp-define"]
     ["--cpp-define","val","x","y"] === hlint{cpp_define=["val"],files=["x","y"]}
-
-
+    completion ["T"] (0,1) [CompleteFile "" "T", CompleteDir "" "T"]
diff --git a/System/Console/CmdArgs/Test/Implicit/Maker.hs b/System/Console/CmdArgs/Test/Implicit/Maker.hs
--- a/System/Console/CmdArgs/Test/Implicit/Maker.hs
+++ b/System/Console/CmdArgs/Test/Implicit/Maker.hs
@@ -33,6 +33,8 @@
 
 mode = cmdArgsMode $ modes [build,wipe,test_] &= help "Build helper program" &= program "maker" &= summary "Maker v1.0\nMake it"
 
+-- STOP MANUAL
+
 mode_ = cmdArgsMode_ $ modes_ [build,wipe,test_] += help "Build helper program" += program "maker" += summary "Maker v1.0\nMake it"
   where
     threads_ = threads := def += help "Number of threads to use" += name "j" += typ "NUM"
@@ -45,7 +47,7 @@
         ] += help "Run the test suite"
 
     build = record Build{}
-        [threads_ 
+        [threads_
         ,enum_ method
             [atom Release += help "Release build"
             ,atom Debug += help "Debug build"
diff --git a/System/Console/CmdArgs/Test/Implicit/Tests.hs b/System/Console/CmdArgs/Test/Implicit/Tests.hs
--- a/System/Console/CmdArgs/Test/Implicit/Tests.hs
+++ b/System/Console/CmdArgs/Test/Implicit/Tests.hs
@@ -1,33 +1,32 @@
-{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}
-{-# OPTIONS_GHC -fno-warn-missing-fields -fno-warn-unused-binds #-}
+{-# LANGUAGE DeriveDataTypeable, RecordWildCards, TemplateHaskell, MagicHash #-}
+{-# OPTIONS_GHC -fno-warn-missing-fields -fno-warn-unused-binds -fno-cse #-}
 
-module System.Console.CmdArgs.Test.Implicit.Tests where
+module System.Console.CmdArgs.Test.Implicit.Tests(test, demos) where
 
 import System.Console.CmdArgs
 import System.Console.CmdArgs.Explicit(modeHelp)
 import System.Console.CmdArgs.Test.Implicit.Util
-
-
-test = test1 >> test2 >> test3 >> test4 >> test5 >> test6 >> test7 >> test8 >> test9 >> test10 >>
-       test11 >> test12 >> test13 >> test14 >> test15 >> test16
-demos = zipWith f [1..]
-        [toDemo mode1, toDemo mode2, toDemo mode3, toDemo mode4, toDemo mode5, toDemo mode6
-        ,toDemo mode7, toDemo mode8, toDemo mode9, toDemo mode10, toDemo mode11, toDemo mode12
-        ,toDemo mode13, toDemo mode14, toDemo mode15, toDemo mode16]
-    where f i x = x{modeHelp = "Testing various corner cases (" ++ show i ++ ")"}
+import System.Console.CmdArgs.Quote
+import Data.Int
+import Data.Ratio
 
 
 -- from bug #256 and #231
 data Test1
     = Test1 {maybeInt :: Maybe Int, listDouble :: [Double], maybeStr :: Maybe String, float :: Float
-            ,bool :: Bool, maybeBool :: Maybe Bool, listBool :: [Bool]}
+            ,bool :: Bool, maybeBool :: Maybe Bool, listBool :: [Bool], int64 :: Int64}
       deriving (Show,Eq,Data,Typeable)
 
-def1 = Test1 def def def (def &= args) def def def
+def1 = Test1 def def def (def &= args) def def def def
 mode1 = cmdArgsMode def1
 
+$(cmdArgsQuote [d|
+    mode1_ = cmdArgsMode# def1_
+    def1_ = Test1 def def def (def &=# args) def def def def
+    |])
+
 test1 = do
-    let Tester{..} = tester "Test1" mode1
+    let Tester{..} = testers "Test1" [mode1,mode1_]
     [] === def1
     ["--maybeint=12"] === def1{maybeInt = Just 12}
     ["--maybeint=12","--maybeint=14"] === def1{maybeInt = Just 14}
@@ -43,8 +42,9 @@
     ["--maybebool"] === def1{maybeBool=Just True}
     ["--maybebool=off"] === def1{maybeBool=Just False}
     ["--listbool","--listbool=true","--listbool=false"] === def1{listBool=[True,True,False]}
+    ["--int64=12"] === def1{int64=12}
     fails ["--listbool=fred"]
-    invalid $ \_ -> def1{listBool = def &= opt "yes"}
+    invalid $ const def1{listBool = def &= opt "yes"}
 
 
 -- from bug #230
@@ -67,8 +67,11 @@
 
 mode3 = cmdArgsMode $ Test3 (def &= argPos 1) (def &= argPos 2 &= opt "foo") (def &= args)
 
+$(cmdArgsQuote [d| mode3_ = cmdArgsMode# $ Test3 (def &=# argPos 1) (def &=# argPos 2 &=# opt "foo") (def &=# args) |])
+
+
 test3 = do
-    let Tester{..} = tester "Test3" mode3
+    let Tester{..} = testers "Test3" [mode3,mode3_]
     fails []
     fails ["a"]
     ["a","1"] === Test3 [1] ["foo"] ["a"]
@@ -246,6 +249,7 @@
     ["test13c","--foo13=13"] === Test13C 13
     isHelp ["--help"] ["Help text here"]
     isVersion ["--version"] "Version text here"
+    fails ["--numeric-version"]
 
 -- check a list becomes modes not an enum
 data Test14 = Test14A | Test14B | Test14C deriving (Eq,Show,Data,Typeable)
@@ -262,11 +266,17 @@
 data Test15 = Test15 {test15a :: Bool} deriving (Eq,Show,Data,Typeable)
 
 mode15 = cmdArgsMode $ Test15 (False &= name "help")
-         &= helpArg [groupname "GROUP", name "h", name "nohelp", explicit, help "whatever"] &= versionArg [ignore]
+         &= helpArg [groupname "GROUP", name "h", name "nohelp", explicit, help "whatever\nstuff"] &= versionArg [ignore]
          &= verbosityArgs [ignore] [explicit,name "silent"]
 
+$(cmdArgsQuote [d|
+    mode15_ = cmdArgsMode# $ Test15 (False &=# name "help")
+              &=# helpArg [groupname "GROUP", name "h", name "nohelp", explicit, help "whatever\nstuff"] &=# versionArg [ignore]
+              &=# verbosityArgs [ignore] [explicit,name "silent"]
+    |])
+
 test15 = do
-    let Tester{..} = tester "Test15" mode15
+    let Tester{..} = testers "Test15" [mode15,mode15_]
     invalid $ \_ -> Test15 (False &= name "help")
     ["--help"] === Test15 True
     ["-t"] === Test15 True
@@ -275,6 +285,7 @@
     isHelp ["-h"] []
     isHelp ["-h"] ["GROUP:"]
     fails ["--version"]
+    fails ["--numeric-version"]
     fails ["--verbose"]
     fails ["--quiet"]
     isVerbosity ["--help","--silent"] Quiet
@@ -284,11 +295,125 @@
 
 data Test16 = Test16 {test16a :: MyInt, test16b :: [MyInt]} deriving (Eq,Show,Data,Typeable)
 
-mode16 = cmdArgsMode $ Test16 (MyInt 12) []
+mode16 = cmdArgsMode $ Test16 (MyInt 12) [] &= summary "The Glorious Glasgow Haskell Compilation System, version 7.6.3"
 
 test16 = do
     let Tester{..} = tester "Test16" mode16
     [] === Test16 (MyInt 12) []
+    isVersion ["--numeric-version"] "7.6.3"
     fails ["--test16a"]
     ["--test16a=5"] === Test16 (MyInt 5) []
     ["--test16b=5","--test16b=82"] === Test16 (MyInt 12) [MyInt 5, MyInt 82]
+
+-- #552, @ directives not expanded after -- symbols
+-- not actually checked because this path doesn't go through processArgs
+data Test17 = Test17 {test17_ :: [String]} deriving (Eq,Show,Data,Typeable)
+
+mode17 = cmdArgsMode $ Test17 ([] &= args) &= noAtExpand &= summary "bzip2 3.5-windows version"
+
+test17 = do
+    let Tester{..} = tester "Test17" mode17
+    [] === Test17 []
+    ["test","of","this"] === Test17 ["test","of","this"]
+    ["test","--","@foo"] === Test17 ["test","@foo"]
+    isVersion ["--numeric-version"] "3.5-windows"
+
+
+data Debuggable = This | That deriving (Eq,Show,Data,Typeable)
+data Test18 = Test18 {test18_ :: [Debuggable]} deriving (Eq,Show,Data,Typeable)
+
+mode18 = cmdArgsMode $ Test18 $ enum [[] &= ignore, [This] &= name "debug-this", [That] &= name "debug-that"]
+
+test18 = do
+    let Tester{..} = tester "Test18" mode18
+    [] === Test18 []
+    ["--debug-this","--debug-that","--debug-this"] === Test18 [This,That,This]
+
+-- #610, check performance for long lists (took ~20s before)
+
+data Test19 = Test19 {test19_ :: [String]} deriving (Eq,Show,Data,Typeable)
+
+mode19 = cmdArgsMode $ Test19 ([] &= args)
+
+test19 = do
+    let Tester{..} = tester "Test19" mode19
+    let args = map show [1..1000]
+    args === Test19 args
+
+
+-- #615, newtype wrappers of lists/Maybe should accumulate properly
+
+newtype Test20A = Test20A [String] deriving (Eq,Show,Data,Typeable)
+data Test20 = Test20 {test20_ :: Test20A} deriving (Eq,Show,Data,Typeable)
+
+mode20 = cmdArgsMode $ Test20 (Test20A [] &= args)
+
+test20 = do
+    let Tester{..} = tester "Test20" mode20
+    ["a","b","c"] === Test20 (Test20A ["a","b","c"])
+
+
+-- #626, don't reverse values too much
+
+newtype Test21A = Test21A [String] deriving (Eq,Show,Data,Typeable)
+data Test21 = Test21 {test21A :: Test21A, test21B :: [String], test21C :: [Int]} deriving (Eq,Show,Data,Typeable)
+
+mode21 = cmdArgsMode $ Test21 (Test21A ["a","b","c"]) ["A","B","C"] [1,2,3]
+
+test21 = do
+    let Tester{..} = tester "Test21" mode21
+    [] === Test21 (Test21A ["a","b","c"]) ["A","B","C"] [1,2,3]
+
+-- #10, don't break elm-server
+
+data Test22 = Test22 {port :: Int, runtime :: Maybe FilePath} deriving (Data,Typeable,Show,Eq)
+
+mode22 = cmdArgsMode $ Test22
+  { port = 8000 &= help "set the port of the server"
+  , runtime = Nothing &= typFile
+              &= help "Specify a custom location for Elm's runtime system."
+  } &= help "Quickly reload Elm projects in your browser. Just refresh to recompile.\n\
+            \It serves static files and freshly recompiled Elm files."
+    &= helpArg [explicit, name "help", name "h"]
+    &= versionArg [ explicit, name "version", name "v"
+                  , summary "0.12.0.1"
+                  ]
+    &= summary "Elm Server 0.11.0.1, (c) Evan Czaplicki 2011-2014"
+
+test22 = do
+    let Tester{..} = tester "Test22" mode22
+    [] === Test22 8000 Nothing
+    isVersion ["-v"] "0.12.0.1"
+    isVersion ["--version"] "0.12.0.1"
+    isVersion ["--numeric-version"] "0.12.0.1"
+    isHelp ["--help"] ["Elm Server 0.11.0.1, (c) Evan Czaplicki 2011-2014"]
+    isHelp ["--h"] ["Elm Server 0.11.0.1, (c) Evan Czaplicki 2011-2014"]
+    fails ["-?"]
+    ["--port=20"] === Test22 20 Nothing
+    ["--runtime=20"] === Test22 8000 (Just "20")
+    fails ["bob"]
+
+-- # 24, doesn't work with Ratio
+
+data Test23 = Test23 {test23A :: Ratio Int} deriving (Show, Data, Typeable, Eq)
+
+mode23 = cmdArgsMode $ Test23 {test23A = 4 % 7 }
+
+test23 = do
+    let Tester{..} = tester "Test23" mode23
+    [] === Test23 (4 % 7)
+    ["--test23=1,6"] === Test23 (1 % 6)
+
+
+-- For some reason, these must be at the end, otherwise the Template Haskell
+-- stage restriction kicks in.
+
+test = test1 >> test2 >> test3 >> test4 >> test5 >> test6 >> test7 >> test8 >> test9 >> test10 >>
+       test11 >> test12 >> test13 >> test14 >> test15 >> test16 >> test18 >> test19 >> test20 >>
+       test21 >> test22 >> test23
+demos = zipWith f [1..]
+        [toDemo mode1, toDemo mode2, toDemo mode3, toDemo mode4, toDemo mode5, toDemo mode6
+        ,toDemo mode7, toDemo mode8, toDemo mode9, toDemo mode10, toDemo mode11, toDemo mode12
+        ,toDemo mode13, toDemo mode14, toDemo mode15, toDemo mode16, toDemo mode17, toDemo mode18
+        ,toDemo mode19, toDemo mode20, toDemo mode21, toDemo mode22, toDemo mode23]
+    where f i x = x{modeHelp = "Testing various corner cases (" ++ show i ++ ")"}
diff --git a/System/Console/CmdArgs/Test/Implicit/Util.hs b/System/Console/CmdArgs/Test/Implicit/Util.hs
--- a/System/Console/CmdArgs/Test/Implicit/Util.hs
+++ b/System/Console/CmdArgs/Test/Implicit/Util.hs
@@ -1,6 +1,9 @@
 {-# LANGUAGE PatternGuards #-}
 
-module System.Console.CmdArgs.Test.Implicit.Util where
+module System.Console.CmdArgs.Test.Implicit.Util(
+    module System.Console.CmdArgs.Test.Implicit.Util,
+    Complete(..)
+    ) where
 
 import System.Console.CmdArgs.Implicit
 import System.Console.CmdArgs.Explicit
@@ -29,19 +32,21 @@
     ,isHelpNot :: [String] -> [String] -> IO ()
     ,isVersion :: [String] -> String -> IO ()
     ,isVerbosity :: [String] -> Verbosity -> IO ()
+    ,completion :: [String] -> (Int,Int) -> [Complete] -> IO ()
     }
 
 testers :: (Show a, Eq a) => String -> [Mode (CmdArgs a)] -> Tester a
 testers name = foldr1 f . map (tester name)
     where
-        f (Tester x1 x2 x3 x4 x5 x6) (Tester y1 y2 y3 y4 y5 y6) =
-            Tester (f2 x1 y1) (f1 x2 y2) (f2 x3 y3) (f2 x4 y4) (f2 x5 y5) (f2 x6 y6)
+        f (Tester x1 x2 x3 x4 x5 x6 x7) (Tester y1 y2 y3 y4 y5 y6 y7) =
+            Tester (f2 x1 y1) (f1 x2 y2) (f2 x3 y3) (f2 x4 y4) (f2 x5 y5) (f2 x6 y6) (f3 x7 y7)
         f1 x y a = x a >> y a
         f2 x y a b = x a b >> y a b
+        f3 x y a b c = x a b c >> y a b c
 
 
 tester :: (Show a, Eq a) => String -> Mode (CmdArgs a) -> Tester a
-tester name m = Tester (===) fails isHelp isHelpNot isVersion isVerbosity
+tester name m = Tester (===) fails isHelp isHelpNot isVersion isVerbosity completion
     where
         failed msg args xs = failure msg $ ("Name","Implicit "++name):("Args",show args):xs
 
@@ -68,23 +73,33 @@
             Left x -> success
             Right x -> failed "Succeeded 52 should have failed" args [("Result",show x)]
 
+        showGot sel x = [("Got",show got) | Right x <- [x], Just got <- [sel x]]
+
         isHelp args want = f args $ \x -> case x of
             Right x | Just got <- cmdArgsHelp x, match want (lines got) -> success
-            _ -> failed "Failed on isHelp" args []
+            _ -> failed "Failed on isHelp" args $
+                ("Want",show want) : showGot cmdArgsHelp x
 
         isHelpNot args want = f args $ \x -> case x of
             Right x | Just got <- cmdArgsHelp x, not $ match want (lines got) -> success
             _ -> failed "Failed on isHelpNot" args []
 
         isVersion args want = f args $ \x -> case x of
-            Right x | Just got <- cmdArgsVersion x, match [want] (lines got) -> success
-            _ -> failed "Failed on isVersion" args [("Want",want)]
+            Right x | Just got <- cmdArgsVersion x, (want ++ "\n") == got -> success
+            _ -> failed "Failed on isVersion" args $
+                ("Want",show $ want ++ "\n") : showGot cmdArgsVersion x
 
         isVerbosity args v = f args $ \x -> case x of
             Right x | fromMaybe Normal (cmdArgsVerbosity x) == v -> success
             _ -> failed "Failed on isVerbosity" args []
 
+        completion args pos res
+            | res == ans = success
+            | otherwise = failed "Failed on completion" args [("Position",show pos),("Want",shw res),("Got",shw ans)]
+            where ans = complete m args pos
+                  shw = intercalate ", " . lines . show
 
+
 match :: [String] -> [String] -> Bool
 match want got = any f $ tails got
     where f xs = length xs >= length want && and (zipWith matchLine want xs)
@@ -95,4 +110,3 @@
 matchLine (x:xs) (y:ys) | x == y = matchLine xs ys
 matchLine [] [] = True
 matchLine _ _ = False
-
diff --git a/System/Console/CmdArgs/Test/SplitJoin.hs b/System/Console/CmdArgs/Test/SplitJoin.hs
new file mode 100644
--- /dev/null
+++ b/System/Console/CmdArgs/Test/SplitJoin.hs
@@ -0,0 +1,149 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module System.Console.CmdArgs.Test.SplitJoin(test) where
+
+import System.Console.CmdArgs.Explicit
+import System.Console.CmdArgs.Test.Util
+import Control.Monad
+
+
+
+test = do
+    forM_ tests $ \(src,parsed) -> do
+        let a = splitArgs src
+            b1 = joinArgs parsed
+            b2 = joinArgs $ splitArgs b1
+        if a == parsed then return () else failure "splitArgs" [("Given   ",src),("Expected",show parsed),("Found   ",show a)]
+        if b1 == b2 then return () else failure "joinArgs" [("Given   ",show parsed),("Expected",b1),("Found   ",b2)]
+    success
+
+{-
+newtype CmdLine = CmdLine String deriving Show
+
+instance Arbitrary CmdLine where
+    arbitrary = fmap CmdLine $ listOf $ elements "abcd \\/\'\""
+
+generateTests :: IO ()
+generateTests = withTempFile $ \src -> do
+    writeFile src "import System.Environment\nmain = print =<< getArgs\n"
+    quickCheckWith stdArgs{chatty=False} $ \(CmdLine x) -> unsafePerformIO $ do
+        putStr $ ",(,) " ++ (show x) ++ " "
+        system $ "runghc \"" ++ src ++ "\" " ++ x
+        return True
+
+
+withTempFile :: (FilePath -> IO a) -> IO a
+withTempFile f = bracket
+    (do (file,h) <- openTempFile "." "cmdargs.hs"; hClose h; return file)
+    removeFile
+    f
+-}
+
+-- Pregenerate the QuickCheck tests and run them through the system console
+-- Not done each time for three reasons
+-- * Avoids an extra dependency on QuickCheck + process
+-- * Slow to run through the command line
+-- * Can't figure out how to read the output, without adding more escaping (which breaks the test)
+tests =
+    [f "" []
+    ,f "c" ["c"]
+    ,f "b" ["b"]
+    ,f "\\" ["\\"]
+    ,f "'//" ["'//"]
+    ,f "a" ["a"]
+    ,f "cda" ["cda"]
+    ,f "b'" ["b'"]
+    ,f "" []
+    ,f " " []
+    ,f "/b" ["/b"]
+    ,f "\"b/\"d a'b'b" ["b/d","a'b'b"]
+    ,f "d'c a\"/\\" ["d'c","a/\\"]
+    ,f "d" ["d"]
+    ,f "bb' " ["bb'"]
+    ,f "b'\\" ["b'\\"]
+    ,f "\"\\ac" ["\\ac"]
+    ,f "\\'\"abbb\"c/''' \\ c" ["\\'abbbc/'''","\\","c"]
+    ,f "/bbdbb a " ["/bbdbb","a"]
+    ,f "b\" d" ["b d"]
+    ,f "" []
+    ,f "\\cc/''\\b\\ccc\\'\\b\\" ["\\cc/''\\b\\ccc\\'\\b\\"]
+    ,f "/" ["/"]
+    ,f "///\"b\\c/b\"cd//c'\"" ["///b\\c/bcd//c'"]
+    ,f "\\\"d\\\\' /d\\\\/bb'a /\\d" ["\"d\\\\'","/d\\\\/bb'a","/\\d"]
+    ,f "c/ \\''/c b\\'" ["c/","\\''/c","b\\'"]
+    ,f "dd'b\\\\\\' /c'aaa\"" ["dd'b\\\\\\'","/c'aaa"]
+    ,f "b'd''\\/ b\\'b'db/'cd " ["b'd''\\/","b\\'b'db/'cd"]
+    ,f "a\"ba\\/\\ " ["aba\\/\\ "]
+    ,f "b\"'dd'c /b/c\"bbd \"\"\\ad'\"c\\\"" ["b'dd'c /b/cbbd","\\ad'c\""]
+    ,f "da 'c\\\\acd/'dbaaa///dccbc a \\" ["da","'c\\\\acd/'dbaaa///dccbc","a","\\"]
+    ,f "a'ac \"da\"" ["a'ac","da"]
+    ,f "\"'\\\"/\"\"b\\b  \"'\"\"ccd'a\"/c /da " ["'\"/\"b\\b","'\"ccd'a/c /da "]
+    ,f "d\"\\c\\\\cb c/\"aa' b\"\\/d \"'c c/" ["d\\c\\\\cb c/aa'","b\\/d 'c","c/"]
+    ,f "dbc\\/\"\"//c/\"accda" ["dbc\\///c/accda"]
+    ,f "aca a'' \\ c b'\\/d\\" ["aca","a''","\\","c","b'\\/d\\"]
+    ,f "dc\"bc/a\\ccdd\\\\aad\\c'ab '\\cddcdba" ["dcbc/a\\ccdd\\\\aad\\c'ab '\\cddcdba"]
+    ,f " c'\"ba \"b\\dc\"" ["c'ba b\\dc"]
+    ,f "a\\acd/a \"'c /'c'" ["a\\acd/a","'c /'c'"]
+    ,f " ac ddc/\"\"a/\\bd\\d c'cac\"c\\a/a''c" ["ac","ddc/a/\\bd\\d","c'cacc\\a/a''c"]
+    ,f "b/cd\"//bb\"/daaab/  b b \"'     d\"a\" 'd b" ["b/cd//bb/daaab/","b","b","'     da 'd b"]
+    ,f "a\"cc'cd\"\\'ad '\"dcc acb\"\\\\" ["acc'cd\\'ad","'dcc acb\\\\"]
+    ,f "/bc/bc'/\"d  \"a/\"\\ad aba\\da" ["/bc/bc'/d  a/\\ad aba\\da"]
+    ,f "b\\a" ["b\\a"]
+    ,f "/dc ''c'a\"'/'\\ /'cd\\'d/'db/b\"' cabacaaa\"\"dd" ["/dc","''c'a'/'\\ /'cd\\'d/'db/b'","cabacaaadd"]
+    ,f "\"ac\\\"c'/c'b\"b\"b'd\"c\"\"" ["ac\"c'/c'bbb'dc"]
+    ,f "/ 'ccc\"d\\dc'\"'\\  b" ["/","'cccd\\dc''\\","b"]
+    ,f "  '\"/\\cc\\/c '\\\\" ["'/\\cc\\/c '\\\\"]
+    ,f "\\ \\' ' /d  \"cc\\\\//da\"d'a/a\"ca\\\\\"\\cb c\"d'b 'acb" ["\\","\\'","'","/d","cc\\\\//dad'a/aca\\\\cb","cd'b 'acb"]
+    ,f "a\"\"d'\"a\"\\ \\c db'da/d\\c\"a/ aa c/db" ["ad'a\\","\\c","db'da/d\\ca/ aa c/db"]
+    ,f " d\\" ["d\\"]
+    ,f "d c b'/\\/'\"/'a'aa\"a\"/ad\\/" ["d","c","b'/\\/'/'a'aaa/ad\\/"]
+    ,f "  a \\' /" ["a","\\'","/"]
+    ,f "'/ c" ["'/","c"]
+    ,f "acd 'bcab /ba'daa'/ba/\"dcdadbcacb" ["acd","'bcab","/ba'daa'/ba/dcdadbcacb"]
+    ,f "a\\\"dd'a c\"a\"\"ac\\" ["a\"dd'a","ca\"ac\\"]
+    ,f "\"dba /'bb\\ d ba '/c' \"dd\\' cbcd c /b/\\b///" ["dba /'bb\\ d ba '/c' dd\\'","cbcd","c","/b/\\b///"]
+    ,f "a'c/c \"ccb '/d\\abd/bc  " ["a'c/c","ccb '/d\\abd/bc  "]
+    ,f "\\da\"\\//add\\\\ c" ["\\da\\//add\\\\ c"]
+    ,f "c/\\\"//  a/\"ac\"//''ba\"c/\\bc\\\"d\"bc/d" ["c/\"//","a/ac//''bac/\\bc\"dbc/d"]
+    ,f "/d/ a   dc'\\ \"" ["/d/","a","dc'\\",""]
+    ,f " \"dc//b\\cd/ \\ac\"b\"b\"d\"\"\"dd\"\" ' a\\'/ \"/'/\\a/abd\\ddd" ["dc//b\\cd/ \\acbbd\"dd","'","a\\'/","/'/\\a/abd\\ddd"]
+    ,f "\\'  ' d\"b bbc" ["\\'","'","db bbc"]
+    ,f "'ba\\a'db/bd d\\'b\\ \\/a'da' " ["'ba\\a'db/bd","d\\'b\\","\\/a'da'"]
+    ,f "\\b\\cc\"\"d' dd ddcb\"d" ["\\b\\ccd'","dd","ddcbd"]
+    ,f "d\"dc'\\d\"/'\\\"b\\c'c\" db' \\'b/\"a' / da'\"/ab'\\ c\\bc\\//dbcb\\" ["ddc'\\d/'\"b\\c'c db' \\'b/a'","/","da'/ab'\\ c\\bc\\//dbcb\\"]
+    ,f " b ddbbbbc\"da\\c\"'\\" ["b","ddbbbbcda\\c'\\"]
+    ,f "b/\"d dacd'/'\\\"''a a /'\\c'b ab\\  dda\\c'abdd'a\"//d \\\\\\ d\"\"" ["b/d dacd'/'\"''a a /'\\c'b ab\\  dda\\c'abdd'a//d","\\\\\\","d"]
+    ,f "/c\"\" dd'a'/b\\/'\"'/" ["/c","dd'a'/b\\/''/"]
+    ,f "/\"'\"\"'cc a a\\dd''\\'b" ["/'\"'cc","a","a\\dd''\\'b"]
+    ,f "c\"dcd''aba\" \" /'" ["cdcd''aba"," /'"]
+    ,f "'\"/''\\\\d'/ad\\baadabdca\\ /\\'''bd\\/\"'/' aca \\  \\a'\\ cd\"d /bdcd''cac" ["'/''\\\\d'/ad\\baadabdca\\ /\\'''bd\\/'/'","aca","\\","\\a'\\","cdd /bdcd''cac"]
+    ,f "\" /\"da" [" /da"]
+    ,f "'\"ca/'d/d/d\\ca\"/\"\" ddac cc\" ''a c''bd\"bc'dc\\/\"b\"a\\\"\"a/\\ " ["'ca/'d/d/d\\ca/","ddac","cc ''a c''bdbc'dc\\/ba\"a/\\ "]
+    ,f "\\\\d'ad ' ''\"cd/a \"\"\\'\\\"'dc\\" ["\\\\d'ad","'","''cd/a \"\\'\"'dc\\"]
+    ,f " ab  c'\\a" ["ab","c'\\a"]
+    ,f "b" ["b"]
+    ,f "''c dc c\\'d'ab'd\"\\\"cca\"b'da\"dbcdbd\"cd'/d \\cd'\"d  \"\"b cdc''/\\\"b'" ["''c","dc","c\\'d'ab'd\"ccab'dadbcdbdcd'/d","\\cd'd  \"b","cdc''/\"b'"]
+    ,f " \"'cb dbddbdd/" ["'cb dbddbdd/"]
+    ,f "a/\"d// dd/cc/\"cc\"d\" d\\/a a \\c\"  \\\\/\"\\ bcc'ac'\"\\c//d\"da/\\aac\\b\"c/'b\"\"bbd/\\" ["a/d// dd/cc/ccd","d\\/a","a","\\c  \\\\/\\","bcc'ac'\\c//dda/\\aac\\bc/'b\"bbd/\\"]
+    ,f "b\"ddccd\"a\"/ba\"" ["bddccda/ba"]
+    ,f " \"  c/b/'/bdd  cb d'c a'\"'a d\\\\db//\\\"' c'/'c\\/aa" ["  c/b/'/bdd  cb d'c a''a","d\\\\db//\"'","c'/'c\\/aa"]
+    ,f "\\caab" ["\\caab"]
+    ,f "bb\"'\"/d'bad 'd\\/'\\b//\\\\ \\d''c\"c b\\b/\\" ["bb'/d'bad","'d\\/'\\b//\\\\","\\d''cc b\\b/\\"]
+    ,f " c'a\"  \\cab\"bd\"dcd\"/cb/\"\"b\"b'\"d" ["c'a  \\cabbddcd/cb/bb'd"]
+    ,f "\\/ \"c'ca" ["\\/","c'ca"]
+    ,f "  d' /c'bc\"'/'\\\\dca'cc\"'\"''/d cb//'a \"bd ab\"dcaadc\\\"'d\\\"/a\"a\\\"ba//b/ d/dbac/d\\caa\"bc/ " ["d'","/c'bc'/'\\\\dca'cc'''/d cb//'a bd","abdcaadc\"'d\"/aa\"ba//b/","d/dbac/d\\caabc/ "]
+    ,f "/\"\\db'd/ ca\"ad b\\\\\"cd/a bbc\\ " ["/\\db'd/ caad","b\\cd/a bbc\\ "]
+    ,f "cdc bd'/\"c''c d \\\"aa \\d\\ bb'b/ /b/a/c'acda\\'\"\"c \"bbbaa/'/a \\aca\"'/ac' " ["cdc","bd'/c''c d \"aa \\d\\ bb'b/ /b/a/c'acda\\'\"c","bbbaa/'/a \\aca'/ac'"]
+    ,f "ad/'b\\d /cc\"\"ab \\ \"'  ''b\\\"/\\  a\"'d\"\\ddacdbbabb b b  //' acd\"c\\d'd\\b\"'\\\"aaba/bda/c'// \\b" ["ad/'b\\d","/ccab","\\","'  ''b\"/\\  a'd\\ddacdbbabb b b  //' acdc\\d'd\\b'\"aaba/bda/c'// \\b"]
+    ,f "bac cc \"ac\"/ca/ '\"\" b/b d /cd'\\'bb\" \\ \"b '/ b c ' c''\"a/ad\\ " ["bac","cc","ac/ca/","'","b/b","d","/cd'\\'bb \\ b","'/","b","c","'","c''a/ad\\ "]
+    ,f "baa'  b'b''\\dab/'c" ["baa'","b'b''\\dab/'c"]
+    ,f "cb\\\\ " ["cb\\\\"]
+    ,f "/b'a''d\"b\"   'c'b ba\\'b\" bb" ["/b'a''db","'c'b","ba\\'b bb"]
+    ,f "b /\"ca\\cbac " ["b","/ca\\cbac "]
+    ,f " \"\"/\"bcaa\"\"a' \\/bb \"a\\\"'\"" ["/bcaa\"a'","\\/bb","a\"'"]
+    ,f "\"c /''c\"\\badc/\\daa/\\ c\"a c\\ \\/cab \"b\"\\ ba\"\"/d/cd'a ad'c/ad\"' a\\d/d\\c\\'cdccd/\"a'/\"b///ac\"" ["c /''c\\badc/\\daa/\\","ca c\\ \\/cab b\\ ba\"/d/cd'a","ad'c/ad' a\\d/d\\c\\'cdccd/a'/b///ac"]
+    ,f "/cbbd\"/b' /dd\"/c\\ca/'\"\\ cc  \\d\"aca/\"b caa\\d\\'\"b'b  dc\"cd\\'c\" 'd/ac\"cacc\"" ["/cbbd/b' /dd/c\\ca/'\\ cc  \\daca/b caa\\d\\'b'b","dccd\\'c","'d/accacc"]
+    ,f "bc/bd\\ca\\bcacca\"\"\\c/\\ /\"\"a/\"c'//b'\\d/a/'ab/cbd/cacb//b \\d\"aac\\d'\"/" ["bc/bd\\ca\\bcacca\\c/\\","/a/c'//b'\\d/a/'ab/cbd/cacb//b \\daac\\d'/"]
+    ,f "bbac bdc/d\\\"/db\"dbdb\"a \" /\"/'a\\acacbcc c'//\\//b\"ca\"bcca c\\/aaa/c/bccbccaa  \"\" cdccc/bddcbc c''" ["bbac","bdc/d\"/dbdbdba"," //'a\\acacbcc","c'//\\//bcabcca","c\\/aaa/c/bccbccaa","","cdccc/bddcbc","c''"]
+    ]
+    where f = (,)
diff --git a/System/Console/CmdArgs/Text.hs b/System/Console/CmdArgs/Text.hs
--- a/System/Console/CmdArgs/Text.hs
+++ b/System/Console/CmdArgs/Text.hs
@@ -107,7 +107,7 @@
 
 -- | Split the text into strips of no-more than the given width
 wrap :: Int -> String -> [String]
-wrap width = combine . split
+wrap width = concatMap (combine . split) . lines
     where
         split :: String -> [(String,Int)] -- string, amount of space after
         split "" = []
@@ -131,18 +131,24 @@
     map f xs ++
     ["</table>"]
     where
-        cols = maximum [length x | Cols x <- xs]
+        maxCols = maximum [length x | Cols x <- xs]
 
-        f (Line x) = tr $ td cols x
-        f (Cols xs) = tr $ concatMap (td 1) (init xs) ++ td (cols + 1 - length xs) (last xs)
+        f (Line x) = tr $ td maxCols x
+        f (Cols xs) = tr $ concatMap (td 1) (init xs) ++ td (maxCols + 1 - length xs) (last xs)
 
         tr x = "<tr>" ++ x ++ "</tr>"
         td cols x = "<td" ++ (if cols == 1 then "" else " colspan='" ++ show cols ++ "'")
-                          ++ (if a /= "" then " style='padding-left:" ++ show (length a) ++ "ex;'" else "") ++
+                          ++ (if null styles then "" else " style='" ++ unwords styles ++ "'") ++
                      ">" ++ if null b then "&nbsp;" else concatMap esc b ++ "</td>"
             where (a,b) = span isSpace x
+                  -- if the first letter of the contents is '-', assume this is a flag
+                  -- and be aware that HTML might try to line-break it, see #39
+                  isFlag = take 1 b == "-"
+                  styles = [ "padding-left:" ++ show (length a) ++ "ex;" | a /= "" ]
+                        ++ [ "white-space:nowrap;" | isFlag ]
 
         esc '&' = "&amp;"
         esc '>' = "&gt;"
         esc '<' = "&lt;"
+        esc '\n' = "<br />"
         esc x = [x]
diff --git a/cmdargs.cabal b/cmdargs.cabal
--- a/cmdargs.cabal
+++ b/cmdargs.cabal
@@ -1,13 +1,13 @@
-cabal-version:      >= 1.6
+cabal-version:      1.18
 build-type:         Simple
 name:               cmdargs
-version:            0.6.9
+version:            0.10.22
 license:            BSD3
 license-file:       LICENSE
 category:           Console
 author:             Neil Mitchell <ndmitchell@gmail.com>
 maintainer:         Neil Mitchell <ndmitchell@gmail.com>
-copyright:          Neil Mitchell 2009-2011
+copyright:          Neil Mitchell 2009-2023
 synopsis:           Command line argument processing
 description:
     This library provides an easy way to define command line parsers. Most users
@@ -28,20 +28,44 @@
     .
     For a general reference on what command line flags are commonly used,
     see <http://www.faqs.org/docs/artu/ch10s05.html>.
-homepage:           http://community.haskell.org/~ndm/cmdargs/
-stability:          Beta
-extra-source-files:
-    cmdargs.htm
+bug-reports:        https://github.com/ndmitchell/cmdargs/issues
+homepage:           https://github.com/ndmitchell/cmdargs#readme
+extra-doc-files:
+    README.md
+    CHANGES.txt
+tested-with:        GHC==9.2, GHC==9.0, GHC==8.10, GHC==8.8, GHC==8.6, GHC==8.4, GHC==8.2, GHC==8.0
 
+source-repository head
+    type:     git
+    location: https://github.com/ndmitchell/cmdargs.git
+
 flag testprog
-    default: True
+    default: False
+    manual: True
     description: Build the test program
 
+flag quotation
+    default: True
+    manual: True
+    description: Build the Quote module
+
 library
+    default-language: Haskell2010
     build-depends:
-        base == 4.*,
-        transformers == 0.2.*
+        base >= 4.4 && < 5,
+        filepath,
+        transformers >= 0.2,
+        process >= 1.0
 
+    if impl(ghc < 8.0)
+        build-depends: semigroups >= 0.18
+
+    if flag(quotation)
+        build-depends: template-haskell
+        exposed-modules: System.Console.CmdArgs.Quote
+        -- See bug #539 for why this magic is required
+        other-extensions: TemplateHaskell
+
     exposed-modules:
         System.Console.CmdArgs
         System.Console.CmdArgs.Annotate
@@ -50,13 +74,17 @@
         System.Console.CmdArgs.GetOpt
         System.Console.CmdArgs.Implicit
         System.Console.CmdArgs.Text
+        System.Console.CmdArgs.Helper
         System.Console.CmdArgs.Verbosity
 
     other-modules:
         Data.Generics.Any
         Data.Generics.Any.Prelude
+        System.Console.CmdArgs.Explicit.Complete
+        System.Console.CmdArgs.Explicit.ExpandArgsAt
         System.Console.CmdArgs.Explicit.Help
         System.Console.CmdArgs.Explicit.Process
+        System.Console.CmdArgs.Explicit.SplitJoin
         System.Console.CmdArgs.Explicit.Type
         System.Console.CmdArgs.Implicit.Ann
         System.Console.CmdArgs.Implicit.Global
@@ -67,8 +95,12 @@
         System.Console.CmdArgs.Implicit.UI
 
 executable cmdargs
+    default-language: Haskell2010
     main-is: Main.hs
-    if flag(testprog)
+    other-extensions: TemplateHaskell
+    build-depends:
+        base, transformers, filepath, process, template-haskell
+    if flag(testprog) && flag(quotation)
         buildable: True
     else
         buildable: False
@@ -83,4 +115,5 @@
         System.Console.CmdArgs.Test.Implicit.Maker
         System.Console.CmdArgs.Test.Implicit.Tests
         System.Console.CmdArgs.Test.Implicit.Util
+        System.Console.CmdArgs.Test.SplitJoin
         System.Console.CmdArgs.Test.Util
diff --git a/cmdargs.htm b/cmdargs.htm
deleted file mode 100644
--- a/cmdargs.htm
+++ /dev/null
@@ -1,432 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-    <head>
-        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-        <title>CmdArgs: Easy Command Line Processing</title>
-        <style type="text/css">
-pre, .cmdargs {
-    border: 2px solid gray;
-    padding: 1px;
-    padding-left: 5px;
-    margin-left: 10px;
-    background-color: #eee;
-}
-
-pre.define {
-    background-color: #ffb;
-    border-color: #cc0;
-}
-
-body {
-    font-family: sans-serif;
-}
-
-h1, h2, h3 {
-    font-family: serif;
-}
-
-h1 {
-    color: rgb(23,54,93);
-    border-bottom: 1px solid rgb(79,129,189);
-    padding-bottom: 2px;
-    font-variant: small-caps;
-    text-align: center;
-}
-
-a {
-    color: rgb(54,95,145);
-}
-
-h2 {
-    color: rgb(54,95,145);
-}
-
-h3 {
-    color: rgb(79,129,189);
-}
-
-p.rule {
-    background-color: #ffb;
-    padding: 3px;
-    margin-left: 50px;
-    margin-right: 50px;
-}
-
-
-.cmdargs {
-	display: block;
-	font-family: monospace;
-}
-.cmdargs td {
-	padding: 0px;
-	margin: 0px;
-}
-        </style>
-    </head>
-    <body>
-
-<h1>CmdArgs: Easy Command Line Processing</h1>
-
-<p style="text-align:right;margin-bottom:25px;">
-    by <a href="http://community.haskell.org/~ndm/">Neil Mitchell</a>
-</p>
-
-<p>
-    <a href="http://community.haskell.org/~ndm/cmdargs/">CmdArgs</a> is a library for defining and parsing command lines. The focus of CmdArgs is allowing the concise definition of fully-featured command line argument processors, in a mainly declarative manner (i.e. little coding needed). CmdArgs also supports multiple mode programs, for example as used in <a href="http://darcs.net/">darcs</a> and <a href="http://haskell.org/cabal/">Cabal</a>.
-</p><p>
-	This document explains how to write the "hello world" of command line processors, then how to extend it with features into a complex command line processor. Finally this document gives three samples, which the <tt>cmdargs</tt> program can run. The three samples are:
-</p>
-<ol>
-    <li><tt>hlint</tt> - the <a href="http://community.haskell.org/~ndm/hlint/">HLint</a> program.</li>
-    <li><tt>diffy</tt> - a program to compare the differences between directories.</li>
-    <li><tt>maker</tt> - a make style program.</li>
-</ol>
-<p>
-	For each example you are encouraged to look at it's source (see the <a href="http://community.haskell.org/~ndm/darcs/hlint">darcs repo</a>, or the bottom of this document) and run it (try <tt>cmdargs hlint --help</tt>). The HLint program is fairly standard in terms of it's argument processing, and previously used the <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/System-Console-GetOpt.html">System.Console.GetOpt</a> library. Using GetOpt required 90 lines and a reasonable amount of duplication. Using CmdArgs the code requires 30 lines, and the logic is much simpler.
-</p>
-<h3>Acknowledgements</h3>
-<p>
-	Thanks to Kevin Quick for substantial patches, and additional code contributions from Sebastian Fischer and Daniel Schoepe.
-</p>
-
-
-<h2>Hello World Example</h2>
-<p>
-	The following code defines a complete command line argument processor:
-</p>
-<pre>
-{-# LANGUAGE DeriveDataTypeable #-}
-module Sample where
-import System.Console.CmdArgs
-
-data Sample = Sample {hello :: String}
-              deriving (Show, Data, Typeable)
-
-sample = Sample{hello = def}
-
-main = print =<< cmdArgs sample
-</pre>
-<p>
-	To use the CmdArgs library there are three steps:
-</p>
-<ol>
-	<li>Define a record data type (<tt>Sample</tt>) that contains a field for each argument. This type needs to have instances for <tt>Show</tt>, <tt>Data</tt> and <tt>Typeable</tt>.</li>
-	<li>Give a value of that type (<tt>sample</tt>) with default values (<tt>def</tt> is a default value of any type, but I could also have written <tt>""</tt>). This value is turned into a command line by calling the <tt>cmdArgs</tt> function.</li>
-</ol>
-<p>
-	Now we have a reasonably functional command line argument processor. Some sample interactions are:
-</p>
-<pre>
-$ runhaskell Sample.hs --hello=world
-Sample {hello = "world"}
-
-$ runhaskell Sample.hs --version
-The sample program
-
-$ runhaskell Sample.hs --help
-The sample program
-
-sample [OPTIONS]
-
-  -? --help        Display help message
-  -V --version     Print version information
-  -h --hello=ITEM
-</pre>
-<p>
-	CmdArgs uses defaults to automatically infer a command line parser for a value, and provides annotations to override any of the the defaults. CmdArgs automatically supports <tt>--help</tt> and <tt>--version</tt> flags, and optionally supports verbosity flags.
-</p>
-
-<h2>Specifying Attributes</h2>
-<p>
-	In order to control the behaviour we can add attributes. For example to add an attribute specifying the help text for the <tt>--hello</tt> argument we can write:
-</p>
-<pre>
-sample = Sample{hello = def &= help "Who to say hello to"}
-</pre>
-<p>
-	We can add additional attributes, for example to specify the type of the value expected by hello:
-</p>
-<pre>
-sample = Sample {hello = def &= help "Who to say hello to" &= typ "WORLD"}
-</pre>
-<p>
-	Now when running <tt>--help</tt> the final line is:
-</p>
-<pre>
-  -h --hello=WORLD  Who to say hello to
-</pre>
-<p>
-	There are many more attributes, detailed in the <a href="http://hackage.haskell.org/packages/archive/cmdargs/latest/doc/html/System-Console-CmdArgs.html#2">Haddock documentation</a>.
-</p>
-
-
-<h2>Multiple Modes</h2>
-<p>
-	To specify a program with multiple modes, similar to <a href="http://darcs.net/">darcs</a>, we can supply a data type with multiple constructors, for example:
-</p>
-<pre>
-data Sample = Hello {whom :: String}
-            | Goodbye
-              deriving (Show, Data, Typeable)
-
-hello = Hello{whom = def}
-goodbye = Goodbye
-
-main = print =<< cmdArgs (modes [hello,goodbye])
-</pre>
-<p>
-	Compared to the first example, we now have multiple constructors, and a sample value for each constructor is passed to <tt>cmdArgs</tt>. Some sample interactions with this command line are:
-</p>
-<pre>
-$ runhaskell Sample.hs hello --whom=world
-Hello {whom = "world"}
-
-$ runhaskell Sample.hs goodbye
-Goodbye
-
-$ runhaskell Sample.hs --help
-The sample program
-
-sample [OPTIONS]
-
- Common flags
-  -? --help       Display help message
-  -V --version    Print version information
-
-sample hello [OPTIONS]
-
-  -w --whom=ITEM
-
-sample goodbye [OPTIONS]
-</pre>
-<p>
-	As before, the behaviour can be customised using attributes.
-</p>
-
-<h2>Larger Examples</h2>
-
-<p>
-	For each of the following examples we first explain the purpose of the program, then give the source code, and finally the output of <tt>--help=HTML</tt>. The programs are intended to show sample uses of CmdArgs, and are available to experiment with through <tt>cmdargs <i>progname</i></tt>.
-</p>
-
-<h3>HLint</h3>
-
-<p>
-	The <a href="http://community.haskell.org/~ndm/hlint/">HLint</a> program analyses a list of files, using various options to control the analysis. The command line processing is simple, but a few interesting points are:
-</p>
-<ul>
-	<li>The <tt>--report</tt> flag can be used to output a report in a standard location, but giving the flag a value changes where the file is output.</li>
-	<li>The <tt>color</tt> field is assigned two flag aliases, <tt>--colour</tt> and <tt>-c</tt>. Assigning the <tt>-c</tt> short flag explicitly stops either of the CPP fields using it.</li>
-	<li>The <tt>show_</tt> field would clash with <tt>show</tt> if given the expected name, but CmdArgs automatically strips the trailing underscore.</li>
-	<li>The <tt>cpp_define</tt> field has an underscore in it's name, which is transformed into a hyphen for the flag name.</li>
-</ul>
-
-<!-- BEGIN code hlint -->
-<pre>
-{-# LANGUAGE DeriveDataTypeable #-}
-module HLint where
-import System.Console.CmdArgs
-
-data HLint = HLint
-    {report :: [FilePath]
-    ,hint :: [FilePath]
-    ,color :: Bool
-    ,ignore :: [String]
-    ,show_ :: Bool
-    ,extension :: [String]
-    ,language :: [String]
-    ,utf8 :: Bool
-    ,encoding :: String
-    ,find :: [FilePath]
-    ,test_ :: Bool
-    ,datadir :: [FilePath]
-    ,cpp_define :: [String]
-    ,cpp_include :: [FilePath]
-    ,files :: [FilePath]
-    }
-    deriving (Data,Typeable,Show,Eq)
-
-hlint = HLint
-    {report = def &= opt "report.html" &= typFile &= help "Generate a report in HTML"
-    ,hint = def &= typFile &= help "Hint/ignore file to use"
-    ,color = def &= name "c" &= name "colour" &= help "Color the output (requires ANSI terminal)"
-    ,ignore = def &= typ "MESSAGE" &= help "Ignore a particular hint"
-    ,show_ = def &= help "Show all ignored ideas"
-    ,extension = def &= typ "EXT" &= help "File extensions to search (defaults to hs and lhs)"
-    ,language = def &= name "X" &= typ "LANG" &= help "Language extension (Arrows, NoCPP)"
-    ,utf8 = def &= help "Use UTF-8 text encoding"
-    ,encoding = def &= typ "ENC" &= help "Choose the text encoding"
-    ,find = def &= typFile &= help "Find hints in a Haskell file"
-    ,test_ = def &= help "Run in test mode"
-    ,datadir = def &= typDir &= help "Override the data directory"
-    ,cpp_define = def &= typ "NAME[=VALUE]" &= help "CPP #define"
-    ,cpp_include = def &= typDir &= help "CPP include path"
-    ,files = def &= args &= typ "FILES/DIRS"
-    } &=
-    verbosity &=
-    help "Suggest improvements to Haskell source code" &=
-    summary "HLint v0.0.0, (C) Neil Mitchell 2006-2010" &=
-    details ["Hlint gives hints on how to improve Haskell code",""
-            ,"To check all Haskell files in 'src' and generate a report type:","  hlint src --report"]
-
-mode = cmdArgsMode hlint
-</pre>
-<!-- END -->
-<!-- BEGIN help hlint -->
-<table class='cmdargs'>
-<tr><td colspan='3'>HLint v0.0.0, (C) Neil Mitchell 2006-2010</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>hlint [OPTIONS] [FILES/DIRS]</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Suggest improvements to Haskell source code</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td style='padding-left:2ex;'>-?</td><td style='padding-left:1ex;'>--help</td><td style='padding-left:2ex;'>Display help message</td></tr>
-<tr><td style='padding-left:2ex;'>-V</td><td style='padding-left:1ex;'>--version</td><td style='padding-left:2ex;'>Print version information</td></tr>
-<tr><td style='padding-left:2ex;'>-v</td><td style='padding-left:1ex;'>--verbose</td><td style='padding-left:2ex;'>Loud verbosity</td></tr>
-<tr><td style='padding-left:2ex;'>-q</td><td style='padding-left:1ex;'>--quiet</td><td style='padding-left:2ex;'>Quiet verbosity</td></tr>
-<tr><td style='padding-left:2ex;'>-r</td><td style='padding-left:1ex;'>--report[=FILE]</td><td style='padding-left:2ex;'>Generate a report in HTML (default=report.html)</td></tr>
-<tr><td style='padding-left:2ex;'>-h</td><td style='padding-left:1ex;'>--hint=FILE</td><td style='padding-left:2ex;'>Hint/ignore file to use</td></tr>
-<tr><td style='padding-left:2ex;'>-c</td><td style='padding-left:1ex;'>--color --colour</td><td style='padding-left:2ex;'>Color the output (requires ANSI terminal)</td></tr>
-<tr><td style='padding-left:2ex;'>-i</td><td style='padding-left:1ex;'>--ignore=MESSAGE</td><td style='padding-left:2ex;'>Ignore a particular hint</td></tr>
-<tr><td style='padding-left:2ex;'>-s</td><td style='padding-left:1ex;'>--show</td><td style='padding-left:2ex;'>Show all ignored ideas</td></tr>
-<tr><td style='padding-left:2ex;'>&nbsp;<td style='padding-left:1ex;'>--extension=EXT</td><td style='padding-left:2ex;'>File extensions to search (defaults to hs and lhs)</td></tr>
-<tr><td style='padding-left:2ex;'>-X</td><td style='padding-left:1ex;'>--language=LANG</td><td style='padding-left:2ex;'>Language extension (Arrows, NoCPP)</td></tr>
-<tr><td style='padding-left:2ex;'>-u</td><td style='padding-left:1ex;'>--utf8</td><td style='padding-left:2ex;'>Use UTF-8 text encoding</td></tr>
-<tr><td style='padding-left:2ex;'>&nbsp;<td style='padding-left:1ex;'>--encoding=ENC</td><td style='padding-left:2ex;'>Choose the text encoding</td></tr>
-<tr><td style='padding-left:2ex;'>-f</td><td style='padding-left:1ex;'>--find=FILE</td><td style='padding-left:2ex;'>Find hints in a Haskell file</td></tr>
-<tr><td style='padding-left:2ex;'>-t</td><td style='padding-left:1ex;'>--test</td><td style='padding-left:2ex;'>Run in test mode</td></tr>
-<tr><td style='padding-left:2ex;'>-d</td><td style='padding-left:1ex;'>--datadir=DIR</td><td style='padding-left:2ex;'>Override the data directory</td></tr>
-<tr><td style='padding-left:2ex;'>&nbsp;<td style='padding-left:1ex;'>--cpp-define=NAME[=VALUE]</td><td style='padding-left:2ex;'>CPP #define</td></tr>
-<tr><td style='padding-left:2ex;'>&nbsp;<td style='padding-left:1ex;'>--cpp-include=DIR</td><td style='padding-left:2ex;'>CPP include path</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Hlint gives hints on how to improve Haskell code</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>&nbsp;</tr>
-<tr><td colspan='3' style='padding-left:1ex;'>To check all Haskell files in 'src' and generate a report type:</td></tr>
-<tr><td colspan='3' style='padding-left:3ex;'>hlint src --report</td></tr>
-</table>
-<!-- END -->
-
-<h3>Diffy</h3>
-
-<p>
-	The Diffy sample is a based on the idea of creating directory listings and comparing them. The tool can operate in two separate modes, <tt>create</tt> or <tt>diff</tt>. This sample is fictional, but the ideas are drawn from a real program. A few notable features:
-</p>
-<ul>
-	<li>There are multiple modes of execution, creating and diffing.</li>
-	<li>The diff mode takes exactly two arguments, the old file and the new file.</li>
-	<li>Default values are given for the <tt>out</tt> field, which are different in both modes.</li>
-</ul>
-
-<!-- BEGIN code diffy -->
-<pre>
-{-# LANGUAGE DeriveDataTypeable #-}
-module Diffy where
-import System.Console.CmdArgs
-
-data Diffy = Create {src :: Maybe FilePath, out :: FilePath}
-           | Diff {old :: FilePath, new :: FilePath, out :: FilePath}
-             deriving (Data,Typeable,Show,Eq)
-
-outFlags x = x &= help "Output file" &= typFile
-
-create = Create
-    {src = def &= help "Source directory" &= typDir
-    ,out = outFlags "ls.txt"
-    } &= help "Create a fingerprint"
-
-diff = Diff
-    {old = def &= typ "OLDFILE" &= argPos 0
-    ,new = def &= typ "NEWFILE" &= argPos 1
-    ,out = outFlags "diff.txt"
-    } &= help "Perform a diff"
-
-mode = cmdArgsMode $ modes [create,diff] &= help "Create and compare differences" &= program "diffy" &= summary "Diffy v1.0"
-</pre>
-<!-- END -->
-<!-- BEGIN help diffy -->
-<table class='cmdargs'>
-<tr><td colspan='3'>Diffy v1.0</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>diffy [OPTIONS]</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Create and compare differences</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Common flags</td></tr>
-<tr><td style='padding-left:2ex;'>-?</td><td style='padding-left:1ex;'>--help</td><td style='padding-left:2ex;'>Display help message</td></tr>
-<tr><td style='padding-left:2ex;'>-V</td><td style='padding-left:1ex;'>--version</td><td style='padding-left:2ex;'>Print version information</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>diffy create [OPTIONS]</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Create a fingerprint</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td style='padding-left:2ex;'>-s</td><td style='padding-left:1ex;'>--src=DIR</td><td style='padding-left:2ex;'>Source directory</td></tr>
-<tr><td style='padding-left:2ex;'>-o</td><td style='padding-left:1ex;'>--out=FILE</td><td style='padding-left:2ex;'>Output file</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>diffy diff [OPTIONS] OLDFILE NEWFILE</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Perform a diff</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td style='padding-left:2ex;'>-o</td><td style='padding-left:1ex;'>--out=FILE</td><td style='padding-left:2ex;'>Output file</td></tr>
-</table>
-<!-- END -->
-
-<h3>Maker</h3>
-
-<p>
-	The Maker sample is based around a build system, where we can either build a project, clean the temporary files, or run a test. Some interesting features are:
-</p>
-<ul>
-	<li>The build mode is the default, so <tt>maker</tt> on it's own will be interpretted as a build command.</li>
-	<li>The build method is an enumeration.</li>
-	<li>The <tt>threads</tt> field is in two of the constructors, but not all three. It is given the short flag <tt>-j</tt>, rather than the default <tt>-t</tt>.</li>
-</ul>
-
-
-<!-- BEGIN code maker -->
-<pre>
-{-# LANGUAGE DeriveDataTypeable #-}
-module Maker where
-import System.Console.CmdArgs
-
-data Method = Debug | Release | Profile
-              deriving (Data,Typeable,Show,Eq)
-
-data Maker
-    = Wipe
-    | Test {threads :: Int, extra :: [String]}
-    | Build {threads :: Int, method :: Method, files :: [FilePath]}
-      deriving (Data,Typeable,Show,Eq)
-
-threadsMsg x = x &= help "Number of threads to use" &= name "j" &= typ "NUM"
-
-wipe = Wipe &= help "Clean all build objects"
-</pre>
-<!-- END -->
-<!-- BEGIN help maker -->
-<table class='cmdargs'>
-<tr><td colspan='3'>Maker v1.0</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>maker [OPTIONS] </td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Build helper program</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Common flags</td></tr>
-<tr><td style='padding-left:2ex;'>-?</td><td style='padding-left:1ex;'>--help</td><td style='padding-left:2ex;'>Display help message</td></tr>
-<tr><td style='padding-left:2ex;'>-V</td><td style='padding-left:1ex;'>--version</td><td style='padding-left:2ex;'>Print version information</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>maker [build] [OPTIONS] [ITEM]</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Build the project</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td style='padding-left:2ex;'>-j</td><td style='padding-left:1ex;'>--threads=NUM</td><td style='padding-left:2ex;'>Number of threads to use</td></tr>
-<tr><td style='padding-left:2ex;'>-r</td><td style='padding-left:1ex;'>--release</td><td style='padding-left:2ex;'>Release build</td></tr>
-<tr><td style='padding-left:2ex;'>-d</td><td style='padding-left:1ex;'>--debug</td><td style='padding-left:2ex;'>Debug build</td></tr>
-<tr><td style='padding-left:2ex;'>-p</td><td style='padding-left:1ex;'>--profile</td><td style='padding-left:2ex;'>Profile build</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>maker wipe [OPTIONS]</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Clean all build objects</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td colspan='3'>maker test [OPTIONS] [ANY]</td></tr>
-<tr><td colspan='3' style='padding-left:1ex;'>Run the test suite</td></tr>
-<tr><td colspan='3'>&nbsp;</tr>
-<tr><td style='padding-left:2ex;'>-j</td><td style='padding-left:1ex;'>--threads=NUM</td><td style='padding-left:2ex;'>Number of threads to use</td></tr>
-</table>
-<!-- END -->
-
-    </body>
-</html>
