/src/ntp-dev/sntp/libopts/putshell.c
Line | Count | Source |
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 |
44 | | string_size(char const * scan, size_t nl_len) |
45 | 0 | { |
46 | | /* |
47 | | * Start by counting the start and end quotes, plus the NUL. |
48 | | */ |
49 | 0 | size_t res_ln = 3; |
50 | |
|
51 | 0 | for (;;) { |
52 | 0 | char ch = *(scan++); |
53 | 0 | if ((ch >= ' ') && (ch <= '~')) { |
54 | | |
55 | | /* |
56 | | * a backslash allowance for double quotes and baskslashes |
57 | | */ |
58 | 0 | res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1; |
59 | 0 | } |
60 | | |
61 | | /* |
62 | | * When not a normal character, then count the characters |
63 | | * required to represent whatever it is. |
64 | | */ |
65 | 0 | else switch (ch) { |
66 | 0 | case NUL: |
67 | 0 | return res_ln; |
68 | | |
69 | 0 | case NL: |
70 | 0 | res_ln += nl_len; |
71 | 0 | break; |
72 | | |
73 | 0 | case HT: |
74 | 0 | case BEL: |
75 | 0 | case BS: |
76 | 0 | case FF: |
77 | 0 | case CR: |
78 | 0 | case VT: |
79 | 0 | res_ln += 2; |
80 | 0 | break; |
81 | | |
82 | 0 | default: |
83 | 0 | res_ln += 4; /* text len for \xNN */ |
84 | 0 | } |
85 | 0 | } |
86 | 0 | } |
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 * |
106 | | optionQuoteString(char const * text, char const * nl) |
107 | 0 | { |
108 | 0 | size_t nl_len = strlen(nl); |
109 | 0 | size_t out_sz = string_size(text, nl_len); |
110 | 0 | char * out; |
111 | 0 | char * res = out = AGALOC(out_sz, "quot str"); |
112 | |
|
113 | 0 | *(out++) = '"'; |
114 | |
|
115 | 0 | for (;;) { |
116 | 0 | unsigned char ch = (unsigned char)*text; |
117 | 0 | if ((ch >= ' ') && (ch <= '~')) { |
118 | 0 | if ((ch == '"') || (ch == '\\')) |
119 | | /* |
120 | | * We must escape these characters in the output string |
121 | | */ |
122 | 0 | *(out++) = '\\'; |
123 | 0 | *(out++) = (char)ch; |
124 | |
|
125 | 0 | } else switch (ch) { |
126 | 0 | # define add_esc_ch(_ch) { *(out++) = '\\'; *(out++) = (_ch); } |
127 | 0 | case BEL: add_esc_ch('a'); break; |
128 | 0 | case BS: add_esc_ch('b'); break; |
129 | 0 | case HT: add_esc_ch('t'); break; |
130 | 0 | case VT: add_esc_ch('v'); break; |
131 | 0 | case FF: add_esc_ch('f'); break; |
132 | 0 | case CR: add_esc_ch('r'); break; |
133 | | |
134 | 0 | 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 | 0 | while (*++text == NL) |
140 | 0 | add_esc_ch('n'); |
141 | | |
142 | | /* |
143 | | * Insert a splice before starting next line |
144 | | */ |
145 | 0 | if (*text != NUL) { |
146 | 0 | memcpy(out, nl, nl_len); |
147 | 0 | out += nl_len; |
148 | |
|
149 | 0 | continue; /* text is already at the next character */ |
150 | 0 | } |
151 | | |
152 | 0 | add_esc_ch('n'); |
153 | | /* FALLTHROUGH */ |
154 | |
|
155 | 0 | 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 | 0 | *(out++) = '"'; |
161 | 0 | *(out++) = NUL; |
162 | 0 | #ifndef NDEBUG |
163 | 0 | if ((size_t)(out - res) > out_sz) { |
164 | 0 | fputs(misguess_len, stderr); |
165 | 0 | option_exits(EXIT_FAILURE); |
166 | 0 | } |
167 | 0 | #endif |
168 | 0 | return res; |
169 | | |
170 | 0 | 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 | 0 | out += sprintf(out, MK_STR_OCT_FMT, ch); |
176 | 0 | } |
177 | | |
178 | 0 | text++; |
179 | 0 | # undef add_esc_ch |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | /** |
184 | | * Print out escaped apostorophes. |
185 | | * |
186 | | * @param[in] str the apostrophies to print |
187 | | */ |
188 | | static char const * |
189 | | print_quoted_apostrophes(char const * str) |
190 | 0 | { |
191 | 0 | while (*str == APOSTROPHE) { |
192 | 0 | fputs(QUOT_APOS, stdout); |
193 | 0 | str++; |
194 | 0 | } |
195 | 0 | return str; |
196 | 0 | } |
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 |
205 | | print_quot_str(char const * str) |
206 | 0 | { |
207 | | /* |
208 | | * Handle empty strings to make the rest of the logic simpler. |
209 | | */ |
210 | 0 | if ((str == NULL) || (*str == NUL)) { |
211 | 0 | fputs(EMPTY_ARG, stdout); |
212 | 0 | return; |
213 | 0 | } |
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 | 0 | str = print_quoted_apostrophes(str); |
220 | 0 | if (*str == NUL) |
221 | 0 | return; |
222 | | |
223 | | /* |
224 | | * Start the single quote string |
225 | | */ |
226 | 0 | fputc(APOSTROPHE, stdout); |
227 | 0 | for (;;) { |
228 | 0 | char const * pz = strchr(str, APOSTROPHE); |
229 | 0 | if (pz == NULL) |
230 | 0 | break; |
231 | | |
232 | | /* |
233 | | * Emit the string up to the single quote (apostrophe) we just found. |
234 | | */ |
235 | 0 | (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 | 0 | fputc(APOSTROPHE, stdout); |
242 | 0 | str = print_quoted_apostrophes(pz); |
243 | 0 | if (*str == NUL) |
244 | 0 | return; |
245 | | |
246 | 0 | fputc(APOSTROPHE, stdout); |
247 | 0 | } |
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 | 0 | fputs(str, stdout); |
254 | 0 | fputc(APOSTROPHE, stdout); |
255 | 0 | } |
256 | | |
257 | | static void |
258 | | print_enumeration(tOptions * pOpts, tOptDesc * pOD) |
259 | 0 | { |
260 | 0 | uintptr_t e_val = pOD->optArg.argEnum; |
261 | 0 | printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
262 | | |
263 | | /* |
264 | | * Convert value to string, print that and restore numeric value. |
265 | | */ |
266 | 0 | (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); |
267 | 0 | printf(QUOT_ARG_FMT, pOD->optArg.argString); |
268 | 0 | if (pOD->fOptState & OPTST_ALLOC_ARG) |
269 | 0 | AGFREE(pOD->optArg.argString); |
270 | 0 | pOD->optArg.argEnum = e_val; |
271 | |
|
272 | 0 | printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
273 | 0 | } |
274 | | |
275 | | static void |
276 | | print_membership(tOptions * pOpts, tOptDesc * pOD) |
277 | 0 | { |
278 | 0 | char const * svstr = pOD->optArg.argString; |
279 | 0 | char const * pz; |
280 | 0 | uintptr_t val = 1; |
281 | 0 | printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
282 | 0 | (int)(uintptr_t)(pOD->optCookie)); |
283 | 0 | pOD->optCookie = VOIDP(~0UL); |
284 | 0 | (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD); |
285 | |
|
286 | 0 | pz = pOD->optArg.argString; |
287 | 0 | while (*pz != NUL) { |
288 | 0 | printf("readonly %s_", pOD->pz_NAME); |
289 | 0 | pz = SPN_PLUS_N_SPACE_CHARS(pz); |
290 | |
|
291 | 0 | for (;;) { |
292 | 0 | int ch = *(pz++); |
293 | 0 | if (IS_LOWER_CASE_CHAR(ch)) fputc(toupper(ch), stdout); |
294 | 0 | else if (IS_UPPER_CASE_CHAR(ch)) fputc(ch, stdout); |
295 | 0 | else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done; |
296 | 0 | else if (ch == NUL) { pz--; goto name_done; } |
297 | 0 | else fputc('_', stdout); |
298 | 0 | } name_done:; |
299 | 0 | printf(SHOW_VAL_FMT, (unsigned long)val); |
300 | 0 | val <<= 1; |
301 | 0 | } |
302 | | |
303 | 0 | AGFREE(pOD->optArg.argString); |
304 | 0 | pOD->optArg.argString = svstr; |
305 | 0 | } |
306 | | |
307 | | static void |
308 | | print_stacked_arg(tOptions * pOpts, tOptDesc * pOD) |
309 | 0 | { |
310 | 0 | tArgList * pAL = (tArgList *)pOD->optCookie; |
311 | 0 | char const ** ppz = pAL->apzArgs; |
312 | 0 | int ct = pAL->useCt; |
313 | |
|
314 | 0 | printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct); |
315 | |
|
316 | 0 | while (--ct >= 0) { |
317 | 0 | printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, |
318 | 0 | pAL->useCt - ct); |
319 | 0 | print_quot_str(*(ppz++)); |
320 | 0 | printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME, |
321 | 0 | pAL->useCt - ct); |
322 | 0 | } |
323 | 0 | } |
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 |
332 | | print_reordering(tOptions * opts) |
333 | 0 | { |
334 | 0 | unsigned int ix; |
335 | |
|
336 | 0 | fputs(set_dash, stdout); |
337 | |
|
338 | 0 | for (ix = opts->curOptIdx; |
339 | 0 | ix < opts->origArgCt; |
340 | 0 | ix++) { |
341 | 0 | fputc(' ', stdout); |
342 | 0 | print_quot_str(opts->origArgVect[ ix ]); |
343 | 0 | } |
344 | 0 | fputs(init_optct, stdout); |
345 | 0 | } |
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 |
355 | | optionPutShell(tOptions * pOpts) |
356 | 0 | { |
357 | 0 | int optIx = 0; |
358 | |
|
359 | 0 | printf(zOptCtFmt, pOpts->curOptIdx-1); |
360 | |
|
361 | 0 | do { |
362 | 0 | tOptDesc * pOD = pOpts->pOptDesc + optIx; |
363 | |
|
364 | 0 | if ((pOD->fOptState & OPTST_NO_OUTPUT_MASK) != 0) |
365 | 0 | 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 | 0 | if (pOD->optEquivIndex != NO_EQUIVALENT) |
375 | 0 | 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 | 0 | if (pOD->optActualIndex != optIx) { |
383 | 0 | tOptDesc * p = pOpts->pOptDesc + pOD->optActualIndex; |
384 | 0 | p->optArg = pOD->optArg; |
385 | 0 | p->fOptState &= OPTST_PERSISTENT_MASK; |
386 | 0 | p->fOptState |= pOD->fOptState & ~OPTST_PERSISTENT_MASK; |
387 | 0 | printf(zEquivMode, pOpts->pzPROGNAME, pOD->pz_NAME, p->pz_NAME); |
388 | 0 | pOD = p; |
389 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) { |
397 | 0 | print_membership(pOpts, pOD); |
398 | 0 | continue; |
399 | 0 | } |
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 | 0 | if (UNUSED_OPT(pOD) && DISABLED_OPT(pOD)) |
408 | 0 | continue; |
409 | | |
410 | | /* |
411 | | * Handle stacked arguments |
412 | | */ |
413 | 0 | if ( (pOD->fOptState & OPTST_STACKED) |
414 | 0 | && (pOD->optCookie != NULL) ) { |
415 | 0 | print_stacked_arg(pOpts, pOD); |
416 | 0 | continue; |
417 | 0 | } |
418 | | |
419 | | /* |
420 | | * If the argument has been disabled, |
421 | | * Then set its value to the disablement string |
422 | | */ |
423 | 0 | if ((pOD->fOptState & OPTST_DISABLED) != 0) { |
424 | 0 | printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME, |
425 | 0 | (pOD->pz_DisablePfx != NULL) |
426 | 0 | ? pOD->pz_DisablePfx : "false"); |
427 | 0 | continue; |
428 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) { |
435 | 0 | printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
436 | 0 | (int)pOD->optArg.argInt); |
437 | 0 | continue; |
438 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) { |
446 | 0 | print_enumeration(pOpts, pOD); |
447 | 0 | continue; |
448 | 0 | } |
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 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) { |
455 | 0 | printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
456 | 0 | (pOD->optArg.argBool == 0) ? "false" : "true"); |
457 | 0 | continue; |
458 | 0 | } |
459 | | |
460 | | /* |
461 | | * IF the option has an empty value, |
462 | | * THEN we set the argument to the occurrence count. |
463 | | */ |
464 | 0 | if ( (pOD->optArg.argString == NULL) |
465 | 0 | || (pOD->optArg.argString[0] == NUL) ) { |
466 | |
|
467 | 0 | printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME, |
468 | 0 | pOD->optOccCt); |
469 | 0 | continue; |
470 | 0 | } |
471 | | |
472 | | /* |
473 | | * This option has a text value |
474 | | */ |
475 | 0 | printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
476 | 0 | print_quot_str(pOD->optArg.argString); |
477 | 0 | printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME); |
478 | |
|
479 | 0 | } while (++optIx < pOpts->presetOptCt ); |
480 | |
|
481 | 0 | if ( ((pOpts->fOptSet & OPTPROC_REORDER) != 0) |
482 | 0 | && (pOpts->curOptIdx < pOpts->origArgCt)) |
483 | 0 | print_reordering(pOpts); |
484 | |
|
485 | | fflush(stdout); |
486 | 0 | } |
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 */ |