/// // Copyright (C) 2003, 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include "cmdline.h" #include CmdLine::CmdLine(int argc, char **argv) : seen_double_dash(false) { for(int i = 1; i < argc; i++) arguments.push_back(argv[i]); argi = arguments.begin(); } int CmdLine::add_option(Option option) { int i = options.size() + 1; option.id = i; options.push_back(option); return i; } namespace { struct match { match(char sname) : c(sname), l("-") {} match(const std::string& lname) : c(1) /* 1 is not printable */, l(lname) {} bool operator() (const CmdLine::Option &option) { return option.short_opt == c || option.long_opt == l; } char c; const std::string l; }; } CmdLine::ParsedOptions CmdLine::parse() { ParsedOptions tmp; while(true) { Arg arg = parse_next(); if(!arg.first && arg.second.empty()) return tmp; tmp.push_back(arg); } } void CmdLine::print_usage(std::ostream &out) { // const int term_width = 80; /// \todo Wrap lines if they exceed the term_width using namespace std; vector name_col; for(Options::const_iterator opt = options.begin(); opt != options.end(); opt++) { std::string name = " "; if(opt->short_opt) { name += "-"; name += opt->short_opt; if(opt->param_mode == Option::REQ_PARAM) name += " " + opt->param_name; } if(!opt->long_opt.empty()) { if(opt->short_opt) name += ", "; name += "--" + opt->long_opt; if(opt->param_mode == Option::OPT_PARAM) name += "[=" + opt->param_name + "]"; else if(opt->param_mode == Option::REQ_PARAM && !opt->short_opt) name + "=" + opt->param_name; } name_col.push_back(name); } int name_col_width = 0; for(vector::const_iterator name = name_col.begin(); name != name_col.end(); name++) { if(name->length() > name_col_width) name_col_width = name->length(); } vector::const_iterator name = name_col.begin(); for(Options::const_iterator opt = options.begin(); opt != options.end(); opt++, name++) { out << *name; int padding = 4 + name_col_width - name->length(); for(int i = 0; i < padding; i++) out << " "; out << opt->desc << endl; } } CmdLine::Arg CmdLine::parse_next() { // anything in the queue? if(!arg_queue.empty()) { Arg tmp = arg_queue.front(); arg_queue.pop(); return tmp; } int null_option = 0; // end of arguments if(argi == arguments.end()) return std::make_pair(null_option, ""); // not an option if(argi->at(0) != '-' || seen_double_dash || *argi == "-") return std::make_pair(null_option, *argi++); // -- if(!seen_double_dash && *argi == "--") { seen_double_dash = true; argi++; return parse_next(); } // long? if(argi->at(1) == '-') { std::string tmp = argi->substr(2); unsigned int eq = tmp.find('='); // if(eq == argi->npos) // eq = argi->length(); std::string lname = tmp.substr(0, eq); bool has_value = false; std::string value; if(eq != tmp.npos) { value = tmp.substr(eq + 1); has_value = true; } argi++; Options::const_iterator option = std::find_if(options.begin(), options.end(), match(lname)); if(option == options.end()) throw UnknownOptionError("--" + lname); // check for argument errors switch(option->param_mode) { case Option::REQ_PARAM: if(!has_value) throw ArgumentError("--" + lname); break; case Option::NO_PARAM: if(has_value) throw ArgumentError("--" + lname, true); break; default: break; } return std::make_pair(option->id, value); } // short? for(unsigned int i = 1; i < argi->length(); i++) { char c = argi->at(i); Options::const_iterator option = std::find_if(options.begin(), options.end(), match(c)); if(option == options.end()) throw UnknownOptionError(std::string("-") + c); // push options on a queue, so we can parse this arg in one go if(option->param_mode == Option::REQ_PARAM) { // no optional parameters for short options if(i == argi->length() - 1) { argi++; if(argi == arguments.end()) throw ArgumentError(std::string("-") + c); arg_queue.push(std::make_pair(option->id, *argi)); break; } else { arg_queue.push(std::make_pair(option->id, argi->substr(i + 1))); break; } } else { // not REQ_PARAM arg_queue.push(std::make_pair(option->id, "")); } } argi++; return parse_next(); } // g++ -Wall -g -o cmdline -DCMDLINE_STANDALONE cmdline.cc #ifdef CMDLINE_STANDALONE int main(int argc, char **argv) { CmdLine cmdline(argc, argv); const int p = cmdline.add_option(CmdLine::Option('p', "", CmdLine::Option::NO_PARAM, "Frobnicate it.")); const int f = cmdline.add_option(CmdLine::Option('f', "foo", CmdLine::Option::REQ_PARAM, "Set foo", "FOO")); const int b = cmdline.add_option(CmdLine::Option('b', "bar", CmdLine::Option::OPT_PARAM, "Maybe set bar", "BAR")); const int n = cmdline.add_option(CmdLine::Option('n', "nisse", CmdLine::Option::NO_PARAM, "Don't set Nisse")); const int lisa = cmdline.add_option(CmdLine::Option(0, "lisa", CmdLine::Option::NO_PARAM, "Don't set Lisa")); using namespace std; try { CmdLine::ParsedOptions opts = cmdline.parse(); for(CmdLine::ParsedOptions::const_iterator arg = opts.begin(); arg != opts.end(); arg++) { if(!arg->first) cout << arg->second << endl; else cout << arg->first << ": " << arg->second << endl; } } catch(const CmdLine::Error& e) { cerr << e.what() << endl; cmdline.print_usage(cerr); } } #endif