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