enum.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /**
  2. * \file enumeration.c
  3. *
  4. * Handle options with enumeration names and bit mask bit names
  5. * for their arguments.
  6. *
  7. * @addtogroup autoopts
  8. * @{
  9. */
  10. /*
  11. * This routine will run run-on options through a pager so the
  12. * user may examine, print or edit them at their leisure.
  13. *
  14. * This file is part of AutoOpts, a companion to AutoGen.
  15. * AutoOpts is free software.
  16. * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
  17. *
  18. * AutoOpts is available under any one of two licenses. The license
  19. * in use must be one of these two and the choice is under the control
  20. * of the user of the license.
  21. *
  22. * The GNU Lesser General Public License, version 3 or later
  23. * See the files "COPYING.lgplv3" and "COPYING.gplv3"
  24. *
  25. * The Modified Berkeley Software Distribution License
  26. * See the file "COPYING.mbsd"
  27. *
  28. * These files have the following sha256 sums:
  29. *
  30. * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
  31. * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
  32. * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
  33. */
  34. static void
  35. enum_err(tOptions * pOpts, tOptDesc * pOD,
  36. char const * const * paz_names, int name_ct)
  37. {
  38. size_t max_len = 0;
  39. size_t ttl_len = 0;
  40. int ct_down = name_ct;
  41. int hidden = 0;
  42. /*
  43. * A real "pOpts" pointer means someone messed up. Give a real error.
  44. */
  45. if (pOpts > OPTPROC_EMIT_LIMIT)
  46. fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName,
  47. pOD->optArg.argString, pOD->pz_Name);
  48. fprintf(option_usage_fp, zValidKeys, pOD->pz_Name);
  49. /*
  50. * If the first name starts with this funny character, then we have
  51. * a first value with an unspellable name. You cannot specify it.
  52. * So, we don't list it either.
  53. */
  54. if (**paz_names == 0x7F) {
  55. paz_names++;
  56. hidden = 1;
  57. ct_down = --name_ct;
  58. }
  59. /*
  60. * Figure out the maximum length of any name, plus the total length
  61. * of all the names.
  62. */
  63. {
  64. char const * const * paz = paz_names;
  65. do {
  66. size_t len = strlen(*(paz++)) + 1;
  67. if (len > max_len)
  68. max_len = len;
  69. ttl_len += len;
  70. } while (--ct_down > 0);
  71. ct_down = name_ct;
  72. }
  73. /*
  74. * IF any one entry is about 1/2 line or longer, print one per line
  75. */
  76. if (max_len > 35) {
  77. do {
  78. fprintf(option_usage_fp, ENUM_ERR_LINE, *(paz_names++));
  79. } while (--ct_down > 0);
  80. }
  81. /*
  82. * ELSE IF they all fit on one line, then do so.
  83. */
  84. else if (ttl_len < 76) {
  85. fputc(' ', option_usage_fp);
  86. do {
  87. fputc(' ', option_usage_fp);
  88. fputs(*(paz_names++), option_usage_fp);
  89. } while (--ct_down > 0);
  90. fputc(NL, option_usage_fp);
  91. }
  92. /*
  93. * Otherwise, columnize the output
  94. */
  95. else {
  96. unsigned int ent_no = 0;
  97. char fmt[16]; /* format for all-but-last entries on a line */
  98. if (snprintf(fmt, 16, ENUM_ERR_WIDTH, (int)max_len) >= 16)
  99. option_exits(EXIT_FAILURE);
  100. max_len = 78 / max_len; /* max_len is now max entries on a line */
  101. fputs(TWO_SPACES_STR, option_usage_fp);
  102. /*
  103. * Loop through all but the last entry
  104. */
  105. ct_down = name_ct;
  106. while (--ct_down > 0) {
  107. if (++ent_no == max_len) {
  108. /*
  109. * Last entry on a line. Start next line, too.
  110. */
  111. fprintf(option_usage_fp, NLSTR_SPACE_FMT, *(paz_names++));
  112. ent_no = 0;
  113. }
  114. else
  115. fprintf(option_usage_fp, fmt, *(paz_names++) );
  116. }
  117. fprintf(option_usage_fp, NLSTR_FMT, *paz_names);
  118. }
  119. if (pOpts > OPTPROC_EMIT_LIMIT) {
  120. fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
  121. (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
  122. /* NOTREACHED */
  123. }
  124. if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
  125. fprintf(option_usage_fp, zLowerBits, name_ct);
  126. fputs(zSetMemberSettings, option_usage_fp);
  127. } else {
  128. fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
  129. }
  130. }
  131. /**
  132. * Convert a name or number into a binary number.
  133. * "~0" and "-1" will be converted to the largest value in the enumeration.
  134. *
  135. * @param name the keyword name (number) to convert
  136. * @param pOpts the program's option descriptor
  137. * @param pOD the option descriptor for this option
  138. * @param paz_names the list of keywords for this option
  139. * @param name_ct the count of keywords
  140. */
  141. static uintptr_t
  142. find_name(char const * name, tOptions * pOpts, tOptDesc * pOD,
  143. char const * const * paz_names, unsigned int name_ct)
  144. {
  145. /*
  146. * Return the matching index as a pointer sized integer.
  147. * The result gets stashed in a char * pointer.
  148. */
  149. uintptr_t res = name_ct;
  150. size_t len = strlen((char *)name);
  151. uintptr_t idx;
  152. if (IS_DEC_DIGIT_CHAR(*name)) {
  153. char * pz = VOIDP(name);
  154. unsigned long val = strtoul(pz, &pz, 0);
  155. if ((*pz == NUL) && (val < name_ct))
  156. return (uintptr_t)val;
  157. pz_enum_err_fmt = znum_too_large;
  158. option_usage_fp = stderr;
  159. enum_err(pOpts, pOD, paz_names, (int)name_ct);
  160. return name_ct;
  161. }
  162. if (IS_INVERSION_CHAR(*name) && (name[2] == NUL)) {
  163. if ( ((name[0] == '~') && (name[1] == '0'))
  164. || ((name[0] == '-') && (name[1] == '1')))
  165. return (uintptr_t)(name_ct - 1);
  166. goto oops;
  167. }
  168. /*
  169. * Look for an exact match, but remember any partial matches.
  170. * Multiple partial matches means we have an ambiguous match.
  171. */
  172. for (idx = 0; idx < name_ct; idx++) {
  173. if (strncmp((char *)paz_names[idx], (char *)name, len) == 0) {
  174. if (paz_names[idx][len] == NUL)
  175. return idx; /* full match */
  176. if (res == name_ct)
  177. res = idx; /* save partial match */
  178. else
  179. res = (uintptr_t)~0; /* may yet find full match */
  180. }
  181. }
  182. if (res < name_ct)
  183. return res; /* partial match */
  184. oops:
  185. pz_enum_err_fmt = (res == name_ct) ? zNoKey : zambiguous_key;
  186. option_usage_fp = stderr;
  187. enum_err(pOpts, pOD, paz_names, (int)name_ct);
  188. return name_ct;
  189. }
  190. /*=export_func optionKeywordName
  191. * what: Convert between enumeration values and strings
  192. * private:
  193. *
  194. * arg: tOptDesc *, pOD, enumeration option description
  195. * arg: unsigned int, enum_val, the enumeration value to map
  196. *
  197. * ret_type: char const *
  198. * ret_desc: the enumeration name from const memory
  199. *
  200. * doc: This converts an enumeration value into the matching string.
  201. =*/
  202. char const *
  203. optionKeywordName(tOptDesc * pOD, unsigned int enum_val)
  204. {
  205. tOptDesc od = { 0 };
  206. od.optArg.argEnum = enum_val;
  207. (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od );
  208. return od.optArg.argString;
  209. }
  210. /*=export_func optionEnumerationVal
  211. * what: Convert from a string to an enumeration value
  212. * private:
  213. *
  214. * arg: tOptions *, pOpts, the program options descriptor
  215. * arg: tOptDesc *, pOD, enumeration option description
  216. * arg: char const * const *, paz_names, list of enumeration names
  217. * arg: unsigned int, name_ct, number of names in list
  218. *
  219. * ret_type: uintptr_t
  220. * ret_desc: the enumeration value
  221. *
  222. * doc: This converts the optArg.argString string from the option description
  223. * into the index corresponding to an entry in the name list.
  224. * This will match the generated enumeration value.
  225. * Full matches are always accepted. Partial matches are accepted
  226. * if there is only one partial match.
  227. =*/
  228. uintptr_t
  229. optionEnumerationVal(tOptions * pOpts, tOptDesc * pOD,
  230. char const * const * paz_names, unsigned int name_ct)
  231. {
  232. uintptr_t res = 0UL;
  233. /*
  234. * IF the program option descriptor pointer is invalid,
  235. * then it is some sort of special request.
  236. */
  237. switch ((uintptr_t)pOpts) {
  238. case (uintptr_t)OPTPROC_EMIT_USAGE:
  239. /*
  240. * print the list of enumeration names.
  241. */
  242. enum_err(pOpts, pOD, paz_names, (int)name_ct);
  243. break;
  244. case (uintptr_t)OPTPROC_EMIT_SHELL:
  245. {
  246. unsigned int ix = (unsigned int)pOD->optArg.argEnum;
  247. /*
  248. * print the name string.
  249. */
  250. if (ix >= name_ct)
  251. printf(INVALID_FMT, ix);
  252. else
  253. fputs(paz_names[ ix ], stdout);
  254. break;
  255. }
  256. case (uintptr_t)OPTPROC_RETURN_VALNAME:
  257. {
  258. unsigned int ix = (unsigned int)pOD->optArg.argEnum;
  259. /*
  260. * Replace the enumeration value with the name string.
  261. */
  262. if (ix >= name_ct)
  263. return (uintptr_t)INVALID_STR;
  264. pOD->optArg.argString = paz_names[ix];
  265. break;
  266. }
  267. default:
  268. if ((pOD->fOptState & OPTST_RESET) != 0)
  269. break;
  270. res = find_name(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct);
  271. if (pOD->fOptState & OPTST_ALLOC_ARG) {
  272. AGFREE(pOD->optArg.argString);
  273. pOD->fOptState &= ~OPTST_ALLOC_ARG;
  274. pOD->optArg.argString = NULL;
  275. }
  276. }
  277. return res;
  278. }
  279. static void
  280. set_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
  281. unsigned int name_ct)
  282. {
  283. /*
  284. * print the name string.
  285. */
  286. unsigned int ix = 0;
  287. uintptr_t bits = (uintptr_t)pOD->optCookie;
  288. size_t len = 0;
  289. (void)pOpts;
  290. bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
  291. while (bits != 0) {
  292. if (bits & 1) {
  293. if (len++ > 0) fputs(OR_STR, stdout);
  294. fputs(paz_names[ix], stdout);
  295. }
  296. if (++ix >= name_ct) break;
  297. bits >>= 1;
  298. }
  299. }
  300. static void
  301. set_memb_names(tOptions * opts, tOptDesc * od, char const * const * nm_list,
  302. unsigned int nm_ct)
  303. {
  304. char * pz;
  305. uintptr_t mask = (1UL << (uintptr_t)nm_ct) - 1UL;
  306. uintptr_t bits = (uintptr_t)od->optCookie & mask;
  307. unsigned int ix = 0;
  308. size_t len = 1;
  309. /*
  310. * Replace the enumeration value with the name string.
  311. * First, determine the needed length, then allocate and fill in.
  312. */
  313. while (bits != 0) {
  314. if (bits & 1)
  315. len += strlen(nm_list[ix]) + PLUS_STR_LEN + 1;
  316. if (++ix >= nm_ct) break;
  317. bits >>= 1;
  318. }
  319. od->optArg.argString = pz = AGALOC(len, "enum");
  320. bits = (uintptr_t)od->optCookie & mask;
  321. if (bits == 0) {
  322. *pz = NUL;
  323. return;
  324. }
  325. for (ix = 0; ; ix++) {
  326. size_t nln;
  327. int doit = bits & 1;
  328. bits >>= 1;
  329. if (doit == 0)
  330. continue;
  331. nln = strlen(nm_list[ix]);
  332. memcpy(pz, nm_list[ix], nln);
  333. pz += nln;
  334. if (bits == 0)
  335. break;
  336. memcpy(pz, PLUS_STR, PLUS_STR_LEN);
  337. pz += PLUS_STR_LEN;
  338. }
  339. *pz = NUL;
  340. (void)opts;
  341. }
  342. /**
  343. * Check membership start conditions. An equal character (@samp{=}) says to
  344. * clear the result and not carry over any residual value. A carat
  345. * (@samp{^}), which may follow the equal character, says to invert the
  346. * result. The scanning pointer is advanced past these characters and any
  347. * leading white space. Invalid sequences are indicated by setting the
  348. * scanning pointer to NULL.
  349. *
  350. * @param od the set membership option description
  351. * @param argp a pointer to the string scanning pointer
  352. * @param invert a pointer to the boolean inversion indicator
  353. *
  354. * @returns either zero or the original value for the optCookie.
  355. */
  356. static uintptr_t
  357. check_membership_start(tOptDesc * od, char const ** argp, bool * invert)
  358. {
  359. uintptr_t res = (uintptr_t)od->optCookie;
  360. char const * arg = SPN_WHITESPACE_CHARS(od->optArg.argString);
  361. if ((arg == NULL) || (*arg == NUL))
  362. goto member_start_fail;
  363. *invert = false;
  364. switch (*arg) {
  365. case '=':
  366. res = 0UL;
  367. arg = SPN_WHITESPACE_CHARS(arg + 1);
  368. switch (*arg) {
  369. case '=': case ',':
  370. goto member_start_fail;
  371. case '^':
  372. goto inversion;
  373. default:
  374. break;
  375. }
  376. break;
  377. case '^':
  378. inversion:
  379. *invert = true;
  380. arg = SPN_WHITESPACE_CHARS(arg + 1);
  381. if (*arg != ',')
  382. break;
  383. /* FALLTHROUGH */
  384. case ',':
  385. goto member_start_fail;
  386. default:
  387. break;
  388. }
  389. *argp = arg;
  390. return res;
  391. member_start_fail:
  392. *argp = NULL;
  393. return 0UL;
  394. }
  395. /**
  396. * convert a name to a bit. Look up a name string to get a bit number
  397. * and shift the value "1" left that number of bits.
  398. *
  399. * @param opts program options descriptor
  400. * @param od the set membership option description
  401. * @param pz address of the start of the bit name
  402. * @param nm_list the list of names for this option
  403. * @param nm_ct the number of entries in this list
  404. *
  405. * @returns 0UL on error, other an unsigned long with the correct bit set.
  406. */
  407. static uintptr_t
  408. find_member_bit(tOptions * opts, tOptDesc * od, char const * pz, int len,
  409. char const * const * nm_list, unsigned int nm_ct)
  410. {
  411. char nm_buf[ AO_NAME_SIZE ];
  412. memcpy(nm_buf, pz, len);
  413. nm_buf[len] = NUL;
  414. {
  415. unsigned int shift_ct = (unsigned int)
  416. find_name(nm_buf, opts, od, nm_list, nm_ct);
  417. if (shift_ct >= nm_ct)
  418. return 0UL;
  419. return 1UL << shift_ct;
  420. }
  421. }
  422. /*=export_func optionMemberList
  423. * what: Get the list of members of a bit mask set
  424. *
  425. * arg: tOptDesc *, od, the set membership option description
  426. *
  427. * ret_type: char *
  428. * ret_desc: the names of the set bits
  429. *
  430. * doc: This converts the OPT_VALUE_name mask value to a allocated string.
  431. * It is the caller's responsibility to free the string.
  432. =*/
  433. char *
  434. optionMemberList(tOptDesc * od)
  435. {
  436. uintptr_t sv = od->optArg.argIntptr;
  437. char * res;
  438. (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
  439. res = VOIDP(od->optArg.argString);
  440. od->optArg.argIntptr = sv;
  441. return res;
  442. }
  443. /*=export_func optionSetMembers
  444. * what: Convert between bit flag values and strings
  445. * private:
  446. *
  447. * arg: tOptions *, opts, the program options descriptor
  448. * arg: tOptDesc *, od, the set membership option description
  449. * arg: char const * const *,
  450. * nm_list, list of enumeration names
  451. * arg: unsigned int, nm_ct, number of names in list
  452. *
  453. * doc: This converts the optArg.argString string from the option description
  454. * into the index corresponding to an entry in the name list.
  455. * This will match the generated enumeration value.
  456. * Full matches are always accepted. Partial matches are accepted
  457. * if there is only one partial match.
  458. =*/
  459. void
  460. optionSetMembers(tOptions * opts, tOptDesc * od,
  461. char const * const * nm_list, unsigned int nm_ct)
  462. {
  463. /*
  464. * IF the program option descriptor pointer is invalid,
  465. * then it is some sort of special request.
  466. */
  467. switch ((uintptr_t)opts) {
  468. case (uintptr_t)OPTPROC_EMIT_USAGE:
  469. enum_err(OPTPROC_EMIT_USAGE, od, nm_list, nm_ct);
  470. return;
  471. case (uintptr_t)OPTPROC_EMIT_SHELL:
  472. set_memb_shell(opts, od, nm_list, nm_ct);
  473. return;
  474. case (uintptr_t)OPTPROC_RETURN_VALNAME:
  475. set_memb_names(opts, od, nm_list, nm_ct);
  476. return;
  477. default:
  478. break;
  479. }
  480. if ((od->fOptState & OPTST_RESET) != 0)
  481. return;
  482. {
  483. char const * arg;
  484. bool invert;
  485. uintptr_t res = check_membership_start(od, &arg, &invert);
  486. if (arg == NULL)
  487. goto fail_return;
  488. while (*arg != NUL) {
  489. bool inv_val = false;
  490. int len;
  491. switch (*arg) {
  492. case ',':
  493. arg = SPN_WHITESPACE_CHARS(arg+1);
  494. if ((*arg == ',') || (*arg == '|'))
  495. goto fail_return;
  496. continue;
  497. case '-':
  498. case '!':
  499. inv_val = true;
  500. /* FALLTHROUGH */
  501. case '+':
  502. case '|':
  503. arg = SPN_WHITESPACE_CHARS(arg+1);
  504. }
  505. len = (int)(BRK_SET_SEPARATOR_CHARS(arg) - arg);
  506. if (len == 0)
  507. break;
  508. if ((len == 3) && (strncmp(arg, zAll, 3) == 0)) {
  509. if (inv_val)
  510. res = 0;
  511. else res = ~0UL;
  512. }
  513. else if ((len == 4) && (strncmp(arg, zNone, 4) == 0)) {
  514. if (! inv_val)
  515. res = 0;
  516. }
  517. else do {
  518. char * pz;
  519. uintptr_t bit = strtoul(arg, &pz, 0);
  520. if (pz != arg + len) {
  521. bit = find_member_bit(opts, od, pz, len, nm_list, nm_ct);
  522. if (bit == 0UL)
  523. goto fail_return;
  524. }
  525. if (inv_val)
  526. res &= ~bit;
  527. else res |= bit;
  528. } while (false);
  529. arg = SPN_WHITESPACE_CHARS(arg + len);
  530. }
  531. if (invert)
  532. res ^= ~0UL;
  533. if (nm_ct < (8 * sizeof(uintptr_t)))
  534. res &= (1UL << nm_ct) - 1UL;
  535. od->optCookie = VOIDP(res);
  536. }
  537. return;
  538. fail_return:
  539. od->optCookie = VOIDP(0);
  540. }
  541. /** @}
  542. *
  543. * Local Variables:
  544. * mode: C
  545. * c-file-style: "stroustrup"
  546. * indent-tabs-mode: nil
  547. * End:
  548. * end of autoopts/enum.c */