/src/ntp-dev/sntp/libopts/putshell.c
Line | Count | Source (jump to first uncovered line) |
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 |
67 | | string_size(char const * scan, size_t nl_len) |
68 | 0 | { |
69 | | /* |
70 | | * Start by counting the start and end quotes, plus the NUL. |
71 | | */ |
72 | 0 | size_t res_ln = 3; |
73 | |
|
74 | 0 | for (;;) { |
75 | 0 | char ch = *(scan++); |
76 | 0 | if ((ch >= ' ') && (ch <= '~')) { |
77 | | |
78 | | /* |
79 | | * a backslash allowance for double quotes and baskslashes |
80 | | */ |
81 | 0 | res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1; |
82 | 0 | } |
83 | | |
84 | | /* |
85 | | * When not a normal character, then count the characters |
86 | | * required to represent whatever it is. |
87 | | */ |
88 | 0 | else switch (ch) { |
89 | 0 | case NUL: |
90 | 0 | return res_ln; |
91 | | |
92 | 0 | case NL: |
93 | 0 | res_ln += nl_len; |
94 | 0 | break; |
95 | | |
96 | 0 | case HT: |
97 | 0 | case BEL: |
98 | 0 | case BS: |
99 | 0 | case FF: |
100 | 0 | case CR: |
101 | 0 | case VT: |
102 | 0 | res_ln += 2; |
103 | 0 | break; |
104 | | |
105 | 0 | default: |
106 | 0 | res_ln += 4; /* text len for \xNN */ |
107 | 0 | } |
108 | 0 | } |
109 | 0 | } |
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 * |
129 | | optionQuoteString(char const * text, char const * nl) |
130 | 0 | { |
131 | 0 | size_t nl_len = strlen(nl); |
132 | 0 | char * out; |
133 | 0 | char * res = out = AGALOC(string_size(text, nl_len), "quot str"); |
134 | 0 | *(out++) = '"'; |
135 | |
|
136 | 0 | for (;;) { |
137 | 0 | unsigned char ch = (unsigned char)*text; |
138 | 0 | if ((ch >= ' ') && (ch <= '~')) { |
139 | 0 | if ((ch == '"') || (ch == '\\')) |
140 | | /* |
141 | | * We must escape these characters in the output string |
142 | | */ |
143 | 0 | *(out++) = '\\'; |
144 | 0 | *(out++) = (char)ch; |
145 | |
|
146 | 0 | } else switch (ch) { |
147 | 0 | # define add_esc_ch(_ch) { *(out++) = '\\'; *(out++) = (_ch); } |
148 | 0 | case BEL: add_esc_ch('a'); break; |
149 | 0 | case BS: add_esc_ch('b'); break; |
150 | 0 | case HT: add_esc_ch('t'); break; |
151 | 0 | case VT: add_esc_ch('v'); break; |
152 | 0 | case FF: add_esc_ch('f'); break; |
153 | 0 | case CR: add_esc_ch('r'); break; |
154 | | |
155 | 0 | 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 | 0 | while (*++text == NL) |
161 | 0 | add_esc_ch('n'); |
162 | | |
163 | | /* |
164 | | * Insert a splice before starting next line |
165 | | */ |
166 | 0 | if (*text != NUL) { |
167 | 0 | memcpy(out, nl, nl_len); |
168 | 0 | out += nl_len; |
169 | |
|
170 | 0 | continue; /* text is already at the next character */ |
171 | 0 | } |
172 | | |
173 | 0 | add_esc_ch('n'); |
174 | | /* FALLTHROUGH */ |
175 | |
|
176 | 0 | 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 | 0 | *(out++) = '"'; |
182 | 0 | *out = NUL; |
183 | 0 | return res; |
184 | | |
185 | 0 | default: |
186 | | /* |
187 | | * sprintf is safe here, because we already computed |
188 | | * the amount of space we will be using. |
189 | | */ |
190 | 0 | sprintf(out, MK_STR_OCT_FMT, ch); |
191 | 0 | out += 4; |
192 | 0 | } |
193 | | |
194 | 0 | text++; |
195 | 0 | # undef add_esc_ch |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | | /** |
200 | | * Print out escaped apostorophes. |
201 | | * |
202 | | * @param[in] str the apostrophies to print |
203 | | */ |
204 | | static char const * |
205 | | print_quoted_apostrophes(char const * str) |
206 | 0 | { |
207 | 0 | while (*str == APOSTROPHE) { |
208 | 0 | fputs(QUOT_APOS, stdout); |
209 | 0 | str++; |
210 | 0 | } |
211 | 0 | return str; |
212 | 0 | } |
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 |
221 | | print_quot_str(char const * str) |
222 | 0 | { |
223 | | /* |
224 | | * Handle empty strings to make the rest of the logic simpler. |
225 | | */ |
226 | 0 | if ((str == NULL) || (*str == NUL)) { |
227 | 0 | fputs(EMPTY_ARG, stdout); |
228 | 0 | return; |
229 | 0 | } |
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 | 0 | str = print_quoted_apostrophes(str); |
236 | 0 | if (*str == NUL) |
237 | 0 | return; |
238 | | |
239 | | /* |
240 | | * Start the single quote string |
241 | | */ |
242 | 0 | fputc(APOSTROPHE, stdout); |
243 | 0 | for (;;) { |
244 | 0 | char const * pz = strchr(str, APOSTROPHE); |
245 | 0 | if (pz == NULL) |
246 | 0 | break; |
247 | | |
248 | | /* |
249 | | * Emit the string up to the single quote (apostrophe) we just found. |
250 | | */ |
251 | 0 | (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 | 0 | fputc(APOSTROPHE, stdout); |
258 | 0 | str = print_quoted_apostrophes(pz); |
259 | 0 | if (*str == NUL) |
260 | 0 | return; |
261 | | |
262 | 0 | fputc(APOSTROPHE, stdout); |
263 | 0 | } |
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 | 0 | fputs(str, stdout); |
270 | 0 | fputc(APOSTROPHE, stdout); |
271 | 0 | } |
272 | | |
273 | | static void |
274 | | print_enumeration(tOptions * pOpts, tOptDesc * pOD) |
275 | 0 | { |
276 | 0 | uintptr_t e_val = pOD->optArg.argEnum; |
277 | 0 | printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
278 | | |
279 | | /* |
280 | | * Convert value to string, print that and restore numeric value. |
281 | | */ |
282 | 0 | (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); |
283 | 0 | printf(QUOT_ARG_FMT, pOD->optArg.argString); |
284 | 0 | if (pOD->fOptState & OPTST_ALLOC_ARG) |
285 | 0 | AGFREE(pOD->optArg.argString); |
286 | 0 | pOD->optArg.argEnum = e_val; |
287 | |
|
288 | 0 | printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
289 | 0 | } |
290 | | |
291 | | static void |
292 | | print_membership(tOptions * pOpts, tOptDesc * pOD) |
293 | 0 | { |
294 | 0 | char const * svstr = pOD->optArg.argString; |
295 | 0 | char const * pz; |
296 | 0 | uintptr_t val = 1; |
297 | 0 | printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
298 | 0 | (int)(uintptr_t)(pOD->optCookie)); |
299 | 0 | pOD->optCookie = VOIDP(~0UL); |
300 | 0 | (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); |
301 | |
|
302 | 0 | pz = pOD->optArg.argString; |
303 | 0 | while (*pz != NUL) { |
304 | 0 | printf("readonly %s_", pOD->pz_NAME); |
305 | 0 | pz = SPN_PLUS_N_SPACE_CHARS(pz); |
306 | |
|
307 | 0 | for (;;) { |
308 | 0 | int ch = *(pz++); |
309 | 0 | if (IS_LOWER_CASE_CHAR(ch)) fputc(toupper(ch), stdout); |
310 | 0 | else if (IS_UPPER_CASE_CHAR(ch)) fputc(ch, stdout); |
311 | 0 | else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done; |
312 | 0 | else if (ch == NUL) { pz--; goto name_done; } |
313 | 0 | else fputc('_', stdout); |
314 | 0 | } name_done:; |
315 | 0 | printf(SHOW_VAL_FMT, (unsigned long)val); |
316 | 0 | val <<= 1; |
317 | 0 | } |
318 | | |
319 | 0 | AGFREE(pOD->optArg.argString); |
320 | 0 | pOD->optArg.argString = svstr; |
321 | 0 | } |
322 | | |
323 | | static void |
324 | | print_stacked_arg(tOptions * pOpts, tOptDesc * pOD) |
325 | 0 | { |
326 | 0 | tArgList * pAL = (tArgList *)pOD->optCookie; |
327 | 0 | char const ** ppz = pAL->apzArgs; |
328 | 0 | int ct = pAL->useCt; |
329 | |
|
330 | 0 | printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct); |
331 | |
|
332 | 0 | while (--ct >= 0) { |
333 | 0 | printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, |
334 | 0 | pAL->useCt - ct); |
335 | 0 | print_quot_str(*(ppz++)); |
336 | 0 | printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, |
337 | 0 | pAL->useCt - ct); |
338 | 0 | } |
339 | 0 | } |
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 |
348 | | print_reordering(tOptions * opts) |
349 | 0 | { |
350 | 0 | unsigned int ix; |
351 | |
|
352 | 0 | fputs(set_dash, stdout); |
353 | |
|
354 | 0 | for (ix = opts->curOptIdx; |
355 | 0 | ix < opts->origArgCt; |
356 | 0 | ix++) { |
357 | 0 | fputc(' ', stdout); |
358 | 0 | print_quot_str(opts->origArgVect[ ix ]); |
359 | 0 | } |
360 | 0 | fputs(init_optct, stdout); |
361 | 0 | } |
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 |
371 | | optionPutShell(tOptions * pOpts) |
372 | 0 | { |
373 | 0 | int optIx = 0; |
374 | |
|
375 | 0 | printf(zOptCtFmt, pOpts->curOptIdx-1); |
376 | |
|
377 | 0 | do { |
378 | 0 | tOptDesc * pOD = pOpts->pOptDesc + optIx; |
379 | |
|
380 | 0 | if ((pOD->fOptState & OPTST_NO_OUTPUT_MASK) != 0) |
381 | 0 | 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 | 0 | if (pOD->optEquivIndex != NO_EQUIVALENT) |
391 | 0 | 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 | 0 | if (pOD->optActualIndex != optIx) { |
399 | 0 | tOptDesc * p = pOpts->pOptDesc + pOD->optActualIndex; |
400 | 0 | p->optArg = pOD->optArg; |
401 | 0 | p->fOptState &= OPTST_PERSISTENT_MASK; |
402 | 0 | p->fOptState |= pOD->fOptState & ~OPTST_PERSISTENT_MASK; |
403 | 0 | printf(zEquivMode, pOpts->pzPROGNAME, pOD->pz_NAME, p->pz_NAME); |
404 | 0 | pOD = p; |
405 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) { |
413 | 0 | print_membership(pOpts, pOD); |
414 | 0 | continue; |
415 | 0 | } |
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 | 0 | if (UNUSED_OPT(pOD) && DISABLED_OPT(pOD)) |
424 | 0 | continue; |
425 | | |
426 | | /* |
427 | | * Handle stacked arguments |
428 | | */ |
429 | 0 | if ( (pOD->fOptState & OPTST_STACKED) |
430 | 0 | && (pOD->optCookie != NULL) ) { |
431 | 0 | print_stacked_arg(pOpts, pOD); |
432 | 0 | continue; |
433 | 0 | } |
434 | | |
435 | | /* |
436 | | * If the argument has been disabled, |
437 | | * Then set its value to the disablement string |
438 | | */ |
439 | 0 | if ((pOD->fOptState & OPTST_DISABLED) != 0) { |
440 | 0 | printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME, |
441 | 0 | (pOD->pz_DisablePfx != NULL) |
442 | 0 | ? pOD->pz_DisablePfx : "false"); |
443 | 0 | continue; |
444 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) { |
451 | 0 | printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
452 | 0 | (int)pOD->optArg.argInt); |
453 | 0 | continue; |
454 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) { |
462 | 0 | print_enumeration(pOpts, pOD); |
463 | 0 | continue; |
464 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) { |
471 | 0 | printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
472 | 0 | (pOD->optArg.argBool == 0) ? "false" : "true"); |
473 | 0 | continue; |
474 | 0 | } |
475 | | |
476 | | /* |
477 | | * IF the option has an empty value, |
478 | | * THEN we set the argument to the occurrence count. |
479 | | */ |
480 | 0 | if ( (pOD->optArg.argString == NULL) |
481 | 0 | || (pOD->optArg.argString[0] == NUL) ) { |
482 | |
|
483 | 0 | printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
484 | 0 | pOD->optOccCt); |
485 | 0 | continue; |
486 | 0 | } |
487 | | |
488 | | /* |
489 | | * This option has a text value |
490 | | */ |
491 | 0 | printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
492 | 0 | print_quot_str(pOD->optArg.argString); |
493 | 0 | printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
494 | |
|
495 | 0 | } while (++optIx < pOpts->presetOptCt ); |
496 | | |
497 | 0 | if ( ((pOpts->fOptSet & OPTPROC_REORDER) != 0) |
498 | 0 | && (pOpts->curOptIdx < pOpts->origArgCt)) |
499 | 0 | print_reordering(pOpts); |
500 | |
|
501 | 0 | fflush(stdout); |
502 | 0 | } |
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 */ |