1
0

putshell.c 14 KB

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