putshell.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /**
  2. * \file putshell.c
  3. *
  4. * This module will interpret the options set in the tOptions
  5. * structure and print them to standard out in a fashion that
  6. * will allow them to be interpreted by the Bourne or Korn shells.
  7. *
  8. * @addtogroup autoopts
  9. * @{
  10. */
  11. /*
  12. * This file is part of AutoOpts, a companion to AutoGen.
  13. * AutoOpts is free software.
  14. * AutoOpts is Copyright (C) 1992-2016 by Bruce Korb - all rights reserved
  15. *
  16. * AutoOpts is available under any one of two licenses. The license
  17. * in use must be one of these two and the choice is under the control
  18. * of the user of the license.
  19. *
  20. * The GNU Lesser General Public License, version 3 or later
  21. * See the files "COPYING.lgplv3" and "COPYING.gplv3"
  22. *
  23. * The Modified Berkeley Software Distribution License
  24. * See the file "COPYING.mbsd"
  25. *
  26. * These files have the following sha256 sums:
  27. *
  28. * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
  29. * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
  30. * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
  31. */
  32. /* = = = START-STATIC-FORWARD = = = */
  33. static size_t
  34. string_size(char const * scan, size_t nl_len);
  35. static char const *
  36. print_quoted_apostrophes(char const * str);
  37. static void
  38. print_quot_str(char const * str);
  39. static void
  40. print_enumeration(tOptions * pOpts, tOptDesc * pOD);
  41. static void
  42. print_membership(tOptions * pOpts, tOptDesc * pOD);
  43. static void
  44. print_stacked_arg(tOptions * pOpts, tOptDesc * pOD);
  45. static void
  46. print_reordering(tOptions * opts);
  47. /* = = = END-STATIC-FORWARD = = = */
  48. /**
  49. * Count the number of bytes required to represent a string as a
  50. * compilable string.
  51. *
  52. * @param[in] scan the text to be rewritten as a C program text string.
  53. * @param[in] nl_len the number of bytes used for each embedded newline.
  54. *
  55. * @returns the count, including the terminating NUL byte.
  56. */
  57. static size_t
  58. string_size(char const * scan, size_t nl_len)
  59. {
  60. /*
  61. * Start by counting the start and end quotes, plus the NUL.
  62. */
  63. size_t res_ln = 3;
  64. for (;;) {
  65. char ch = *(scan++);
  66. if ((ch >= ' ') && (ch <= '~')) {
  67. /*
  68. * a backslash allowance for double quotes and baskslashes
  69. */
  70. res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1;
  71. }
  72. /*
  73. * When not a normal character, then count the characters
  74. * required to represent whatever it is.
  75. */
  76. else switch (ch) {
  77. case NUL:
  78. return res_ln;
  79. case NL:
  80. res_ln += nl_len;
  81. break;
  82. case HT:
  83. case BEL:
  84. case BS:
  85. case FF:
  86. case CR:
  87. case VT:
  88. res_ln += 2;
  89. break;
  90. default:
  91. res_ln += 4; /* text len for \xNN */
  92. }
  93. }
  94. }
  95. /*=export_func optionQuoteString
  96. * private:
  97. *
  98. * what: Print a string as quoted text suitable for a C compiler.
  99. * arg: + char const * + text + a block of text to quote +
  100. * arg: + char const * + nl + line splice text +
  101. *
  102. * ret_type: char const *
  103. * ret_desc: the allocated input string as a quoted string
  104. *
  105. * doc:
  106. * This is for internal use by autogen and autoopts.
  107. * It takes an input string and produces text the C compiler can process
  108. * to produce an exact copy of the original string.
  109. * The caller must deallocate the result. Standard C strings and
  110. * K&R strings are distinguished by the "nl" string.
  111. =*/
  112. char const *
  113. optionQuoteString(char const * text, char const * nl)
  114. {
  115. size_t nl_len = strlen(nl);
  116. size_t out_sz = string_size(text, nl_len);
  117. char * out;
  118. char * res = out = AGALOC(out_sz, "quot str");
  119. *(out++) = '"';
  120. for (;;) {
  121. unsigned char ch = (unsigned char)*text;
  122. if ((ch >= ' ') && (ch <= '~')) {
  123. if ((ch == '"') || (ch == '\\'))
  124. /*
  125. * We must escape these characters in the output string
  126. */
  127. *(out++) = '\\';
  128. *(out++) = (char)ch;
  129. } else switch (ch) {
  130. # define add_esc_ch(_ch) { *(out++) = '\\'; *(out++) = (_ch); }
  131. case BEL: add_esc_ch('a'); break;
  132. case BS: add_esc_ch('b'); break;
  133. case HT: add_esc_ch('t'); break;
  134. case VT: add_esc_ch('v'); break;
  135. case FF: add_esc_ch('f'); break;
  136. case CR: add_esc_ch('r'); break;
  137. case LF:
  138. /*
  139. * Place contiguous new-lines on a single line.
  140. * The current character is a NL, check the next one.
  141. */
  142. while (*++text == NL)
  143. add_esc_ch('n');
  144. /*
  145. * Insert a splice before starting next line
  146. */
  147. if (*text != NUL) {
  148. memcpy(out, nl, nl_len);
  149. out += nl_len;
  150. continue; /* text is already at the next character */
  151. }
  152. add_esc_ch('n');
  153. /* FALLTHROUGH */
  154. case NUL:
  155. /*
  156. * End of string. Terminate the quoted output. If necessary,
  157. * deallocate the text string. Return the scan resumption point.
  158. */
  159. *(out++) = '"';
  160. *(out++) = NUL;
  161. #ifndef NDEBUG
  162. if ((size_t)(out - res) > out_sz) {
  163. fputs(misguess_len, stderr);
  164. option_exits(EXIT_FAILURE);
  165. }
  166. #endif
  167. return res;
  168. default:
  169. /*
  170. * sprintf is safe here, because we already computed
  171. * the amount of space we will be using. Assertion is above.
  172. */
  173. out += sprintf(out, MK_STR_OCT_FMT, ch);
  174. }
  175. text++;
  176. # undef add_esc_ch
  177. }
  178. }
  179. /**
  180. * Print out escaped apostorophes.
  181. *
  182. * @param[in] str the apostrophies to print
  183. */
  184. static char const *
  185. print_quoted_apostrophes(char const * str)
  186. {
  187. while (*str == APOSTROPHE) {
  188. fputs(QUOT_APOS, stdout);
  189. str++;
  190. }
  191. return str;
  192. }
  193. /**
  194. * Print a single quote (apostrophe quoted) string.
  195. * Other than somersaults for apostrophes, nothing else needs quoting.
  196. *
  197. * @param[in] str the string to print
  198. */
  199. static void
  200. print_quot_str(char const * str)
  201. {
  202. /*
  203. * Handle empty strings to make the rest of the logic simpler.
  204. */
  205. if ((str == NULL) || (*str == NUL)) {
  206. fputs(EMPTY_ARG, stdout);
  207. return;
  208. }
  209. /*
  210. * Emit any single quotes/apostrophes at the start of the string and
  211. * bail if that is all we need to do.
  212. */
  213. str = print_quoted_apostrophes(str);
  214. if (*str == NUL)
  215. return;
  216. /*
  217. * Start the single quote string
  218. */
  219. fputc(APOSTROPHE, stdout);
  220. for (;;) {
  221. char const * pz = strchr(str, APOSTROPHE);
  222. if (pz == NULL)
  223. break;
  224. /*
  225. * Emit the string up to the single quote (apostrophe) we just found.
  226. */
  227. (void)fwrite(str, (size_t)(pz - str), (size_t)1, stdout);
  228. /*
  229. * Close the current string, emit the apostrophes and re-open the
  230. * string (IFF there is more text to print).
  231. */
  232. fputc(APOSTROPHE, stdout);
  233. str = print_quoted_apostrophes(pz);
  234. if (*str == NUL)
  235. return;
  236. fputc(APOSTROPHE, stdout);
  237. }
  238. /*
  239. * If we broke out of the loop, we must still emit the remaining text
  240. * and then close the single quote string.
  241. */
  242. fputs(str, stdout);
  243. fputc(APOSTROPHE, stdout);
  244. }
  245. static void
  246. print_enumeration(tOptions * pOpts, tOptDesc * pOD)
  247. {
  248. uintptr_t e_val = pOD->optArg.argEnum;
  249. printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
  250. /*
  251. * Convert value to string, print that and restore numeric value.
  252. */
  253. (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
  254. printf(QUOT_ARG_FMT, pOD->optArg.argString);
  255. if (pOD->fOptState & OPTST_ALLOC_ARG)
  256. AGFREE(pOD->optArg.argString);
  257. pOD->optArg.argEnum = e_val;
  258. printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
  259. }
  260. static void
  261. print_membership(tOptions * pOpts, tOptDesc * pOD)
  262. {
  263. char const * svstr = pOD->optArg.argString;
  264. char const * pz;
  265. uintptr_t val = 1;
  266. printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
  267. (int)(uintptr_t)(pOD->optCookie));
  268. pOD->optCookie = VOIDP(~0UL);
  269. (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
  270. pz = pOD->optArg.argString;
  271. while (*pz != NUL) {
  272. printf("readonly %s_", pOD->pz_NAME);
  273. pz = SPN_PLUS_N_SPACE_CHARS(pz);
  274. for (;;) {
  275. int ch = *(pz++);
  276. if (IS_LOWER_CASE_CHAR(ch)) fputc(toupper(ch), stdout);
  277. else if (IS_UPPER_CASE_CHAR(ch)) fputc(ch, stdout);
  278. else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done;
  279. else if (ch == NUL) { pz--; goto name_done; }
  280. else fputc('_', stdout);
  281. } name_done:;
  282. printf(SHOW_VAL_FMT, (unsigned long)val);
  283. val <<= 1;
  284. }
  285. AGFREE(pOD->optArg.argString);
  286. pOD->optArg.argString = svstr;
  287. }
  288. static void
  289. print_stacked_arg(tOptions * pOpts, tOptDesc * pOD)
  290. {
  291. tArgList * pAL = (tArgList *)pOD->optCookie;
  292. char const ** ppz = pAL->apzArgs;
  293. int ct = pAL->useCt;
  294. printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct);
  295. while (--ct >= 0) {
  296. printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME,
  297. pAL->useCt - ct);
  298. print_quot_str(*(ppz++));
  299. printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME,
  300. pAL->useCt - ct);
  301. }
  302. }
  303. /**
  304. * emit the arguments as readily parsed text.
  305. * The program options are set by emitting the shell "set" command.
  306. *
  307. * @param[in] opts the program options structure
  308. */
  309. static void
  310. print_reordering(tOptions * opts)
  311. {
  312. unsigned int ix;
  313. fputs(set_dash, stdout);
  314. for (ix = opts->curOptIdx;
  315. ix < opts->origArgCt;
  316. ix++) {
  317. fputc(' ', stdout);
  318. print_quot_str(opts->origArgVect[ ix ]);
  319. }
  320. fputs(init_optct, stdout);
  321. }
  322. /*=export_func optionPutShell
  323. * what: write a portable shell script to parse options
  324. * private:
  325. * arg: tOptions *, pOpts, the program options descriptor
  326. * doc: This routine will emit portable shell script text for parsing
  327. * the options described in the option definitions.
  328. =*/
  329. void
  330. optionPutShell(tOptions * pOpts)
  331. {
  332. int optIx = 0;
  333. printf(zOptCtFmt, pOpts->curOptIdx-1);
  334. do {
  335. tOptDesc * pOD = pOpts->pOptDesc + optIx;
  336. if ((pOD->fOptState & OPTST_NO_OUTPUT_MASK) != 0)
  337. continue;
  338. /*
  339. * Equivalence classes are hard to deal with. Where the
  340. * option data wind up kind of squishes around. For the purposes
  341. * of emitting shell state, they are not recommended, but we'll
  342. * do something. I guess we'll emit the equivalenced-to option
  343. * at the point in time when the base option is found.
  344. */
  345. if (pOD->optEquivIndex != NO_EQUIVALENT)
  346. continue; /* equivalence to a different option */
  347. /*
  348. * Equivalenced to a different option. Process the current option
  349. * as the equivalenced-to option. Keep the persistent state bits,
  350. * but copy over the set-state bits.
  351. */
  352. if (pOD->optActualIndex != optIx) {
  353. tOptDesc * p = pOpts->pOptDesc + pOD->optActualIndex;
  354. p->optArg = pOD->optArg;
  355. p->fOptState &= OPTST_PERSISTENT_MASK;
  356. p->fOptState |= pOD->fOptState & ~OPTST_PERSISTENT_MASK;
  357. printf(zEquivMode, pOpts->pzPROGNAME, pOD->pz_NAME, p->pz_NAME);
  358. pOD = p;
  359. }
  360. /*
  361. * If the argument type is a set membership bitmask, then we always
  362. * emit the thing. We do this because it will always have some sort
  363. * of bitmask value and we need to emit the bit values.
  364. */
  365. if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
  366. print_membership(pOpts, pOD);
  367. continue;
  368. }
  369. /*
  370. * IF the option was either specified or it wakes up enabled,
  371. * then we will emit information. Otherwise, skip it.
  372. * The idea is that if someone defines an option to initialize
  373. * enabled, we should tell our shell script that it is enabled.
  374. */
  375. if (UNUSED_OPT(pOD) && DISABLED_OPT(pOD))
  376. continue;
  377. /*
  378. * Handle stacked arguments
  379. */
  380. if ( (pOD->fOptState & OPTST_STACKED)
  381. && (pOD->optCookie != NULL) ) {
  382. print_stacked_arg(pOpts, pOD);
  383. continue;
  384. }
  385. /*
  386. * If the argument has been disabled,
  387. * Then set its value to the disablement string
  388. */
  389. if ((pOD->fOptState & OPTST_DISABLED) != 0) {
  390. printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME,
  391. (pOD->pz_DisablePfx != NULL)
  392. ? pOD->pz_DisablePfx : "false");
  393. continue;
  394. }
  395. /*
  396. * If the argument type is numeric, the last arg pointer
  397. * is really the VALUE of the string that was pointed to.
  398. */
  399. if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) {
  400. printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
  401. (int)pOD->optArg.argInt);
  402. continue;
  403. }
  404. /*
  405. * If the argument type is an enumeration, then it is much
  406. * like a text value, except we call the callback function
  407. * to emit the value corresponding to the "optArg" number.
  408. */
  409. if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) {
  410. print_enumeration(pOpts, pOD);
  411. continue;
  412. }
  413. /*
  414. * If the argument type is numeric, the last arg pointer
  415. * is really the VALUE of the string that was pointed to.
  416. */
  417. if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) {
  418. printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
  419. (pOD->optArg.argBool == 0) ? "false" : "true");
  420. continue;
  421. }
  422. /*
  423. * IF the option has an empty value,
  424. * THEN we set the argument to the occurrence count.
  425. */
  426. if ( (pOD->optArg.argString == NULL)
  427. || (pOD->optArg.argString[0] == NUL) ) {
  428. printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
  429. pOD->optOccCt);
  430. continue;
  431. }
  432. /*
  433. * This option has a text value
  434. */
  435. printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
  436. print_quot_str(pOD->optArg.argString);
  437. printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
  438. } while (++optIx < pOpts->presetOptCt );
  439. if ( ((pOpts->fOptSet & OPTPROC_REORDER) != 0)
  440. && (pOpts->curOptIdx < pOpts->origArgCt))
  441. print_reordering(pOpts);
  442. fflush(stdout);
  443. }
  444. /** @}
  445. *
  446. * Local Variables:
  447. * mode: C
  448. * c-file-style: "stroustrup"
  449. * indent-tabs-mode: nil
  450. * End:
  451. * end of autoopts/putshell.c */