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