OptionParser.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /**
  2. * Copyright (C) 2010 Johannes Weißl <jargon@molb.org>
  3. * License: MIT License
  4. * URL: https://github.com/weisslj/cpp-optparse
  5. */
  6. #include "OptionParser.h"
  7. #include <cstdlib>
  8. #include <algorithm>
  9. #include <complex>
  10. #include <ciso646>
  11. #if defined(ENABLE_NLS) && ENABLE_NLS
  12. # include <libintl.h>
  13. # define _(s) gettext(s)
  14. #else
  15. # define _(s) ((const char *) (s))
  16. #endif
  17. using namespace std;
  18. namespace optparse {
  19. ////////// auxiliary (string) functions { //////////
  20. class str_wrap {
  21. public:
  22. str_wrap(const string& l, const string& r) : lwrap(l), rwrap(r) {}
  23. str_wrap(const string& w) : lwrap(w), rwrap(w) {}
  24. string operator() (const string& s) { return lwrap + s + rwrap; }
  25. const string lwrap, rwrap;
  26. };
  27. template<typename InputIterator, typename UnaryOperator>
  28. static string str_join_trans(const string& sep, InputIterator begin, InputIterator end, UnaryOperator op) {
  29. string buf;
  30. for (InputIterator it = begin; it != end; ++it) {
  31. if (it != begin)
  32. buf += sep;
  33. buf += op(*it);
  34. }
  35. return buf;
  36. }
  37. template<class InputIterator>
  38. static string str_join(const string& sep, InputIterator begin, InputIterator end) {
  39. return str_join_trans(sep, begin, end, str_wrap(""));
  40. }
  41. static string& str_replace(string& s, const string& patt, const string& repl) {
  42. size_t pos = 0, n = patt.length();
  43. while (true) {
  44. pos = s.find(patt, pos);
  45. if (pos == string::npos)
  46. break;
  47. s.replace(pos, n, repl);
  48. pos += repl.size();
  49. }
  50. return s;
  51. }
  52. static string str_replace(const string& s, const string& patt, const string& repl) {
  53. string tmp = s;
  54. str_replace(tmp, patt, repl);
  55. return tmp;
  56. }
  57. static string str_format(const string& str, size_t pre, size_t len, bool running_text = true, bool indent_first = true) {
  58. string s = str;
  59. stringstream ss;
  60. string p;
  61. len -= 2; // Python seems to not use full length
  62. if (running_text)
  63. replace(s.begin(), s.end(), '\n', ' ');
  64. if (indent_first)
  65. p = string(pre, ' ');
  66. size_t pos = 0, linestart = 0;
  67. size_t line = 0;
  68. while (true) {
  69. bool wrap = false;
  70. size_t new_pos = s.find_first_of(" \n\t", pos);
  71. if (new_pos == string::npos)
  72. break;
  73. if (s[new_pos] == '\n') {
  74. pos = new_pos + 1;
  75. wrap = true;
  76. }
  77. if (line == 1)
  78. p = string(pre, ' ');
  79. if (wrap || new_pos + pre > linestart + len) {
  80. ss << p << s.substr(linestart, pos - linestart - 1) << endl;
  81. linestart = pos;
  82. line++;
  83. }
  84. pos = new_pos + 1;
  85. }
  86. ss << p << s.substr(linestart) << endl;
  87. return ss.str();
  88. }
  89. static string str_inc(const string& s) {
  90. stringstream ss;
  91. string v = (s != "") ? s : "0";
  92. long i;
  93. istringstream(v) >> i;
  94. ss << i+1;
  95. return ss.str();
  96. }
  97. static unsigned int cols() {
  98. unsigned int n = 80;
  99. #ifndef _WIN32
  100. const char *s = getenv("COLUMNS");
  101. if (s)
  102. istringstream(s) >> n;
  103. #endif
  104. return n;
  105. }
  106. static string basename(const string& s) {
  107. string b = s;
  108. size_t i = b.find_last_not_of('/');
  109. if (i == string::npos) {
  110. if (b[0] == '/')
  111. b.erase(1);
  112. return b;
  113. }
  114. b.erase(i+1, b.length()-i-1);
  115. i = b.find_last_of("/");
  116. if (i != string::npos)
  117. b.erase(0, i+1);
  118. return b;
  119. }
  120. ////////// } auxiliary (string) functions //////////
  121. ////////// class OptionContainer { //////////
  122. Option& OptionContainer::add_option(const string& opt) {
  123. const string tmp[1] = { opt };
  124. return add_option(vector<string>(&tmp[0], &tmp[1]));
  125. }
  126. Option& OptionContainer::add_option(const string& opt1, const string& opt2) {
  127. const string tmp[2] = { opt1, opt2 };
  128. return add_option(vector<string>(&tmp[0], &tmp[2]));
  129. }
  130. Option& OptionContainer::add_option(const string& opt1, const string& opt2, const string& opt3) {
  131. const string tmp[3] = { opt1, opt2, opt3 };
  132. return add_option(vector<string>(&tmp[0], &tmp[3]));
  133. }
  134. Option& OptionContainer::add_option(const vector<string>& v) {
  135. _opts.resize(_opts.size()+1, Option(get_parser()));
  136. Option& option = _opts.back();
  137. string dest_fallback;
  138. for (vector<string>::const_iterator it = v.begin(); it != v.end(); ++it) {
  139. if (it->substr(0,2) == "--") {
  140. const string s = it->substr(2);
  141. if (option.dest() == "")
  142. option.dest(str_replace(s, "-", "_"));
  143. option._long_opts.insert(s);
  144. _optmap_l[s] = &option;
  145. } else {
  146. const string s = it->substr(1,1);
  147. if (dest_fallback == "")
  148. dest_fallback = s;
  149. option._short_opts.insert(s);
  150. _optmap_s[s] = &option;
  151. }
  152. }
  153. if (option.dest() == "")
  154. option.dest(dest_fallback);
  155. return option;
  156. }
  157. string OptionContainer::format_option_help(unsigned int indent /* = 2 */) const {
  158. stringstream ss;
  159. if (_opts.empty())
  160. return ss.str();
  161. for (list<Option>::const_iterator it = _opts.begin(); it != _opts.end(); ++it) {
  162. if (it->help() != SUPPRESS_HELP)
  163. ss << it->format_help(indent);
  164. }
  165. return ss.str();
  166. }
  167. ////////// } class OptionContainer //////////
  168. ////////// class OptionParser { //////////
  169. OptionParser::OptionParser() :
  170. OptionContainer(),
  171. _usage(_("%prog [options]")),
  172. _add_help_option(true),
  173. _add_version_option(true),
  174. _interspersed_args(true) {}
  175. OptionParser& OptionParser::add_option_group(const OptionGroup& group) {
  176. for (list<Option>::const_iterator oit = group._opts.begin(); oit != group._opts.end(); ++oit) {
  177. const Option& option = *oit;
  178. for (set<string>::const_iterator it = option._short_opts.begin(); it != option._short_opts.end(); ++it)
  179. _optmap_s[*it] = &option;
  180. for (set<string>::const_iterator it = option._long_opts.begin(); it != option._long_opts.end(); ++it)
  181. _optmap_l[*it] = &option;
  182. }
  183. _groups.push_back(&group);
  184. return *this;
  185. }
  186. const Option& OptionParser::lookup_short_opt(const string& opt) const {
  187. optMap::const_iterator it = _optmap_s.find(opt);
  188. if (it == _optmap_s.end())
  189. error(_("no such option") + string(": -") + opt);
  190. return *it->second;
  191. }
  192. void OptionParser::handle_short_opt(const string& opt, const string& arg) {
  193. _remaining.pop_front();
  194. string value;
  195. const Option& option = lookup_short_opt(opt);
  196. if (option._nargs == 1) {
  197. value = arg.substr(2);
  198. if (value == "") {
  199. if (_remaining.empty())
  200. error("-" + opt + " " + _("option requires an argument"));
  201. value = _remaining.front();
  202. _remaining.pop_front();
  203. }
  204. } else {
  205. if (arg.length() > 2)
  206. _remaining.push_front(string("-") + arg.substr(2));
  207. }
  208. process_opt(option, string("-") + opt, value);
  209. }
  210. const Option& OptionParser::lookup_long_opt(const string& opt) const {
  211. list<string> matching;
  212. for (optMap::const_iterator it = _optmap_l.begin(); it != _optmap_l.end(); ++it) {
  213. if (it->first.compare(0, opt.length(), opt) == 0) {
  214. matching.push_back(it->first);
  215. if (it->first.length() == opt.length())
  216. break;
  217. }
  218. }
  219. if (matching.size() > 1) {
  220. string x = str_join_trans(", ", matching.begin(), matching.end(), str_wrap("--", ""));
  221. error(_("ambiguous option") + string(": --") + opt + " (" + x + "?)");
  222. }
  223. if (matching.size() == 0)
  224. error(_("no such option") + string(": --") + opt);
  225. return *_optmap_l.find(matching.front())->second;
  226. }
  227. void OptionParser::handle_long_opt(const string& optstr) {
  228. _remaining.pop_front();
  229. string opt, value;
  230. size_t delim = optstr.find("=");
  231. if (delim != string::npos) {
  232. opt = optstr.substr(0, delim);
  233. value = optstr.substr(delim+1);
  234. } else
  235. opt = optstr;
  236. const Option& option = lookup_long_opt(opt);
  237. if (option._nargs == 1 and delim == string::npos) {
  238. if (not _remaining.empty()) {
  239. value = _remaining.front();
  240. _remaining.pop_front();
  241. }
  242. }
  243. if (option._nargs == 1 and value == "")
  244. error("--" + opt + " " + _("option requires an argument"));
  245. process_opt(option, string("--") + opt, value);
  246. }
  247. Values& OptionParser::parse_args(const int argc, char const* const* const argv) {
  248. if (prog() == "")
  249. prog(basename(argv[0]));
  250. return parse_args(&argv[1], &argv[argc]);
  251. }
  252. Values& OptionParser::parse_args(const vector<string>& v) {
  253. _remaining.assign(v.begin(), v.end());
  254. if (add_help_option()) {
  255. add_option("-h", "--help") .action("help") .help(_("show this help message and exit"));
  256. _opts.splice(_opts.begin(), _opts, --(_opts.end()));
  257. }
  258. if (add_version_option() and version() != "") {
  259. add_option("--version") .action("version") .help(_("show program's version number and exit"));
  260. _opts.splice(_opts.begin(), _opts, --(_opts.end()));
  261. }
  262. while (not _remaining.empty()) {
  263. const string arg = _remaining.front();
  264. if (arg == "--") {
  265. _remaining.pop_front();
  266. break;
  267. }
  268. if (arg.substr(0,2) == "--") {
  269. handle_long_opt(arg.substr(2));
  270. } else if (arg.substr(0,1) == "-" and arg.length() > 1) {
  271. handle_short_opt(arg.substr(1,1), arg);
  272. } else {
  273. _remaining.pop_front();
  274. _leftover.push_back(arg);
  275. if (not interspersed_args())
  276. break;
  277. }
  278. }
  279. while (not _remaining.empty()) {
  280. const string arg = _remaining.front();
  281. _remaining.pop_front();
  282. _leftover.push_back(arg);
  283. }
  284. for (list<Option>::const_iterator it = _opts.begin(); it != _opts.end(); ++it) {
  285. if (it->get_default() != "" and not _values.is_set(it->dest()))
  286. _values[it->dest()] = it->get_default();
  287. }
  288. for (list<OptionGroup const*>::iterator group_it = _groups.begin(); group_it != _groups.end(); ++group_it) {
  289. for (list<Option>::const_iterator it = (*group_it)->_opts.begin(); it != (*group_it)->_opts.end(); ++it) {
  290. if (it->get_default() != "" and not _values.is_set(it->dest()))
  291. _values[it->dest()] = it->get_default();
  292. }
  293. }
  294. return _values;
  295. }
  296. void OptionParser::process_opt(const Option& o, const string& opt, const string& value) {
  297. if (o.action() == "store") {
  298. string err = o.check_type(opt, value);
  299. if (err != "")
  300. error(err);
  301. _values[o.dest()] = value;
  302. _values.is_set_by_user(o.dest(), true);
  303. }
  304. else if (o.action() == "store_const") {
  305. _values[o.dest()] = o.get_const();
  306. _values.is_set_by_user(o.dest(), true);
  307. }
  308. else if (o.action() == "store_true") {
  309. _values[o.dest()] = "1";
  310. _values.is_set_by_user(o.dest(), true);
  311. }
  312. else if (o.action() == "store_false") {
  313. _values[o.dest()] = "0";
  314. _values.is_set_by_user(o.dest(), true);
  315. }
  316. else if (o.action() == "append") {
  317. string err = o.check_type(opt, value);
  318. if (err != "")
  319. error(err);
  320. _values[o.dest()] = value;
  321. _values.all(o.dest()).push_back(value);
  322. _values.is_set_by_user(o.dest(), true);
  323. }
  324. else if (o.action() == "append_const") {
  325. _values[o.dest()] = o.get_const();
  326. _values.all(o.dest()).push_back(o.get_const());
  327. _values.is_set_by_user(o.dest(), true);
  328. }
  329. else if (o.action() == "count") {
  330. _values[o.dest()] = str_inc(_values[o.dest()]);
  331. _values.is_set_by_user(o.dest(), true);
  332. }
  333. else if (o.action() == "help") {
  334. print_help();
  335. std::exit(0);
  336. }
  337. else if (o.action() == "version") {
  338. print_version();
  339. std::exit(0);
  340. }
  341. else if (o.action() == "callback" && o.callback()) {
  342. string err = o.check_type(opt, value);
  343. if (err != "")
  344. error(err);
  345. (*o.callback())(o, opt, value, *this);
  346. }
  347. }
  348. string OptionParser::format_help() const {
  349. stringstream ss;
  350. if (usage() != SUPPRESS_USAGE)
  351. ss << get_usage() << endl;
  352. if (description() != "")
  353. ss << str_format(description(), 0, cols()) << endl;
  354. ss << _("Options") << ":" << endl;
  355. ss << format_option_help();
  356. for (list<OptionGroup const*>::const_iterator it = _groups.begin(); it != _groups.end(); ++it) {
  357. const OptionGroup& group = **it;
  358. ss << endl << " " << group.title() << ":" << endl;
  359. if (group.description() != "") {
  360. unsigned int malus = 4; // Python seems to not use full length
  361. ss << str_format(group.description(), 4, cols() - malus) << endl;
  362. }
  363. ss << group.format_option_help(4);
  364. }
  365. if (epilog() != "")
  366. ss << endl << str_format(epilog(), 0, cols());
  367. return ss.str();
  368. }
  369. void OptionParser::print_help() const {
  370. cout << format_help();
  371. }
  372. void OptionParser::set_usage(const string& u) {
  373. string lower = u;
  374. transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
  375. if (lower.compare(0, 7, "usage: ") == 0)
  376. _usage = u.substr(7);
  377. else
  378. _usage = u;
  379. }
  380. string OptionParser::format_usage(const string& u) const {
  381. stringstream ss;
  382. ss << _("Usage") << ": " << u << endl;
  383. return ss.str();
  384. }
  385. string OptionParser::get_usage() const {
  386. if (usage() == SUPPRESS_USAGE)
  387. return string("");
  388. return format_usage(str_replace(usage(), "%prog", prog()));
  389. }
  390. void OptionParser::print_usage(ostream& out) const {
  391. string u = get_usage();
  392. if (u != "")
  393. out << u << endl;
  394. }
  395. void OptionParser::print_usage() const {
  396. print_usage(cout);
  397. }
  398. string OptionParser::get_version() const {
  399. return str_replace(_version, "%prog", prog());
  400. }
  401. void OptionParser::print_version(ostream& out) const {
  402. out << get_version() << endl;
  403. }
  404. void OptionParser::print_version() const {
  405. print_version(cout);
  406. }
  407. void OptionParser::exit() const {
  408. std::exit(2);
  409. }
  410. void OptionParser::error(const string& msg) const {
  411. print_usage(cerr);
  412. cerr << prog() << ": " << _("error") << ": " << msg << endl;
  413. exit();
  414. }
  415. ////////// } class OptionParser //////////
  416. ////////// class Values { //////////
  417. const string& Values::operator[] (const string& d) const {
  418. strMap::const_iterator it = _map.find(d);
  419. static const string empty = "";
  420. return (it != _map.end()) ? it->second : empty;
  421. }
  422. void Values::is_set_by_user(const string& d, bool yes) {
  423. if (yes)
  424. _userSet.insert(d);
  425. else
  426. _userSet.erase(d);
  427. }
  428. ////////// } class Values //////////
  429. ////////// class Option { //////////
  430. string Option::check_type(const string& opt, const string& val) const {
  431. istringstream ss(val);
  432. stringstream err;
  433. if (type() == "int" || type() == "long") {
  434. long t;
  435. if (not (ss >> t))
  436. err << _("option") << " " << opt << ": " << _("invalid integer value") << ": '" << val << "'";
  437. }
  438. else if (type() == "float" || type() == "double") {
  439. double t;
  440. if (not (ss >> t))
  441. err << _("option") << " " << opt << ": " << _("invalid floating-point value") << ": '" << val << "'";
  442. }
  443. else if (type() == "choice") {
  444. if (find(choices().begin(), choices().end(), val) == choices().end()) {
  445. list<string> tmp = choices();
  446. transform(tmp.begin(), tmp.end(), tmp.begin(), str_wrap("'"));
  447. err << _("option") << " " << opt << ": " << _("invalid choice") << ": '" << val << "'"
  448. << " (" << _("choose from") << " " << str_join(", ", tmp.begin(), tmp.end()) << ")";
  449. }
  450. }
  451. else if (type() == "complex") {
  452. complex<double> t;
  453. if (not (ss >> t))
  454. err << _("option") << " " << opt << ": " << _("invalid complex value") << ": '" << val << "'";
  455. }
  456. return err.str();
  457. }
  458. string Option::format_option_help(unsigned int indent /* = 2 */) const {
  459. string mvar_short, mvar_long;
  460. if (nargs() == 1) {
  461. string mvar = metavar();
  462. if (mvar == "") {
  463. mvar = dest();
  464. transform(mvar.begin(), mvar.end(), mvar.begin(), ::toupper);
  465. }
  466. mvar_short = " " + mvar;
  467. mvar_long = "=" + mvar;
  468. }
  469. stringstream ss;
  470. ss << string(indent, ' ');
  471. if (not _short_opts.empty()) {
  472. ss << str_join_trans(", ", _short_opts.begin(), _short_opts.end(), str_wrap("-", mvar_short));
  473. if (not _long_opts.empty())
  474. ss << ", ";
  475. }
  476. if (not _long_opts.empty())
  477. ss << str_join_trans(", ", _long_opts.begin(), _long_opts.end(), str_wrap("--", mvar_long));
  478. return ss.str();
  479. }
  480. string Option::format_help(unsigned int indent /* = 2 */) const {
  481. stringstream ss;
  482. string h = format_option_help(indent);
  483. unsigned int width = cols();
  484. unsigned int opt_width = min(width*3/10, 36u);
  485. bool indent_first = false;
  486. ss << h;
  487. // if the option list is too long, start a new paragraph
  488. if (h.length() >= (opt_width-1)) {
  489. ss << endl;
  490. indent_first = true;
  491. } else {
  492. ss << string(opt_width - h.length(), ' ');
  493. if (help() == "")
  494. ss << endl;
  495. }
  496. if (help() != "") {
  497. string help_str = (get_default() != "") ? str_replace(help(), "%default", get_default()) : help();
  498. if (type() == "choice") {
  499. help_str = str_replace(help_str, "%choices", str_join("|", _choices.begin(), _choices.end()));
  500. }
  501. ss << str_format(help_str, opt_width, width, false, indent_first);
  502. }
  503. return ss.str();
  504. }
  505. Option& Option::action(const string& a) {
  506. _action = a;
  507. if (a == "store_const" || a == "store_true" || a == "store_false" ||
  508. a == "append_const" || a == "count" || a == "help" || a == "version") {
  509. nargs(0);
  510. } else if (a == "callback") {
  511. nargs(0);
  512. type("");
  513. }
  514. return *this;
  515. }
  516. Option& Option::type(const std::string& t) {
  517. _type = t;
  518. nargs((t == "") ? 0 : 1);
  519. return *this;
  520. }
  521. const std::string& Option::get_default() const {
  522. strMap::const_iterator it = _parser._defaults.find(dest());
  523. if (it != _parser._defaults.end())
  524. return it->second;
  525. else
  526. return _default;
  527. }
  528. ////////// } class Option //////////
  529. }