/src/apps/share/argparse.cpp

http://github.com/imageworks/OpenColorIO · C++ · 528 lines · 341 code · 109 blank · 78 comment · 103 complexity · 7ec7946fb32a0b8a64ab84f04cf1cc30 MD5 · raw file

  1. /*
  2. Copyright 2008 Larry Gritz and the other authors and contributors.
  3. All Rights Reserved.
  4. Based on BSD-licensed software Copyright 2004 NVIDIA Corp.
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions are
  7. met:
  8. * Redistributions of source code must retain the above copyright
  9. notice, this list of conditions and the following disclaimer.
  10. * Redistributions in binary form must reproduce the above copyright
  11. notice, this list of conditions and the following disclaimer in the
  12. documentation and/or other materials provided with the distribution.
  13. * Neither the name of the software's owners nor the names of its
  14. contributors may be used to endorse or promote products derived from
  15. this software without specific prior written permission.
  16. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. (This is the Modified BSD License)
  28. */
  29. #include <cstring>
  30. #include <cctype>
  31. #include <cassert>
  32. #include <cstdlib>
  33. #include <iostream>
  34. #include <cstdarg>
  35. #include <iterator>
  36. #include "strutil.h"
  37. #include "argparse.h"
  38. /*
  39. OIIO_NAMESPACE_ENTER
  40. {
  41. */
  42. class ArgOption {
  43. public:
  44. typedef int (*callback_t) (int, const char**);
  45. ArgOption (const char *str);
  46. ~ArgOption () { }
  47. int initialize ();
  48. int parameter_count () const { return m_count; }
  49. const std::string & name() const { return m_flag; }
  50. const std::string & fmt() const { return m_format; }
  51. bool is_flag () const { return m_type == Flag; }
  52. bool is_sublist () const { return m_type == Sublist; }
  53. bool is_regular () const { return m_type == Regular; }
  54. void add_parameter (int i, void *p);
  55. void set_parameter (int i, const char *argv);
  56. void add_argument (const char *argv);
  57. int invoke_callback () const;
  58. int invoke_callback (int argc, const char **argv) const {
  59. return m_callback ? m_callback (argc, argv) : 0;
  60. }
  61. void set_callback (callback_t cb) { m_callback = cb; }
  62. void found_on_command_line () { m_repetitions++; }
  63. int parsed_count () const { return m_repetitions; }
  64. void description (const char *d) { m_descript = d; }
  65. const std::string & description() const { return m_descript; }
  66. private:
  67. enum OptionType { None, Regular, Flag, Sublist };
  68. std::string m_format; // original format string
  69. std::string m_flag; // just the -flag_foo part
  70. std::string m_code; // paramter types, eg "df"
  71. std::string m_descript;
  72. OptionType m_type;
  73. int m_count; // number of parameters
  74. std::vector<void *> m_param; // pointers to app data vars
  75. callback_t m_callback;
  76. int m_repetitions; // number of times on cmd line
  77. std::vector<std::string> m_argv;
  78. };
  79. // Constructor. Does not do any parsing or error checking.
  80. // Make sure to call initialize() right after construction.
  81. ArgOption::ArgOption (const char *str)
  82. : m_format(str), m_type(None), m_count(0),
  83. m_callback(NULL), m_repetitions(0)
  84. {
  85. }
  86. // Parses the format string ("-option %s %d %f") to extract the
  87. // flag ("-option") and create a code string ("sdf"). After the
  88. // code string is created, the param list of void* pointers is
  89. // allocated to hold the argument variable pointers.
  90. int
  91. ArgOption::initialize()
  92. {
  93. size_t n;
  94. const char *s;
  95. if (m_format.empty() || m_format == "%*") {
  96. m_type = Sublist;
  97. m_count = 1; // sublist callback function pointer
  98. m_code = "*";
  99. m_flag = "";
  100. } else if (m_format == "<SEPARATOR>") {
  101. } else {
  102. // extract the flag name
  103. s = &m_format[0];
  104. assert(*s == '-');
  105. assert(isalpha(s[1]) || (s[1] == '-' && isalpha(s[2])));
  106. s++;
  107. if (*s == '-')
  108. s++;
  109. while (isalnum(*s) || *s == '_' || *s == '-') s++;
  110. if (! *s) {
  111. m_flag = m_format;
  112. m_type = Flag;
  113. m_count = 1;
  114. m_code = "b";
  115. } else {
  116. n = s - (&m_format[0]);
  117. m_flag.assign (m_format.begin(), m_format.begin()+n);
  118. // Parse the scanf-like parameters
  119. m_type = Regular;
  120. n = (m_format.length() - n) / 2; // conservative estimate
  121. m_code.clear ();
  122. while (*s != '\0') {
  123. if (*s == '%') {
  124. s++;
  125. assert(*s != '\0');
  126. m_count++; // adding another parameter
  127. switch (*s) {
  128. case 'd': // 32bit int
  129. case 'g': // float
  130. case 'f': // float
  131. case 'F': // double
  132. case 's': // string
  133. case 'L': // vector<string>
  134. assert (m_type == Regular);
  135. m_code += *s;
  136. break;
  137. case '*':
  138. assert(m_count == 1);
  139. m_type = Sublist;
  140. break;
  141. default:
  142. std::cerr << "Programmer error: Unknown option ";
  143. std::cerr << "type string \"" << *s << "\"" << "\n";
  144. abort();
  145. }
  146. }
  147. s++;
  148. }
  149. }
  150. }
  151. // Allocate space for the parameter pointers and initialize to NULL
  152. m_param.resize (m_count, NULL);
  153. return 0;
  154. }
  155. // Stores the pointer to an argument in the param list and
  156. // initializes flag options to FALSE.
  157. // FIXME -- there is no such initialization. Bug?
  158. void
  159. ArgOption::add_parameter (int i, void *p)
  160. {
  161. assert (i >= 0 && i < m_count);
  162. m_param[i] = p;
  163. }
  164. // Given a string from argv, set the associated option parameter
  165. // at index i using the format conversion code in the code string.
  166. void
  167. ArgOption::set_parameter (int i, const char *argv)
  168. {
  169. assert(i < m_count);
  170. switch (m_code[i]) {
  171. case 'd':
  172. *(int *)m_param[i] = atoi(argv);
  173. break;
  174. case 'f':
  175. case 'g':
  176. *(float *)m_param[i] = (float)atof(argv);
  177. break;
  178. case 'F':
  179. *(double *)m_param[i] = atof(argv);
  180. break;
  181. case 's':
  182. *(std::string *)m_param[i] = argv;
  183. break;
  184. case 'S':
  185. *(std::string *)m_param[i] = argv;
  186. break;
  187. case 'L':
  188. ((std::vector<std::string> *)m_param[i])->push_back (argv);
  189. break;
  190. case 'b':
  191. *(bool *)m_param[i] = true;
  192. break;
  193. case '*':
  194. default:
  195. abort();
  196. }
  197. }
  198. // Call the sublist callback if any arguments have been parsed
  199. int
  200. ArgOption::invoke_callback () const
  201. {
  202. assert (m_count == 1);
  203. int argc = (int) m_argv.size();
  204. if (argc == 0)
  205. return 0;
  206. // Convert the argv's to char*[]
  207. const char **myargv = (const char **) alloca (argc * sizeof(const char *));
  208. for (int i = 0; i < argc; ++i)
  209. myargv[i] = m_argv[i].c_str();
  210. return invoke_callback (argc, myargv);
  211. }
  212. // Add an argument to this sublist option
  213. void
  214. ArgOption::add_argument (const char *argv)
  215. {
  216. m_argv.push_back (argv);
  217. }
  218. ArgParse::ArgParse (int argc, const char **argv)
  219. : m_argc(argc), m_argv(argv), m_global(NULL)
  220. {
  221. }
  222. ArgParse::~ArgParse()
  223. {
  224. for (unsigned int i=0; i<m_option.size(); ++i) {
  225. ArgOption *opt = m_option[i];
  226. delete opt;
  227. }
  228. }
  229. // Top level command line parsing function called after all options
  230. // have been parsed and created from the format strings. This function
  231. // parses the command line (argc,argv) stored internally in the constructor.
  232. // Each command line argument is parsed and checked to see if it matches an
  233. // existing option. If there is no match, and error is reported and the
  234. // function returns early. If there is a match, all the arguments for
  235. // that option are parsed and the associated variables are set.
  236. int
  237. ArgParse::parse (int xargc, const char **xargv)
  238. {
  239. m_argc = xargc;
  240. m_argv = xargv;
  241. for (int i = 1; i < m_argc; i++) {
  242. if (m_argv[i][0] == '-' &&
  243. (isalpha (m_argv[i][1]) || m_argv[i][1] == '-')) { // flag
  244. ArgOption *option = find_option (m_argv[i]);
  245. if (option == NULL) {
  246. error ("Invalid option \"%s\"", m_argv[i]);
  247. return -1;
  248. }
  249. option->found_on_command_line();
  250. if (option->is_flag()) {
  251. option->set_parameter(0, NULL);
  252. } else {
  253. assert (option->is_regular());
  254. for (int j = 0; j < option->parameter_count(); j++) {
  255. if (j+i+1 >= m_argc) {
  256. error ("Missing parameter %d from option "
  257. "\"%s\"", j+1, option->name().c_str());
  258. return -1;
  259. }
  260. option->set_parameter (j, m_argv[i+j+1]);
  261. }
  262. i += option->parameter_count();
  263. }
  264. } else {
  265. // not an option nor an option parameter, glob onto global list
  266. if (m_global)
  267. m_global->invoke_callback (1, m_argv+i);
  268. else {
  269. error ("Argument \"%s\" does not have an associated "
  270. "option", m_argv[i]);
  271. return -1;
  272. }
  273. }
  274. }
  275. return 0;
  276. }
  277. // Primary entry point. This function accepts a set of format strings
  278. // and variable pointers. Each string contains an option name and a
  279. // scanf-like format string to enumerate the arguments of that option
  280. // (eg. "-option %d %f %s"). The format string is followed by a list
  281. // of pointers to the argument variables, just like scanf. All format
  282. // strings and arguments are parsed to create a list of ArgOption objects.
  283. // After all ArgOptions are created, the command line is parsed and
  284. // the sublist option callbacks are invoked.
  285. int
  286. ArgParse::options (const char *intro, ...)
  287. {
  288. va_list ap;
  289. va_start (ap, intro);
  290. m_intro = intro;
  291. for (const char *cur = va_arg(ap, char *); cur; cur = va_arg(ap, char *)) {
  292. if (find_option (cur) &&
  293. strcmp(cur, "<SEPARATOR>")) {
  294. error ("Option \"%s\" is multiply defined", cur);
  295. return -1;
  296. }
  297. // Build a new option and then parse the values
  298. ArgOption *option = new ArgOption (cur);
  299. if (option->initialize() < 0) {
  300. return -1;
  301. }
  302. if (cur[0] == '\0' ||
  303. (cur[0] == '%' && cur[1] == '*' && cur[2] == '\0')) {
  304. // set default global option
  305. m_global = option;
  306. }
  307. // Grab any parameters and store them with this option
  308. for (int i = 0; i < option->parameter_count(); i++) {
  309. void *p = va_arg (ap, void *);
  310. if (p == NULL) {
  311. error ("Missing argument parameter for \"%s\"",
  312. option->name().c_str());
  313. return -1;
  314. }
  315. option->add_parameter (i, p);
  316. if (option == m_global)
  317. option->set_callback ((ArgOption::callback_t)p);
  318. }
  319. // Last argument is description
  320. option->description ((const char *) va_arg (ap, const char *));
  321. m_option.push_back(option);
  322. }
  323. va_end (ap);
  324. return 0;
  325. }
  326. // Find an option by name in the option vector
  327. ArgOption *
  328. ArgParse::find_option (const char *name)
  329. {
  330. for (std::vector<ArgOption *>::const_iterator i = m_option.begin();
  331. i != m_option.end(); i++) {
  332. const char *opt = (*i)->name().c_str();
  333. if (! strcmp(name, opt))
  334. return *i;
  335. // Match even if the user mixes up one dash or two
  336. if (name[0] == '-' && name[1] == '-' && opt[0] == '-' && opt[1] != '-')
  337. if (! strcmp (name+1, opt))
  338. return *i;
  339. if (name[0] == '-' && name[1] != '-' && opt[0] == '-' && opt[1] == '-')
  340. if (! strcmp (name, opt+1))
  341. return *i;
  342. }
  343. return NULL;
  344. }
  345. int
  346. ArgParse::found (const char *option_name)
  347. {
  348. ArgOption *option = find_option(option_name);
  349. if (option == NULL) return 0;
  350. return option->parsed_count();
  351. }
  352. void
  353. ArgParse::error (const char *format, ...)
  354. {
  355. va_list ap;
  356. va_start (ap, format);
  357. m_errmessage = Strutil::vformat (format, ap);
  358. va_end (ap);
  359. }
  360. std::string
  361. ArgParse::geterror () const
  362. {
  363. std::string e = m_errmessage;
  364. m_errmessage.clear ();
  365. return e;
  366. }
  367. void
  368. ArgParse::usage () const
  369. {
  370. const size_t longline = 40;
  371. std::cout << m_intro << '\n';
  372. size_t maxlen = 0;
  373. for (unsigned int i=0; i<m_option.size(); ++i) {
  374. ArgOption *opt = m_option[i];
  375. size_t fmtlen = opt->fmt().length();
  376. // Option lists > 40 chars will be split into multiple lines
  377. if (fmtlen < longline)
  378. maxlen = std::max (maxlen, fmtlen);
  379. }
  380. for (unsigned int i=0; i<m_option.size(); ++i) {
  381. ArgOption *opt = m_option[i];
  382. if (opt->description().length()) {
  383. size_t fmtlen = opt->fmt().length();
  384. if (opt->fmt() == "<SEPARATOR>")
  385. std::cout << opt->description() << '\n';
  386. else if (fmtlen < longline)
  387. std::cout << " " << opt->fmt()
  388. << std::string (maxlen + 2 - fmtlen, ' ')
  389. << opt->description() << '\n';
  390. else
  391. std::cout << " " << opt->fmt() << "\n "
  392. << std::string (maxlen + 2, ' ')
  393. << opt->description() << '\n';
  394. }
  395. }
  396. }
  397. std::string
  398. ArgParse::command_line () const
  399. {
  400. std::string s;
  401. for (int i = 0; i < m_argc; ++i) {
  402. if (strchr (m_argv[i], ' ')) {
  403. s += '\"';
  404. s += m_argv[i];
  405. s += '\"';
  406. } else {
  407. s += m_argv[i];
  408. }
  409. if (i < m_argc-1)
  410. s += ' ';
  411. }
  412. return s;
  413. }
  414. /*
  415. }
  416. OIIO_NAMESPACE_EXIT
  417. */