commandoptions/0040775000076400007640000000000007335712730012255 5ustar oleolecommandoptions/commandoptions.cpp0100664000076400007640000002261007335534126016012 0ustar oleole// Implementation of the command line parsing class. // // Copyright (C) 2001 Ole Laursen // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307 USA. #include #include #include #include #include // for exit #include #include "commandoptions" // true if val is in container con template bool is_in_container(T val, C con) { return std::find(con.begin(), con.end(), val) != con.end(); } void commandoptions::process_command_line(int argc, const char *argv[]) { argument_iterator ait = argument_table.begin(); std::vector processed_flags; int i = 1; while (i < argc) { std::string s(argv[i]); if (s[0] == '-') { // option encountered if (s.size() == 1) // ensure we got some meat to work on throw commandoptions_error("'-' specified without option name"); if (s[1] == '-') { // long option if (s.size() == 2) // ensure we got some meat to work on throw commandoptions_error("'--' specified without option name"); std::string name = s.substr(2); // catch the built-in options if (name == "help") print_help(argv[0]); if (name == "usage") print_usage(argv[0]); // search flag table for (flag_iterator it = flag_table.begin(); it != flag_table.end(); ++it) if (it->long_name == name) { // make sure we don't get "-f -f" = "" if (!is_in_container(it, processed_flags)) { *(it->par) = !(*(it->par)); // reverse flag processed_flags.push_back(it); // remember this flag } goto next_token; } // then the option table for (option_iterator it = option_table.begin(); it != option_table.end(); ++it) if (it->long_name == name) { if (++i >= argc) // check that there's one more waiting throw commandoptions_error("option '--" + name + "' specified without argument"); else if (!it->par->update(argv[i])) throw commandoptions_error("invalid argument '" + std::string(argv[i]) + "' for option '--" + name + "'"); goto next_token; } // if we get here, the option wasn't recognized throw commandoptions_error("unrecognized option '" + s + "'"); } else { // short option(s) bool has_had_option = false; // loop through entire string in case some flags were grouped unsigned int j = 1; while (j < s.length()) { char name = s[j]; // catch the built-in short help option if (name == '?') print_help(argv[0]); // search flag table for (flag_iterator it = flag_table.begin(); it != flag_table.end(); ++it) if (it->short_name == name) { if (!is_in_container(it, processed_flags)) { *(it->par) = !(*(it->par)); // reverse flag processed_flags.push_back(it); // remember the flag } goto next_option_in_group; } // then the option table for (option_iterator it = option_table.begin(); it != option_table.end(); ++it) if (it->short_name == name) { if (has_had_option) throw commandoptions_error("more than one option in the option group '" + s + "' requires an argument"); else if (++i >= argc) // ensure that there's one more waiting throw commandoptions_error(std::string("option '-") + name + "' specified without argument"); else if (!it->par->update(argv[i])) throw commandoptions_error("invalid argument '" + std::string(argv[i]) + "' for option '-" + name + "'"); has_had_option = true; goto next_option_in_group; } // if we get here, the option wasn't recognized throw commandoptions_error(string("unrecognized option '-") + name + "'"); next_option_in_group: ++j; } } } else { // just a normal argument if (ait == argument_table.end()) throw commandoptions_error("too many arguments specified (last one processed is '" + s + "')"); else if (!ait->par->update(s)) throw commandoptions_error("invalid argument '" + s + "' passed as " + requirify(ait->name)); else ++ait; // prepare for next argument } next_token: ++i; } // check that we got enough arguments if (ait != argument_table.end()) { std::string missing; while (ait != argument_table.end()) { // build up list of missing arguments missing += " '" + requirify(ait->name) + "'"; ++ait; } throw commandoptions_error("not enough arguments specified, missing" + missing); } } // FIXME: switch to dedicated functions for handling encountered options, the // above function is much too long std::string commandoptions::strip_path(std::string s) { #ifdef HAVE_DOS return s.substr(s.rfind('\\') + 1); #else return s.substr(s.rfind('/') + 1); #endif } void commandoptions::print_usage(std::string executable) { // print usage and program name std::cerr << "Usage: " << strip_path(executable); // gather and print flags std::string shorts = "-"; // we sort the flags into short and long ones std::string longs; for (flag_iterator i = flag_table.begin(); i != flag_table.end(); ++i) { if (i->short_name != '\0') shorts += i->short_name; else longs += ' ' + optionalify("--" + i->long_name); } // insert -? (for help) shorts += '?'; std::cerr << ' ' << optionalify(shorts) << longs; // print options for (option_iterator i = option_table.begin(); i != option_table.end(); ++i) if (i->short_name != '\0') std::cerr << ' ' << optionalify(string("-") + i->short_name + " " + i->arg_name); else std::cerr << ' ' << optionalify("--" + i->long_name + " " + i->arg_name); // then print --usage so it doesn't clutter up the more interesting options std::cerr << ' ' + optionalify("--usage"); // and finally print arguments for (argument_iterator i = argument_table.begin(); i != argument_table.end(); ++i) std::cerr << ' ' << requirify(i->name); std::cerr << std::endl; std::exit(1); } // helper for securing right indentation void indent_for_description(int current_indent) { const int description_indentation = 24; int extra_space = description_indentation - current_indent; if (extra_space > 0) std::cerr << std::string(extra_space, ' '); else std::cerr << ' '; // we always need at least one space } void commandoptions::print_help(std::string executable) { // print "Usage: programname [OPTIONS...]" std::cerr << "Usage: " << strip_path(executable) << ' ' << optionalify("OPTIONS..."); // concatenate argument names for (argument_iterator i = argument_table.begin(); i != argument_table.end(); ++i) std::cerr << ' ' << requirify(i->name); std::cerr << std::endl; if (flag_table.size() > 0 || option_table.size() > 0) std::cerr << "\nOptions:\n"; // then list the flags and options for (flag_iterator i = flag_table.begin(); i != flag_table.end(); ++i) { std::cerr << " "; // indentation int x = 2; // we need to keep track of how far we've reached if (i->short_name != '\0') { std::cerr << '-' << i->short_name; x += 2; } if (i->long_name.length() > 0) { if (i->short_name != '\0') { std::cerr << ", "; // add separator x += 2; } std::cerr << "--" << i->long_name; x += 2 + i->long_name.length(); } indent_for_description(x); std::cerr << i->des << std::endl; } for (option_iterator i = option_table.begin(); i != option_table.end(); ++i) { std::cerr << " "; int x = 2; if (i->short_name != '\0') { std::cerr << '-' << i->short_name; x += 2; } if (i->long_name.length() > 0) { if (i->short_name != '\0') { // output separator std::cerr << ", "; x += 2; } std::cerr << "--" << i->long_name; x += 2 + i->long_name.length(); } // output the name of the option argument std::cerr << ' ' << i->arg_name; x += 1 + i->arg_name.length(); indent_for_description(x); std::cerr << i->des << std::endl; } bool first_argument = true; // output descriptions of arguments, if any for (argument_iterator i = argument_table.begin(); i != argument_table.end(); ++i) { if (i->des.length() > 0) { if (first_argument) // prefix the first argument std::cerr << "\nArguments:\n"; first_argument = false; std::cerr << " " << i->name; indent_for_description(2 + i->name.length()); std::cerr << i->des << std::endl; } } std::cerr << "\nHelp options:\n"; // and finally output help about help options std::string s = " -h, --help"; std::cerr << s; indent_for_description(s.length()); std::cerr << "Show this help message\n"; s = " --usage"; std::cerr << s; indent_for_description(s.length()); std::cerr << "Display brief usage message\n"; std::exit(1); } commandoptions/INSTALL0100664000076400007640000000240707335712430013303 0ustar oleoleInstallation of the library ought to be simple. If you're using GNU Make together with GCC, start by running make in the source directory. That will give you a test program. Play around with that (hint: try "./test --help" or something similar) to get the feel of what the library is capable of doing, then read the README to get some theoretical background and finally go back to 'test.cpp' to see some full example code. You may want to copy & paste (otherwise known as kill & yank) some of this code to your own project to get you started. Then you're basically done. Otherwise there's no real build system yet. To link with the library, simply add 'commandoptions.cpp' as a source file to your project build system (make or whatever). I reckon it isn't very elegant but at least it will work with whatever compiler and IDE thingie you're using. To build the test program without make/GCC, simply start a new project with 'test.cpp' and 'commandoptions.cpp' as source files. The library has currently only been tested with GCC v. 2.96 (as shipped by Red Hat) so unfortunately I can't yet guarantee that it is fully standard compliant - it might for instance be necessary to add in some std::'s here and there. Please report such bugs so they can be fixed as soon as possible. commandoptions/README0100664000076400007640000002606307335712660013143 0ustar oleoleCommandoptions v. 1.0 ~~~~~~~~~~~~~~~~~~~~~ ...is a class for parsing the options and parameters passed to your program from the command line. It has no requirements besides a relatively standard conforming C++ compiler. A recent GCC will do. The class has been designed with two goals in mind: - to be easy to use - to take over as much as possible of the tedious and boring work of defining and parsing arguments Consequently the class will automatically construct the options and arguments with the least possible given information, format usage and help messages ('--usage' and '-?/--help') which documents the program interface to the user, and report errors together with a meaningful explanation when the supplied arguments are wrong. So when you have registered with the class the variables you want users of your program to be able to influence, the entire command line interface has essentially been taken care of. All code is written by me, Ole Laursen, and released under the terms of the GNU Library General Public License. See COPYING for details. Free software is cool. Tutorial ~~~~~~~~ Usage is (deliberately) very simple: 1. Include the 'commandoptions' header: #include Depending on how you have installed the library, you might need to alter this directive. 2. Define a commandoptions object: commandoptions op; 3. And define the variables which you want the command line options to affect: int foo = 2; bool baz_bar = false; std::string outfile; Options that the user doesn't specify are left alone so it might be a good idea to initialize the variables to sensible defaults before processing the command line - here the default for 'foo' is 2. 4. Register the various arguments and options needed: op.register_option(foo, "foo", 'f', "How many times to foo the bar", "NUMBER"); op.register_flag(baz_bar, "bazzing", 'b', "Turn on bazzing of bars"); op.register_argument(outfile, "output-file", "Processed bars are put here"); 5. Process the command line by feeding argc and argv to the object: op.process_command_line(argc, argv); Since various errors might occur, you may want to wrap this in a try block. Upon error, the method will throw a commandoptions_error with the member function what() which returns a meaningful description of the problem: try { op.process_command_line(argc, argv); } catch (commandoptions_error &ex) { std::cerr << "Error: " << ex.what << std::endl; exit(1); } 6. Use the registered variables foobar(foo, bars); if (baz_bar) baz(bars); ofstream out(outfile); out << bars; Library API ~~~~~~~~~~~ Three different sorts of parameters are currently supported: options, flags and arguments. The first two is based on the notion of a switch which is either a '-' followed by a character or a '--' followed by a string. The following command line contains two switches: foobar --foo 9 -b The basic idea in the library is that you register the variables you want to be configurable by the user - if they are specified by the user, they will then be updated by the commandoptions object, else they are left alone. Thus it is probably a good idea to specify a sensible default value for the variables you use for options; for flags it is required since they reverse the default value so if you don't specify that, you get unpredictable behaviour; for ordinary arguments it doesn't matter since they are mandatory and must be supplied by the user anyway. Options are in this library defined to be an optional switch followed by a (mandatory) argument. The prototype for registration is (the standard namespace is implicitly assumed to be imported to improve readability): template void register_option(T &variable, string long_name, char short_name, string description, string argument_name) The arguments are: the variable to update, a long option name (becomes '--xxx'), a short name (becomes '-y'), and a short description which is displayed in the help message. To get meaningful help messages the name of the mandatory argument for the option is specied as the last parameter. The register call in the tutorial above will produce '[-f NUMBER]' in the usage message. Flags are defined to be optional switches which simply reverses the value of the variable when supplied - flags operate on Boolean variables. Thus if the flag for 'baz_bar' is specified in the command line for a call to the tutorial program above, the value of the variable will be true after calling 'process_command_line'. The prototype for registration is void register_flag(bool &variable, string long_name, char short_name, string description) The arguments for flag registration are the same as for options, though of course with the difference that the last argument, the name of the option argument, is omitted since flags don't take arguments. Arguments are defined to be mandatory ordinary command line arguments (i.e. unlike switches they aren't prefixed with dashes). In the following command line there are two arguments: mv bomb-manufacturing.html doc/howto-dig-potatoes.html The prototype for registering arguments is template void register_argument(T &variable, string name, string description) And the registration arguments are: the variable to set, the name of the argument for use in error and usage reporting, and a description of the argument for the help message. For flags and options, either the long name or short name may be omitted by passing the empty string "" or the null character '\0' respectively to the register method. Option argument names and all descriptions may also be omitted/squashed in a similar way. Supported command line concepts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As mentioned in the introductionary section, the commandoptions class will supply your program with a complete interface to the command line. The argument parsing follows approximately the GNU standard since it is quite sensible and, well..., a standard on the GNU/Linux platform. This means that switches have either the long form '--name-of-switch' which consist of two dashes followed by a name string (that mustn't begin with a space), or the short one '-c' which consist of a single dash followed by a single character. Options which take an argument consist of the long or the short form of the option followed by whitespace followed by the argument, like '--name-of-option argument' or '-c argument'. Ordinary program arguments are recognized by the fact that they don't begin with a dash, and may appear anywhere between the options as long as the relative order among the arguments is preserved, thus the following command lines are equal program --boink -t blip beep program --boink blip beep -t program blip --boink beep -t provided 'boink' and 't' are flags. All options are processed from left to right so the rightmost supplied option will overwrite any previous specifications. Consequently program --sound boink --sound blip results in the variable behind the option 'sound' getting the value "blip". If the same flag is specified multiple times, all but one is ignored. So if the variable behind the flag 'think' has a default value of 'false', the two commands program --think program --think --think are the same way of giving it the value 'true' (although the second is somewhat more cumbersome). Short flags may be combined by putting multiple characters after the single dash, like this: '-trf'. Any number of flags may be combined this way, but since options take an argument, only one option may be stuffed in such group. If '-o' and '-i' are two options, the following command lines are all equal program -s -q -w -o floink -i boink program -sq -w -o floink -i boink program -sqw -o floink -i boink program -sqwo floink -i boink program -sqwi boink -o floink A few swiches are generated automatically by the library from the information you supply when you register switches. Currently these are '--usage', '--help' and '-?'. When one of these switches are met, a help message is output on std::cerr and the program is terminated with the exit code 1. '--usage' outputs a very terse listing of the available flags, options (short names are used when available) and necessary arguments, like Usage: program [-t?] [-o OUTPUT] [--usage] for a program with the flag '-t', the option '-o' and the argument 'inputfile'. '--help' and '-?' are the same and output a complete listing of the available switches with both long and short names together with the registered descriptions. If an ordinary argument has a description it will also get a special entry for that. So if your arguments are selfdescriptive, like 'inputfile' or 'outputfile', thus making any comment superfluous, feel free to omit the description (by passing an empty string). This avoids the extra entry which saves some space in the output and makes it easier for the user to get an overview of the program interface. Invalid supplied command lines at runtime results in the library trowing an commandoptions_error exception which has the member function what() that returns a const char * with a description of the error. A lot of different things are detected, e.g. wrong number of arguments, unknown options, etc. An important thing is that invalid arguments (either option arguments or ordinary program arguments) cause an exception. If, for instance, an option '--pointless-sounds' which is registered to update an integer variable is passed a string that can't be converted to a number, as in '--pointless-sounds three', the parsing will fail and throw an exception. This releaves the burden of parsing (and reporting errors in connection with) arguments from your shoulders. If you really need the kind of flexibility that homebrewed parsing gives, you can always simply register a string and then do the parsing with that afterwards. Though another, and better, option is to define a new type, e.g. a class, for the argument and then define the input operator >> for the type; internally the library puts the argument string inside an istringstream and then uses '>>' to extract it. This also implies that userdefined types for which operator >> already has been defined will work seamlessly with this library. Contact info ~~~~~~~~~~~~ Suggestions, criticism or any other sorts of comments are welcome - my email address is Ole Laursen Some people think the last part of the address is a joke; it isn't, it's the truth. The .dk suffix means I live in Denmark. Which also means I normally speak Danish - that's the cause (and the only one, of course) of all spelling mistakes & other linguistic errors in this documentation. I was wondering if I ought to include my snail mail address here too, but since you don't need that unless you're going to date me, I've left it out (in my experience not many C++ coders are female anyway). You can probably find the library home at http://www.sunsite.dk/olau/commandoptions/ in case you want to know whether a new version is out. Or if you want to enjoy my web art. commandoptions/commandoptions0100664000076400007640000001147507335532747015247 0ustar oleole// A -*- C++ -*- class for parsing command line options. // // Copyright (C) 2001 Ole Laursen // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307 USA. #ifndef COMMANDOPTIONS_HEADER #define COMMANDOPTIONS_HEADER #include #include #include #include class commandoptions { public: // register an option with pointer to variable, long and short argument names, a // description of the option and the name of the argument to the option template void register_option(T &par, std::string long_name, char short_name, std::string des, std::string arg_name) { option_table.push_back(option(new updater(par), long_name, short_name, des, arg_name)); } void register_flag(bool &par, std::string long_name, char short_name, std::string des) { flag_table.push_back(flag(par, long_name, short_name, des)); } template void register_argument(T &par, std::string name, std::string des) { argument_table.push_back(argument(new updater(par), name, des)); } void process_command_line(int argc, const char *argv[]); ~commandoptions() { // make sure dynamically allocated stuff is cleaned up for (vector::iterator it = clean_up_table.begin(); it != clean_up_table.end(); ++it) delete (*it); } private: // member helpers std::string requirify(std::string s) { return '<' + s + '>'; } std::string optionalify(std::string s) { return '[' + s + ']'; } std::string strip_path(std::string s); void print_usage(std::string executable); void print_help(std::string executable); // private data structures // first a few classes for updating a variable from a stream class updaterbase { public: virtual bool update(std::string s) = 0; }; template class updater: public updaterbase { public: updater(T &par): var(par) {} // update the contained variable through reference - return false upon failure virtual bool update(std::string s) { std::istringstream is(s); is >> var; if (!is) return false; else return true; } private: T &var; }; // a structure for keeping a command line option struct option { updaterbase *par; std::string long_name, des, arg_name; char short_name; option(updaterbase *p, std::string ln, char sn, std::string d, std::string an): par(p), long_name(ln), des(d), arg_name(an), short_name(sn) {} option(const option &o): par(o.par), long_name(o.long_name), des(o.des), arg_name(o.arg_name), short_name(o.short_name) {} }; // one for keeping a flag struct flag { bool *par; std::string long_name, des; char short_name; flag(bool &p, std::string ln, char sn, std::string d): par(&p), long_name(ln), des(d), short_name(sn) {} }; // and one for keeping an argument struct argument { updaterbase *par; std::string name, des; argument(updaterbase *p, std::string n, std::string d): par(p), name(n), des(d) {} argument(const argument &a): par(a.par), name(a.name), des(a.des) {} }; std::vector