enum.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  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;
  206. memset(&od, 0, sizeof(od));
  207. od.optArg.argEnum = enum_val;
  208. (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od );
  209. return od.optArg.argString;
  210. }
  211. /*=export_func optionEnumerationVal
  212. * what: Convert from a string to an enumeration value
  213. * private:
  214. *
  215. * arg: tOptions *, pOpts, the program options descriptor
  216. * arg: tOptDesc *, pOD, enumeration option description
  217. * arg: char const * const *, paz_names, list of enumeration names
  218. * arg: unsigned int, name_ct, number of names in list
  219. *
  220. * ret_type: uintptr_t
  221. * ret_desc: the enumeration value
  222. *
  223. * doc: This converts the optArg.argString string from the option description
  224. * into the index corresponding to an entry in the name list.
  225. * This will match the generated enumeration value.
  226. * Full matches are always accepted. Partial matches are accepted
  227. * if there is only one partial match.
  228. =*/
  229. uintptr_t
  230. optionEnumerationVal(tOptions * pOpts, tOptDesc * pOD,
  231. char const * const * paz_names, unsigned int name_ct)
  232. {
  233. uintptr_t res = 0UL;
  234. /*
  235. * IF the program option descriptor pointer is invalid,
  236. * then it is some sort of special request.
  237. */
  238. switch ((uintptr_t)pOpts) {
  239. case (uintptr_t)OPTPROC_EMIT_USAGE:
  240. /*
  241. * print the list of enumeration names.
  242. */
  243. enum_err(pOpts, pOD, paz_names, (int)name_ct);
  244. break;
  245. case (uintptr_t)OPTPROC_EMIT_SHELL:
  246. {
  247. unsigned int ix = (unsigned int)pOD->optArg.argEnum;
  248. /*
  249. * print the name string.
  250. */
  251. if (ix >= name_ct)
  252. printf(INVALID_FMT, ix);
  253. else
  254. fputs(paz_names[ ix ], stdout);
  255. break;
  256. }
  257. case (uintptr_t)OPTPROC_RETURN_VALNAME:
  258. {
  259. unsigned int ix = (unsigned int)pOD->optArg.argEnum;
  260. /*
  261. * Replace the enumeration value with the name string.
  262. */
  263. if (ix >= name_ct)
  264. return (uintptr_t)INVALID_STR;
  265. pOD->optArg.argString = paz_names[ix];
  266. break;
  267. }
  268. default:
  269. if ((pOD->fOptState & OPTST_RESET) != 0)
  270. break;
  271. res = find_name(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct);
  272. if (pOD->fOptState & OPTST_ALLOC_ARG) {
  273. AGFREE(pOD->optArg.argString);
  274. pOD->fOptState &= ~OPTST_ALLOC_ARG;
  275. pOD->optArg.argString = NULL;
  276. }
  277. }
  278. return res;
  279. }
  280. static void
  281. set_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
  282. unsigned int name_ct)
  283. {
  284. /*
  285. * print the name string.
  286. */
  287. unsigned int ix = 0;
  288. uintptr_t bits = (uintptr_t)pOD->optCookie;
  289. size_t len = 0;
  290. (void)pOpts;
  291. bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
  292. while (bits != 0) {
  293. if (bits & 1) {
  294. if (len++ > 0) fputs(OR_STR, stdout);
  295. fputs(paz_names[ix], stdout);
  296. }
  297. if (++ix >= name_ct) break;
  298. bits >>= 1;
  299. }
  300. }
  301. static void
  302. set_memb_names(tOptions * opts, tOptDesc * od, char const * const * nm_list,
  303. unsigned int nm_ct)
  304. {
  305. char * pz;
  306. uintptr_t mask = (1UL << (uintptr_t)nm_ct) - 1UL;
  307. uintptr_t bits = (uintptr_t)od->optCookie & mask;
  308. unsigned int ix = 0;
  309. size_t len = 1;
  310. /*
  311. * Replace the enumeration value with the name string.
  312. * First, determine the needed length, then allocate and fill in.
  313. */
  314. while (bits != 0) {
  315. if (bits & 1)
  316. len += strlen(nm_list[ix]) + PLUS_STR_LEN + 1;
  317. if (++ix >= nm_ct) break;
  318. bits >>= 1;
  319. }
  320. od->optArg.argString = pz = AGALOC(len, "enum");
  321. bits = (uintptr_t)od->optCookie & mask;
  322. if (bits == 0) {
  323. *pz = NUL;
  324. return;
  325. }
  326. for (ix = 0; ; ix++) {
  327. size_t nln;
  328. int doit = bits & 1;
  329. bits >>= 1;
  330. if (doit == 0)
  331. continue;
  332. nln = strlen(nm_list[ix]);
  333. memcpy(pz, nm_list[ix], nln);
  334. pz += nln;
  335. if (bits == 0)
  336. break;
  337. memcpy(pz, PLUS_STR, PLUS_STR_LEN);
  338. pz += PLUS_STR_LEN;
  339. }
  340. *pz = NUL;
  341. (void)opts;
  342. }
  343. /**
  344. * Check membership start conditions. An equal character (@samp{=}) says to
  345. * clear the result and not carry over any residual value. A carat
  346. * (@samp{^}), which may follow the equal character, says to invert the
  347. * result. The scanning pointer is advanced past these characters and any
  348. * leading white space. Invalid sequences are indicated by setting the
  349. * scanning pointer to NULL.
  350. *
  351. * @param od the set membership option description
  352. * @param argp a pointer to the string scanning pointer
  353. * @param invert a pointer to the boolean inversion indicator
  354. *
  355. * @returns either zero or the original value for the optCookie.
  356. */
  357. static uintptr_t
  358. check_membership_start(tOptDesc * od, char const ** argp, bool * invert)
  359. {
  360. uintptr_t res = (uintptr_t)od->optCookie;
  361. char const * arg = SPN_WHITESPACE_CHARS(od->optArg.argString);
  362. if ((arg == NULL) || (*arg == NUL))
  363. goto member_start_fail;
  364. *invert = false;
  365. switch (*arg) {
  366. case '=':
  367. res = 0UL;
  368. arg = SPN_WHITESPACE_CHARS(arg + 1);
  369. switch (*arg) {
  370. case '=': case ',':
  371. goto member_start_fail;
  372. case '^':
  373. goto inversion;
  374. default:
  375. break;
  376. }
  377. break;
  378. case '^':
  379. inversion:
  380. *invert = true;
  381. arg = SPN_WHITESPACE_CHARS(arg + 1);
  382. if (*arg != ',')
  383. break;
  384. /* FALLTHROUGH */
  385. case ',':
  386. goto member_start_fail;
  387. default:
  388. break;
  389. }
  390. *argp = arg;
  391. return res;
  392. member_start_fail:
  393. *argp = NULL;
  394. return 0UL;
  395. }
  396. /**
  397. * convert a name to a bit. Look up a name string to get a bit number
  398. * and shift the value "1" left that number of bits.
  399. *
  400. * @param opts program options descriptor
  401. * @param od the set membership option description
  402. * @param pz address of the start of the bit name
  403. * @param nm_list the list of names for this option
  404. * @param nm_ct the number of entries in this list
  405. *
  406. * @returns 0UL on error, other an unsigned long with the correct bit set.
  407. */
  408. static uintptr_t
  409. find_member_bit(tOptions * opts, tOptDesc * od, char const * pz, int len,
  410. char const * const * nm_list, unsigned int nm_ct)
  411. {
  412. char nm_buf[ AO_NAME_SIZE ];
  413. memcpy(nm_buf, pz, len);
  414. nm_buf[len] = NUL;
  415. {
  416. unsigned int shift_ct = (unsigned int)
  417. find_name(nm_buf, opts, od, nm_list, nm_ct);
  418. if (shift_ct >= nm_ct)
  419. return 0UL;
  420. return 1UL << shift_ct;
  421. }
  422. }
  423. /*=export_func optionMemberList
  424. * what: Get the list of members of a bit mask set
  425. *
  426. * arg: tOptDesc *, od, the set membership option description
  427. *
  428. * ret_type: char *
  429. * ret_desc: the names of the set bits
  430. *
  431. * doc: This converts the OPT_VALUE_name mask value to a allocated string.
  432. * It is the caller's responsibility to free the string.
  433. =*/
  434. char *
  435. optionMemberList(tOptDesc * od)
  436. {
  437. uintptr_t sv = od->optArg.argIntptr;
  438. char * res;
  439. (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
  440. res = VOIDP(od->optArg.argString);
  441. od->optArg.argIntptr = sv;
  442. return res;
  443. }
  444. /*=export_func optionSetMembers
  445. * what: Convert between bit flag values and strings
  446. * private:
  447. *
  448. * arg: tOptions *, opts, the program options descriptor
  449. * arg: tOptDesc *, od, the set membership option description
  450. * arg: char const * const *,
  451. * nm_list, list of enumeration names
  452. * arg: unsigned int, nm_ct, number of names in list
  453. *
  454. * doc: This converts the optArg.argString string from the option description
  455. * into the index corresponding to an entry in the name list.
  456. * This will match the generated enumeration value.
  457. * Full matches are always accepted. Partial matches are accepted
  458. * if there is only one partial match.
  459. =*/
  460. void
  461. optionSetMembers(tOptions * opts, tOptDesc * od,
  462. char const * const * nm_list, unsigned int nm_ct)
  463. {
  464. /*
  465. * IF the program option descriptor pointer is invalid,
  466. * then it is some sort of special request.
  467. */
  468. switch ((uintptr_t)opts) {
  469. case (uintptr_t)OPTPROC_EMIT_USAGE:
  470. enum_err(OPTPROC_EMIT_USAGE, od, nm_list, nm_ct);
  471. return;
  472. case (uintptr_t)OPTPROC_EMIT_SHELL:
  473. set_memb_shell(opts, od, nm_list, nm_ct);
  474. return;
  475. case (uintptr_t)OPTPROC_RETURN_VALNAME:
  476. set_memb_names(opts, od, nm_list, nm_ct);
  477. return;
  478. default:
  479. break;
  480. }
  481. if ((od->fOptState & OPTST_RESET) != 0)
  482. return;
  483. {
  484. char const * arg;
  485. bool invert;
  486. uintptr_t res = check_membership_start(od, &arg, &invert);
  487. if (arg == NULL)
  488. goto fail_return;
  489. while (*arg != NUL) {
  490. bool inv_val = false;
  491. int len;
  492. switch (*arg) {
  493. case ',':
  494. arg = SPN_WHITESPACE_CHARS(arg+1);
  495. if ((*arg == ',') || (*arg == '|'))
  496. goto fail_return;
  497. continue;
  498. case '-':
  499. case '!':
  500. inv_val = true;
  501. /* FALLTHROUGH */
  502. case '+':
  503. case '|':
  504. arg = SPN_WHITESPACE_CHARS(arg+1);
  505. }
  506. len = (int)(BRK_SET_SEPARATOR_CHARS(arg) - arg);
  507. if (len == 0)
  508. break;
  509. if ((len == 3) && (strncmp(arg, zAll, 3) == 0)) {
  510. if (inv_val)
  511. res = 0;
  512. else res = ~0UL;
  513. }
  514. else if ((len == 4) && (strncmp(arg, zNone, 4) == 0)) {
  515. if (! inv_val)
  516. res = 0;
  517. }
  518. else do {
  519. char * pz;
  520. uintptr_t bit = strtoul(arg, &pz, 0);
  521. if (pz != arg + len) {
  522. bit = find_member_bit(opts, od, pz, len, nm_list, nm_ct);
  523. if (bit == 0UL)
  524. goto fail_return;
  525. }
  526. if (inv_val)
  527. res &= ~bit;
  528. else res |= bit;
  529. } while (false);
  530. arg = SPN_WHITESPACE_CHARS(arg + len);
  531. }
  532. if (invert)
  533. res ^= ~0UL;
  534. if (nm_ct < (8 * sizeof(uintptr_t)))
  535. res &= (1UL << nm_ct) - 1UL;
  536. od->optCookie = VOIDP(res);
  537. }
  538. return;
  539. fail_return:
  540. od->optCookie = VOIDP(0);
  541. }
  542. /** @}
  543. *
  544. * Local Variables:
  545. * mode: C
  546. * c-file-style: "stroustrup"
  547. * indent-tabs-mode: nil
  548. * End:
  549. * end of autoopts/enum.c */