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