cmdline_parser.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. #define _XOPEN_SOURCE 600 /* Make sure <string.h> has strdup() */
  2. #include "xmlrpc_config.h" /* prereq for mallocvar.h -- defines __inline__ */
  3. #include <sys/types.h>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include <limits.h>
  8. #include "bool.h"
  9. #include "int.h"
  10. #include "mallocvar.h"
  11. #include "casprintf.h"
  12. #include "getoptx.h"
  13. #include "string_parser.h"
  14. #include "cmdline_parser.h"
  15. #define MAXOPTS 100
  16. struct optionDesc {
  17. const char * name;
  18. enum optiontype type;
  19. bool present;
  20. union {
  21. unsigned int u;
  22. int i;
  23. const char * s;
  24. uint64_t llu;
  25. double d;
  26. } value;
  27. };
  28. struct cmdlineParserCtl {
  29. struct optionDesc * optionDescArray;
  30. unsigned int numOptions;
  31. const char ** argumentArray;
  32. unsigned int numArguments;
  33. };
  34. static struct optionx *
  35. createLongOptsArray(struct optionDesc * const optionDescArray,
  36. unsigned int const numOptions) {
  37. struct optionx * longopts;
  38. MALLOCARRAY(longopts, numOptions+1);
  39. if (longopts != NULL) {
  40. unsigned int i;
  41. for (i = 0; i < numOptions; ++i) {
  42. longopts[i].name = optionDescArray[i].name;
  43. /* If the option takes a value, we say it is optional even
  44. though it never is. That's because if we say it is
  45. mandatory, getopt_long_only() pretends it doesn't even
  46. recognize the option if the user doesn't give a value.
  47. We prefer to generate a meaningful error message when
  48. the user omits a required option value.
  49. */
  50. longopts[i].has_arg =
  51. optionDescArray[i].type == OPTTYPE_FLAG ?
  52. no_argument : optional_argument;
  53. longopts[i].flag = NULL;
  54. longopts[i].val = i;
  55. }
  56. longopts[numOptions].name = 0;
  57. longopts[numOptions].has_arg = 0;
  58. longopts[numOptions].flag = 0;
  59. longopts[numOptions].val = 0;
  60. }
  61. return longopts;
  62. }
  63. static void
  64. parseInt(enum optiontype const type,
  65. const char * const optarg,
  66. unsigned int * const valueUintP,
  67. int * const valueIntP,
  68. const char ** const errorP) {
  69. if (optarg == NULL)
  70. casprintf(errorP, "Option requires a value");
  71. else if (strlen(optarg) == 0)
  72. casprintf(errorP, "Numeric option value is null string");
  73. else {
  74. char * tailptr;
  75. long const longvalue = strtol(optarg, &tailptr, 10);
  76. if (*tailptr != '\0')
  77. casprintf(errorP, "Non-numeric value "
  78. "for numeric option value: '%s'", optarg);
  79. else if (errno == ERANGE || longvalue > INT_MAX)
  80. casprintf(errorP, "Numeric value out of range: %s", optarg);
  81. else {
  82. if (type == OPTTYPE_UINT) {
  83. if (longvalue < 0)
  84. casprintf(errorP, "Unsigned numeric value is "
  85. "negative: %ld", longvalue);
  86. else {
  87. *errorP = NULL;
  88. *valueUintP = (unsigned int) longvalue;
  89. }
  90. } else {
  91. *errorP = NULL;
  92. *valueIntP = (int) longvalue;
  93. }
  94. }
  95. }
  96. }
  97. static void
  98. parseBinUint(const char * const optarg,
  99. uint64_t * const valueP,
  100. const char ** const errorP) {
  101. if (optarg == NULL)
  102. casprintf(errorP, "Option requires a value");
  103. else if (strlen(optarg) == 0)
  104. casprintf(errorP, "Numeric option value is null string");
  105. else {
  106. const char * error;
  107. interpretBinUint(optarg, valueP, &error);
  108. if (error) {
  109. casprintf(errorP, "Invalid numeric option value '%s'. %s",
  110. optarg, error);
  111. strfree(error);
  112. }
  113. }
  114. }
  115. static void
  116. parseFloat(const char * const optarg,
  117. double * const valueP,
  118. const char ** const errorP) {
  119. if (optarg == NULL)
  120. casprintf(errorP, "Option requires a value");
  121. else if (strlen(optarg) == 0)
  122. casprintf(errorP, "Numeric option value is null string");
  123. else {
  124. char * tailptr;
  125. double const doublevalue = strtod(optarg, &tailptr);
  126. if (*tailptr != '\0')
  127. casprintf(errorP, "Non-numeric value "
  128. "for numeric option value: '%s'", optarg);
  129. else if (errno == ERANGE)
  130. casprintf(errorP, "Numeric value out of range: %s", optarg);
  131. else {
  132. *errorP = NULL;
  133. *valueP = doublevalue;
  134. }
  135. }
  136. }
  137. static void
  138. parseOptionValue(const char * const optarg,
  139. struct optionDesc * const optionP,
  140. const char ** const errorP) {
  141. switch (optionP->type) {
  142. case OPTTYPE_FLAG:
  143. *errorP = NULL;
  144. break;
  145. case OPTTYPE_INT:
  146. case OPTTYPE_UINT:
  147. parseInt(optionP->type, optarg, &optionP->value.u, &optionP->value.i,
  148. errorP);
  149. break;
  150. case OPTTYPE_STRING:
  151. if (optarg == NULL)
  152. casprintf(errorP, "Option requires a value");
  153. else {
  154. *errorP = NULL;
  155. optionP->value.s = strdup(optarg);
  156. }
  157. break;
  158. case OPTTYPE_BINUINT:
  159. parseBinUint(optarg, &optionP->value.llu, errorP);
  160. break;
  161. case OPTTYPE_FLOAT:
  162. parseFloat(optarg, &optionP->value.d, errorP);
  163. break;
  164. }
  165. }
  166. static void
  167. processOption(struct optionDesc * const optionP,
  168. const char * const optarg,
  169. const char ** const errorP) {
  170. const char * error;
  171. parseOptionValue(optarg, optionP, &error);
  172. if (error)
  173. casprintf(errorP, "Error in '%s' option: %s", optionP->name, error);
  174. else
  175. optionP->present = true;
  176. }
  177. static void
  178. extractArguments(struct cmdlineParserCtl * const cpP,
  179. unsigned int const argc,
  180. const char ** const argv) {
  181. cpP->numArguments = argc - getopt_argstart();
  182. MALLOCARRAY(cpP->argumentArray, cpP->numArguments);
  183. if (cpP->argumentArray == NULL) {
  184. fprintf(stderr, "Unable to allocate memory for argument array "
  185. "(%u arguments)\n", cpP->numArguments);
  186. abort();
  187. } else {
  188. unsigned int i;
  189. for (i = 0; i < cpP->numArguments; ++i) {
  190. cpP->argumentArray[i] = strdup(argv[getopt_argstart() + i]);
  191. if (cpP->argumentArray[i] == NULL) {
  192. fprintf(stderr, "Unable to allocate memory for Argument %u\n",
  193. i);
  194. abort();
  195. }
  196. }
  197. }
  198. }
  199. void
  200. cmd_processOptions(cmdlineParser const cpP,
  201. int const argc,
  202. const char ** const argv,
  203. const char ** const errorP) {
  204. struct optionx * longopts;
  205. longopts = createLongOptsArray(cpP->optionDescArray, cpP->numOptions);
  206. if (longopts == NULL)
  207. casprintf(errorP, "Unable to get memory for longopts array");
  208. else {
  209. int endOfOptions;
  210. unsigned int i;
  211. *errorP = NULL;
  212. /* Set up initial assumption: No options present */
  213. for (i = 0; i < cpP->numOptions; ++i)
  214. cpP->optionDescArray[i].present = false;
  215. endOfOptions = false; /* initial value */
  216. while (!endOfOptions && !*errorP) {
  217. int const opterr0 = 0;
  218. /* Don't let getopt_long_only() print an error message */
  219. unsigned int longoptsIndex;
  220. const char * unrecognizedOption;
  221. const char * optarg;
  222. getopt_long_onlyx(argc, (char**) argv, "", longopts,
  223. &longoptsIndex, opterr0,
  224. &endOfOptions, &optarg, &unrecognizedOption);
  225. if (unrecognizedOption)
  226. casprintf(errorP, "Unrecognized option: '%s'",
  227. unrecognizedOption);
  228. else {
  229. if (!endOfOptions)
  230. processOption(&cpP->optionDescArray[longoptsIndex], optarg,
  231. errorP);
  232. }
  233. }
  234. if (!*errorP)
  235. extractArguments(cpP, argc, argv);
  236. free(longopts);
  237. }
  238. }
  239. cmdlineParser
  240. cmd_createOptionParser(void) {
  241. struct cmdlineParserCtl * cpP;
  242. MALLOCVAR(cpP);
  243. if (cpP != NULL) {
  244. struct optionDesc * optionDescArray;
  245. cpP->numOptions = 0;
  246. MALLOCARRAY(optionDescArray, MAXOPTS);
  247. if (optionDescArray == NULL) {
  248. free(cpP);
  249. cpP = NULL;
  250. } else
  251. cpP->optionDescArray = optionDescArray;
  252. }
  253. return cpP;
  254. }
  255. void
  256. cmd_destroyOptionParser(cmdlineParser const cpP) {
  257. unsigned int i;
  258. for (i = 0; i < cpP->numOptions; ++i) {
  259. struct optionDesc const option = cpP->optionDescArray[i];
  260. if (option.type == OPTTYPE_STRING && option.present)
  261. strfree(option.value.s);
  262. strfree(option.name);
  263. }
  264. for (i = 0; i < cpP->numArguments; ++i)
  265. strfree(cpP->argumentArray[i]);
  266. free(cpP->optionDescArray);
  267. free(cpP);
  268. }
  269. void
  270. cmd_defineOption(cmdlineParser const cpP,
  271. const char * const name,
  272. enum optiontype const type) {
  273. if (cpP->numOptions < MAXOPTS) {
  274. cpP->optionDescArray[cpP->numOptions].name = strdup(name);
  275. cpP->optionDescArray[cpP->numOptions].type = type;
  276. ++cpP->numOptions;
  277. }
  278. }
  279. static struct optionDesc *
  280. findOptionDesc(struct cmdlineParserCtl * const cpP,
  281. const char * const name) {
  282. struct optionDesc * retval;
  283. unsigned int i;
  284. retval = NULL;
  285. for (i = 0; i < cpP->numOptions && !retval; ++i)
  286. if (strcmp(cpP->optionDescArray[i].name, name) == 0)
  287. retval = &cpP->optionDescArray[i];
  288. return retval;
  289. }
  290. int
  291. cmd_optionIsPresent(cmdlineParser const cpP,
  292. const char * const name) {
  293. struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
  294. bool present;
  295. if (!optionDescP) {
  296. fprintf(stderr, "cmdlineParser called incorrectly. "
  297. "optionIsPresent() called for undefined option '%s'\n",
  298. name);
  299. abort();
  300. } else
  301. present = optionDescP->present;
  302. return present;
  303. }
  304. unsigned int
  305. cmd_getOptionValueUint(cmdlineParser const cpP,
  306. const char * const name) {
  307. struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
  308. unsigned int retval;
  309. if (!optionDescP) {
  310. fprintf(stderr, "cmdlineParser called incorrectly. "
  311. "cmd_getOptionValueUint() called for undefined option '%s'\n",
  312. name);
  313. abort();
  314. } else {
  315. if (optionDescP->type != OPTTYPE_UINT) {
  316. fprintf(stderr, "cmdlineParser called incorrectly. "
  317. "cmd_getOptionValueUint() called for non-unsigned integer "
  318. "option '%s'\n", optionDescP->name);
  319. abort();
  320. } else {
  321. if (optionDescP->present)
  322. retval = optionDescP->value.u;
  323. else
  324. retval = 0;
  325. }
  326. }
  327. return retval;
  328. }
  329. int
  330. cmd_getOptionValueInt(cmdlineParser const cpP,
  331. const char * const name) {
  332. struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
  333. int retval;
  334. if (!optionDescP) {
  335. fprintf(stderr, "cmdlineParser called incorrectly. "
  336. "cmd_getOptionValueInt() called for undefined option '%s'\n",
  337. name);
  338. abort();
  339. } else {
  340. if (optionDescP->type != OPTTYPE_INT) {
  341. fprintf(stderr, "cmdlineParser called incorrectly. "
  342. "cmd_getOptionValueInt() called for non-integer "
  343. "option '%s'\n", optionDescP->name);
  344. abort();
  345. } else {
  346. if (optionDescP->present)
  347. retval = optionDescP->value.i;
  348. else
  349. retval = 0;
  350. }
  351. }
  352. return retval;
  353. }
  354. const char *
  355. cmd_getOptionValueString(cmdlineParser const cpP,
  356. const char * const name) {
  357. struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
  358. const char * retval;
  359. if (!optionDescP) {
  360. fprintf(stderr, "cmdlineParser called incorrectly. "
  361. "cmd_getOptionValueString() called for "
  362. "undefined option '%s'\n",
  363. name);
  364. abort();
  365. } else {
  366. if (optionDescP->type != OPTTYPE_STRING) {
  367. fprintf(stderr, "cmdlineParser called incorrectly. "
  368. "getOptionValueString() called for non-string "
  369. "option '%s'\n", optionDescP->name);
  370. abort();
  371. } else {
  372. if (optionDescP->present) {
  373. retval = strdup(optionDescP->value.s);
  374. if (retval == NULL) {
  375. fprintf(stderr,
  376. "out of memory in cmd_getOptionValueString()\n");
  377. abort();
  378. }
  379. } else
  380. retval = NULL;
  381. }
  382. }
  383. return retval;
  384. }
  385. uint64_t
  386. cmd_getOptionValueBinUint(cmdlineParser const cpP,
  387. const char * const name) {
  388. struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
  389. uint64_t retval;
  390. if (!optionDescP) {
  391. fprintf(stderr, "cmdlineParser called incorrectly. "
  392. "cmd_getOptionValueUint() called for undefined option '%s'\n",
  393. name);
  394. abort();
  395. } else {
  396. if (optionDescP->type != OPTTYPE_BINUINT) {
  397. fprintf(stderr, "cmdlineParser called incorrectly. "
  398. "cmd_getOptionValueBinUint() called for "
  399. "non-OPTTYPE_BINUINT "
  400. "option '%s'\n", optionDescP->name);
  401. abort();
  402. } else {
  403. if (optionDescP->present)
  404. retval = optionDescP->value.llu;
  405. else
  406. retval = 0;
  407. }
  408. }
  409. return retval;
  410. }
  411. double
  412. cmd_getOptionValueFloat(cmdlineParser const cpP,
  413. const char * const name) {
  414. struct optionDesc * const optionDescP = findOptionDesc(cpP, name);
  415. double retval;
  416. if (!optionDescP) {
  417. fprintf(stderr, "cmdlineParser called incorrectly. "
  418. "cmd_getOptionValueInt() called for undefined option '%s'\n",
  419. name);
  420. abort();
  421. } else {
  422. if (optionDescP->type != OPTTYPE_FLOAT) {
  423. fprintf(stderr, "cmdlineParser called incorrectly. "
  424. "cmd_getOptionValueInt() called for non-float "
  425. "option '%s'\n", optionDescP->name);
  426. abort();
  427. } else {
  428. if (optionDescP->present)
  429. retval = optionDescP->value.d;
  430. else
  431. retval = 0.0;
  432. }
  433. }
  434. return retval;
  435. }
  436. unsigned int
  437. cmd_argumentCount(cmdlineParser const cpP) {
  438. return cpP->numArguments;
  439. }
  440. const char *
  441. cmd_getArgument(cmdlineParser const cpP,
  442. unsigned int const argNumber) {
  443. const char * retval;
  444. if (argNumber >= cpP->numArguments)
  445. retval = NULL;
  446. else {
  447. retval = strdup(cpP->argumentArray[argNumber]);
  448. if (retval == NULL) {
  449. fprintf(stderr,
  450. "out of memory in cmd_getArgument()\n");
  451. abort();
  452. }
  453. }
  454. return retval;
  455. }