xref: /freebsd/contrib/ntp/sntp/libopts/putshell.c (revision 0a36787e4c1fa0cf77dcf83be0867178476e372b)
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 {
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 *
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 *
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
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
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
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
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
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
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