Changeset 76

Show
Ignore:
Timestamp:
01/11/07 06:45:53 (2 years ago)
Author:
KirkMcDonald
Message:

Built-in help support. '--' now terminates the option list. Can add callback for "leftover" args. Can add hook for "error" calls.

Files:

Legend:

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

    r75 r76  
    2525module optparse; 
    2626 
    27 import std.stdio : writefln
    28 import std.string : split, find
    29 import std.c.stdlib : exit, EXIT_FAILURE
     27import std.stdio : writefln, writef
     28import std.string : split, find, toupper
     29import std.c.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS
    3030import std.conv : toInt, ConvError; 
     31import std.path : getName, getBaseName; 
    3132 
    3233/* 
     
    169170    Action action; 
    170171    ArgType type; 
    171     char[] name
     172    char[] name, argname
    172173    char[] const_value; 
    173174    OptionCallbackArg callback; 
    174175    OptionCallbackInt int_callback; 
    175176    OptionCallback void_callback; 
     177    char[] helptext; 
    176178    this( 
    177179        char[][] shorts, char[][] longs, ArgType type, 
    178180        Action act, char[] name, char[] const_value, 
    179181        OptionCallbackArg dga, OptionCallback dg, 
    180         OptionCallbackInt dgi 
     182        OptionCallbackInt dgi, 
     183        char[] help 
    181184    ) { 
    182185        this.shortopts = shorts; 
     
    185188        this.type = type; 
    186189        this.name = name; 
     190        this.argname = toupper(name); 
     191        this.helptext = help; 
    187192 
    188193        // Perform sanity checks. 
     
    217222        this.void_callback = dg; 
    218223    } 
     224    char[] toString() { 
     225        int optCount = this.shortopts.length + this.longopts.length; 
     226        char[] result; 
     227        bool printed_arg = false; 
     228        foreach(i, opt; this.shortopts ~ this.longopts) { 
     229            result ~= opt; 
     230            if (i < optCount-1) { 
     231                result ~= ", "; 
     232            } else if (this.hasArg()) { 
     233                result ~= "=" ~ toupper(this.argname); 
     234            } 
     235        } 
     236        return result; 
     237    } 
    219238    // Does whatever this option is supposed to do. 
    220     void perform_action(Options results, char[] arg) { 
     239    void perform_action(OptionParser parser, Options results, char[] arg) { 
    221240        int i; 
    222241        if (this.type == ArgType.Integer) { 
     
    257276                break; 
    258277            case Action.Help: 
    259                 writefln("<Print useful help info here.>"); 
     278                parser.help_text(); 
     279                exit(EXIT_SUCCESS); 
    260280                break; 
    261281        } 
     
    263283    bool hasArg() { 
    264284        return this.type != ArgType.None; 
     285    } 
     286    Option help(char[] help) { 
     287        this.helptext = help; 
     288        return this; 
     289    } 
     290    Option argName(char[] argname) { 
     291        this.argname = argname; 
     292        return this; 
    265293    } 
    266294    // Returns true if the passed option string matches this option. 
     
    290318} 
    291319 
     320private void delegate(char[]) error_callback; 
     321void set_error_callback(void delegate(char[]) dg) { 
     322    error_callback = dg; 
     323} 
     324 
    292325void unknown_opt_error(char[] opt) { 
    293326    error("Unknown argument '"~opt~"'"); 
     
    297330} 
    298331void error(char[] err) { 
    299     writefln(err); 
     332    if (error_callback !is null) { 
     333        error_callback(err); 
     334    } else { 
     335        writefln(err); 
     336    } 
    300337    exit(EXIT_FAILURE); 
    301338} 
    302339 
    303340class OptionParser { 
     341    OptionCallbackArg leftover_cb; 
    304342    Option[] options; 
    305  
     343    char[] name, desc, argdesc; 
     344 
     345    this(char[] desc="") { 
     346        this.name = ""; 
     347        this.desc = desc; 
     348        this.argdesc = "[options] args..."; 
     349    } 
     350 
     351    void help_text() { 
     352        int optWidth; 
     353        char[][] optStrs; 
     354        // Calculate the maximum width of the option lists. 
     355        foreach(i, opt; options) { 
     356            optStrs ~= opt.toString(); 
     357            if (optStrs[i].length > optWidth) { 
     358                optWidth = optStrs[i].length; 
     359            } 
     360        } 
     361        writefln("Usage: %s %s", this.name, this.argdesc); 
     362        if (this.desc !is null && this.desc != "") writefln(this.desc); 
     363        writefln("\nOptions:"); 
     364        foreach(i, opt; options) { 
     365            // The commented-out code is a great idea I'll do properly later. 
     366            //if (opt.helptext.length + optWidth + 3 > 80) { 
     367            //    writefln("  %s\n\t%s", optStrs[i], opt.helptext); 
     368            //} else { 
     369            writefln("  %-*s %s", optWidth, optStrs[i], opt.helptext); 
     370            //} 
     371        } 
     372    } 
     373     
    306374    // Checks the passed arg against all the options in the parser. 
    307375    // Returns null if no match is found. 
     
    314382        return null; 
    315383    } 
     384    char[] get_program_name(char[] path) { 
     385        char[] result; 
     386        version(Windows) { 
     387            assert(path[$-4 .. $] == ".exe"); 
     388            result = path[0 .. $-4]; 
     389        } 
     390        return getBaseName(result); 
     391    } 
    316392    Options parse_args(char[][] args) { 
     393        this.name = get_program_name(args[0]); 
    317394        args = args[1 .. $]; 
    318395        Options options = new Options; 
     
    343420        for (int i=0; i<args.length; ++i) { 
    344421            opt = args[i]; 
    345             if (opt.startswith("--")) { 
     422            // -- ends the option list, the remainder is dumped into args 
     423            if (opt == "--") { 
     424                options.args ~= args[i+1 .. $]; 
     425                i = args.length; 
     426            } else if (opt.startswith("--")) { 
    346427                idx = find(opt, '='); 
    347428                if (idx != -1) { 
     
    363444                    arg = null; 
    364445                } 
    365                 match.perform_action(options, arg); 
     446                match.perform_action(this, options, arg); 
    366447            } else if (opt.startswith("-")) { 
    367448                if (opt.length >= 2) { 
     
    383464                            } else { 
    384465                                arg = opt[j+2 .. $]; 
    385                                 match.perform_action(options, arg); 
     466                                match.perform_action(this, options, arg); 
    386467                                break; 
    387468                            } 
     
    389470                            arg = null; 
    390471                        } 
    391                         match.perform_action(options, arg); 
     472                        match.perform_action(this, options, arg); 
    392473                    } 
    393474                } 
    394475            } else { 
    395                 options.args ~= opt; 
     476                if (this.leftover_cb is null) { 
     477                    options.args ~= opt; 
     478                } else { 
     479                    this.leftover_cb(opt); 
     480                } 
    396481            } 
    397482        } 
     
    399484    } 
    400485 
    401     //             options  action               name  type                              const_value  dga   dgv   dgi 
    402     void add_option(char[][] options ...) { 
    403         add_option(options, Action.Store,        null, defaultType(Action.Store),        null,        null, null, null); 
    404     } 
    405     void add_option(char[][] options, char[] name) { 
    406         add_option(options, Action.Store,        name, defaultType(Action.Store),        null,        null, null, null); 
    407     } 
    408     void add_option(char[][] options, Action action) { 
    409         add_option(options, action,              null, defaultType(action),              null,        null, null, null); 
    410     } 
    411     void add_option(char[][] options, ArgType type) { 
    412         add_option(options, Action.Store,        null, type,                             null,        null, null, null); 
    413     } 
    414     void add_option(char[][] options, Action action, ArgType type) { 
    415         add_option(options, action,              null, type,                             null,        null, null, null); 
    416     } 
    417     void add_option(char[][] options, char[] name, Action action) { 
    418         add_option(options, action,              name, defaultType(action),              null,        null, null, null); 
    419     } 
    420     void add_option(char[][] options, char[] name, Action action, ArgType type) { 
    421         add_option(options, action,              name, type,                             null,        null, null, null); 
    422     } 
    423     void add_option(char[][] options, Action action, char[] const_value) { 
    424         add_option(options, action,              null, defaultType(action),              const_value, null, null, null); 
    425     } 
    426     void add_option(char[][] options, char[] name, char[] const_value) { 
    427         add_option(options, Action.StoreConst,   name, defaultType(Action.Store),        const_value, null, null, null); 
    428     } 
    429     void add_option(char[][] options, char[] name, Action action, char[] const_value) { 
    430         add_option(options, action,              name, defaultType(action),              const_value, null, null, null); 
    431     } 
    432     void add_option(char[][] options, OptionCallbackArg dg) { 
    433         add_option(options, Action.Callback,     null, defaultType(Action.Callback),     null,        dg,   null, null); 
    434     } 
    435     void add_option(char[][] options, OptionCallback dg) { 
    436         add_option(options, Action.Callback,     null, ArgType.None,                     null,        null, dg,   null); 
    437     } 
    438     void add_option(char[][] options, OptionCallbackInt dg) { 
    439         add_option(options, Action.Callback,     null, ArgType.Integer,                  null,        null, null, dg); 
     486    void leftover_callback(OptionCallbackArg dg) { 
     487        this.leftover_cb = dg; 
     488    } 
     489    Option add_option(Option option) { 
     490        this.options ~= option; 
     491        return option; 
     492    } 
     493    //                    options  action               name  type                              const_value  dga   dgv   dgi   help 
     494    Option add_option(char[][] options ...) { 
     495        return add_option(options, Action.Store,        null, defaultType(Action.Store),        null,        null, null, null, null); 
     496    } 
     497    Option add_option(char[][] options, char[] name) { 
     498        return add_option(options, Action.Store,        name, defaultType(Action.Store),        null,        null, null, null, null); 
     499    } 
     500    Option add_option(char[][] options, Action action) { 
     501        return add_option(options, action,              null, defaultType(action),              null,        null, null, null, null); 
     502    } 
     503    Option add_option(char[][] options, ArgType type) { 
     504        return add_option(options, Action.Store,        null, type,                             null,        null, null, null, null); 
     505    } 
     506    Option add_option(char[][] options, Action action, ArgType type) { 
     507        return add_option(options, action,              null, type,                             null,        null, null, null, null); 
     508    } 
     509    Option add_option(char[][] options, char[] name, Action action) { 
     510        return add_option(options, action,              name, defaultType(action),              null,        null, null, null, null); 
     511    } 
     512    Option add_option(char[][] options, char[] name, Action action, ArgType type) { 
     513        return add_option(options, action,              name, type,                             null,        null, null, null, null); 
     514    } 
     515    Option add_option(char[][] options, Action action, char[] const_value) { 
     516        return add_option(options, action,              null, defaultType(action),              const_value, null, null, null, null); 
     517    } 
     518    Option add_option(char[][] options, char[] name, char[] const_value) { 
     519        return add_option(options, Action.StoreConst,   name, defaultType(Action.Store),        const_value, null, null, null, null); 
     520    } 
     521    Option add_option(char[][] options, char[] name, Action action, char[] const_value) { 
     522        return add_option(options, action,              name, defaultType(action),              const_value, null, null, null, null); 
     523    } 
     524    Option add_option(char[][] options, OptionCallbackArg dg) { 
     525        return add_option(options, Action.Callback,     null, defaultType(Action.Callback),     null,        dg,   null, null, null); 
     526    } 
     527    Option add_option(char[][] options, OptionCallback dg) { 
     528        return add_option(options, Action.Callback,     null, ArgType.None,                     null,        null, dg,   null, null); 
     529    } 
     530    Option add_option(char[][] options, OptionCallbackInt dg) { 
     531        return add_option(options, Action.Callback,     null, ArgType.Integer,                  null,        null, null, dg,   null); 
    440532    } 
    441533    // Although users certainly /can/ call this, all those overloads are there 
    442534    // for a reason. 
    443     void add_option( 
     535    Option add_option( 
    444536        char[][] options,  
    445537        Action action, char[] name, ArgType type, char[] const_value, 
    446538        OptionCallbackArg callback, OptionCallback vcall, 
    447         OptionCallbackInt icall 
     539        OptionCallbackInt icall, 
     540        char[] help 
    448541    ) { 
    449542        char[][] shortopts; 
    450543        char[][] longopts; 
     544        Option option; 
    451545        foreach (opt; options) { 
    452546            if (opt.length < 2) { 
     
    471565            if (longopts.length > 0) 
    472566                name = longopts[0][2 .. $]; 
     567            else if (shortopts.length > 0) 
     568                name = shortopts[0][1 .. 2]; 
    473569            else 
    474                 name = shortopts[0][1 .. 2]; 
    475         } 
    476         this.options ~= new Option(shortopts, longopts, type, action, name, const_value, callback, vcall, icall); 
    477     } 
    478 
    479  
     570                throw new OptionError( 
     571                    "No options provided to add_option!" 
     572                ); 
     573        } 
     574        option = new Option(shortopts, longopts, type, action, name, const_value, callback, vcall, icall, help); 
     575        this.options ~= option; 
     576        return option; 
     577    } 
     578
     579 
  • misc/opttest.d

    r75 r76  
    99    writefln("value:       ", options.value("value")); 
    1010    writefln("number list: ", options.value_list("list")); 
    11     foreach (a; options.args) { 
    12         writefln("arg:         ", a); 
    13     } 
     11    writefln("args:        ", options.args); 
    1412} 
    1513 
    1614int main(char[][] args) { 
    17     auto parser = new OptionParser
    18     parser.add_option("-f", "--file")
    19     parser.add_option(["-I", "--import"], "importPath", Action.Append)
     15    auto parser = new OptionParser("A test suite for optparser.")
     16    parser.add_option("-f", "--file").help("Stores a single argument.")
     17    parser.add_option(["-I", "--import"], "importPath", Action.Append).help("Adds arguments to a list.")
    2018    parser.add_option(["-c", "--callback"], { 
    2119        writefln("callback encountered"); 
    22     })
     20    }).help("Calls a callback.")
    2321    parser.add_option(["-n", "--number"], (int i) { 
    2422        writefln("number callback: %d * 2 = %d", i, i*2); 
    25     }); 
    26     parser.add_option(["-V", "--value"], ArgType.Integer); 
    27     parser.add_option(["-l", "--list"], Action.Append, ArgType.Integer); 
    28     parser.add_option(["-v", "--verbose"], Action.Count); 
     23    }).help("Calls a callback with a number."); 
     24    parser.add_option(["-V", "--value"], ArgType.Integer).help("Stores a single number").argName("NUMBER"); 
     25    parser.add_option(["-l", "--list"], Action.Append, ArgType.Integer).help("Adds numbers to a list.").argName("NUMBER"); 
     26    parser.add_option(["-v", "--verbose"], Action.Count).help("Counts the number of times this option appears."); 
     27    parser.add_option(["-h", "--help"], Action.Help).help("Displays a help message."); 
    2928    auto options = parser.parse_args(args); 
    3029