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