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