makeshell.c revision 290001
1
2/**
3 * \file makeshell.c
4 *
5 *  This module will interpret the options set in the tOptions
6 *  structure and create a Bourne shell script capable of parsing them.
7 *
8 * @addtogroup autoopts
9 * @{
10 */
11/*
12 *  This file is part of AutoOpts, a companion to AutoGen.
13 *  AutoOpts is free software.
14 *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
15 *
16 *  AutoOpts is available under any one of two licenses.  The license
17 *  in use must be one of these two and the choice is under the control
18 *  of the user of the license.
19 *
20 *   The GNU Lesser General Public License, version 3 or later
21 *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
22 *
23 *   The Modified Berkeley Software Distribution License
24 *      See the file "COPYING.mbsd"
25 *
26 *  These files have the following sha256 sums:
27 *
28 *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
29 *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
30 *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
31 */
32
33 static inline unsigned char to_uchar (char ch) { return ch; }
34
35#define UPPER(_c) (toupper(to_uchar(_c)))
36#define LOWER(_c) (tolower(to_uchar(_c)))
37
38/* = = = START-STATIC-FORWARD = = = */
39static void
40emit_var_text(char const * prog, char const * var, int fdin);
41
42static void
43text_to_var(tOptions * opts, teTextTo which, tOptDesc * od);
44
45static void
46emit_usage(tOptions * opts);
47
48static void
49emit_wrapup(tOptions * opts);
50
51static void
52emit_setup(tOptions * opts);
53
54static void
55emit_action(tOptions * opts, tOptDesc * od);
56
57static void
58emit_inaction(tOptions * opts, tOptDesc * od);
59
60static void
61emit_flag(tOptions * opts);
62
63static void
64emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts);
65
66static void
67emit_long(tOptions * opts);
68
69static char *
70load_old_output(char const * fname, char const * pname);
71
72static void
73open_out(char const * fname, char const * pname);
74/* = = = END-STATIC-FORWARD = = = */
75
76LOCAL noreturn void
77option_exits(int exit_code)
78{
79    if (print_exit)
80        printf("\nexit %d\n", exit_code);
81    exit(exit_code);
82}
83
84LOCAL noreturn void
85ao_bug(char const * msg)
86{
87    fprintf(stderr, zao_bug_msg, msg);
88    option_exits(EX_SOFTWARE);
89}
90
91LOCAL void
92fserr_warn(char const * prog, char const * op, char const * fname)
93{
94    fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno),
95            op, fname);
96}
97
98LOCAL noreturn void
99fserr_exit(char const * prog, char const * op, char const * fname)
100{
101    fserr_warn(prog, op, fname);
102    option_exits(EXIT_FAILURE);
103}
104
105/*=export_func  optionParseShell
106 * private:
107 *
108 * what:  Decipher a boolean value
109 * arg:   + tOptions * + pOpts    + program options descriptor +
110 *
111 * doc:
112 *  Emit a shell script that will parse the command line options.
113=*/
114void
115optionParseShell(tOptions * opts)
116{
117    /*
118     *  Check for our SHELL option now.
119     *  IF the output file contains the "#!" magic marker,
120     *  it will override anything we do here.
121     */
122    if (HAVE_GENSHELL_OPT(SHELL))
123        shell_prog = GENSHELL_OPT_ARG(SHELL);
124
125    else if (! ENABLED_GENSHELL_OPT(SHELL))
126        shell_prog = NULL;
127
128    else if ((shell_prog = getenv("SHELL")),
129             shell_prog == NULL)
130
131        shell_prog = POSIX_SHELL;
132
133    /*
134     *  Check for a specified output file
135     */
136    if (HAVE_GENSHELL_OPT(SCRIPT))
137        open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName);
138
139    emit_usage(opts);
140    emit_setup(opts);
141
142    /*
143     *  There are four modes of option processing.
144     */
145    switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
146    case OPTPROC_LONGOPT:
147        fputs(LOOP_STR,         stdout);
148
149        fputs(LONG_OPT_MARK,    stdout);
150        fputs(INIT_LOPT_STR,    stdout);
151        emit_long(opts);
152        printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
153        fputs(END_OPT_SEL_STR,  stdout);
154
155        fputs(NOT_FOUND_STR,    stdout);
156        break;
157
158    case 0:
159        fputs(ONLY_OPTS_LOOP,   stdout);
160        fputs(INIT_LOPT_STR,    stdout);
161        emit_long(opts);
162        printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
163        break;
164
165    case OPTPROC_SHORTOPT:
166        fputs(LOOP_STR,         stdout);
167
168        fputs(FLAG_OPT_MARK,    stdout);
169        fputs(INIT_OPT_STR,     stdout);
170        emit_flag(opts);
171        printf(OPT_ARG_FMT,     opts->pzPROGNAME);
172        fputs(END_OPT_SEL_STR,  stdout);
173
174        fputs(NOT_FOUND_STR,    stdout);
175        break;
176
177    case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
178        fputs(LOOP_STR,         stdout);
179
180        fputs(LONG_OPT_MARK,    stdout);
181        fputs(INIT_LOPT_STR,    stdout);
182        emit_long(opts);
183        printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
184        fputs(END_OPT_SEL_STR,  stdout);
185
186        fputs(FLAG_OPT_MARK,    stdout);
187        fputs(INIT_OPT_STR,     stdout);
188        emit_flag(opts);
189        printf(OPT_ARG_FMT,     opts->pzPROGNAME);
190        fputs(END_OPT_SEL_STR,  stdout);
191
192        fputs(NOT_FOUND_STR,    stdout);
193        break;
194    }
195
196    emit_wrapup(opts);
197    if ((script_trailer != NULL) && (*script_trailer != NUL))
198        fputs(script_trailer, stdout);
199    else if (ENABLED_GENSHELL_OPT(SHELL))
200        printf(SHOW_PROG_ENV, opts->pzPROGNAME);
201
202#ifdef HAVE_FCHMOD
203    fchmod(STDOUT_FILENO, 0755);
204#endif
205    fclose(stdout);
206
207    if (ferror(stdout))
208        fserr_exit(opts->pzProgName, zwriting, zstdout_name);
209
210    AGFREE(script_text);
211    script_leader    = NULL;
212    script_trailer   = NULL;
213    script_text      = NULL;
214}
215
216#ifdef HAVE_WORKING_FORK
217/**
218 * Print the value of "var" to a file descriptor.
219 * The "fdin" is the read end of a pipe to a forked process that
220 * is writing usage text to it.  We read that text in and re-emit
221 * to standard out, formatting it so that it is assigned to a
222 * shell variable.
223 *
224 * @param[in] prog  The capitalized, c-variable-formatted program name
225 * @param[in] var   a similarly formatted type name
226 *                  (LONGUSAGE, USAGE or VERSION)
227 * @param[in] fdin  the input end of a pipe
228 */
229static void
230emit_var_text(char const * prog, char const * var, int fdin)
231{
232    FILE * fp   = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
233    int    nlct = 0; /* defer newlines and skip trailing ones */
234
235    printf(SET_TEXT_FMT, prog, var);
236    if (fp == NULL)
237        goto skip_text;
238
239    for (;;) {
240        int  ch = fgetc(fp);
241        switch (ch) {
242
243        case NL:
244            nlct++;
245            break;
246
247        case '\'':
248            while (nlct > 0) {
249                fputc(NL, stdout);
250                nlct--;
251            }
252            fputs(apostrophe, stdout);
253            break;
254
255        case EOF:
256            goto done;
257
258        default:
259            while (nlct > 0) {
260                fputc(NL, stdout);
261                nlct--;
262            }
263            fputc(ch, stdout);
264            break;
265        }
266    } done:;
267
268    fclose(fp);
269
270 skip_text:
271
272    fputs(END_SET_TEXT, stdout);
273}
274#endif
275
276/**
277 *  The purpose of this function is to assign "long usage", short usage
278 *  and version information to a shell variable.  Rather than wind our
279 *  way through all the logic necessary to emit the text directly, we
280 *  fork(), have our child process emit the text the normal way and
281 *  capture the output in the parent process.
282 *
283 * @param[in] opts  the program options
284 * @param[in] which what to print: long usage, usage or version
285 * @param[in] od    for TT_VERSION, it is the version option
286 */
287static void
288text_to_var(tOptions * opts, teTextTo which, tOptDesc * od)
289{
290#   define _TT_(n) static char const z ## n [] = #n;
291    TEXTTO_TABLE
292#   undef _TT_
293#   define _TT_(n) z ## n ,
294      static char const * ttnames[] = { TEXTTO_TABLE };
295#   undef _TT_
296
297#if ! defined(HAVE_WORKING_FORK)
298    printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]);
299#else
300    int  fdpair[2];
301
302    fflush(stdout);
303    fflush(stderr);
304
305    if (pipe(fdpair) != 0)
306        fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe);
307
308    switch (fork()) {
309    case -1:
310        fserr_exit(opts->pzProgName, "fork", opts->pzProgName);
311        /* NOTREACHED */
312
313    case 0:
314        /*
315         * Send both stderr and stdout to the pipe.  No matter which
316         * descriptor is used, we capture the output on the read end.
317         */
318        dup2(fdpair[1], STDERR_FILENO);
319        dup2(fdpair[1], STDOUT_FILENO);
320        close(fdpair[0]);
321
322        switch (which) {
323        case TT_LONGUSAGE:
324            (*(opts->pUsageProc))(opts, EXIT_SUCCESS);
325            /* NOTREACHED */
326
327        case TT_USAGE:
328            (*(opts->pUsageProc))(opts, EXIT_FAILURE);
329            /* NOTREACHED */
330
331        case TT_VERSION:
332            if (od->fOptState & OPTST_ALLOC_ARG) {
333                AGFREE(od->optArg.argString);
334                od->fOptState &= ~OPTST_ALLOC_ARG;
335            }
336            od->optArg.argString = "c";
337            optionPrintVersion(opts, od);
338            /* NOTREACHED */
339
340        default:
341            option_exits(EXIT_FAILURE);
342            /* NOTREACHED */
343        }
344        /* NOTREACHED */
345
346    default:
347        close(fdpair[1]);
348    }
349
350    emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]);
351#endif
352}
353
354/**
355 * capture usage text in shell variables.
356 *
357 */
358static void
359emit_usage(tOptions * opts)
360{
361    char tm_nm_buf[AO_NAME_SIZE];
362
363    /*
364     *  First, switch stdout to the output file name.
365     *  Then, change the program name to the one defined
366     *  by the definitions (rather than the current
367     *  executable name).  Down case the upper cased name.
368     */
369    if (script_leader != NULL)
370        fputs(script_leader, stdout);
371
372    {
373        char const * out_nm;
374
375        {
376            time_t    c_tim = time(NULL);
377            struct tm * ptm = localtime(&c_tim);
378            strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm );
379        }
380
381        if (HAVE_GENSHELL_OPT(SCRIPT))
382             out_nm = GENSHELL_OPT_ARG(SCRIPT);
383        else out_nm = STDOUT;
384
385        if ((script_leader == NULL) && (shell_prog != NULL))
386            printf(SHELL_MAGIC, shell_prog);
387
388        printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf);
389    }
390
391    printf(END_PRE_FMT, opts->pzPROGNAME);
392
393    /*
394     *  Get a copy of the original program name in lower case and
395     *  fill in an approximation of the program name from it.
396     */
397    {
398        char *       pzPN = tm_nm_buf;
399        char const * pz   = opts->pzPROGNAME;
400        char **      pp;
401
402        /* Copy the program name into the time/name buffer */
403        for (;;) {
404            if ((*pzPN++ = (char)tolower(*pz++)) == NUL)
405                break;
406        }
407
408        pp  = VOIDP(&(opts->pzProgPath));
409        *pp = tm_nm_buf;
410        pp  = VOIDP(&(opts->pzProgName));
411        *pp = tm_nm_buf;
412    }
413
414    text_to_var(opts, TT_LONGUSAGE, NULL);
415    text_to_var(opts, TT_USAGE,     NULL);
416
417    {
418        tOptDesc * pOptDesc = opts->pOptDesc;
419        int        optionCt = opts->optCt;
420
421        for (;;) {
422            if (pOptDesc->pOptProc == optionPrintVersion) {
423                text_to_var(opts, TT_VERSION, pOptDesc);
424                break;
425            }
426
427            if (--optionCt <= 0)
428                break;
429            pOptDesc++;
430        }
431    }
432}
433
434static void
435emit_wrapup(tOptions * opts)
436{
437    tOptDesc *   od     = opts->pOptDesc;
438    int          opt_ct = opts->presetOptCt;
439    char const * fmt;
440
441    printf(FINISH_LOOP, opts->pzPROGNAME);
442    for (;opt_ct > 0; od++, --opt_ct) {
443        /*
444         *  Options that are either usage documentation or are compiled out
445         *  are not to be processed.
446         */
447        if (SKIP_OPT(od) || (od->pz_NAME == NULL))
448            continue;
449
450        /*
451         *  do not presence check if there is no minimum/must-set
452         */
453        if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0))
454            continue;
455
456        if (od->optMaxCt > 1)
457             fmt = CHK_MIN_COUNT;
458        else fmt = CHK_ONE_REQUIRED;
459
460        {
461            int min = (od->optMinCt == 0) ? 1 : od->optMinCt;
462            printf(fmt, opts->pzPROGNAME, od->pz_NAME, min);
463        }
464    }
465    fputs(END_MARK, stdout);
466}
467
468static void
469emit_setup(tOptions * opts)
470{
471    tOptDesc *   od     = opts->pOptDesc;
472    int          opt_ct = opts->presetOptCt;
473    char const * fmt;
474    char const * def_val;
475
476    for (;opt_ct > 0; od++, --opt_ct) {
477        char int_val_buf[32];
478
479        /*
480         *  Options that are either usage documentation or are compiled out
481         *  are not to be processed.
482         */
483        if (SKIP_OPT(od) || (od->pz_NAME == NULL))
484            continue;
485
486        if (od->optMaxCt > 1)
487             fmt = MULTI_DEF_FMT;
488        else fmt = SGL_DEF_FMT;
489
490        /*
491         *  IF this is an enumeration/bitmask option, then convert the value
492         *  to a string before printing the default value.
493         */
494        switch (OPTST_GET_ARGTYPE(od->fOptState)) {
495        case OPARG_TYPE_ENUMERATION:
496            (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od );
497            def_val = od->optArg.argString;
498            break;
499
500        /*
501         *  Numeric and membership bit options are just printed as a number.
502         */
503        case OPARG_TYPE_NUMERIC:
504            snprintf(int_val_buf, sizeof(int_val_buf), "%d",
505                     (int)od->optArg.argInt);
506            def_val = int_val_buf;
507            break;
508
509        case OPARG_TYPE_MEMBERSHIP:
510            snprintf(int_val_buf, sizeof(int_val_buf), "%lu",
511                     (unsigned long)od->optArg.argIntptr);
512            def_val = int_val_buf;
513            break;
514
515        case OPARG_TYPE_BOOLEAN:
516            def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR;
517            break;
518
519        default:
520            if (od->optArg.argString == NULL) {
521                if (fmt == SGL_DEF_FMT)
522                    fmt = SGL_NO_DEF_FMT;
523                def_val = NULL;
524            }
525            else
526                def_val = od->optArg.argString;
527        }
528
529        printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val);
530    }
531}
532
533static void
534emit_action(tOptions * opts, tOptDesc * od)
535{
536    if (od->pOptProc == optionPrintVersion)
537        printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR);
538
539    else if (od->pOptProc == optionPagedUsage)
540        printf(PAGE_USAGE_TEXT, opts->pzPROGNAME);
541
542    else if (od->pOptProc == optionLoadOpt) {
543        printf(LVL3_CMD, NO_LOAD_WARN);
544        printf(LVL3_CMD, YES_NEED_OPT_ARG);
545
546    } else if (od->pz_NAME == NULL) {
547
548        if (od->pOptProc == NULL) {
549            printf(LVL3_CMD, NO_SAVE_OPTS);
550            printf(LVL3_CMD, OK_NEED_OPT_ARG);
551        } else
552            printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR);
553
554    } else {
555        if (od->optMaxCt == 1)
556            printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
557        else {
558            if ((unsigned)od->optMaxCt < NOLIMIT)
559                printf(CHK_MAX_COUNT, opts->pzPROGNAME,
560                       od->pz_NAME, od->optMaxCt);
561
562            printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
563        }
564
565        /*
566         *  Fix up the args.
567         */
568        if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) {
569            printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
570            printf(LVL3_CMD, NO_ARG_NEEDED);
571
572        } else if (od->fOptState & OPTST_ARG_OPTIONAL) {
573            printf(SET_MULTI_ARG,  opts->pzPROGNAME, od->pz_NAME);
574            printf(LVL3_CMD, OK_NEED_OPT_ARG);
575
576        } else {
577            printf(LVL3_CMD, YES_NEED_OPT_ARG);
578        }
579    }
580    fputs(zOptionEndSelect, stdout);
581}
582
583static void
584emit_inaction(tOptions * opts, tOptDesc * od)
585{
586    if (od->pOptProc == optionLoadOpt) {
587        printf(LVL3_CMD, NO_SUPPRESS_LOAD);
588
589    } else if (od->optMaxCt == 1)
590        printf(NO_SGL_ARG_FMT, opts->pzPROGNAME,
591               od->pz_NAME, od->pz_DisablePfx);
592    else
593        printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME,
594               od->pz_NAME, od->pz_DisablePfx);
595
596    printf(LVL3_CMD, NO_ARG_NEEDED);
597    fputs(zOptionEndSelect, stdout);
598}
599
600/**
601 * recognize flag options.  These go at the end.
602 * At the end, emit code to handle options we don't recognize.
603 *
604 * @param[in] opts  the program options
605 */
606static void
607emit_flag(tOptions * opts)
608{
609    tOptDesc * od = opts->pOptDesc;
610    int        opt_ct = opts->optCt;
611
612    fputs(zOptionCase, stdout);
613
614    for (;opt_ct > 0; od++, --opt_ct) {
615
616        if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue))
617            continue;
618
619        printf(zOptionFlag, od->optValue);
620        emit_action(opts, od);
621    }
622    printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME);
623}
624
625/**
626 *  Emit the match text for a long option.  The passed in \a name may be
627 *  either the enablement name or the disablement name.
628 *
629 * @param[in] name  The current name to check.
630 * @param[in] cod   current option descriptor
631 * @param[in] opts  the program options
632 */
633static void
634emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts)
635{
636    char name_bf[32];
637    unsigned int    min_match_ct = 2;
638    unsigned int    max_match_ct = strlen(name) - 1;
639
640    if (max_match_ct >= sizeof(name_bf) - 1)
641        goto leave;
642
643    {
644        tOptDesc *  od = opts->pOptDesc;
645        int         ct = opts->optCt;
646
647        for (; ct-- > 0; od++) {
648            unsigned int match_ct = 0;
649
650            /*
651             *  Omit the current option, Doc opts and compiled out opts.
652             */
653            if ((od == cod) || SKIP_OPT(od))
654                continue;
655
656            /*
657             *  Check each character of the name case insensitively.
658             *  They must not be the same.  They cannot be, because it would
659             *  not compile correctly if they were.
660             */
661            while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct]))
662                match_ct++;
663
664            if (match_ct > min_match_ct)
665                min_match_ct = match_ct;
666
667            /*
668             *  Check the disablement name, too.
669             */
670            if (od->pz_DisableName == NULL)
671                continue;
672
673            match_ct = 0;
674            while (  toupper(od->pz_DisableName[match_ct])
675                  == toupper(name[match_ct]))
676                match_ct++;
677            if (match_ct > min_match_ct)
678                min_match_ct = match_ct;
679        }
680    }
681
682    /*
683     *  Don't bother emitting partial matches if there is only one possible
684     *  partial match.
685     */
686    if (min_match_ct < max_match_ct) {
687        char *  pz    = name_bf + min_match_ct;
688        int     nm_ix = min_match_ct;
689
690        memcpy(name_bf, name, min_match_ct);
691
692        for (;;) {
693            *pz = NUL;
694            printf(zOptionPartName, name_bf);
695            *pz++ = name[nm_ix++];
696            if (name[nm_ix] == NUL) {
697                *pz = NUL;
698                break;
699            }
700        }
701    }
702
703leave:
704    printf(zOptionFullName, name);
705}
706
707/**
708 *  Emit GNU-standard long option handling code.
709 *
710 * @param[in] opts  the program options
711 */
712static void
713emit_long(tOptions * opts)
714{
715    tOptDesc * od = opts->pOptDesc;
716    int        ct  = opts->optCt;
717
718    fputs(zOptionCase, stdout);
719
720    /*
721     *  do each option, ...
722     */
723    do  {
724        /*
725         *  Documentation & compiled-out options
726         */
727        if (SKIP_OPT(od))
728            continue;
729
730        emit_match_expr(od->pz_Name, od, opts);
731        emit_action(opts, od);
732
733        /*
734         *  Now, do the same thing for the disablement version of the option.
735         */
736        if (od->pz_DisableName != NULL) {
737            emit_match_expr(od->pz_DisableName, od, opts);
738            emit_inaction(opts, od);
739        }
740    } while (od++, --ct > 0);
741
742    printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME);
743}
744
745/**
746 * Load the previous shell script output file.  We need to preserve any
747 * hand-edited additions outside of the START_MARK and END_MARKs.
748 *
749 * @param[in] fname  the output file name
750 */
751static char *
752load_old_output(char const * fname, char const * pname)
753{
754    /*
755     *  IF we cannot stat the file,
756     *  THEN assume we are creating a new file.
757     *       Skip the loading of the old data.
758     */
759    FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG);
760    struct stat stbf;
761    char * text;
762    char * scan;
763
764    if (fp == NULL)
765        return NULL;
766
767    /*
768     * If we opened it, we should be able to stat it and it needs
769     * to be a regular file
770     */
771    if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode)))
772        fserr_exit(pname, "fstat", fname);
773
774    scan = text = AGALOC(stbf.st_size + 1, "f data");
775
776    /*
777     *  Read in all the data as fast as our OS will let us.
778     */
779    for (;;) {
780        size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp);
781        if (inct == 0)
782            break;
783
784        stbf.st_size -= (ssize_t)inct;
785
786        if (stbf.st_size == 0)
787            break;
788
789        scan += inct;
790    }
791
792    *scan = NUL;
793    fclose(fp);
794
795    return text;
796}
797
798/**
799 * Open the specified output file.  If it already exists, load its
800 * contents and save the non-generated (hand edited) portions.
801 * If a "start mark" is found, everything before it is preserved leader.
802 * If not, the entire thing is a trailer.  Assuming the start is found,
803 * then everything after the end marker is the trailer.  If the end
804 * mark is not found, the file is actually corrupt, but we take the
805 * remainder to be the trailer.
806 *
807 * @param[in] fname  the output file name
808 */
809static void
810open_out(char const * fname, char const * pname)
811{
812
813    do  {
814        char * txt = script_text = load_old_output(fname, pname);
815        char * scn;
816
817        if (txt == NULL)
818            break;
819
820        scn = strstr(txt, START_MARK);
821        if (scn == NULL) {
822            script_trailer = txt;
823            break;
824        }
825
826        *(scn++) = NUL;
827        scn = strstr(scn, END_MARK);
828        if (scn == NULL) {
829            /*
830             * The file is corrupt.  Set the trailer to be everything
831             * after the start mark. The user will need to fix it up.
832             */
833            script_trailer = txt + strlen(txt) + START_MARK_LEN + 1;
834            break;
835        }
836
837        /*
838         *  Check to see if the data contains our marker.
839         *  If it does, then we will skip over it
840         */
841        script_trailer = scn + END_MARK_LEN;
842        script_leader  = txt;
843    } while (false);
844
845    if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout)
846        fserr_exit(pname, "freopen", fname);
847}
848
849/*=export_func genshelloptUsage
850 * private:
851 * what: The usage function for the genshellopt generated program
852 *
853 * arg:  + tOptions * + opts    + program options descriptor +
854 * arg:  + int        + exit_cd + usage text type to produce +
855 *
856 * doc:
857 *  This function is used to create the usage strings for the option
858 *  processing shell script code.  Two child processes are spawned
859 *  each emitting the usage text in either the short (error exit)
860 *  style or the long style.  The generated program will capture this
861 *  and create shell script variables containing the two types of text.
862=*/
863void
864genshelloptUsage(tOptions * opts, int exit_cd)
865{
866#if ! defined(HAVE_WORKING_FORK)
867    optionUsage(opts, exit_cd);
868#else
869    /*
870     *  IF not EXIT_SUCCESS,
871     *  THEN emit the short form of usage.
872     */
873    if (exit_cd != EXIT_SUCCESS)
874        optionUsage(opts, exit_cd);
875    fflush(stderr);
876    fflush(stdout);
877    if (ferror(stdout) || ferror(stderr))
878        option_exits(EXIT_FAILURE);
879
880    option_usage_fp = stdout;
881
882    /*
883     *  First, print our usage
884     */
885    switch (fork()) {
886    case -1:
887        optionUsage(opts, EXIT_FAILURE);
888        /* NOTREACHED */
889
890    case 0:
891        pagerState = PAGER_STATE_CHILD;
892        optionUsage(opts, EXIT_SUCCESS);
893        /* NOTREACHED */
894        _exit(EXIT_FAILURE);
895
896    default:
897    {
898        int  sts;
899        wait(&sts);
900    }
901    }
902
903    /*
904     *  Generate the pzProgName, since optionProcess() normally
905     *  gets it from the command line
906     */
907    {
908        char *  pz;
909        char ** pp = VOIDP(&(optionParseShellOptions->pzProgName));
910        AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
911        *pp = pz;
912        while (*pz != NUL) {
913            *pz = (char)LOWER(*pz);
914            pz++;
915        }
916    }
917
918    /*
919     *  Separate the makeshell usage from the client usage
920     */
921    fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
922    fflush(option_usage_fp);
923
924    /*
925     *  Now, print the client usage.
926     */
927    switch (fork()) {
928    case 0:
929        pagerState = PAGER_STATE_CHILD;
930        /*FALLTHROUGH*/
931    case -1:
932        optionUsage(optionParseShellOptions, EXIT_FAILURE);
933
934    default:
935    {
936        int  sts;
937        wait(&sts);
938    }
939    }
940
941    fflush(stdout);
942    if (ferror(stdout))
943        fserr_exit(opts->pzProgName, zwriting, zstdout_name);
944
945    option_exits(EXIT_SUCCESS);
946#endif
947}
948
949/** @}
950 *
951 * Local Variables:
952 * mode: C
953 * c-file-style: "stroustrup"
954 * indent-tabs-mode: nil
955 * End:
956 * end of autoopts/makeshell.c */
957