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 = = = */
35static size_t
36string_size(char const * scan, size_t nl_len);
37
38static char const *
39print_quoted_apostrophes(char const * str);
40
41static void
42print_quot_str(char const * str);
43
44static void
45print_enumeration(tOptions * pOpts, tOptDesc * pOD);
46
47static void
48print_membership(tOptions * pOpts, tOptDesc * pOD);
49
50static void
51print_stacked_arg(tOptions * pOpts, tOptDesc * pOD);
52
53static void
54print_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 */
66static size_t
67string_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=*/
128char const *
129optionQuoteString(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 */
204static char const *
205print_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 */
220static void
221print_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
273static void
274print_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
291static void
292print_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
323static void
324print_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 */
347static void
348print_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=*/
370void
371optionPutShell(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