1/*	$NetBSD: makeshell.c,v 1.2 2012/02/03 21:36:40 christos Exp $	*/
2
3
4/**
5 * \file makeshell.c
6 *
7 * Time-stamp:      "2011-04-20 11:06:57 bkorb"
8 *
9 *  This module will interpret the options set in the tOptions
10 *  structure and create a Bourne shell script capable of parsing them.
11 *
12 *  This file is part of AutoOpts, a companion to AutoGen.
13 *  AutoOpts is free software.
14 *  AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved
15 *
16 *  AutoOpts is available under any one of two licenses.  The license
17 *  in use must be one of these two and the choice is under the control
18 *  of the user of the license.
19 *
20 *   The GNU Lesser General Public License, version 3 or later
21 *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
22 *
23 *   The Modified Berkeley Software Distribution License
24 *      See the file "COPYING.mbsd"
25 *
26 *  These files have the following md5sums:
27 *
28 *  43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
29 *  06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
30 *  66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
31 */
32
33tOptions * optionParseShellOptions = NULL;
34
35/* * * * * * * * * * * * * * * * * * * * *
36 *
37 *  Setup Format Strings
38 */
39static char const zStartMarker[] =
40"# # # # # # # # # # -- do not modify this marker --\n#\n"
41"#  DO NOT EDIT THIS SECTION";
42
43static char const zPreamble[] =
44"%s OF %s\n#\n"
45"#  From here to the next `-- do not modify this marker --',\n"
46"#  the text has been generated %s\n";
47
48static char const zEndPreamble[] =
49"#  From the %s option definitions\n#\n";
50
51static char const zMultiDef[] = "\n"
52"if test -z \"${%1$s_%2$s}\"\n"
53"then\n"
54"  %1$s_%2$s_CT=0\n"
55"else\n"
56"  %1$s_%2$s_CT=1\n"
57"  %1$s_%2$s_1=\"${%1$s_%2$s}\"\n"
58"fi\n"
59"export %1$s_%2$s_CT";
60
61static char const zSingleDef[] = "\n"
62"%1$s_%2$s=\"${%1$s_%2$s-'%3$s'}\"\n"
63"%1$s_%2$s_set=false\n"
64"export %1$s_%2$s\n";
65
66static char const zSingleNoDef[] = "\n"
67"%1$s_%2$s=\"${%1$s_%2$s}\"\n"
68"%1$s_%2$s_set=false\n"
69"export %1$s_%2$s\n";
70
71/* * * * * * * * * * * * * * * * * * * * *
72 *
73 *  LOOP START
74 *
75 *  The loop may run in either of two modes:
76 *  all options are named options (loop only)
77 *  regular, marked option processing.
78 */
79static char const zLoopCase[] = "\n"
80"OPT_PROCESS=true\n"
81"OPT_ARG=\"$1\"\n\n"
82"while ${OPT_PROCESS} && [ $# -gt 0 ]\ndo\n"
83"    OPT_ELEMENT=''\n"
84"    OPT_ARG_VAL=''\n\n"
85     /*
86      *  'OPT_ARG' may or may not match the current $1
87      */
88"    case \"${OPT_ARG}\" in\n"
89"    -- )\n"
90"        OPT_PROCESS=false\n"
91"        shift\n"
92"        ;;\n\n";
93
94static char const zLoopOnly[] = "\n"
95"OPT_ARG=\"$1\"\n\n"
96"while [ $# -gt 0 ]\ndo\n"
97"    OPT_ELEMENT=''\n"
98"    OPT_ARG_VAL=''\n\n"
99"    OPT_ARG=\"${1}\"\n";
100
101/* * * * * * * * * * * * * * * *
102 *
103 *  CASE SELECTORS
104 *
105 *  If the loop runs as a regular option loop,
106 *  then we must have selectors for each acceptable option
107 *  type (long option, flag character and non-option)
108 */
109static char const zLongSelection[] =
110"    --* )\n";
111
112static char const zFlagSelection[] =
113"    -* )\n";
114
115static char const zEndSelection[] =
116"        ;;\n\n";
117
118static char const zNoSelection[] =
119"    * )\n"
120"         OPT_PROCESS=false\n"
121"         ;;\n"
122"    esac\n\n";
123
124/* * * * * * * * * * * * * * * *
125 *
126 *  LOOP END
127 */
128static char const zLoopEnd[] =
129"    if [ -n \"${OPT_ARG_VAL}\" ]\n"
130"    then\n"
131"        eval %1$s_${OPT_NAME}${OPT_ELEMENT}=\"'${OPT_ARG_VAL}'\"\n"
132"        export %1$s_${OPT_NAME}${OPT_ELEMENT}\n"
133"    fi\n"
134"done\n\n"
135"unset OPT_PROCESS || :\n"
136"unset OPT_ELEMENT || :\n"
137"unset OPT_ARG || :\n"
138"unset OPT_ARG_NEEDED || :\n"
139"unset OPT_NAME || :\n"
140"unset OPT_CODE || :\n"
141"unset OPT_ARG_VAL || :\n%2$s";
142
143static char const zTrailerMarker[] = "\n"
144"# # # # # # # # # #\n#\n"
145"#  END OF AUTOMATED OPTION PROCESSING\n"
146"#\n# # # # # # # # # # -- do not modify this marker --\n";
147
148/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
149 *
150 *  OPTION SELECTION
151 */
152static char const zOptionCase[] =
153"        case \"${OPT_CODE}\" in\n";
154
155static char const zOptionPartName[] =
156"        '%s' | \\\n";
157
158static char const zOptionFullName[] =
159"        '%s' )\n";
160
161static char const zOptionFlag[] =
162"        '%c' )\n";
163
164static char const zOptionEndSelect[] =
165"            ;;\n\n";
166
167static char const zOptionUnknown[] =
168"        * )\n"
169"            echo Unknown %s: \"${OPT_CODE}\" >&2\n"
170"            echo \"$%s_USAGE_TEXT\"\n"
171"            exit 1\n"
172"            ;;\n"
173"        esac\n\n";
174
175/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
176 *
177 *  OPTION PROCESSING
178 *
179 *  Formats for emitting the text for handling particular options
180 */
181static char const zTextExit[] =
182"            echo \"$%s_%s_TEXT\"\n"
183"            exit 0\n";
184
185static char const zPagedUsageExit[] =
186"            echo \"$%s_LONGUSAGE_TEXT\" | ${PAGER-more}\n"
187"            exit 0\n";
188
189static char const zCmdFmt[] =
190"            %s\n";
191
192static char const zCountTest[] =
193"            if [ $%1$s_%2$s_CT -ge %3$d ] ; then\n"
194"                echo Error:  more than %3$d %2$s options >&2\n"
195"                echo \"$%1$s_USAGE_TEXT\"\n"
196"                exit 1 ; fi\n";
197
198static char const zMultiArg[] =
199"            %1$s_%2$s_CT=`expr ${%1$s_%2$s_CT} + 1`\n"
200"            OPT_ELEMENT=\"_${%1$s_%2$s_CT}\"\n"
201"            OPT_NAME='%2$s'\n";
202
203static char const zSingleArg[] =
204"            if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n"
205"                echo Error:  duplicate %2$s option >&2\n"
206"                echo \"$%1$s_USAGE_TEXT\"\n"
207"                exit 1 ; fi\n"
208"            %1$s_%2$s_set=true\n"
209"            OPT_NAME='%2$s'\n";
210
211static char const zNoMultiArg[] =
212"            %1$s_%2$s_CT=0\n"
213"            OPT_ELEMENT=''\n"
214"            %1$s_%2$s='%3$s'\n"
215"            export %1$s_%2$s\n"
216"            OPT_NAME='%2$s'\n";
217
218static char const zNoSingleArg[] =
219"            if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n"
220"                echo Error:  duplicate %2$s option >&2\n"
221"                echo \"$%1$s_USAGE_TEXT\"\n"
222"                exit 1 ; fi\n"
223"            %1$s_%2$s_set=true\n"
224"            %1$s_%2$s='%3$s'\n"
225"            export %1$s_%2$s\n"
226"            OPT_NAME='%2$s'\n";
227
228static char const zMayArg[]  =
229"            eval %1$s_%2$s${OPT_ELEMENT}=true\n"
230"            export %1$s_%2$s${OPT_ELEMENT}\n"
231"            OPT_ARG_NEEDED=OK\n";
232
233static char const zMustArg[] =
234"            OPT_ARG_NEEDED=YES\n";
235
236static char const zCantArg[] =
237"            eval %1$s_%2$s${OPT_ELEMENT}=true\n"
238"            export %1$s_%2$s${OPT_ELEMENT}\n"
239"            OPT_ARG_NEEDED=NO\n";
240
241/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
242 *
243 *  LONG OPTION PROCESSING
244 *
245 *  Formats for emitting the text for handling long option types
246 */
247static char const zLongOptInit[] =
248"        OPT_CODE=`echo \"X${OPT_ARG}\"|sed 's/^X-*//'`\n"
249"        shift\n"
250"        OPT_ARG=\"$1\"\n\n"
251"        case \"${OPT_CODE}\" in *=* )\n"
252"            OPT_ARG_VAL=`echo \"${OPT_CODE}\"|sed 's/^[^=]*=//'`\n"
253"            OPT_CODE=`echo \"${OPT_CODE}\"|sed 's/=.*$//'` ;; esac\n\n";
254
255static char const zLongOptArg[] =
256"        case \"${OPT_ARG_NEEDED}\" in\n"
257"        NO )\n"
258"            OPT_ARG_VAL=''\n"
259"            ;;\n\n"
260"        YES )\n"
261"            if [ -z \"${OPT_ARG_VAL}\" ]\n"
262"            then\n"
263"                if [ $# -eq 0 ]\n"
264"                then\n"
265"                    echo No argument provided for ${OPT_NAME} option >&2\n"
266"                    echo \"$%s_USAGE_TEXT\"\n"
267"                    exit 1\n"
268"                fi\n\n"
269"                OPT_ARG_VAL=\"${OPT_ARG}\"\n"
270"                shift\n"
271"                OPT_ARG=\"$1\"\n"
272"            fi\n"
273"            ;;\n\n"
274"        OK )\n"
275"            if [ -z \"${OPT_ARG_VAL}\" ] && [ $# -gt 0 ]\n"
276"            then\n"
277"                case \"${OPT_ARG}\" in -* ) ;; * )\n"
278"                    OPT_ARG_VAL=\"${OPT_ARG}\"\n"
279"                    shift\n"
280"                    OPT_ARG=\"$1\" ;; esac\n"
281"            fi\n"
282"            ;;\n"
283"        esac\n";
284
285/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
286 *
287 *  FLAG OPTION PROCESSING
288 *
289 *  Formats for emitting the text for handling flag option types
290 */
291static char const zFlagOptInit[] =
292"        OPT_CODE=`echo \"X${OPT_ARG}\" | sed 's/X-\\(.\\).*/\\1/'`\n"
293"        OPT_ARG=` echo \"X${OPT_ARG}\" | sed 's/X-.//'`\n\n";
294
295static char const zFlagOptArg[] =
296"        case \"${OPT_ARG_NEEDED}\" in\n"
297"        NO )\n"
298"            if [ -n \"${OPT_ARG}\" ]\n"
299"            then\n"
300"                OPT_ARG=-\"${OPT_ARG}\"\n"
301"            else\n"
302"                shift\n"
303"                OPT_ARG=\"$1\"\n"
304"            fi\n"
305"            ;;\n\n"
306"        YES )\n"
307"            if [ -n \"${OPT_ARG}\" ]\n"
308"            then\n"
309"                OPT_ARG_VAL=\"${OPT_ARG}\"\n\n"
310"            else\n"
311"                if [ $# -eq 0 ]\n"
312"                then\n"
313"                    echo No argument provided for ${OPT_NAME} option >&2\n"
314"                    echo \"$%s_USAGE_TEXT\"\n"
315"                    exit 1\n"
316"                fi\n"
317"                shift\n"
318"                OPT_ARG_VAL=\"$1\"\n"
319"            fi\n\n"
320"            shift\n"
321"            OPT_ARG=\"$1\"\n"
322"            ;;\n\n"
323"        OK )\n"
324"            if [ -n \"${OPT_ARG}\" ]\n"
325"            then\n"
326"                OPT_ARG_VAL=\"${OPT_ARG}\"\n"
327"                shift\n"
328"                OPT_ARG=\"$1\"\n\n"
329"            else\n"
330"                shift\n"
331"                if [ $# -gt 0 ]\n"
332"                then\n"
333"                    case \"$1\" in -* ) ;; * )\n"
334"                        OPT_ARG_VAL=\"$1\"\n"
335"                        shift ;; esac\n"
336"                    OPT_ARG=\"$1\"\n"
337"                fi\n"
338"            fi\n"
339"            ;;\n"
340"        esac\n";
341
342tSCC* pzShell = NULL;
343static char*  pzLeader  = NULL;
344static char*  pzTrailer = NULL;
345
346/* = = = START-STATIC-FORWARD = = = */
347static void
348emit_var_text(char const * prog, char const * var, int fdin);
349
350static void
351textToVariable(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD);
352
353static void
354emitUsage(tOptions* pOpts);
355
356static void
357emitSetup(tOptions* pOpts);
358
359static void
360printOptionAction(tOptions* pOpts, tOptDesc* pOptDesc);
361
362static void
363printOptionInaction(tOptions* pOpts, tOptDesc* pOptDesc);
364
365static void
366emitFlag(tOptions* pOpts);
367
368static void
369emitMatchExpr(tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts);
370
371static void
372emitLong(tOptions* pOpts);
373
374static void
375openOutput(char const* pzFile);
376/* = = = END-STATIC-FORWARD = = = */
377
378/*=export_func  optionParseShell
379 * private:
380 *
381 * what:  Decipher a boolean value
382 * arg:   + tOptions* + pOpts    + program options descriptor +
383 *
384 * doc:
385 *  Emit a shell script that will parse the command line options.
386=*/
387void
388optionParseShell(tOptions* pOpts)
389{
390    /*
391     *  Check for our SHELL option now.
392     *  IF the output file contains the "#!" magic marker,
393     *  it will override anything we do here.
394     */
395    if (HAVE_GENSHELL_OPT(SHELL))
396        pzShell = GENSHELL_OPT_ARG(SHELL);
397
398    else if (! ENABLED_GENSHELL_OPT(SHELL))
399        pzShell = NULL;
400
401    else if ((pzShell = getenv("SHELL")),
402             pzShell == NULL)
403
404        pzShell = POSIX_SHELL;
405
406    /*
407     *  Check for a specified output file
408     */
409    if (HAVE_GENSHELL_OPT(SCRIPT))
410        openOutput(GENSHELL_OPT_ARG(SCRIPT));
411
412    emitUsage(pOpts);
413    emitSetup(pOpts);
414
415    /*
416     *  There are four modes of option processing.
417     */
418    switch (pOpts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
419    case OPTPROC_LONGOPT:
420        fputs(zLoopCase,        stdout);
421
422        fputs(zLongSelection,   stdout);
423        fputs(zLongOptInit,     stdout);
424        emitLong(pOpts);
425        printf(zLongOptArg,     pOpts->pzPROGNAME);
426        fputs(zEndSelection,    stdout);
427
428        fputs(zNoSelection,     stdout);
429        break;
430
431    case 0:
432        fputs(zLoopOnly,        stdout);
433        fputs(zLongOptInit,     stdout);
434        emitLong(pOpts);
435        printf(zLongOptArg,     pOpts->pzPROGNAME);
436        break;
437
438    case OPTPROC_SHORTOPT:
439        fputs(zLoopCase,        stdout);
440
441        fputs(zFlagSelection,   stdout);
442        fputs(zFlagOptInit,     stdout);
443        emitFlag(pOpts);
444        printf(zFlagOptArg,     pOpts->pzPROGNAME);
445        fputs(zEndSelection,    stdout);
446
447        fputs(zNoSelection,     stdout);
448        break;
449
450    case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
451        fputs(zLoopCase,        stdout);
452
453        fputs(zLongSelection,   stdout);
454        fputs(zLongOptInit,     stdout);
455        emitLong(pOpts);
456        printf(zLongOptArg,     pOpts->pzPROGNAME);
457        fputs(zEndSelection,    stdout);
458
459        fputs(zFlagSelection,   stdout);
460        fputs(zFlagOptInit,     stdout);
461        emitFlag(pOpts);
462        printf(zFlagOptArg,     pOpts->pzPROGNAME);
463        fputs(zEndSelection,    stdout);
464
465        fputs(zNoSelection,     stdout);
466        break;
467    }
468
469    printf(zLoopEnd, pOpts->pzPROGNAME, zTrailerMarker);
470    if ((pzTrailer != NULL) && (*pzTrailer != '\0'))
471        fputs(pzTrailer, stdout);
472    else if (ENABLED_GENSHELL_OPT(SHELL))
473        printf("\nenv | grep '^%s_'\n", pOpts->pzPROGNAME);
474
475    fflush(stdout);
476    fchmod(STDOUT_FILENO, 0755);
477    fclose(stdout);
478    if (ferror(stdout)) {
479        fputs(zOutputFail, stderr);
480        exit(EXIT_FAILURE);
481    }
482}
483
484#ifdef HAVE_WORKING_FORK
485static void
486emit_var_text(char const * prog, char const * var, int fdin)
487{
488    FILE * fp   = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
489    int    nlct = 0; /* defer newlines and skip trailing ones */
490
491    printf("%s_%s_TEXT='", prog, var);
492    if (fp == NULL)
493        goto skip_text;
494
495    for (;;) {
496        int  ch = fgetc(fp);
497        switch (ch) {
498
499        case '\n':
500            nlct++;
501            break;
502
503        case '\'':
504            while (nlct > 0) {
505                fputc('\n', stdout);
506                nlct--;
507            }
508            fputs("'\\''", stdout);
509            break;
510
511        case EOF:
512            goto endCharLoop;
513
514        default:
515            while (nlct > 0) {
516                fputc('\n', stdout);
517                nlct--;
518            }
519            fputc(ch, stdout);
520            break;
521        }
522    } endCharLoop:;
523
524    fclose(fp);
525
526skip_text:
527
528    fputs("'\n\n", stdout);
529}
530
531#endif
532
533/*
534 *  The purpose of this function is to assign "long usage", short usage
535 *  and version information to a shell variable.  Rather than wind our
536 *  way through all the logic necessary to emit the text directly, we
537 *  fork(), have our child process emit the text the normal way and
538 *  capture the output in the parent process.
539 */
540static void
541textToVariable(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD)
542{
543#   define _TT_(n) static char const z ## n [] = #n;
544    TEXTTO_TABLE
545#   undef _TT_
546#   define _TT_(n) z ## n ,
547      static char const * apzTTNames[] = { TEXTTO_TABLE };
548#   undef _TT_
549
550#if ! defined(HAVE_WORKING_FORK)
551    printf("%1$s_%2$s_TEXT='no %2$s text'\n",
552           pOpts->pzPROGNAME, apzTTNames[ whichVar ]);
553#else
554    int  pipeFd[2];
555
556    fflush(stdout);
557    fflush(stderr);
558
559    if (pipe(pipeFd) != 0) {
560        fprintf(stderr, zBadPipe, errno, strerror(errno));
561        exit(EXIT_FAILURE);
562    }
563
564    switch (fork()) {
565    case -1:
566        fprintf(stderr, zForkFail, errno, strerror(errno), pOpts->pzProgName);
567        exit(EXIT_FAILURE);
568        break;
569
570    case 0:
571        /*
572         * Send both stderr and stdout to the pipe.  No matter which
573         * descriptor is used, we capture the output on the read end.
574         */
575        dup2(pipeFd[1], STDERR_FILENO);
576        dup2(pipeFd[1], STDOUT_FILENO);
577        close(pipeFd[0]);
578
579        switch (whichVar) {
580        case TT_LONGUSAGE:
581            (*(pOpts->pUsageProc))(pOpts, EXIT_SUCCESS);
582            /* NOTREACHED */
583
584        case TT_USAGE:
585            (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
586            /* NOTREACHED */
587
588        case TT_VERSION:
589            if (pOD->fOptState & OPTST_ALLOC_ARG) {
590                AGFREE(pOD->optArg.argString);
591                pOD->fOptState &= ~OPTST_ALLOC_ARG;
592            }
593            pOD->optArg.argString = "c";
594            optionPrintVersion(pOpts, pOD);
595            /* NOTREACHED */
596
597        default:
598            exit(EXIT_FAILURE);
599        }
600
601    default:
602        close(pipeFd[1]);
603    }
604
605    emit_var_text(pOpts->pzPROGNAME, apzTTNames[whichVar], pipeFd[0]);
606#endif
607}
608
609
610static void
611emitUsage(tOptions* pOpts)
612{
613    char zTimeBuf[AO_NAME_SIZE];
614
615    /*
616     *  First, switch stdout to the output file name.
617     *  Then, change the program name to the one defined
618     *  by the definitions (rather than the current
619     *  executable name).  Down case the upper cased name.
620     */
621    if (pzLeader != NULL)
622        fputs(pzLeader, stdout);
623
624    {
625        tSCC    zStdout[] = "stdout";
626        tCC*    pzOutName;
627
628        {
629            time_t    curTime = time(NULL);
630            struct tm*  pTime = localtime(&curTime);
631            strftime(zTimeBuf, AO_NAME_SIZE, "%A %B %e, %Y at %r %Z", pTime );
632        }
633
634        if (HAVE_GENSHELL_OPT(SCRIPT))
635             pzOutName = GENSHELL_OPT_ARG(SCRIPT);
636        else pzOutName = zStdout;
637
638        if ((pzLeader == NULL) && (pzShell != NULL))
639            printf("#! %s\n", pzShell);
640
641        printf(zPreamble, zStartMarker, pzOutName, zTimeBuf);
642    }
643
644    printf(zEndPreamble, pOpts->pzPROGNAME);
645
646    /*
647     *  Get a copy of the original program name in lower case and
648     *  fill in an approximation of the program name from it.
649     */
650    {
651        char *       pzPN = zTimeBuf;
652        char const * pz   = pOpts->pzPROGNAME;
653        char **      pp;
654
655        for (;;) {
656            if ((*pzPN++ = tolower((unsigned char)*pz++)) == '\0')
657                break;
658        }
659
660        pp = (char **)(intptr_t)&(pOpts->pzProgPath);
661        *pp = zTimeBuf;
662        pp  = (char **)(intptr_t)&(pOpts->pzProgName);
663        *pp = zTimeBuf;
664    }
665
666    textToVariable(pOpts, TT_LONGUSAGE, NULL);
667    textToVariable(pOpts, TT_USAGE,     NULL);
668
669    {
670        tOptDesc* pOptDesc = pOpts->pOptDesc;
671        int       optionCt = pOpts->optCt;
672
673        for (;;) {
674            if (pOptDesc->pOptProc == optionPrintVersion) {
675                textToVariable(pOpts, TT_VERSION, pOptDesc);
676                break;
677            }
678
679            if (--optionCt <= 0)
680                break;
681            pOptDesc++;
682        }
683    }
684}
685
686
687static void
688emitSetup(tOptions* pOpts)
689{
690    tOptDesc* pOptDesc = pOpts->pOptDesc;
691    int       optionCt = pOpts->presetOptCt;
692    char const* pzFmt;
693    char const* pzDefault;
694
695    for (;optionCt > 0; pOptDesc++, --optionCt) {
696        char zVal[16];
697
698        /*
699         *  Options that are either usage documentation or are compiled out
700         *  are not to be processed.
701         */
702        if (SKIP_OPT(pOptDesc) || (pOptDesc->pz_NAME == NULL))
703            continue;
704
705        if (pOptDesc->optMaxCt > 1)
706             pzFmt = zMultiDef;
707        else pzFmt = zSingleDef;
708
709        /*
710         *  IF this is an enumeration/bitmask option, then convert the value
711         *  to a string before printing the default value.
712         */
713        switch (OPTST_GET_ARGTYPE(pOptDesc->fOptState)) {
714        case OPARG_TYPE_ENUMERATION:
715            (*(pOptDesc->pOptProc))(OPTPROC_EMIT_SHELL, pOptDesc );
716            pzDefault = pOptDesc->optArg.argString;
717            break;
718
719        /*
720         *  Numeric and membership bit options are just printed as a number.
721         */
722        case OPARG_TYPE_NUMERIC:
723            snprintf(zVal, sizeof(zVal), "%d",
724                     (int)pOptDesc->optArg.argInt);
725            pzDefault = zVal;
726            break;
727
728        case OPARG_TYPE_MEMBERSHIP:
729            snprintf(zVal, sizeof(zVal), "%lu",
730                     (unsigned long)pOptDesc->optArg.argIntptr);
731            pzDefault = zVal;
732            break;
733
734        case OPARG_TYPE_BOOLEAN:
735            pzDefault = (pOptDesc->optArg.argBool) ? "true" : "false";
736            break;
737
738        default:
739            if (pOptDesc->optArg.argString == NULL) {
740                if (pzFmt == zSingleDef)
741                    pzFmt = zSingleNoDef;
742                pzDefault = NULL;
743            }
744            else
745                pzDefault = pOptDesc->optArg.argString;
746        }
747
748        printf(pzFmt, pOpts->pzPROGNAME, pOptDesc->pz_NAME, pzDefault);
749    }
750}
751
752
753static void
754printOptionAction(tOptions* pOpts, tOptDesc* pOptDesc)
755{
756    if (pOptDesc->pOptProc == optionPrintVersion)
757        printf(zTextExit, pOpts->pzPROGNAME, "VERSION");
758
759    else if (pOptDesc->pOptProc == optionPagedUsage)
760        printf(zPagedUsageExit, pOpts->pzPROGNAME);
761
762    else if (pOptDesc->pOptProc == optionLoadOpt) {
763        printf(zCmdFmt, "echo 'Warning:  Cannot load options files' >&2");
764        printf(zCmdFmt, "OPT_ARG_NEEDED=YES");
765
766    } else if (pOptDesc->pz_NAME == NULL) {
767
768        if (pOptDesc->pOptProc == NULL) {
769            printf(zCmdFmt, "echo 'Warning:  Cannot save options files' "
770                    ">&2");
771            printf(zCmdFmt, "OPT_ARG_NEEDED=OK");
772        } else
773            printf(zTextExit, pOpts->pzPROGNAME, "LONGUSAGE");
774
775    } else {
776        if (pOptDesc->optMaxCt == 1)
777            printf(zSingleArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
778        else {
779            if ((unsigned)pOptDesc->optMaxCt < NOLIMIT)
780                printf(zCountTest, pOpts->pzPROGNAME,
781                       pOptDesc->pz_NAME, pOptDesc->optMaxCt);
782
783            printf(zMultiArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
784        }
785
786        /*
787         *  Fix up the args.
788         */
789        if (OPTST_GET_ARGTYPE(pOptDesc->fOptState) == OPARG_TYPE_NONE) {
790            printf(zCantArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
791
792        } else if (pOptDesc->fOptState & OPTST_ARG_OPTIONAL) {
793            printf(zMayArg,  pOpts->pzPROGNAME, pOptDesc->pz_NAME);
794
795        } else {
796            fputs(zMustArg, stdout);
797        }
798    }
799    fputs(zOptionEndSelect, stdout);
800}
801
802
803static void
804printOptionInaction(tOptions* pOpts, tOptDesc* pOptDesc)
805{
806    if (pOptDesc->pOptProc == optionLoadOpt) {
807        printf(zCmdFmt, "echo 'Warning:  Cannot suppress the loading of "
808                "options files' >&2");
809
810    } else if (pOptDesc->optMaxCt == 1)
811        printf(zNoSingleArg, pOpts->pzPROGNAME,
812               pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
813    else
814        printf(zNoMultiArg, pOpts->pzPROGNAME,
815               pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
816
817    printf(zCmdFmt, "OPT_ARG_NEEDED=NO");
818    fputs(zOptionEndSelect, stdout);
819}
820
821
822static void
823emitFlag(tOptions* pOpts)
824{
825    tOptDesc* pOptDesc = pOpts->pOptDesc;
826    int       optionCt = pOpts->optCt;
827
828    fputs(zOptionCase, stdout);
829
830    for (;optionCt > 0; pOptDesc++, --optionCt) {
831
832        if (SKIP_OPT(pOptDesc))
833            continue;
834
835        if (IS_GRAPHIC_CHAR(pOptDesc->optValue)) {
836            printf(zOptionFlag, pOptDesc->optValue);
837            printOptionAction(pOpts, pOptDesc);
838        }
839    }
840    printf(zOptionUnknown, "flag", pOpts->pzPROGNAME);
841}
842
843
844/*
845 *  Emit the match text for a long option
846 */
847static void
848emitMatchExpr(tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts)
849{
850    tOptDesc* pOD = pOpts->pOptDesc;
851    int       oCt = pOpts->optCt;
852    int       min = 1;
853    char      zName[ 256 ];
854    char*     pz  = zName;
855
856    for (;;) {
857        int matchCt = 0;
858
859        /*
860         *  Omit the current option, Documentation opts and compiled out opts.
861         */
862        if ((pOD == pCurOpt) || SKIP_OPT(pOD)){
863            if (--oCt <= 0)
864                break;
865            pOD++;
866            continue;
867        }
868
869        /*
870         *  Check each character of the name case insensitively.
871         *  They must not be the same.  They cannot be, because it would
872         *  not compile correctly if they were.
873         */
874        while (  toupper((unsigned char)pOD->pz_Name[matchCt])
875              == toupper((unsigned char)pzMatchName[matchCt]))
876            matchCt++;
877
878        if (matchCt > min)
879            min = matchCt;
880
881        /*
882         *  Check the disablement name, too.
883         */
884        if (pOD->pz_DisableName != NULL) {
885            matchCt = 0;
886            while (  toupper((unsigned char)pOD->pz_DisableName[matchCt])
887                  == toupper((unsigned char)pzMatchName[matchCt]))
888                matchCt++;
889            if (matchCt > min)
890                min = matchCt;
891        }
892        if (--oCt <= 0)
893            break;
894        pOD++;
895    }
896
897    /*
898     *  IF the 'min' is all or one short of the name length,
899     *  THEN the entire string must be matched.
900     */
901    if (  (pzMatchName[min  ] == NUL)
902       || (pzMatchName[min+1] == NUL) )
903        printf(zOptionFullName, pzMatchName);
904
905    else {
906        int matchCt = 0;
907        for (; matchCt <= min; matchCt++)
908            *pz++ = pzMatchName[matchCt];
909
910        for (;;) {
911            *pz = NUL;
912            printf(zOptionPartName, zName);
913            *pz++ = pzMatchName[matchCt++];
914            if (pzMatchName[matchCt] == NUL) {
915                *pz = NUL;
916                printf(zOptionFullName, zName);
917                break;
918            }
919        }
920    }
921}
922
923
924/*
925 *  Emit GNU-standard long option handling code
926 */
927static void
928emitLong(tOptions* pOpts)
929{
930    tOptDesc* pOD = pOpts->pOptDesc;
931    int       ct  = pOpts->optCt;
932
933    fputs(zOptionCase, stdout);
934
935    /*
936     *  do each option, ...
937     */
938    do  {
939        /*
940         *  Documentation & compiled-out options
941         */
942        if (SKIP_OPT(pOD))
943            continue;
944
945        emitMatchExpr(pOD->pz_Name, pOD, pOpts);
946        printOptionAction(pOpts, pOD);
947
948        /*
949         *  Now, do the same thing for the disablement version of the option.
950         */
951        if (pOD->pz_DisableName != NULL) {
952            emitMatchExpr(pOD->pz_DisableName, pOD, pOpts);
953            printOptionInaction(pOpts, pOD);
954        }
955    } while (pOD++, --ct > 0);
956
957    printf(zOptionUnknown, "option", pOpts->pzPROGNAME);
958}
959
960
961static void
962openOutput(char const* pzFile)
963{
964    FILE* fp;
965    char* pzData = NULL;
966    struct stat stbf;
967
968    do  {
969        char*    pzScan;
970        size_t sizeLeft;
971
972        /*
973         *  IF we cannot stat the file,
974         *  THEN assume we are creating a new file.
975         *       Skip the loading of the old data.
976         */
977        if (stat(pzFile, &stbf) != 0)
978            break;
979
980        /*
981         *  The file must be a regular file
982         */
983        if (! S_ISREG(stbf.st_mode)) {
984            fprintf(stderr, zNotFile, pzFile);
985            exit(EXIT_FAILURE);
986        }
987
988        pzData = AGALOC(stbf.st_size + 1, "file data");
989        fp = fopen(pzFile, "r" FOPEN_BINARY_FLAG);
990
991        sizeLeft = (unsigned)stbf.st_size;
992        pzScan   = pzData;
993
994        /*
995         *  Read in all the data as fast as our OS will let us.
996         */
997        for (;;) {
998            int inct = fread((void*)pzScan, (size_t)1, sizeLeft, fp);
999            if (inct == 0)
1000                break;
1001
1002            pzScan   += inct;
1003            sizeLeft -= inct;
1004
1005            if (sizeLeft == 0)
1006                break;
1007        }
1008
1009        /*
1010         *  NUL-terminate the leader and look for the trailer
1011         */
1012        *pzScan = '\0';
1013        fclose(fp);
1014        pzScan  = strstr(pzData, zStartMarker);
1015        if (pzScan == NULL) {
1016            pzTrailer = pzData;
1017            break;
1018        }
1019
1020        *(pzScan++) = NUL;
1021        pzScan  = strstr(pzScan, zTrailerMarker);
1022        if (pzScan == NULL) {
1023            pzTrailer = pzData;
1024            break;
1025        }
1026
1027        /*
1028         *  Check to see if the data contains our marker.
1029         *  If it does, then we will skip over it
1030         */
1031        pzTrailer = pzScan + sizeof(zTrailerMarker) - 1;
1032        pzLeader  = pzData;
1033    } while (AG_FALSE);
1034
1035    if (freopen(pzFile, "w" FOPEN_BINARY_FLAG, stdout) != stdout) {
1036        fprintf(stderr, zFreopenFail, errno, strerror(errno));
1037        exit(EXIT_FAILURE);
1038    }
1039}
1040
1041
1042/*=export_func genshelloptUsage
1043 * private:
1044 * what: The usage function for the genshellopt generated program
1045 *
1046 * arg:  + tOptions* + pOpts    + program options descriptor +
1047 * arg:  + int       + exitCode + usage text type to produce +
1048 *
1049 * doc:
1050 *  This function is used to create the usage strings for the option
1051 *  processing shell script code.  Two child processes are spawned
1052 *  each emitting the usage text in either the short (error exit)
1053 *  style or the long style.  The generated program will capture this
1054 *  and create shell script variables containing the two types of text.
1055=*/
1056void
1057genshelloptUsage(tOptions * pOpts, int exitCode)
1058{
1059#if ! defined(HAVE_WORKING_FORK)
1060    optionUsage(pOpts, exitCode);
1061#else
1062    /*
1063     *  IF not EXIT_SUCCESS,
1064     *  THEN emit the short form of usage.
1065     */
1066    if (exitCode != EXIT_SUCCESS)
1067        optionUsage(pOpts, exitCode);
1068    fflush(stderr);
1069    fflush(stdout);
1070    if (ferror(stdout) || ferror(stderr))
1071        exit(EXIT_FAILURE);
1072
1073    option_usage_fp = stdout;
1074
1075    /*
1076     *  First, print our usage
1077     */
1078    switch (fork()) {
1079    case -1:
1080        optionUsage(pOpts, EXIT_FAILURE);
1081        /* NOTREACHED */
1082
1083    case 0:
1084        pagerState = PAGER_STATE_CHILD;
1085        optionUsage(pOpts, EXIT_SUCCESS);
1086        /* NOTREACHED */
1087        _exit(EXIT_FAILURE);
1088
1089    default:
1090    {
1091        int  sts;
1092        wait(&sts);
1093    }
1094    }
1095
1096    /*
1097     *  Generate the pzProgName, since optionProcess() normally
1098     *  gets it from the command line
1099     */
1100    {
1101        char *  pz;
1102        char ** pp = (char **)(intptr_t)&(optionParseShellOptions->pzProgName);
1103        AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "program name");
1104        *pp = pz;
1105        while (*pz != NUL) {
1106            *pz = tolower((unsigned char)*pz);
1107            pz++;
1108        }
1109    }
1110
1111    /*
1112     *  Separate the makeshell usage from the client usage
1113     */
1114    fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
1115    fflush(option_usage_fp);
1116
1117    /*
1118     *  Now, print the client usage.
1119     */
1120    switch (fork()) {
1121    case 0:
1122        pagerState = PAGER_STATE_CHILD;
1123        /*FALLTHROUGH*/
1124    case -1:
1125        optionUsage(optionParseShellOptions, EXIT_FAILURE);
1126
1127    default:
1128    {
1129        int  sts;
1130        wait(&sts);
1131    }
1132    }
1133
1134    fflush(stdout);
1135    if (ferror(stdout)) {
1136        fputs(zOutputFail, stderr);
1137        exit(EXIT_FAILURE);
1138    }
1139
1140    exit(EXIT_SUCCESS);
1141#endif
1142}
1143
1144/*
1145 * Local Variables:
1146 * mode: C
1147 * c-file-style: "stroustrup"
1148 * indent-tabs-mode: nil
1149 * End:
1150 * end of autoopts/makeshell.c */
1151