enum.c 18 KB

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