00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "GetOpt.h"
00026
00027 using namespace std;
00028
00029 Option::Option(char opt,
00030 const std::string &long_opt,
00031 const OptionType &type,
00032 unsigned int flags,
00033 const std::string &defval,
00034 const OptionDesc &optdesc)
00035 {
00036 init(opt, long_opt, type, flags, defval, optdesc);
00037 }
00038
00039 Option::Option(char opt,
00040 const std::string &long_opt,
00041 const OptionType &type,
00042 unsigned int flags,
00043 const OptionDesc &optdesc)
00044 {
00045 if (flags & SetByDefault)
00046 defval = type.getDefaultValue();
00047
00048 init(opt, long_opt, type, flags, defval, optdesc);
00049 }
00050
00051 Option::Option(char opt,
00052 const OptionType &type,
00053 unsigned int flags,
00054 const OptionDesc &optdesc)
00055 {
00056 if (flags & SetByDefault)
00057 defval = type.getDefaultValue();
00058
00059 init(opt, "", type, flags, defval, optdesc);
00060 }
00061
00062
00063 Option::Option(const std::string &long_opt,
00064 const OptionType &type,
00065 unsigned int flags,
00066 const OptionDesc &optdesc)
00067 {
00068 if (flags & SetByDefault)
00069 defval = type.getDefaultValue();
00070
00071 init(0, long_opt, type, flags, defval, optdesc);
00072 }
00073
00074 Option::Option(char opt,
00075 const OptionType &type,
00076 unsigned int flags,
00077 const std::string &defval,
00078 const OptionDesc &optdesc)
00079 {
00080 init(opt, "", type, flags, defval, optdesc);
00081 }
00082
00083 Option::Option(const std::string &long_opt,
00084 const OptionType &type,
00085 unsigned int flags,
00086 const std::string &defval,
00087 const OptionDesc &optdesc)
00088 {
00089 init(0, long_opt, type, flags, defval, optdesc);
00090 }
00091
00092 void Option::init(char _opt,
00093 const std::string &_long_opt,
00094 const OptionType &_type,
00095 unsigned int _flags,
00096 const std::string &_defval,
00097 const OptionDesc &_optdesc)
00098 {
00099 opt = _opt;
00100 long_opt = _long_opt;
00101 type = _type.clone();
00102 flags = _flags;
00103 defval = _defval;
00104
00105 optdesc = _optdesc;
00106 }
00107
00108
00109 GetOpt::GetOpt(const std::string &prog, const Option opts[],
00110 unsigned int opt_cnt, unsigned int flags,
00111 std::ostream &err_os) : prog(prog), flags(flags),
00112 err_os(err_os), _maxlen(0)
00113 {
00114 for (unsigned int n = 0; n < opt_cnt; n++)
00115 add(opts[n]);
00116 }
00117
00118 GetOpt::GetOpt(const std::string &prog, const std::vector<Option> &opts,
00119 unsigned int flags,
00120 std::ostream &err_os) : prog(prog), flags(flags),
00121 err_os(err_os), _maxlen(0)
00122 {
00123 unsigned int opt_cnt = opts.size();
00124
00125 for (unsigned int n = 0; n < opt_cnt; n++)
00126 add(opts[n]);
00127 }
00128
00129 void GetOpt::usage(const std::string &append, const std::string &prefix,
00130 std::ostream &os) const
00131 {
00132 std::vector<Option>::const_iterator begin = opt_v.begin();
00133 std::vector<Option>::const_iterator end = opt_v.end();
00134
00135 os << prefix << prog;
00136 if (prog.length() > 0)
00137 os << ' ';
00138
00139 while (begin != end) {
00140 const Option &opt = (*begin);
00141 if (begin != opt_v.begin())
00142 os << " ";
00143
00144 if (!(opt.getFlags() & Option::Mandatory))
00145 os << '[';
00146
00147 if (opt.getOpt()) {
00148 os << "-" << opt.getOpt();
00149 if (opt.getFlags() & Option::MandatoryValue)
00150 os << " " << opt.getOptionDesc().value_name;
00151 else if (opt.getFlags() & Option::OptionalValue)
00152 os << " [" << opt.getOptionDesc().value_name << "]";
00153 }
00154
00155 if (opt.getLongOpt().length() > 0) {
00156 os << (opt.getOpt() ? "|" : "") << "--" << opt.getLongOpt();
00157 if (opt.getFlags() & Option::MandatoryValue)
00158 os << "=" << opt.getOptionDesc().value_name;
00159 else if (opt.getFlags() & Option::OptionalValue)
00160 os << "[=" << opt.getOptionDesc().value_name << "]";
00161 }
00162
00163 if (!(opt.getFlags() & Option::Mandatory))
00164 os << ']';
00165
00166 ++begin;
00167 }
00168
00169 os << append;
00170 }
00171
00172 void GetOpt::displayHelpOpt(const Option &opt, ostream &os) const
00173 {
00174 if (opt.getOpt()) {
00175 os << "-" << opt.getOpt();
00176 if (opt.getFlags() & Option::MandatoryValue)
00177 os << " " << opt.getOptionDesc().value_name;
00178 else if (opt.getFlags() & Option::OptionalValue)
00179 os << " [" << opt.getOptionDesc().value_name << "]";
00180 }
00181
00182 if (opt.getLongOpt().length() > 0) {
00183 os << (opt.getOpt() ? ", " : "") << "--" << opt.getLongOpt();
00184 if (opt.getFlags() & Option::MandatoryValue)
00185 os << "=" << opt.getOptionDesc().value_name;
00186 else if (opt.getFlags() & Option::OptionalValue)
00187 os << "[=" << opt.getOptionDesc().value_name << "]";
00188 }
00189 }
00190
00191 void GetOpt::adjustMaxLen(const std::string &opt)
00192 {
00193 adjustMaxLen(opt.length());
00194 }
00195
00196 void GetOpt::adjustMaxLen(unsigned int maxlen)
00197 {
00198 (void)getMaxLen();
00199 if (_maxlen < maxlen) {
00200 _maxlen = maxlen;
00201 }
00202 }
00203
00204 unsigned int GetOpt::getMaxLen() const
00205 {
00206 if (_maxlen)
00207 return _maxlen;
00208
00209 std::vector<Option>::const_iterator begin = opt_v.begin();
00210 std::vector<Option>::const_iterator end = opt_v.end();
00211
00212 while (begin != end) {
00213 const Option &opt = *begin;
00214
00215 ostringstream ostr;
00216
00217 displayHelpOpt(opt, ostr);
00218
00219 if (ostr.str().length() > _maxlen)
00220 const_cast<GetOpt*>(this)->_maxlen = ostr.str().length();
00221
00222 ++begin;
00223 }
00224
00225 return _maxlen;
00226 }
00227
00228 void GetOpt::helpLine(const std::string &option, const std::string &detail,
00229 std::ostream &os, const std::string &indent) const
00230 {
00231 os << indent;
00232
00233 unsigned int maxlen = getMaxLen();
00234
00235 ostringstream ostr;
00236 ostr << option;
00237
00238 os << ostr.str();
00239 for (unsigned int n = ostr.str().length(); n < maxlen; n++)
00240 os << ' ';
00241
00242 os << ' ' << detail << '\n';
00243 }
00244
00245 void GetOpt::displayOpt(const std::string &opt, const std::string &detail, std::ostream &os, const std::string &indent) const
00246 {
00247 unsigned int maxlen = getMaxLen();
00248
00249 os << indent;
00250 ostringstream ostr;
00251 ostr << opt;
00252
00253 os << ostr.str();
00254
00255 for (unsigned int n = ostr.str().length(); n < maxlen; n++)
00256 os << ' ';
00257
00258 os << ' ' << detail << endl;
00259 }
00260
00261 void GetOpt::help(std::ostream &os, const std::string &indent) const
00262 {
00263 std::vector<Option>::const_iterator begin = opt_v.begin();
00264 std::vector<Option>::const_iterator end = opt_v.end();
00265
00266 unsigned int maxlen = getMaxLen();
00267
00268 while (begin != end) {
00269 const Option &opt = (*begin);
00270 os << indent;
00271 ostringstream ostr;
00272
00273 displayHelpOpt(opt, ostr);
00274
00275 os << ostr.str();
00276 for (unsigned int n = ostr.str().length(); n < maxlen; n++)
00277 os << ' ';
00278
00279 os << ' ' << opt.getOptionDesc().help << endl;
00280 ++begin;
00281 }
00282 }
00283
00284 void GetOpt::add(const Option &opt)
00285 {
00286 opt_v.push_back(opt);
00287
00288 char c = opt.getOpt();
00289 if (c) {
00290 char s[3];
00291 s[0] = '-';
00292 s[1] = c;
00293 s[2] = 0;
00294 opt_map[s] = opt;
00295 }
00296
00297 const std::string &l = opt.getLongOpt();
00298 if (l.length()) {
00299 std::string s = "--" + l;
00300 long_opt_map[s] = opt;
00301 }
00302 }
00303
00304 bool OptionIntType::checkValue(const std::string &value,
00305 const std::string &prog,
00306 ostream &err_os) const
00307 {
00308 const char *s = value.c_str();
00309 while (*s) {
00310 if (*s < '0' || *s > '9') {
00311 err_os << prog << ": invalid integer value " << s << endl;
00312 return false;
00313 }
00314 s++;
00315 }
00316
00317 return true;
00318 }
00319
00320 bool OptionBoolType::checkValue(const std::string &value,
00321 const std::string &prog,
00322 ostream &err_os) const
00323 {
00324 const char *s = value.c_str();
00325
00326 if (!strcasecmp(s, "true") ||
00327 !strcasecmp(s, "yes") ||
00328 !strcasecmp(s, "on") ||
00329 !strcasecmp(s, "false") ||
00330 !strcasecmp(s, "no") ||
00331 !strcasecmp(s, "off"))
00332 return true;
00333
00334 err_os << prog << ": unexpected boolean value " << s << endl;
00335 return false;
00336 }
00337
00338 bool OptionBoolType::getBoolValue(const std::string &value) const
00339 {
00340 const char *s = value.c_str();
00341
00342 if (!strcasecmp(s, "true") ||
00343 !strcasecmp(s, "yes") ||
00344 !strcasecmp(s, "on"))
00345 return true;
00346
00347 if (!strcasecmp(s, "false") ||
00348 !strcasecmp(s, "no") ||
00349 !strcasecmp(s, "off"))
00350 return false;
00351
00352 return false;
00353 }
00354
00355
00356 int OptionIntType::getIntValue(const std::string &value) const
00357 {
00358 return atoi(value.c_str());
00359 }
00360
00361 bool OptionChoiceType::checkValue(const std::string &value,
00362 const std::string &prog,
00363 ostream &err_os) const
00364 {
00365 std::vector<std::string>::const_iterator begin = choice.begin();
00366 std::vector<std::string>::const_iterator end = choice.end();
00367
00368 while (begin != end) {
00369 if (*begin == value)
00370 return true;
00371 ++begin;
00372 }
00373
00374 err_os << prog << ": invalid value " << value << endl;
00375 return false;
00376 }
00377
00378 unsigned int GetOpt::add_map(const Option &opt, const std::string &_value)
00379 {
00380 if (!opt.getOptionType().checkValue(_value, prog, err_os))
00381 return 1;
00382
00383 OptionValue value(opt.getOptionType(), _value);
00384
00385 char c = opt.getOpt();
00386 if (c) {
00387 char s[2];
00388 s[0] = c;
00389 s[1] = 0;
00390 if (map.find(s) != map.end()) {
00391 if (!map[s].def) {
00392 err_os << prog << ": option -" << s;
00393 if (opt.getLongOpt().length())
00394 err_os << "/--" << opt.getLongOpt();
00395 err_os << " already set" << endl;
00396 return 1;
00397 }
00398 }
00399 map[s] = value;
00400 }
00401
00402 const std::string &s = opt.getLongOpt();
00403 if (s.length()) {
00404 if (map.find(s) != map.end()) {
00405 if (!map[s].def) {
00406 err_os << prog << ": option ";
00407 if (opt.getOpt())
00408 err_os << " -" << opt.getOpt() << "/";
00409 err_os << "--" << opt.getLongOpt();
00410 err_os << " already set" << endl;
00411 return 1;
00412 }
00413 }
00414 map[s] = value;
00415 }
00416 return 0;
00417 }
00418
00419 void GetOpt::init_map(std::map<std::string, Option> &_map)
00420 {
00421 std::map<std::string, Option>::iterator begin = _map.begin();
00422 std::map<std::string, Option>::iterator end = _map.end();
00423
00424 while (begin != end) {
00425 const Option &opt = (*begin).second;
00426 unsigned int flags = opt.getFlags();
00427 if (flags & Option::SetByDefault) {
00428 add_map(opt, opt.getDefaultValue());
00429 }
00430 ++begin;
00431 }
00432 }
00433
00434 unsigned int GetOpt::check_mandatory()
00435 {
00436 unsigned int error = 0;
00437
00438 std::map<std::string, Option>::iterator begin = opt_map.begin();
00439 std::map<std::string, Option>::iterator end = opt_map.end();
00440
00441 bool found = false;
00442 while (begin != end) {
00443 const Option &opt = (*begin).second;
00444 if (opt.getFlags() & Option::Mandatory) {
00445 char s[2];
00446 s[0] = opt.getOpt();
00447 s[1] = 0;
00448 if (map.find(s) == map.end()) {
00449 err_os << prog << ": mandatory option -" << opt.getOpt() << " is missing" <<
00450 endl;
00451 if (opt.getLongOpt().length() > 0)
00452 map[opt.getLongOpt()] = OptionValue();
00453 error++;
00454 }
00455 }
00456 ++begin;
00457 }
00458
00459 begin = long_opt_map.begin();
00460 end = long_opt_map.end();
00461
00462 while (begin != end) {
00463 const Option &opt = (*begin).second;
00464 if (opt.getFlags() & Option::Mandatory) {
00465 if (map.find(opt.getLongOpt()) == map.end()) {
00466 err_os << prog << ": mandatory option -" << opt.getLongOpt() << " is missing" <<
00467 endl;
00468 error++;
00469 }
00470 }
00471 ++begin;
00472 }
00473
00474 return error;
00475 }
00476
00477 bool GetOpt::parseLongOpt(const std::string &arg, const std::string &opt,
00478 std::string *value)
00479 {
00480 if (value) {
00481 if (!strncmp((arg + "=").c_str(),
00482 ("--" + opt + "=").c_str(),
00483 2 + strlen(opt.c_str()) + 1)) {
00484 *value = arg.c_str() + 3 + strlen(opt.c_str());
00485 return true;
00486 }
00487
00488 return false;
00489 }
00490
00491 if (!strcmp(arg.c_str(), ("--" + opt).c_str()))
00492 return true;
00493
00494 return false;
00495 }
00496
00497 static void display(const std::string &action, const std::vector<std::string> &argv)
00498 {
00499 std::cout << action << ":\n";
00500 for (unsigned int n = 0; n < argv.size(); n++) {
00501 std::cout << "#" << n << ": " << argv[n] << '\n';
00502 }
00503 }
00504
00505 bool GetOpt::parse(const std::string &prog, std::vector<std::string> &argv)
00506 {
00507 unsigned int argv_cnt = argv.size();
00508 char ** c_argv = new char*[argv_cnt + 2];
00509
00510 c_argv[0] = strdup(prog.c_str());
00511
00512 for (unsigned int n = 0; n < argv_cnt; n++) {
00513 c_argv[n+1] = strdup(argv[n].c_str());
00514 }
00515
00516 c_argv[argv_cnt + 1] = 0;
00517
00518
00519 int argc = argv_cnt + 1;
00520 int oargc = argc;
00521 bool r = parse(argc, c_argv);
00522
00523 if (oargc != argc) {
00524 argv.clear();
00525 for (unsigned int n = 1; n < argc; n++) {
00526 argv.push_back(c_argv[n]);
00527 }
00528 }
00529
00530
00531
00532 #if 1
00533 for (unsigned int n = 0; n < argc; n++) {
00534 free(c_argv[n]);
00535 }
00536 #else
00537 for (unsigned int n = 0; n < argv_cnt + 1; n++) {
00538 free(c_argv[n]);
00539 }
00540 #endif
00541
00542 delete [] c_argv;
00543 return r;
00544 }
00545
00546 bool GetOpt::parse(int &argc, char *argv[])
00547 {
00548 init_map(opt_map);
00549 init_map(long_opt_map);
00550
00551 unsigned int error = 0;
00552
00553 vector<char *> keep_v;
00554 for (int n = 1; n < argc; n++) {
00555 char *s = argv[n];
00556 if (*s != '-') {
00557 keep_v.push_back(argv[n]);
00558 continue;
00559 }
00560
00561 if (!(flags & PurgeArgv))
00562 keep_v.push_back(argv[n]);
00563
00564 s = strdup(s);
00565
00566 std::map<std::string, Option> *omap;
00567 char *value = 0;
00568 bool long_opt = false;
00569
00570 if (strlen(s) > 2 && *(s+1) == '-') {
00571 long_opt = true;
00572 omap = &long_opt_map;
00573 char *c = strchr(s, '=');
00574 if (c) {
00575 *c = 0;
00576 value = c+1;
00577 }
00578 }
00579 else {
00580 long_opt = false;
00581 omap = &opt_map;
00582 if (n < argc-1) {
00583 if (*argv[n+1] != '-')
00584 value = argv[n+1];
00585 }
00586 }
00587
00588 std::map<std::string, Option>::iterator begin = omap->begin();
00589 std::map<std::string, Option>::iterator end = omap->end();
00590
00591 bool found = false;
00592 while (begin != end) {
00593 if (!strcmp(s, (*begin).first.c_str())) {
00594 const Option &opt = (*begin).second;
00595 found = true;
00596 if (opt.getFlags() & Option::MandatoryValue) {
00597 if (!value) {
00598 err_os << prog << ": missing value after " <<
00599 s << endl;
00600 error++;
00601 ++begin;
00602 continue;
00603 }
00604 error += add_map(opt, value);
00605 if (!long_opt) {
00606 if (!(flags & PurgeArgv))
00607 keep_v.push_back(value);
00608 ++n;
00609 }
00610 }
00611 else if (opt.getFlags() & Option::OptionalValue) {
00612 if (!value)
00613 value = (char *)opt.getDefaultValue().c_str();
00614 error += add_map(opt, value);
00615 if (!long_opt) {
00616 if (!(flags & PurgeArgv))
00617 keep_v.push_back(value);
00618 ++n;
00619 }
00620 }
00621
00622
00623
00624 #if 0
00625 else if (value) {
00626 error += add_map(opt, value);
00627 if (!long_opt)
00628 ++n;
00629 }
00630 #else
00631 else if (value && long_opt) {
00632 error += add_map(opt, value);
00633 if (!long_opt) {
00634 if (!(flags & PurgeArgv))
00635 keep_v.push_back(value);
00636 ++n;
00637 }
00638 }
00639 #endif
00640 else
00641 error += add_map(opt, "true");
00642 }
00643 ++begin;
00644 }
00645
00646 if (!found) {
00647 if (!(flags & SkipUnknownOption)) {
00648 err_os << prog << ": unknown option " << s << endl;
00649 error++;
00650 }
00651 if (flags & PurgeArgv)
00652 keep_v.push_back(argv[n]);
00653 }
00654
00655 free(s);
00656 }
00657
00658 if (!error)
00659 error += check_mandatory();
00660
00661 if (error)
00662 map.erase(map.begin(), map.end());
00663
00664 if (!error) {
00665 vector<char *>::iterator b = keep_v.begin();
00666 vector<char *>::iterator e = keep_v.end();
00667 for (int n = 1; b != e; n++) {
00668 argv[n] = *b;
00669 ++b;
00670 }
00671
00672 argc = keep_v.size() + 1;
00673 argv[argc] = 0;
00674 }
00675
00676 return error == 0;
00677 }
00678
00679
00680 #if 0
00681 int
00682 main(int argc, char *argv[])
00683 {
00684 Option opts[] = {
00685 Option('d', "database", OptionStringType(),
00686 Option::Mandatory|Option::MandatoryValue,
00687 OptionDesc("name of the database", "dbname")),
00688 Option('p', "print", OptionStringType(),
00689 Option::Mandatory|Option::OptionalValue,
00690 "/dev/tty1",
00691 OptionDesc("name of the device", "device")),
00692 Option('w', "read-write"),
00693 Option('x'),
00694 Option("zoulou")
00695 };
00696
00697 GetOpt getopt("test", opts, sizeof(opts)/sizeof(opts[0]),
00698 GetOpt::SkipUnknownOption|GetOpt::PurgeArgv);
00699
00700 getopt.usage();
00701 getopt.help(cerr, "\t", 30);
00702
00703 if (getopt.parse(argc, argv)) {
00704 cout << "argc is: " << argc << endl;
00705 for (int n = 0; n < argc; n++)
00706 cout << "argv[" << n << "] = " << argv[n] << endl;
00707 const GetOpt::Map &map = getopt.getMap();
00708 GetOpt::Map::const_iterator begin = map.begin();
00709 GetOpt::Map::const_iterator end = map.end();
00710 while (begin != end) {
00711 const OptionValue &value = (*begin).second;
00712 cout << (*begin).first << "=" << value.value << endl;
00713 ++begin;
00714 }
00715 }
00716 }
00717 #endif