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