Arguments.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. //==============================================================================
  2. //
  3. // Arguments - the argument parser class
  4. //
  5. // Copyright (C) 2018 Dick van Oudheusden
  6. //
  7. // This library is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU Lesser General Public
  9. // License as published by the Free Software Foundation; either
  10. // version 3 of the License, or (at your option) any later version.
  11. //
  12. // This library is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. // Lesser General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU Lesser General Public
  18. // License along with this library; if not, write to the Free
  19. // Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. //
  21. //==============================================================================
  22. #include <iostream>
  23. #include <sstream>
  24. #include <cstring>
  25. #include "Arguments.h"
  26. namespace arg
  27. {
  28. // Switch argument implementation
  29. Argument::Argument(Arguments &arguments,
  30. bool optional,
  31. char shortOption,
  32. const std::string &longOption,
  33. const std::string &help) :
  34. _optional(optional),
  35. _withValue(false),
  36. _active(false),
  37. _shortOption(shortOption),
  38. _longOption(longOption),
  39. _valueInfo(""),
  40. _value(""),
  41. _helpInfo(help)
  42. {
  43. arguments.add(*this);
  44. }
  45. // Value argument implementation
  46. Argument::Argument(Arguments &arguments,
  47. bool optional,
  48. char shortOption,
  49. const std::string &longOption,
  50. const std::string &valueInfo,
  51. const std::string &helpInfo,
  52. const std::string &initial) :
  53. _optional(optional),
  54. _withValue(true),
  55. _active(false),
  56. _shortOption(shortOption),
  57. _longOption(longOption),
  58. _valueInfo(valueInfo),
  59. _helpInfo(helpInfo),
  60. _value(initial)
  61. {
  62. arguments.add(*this);
  63. }
  64. Argument::~Argument()
  65. {
  66. }
  67. int Argument::optionWidth() const
  68. {
  69. return _longOption.length() + (_valueInfo.empty() ? 0 : _valueInfo.length() + 1);
  70. }
  71. void Argument::print(int column) const
  72. {
  73. Argument::print(_shortOption, _longOption, _valueInfo, _helpInfo, column);
  74. }
  75. void Argument::print(char shortOption, const std::string &longOption, const std::string &valueInfo, const std::string &helpInfo, int column)
  76. {
  77. std::stringstream str;
  78. str << " ";
  79. if (shortOption != '\0')
  80. {
  81. str << '-' << shortOption;
  82. }
  83. else
  84. {
  85. str << " ";
  86. }
  87. if ((shortOption != '\0') && (!longOption.empty()))
  88. {
  89. str << ", ";
  90. }
  91. else
  92. {
  93. str << " ";
  94. }
  95. if (!longOption.empty())
  96. {
  97. str << "--" << longOption;
  98. if (!valueInfo.empty())
  99. {
  100. str << "=" << valueInfo;
  101. }
  102. }
  103. int extra = column - str.str().size();
  104. str << std::string(extra, ' ') << helpInfo;
  105. std::cout << str.str() << std::endl;
  106. }
  107. // Arguments implementation
  108. Arguments::Arguments(const std::string &usage, const std::string &version, const std::string &help) :
  109. _usage(usage),
  110. _version(version),
  111. _help(help)
  112. {
  113. }
  114. Arguments::~Arguments()
  115. {
  116. }
  117. void Arguments::add(Argument &argument)
  118. {
  119. _arguments.push_back(&argument);
  120. }
  121. bool Arguments::parse(int argc, char *argv[], std::vector<std::string> &unprocessed)
  122. {
  123. // Reset in-use
  124. for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
  125. {
  126. (*argument)->active(false);
  127. }
  128. // Parse the arguments
  129. int arg = 1;
  130. while (arg < argc)
  131. {
  132. size_t arglen = strlen(argv[arg]);
  133. if ((arglen >= 2) && (argv[arg][0] == '-') && (argv[arg][1] == '-'))
  134. {
  135. if (arglen == 2) break; // special: --, stop parsing
  136. if (!parseLongOption(argc, argv, arg)) return false;
  137. }
  138. else if ((arglen >= 1) && (argv[arg][0] == '-'))
  139. {
  140. if (!parseShortOption(argc, argv, arg)) return false;
  141. }
  142. else
  143. {
  144. unprocessed.push_back(argv[arg]);
  145. }
  146. arg++;
  147. }
  148. // Save the arguments after --
  149. for (arg++; arg < argc; arg++)
  150. {
  151. unprocessed.push_back(argv[arg]);
  152. }
  153. // Check the required arguments
  154. for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
  155. {
  156. if (!checkRequiredArgument(*argument)) return false;
  157. }
  158. return true;
  159. }
  160. bool Arguments::parseShortOption(int argc, char *argv[], int &arg)
  161. {
  162. if (argv[arg][1] == '\0')
  163. {
  164. std::cerr << "Invalid argument: " << argv[arg] << std::endl;
  165. return false;
  166. }
  167. if (argv[arg][1] == '?')
  168. {
  169. printHelp();
  170. return false;
  171. }
  172. size_t i = 1;
  173. while (argv[arg][i] != '\0')
  174. {
  175. auto argument = _arguments.begin();
  176. for (; argument != _arguments.end(); ++argument)
  177. {
  178. char shortOption = (*argument)->shortOption();
  179. if ((shortOption != '\0') && (shortOption == argv[arg][i]))
  180. {
  181. (*argument)->active(true);
  182. if ((*argument)->withValue())
  183. {
  184. if (argv[arg][i+1] != '\0')
  185. {
  186. (*argument)->value(&argv[arg][i+1]);
  187. return true;
  188. }
  189. else if ((arg+1) < argc)
  190. {
  191. (*argument)->value(argv[++arg]);
  192. return true;
  193. }
  194. else
  195. {
  196. std::cerr << "Missing value for argument: -" << argv[arg][i] << std::endl;
  197. return false;
  198. }
  199. }
  200. break;
  201. }
  202. }
  203. if (argument == _arguments.end())
  204. {
  205. std::cerr << "Unknown argument: -" << argv[arg][i] << std::endl;
  206. return false;
  207. }
  208. i++;
  209. }
  210. return true;
  211. }
  212. bool Arguments::parseLongOption(int argc, char *argv[], int arg)
  213. {
  214. std::string option(argv[arg] + 2); // skip --
  215. std::string value;
  216. if (option == "help")
  217. {
  218. printHelp();
  219. return false;
  220. }
  221. if (option == "version")
  222. {
  223. std::cout << _version << std::endl;
  224. return false;
  225. }
  226. // Split option and value
  227. std::size_t assign = option.find_first_of("=:");
  228. if (assign != std::string::npos)
  229. {
  230. value = option.substr(assign+1);
  231. option = option.substr(0, assign);
  232. }
  233. auto argument = _arguments.begin();
  234. for (; argument != _arguments.end(); ++argument)
  235. {
  236. if (option == (*argument)->longOption())
  237. {
  238. (*argument)->active(true);
  239. if ((*argument)->withValue())
  240. {
  241. if (!value.empty())
  242. {
  243. (*argument)->value(value);
  244. }
  245. else
  246. {
  247. std::cerr << "Missing value for argument: " << argv[arg] << std::endl;
  248. return false;
  249. }
  250. }
  251. break;
  252. }
  253. }
  254. if (argument == _arguments.end())
  255. {
  256. std::cerr << "Unknown argument: " << argv[arg] << std::endl;
  257. return false;
  258. }
  259. return true;
  260. }
  261. bool Arguments::checkRequiredArgument(Argument *argument)
  262. {
  263. if ((argument->optional()) || (argument->active())) return true;
  264. std::cerr << "Missing argument: ";
  265. if (argument->shortOption() != '\0')
  266. {
  267. std::cerr << '-' << argument->shortOption();
  268. }
  269. else
  270. {
  271. std::cerr << "--" << argument->longOption();
  272. }
  273. std::cerr << std::endl;
  274. return false;
  275. }
  276. void Arguments::printHelp() const
  277. {
  278. std::cout << "Usage: " << _usage << std::endl;
  279. int column = 7; // "help" + 3 spaces
  280. for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
  281. {
  282. column = std::max(column, (*argument)->optionWidth());
  283. }
  284. column += 12; // short option, six spaces and --
  285. Argument::print('?', "help", "", "display this help and exit", column);
  286. Argument::print('\0', "version", "", "display version and exit", column);
  287. for (auto argument = _arguments.begin(); argument != _arguments.end(); ++argument)
  288. {
  289. (*argument)->print(column);
  290. }
  291. std::cout << std::endl << _help << std::endl;
  292. }
  293. }