enum.c 18 KB

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