1181834Sroberto
2280849Scy/**
3280849Scy * \file putshell.c
4181834Sroberto *
5181834Sroberto *  This module will interpret the options set in the tOptions
6181834Sroberto *  structure and print them to standard out in a fashion that
7181834Sroberto *  will allow them to be interpreted by the Bourne or Korn shells.
8280849Scy *
9280849Scy * @addtogroup autoopts
10280849Scy * @{
11181834Sroberto */
12181834Sroberto/*
13280849Scy *  This file is part of AutoOpts, a companion to AutoGen.
14280849Scy *  AutoOpts is free software.
15285169Scy *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
16181834Sroberto *
17280849Scy *  AutoOpts is available under any one of two licenses.  The license
18280849Scy *  in use must be one of these two and the choice is under the control
19280849Scy *  of the user of the license.
20181834Sroberto *
21280849Scy *   The GNU Lesser General Public License, version 3 or later
22280849Scy *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
23181834Sroberto *
24280849Scy *   The Modified Berkeley Software Distribution License
25280849Scy *      See the file "COPYING.mbsd"
26181834Sroberto *
27280849Scy *  These files have the following sha256 sums:
28181834Sroberto *
29280849Scy *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
30280849Scy *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
31280849Scy *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
32181834Sroberto */
33181834Sroberto
34181834Sroberto/* = = = START-STATIC-FORWARD = = = */
35280849Scystatic size_t
36280849Scystring_size(char const * scan, size_t nl_len);
37280849Scy
38280849Scystatic char const *
39280849Scyprint_quoted_apostrophes(char const * str);
40280849Scy
41181834Srobertostatic void
42280849Scyprint_quot_str(char const * str);
43280849Scy
44280849Scystatic void
45280849Scyprint_enumeration(tOptions * pOpts, tOptDesc * pOD);
46280849Scy
47280849Scystatic void
48280849Scyprint_membership(tOptions * pOpts, tOptDesc * pOD);
49280849Scy
50280849Scystatic void
51280849Scyprint_stacked_arg(tOptions * pOpts, tOptDesc * pOD);
52280849Scy
53280849Scystatic void
54280849Scyprint_reordering(tOptions * opts);
55181834Sroberto/* = = = END-STATIC-FORWARD = = = */
56181834Sroberto
57280849Scy/**
58280849Scy * Count the number of bytes required to represent a string as a
59280849Scy * compilable string.
60280849Scy *
61280849Scy * @param[in] scan    the text to be rewritten as a C program text string.
62280849Scy * @param[in] nl_len  the number of bytes used for each embedded newline.
63280849Scy *
64280849Scy * @returns the count, including the terminating NUL byte.
65181834Sroberto */
66280849Scystatic size_t
67280849Scystring_size(char const * scan, size_t nl_len)
68280849Scy{
69280849Scy    /*
70280849Scy     *  Start by counting the start and end quotes, plus the NUL.
71280849Scy     */
72280849Scy    size_t res_ln = 3;
73280849Scy
74280849Scy    for (;;) {
75280849Scy        char ch = *(scan++);
76280849Scy        if ((ch >= ' ') && (ch <= '~')) {
77280849Scy
78280849Scy            /*
79280849Scy             * a backslash allowance for double quotes and baskslashes
80280849Scy             */
81280849Scy            res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1;
82280849Scy        }
83280849Scy
84280849Scy        /*
85280849Scy         *  When not a normal character, then count the characters
86280849Scy         *  required to represent whatever it is.
87280849Scy         */
88280849Scy        else switch (ch) {
89280849Scy        case NUL:
90280849Scy            return res_ln;
91280849Scy
92280849Scy        case NL:
93280849Scy            res_ln += nl_len;
94280849Scy            break;
95280849Scy
96280849Scy        case HT:
97280849Scy        case BEL:
98280849Scy        case BS:
99280849Scy        case FF:
100280849Scy        case CR:
101280849Scy        case VT:
102280849Scy            res_ln += 2;
103280849Scy            break;
104280849Scy
105280849Scy        default:
106280849Scy            res_ln += 4; /* text len for \xNN */
107280849Scy        }
108280849Scy    }
109280849Scy}
110280849Scy
111280849Scy/*=export_func  optionQuoteString
112280849Scy * private:
113280849Scy *
114280849Scy * what:  Print a string as quoted text suitable for a C compiler.
115280849Scy * arg:   + char const * + text  + a block of text to quote +
116280849Scy * arg:   + char const * + nl    + line splice text         +
117280849Scy *
118280849Scy * ret_type:  char const *
119280849Scy * ret_desc:  the allocated input string as a quoted string
120280849Scy *
121280849Scy * doc:
122280849Scy *  This is for internal use by autogen and autoopts.
123280849Scy *  It takes an input string and produces text the C compiler can process
124280849Scy *  to produce an exact copy of the original string.
125280849Scy *  The caller must deallocate the result.  Standard C strings and
126280849Scy *  K&R strings are distinguished by the "nl" string.
127280849Scy=*/
128280849Scychar const *
129280849ScyoptionQuoteString(char const * text, char const * nl)
130280849Scy{
131280849Scy    size_t   nl_len = strlen(nl);
132280849Scy    char *   out;
133280849Scy    char *   res = out = AGALOC(string_size(text, nl_len), "quot str");
134280849Scy    *(out++) = '"';
135280849Scy
136280849Scy    for (;;) {
137280849Scy        unsigned char ch = (unsigned char)*text;
138280849Scy        if ((ch >= ' ') && (ch <= '~')) {
139280849Scy            if ((ch == '"') || (ch == '\\'))
140280849Scy                /*
141280849Scy                 *  We must escape these characters in the output string
142280849Scy                 */
143280849Scy                *(out++) = '\\';
144280849Scy            *(out++) = (char)ch;
145280849Scy
146280849Scy        } else switch (ch) {
147280849Scy#       define   add_esc_ch(_ch)  { *(out++) = '\\'; *(out++) = (_ch); }
148280849Scy        case BEL: add_esc_ch('a'); break;
149280849Scy        case BS:  add_esc_ch('b'); break;
150280849Scy        case HT:  add_esc_ch('t'); break;
151280849Scy        case VT:  add_esc_ch('v'); break;
152280849Scy        case FF:  add_esc_ch('f'); break;
153280849Scy        case CR:  add_esc_ch('r'); break;
154280849Scy
155280849Scy        case LF:
156280849Scy            /*
157280849Scy             *  Place contiguous new-lines on a single line.
158280849Scy             *  The current character is a NL, check the next one.
159280849Scy             */
160280849Scy            while (*++text == NL)
161280849Scy                add_esc_ch('n');
162280849Scy
163280849Scy            /*
164280849Scy             *  Insert a splice before starting next line
165280849Scy             */
166280849Scy            if (*text != NUL) {
167280849Scy                memcpy(out, nl, nl_len);
168280849Scy                out += nl_len;
169280849Scy
170280849Scy                continue; /* text is already at the next character */
171280849Scy            }
172280849Scy
173280849Scy            add_esc_ch('n');
174280849Scy            /* FALLTHROUGH */
175280849Scy
176280849Scy        case NUL:
177280849Scy            /*
178280849Scy             *  End of string.  Terminate the quoted output.  If necessary,
179280849Scy             *  deallocate the text string.  Return the scan resumption point.
180280849Scy             */
181280849Scy            *(out++) = '"';
182280849Scy            *out = NUL;
183280849Scy            return res;
184280849Scy
185280849Scy        default:
186280849Scy            /*
187280849Scy             *  sprintf is safe here, because we already computed
188280849Scy             *  the amount of space we will be using.
189280849Scy             */
190280849Scy            sprintf(out, MK_STR_OCT_FMT, ch);
191280849Scy            out += 4;
192280849Scy        }
193280849Scy
194280849Scy        text++;
195280849Scy#       undef add_esc_ch
196280849Scy    }
197280849Scy}
198280849Scy
199280849Scy/**
200280849Scy *  Print out escaped apostorophes.
201280849Scy *
202280849Scy *  @param[in] str  the apostrophies to print
203280849Scy */
204280849Scystatic char const *
205280849Scyprint_quoted_apostrophes(char const * str)
206280849Scy{
207280849Scy    while (*str == APOSTROPHE) {
208280849Scy        fputs(QUOT_APOS, stdout);
209280849Scy        str++;
210280849Scy    }
211280849Scy    return str;
212280849Scy}
213280849Scy
214280849Scy/**
215280849Scy *  Print a single quote (apostrophe quoted) string.
216280849Scy *  Other than somersaults for apostrophes, nothing else needs quoting.
217280849Scy *
218280849Scy *  @param[in] str  the string to print
219280849Scy */
220181834Srobertostatic void
221280849Scyprint_quot_str(char const * str)
222181834Sroberto{
223181834Sroberto    /*
224181834Sroberto     *  Handle empty strings to make the rest of the logic simpler.
225181834Sroberto     */
226280849Scy    if ((str == NULL) || (*str == NUL)) {
227280849Scy        fputs(EMPTY_ARG, stdout);
228181834Sroberto        return;
229181834Sroberto    }
230181834Sroberto
231181834Sroberto    /*
232181834Sroberto     *  Emit any single quotes/apostrophes at the start of the string and
233181834Sroberto     *  bail if that is all we need to do.
234181834Sroberto     */
235280849Scy    str = print_quoted_apostrophes(str);
236280849Scy    if (*str == NUL)
237181834Sroberto        return;
238181834Sroberto
239181834Sroberto    /*
240181834Sroberto     *  Start the single quote string
241181834Sroberto     */
242280849Scy    fputc(APOSTROPHE, stdout);
243181834Sroberto    for (;;) {
244280849Scy        char const * pz = strchr(str, APOSTROPHE);
245181834Sroberto        if (pz == NULL)
246181834Sroberto            break;
247181834Sroberto
248181834Sroberto        /*
249181834Sroberto         *  Emit the string up to the single quote (apostrophe) we just found.
250181834Sroberto         */
251280849Scy        (void)fwrite(str, (size_t)(pz - str), (size_t)1, stdout);
252181834Sroberto
253181834Sroberto        /*
254280849Scy         * Close the current string, emit the apostrophes and re-open the
255280849Scy         * string (IFF there is more text to print).
256181834Sroberto         */
257280849Scy        fputc(APOSTROPHE, stdout);
258280849Scy        str = print_quoted_apostrophes(pz);
259280849Scy        if (*str == NUL)
260181834Sroberto            return;
261181834Sroberto
262280849Scy        fputc(APOSTROPHE, stdout);
263181834Sroberto    }
264181834Sroberto
265181834Sroberto    /*
266181834Sroberto     *  If we broke out of the loop, we must still emit the remaining text
267181834Sroberto     *  and then close the single quote string.
268181834Sroberto     */
269280849Scy    fputs(str, stdout);
270280849Scy    fputc(APOSTROPHE, stdout);
271181834Sroberto}
272181834Sroberto
273280849Scystatic void
274280849Scyprint_enumeration(tOptions * pOpts, tOptDesc * pOD)
275280849Scy{
276280849Scy    uintptr_t e_val = pOD->optArg.argEnum;
277280849Scy    printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
278181834Sroberto
279280849Scy    /*
280280849Scy     *  Convert value to string, print that and restore numeric value.
281280849Scy     */
282280849Scy    (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
283280849Scy    printf(QUOT_ARG_FMT, pOD->optArg.argString);
284280849Scy    if (pOD->fOptState & OPTST_ALLOC_ARG)
285280849Scy        AGFREE(pOD->optArg.argString);
286280849Scy    pOD->optArg.argEnum = e_val;
287280849Scy
288280849Scy    printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
289280849Scy}
290280849Scy
291280849Scystatic void
292280849Scyprint_membership(tOptions * pOpts, tOptDesc * pOD)
293280849Scy{
294280849Scy    char const * svstr = pOD->optArg.argString;
295280849Scy    char const * pz;
296280849Scy    uintptr_t val = 1;
297280849Scy    printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
298280849Scy           (int)(uintptr_t)(pOD->optCookie));
299285169Scy    pOD->optCookie = VOIDP(~0UL);
300280849Scy    (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
301280849Scy
302280849Scy    pz = pOD->optArg.argString;
303280849Scy    while (*pz != NUL) {
304280849Scy        printf("readonly %s_", pOD->pz_NAME);
305280849Scy        pz = SPN_PLUS_N_SPACE_CHARS(pz);
306280849Scy
307280849Scy        for (;;) {
308280849Scy            int ch = *(pz++);
309280849Scy            if (IS_LOWER_CASE_CHAR(ch))   fputc(toupper(ch), stdout);
310280849Scy            else if (IS_UPPER_CASE_CHAR(ch))   fputc(ch, stdout);
311280849Scy            else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done;
312280849Scy            else if (ch == NUL)        { pz--; goto name_done; }
313280849Scy            else fputc('_', stdout);
314280849Scy        } name_done:;
315280849Scy        printf(SHOW_VAL_FMT, (unsigned long)val);
316280849Scy        val <<= 1;
317280849Scy    }
318280849Scy
319280849Scy    AGFREE(pOD->optArg.argString);
320280849Scy    pOD->optArg.argString = svstr;
321280849Scy}
322280849Scy
323280849Scystatic void
324280849Scyprint_stacked_arg(tOptions * pOpts, tOptDesc * pOD)
325280849Scy{
326285169Scy    tArgList *      pAL = (tArgList *)pOD->optCookie;
327280849Scy    char const **   ppz = pAL->apzArgs;
328280849Scy    int             ct  = pAL->useCt;
329280849Scy
330280849Scy    printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct);
331280849Scy
332280849Scy    while (--ct >= 0) {
333280849Scy        printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME,
334280849Scy               pAL->useCt - ct);
335280849Scy        print_quot_str(*(ppz++));
336280849Scy        printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME,
337280849Scy               pAL->useCt - ct);
338280849Scy    }
339280849Scy}
340280849Scy
341280849Scy/**
342280849Scy * emit the arguments as readily parsed text.
343280849Scy * The program options are set by emitting the shell "set" command.
344280849Scy *
345280849Scy * @param[in] opts  the program options structure
346280849Scy */
347280849Scystatic void
348280849Scyprint_reordering(tOptions * opts)
349280849Scy{
350280849Scy    unsigned int ix;
351280849Scy
352280849Scy    fputs(set_dash, stdout);
353280849Scy
354280849Scy    for (ix = opts->curOptIdx;
355280849Scy         ix < opts->origArgCt;
356280849Scy         ix++) {
357280849Scy        fputc(' ', stdout);
358280849Scy        print_quot_str(opts->origArgVect[ ix ]);
359280849Scy    }
360280849Scy    fputs(init_optct, stdout);
361280849Scy}
362280849Scy
363181834Sroberto/*=export_func  optionPutShell
364181834Sroberto * what:  write a portable shell script to parse options
365181834Sroberto * private:
366285169Scy * arg:   tOptions *, pOpts, the program options descriptor
367181834Sroberto * doc:   This routine will emit portable shell script text for parsing
368181834Sroberto *        the options described in the option definitions.
369181834Sroberto=*/
370181834Srobertovoid
371285169ScyoptionPutShell(tOptions * pOpts)
372181834Sroberto{
373181834Sroberto    int  optIx = 0;
374181834Sroberto
375280849Scy    printf(zOptCtFmt, pOpts->curOptIdx-1);
376181834Sroberto
377181834Sroberto    do  {
378285169Scy        tOptDesc * pOD = pOpts->pOptDesc + optIx;
379181834Sroberto
380280849Scy        if ((pOD->fOptState & OPTST_NO_OUTPUT_MASK) != 0)
381181834Sroberto            continue;
382181834Sroberto
383181834Sroberto        /*
384181834Sroberto         *  Equivalence classes are hard to deal with.  Where the
385181834Sroberto         *  option data wind up kind of squishes around.  For the purposes
386181834Sroberto         *  of emitting shell state, they are not recommended, but we'll
387181834Sroberto         *  do something.  I guess we'll emit the equivalenced-to option
388181834Sroberto         *  at the point in time when the base option is found.
389181834Sroberto         */
390181834Sroberto        if (pOD->optEquivIndex != NO_EQUIVALENT)
391181834Sroberto            continue; /* equivalence to a different option */
392181834Sroberto
393181834Sroberto        /*
394181834Sroberto         *  Equivalenced to a different option.  Process the current option
395181834Sroberto         *  as the equivalenced-to option.  Keep the persistent state bits,
396181834Sroberto         *  but copy over the set-state bits.
397181834Sroberto         */
398181834Sroberto        if (pOD->optActualIndex != optIx) {
399285169Scy            tOptDesc * p  = pOpts->pOptDesc + pOD->optActualIndex;
400181834Sroberto            p->optArg     = pOD->optArg;
401181834Sroberto            p->fOptState &= OPTST_PERSISTENT_MASK;
402181834Sroberto            p->fOptState |= pOD->fOptState & ~OPTST_PERSISTENT_MASK;
403280849Scy            printf(zEquivMode, pOpts->pzPROGNAME, pOD->pz_NAME, p->pz_NAME);
404181834Sroberto            pOD = p;
405181834Sroberto        }
406181834Sroberto
407181834Sroberto        /*
408181834Sroberto         *  If the argument type is a set membership bitmask, then we always
409181834Sroberto         *  emit the thing.  We do this because it will always have some sort
410181834Sroberto         *  of bitmask value and we need to emit the bit values.
411181834Sroberto         */
412181834Sroberto        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
413280849Scy            print_membership(pOpts, pOD);
414181834Sroberto            continue;
415181834Sroberto        }
416181834Sroberto
417181834Sroberto        /*
418181834Sroberto         *  IF the option was either specified or it wakes up enabled,
419181834Sroberto         *  then we will emit information.  Otherwise, skip it.
420181834Sroberto         *  The idea is that if someone defines an option to initialize
421181834Sroberto         *  enabled, we should tell our shell script that it is enabled.
422181834Sroberto         */
423280849Scy        if (UNUSED_OPT(pOD) && DISABLED_OPT(pOD))
424181834Sroberto            continue;
425181834Sroberto
426181834Sroberto        /*
427181834Sroberto         *  Handle stacked arguments
428181834Sroberto         */
429181834Sroberto        if (  (pOD->fOptState & OPTST_STACKED)
430181834Sroberto           && (pOD->optCookie != NULL) )  {
431280849Scy            print_stacked_arg(pOpts, pOD);
432280849Scy            continue;
433181834Sroberto        }
434181834Sroberto
435181834Sroberto        /*
436181834Sroberto         *  If the argument has been disabled,
437181834Sroberto         *  Then set its value to the disablement string
438181834Sroberto         */
439280849Scy        if ((pOD->fOptState & OPTST_DISABLED) != 0) {
440280849Scy            printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME,
441280849Scy                   (pOD->pz_DisablePfx != NULL)
442280849Scy                   ? pOD->pz_DisablePfx : "false");
443280849Scy            continue;
444280849Scy        }
445181834Sroberto
446181834Sroberto        /*
447181834Sroberto         *  If the argument type is numeric, the last arg pointer
448181834Sroberto         *  is really the VALUE of the string that was pointed to.
449181834Sroberto         */
450280849Scy        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) {
451280849Scy            printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
452280849Scy                   (int)pOD->optArg.argInt);
453280849Scy            continue;
454280849Scy        }
455181834Sroberto
456181834Sroberto        /*
457181834Sroberto         *  If the argument type is an enumeration, then it is much
458181834Sroberto         *  like a text value, except we call the callback function
459181834Sroberto         *  to emit the value corresponding to the "optArg" number.
460181834Sroberto         */
461280849Scy        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) {
462280849Scy            print_enumeration(pOpts, pOD);
463280849Scy            continue;
464181834Sroberto        }
465181834Sroberto
466181834Sroberto        /*
467181834Sroberto         *  If the argument type is numeric, the last arg pointer
468181834Sroberto         *  is really the VALUE of the string that was pointed to.
469181834Sroberto         */
470280849Scy        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) {
471280849Scy            printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
472280849Scy                   (pOD->optArg.argBool == 0) ? "false" : "true");
473280849Scy            continue;
474280849Scy        }
475181834Sroberto
476181834Sroberto        /*
477181834Sroberto         *  IF the option has an empty value,
478181834Sroberto         *  THEN we set the argument to the occurrence count.
479181834Sroberto         */
480280849Scy        if (  (pOD->optArg.argString == NULL)
481280849Scy           || (pOD->optArg.argString[0] == NUL) ) {
482181834Sroberto
483280849Scy            printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
484280849Scy                   pOD->optOccCt);
485280849Scy            continue;
486280849Scy        }
487181834Sroberto
488181834Sroberto        /*
489181834Sroberto         *  This option has a text value
490181834Sroberto         */
491280849Scy        printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
492280849Scy        print_quot_str(pOD->optArg.argString);
493280849Scy        printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
494280849Scy
495181834Sroberto    } while (++optIx < pOpts->presetOptCt );
496181834Sroberto
497181834Sroberto    if (  ((pOpts->fOptSet & OPTPROC_REORDER) != 0)
498280849Scy       && (pOpts->curOptIdx < pOpts->origArgCt))
499280849Scy        print_reordering(pOpts);
500280849Scy
501280849Scy    fflush(stdout);
502181834Sroberto}
503181834Sroberto
504280849Scy/** @}
505280849Scy *
506181834Sroberto * Local Variables:
507181834Sroberto * mode: C
508181834Sroberto * c-file-style: "stroustrup"
509181834Sroberto * indent-tabs-mode: nil
510181834Sroberto * End:
511181834Sroberto * end of autoopts/putshell.c */
512