1 /*	$NetBSD: putshell.c,v 1.4 2014/12/19 20:43:20 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-2014 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
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 *
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 *
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
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
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
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 = (void*)(uintptr_t)~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
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
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
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