Changeset 130 for misc

Show
Ignore:
Timestamp:
08/18/07 15:05:21 (1 year ago)
Author:
KirkMcDonald
Message:

Split optparse into two modules, one with the command-line interface, one allowing the use of optparse outside of the command-line.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • misc/optparse.d

    r126 r130  
    2020SOFTWARE. 
    2121*/ 
    22 /* 
    23 2007-08-15 - Added changes to make GDC happy, submitted by Tim Burrell. 
    24 */ 
    2522/** 
    2623 * Command-line option parsing, in the style of Python's optparse. 
    2724 * 
    28  * Refer to the complete docs for more information
     25 * This is the command-line interface to optparse
    2926 */ 
    3027module optparse; 
    3128 
     29import optimpl; 
     30public import optimpl : Options, Option, Action, ArgType, 
     31    OptionCallbackFancy, 
     32    OptionCallbackFancyArg, 
     33    OptionCallbackFancyInt, 
     34    OptionCallback, 
     35    OptionCallbackArg, 
     36    OptionCallbackInt; 
     37 
    3238version (Tango) { 
    3339    import tango.io.Stdout : Stdout; 
     40    import tango.stdc.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS; 
     41    /+ 
    3442    import tango.text.Util : find = locate, locatePrior; 
    3543    import tango.text.Ascii : toupper = toUpper; 
    36     import tango.stdc.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS; 
    3744    import tango.text.convert.Integer : parse, toInt, toString = toUtf8; 
    3845    import tango.text.convert.Utf : toUTF8 = toUtf8, toUTF32 = toUtf32; 
     
    4148    } 
    4249    //alias char[] string; 
     50    +/ 
    4351} else { 
    4452    import std.stdio : writefln, writef; 
     53    import std.c.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS; 
     54    /+ 
    4555    import std.string : find, toupper, toString; 
    46     import std.c.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS; 
    4756    import std.conv : toInt, ConvError; 
    4857    import std.path : getBaseName; 
     
    5160        return -1; 
    5261    } 
    53 
    54  
    55 /* 
    56 Options may be in two forms: long and short. Short options start with a single 
    57 dash and are one letter long. Long options start with two dashes and may 
    58 consist of any number of characters (so long as they don't start with a dash, 
    59 though they may contain dashes). Options are case-sensitive. 
    60  
    61 Short options may be combined. The following are equivalent: 
    62  
    63 $ myapp -a -b -c 
    64 $ myapp -abc 
    65 $ myapp -ab -c 
    66  
    67 If -f and --file are aliases of the same option, which accepts an argument, 
    68 the following are equivalent: 
    69  
    70 $ myapp -f somefile.txt 
    71 $ myapp -fsomefile.txt 
    72 $ myapp --file somefile.txt 
    73 $ myapp --file=somefile.txt 
    74  
    75 The following are also valid: 
    76  
    77 $ myapp -abcf somefile.txt 
    78 $ myapp -abcfsomefile.txt 
    79 $ myapp -abc --file somefile.txt 
    80  
    81 If an option occurs multiple times, the last one is the one recorded: 
    82  
    83 $ myapp -f somefile.txt --file otherfile.txt 
    84  
    85 Matches 'otherfile.txt'. 
    86 */ 
    87  
    88 bool startswith(char[] s, char[] start) { 
    89     if (s.length < start.length) return false; 
    90     return s[0 .. start.length] == start; 
    91 
    92 bool endswith(char[] s, char[] end) { 
    93     if (s.length < end.length) return false; 
    94     return s[$ - end.length .. $] == end; 
    95 
    96  
    97 /// Thrown if client code tries to set up an improper option. 
    98 class OptionError : Exception { 
    99     this(char[] msg) { super(msg); } 
    100 
    101 // Thrown if client code tries to extract the wrong type from an option. 
    102 class OptionTypeError : Exception { 
    103     this(char[] msg) { super(msg); } 
    104 
    105  
    106 /++ 
    107 This class represents the results after parsing the command-line. 
    108 +/ 
    109 class Options { 
    110     char[][][char[]] opts; 
    111     int[char[]] counted_opts; 
    112     /// By default, leftover arguments are placed in this array. 
    113     char[][] args; 
    114  
    115     /// Retrieves the results of the Store and StoreConst actions. 
    116     char[] opIndex(char[] opt) { 
    117         char[][]* o = opt in opts; 
    118         if (o) { 
    119             return (*o)[0]; 
    120         } else { 
    121             return ""; 
    122         } 
    123     } 
    124     /// Retrieves the results of the Store action, when the type is Integer. 
    125     int value(char[] opt) { 
    126         char[][]* o = opt in opts; 
    127         if (o) { 
    128             return toInt((*o)[0]); 
    129         } else { 
    130             return 0; 
    131         } 
    132     } 
    133     /// Retrieves the results of the Append and AppendConst actions. 
    134     char[][] list(char[] opt) { 
    135         char[][]* o = opt in opts; 
    136         if (o) { 
    137             return *o; 
    138         } else { 
    139             return null; 
    140         } 
    141     } 
    142     /// Retrieves the results of the Append action, when the type is Integer. 
    143     int[] valueList(char[] opt) { 
    144         char[][]* o = opt in opts; 
    145         int[] l; 
    146         if (o) { 
    147             l.length = (*o).length; 
    148             foreach (i, s; *o) { 
    149                 l[i] = toInt(s); 
    150             } 
    151         } 
    152         return l; 
    153     } 
    154     /// Retrieves the results of the Count action. 
    155     int count(char[] opt) { 
    156         int* c = opt in counted_opts; 
    157         if (c) { 
    158             return *c; 
    159         } else { 
    160             return 0; 
    161         } 
    162     } 
    163     /// Retrieves the results of the SetTrue and SetFalse actions. 
    164     bool flag(char[] opt) { 
    165         char[][]* o = opt in opts; 
    166         if (o) { 
    167             return (*o)[0] == "1"; 
    168         } else { 
    169             return false; 
    170         } 
    171     } 
    172     private { 
    173         void storeArg(char[] name, char[] arg) { 
    174             opts[name] = [arg]; 
    175         } 
    176         void storeArg(char[] name, bool arg) { 
    177             if (arg) { 
    178                 opts[name] = ["1"]; 
    179             } else { 
    180                 opts[name] = ["0"]; 
    181             } 
    182         } 
    183         void appendArg(char[] name, char[] arg) { 
    184             opts[name] ~= arg; 
    185         } 
    186         void increment(char[] name) { 
    187             ++counted_opts[name]; 
    188         } 
    189         bool hasName(char[] name) { 
    190             return name in opts || name in counted_opts; 
    191         } 
    192     } 
    193 
    194  
    195 // Options, args, this opt's index in args, name[, arg] 
    196 /// 
    197 alias void delegate(Options, inout char[][], inout int, char[], char[]) OptionCallbackFancyArg; 
    198 /// 
    199 alias void delegate(Options, inout char[][], inout int, char[], int)    OptionCallbackFancyInt; 
    200 /// 
    201 alias void delegate(Options, inout char[][], inout int, char[])         OptionCallbackFancy; 
    202  
    203 /// 
    204 alias void delegate(char[]) OptionCallbackArg; 
    205 /// 
    206 alias void delegate(int)    OptionCallbackInt; 
    207 /// 
    208 alias void delegate()       OptionCallback; 
    209  
    210 /// 
    211 enum Action { /+++/Store, /+++/StoreConst, /+++/Append, /+++/AppendConst, /+++/Count, /+++/SetTrue, /+++/SetFalse, /+++/Callback, /+++/CallbackFancy, /+++/Help /+++/} 
    212 /// 
    213 enum ArgType { /+/+++/None,+/ /+++/String, /+++/Integer, /+++/Bool /+++/} 
    214  
    215 /++ 
    216 This class represents a single command-line option. 
    217 +/ 
    218 abstract class Option { 
    219     char[][] shortopts, longopts; 
    220     char[] name, argname; 
    221     char[] helptext; 
    222     this(char[][] options, char[] name) { 
    223         dchar[] opt; 
    224         foreach (_opt; options) { 
    225             // (Unicode note: We convert to dchar[] so the length checks work 
    226             // out in the event of a short opt with a >127 character.) 
    227             opt = toUTF32(_opt); 
    228             if (opt.length < 2) { 
    229                 throw new OptionError( 
    230                     "invalid option string '" ~ _opt ~ "': must be at least two characters long" 
    231                 ); 
    232             } else if (opt.length > 2) { 
    233                 if (opt[0 .. 2] != "--" || opt[2] == '-') 
    234                     throw new OptionError( 
    235                         "invalid long option string '" ~ _opt ~ "': must start with --, followed by non-dash" 
    236                     ); 
    237                 longopts ~= _opt; 
    238             } else { 
    239                 if (opt[0] != '-' || opt[1] == '-') 
    240                     throw new OptionError( 
    241                         "invalid short option string '" ~ _opt ~ "': must be of the form -x, where x is non-dash" 
    242                     ); 
    243                 shortopts ~= _opt; 
    244             } 
    245         } 
    246         if (name is null) { 
    247             // (Unicode note: We know '-' is a single code unit, so these 
    248             // slices are okay.) 
    249             if (longopts.length > 0) 
    250                 this.name = longopts[0][2 .. $]; 
    251             else if (shortopts.length > 0) 
    252                 this.name = shortopts[0][1 .. 2]; 
    253             else 
    254                 throw new OptionError( 
    255                     "No options provided to addOption!" 
    256                 ); 
    257         } else { 
    258             this.name = name; 
    259         } 
    260         argname = toupper(this.name.dup); 
    261     } 
    262     char[] toString() { 
    263         int optCount = this.shortopts.length + this.longopts.length; 
    264         char[] result; 
    265         //bool printed_arg = false; 
    266         foreach(i, opt; this.shortopts ~ this.longopts) { 
    267             result ~= opt; 
    268             if (i < optCount-1) { 
    269                 result ~= ", "; 
    270             } else if (this.hasArg()) { 
    271                 result ~= "=" ~ toupper(this.argname.dup); 
    272             } 
    273         } 
    274         return result; 
    275     } 
    276     //enum Action { Store, StoreConst, Append, AppendConst, Count, SetTrue, SetFalse, Callback, CallbackFancy, Help } 
    277     bool supports_default() { 
    278         return false; 
    279     } 
    280     ArgType def_type() { 
    281         throw new OptionError("Option "~name~" does not support default arguments."); 
    282     } 
    283     bool has_default() { 
    284         throw new OptionError("Option "~name~" does not support default arguments."); 
    285     } 
    286     void issue_default(Options results) { 
    287         return; 
    288     } 
    289     /// Does whatever this option is supposed to do. 
    290     abstract void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg); 
    291     /// Returns whether this option accepts an argument. 
    292     bool hasArg() { 
    293         return false; 
    294     } 
    295     /// Sets the help text for this option. 
    296     Option help(char[] help) { 
    297         this.helptext = help; 
    298         return this; 
    299     } 
    300     /// Sets the name of this option's argument, if it has one. 
    301     Option argName(char[] argname) { 
    302         this.argname = argname; 
    303         return this; 
    304     } 
    305     Option def(char[] val) { 
    306         throw new OptionError("Cannot specify string default for non-string option '"~this.name~"'"); 
    307     } 
    308     Option def(int val) { 
    309         throw new OptionError("Cannot specify integer default for non-integer option '"~this.name~"'"); 
    310     } 
    311     Option def(bool val) { 
    312         throw new OptionError("Cannot specify boolean default for non-flag option '"~this.name~"'"); 
    313     } 
    314     // Returns true if the passed option string matches this option. 
    315     bool matches(char[] _arg) { 
    316         dchar[] arg = toUTF32(_arg); 
    317         if ( 
    318             arg.length < 2 || 
    319             arg.length == 2 && (arg[0] != '-' || arg[1] == '-') || 
    320             arg.length > 2 && (arg[0 .. 2] != "--" || arg[2] == '-') 
    321         ) { 
    322             return false; 
    323         } 
    324         if (arg.length == 2) { 
    325             foreach (opt; shortopts) { 
    326                 if (_arg == opt) { 
    327                     return true; 
    328                 } 
    329             } 
    330         } else { 
    331             foreach (opt; longopts) { 
    332                 if (_arg == opt) { 
    333                     return true; 
    334                 } 
    335             } 
    336         } 
    337         return false; 
    338     } 
    339 
    340  
    341 abstract class ArgOption : Option { 
    342     static const ArgType default_type = ArgType.String; 
    343     ArgType type; 
    344     bool _has_default = false; 
    345     char[] default_string; 
    346     this(char[][] options, char[] name, ArgType type) { 
    347         super(options, name); 
    348         if (type != ArgType.Integer && type != ArgType.String) { 
    349             throw new OptionError("Argument type for Store and Append must be Integer or String."); 
    350         } 
    351         this.type = type; 
    352     } 
    353     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    354         if (this.type == ArgType.Integer) { 
    355             // Verify that it's an int. 
    356             int i = parser.toOptInt(arg); 
    357         } 
    358     } 
    359     override ArgType def_type() { 
    360         return this.type; 
    361     } 
    362     override Option def(char[] val) { 
    363         if (this.type != ArgType.String) 
    364             super.def(val); 
    365         this._has_default = true; 
    366         this.default_string = val; 
    367         return this; 
    368     } 
    369     override Option def(int val) { 
    370         if (this.type != ArgType.Integer) 
    371             super.def(val); 
    372         this._has_default = true; 
    373         this.default_string = .toString(val); 
    374         return this; 
    375     } 
    376     override bool supports_default() { 
    377         return true; 
    378     } 
    379     override bool has_default() { 
    380         return _has_default; 
    381     } 
    382     override void issue_default(Options results) { 
    383         if (_has_default) results.storeArg(this.name, this.default_string); 
    384     } 
    385     override bool hasArg() { 
    386         return true; 
    387     } 
    388 
    389  
    390 class Store : ArgOption { 
    391     this(char[][] options, char[] name, ArgType type=Store.default_type) { 
    392         super(options, name, type); 
    393     } 
    394     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    395         super.performAction(parser, results, args, idx, arg); 
    396         results.storeArg(this.name, arg); 
    397     } 
    398 
    399 class Append : ArgOption { 
    400     this(char[][] options, char[] name, ArgType type=Append.default_type) { 
    401         super(options, name, type); 
    402     } 
    403     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    404         super.performAction(parser, results, args, idx, arg); 
    405         results.appendArg(this.name, arg); 
    406     } 
    407 
    408 abstract class ConstOption : Option { 
    409     bool _has_default = false; 
    410     char[] default_value; 
    411     char[] const_value; 
    412     this(char[][] options, char[] name, char[] const_value) { 
    413         super(options, name); 
    414         this.const_value = const_value; 
    415     } 
    416     override Option def(char[] val) { 
    417         this._has_default = true; 
    418         this.default_value = val; 
    419         return this; 
    420     } 
    421     override ArgType def_type() { 
    422         return ArgType.String; 
    423     } 
    424     override bool supports_default() { 
    425         return true; 
    426     } 
    427     override bool has_default() { 
    428         return _has_default; 
    429     } 
    430     override void issue_default(Options results) { 
    431         if (_has_default) results.storeArg(this.name, this.default_value); 
    432     } 
    433 
    434 class StoreConst : ConstOption { 
    435     this(char[][] options, char[] name, char[] const_value) { 
    436         super(options, name, const_value); 
    437     } 
    438     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    439         results.storeArg(this.name, this.const_value); 
    440     } 
    441 
    442 class AppendConst : ConstOption { 
    443     this(char[][] options, char[] name, char[] const_value) { 
    444         super(options, name, const_value); 
    445     } 
    446     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    447         results.appendArg(this.name, this.const_value); 
    448     } 
    449 
    450 abstract class SetBool : Option { 
    451     bool _has_default = false; 
    452     bool default_flag = false; 
    453     this(char[][] options, char[] name) { 
    454         super(options, name); 
    455     } 
    456     override Option def(bool val) { 
    457         this._has_default = true; 
    458         this.default_flag = val; 
    459         return this; 
    460     } 
    461     override ArgType def_type() { 
    462         return ArgType.Bool; 
    463     } 
    464     override bool supports_default() { 
    465         return true; 
    466     } 
    467     override bool has_default() { 
    468         return _has_default; 
    469     } 
    470     override void issue_default(Options results) { 
    471         if (_has_default) results.storeArg(this.name, this.default_flag); 
    472     } 
    473 
    474 class SetTrue : SetBool { 
    475     this(char[][] options, char[] name) { 
    476         super(options, name); 
    477     } 
    478     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    479         results.storeArg(this.name, true); 
    480     } 
    481 
    482 class SetFalse : SetBool { 
    483     this(char[][] options, char[] name) { 
    484         super(options, name); 
    485     } 
    486     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    487         results.storeArg(this.name, false); 
    488     } 
    489 
    490 class Count : Option { 
    491     this(char[][] options, char[] name) { 
    492         super(options, name); 
    493     } 
    494     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    495         results.increment(this.name); 
    496     } 
    497 
    498 class Callback : Option { 
    499     OptionCallback cb; 
    500     this(char[][] options, OptionCallback cb) { 
    501         super(options, null); 
    502         this.cb = cb; 
    503     } 
    504     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    505         this.cb(); 
    506     } 
    507 
    508 class CallbackArg : Option { 
    509     OptionCallbackArg cb; 
    510     this(char[][] options, OptionCallbackArg cb) { 
    511         super(options, null); 
    512         this.cb = cb; 
    513     } 
    514     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    515         this.cb(arg); 
    516     } 
    517     override bool hasArg() { 
    518         return true; 
    519     } 
    520 
    521 class CallbackInt : Option { 
    522     OptionCallbackInt cb; 
    523     this(char[][] options, OptionCallbackInt cb) { 
    524         super(options, null); 
    525         this.cb = cb; 
    526     } 
    527     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    528         int i = parser.toOptInt(arg); 
    529         this.cb(i); 
    530     } 
    531     override bool hasArg() { 
    532         return true; 
    533     } 
    534 
    535 class FancyCallback : Option { 
    536     OptionCallbackFancy cb; 
    537     this(char[][] options, char[] name, OptionCallbackFancy cb) { 
    538         super(options, name); 
    539         this.cb = cb; 
    540     } 
    541     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    542         this.cb(results, args, idx, this.name); 
    543     } 
    544 
    545 class FancyCallbackArg : Option { 
    546     OptionCallbackFancyArg cb; 
    547     this(char[][] options, char[] name, OptionCallbackFancyArg cb) { 
    548         super(options, name); 
    549         this.cb = cb; 
    550     } 
    551     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    552         this.cb(results, args, idx, this.name, arg); 
    553     } 
    554     override bool hasArg() { 
    555         return true; 
    556     } 
    557 
    558 class FancyCallbackInt : Option { 
    559     OptionCallbackFancyInt cb; 
    560     this(char[][] options, char[] name, OptionCallbackFancyInt cb) { 
    561         super(options, name); 
    562         this.cb = cb; 
    563     } 
    564     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    565         int i = parser.toOptInt(arg); 
    566         this.cb(results, args, idx, this.name, i); 
    567     } 
    568     override bool hasArg() { 
    569         return true; 
    570     } 
    571 
    572 class Help : Option { 
    573     this(char[][] options) { 
    574         super(options, null); 
    575     } 
    576     override void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) { 
    577         parser.helpText(); 
    578         exit(EXIT_SUCCESS); 
    579     } 
     62    +/ 
    58063} 
    58164 
     
    58467arguments. 
    58568+/ 
    586 class OptionParser { 
    587     OptionCallbackArg leftover_cb; 
    588     /// An array of all of the options known by this parser. 
    589     Option[] options; 
    590     char[] name, desc; 
    591     /// The description of the programs arguments, as used in the Help action. 
    592     char[] argdesc; 
    593     private void delegate(char[]) error_callback; 
    594  
     69class OptionParser : optimpl.OptionParser { 
    59570    this(char[] desc="") { 
    596         this.name = ""; 
    597         this.desc = desc; 
    598         this.argdesc = "[options] args..."; 
    599     } 
    600  
    601     /// Sets a callback, to override the default error behavior. 
    602     void setErrorCallback(void delegate(char[]) dg) { 
    603         error_callback = dg; 
    604     } 
    605     void unknownOptError(char[] opt) { 
    606         error("Unknown argument '"~opt~"'"); 
    607     } 
    608     void expectedArgError(char[] opt) { 
    609         error("'"~opt~"' option expects an argument."); 
     71        super(desc); 
    61072    } 
    61173    /// Displays an error message and terminates the program. 
    61274    void error(char[] err) { 
    613         if (error_callback !is null) { 
    614             error_callback(err); 
    615         } else { 
    616             this.helpText(); 
     75        try { 
     76            super.error(err); 
     77        } catch(OptionParsingError e) { 
    61778            version (Tango) { 
    618                 Stdout.formatln(err).newline
     79                Stdout.formatln("{0}", e)
    61980            } else { 
    620                 writefln(err ~ "\n"); 
     81                writefln(e); 
    62182            } 
    622         } 
    623         exit(EXIT_FAILURE); 
    624     } 
    625     version (Tango) { 
    626         int toOptInt(char[] s) { 
    627             uint ate; 
    628             int i = cast(int)(.parse(s, 10u, &ate)); 
    629             if (ate != s.length) 
    630                 error("Could not convert '"~s~"' to an integer."); 
    631             return i; 
    632         } 
    633     } else { 
    634         int toOptInt(char[] s) { 
    635             int i; 
    636             try { 
    637                 i = toInt(s); 
    638             } catch (ConvError e) { 
    639                 error("Could not convert '"~s~"' to an integer."); 
    640             } 
    641             return i; 
     83            exit(EXIT_FAILURE); 
    64284        } 
    64385    } 
     86    /+ 
     87    Returns an array of arrays of strings with  useful "help" information about the program's 
     88    options. 
    64489 
    645     /// Displays useful "help" information about the program's options. 
    646     void helpText() { 
    647         int optWidth; 
    648         char[][] optStrs; 
     90    The array looks something like this: 
     91    [ 
     92        ['option', 'help text'], 
     93        ['option', 'help text'], 
     94        ... 
     95    ] 
     96    +/ 
     97    char[][][] helpText() { 
     98        int optWidth = 0; 
     99        char[][][] help = super.helpText(); 
    649100        // Calculate the maximum width of the option lists. 
    650         foreach(i, opt; options) { 
    651             optStrs ~= opt.toString(); 
    652             if (optStrs[i].length > optWidth) { 
    653                 optWidth = optStrs[i].length; 
     101        foreach(i, opt; help) { 
     102            if (opt[0].length > optWidth) { 
     103                optWidth = opt[0].length; 
    654104            } 
    655105        } 
     
    660110            if (this.desc !is null && this.desc != "") Stdout(this.desc).newline; 
    661111            Stdout("\nOptions:").newline; 
    662             foreach(i, opt; options) { 
    663                 padding.length = optWidth - optStrs[i].length; 
    664                 Stdout.formatln("  {0}{1} {2}", optStrs[i], cast(char[])padding, opt.helptext); 
     112            foreach(i, opt; help) { 
     113                padding.length = optWidth - opt[0].length; 
     114                Stdout.formatln("  {0}{1} {2}", opt[0], cast(char[])padding, opt[1]); 
    665115            } 
    666             Stdout.newline; 
    667116        } else { 
    668117            writefln("Usage: %s %s", this.name, this.argdesc); 
    669118            if (this.desc !is null && this.desc != "") writefln(this.desc); 
    670119            writefln("\nOptions:"); 
    671             foreach(i, opt; options) { 
     120            foreach(i, opt; help) { 
    672121                // The commented-out code is a great idea I'll do properly later. 
    673122                //if (opt.helptext.length + optWidth + 3 > 80) { 
    674123                //    writefln("  %s\n\t%s", optStrs[i], opt.helptext); 
    675124                //} else { 
    676                 writefln("  %-*s %s", optWidth, optStrs[i], opt.helptext); 
     125                writefln("  %-*s %s", optWidth, opt[0], opt[1]); 
    677126                //} 
    678127            } 
    679128        } 
    680     } 
    681      
    682     // Checks the passed arg against all the options in the parser. 
    683     // Returns null if no match is found. 
    684     Option matches(char[] arg) { 
    685         foreach(o; options) { 
    686             if (o.matches(arg)) { 
    687                 return o; 
    688             } 
    689         } 
    690         return null; 
    691     } 
    692     version (Tango) { 
    693         char[] getProgramName(char[] path) { 
    694             version(Windows) { 
    695                 char delimiter = '\\'; 
    696             } else { 
    697                 char delimiter = '/'; 
    698             } 
    699             uint idx = locatePrior(path, delimiter); 
    700             if (idx == path.length) return path; 
    701             return path[idx+1 .. $]; 
    702         } 
    703     } else { 
    704         char[] getProgramName(char[] path) { 
    705             version(Windows) { 
    706                 // (Unicode note: ".exe" only contains 4 code units, so this slice 
    707                 // should Just Work.) (Although it remains to be seen how robust 
    708                 // this code actually is.) 
    709                 assert(path[$-4 .. $] == ".exe"); 
    710                 path = path[0 .. $-4]; 
    711             } 
    712             return getBaseName(path); 
    713         } 
    714     } 
    715     /// Parses the passed command-line arguments and returns the results. 
    716     Options parse(char[][] args) { 
    717         this.name = getProgramName(args[0]); 
    718         args = args[1 .. $]; 
    719         Options options = new Options; 
    720         /* 
    721         The issue is this: 
    722  
    723         $ myapp -abc 
    724  
    725         This might be three short opts, or one or two opts, the last of which 
    726         accepts an argument. In the three-opt case, we want to get: 
    727  
    728         $ myapp -a -b -c 
    729  
    730         In the one-opt case, we want: 
    731  
    732         $ myapp -a bc 
    733  
    734         In the two-opt case, we want: 
    735  
    736         $ myapp -a -b c 
    737  
    738         We also want to parse apart "--file=somefile" into "--file somefile" 
    739         */ 
    740         char[] opt, newopt, arg; 
    741         dchar[] opt32; 
    742         int idx; 
    743         Option match; 
    744  
    745         foreach (o; this.options) { 
    746             o.issue_default(options); 
    747         } 
    748  
    749         for (int i=0; i<args.length; ++i) { 
    750             opt = args[i]; 
    751             // -- ends the option list, the remainder is dumped into args 
    752             if (opt == "--") { 
    753                 if (this.leftover_cb !is null) { 
    754                     foreach(a; args[i+1 .. $]) { 
    755                         this.leftover_cb(a); 
    756                     } 
    757                 } else { 
    758                     options.args ~= args[i+1 .. $]; 
    759                 } 
    760                 i = args.length; 
    761             } else if (opt.startswith("--")) { 
    762                 idx = find(opt, '='); 
    763                 if (idx != getNotFound(opt)) { 
    764                     newopt = opt[0 .. idx]; 
    765                     // Stitch out the old arg, stitch in the newopt, arg pair. 
    766                     // (Unicode note: idx+1 works, since we know '=' is a 
    767                     // single code unit.) 
    768                     args = args[0 .. i] ~ [newopt, opt[idx+1 .. $]] ~ args[i+1 .. $]; 
    769                 } else { 
    770                     newopt = opt; 
    771                 } 
    772                 match = matches(newopt); 
    773                 if (match is null) { 
    774                     unknownOptError(newopt); 
    775                 } 
    776                 if (match.hasArg) { 
    777                     if (i == args.length-1) expectedArgError(match.name); 
    778                     arg = args[i+1]; 
    779                     ++i; 
    780                 } else { 
    781                     arg = null; 
    782                 } 
    783                 match.performAction(this, options, args, i, arg); 
    784             } else if (opt.startswith("-")) { 
    785                 if (opt.length >= 2) { 
    786                     opt32 = toUTF32(opt[1 .. $]); 
    787                     foreach (j, c; opt32) { 
    788                         newopt = toUTF8("-" ~ [c]); 
    789                         match = matches(newopt); 
    790                         if (match is null) { 
    791                             unknownOptError(newopt); 
    792                         } 
    793                         if (match.hasArg) { 
    794                             // This is the last char in the group, look to the 
    795                             // next element of args for the arg. 
    796                             if (j == opt32.length-1) { 
    797                                 if (i == args.length-1) expectedArgError(match.name); 
    798                                 arg = args[i+1]; 
    799                                 ++i; 
    800                             // Otherwise, consume the rest of this group for 
    801                             // the arg. 
    802                             } else { 
    803                                 arg = toUTF8(opt32[j+1 .. $]); 
    804                                 match.performAction(this, options, args, i, arg); 
    805                                 break; 
    806                             } 
    807                         } else { 
    808                             arg = null; 
    809                         } 
    810                         match.performAction(this, options, args, i, arg); 
    811                     } 
    812                 } else { 
    813                     unknownOptError(opt); 
    814                 } 
    815             } else { 
    816                 if (this.leftover_cb is null) { 
    817                     options.args ~= opt; 
    818                 } else { 
    819                     this.leftover_cb(opt); 
    820                 } 
    821             } 
    822         } 
    823         return options; 
    824     } 
    825  
    826     /++ 
    827     Overrides the default behavior of leftover arguments, calling this callback 
    828     with them instead of adding them an array. 
    829     +/ 
    830     void leftoverCallback(OptionCallbackArg dg) { 
    831         this.leftover_cb = dg; 
    832     } 
    833     private void wrong_args(char[][] options) { 
    834         if (options.length > 0) { 
    835             throw new OptionError( 
    836                 "Wrong arguments to addOption for option '" 
    837                 ~options[0]~"'." 
    838             ); 
    839         } else { 
    840             throw new OptionError("Found an empty option!"); 
    841         } 
    842     } 
    843     /// 
    844     Option addOption(char[][] options ...) { 
    845         return addOption(new Store(options, null)); 
    846     } 
    847     /// 
    848     Option addOption(char[][] options, char[] name) { 
    849         return addOption(new Store(options, name)); 
    850     } 
    851     /// 
    852     Option addOption(char[][] options, Action action) { 
    853         Option o; 
    854         switch (action) { 
    855             case Action.Store, Action.Append: 
    856                 return addOption(options, null, action, ArgOption.default_type); 
    857             case Action.Count: 
    858                 return addOption(options, null, action); 
    859             case Action.Help: 
    860                 return addOption(new Help(options)); 
    861             default: 
    862                 wrong_args(options); 
    863                 break; 
    864         } 
    865         return addOption(o); 
    866     } 
    867     /// 
    868     Option addOption(char[][] options, ArgType type) { 
    869         return addOption(new Store(options, null, type)); 
    870     } 
    871     /// 
    872     Option addOption(char[][] options, Action action, ArgType type) { 
    873         return addOption(options, null, action, type); 
    874     } 
    875     /// 
    876     Option addOption(char[][] options, char[] name, Action action) { 
    877         Option o; 
    878         switch (action) { 
    879             case Action.Store, Action.Append: 
    880                 return addOption(options, name, action, ArgOption.default_type); 
    881             case Action.Count: 
    882                 o = new Count(options, name); 
    883                 break; 
    884             case Action.SetTrue: 
    885                 o = new SetTrue(options, name); 
    886                 break; 
    887             case Action.SetFalse: 
    888                 o = new SetFalse(options, name); 
    889                 break; 
    890             default: 
    891                 wrong_args(options); 
    892                 break; 
    893         } 
    894         return addOption(o); 
    895     } 
    896     /// 
    897     Option addOption(char[][] options, char[] name, Action action, ArgType type) { 
    898         Option o; 
    899         switch (action) { 
    900             case Action.Store: 
    901                 o = new Store(options, name, type); 
    902                 break; 
    903             case Action.Append: 
    904                 o = new Append(options, name, type); 
    905                 break; 
    906             default: 
    907                 wrong_args(options); 
    908                 break; 
    909         } 
    910         return addOption(o); 
    911     } 
    912     /// 
    913     Option addOption(char[][] options, Action action, char[] const_value) { 
    914         return addOption(options, null, action, const_value); 
    915     } 
    916     /// 
    917     Option addOption(char[][] options, char[] name, char[] const_value) { 
    918         return addOption(options, name, Action.StoreConst, const_value); 
    919     } 
    920     /// 
    921     Option addOption(char[][] options, char[] name, Action action, char[] const_value) { 
    922         Option o; 
    923         switch (action) { 
    924             case Action.StoreConst: 
    925                 o = new StoreConst(options, name, const_value); 
    926                 break; 
    927             case Action.AppendConst: 
    928                 o = new AppendConst(options, name, const_value); 
    929                 break; 
    930             default: 
    931                 wrong_args(options); 
    932                 break; 
    933         } 
    934         return addOption(o); 
    935     } 
    936     /// 
    937     Option addOption(char[][] options, OptionCallback dg) { 
    938         return addOption(new Callback(options, dg)); 
    939     } 
    940     /// 
    941     Option addOption(char[][] options, OptionCallbackArg dg) { 
    942         return addOption(new CallbackArg(options, dg)); 
    943     } 
    944     /// 
    945     Option addOption(char[][] options, OptionCallbackInt dg) { 
    946         return addOption(new CallbackInt(options, dg)); 
    947     } 
    948     /// 
    949     Option addOption(char[][] options, OptionCallbackFancy dg) { 
    950         return addOption(new FancyCallback(options, null, dg)); 
    951     } 
    952     /// 
    953     Option addOption(char[][] options, OptionCallbackFancyArg dg) { 
    954         return addOption(new FancyCallbackArg(options, null, dg)); 
    955     } 
    956     /// 
    957     Option addOption(char[][] options, OptionCallbackFancyInt dg) { 
    958         return addOption(new FancyCallbackInt(options, null, dg)); 
    959     } 
    960     /// 
    961     Option addOption(char[][] options, char[] name, OptionCallbackFancy dg) { 
    962         return addOption(new FancyCallback(options, name, dg)); 
    963     } 
    964     /// 
    965     Option addOption(char[][] options, char[] name, OptionCallbackFancyArg dg) { 
    966         return addOption(new FancyCallbackArg(options, name, dg)); 
    967     } 
    968     /// 
    969     Option addOption(char[][] options, char[] name, OptionCallbackFancyInt dg) { 
    970         return addOption(new FancyCallbackInt(options, name, dg)); 
    971     } 
    972     // Although users certainly /can/ call this, all those overloads are there 
    973     // for a reason. 
    974     Option addOption(Option option) { 
    975         this.options ~= option; 
    976         return option; 
     129        return help; 
    977130    } 
    978131}