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