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