putshell.c 15 KB

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