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