xref: /freebsd/contrib/ntp/sntp/libopts/makeshell.c (revision 276da39af92f48350aa01091a2b8b3e735217eea)
1ea906c41SOllivier Robert 
22b15cb3dSCy Schubert /**
32b15cb3dSCy Schubert  * \file makeshell.c
4ea906c41SOllivier Robert  *
5ea906c41SOllivier Robert  *  This module will interpret the options set in the tOptions
6ea906c41SOllivier Robert  *  structure and create a Bourne shell script capable of parsing them.
72b15cb3dSCy Schubert  *
82b15cb3dSCy Schubert  * @addtogroup autoopts
92b15cb3dSCy Schubert  * @{
10ea906c41SOllivier Robert  */
11ea906c41SOllivier Robert /*
122b15cb3dSCy Schubert  *  This file is part of AutoOpts, a companion to AutoGen.
132b15cb3dSCy Schubert  *  AutoOpts is free software.
14*276da39aSCy Schubert  *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
15ea906c41SOllivier Robert  *
162b15cb3dSCy Schubert  *  AutoOpts is available under any one of two licenses.  The license
172b15cb3dSCy Schubert  *  in use must be one of these two and the choice is under the control
182b15cb3dSCy Schubert  *  of the user of the license.
19ea906c41SOllivier Robert  *
202b15cb3dSCy Schubert  *   The GNU Lesser General Public License, version 3 or later
212b15cb3dSCy Schubert  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
22ea906c41SOllivier Robert  *
232b15cb3dSCy Schubert  *   The Modified Berkeley Software Distribution License
242b15cb3dSCy Schubert  *      See the file "COPYING.mbsd"
25ea906c41SOllivier Robert  *
262b15cb3dSCy Schubert  *  These files have the following sha256 sums:
27ea906c41SOllivier Robert  *
282b15cb3dSCy Schubert  *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
292b15cb3dSCy Schubert  *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
302b15cb3dSCy Schubert  *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
31ea906c41SOllivier Robert  */
32ea906c41SOllivier Robert 
33*276da39aSCy Schubert  static inline unsigned char to_uchar (char ch) { return ch; }
34*276da39aSCy Schubert 
35*276da39aSCy Schubert #define UPPER(_c) (toupper(to_uchar(_c)))
36*276da39aSCy Schubert #define LOWER(_c) (tolower(to_uchar(_c)))
37*276da39aSCy Schubert 
38ea906c41SOllivier Robert /* = = = START-STATIC-FORWARD = = = */
39ea906c41SOllivier Robert static void
402b15cb3dSCy Schubert emit_var_text(char const * prog, char const * var, int fdin);
41ea906c41SOllivier Robert 
42ea906c41SOllivier Robert static void
432b15cb3dSCy Schubert text_to_var(tOptions * opts, teTextTo which, tOptDesc * od);
44ea906c41SOllivier Robert 
45ea906c41SOllivier Robert static void
462b15cb3dSCy Schubert emit_usage(tOptions * opts);
47ea906c41SOllivier Robert 
48ea906c41SOllivier Robert static void
492b15cb3dSCy Schubert emit_wrapup(tOptions * opts);
50ea906c41SOllivier Robert 
51ea906c41SOllivier Robert static void
522b15cb3dSCy Schubert emit_setup(tOptions * opts);
53ea906c41SOllivier Robert 
54ea906c41SOllivier Robert static void
552b15cb3dSCy Schubert emit_action(tOptions * opts, tOptDesc * od);
56ea906c41SOllivier Robert 
57ea906c41SOllivier Robert static void
582b15cb3dSCy Schubert emit_inaction(tOptions * opts, tOptDesc * od);
59ea906c41SOllivier Robert 
60ea906c41SOllivier Robert static void
612b15cb3dSCy Schubert emit_flag(tOptions * opts);
62ea906c41SOllivier Robert 
63ea906c41SOllivier Robert static void
642b15cb3dSCy Schubert emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts);
652b15cb3dSCy Schubert 
662b15cb3dSCy Schubert static void
672b15cb3dSCy Schubert emit_long(tOptions * opts);
682b15cb3dSCy Schubert 
692b15cb3dSCy Schubert static char *
702b15cb3dSCy Schubert load_old_output(char const * fname, char const * pname);
712b15cb3dSCy Schubert 
722b15cb3dSCy Schubert static void
732b15cb3dSCy Schubert open_out(char const * fname, char const * pname);
74ea906c41SOllivier Robert /* = = = END-STATIC-FORWARD = = = */
75ea906c41SOllivier Robert 
762b15cb3dSCy Schubert LOCAL noreturn void
772b15cb3dSCy Schubert option_exits(int exit_code)
782b15cb3dSCy Schubert {
792b15cb3dSCy Schubert     if (print_exit)
802b15cb3dSCy Schubert         printf("\nexit %d\n", exit_code);
812b15cb3dSCy Schubert     exit(exit_code);
822b15cb3dSCy Schubert }
832b15cb3dSCy Schubert 
842b15cb3dSCy Schubert LOCAL noreturn void
852b15cb3dSCy Schubert ao_bug(char const * msg)
862b15cb3dSCy Schubert {
872b15cb3dSCy Schubert     fprintf(stderr, zao_bug_msg, msg);
882b15cb3dSCy Schubert     option_exits(EX_SOFTWARE);
892b15cb3dSCy Schubert }
902b15cb3dSCy Schubert 
912b15cb3dSCy Schubert LOCAL void
922b15cb3dSCy Schubert fserr_warn(char const * prog, char const * op, char const * fname)
932b15cb3dSCy Schubert {
942b15cb3dSCy Schubert     fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno),
952b15cb3dSCy Schubert             op, fname);
962b15cb3dSCy Schubert }
972b15cb3dSCy Schubert 
982b15cb3dSCy Schubert LOCAL noreturn void
992b15cb3dSCy Schubert fserr_exit(char const * prog, char const * op, char const * fname)
1002b15cb3dSCy Schubert {
1012b15cb3dSCy Schubert     fserr_warn(prog, op, fname);
1022b15cb3dSCy Schubert     option_exits(EXIT_FAILURE);
1032b15cb3dSCy Schubert }
1042b15cb3dSCy Schubert 
105ea906c41SOllivier Robert /*=export_func  optionParseShell
106ea906c41SOllivier Robert  * private:
107ea906c41SOllivier Robert  *
108ea906c41SOllivier Robert  * what:  Decipher a boolean value
109ea906c41SOllivier Robert  * arg:   + tOptions * + pOpts    + program options descriptor +
110ea906c41SOllivier Robert  *
111ea906c41SOllivier Robert  * doc:
112ea906c41SOllivier Robert  *  Emit a shell script that will parse the command line options.
113ea906c41SOllivier Robert =*/
114ea906c41SOllivier Robert void
1152b15cb3dSCy Schubert optionParseShell(tOptions * opts)
116ea906c41SOllivier Robert {
117ea906c41SOllivier Robert     /*
118ea906c41SOllivier Robert      *  Check for our SHELL option now.
119ea906c41SOllivier Robert      *  IF the output file contains the "#!" magic marker,
120ea906c41SOllivier Robert      *  it will override anything we do here.
121ea906c41SOllivier Robert      */
1222b15cb3dSCy Schubert     if (HAVE_GENSHELL_OPT(SHELL))
1232b15cb3dSCy Schubert         shell_prog = GENSHELL_OPT_ARG(SHELL);
124ea906c41SOllivier Robert 
1252b15cb3dSCy Schubert     else if (! ENABLED_GENSHELL_OPT(SHELL))
1262b15cb3dSCy Schubert         shell_prog = NULL;
127ea906c41SOllivier Robert 
1282b15cb3dSCy Schubert     else if ((shell_prog = getenv("SHELL")),
1292b15cb3dSCy Schubert              shell_prog == NULL)
130ea906c41SOllivier Robert 
1312b15cb3dSCy Schubert         shell_prog = POSIX_SHELL;
132ea906c41SOllivier Robert 
133ea906c41SOllivier Robert     /*
134ea906c41SOllivier Robert      *  Check for a specified output file
135ea906c41SOllivier Robert      */
1362b15cb3dSCy Schubert     if (HAVE_GENSHELL_OPT(SCRIPT))
1372b15cb3dSCy Schubert         open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName);
138ea906c41SOllivier Robert 
1392b15cb3dSCy Schubert     emit_usage(opts);
1402b15cb3dSCy Schubert     emit_setup(opts);
141ea906c41SOllivier Robert 
142ea906c41SOllivier Robert     /*
143ea906c41SOllivier Robert      *  There are four modes of option processing.
144ea906c41SOllivier Robert      */
1452b15cb3dSCy Schubert     switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
146ea906c41SOllivier Robert     case OPTPROC_LONGOPT:
1472b15cb3dSCy Schubert         fputs(LOOP_STR,         stdout);
148ea906c41SOllivier Robert 
1492b15cb3dSCy Schubert         fputs(LONG_OPT_MARK,    stdout);
1502b15cb3dSCy Schubert         fputs(INIT_LOPT_STR,    stdout);
1512b15cb3dSCy Schubert         emit_long(opts);
1522b15cb3dSCy Schubert         printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
1532b15cb3dSCy Schubert         fputs(END_OPT_SEL_STR,  stdout);
154ea906c41SOllivier Robert 
1552b15cb3dSCy Schubert         fputs(NOT_FOUND_STR,    stdout);
156ea906c41SOllivier Robert         break;
157ea906c41SOllivier Robert 
158ea906c41SOllivier Robert     case 0:
1592b15cb3dSCy Schubert         fputs(ONLY_OPTS_LOOP,   stdout);
1602b15cb3dSCy Schubert         fputs(INIT_LOPT_STR,    stdout);
1612b15cb3dSCy Schubert         emit_long(opts);
1622b15cb3dSCy Schubert         printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
163ea906c41SOllivier Robert         break;
164ea906c41SOllivier Robert 
165ea906c41SOllivier Robert     case OPTPROC_SHORTOPT:
1662b15cb3dSCy Schubert         fputs(LOOP_STR,         stdout);
167ea906c41SOllivier Robert 
1682b15cb3dSCy Schubert         fputs(FLAG_OPT_MARK,    stdout);
1692b15cb3dSCy Schubert         fputs(INIT_OPT_STR,     stdout);
1702b15cb3dSCy Schubert         emit_flag(opts);
1712b15cb3dSCy Schubert         printf(OPT_ARG_FMT,     opts->pzPROGNAME);
1722b15cb3dSCy Schubert         fputs(END_OPT_SEL_STR,  stdout);
173ea906c41SOllivier Robert 
1742b15cb3dSCy Schubert         fputs(NOT_FOUND_STR,    stdout);
175ea906c41SOllivier Robert         break;
176ea906c41SOllivier Robert 
177ea906c41SOllivier Robert     case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
1782b15cb3dSCy Schubert         fputs(LOOP_STR,         stdout);
179ea906c41SOllivier Robert 
1802b15cb3dSCy Schubert         fputs(LONG_OPT_MARK,    stdout);
1812b15cb3dSCy Schubert         fputs(INIT_LOPT_STR,    stdout);
1822b15cb3dSCy Schubert         emit_long(opts);
1832b15cb3dSCy Schubert         printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
1842b15cb3dSCy Schubert         fputs(END_OPT_SEL_STR,  stdout);
185ea906c41SOllivier Robert 
1862b15cb3dSCy Schubert         fputs(FLAG_OPT_MARK,    stdout);
1872b15cb3dSCy Schubert         fputs(INIT_OPT_STR,     stdout);
1882b15cb3dSCy Schubert         emit_flag(opts);
1892b15cb3dSCy Schubert         printf(OPT_ARG_FMT,     opts->pzPROGNAME);
1902b15cb3dSCy Schubert         fputs(END_OPT_SEL_STR,  stdout);
191ea906c41SOllivier Robert 
1922b15cb3dSCy Schubert         fputs(NOT_FOUND_STR,    stdout);
193ea906c41SOllivier Robert         break;
194ea906c41SOllivier Robert     }
195ea906c41SOllivier Robert 
1962b15cb3dSCy Schubert     emit_wrapup(opts);
1972b15cb3dSCy Schubert     if ((script_trailer != NULL) && (*script_trailer != NUL))
1982b15cb3dSCy Schubert         fputs(script_trailer, stdout);
1992b15cb3dSCy Schubert     else if (ENABLED_GENSHELL_OPT(SHELL))
2002b15cb3dSCy Schubert         printf(SHOW_PROG_ENV, opts->pzPROGNAME);
201ea906c41SOllivier Robert 
2022b15cb3dSCy Schubert #ifdef HAVE_FCHMOD
203ea906c41SOllivier Robert     fchmod(STDOUT_FILENO, 0755);
2042b15cb3dSCy Schubert #endif
205ea906c41SOllivier Robert     fclose(stdout);
2062b15cb3dSCy Schubert 
2072b15cb3dSCy Schubert     if (ferror(stdout))
2082b15cb3dSCy Schubert         fserr_exit(opts->pzProgName, zwriting, zstdout_name);
2092b15cb3dSCy Schubert 
2102b15cb3dSCy Schubert     AGFREE(script_text);
2112b15cb3dSCy Schubert     script_leader    = NULL;
2122b15cb3dSCy Schubert     script_trailer   = NULL;
2132b15cb3dSCy Schubert     script_text      = NULL;
214ea906c41SOllivier Robert }
215ea906c41SOllivier Robert 
2162b15cb3dSCy Schubert #ifdef HAVE_WORKING_FORK
2172b15cb3dSCy Schubert /**
2182b15cb3dSCy Schubert  * Print the value of "var" to a file descriptor.
2192b15cb3dSCy Schubert  * The "fdin" is the read end of a pipe to a forked process that
2202b15cb3dSCy Schubert  * is writing usage text to it.  We read that text in and re-emit
2212b15cb3dSCy Schubert  * to standard out, formatting it so that it is assigned to a
2222b15cb3dSCy Schubert  * shell variable.
2232b15cb3dSCy Schubert  *
2242b15cb3dSCy Schubert  * @param[in] prog  The capitalized, c-variable-formatted program name
2252b15cb3dSCy Schubert  * @param[in] var   a similarly formatted type name
2262b15cb3dSCy Schubert  *                  (LONGUSAGE, USAGE or VERSION)
2272b15cb3dSCy Schubert  * @param[in] fdin  the input end of a pipe
2282b15cb3dSCy Schubert  */
229ea906c41SOllivier Robert static void
2302b15cb3dSCy Schubert emit_var_text(char const * prog, char const * var, int fdin)
231ea906c41SOllivier Robert {
2322b15cb3dSCy Schubert     FILE * fp   = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
2332b15cb3dSCy Schubert     int    nlct = 0; /* defer newlines and skip trailing ones */
234ea906c41SOllivier Robert 
2352b15cb3dSCy Schubert     printf(SET_TEXT_FMT, prog, var);
2362b15cb3dSCy Schubert     if (fp == NULL)
2372b15cb3dSCy Schubert         goto skip_text;
238ea906c41SOllivier Robert 
239ea906c41SOllivier Robert     for (;;) {
240ea906c41SOllivier Robert         int  ch = fgetc(fp);
241ea906c41SOllivier Robert         switch (ch) {
242ea906c41SOllivier Robert 
2432b15cb3dSCy Schubert         case NL:
2442b15cb3dSCy Schubert             nlct++;
245ea906c41SOllivier Robert             break;
246ea906c41SOllivier Robert 
247ea906c41SOllivier Robert         case '\'':
2482b15cb3dSCy Schubert             while (nlct > 0) {
2492b15cb3dSCy Schubert                 fputc(NL, stdout);
2502b15cb3dSCy Schubert                 nlct--;
251ea906c41SOllivier Robert             }
2522b15cb3dSCy Schubert             fputs(apostrophe, stdout);
253ea906c41SOllivier Robert             break;
254ea906c41SOllivier Robert 
255ea906c41SOllivier Robert         case EOF:
2562b15cb3dSCy Schubert             goto done;
257ea906c41SOllivier Robert 
258ea906c41SOllivier Robert         default:
2592b15cb3dSCy Schubert             while (nlct > 0) {
2602b15cb3dSCy Schubert                 fputc(NL, stdout);
2612b15cb3dSCy Schubert                 nlct--;
262ea906c41SOllivier Robert             }
263ea906c41SOllivier Robert             fputc(ch, stdout);
264ea906c41SOllivier Robert             break;
265ea906c41SOllivier Robert         }
2662b15cb3dSCy Schubert     } done:;
267ea906c41SOllivier Robert 
2682b15cb3dSCy Schubert     fclose(fp);
2692b15cb3dSCy Schubert 
2702b15cb3dSCy Schubert  skip_text:
2712b15cb3dSCy Schubert 
2722b15cb3dSCy Schubert     fputs(END_SET_TEXT, stdout);
2732b15cb3dSCy Schubert }
2742b15cb3dSCy Schubert #endif
2752b15cb3dSCy Schubert 
2762b15cb3dSCy Schubert /**
2772b15cb3dSCy Schubert  *  The purpose of this function is to assign "long usage", short usage
2782b15cb3dSCy Schubert  *  and version information to a shell variable.  Rather than wind our
2792b15cb3dSCy Schubert  *  way through all the logic necessary to emit the text directly, we
2802b15cb3dSCy Schubert  *  fork(), have our child process emit the text the normal way and
2812b15cb3dSCy Schubert  *  capture the output in the parent process.
2822b15cb3dSCy Schubert  *
2832b15cb3dSCy Schubert  * @param[in] opts  the program options
2842b15cb3dSCy Schubert  * @param[in] which what to print: long usage, usage or version
2852b15cb3dSCy Schubert  * @param[in] od    for TT_VERSION, it is the version option
2862b15cb3dSCy Schubert  */
2872b15cb3dSCy Schubert static void
2882b15cb3dSCy Schubert text_to_var(tOptions * opts, teTextTo which, tOptDesc * od)
2892b15cb3dSCy Schubert {
2902b15cb3dSCy Schubert #   define _TT_(n) static char const z ## n [] = #n;
2912b15cb3dSCy Schubert     TEXTTO_TABLE
2922b15cb3dSCy Schubert #   undef _TT_
2932b15cb3dSCy Schubert #   define _TT_(n) z ## n ,
2942b15cb3dSCy Schubert       static char const * ttnames[] = { TEXTTO_TABLE };
2952b15cb3dSCy Schubert #   undef _TT_
2962b15cb3dSCy Schubert 
2972b15cb3dSCy Schubert #if ! defined(HAVE_WORKING_FORK)
2982b15cb3dSCy Schubert     printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]);
2992b15cb3dSCy Schubert #else
3002b15cb3dSCy Schubert     int  fdpair[2];
3012b15cb3dSCy Schubert 
3022b15cb3dSCy Schubert     fflush(stdout);
3032b15cb3dSCy Schubert     fflush(stderr);
3042b15cb3dSCy Schubert 
3052b15cb3dSCy Schubert     if (pipe(fdpair) != 0)
3062b15cb3dSCy Schubert         fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe);
3072b15cb3dSCy Schubert 
3082b15cb3dSCy Schubert     switch (fork()) {
3092b15cb3dSCy Schubert     case -1:
3102b15cb3dSCy Schubert         fserr_exit(opts->pzProgName, "fork", opts->pzProgName);
3112b15cb3dSCy Schubert         /* NOTREACHED */
3122b15cb3dSCy Schubert 
3132b15cb3dSCy Schubert     case 0:
3142b15cb3dSCy Schubert         /*
3152b15cb3dSCy Schubert          * Send both stderr and stdout to the pipe.  No matter which
3162b15cb3dSCy Schubert          * descriptor is used, we capture the output on the read end.
3172b15cb3dSCy Schubert          */
3182b15cb3dSCy Schubert         dup2(fdpair[1], STDERR_FILENO);
3192b15cb3dSCy Schubert         dup2(fdpair[1], STDOUT_FILENO);
3202b15cb3dSCy Schubert         close(fdpair[0]);
3212b15cb3dSCy Schubert 
3222b15cb3dSCy Schubert         switch (which) {
3232b15cb3dSCy Schubert         case TT_LONGUSAGE:
3242b15cb3dSCy Schubert             (*(opts->pUsageProc))(opts, EXIT_SUCCESS);
3252b15cb3dSCy Schubert             /* NOTREACHED */
3262b15cb3dSCy Schubert 
3272b15cb3dSCy Schubert         case TT_USAGE:
3282b15cb3dSCy Schubert             (*(opts->pUsageProc))(opts, EXIT_FAILURE);
3292b15cb3dSCy Schubert             /* NOTREACHED */
3302b15cb3dSCy Schubert 
3312b15cb3dSCy Schubert         case TT_VERSION:
3322b15cb3dSCy Schubert             if (od->fOptState & OPTST_ALLOC_ARG) {
3332b15cb3dSCy Schubert                 AGFREE(od->optArg.argString);
3342b15cb3dSCy Schubert                 od->fOptState &= ~OPTST_ALLOC_ARG;
3352b15cb3dSCy Schubert             }
3362b15cb3dSCy Schubert             od->optArg.argString = "c";
3372b15cb3dSCy Schubert             optionPrintVersion(opts, od);
3382b15cb3dSCy Schubert             /* NOTREACHED */
3392b15cb3dSCy Schubert 
3402b15cb3dSCy Schubert         default:
3412b15cb3dSCy Schubert             option_exits(EXIT_FAILURE);
3422b15cb3dSCy Schubert             /* NOTREACHED */
3432b15cb3dSCy Schubert         }
3442b15cb3dSCy Schubert         /* NOTREACHED */
3452b15cb3dSCy Schubert 
3462b15cb3dSCy Schubert     default:
3472b15cb3dSCy Schubert         close(fdpair[1]);
3482b15cb3dSCy Schubert     }
3492b15cb3dSCy Schubert 
3502b15cb3dSCy Schubert     emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]);
351ea906c41SOllivier Robert #endif
352ea906c41SOllivier Robert }
353ea906c41SOllivier Robert 
3542b15cb3dSCy Schubert /**
3552b15cb3dSCy Schubert  * capture usage text in shell variables.
3562b15cb3dSCy Schubert  *
3572b15cb3dSCy Schubert  */
358ea906c41SOllivier Robert static void
3592b15cb3dSCy Schubert emit_usage(tOptions * opts)
360ea906c41SOllivier Robert {
3612b15cb3dSCy Schubert     char tm_nm_buf[AO_NAME_SIZE];
362ea906c41SOllivier Robert 
363ea906c41SOllivier Robert     /*
364ea906c41SOllivier Robert      *  First, switch stdout to the output file name.
365ea906c41SOllivier Robert      *  Then, change the program name to the one defined
366ea906c41SOllivier Robert      *  by the definitions (rather than the current
367ea906c41SOllivier Robert      *  executable name).  Down case the upper cased name.
368ea906c41SOllivier Robert      */
3692b15cb3dSCy Schubert     if (script_leader != NULL)
3702b15cb3dSCy Schubert         fputs(script_leader, stdout);
371ea906c41SOllivier Robert 
372ea906c41SOllivier Robert     {
3732b15cb3dSCy Schubert         char const * out_nm;
374ea906c41SOllivier Robert 
375ea906c41SOllivier Robert         {
3762b15cb3dSCy Schubert             time_t    c_tim = time(NULL);
3772b15cb3dSCy Schubert             struct tm * ptm = localtime(&c_tim);
3782b15cb3dSCy Schubert             strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm );
379ea906c41SOllivier Robert         }
380ea906c41SOllivier Robert 
3812b15cb3dSCy Schubert         if (HAVE_GENSHELL_OPT(SCRIPT))
3822b15cb3dSCy Schubert              out_nm = GENSHELL_OPT_ARG(SCRIPT);
3832b15cb3dSCy Schubert         else out_nm = STDOUT;
384ea906c41SOllivier Robert 
3852b15cb3dSCy Schubert         if ((script_leader == NULL) && (shell_prog != NULL))
3862b15cb3dSCy Schubert             printf(SHELL_MAGIC, shell_prog);
387ea906c41SOllivier Robert 
3882b15cb3dSCy Schubert         printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf);
389ea906c41SOllivier Robert     }
390ea906c41SOllivier Robert 
3912b15cb3dSCy Schubert     printf(END_PRE_FMT, opts->pzPROGNAME);
3922b15cb3dSCy Schubert 
393ea906c41SOllivier Robert     /*
3942b15cb3dSCy Schubert      *  Get a copy of the original program name in lower case and
3952b15cb3dSCy Schubert      *  fill in an approximation of the program name from it.
396ea906c41SOllivier Robert      */
397ea906c41SOllivier Robert     {
3982b15cb3dSCy Schubert         char *       pzPN = tm_nm_buf;
3992b15cb3dSCy Schubert         char const * pz   = opts->pzPROGNAME;
4002b15cb3dSCy Schubert         char **      pp;
4012b15cb3dSCy Schubert 
4022b15cb3dSCy Schubert         /* Copy the program name into the time/name buffer */
403ea906c41SOllivier Robert         for (;;) {
404*276da39aSCy Schubert             if ((*pzPN++ = (char)tolower(*pz++)) == NUL)
405ea906c41SOllivier Robert                 break;
406ea906c41SOllivier Robert         }
4072b15cb3dSCy Schubert 
408*276da39aSCy Schubert         pp  = VOIDP(&(opts->pzProgPath));
4092b15cb3dSCy Schubert         *pp = tm_nm_buf;
410*276da39aSCy Schubert         pp  = VOIDP(&(opts->pzProgName));
4112b15cb3dSCy Schubert         *pp = tm_nm_buf;
412ea906c41SOllivier Robert     }
413ea906c41SOllivier Robert 
4142b15cb3dSCy Schubert     text_to_var(opts, TT_LONGUSAGE, NULL);
4152b15cb3dSCy Schubert     text_to_var(opts, TT_USAGE,     NULL);
416ea906c41SOllivier Robert 
417ea906c41SOllivier Robert     {
4182b15cb3dSCy Schubert         tOptDesc * pOptDesc = opts->pOptDesc;
4192b15cb3dSCy Schubert         int        optionCt = opts->optCt;
420ea906c41SOllivier Robert 
421ea906c41SOllivier Robert         for (;;) {
422ea906c41SOllivier Robert             if (pOptDesc->pOptProc == optionPrintVersion) {
4232b15cb3dSCy Schubert                 text_to_var(opts, TT_VERSION, pOptDesc);
424ea906c41SOllivier Robert                 break;
425ea906c41SOllivier Robert             }
426ea906c41SOllivier Robert 
427ea906c41SOllivier Robert             if (--optionCt <= 0)
428ea906c41SOllivier Robert                 break;
429ea906c41SOllivier Robert             pOptDesc++;
430ea906c41SOllivier Robert         }
431ea906c41SOllivier Robert     }
432ea906c41SOllivier Robert }
433ea906c41SOllivier Robert 
4342b15cb3dSCy Schubert static void
4352b15cb3dSCy Schubert emit_wrapup(tOptions * opts)
4362b15cb3dSCy Schubert {
4372b15cb3dSCy Schubert     tOptDesc *   od     = opts->pOptDesc;
4382b15cb3dSCy Schubert     int          opt_ct = opts->presetOptCt;
4392b15cb3dSCy Schubert     char const * fmt;
4402b15cb3dSCy Schubert 
4412b15cb3dSCy Schubert     printf(FINISH_LOOP, opts->pzPROGNAME);
4422b15cb3dSCy Schubert     for (;opt_ct > 0; od++, --opt_ct) {
4432b15cb3dSCy Schubert         /*
4442b15cb3dSCy Schubert          *  Options that are either usage documentation or are compiled out
4452b15cb3dSCy Schubert          *  are not to be processed.
4462b15cb3dSCy Schubert          */
4472b15cb3dSCy Schubert         if (SKIP_OPT(od) || (od->pz_NAME == NULL))
4482b15cb3dSCy Schubert             continue;
4492b15cb3dSCy Schubert 
4502b15cb3dSCy Schubert         /*
4512b15cb3dSCy Schubert          *  do not presence check if there is no minimum/must-set
4522b15cb3dSCy Schubert          */
4532b15cb3dSCy Schubert         if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0))
4542b15cb3dSCy Schubert             continue;
4552b15cb3dSCy Schubert 
4562b15cb3dSCy Schubert         if (od->optMaxCt > 1)
4572b15cb3dSCy Schubert              fmt = CHK_MIN_COUNT;
4582b15cb3dSCy Schubert         else fmt = CHK_ONE_REQUIRED;
4592b15cb3dSCy Schubert 
4602b15cb3dSCy Schubert         {
4612b15cb3dSCy Schubert             int min = (od->optMinCt == 0) ? 1 : od->optMinCt;
4622b15cb3dSCy Schubert             printf(fmt, opts->pzPROGNAME, od->pz_NAME, min);
4632b15cb3dSCy Schubert         }
4642b15cb3dSCy Schubert     }
4652b15cb3dSCy Schubert     fputs(END_MARK, stdout);
4662b15cb3dSCy Schubert }
467ea906c41SOllivier Robert 
468ea906c41SOllivier Robert static void
4692b15cb3dSCy Schubert emit_setup(tOptions * opts)
470ea906c41SOllivier Robert {
4712b15cb3dSCy Schubert     tOptDesc *   od     = opts->pOptDesc;
4722b15cb3dSCy Schubert     int          opt_ct = opts->presetOptCt;
4732b15cb3dSCy Schubert     char const * fmt;
4742b15cb3dSCy Schubert     char const * def_val;
475ea906c41SOllivier Robert 
4762b15cb3dSCy Schubert     for (;opt_ct > 0; od++, --opt_ct) {
4772b15cb3dSCy Schubert         char int_val_buf[32];
478ea906c41SOllivier Robert 
479ea906c41SOllivier Robert         /*
480ea906c41SOllivier Robert          *  Options that are either usage documentation or are compiled out
481ea906c41SOllivier Robert          *  are not to be processed.
482ea906c41SOllivier Robert          */
4832b15cb3dSCy Schubert         if (SKIP_OPT(od) || (od->pz_NAME == NULL))
484ea906c41SOllivier Robert             continue;
485ea906c41SOllivier Robert 
4862b15cb3dSCy Schubert         if (od->optMaxCt > 1)
4872b15cb3dSCy Schubert              fmt = MULTI_DEF_FMT;
4882b15cb3dSCy Schubert         else fmt = SGL_DEF_FMT;
489ea906c41SOllivier Robert 
490ea906c41SOllivier Robert         /*
491ea906c41SOllivier Robert          *  IF this is an enumeration/bitmask option, then convert the value
492ea906c41SOllivier Robert          *  to a string before printing the default value.
493ea906c41SOllivier Robert          */
4942b15cb3dSCy Schubert         switch (OPTST_GET_ARGTYPE(od->fOptState)) {
495ea906c41SOllivier Robert         case OPARG_TYPE_ENUMERATION:
4962b15cb3dSCy Schubert             (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od );
4972b15cb3dSCy Schubert             def_val = od->optArg.argString;
498ea906c41SOllivier Robert             break;
499ea906c41SOllivier Robert 
500ea906c41SOllivier Robert         /*
501ea906c41SOllivier Robert          *  Numeric and membership bit options are just printed as a number.
502ea906c41SOllivier Robert          */
503ea906c41SOllivier Robert         case OPARG_TYPE_NUMERIC:
5042b15cb3dSCy Schubert             snprintf(int_val_buf, sizeof(int_val_buf), "%d",
5052b15cb3dSCy Schubert                      (int)od->optArg.argInt);
5062b15cb3dSCy Schubert             def_val = int_val_buf;
507ea906c41SOllivier Robert             break;
508ea906c41SOllivier Robert 
509ea906c41SOllivier Robert         case OPARG_TYPE_MEMBERSHIP:
5102b15cb3dSCy Schubert             snprintf(int_val_buf, sizeof(int_val_buf), "%lu",
5112b15cb3dSCy Schubert                      (unsigned long)od->optArg.argIntptr);
5122b15cb3dSCy Schubert             def_val = int_val_buf;
513ea906c41SOllivier Robert             break;
514ea906c41SOllivier Robert 
515ea906c41SOllivier Robert         case OPARG_TYPE_BOOLEAN:
5162b15cb3dSCy Schubert             def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR;
517ea906c41SOllivier Robert             break;
518ea906c41SOllivier Robert 
519ea906c41SOllivier Robert         default:
5202b15cb3dSCy Schubert             if (od->optArg.argString == NULL) {
5212b15cb3dSCy Schubert                 if (fmt == SGL_DEF_FMT)
5222b15cb3dSCy Schubert                     fmt = SGL_NO_DEF_FMT;
5232b15cb3dSCy Schubert                 def_val = NULL;
524ea906c41SOllivier Robert             }
525ea906c41SOllivier Robert             else
5262b15cb3dSCy Schubert                 def_val = od->optArg.argString;
527ea906c41SOllivier Robert         }
528ea906c41SOllivier Robert 
5292b15cb3dSCy Schubert         printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val);
530ea906c41SOllivier Robert     }
531ea906c41SOllivier Robert }
532ea906c41SOllivier Robert 
533ea906c41SOllivier Robert static void
5342b15cb3dSCy Schubert emit_action(tOptions * opts, tOptDesc * od)
535ea906c41SOllivier Robert {
5362b15cb3dSCy Schubert     if (od->pOptProc == optionPrintVersion)
5372b15cb3dSCy Schubert         printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR);
538ea906c41SOllivier Robert 
5392b15cb3dSCy Schubert     else if (od->pOptProc == optionPagedUsage)
5402b15cb3dSCy Schubert         printf(PAGE_USAGE_TEXT, opts->pzPROGNAME);
541ea906c41SOllivier Robert 
5422b15cb3dSCy Schubert     else if (od->pOptProc == optionLoadOpt) {
5432b15cb3dSCy Schubert         printf(LVL3_CMD, NO_LOAD_WARN);
5442b15cb3dSCy Schubert         printf(LVL3_CMD, YES_NEED_OPT_ARG);
545ea906c41SOllivier Robert 
5462b15cb3dSCy Schubert     } else if (od->pz_NAME == NULL) {
547ea906c41SOllivier Robert 
5482b15cb3dSCy Schubert         if (od->pOptProc == NULL) {
5492b15cb3dSCy Schubert             printf(LVL3_CMD, NO_SAVE_OPTS);
5502b15cb3dSCy Schubert             printf(LVL3_CMD, OK_NEED_OPT_ARG);
551ea906c41SOllivier Robert         } else
5522b15cb3dSCy Schubert             printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR);
553ea906c41SOllivier Robert 
554ea906c41SOllivier Robert     } else {
5552b15cb3dSCy Schubert         if (od->optMaxCt == 1)
5562b15cb3dSCy Schubert             printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
557ea906c41SOllivier Robert         else {
5582b15cb3dSCy Schubert             if ((unsigned)od->optMaxCt < NOLIMIT)
5592b15cb3dSCy Schubert                 printf(CHK_MAX_COUNT, opts->pzPROGNAME,
5602b15cb3dSCy Schubert                        od->pz_NAME, od->optMaxCt);
561ea906c41SOllivier Robert 
5622b15cb3dSCy Schubert             printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
563ea906c41SOllivier Robert         }
564ea906c41SOllivier Robert 
565ea906c41SOllivier Robert         /*
566ea906c41SOllivier Robert          *  Fix up the args.
567ea906c41SOllivier Robert          */
5682b15cb3dSCy Schubert         if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) {
5692b15cb3dSCy Schubert             printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
5702b15cb3dSCy Schubert             printf(LVL3_CMD, NO_ARG_NEEDED);
571ea906c41SOllivier Robert 
5722b15cb3dSCy Schubert         } else if (od->fOptState & OPTST_ARG_OPTIONAL) {
5732b15cb3dSCy Schubert             printf(SET_MULTI_ARG,  opts->pzPROGNAME, od->pz_NAME);
5742b15cb3dSCy Schubert             printf(LVL3_CMD, OK_NEED_OPT_ARG);
575ea906c41SOllivier Robert 
576ea906c41SOllivier Robert         } else {
5772b15cb3dSCy Schubert             printf(LVL3_CMD, YES_NEED_OPT_ARG);
578ea906c41SOllivier Robert         }
579ea906c41SOllivier Robert     }
580ea906c41SOllivier Robert     fputs(zOptionEndSelect, stdout);
581ea906c41SOllivier Robert }
582ea906c41SOllivier Robert 
583ea906c41SOllivier Robert static void
5842b15cb3dSCy Schubert emit_inaction(tOptions * opts, tOptDesc * od)
585ea906c41SOllivier Robert {
5862b15cb3dSCy Schubert     if (od->pOptProc == optionLoadOpt) {
5872b15cb3dSCy Schubert         printf(LVL3_CMD, NO_SUPPRESS_LOAD);
588ea906c41SOllivier Robert 
5892b15cb3dSCy Schubert     } else if (od->optMaxCt == 1)
5902b15cb3dSCy Schubert         printf(NO_SGL_ARG_FMT, opts->pzPROGNAME,
5912b15cb3dSCy Schubert                od->pz_NAME, od->pz_DisablePfx);
592ea906c41SOllivier Robert     else
5932b15cb3dSCy Schubert         printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME,
5942b15cb3dSCy Schubert                od->pz_NAME, od->pz_DisablePfx);
595ea906c41SOllivier Robert 
5962b15cb3dSCy Schubert     printf(LVL3_CMD, NO_ARG_NEEDED);
597ea906c41SOllivier Robert     fputs(zOptionEndSelect, stdout);
598ea906c41SOllivier Robert }
599ea906c41SOllivier Robert 
6002b15cb3dSCy Schubert /**
6012b15cb3dSCy Schubert  * recognize flag options.  These go at the end.
6022b15cb3dSCy Schubert  * At the end, emit code to handle options we don't recognize.
6032b15cb3dSCy Schubert  *
6042b15cb3dSCy Schubert  * @param[in] opts  the program options
6052b15cb3dSCy Schubert  */
606ea906c41SOllivier Robert static void
6072b15cb3dSCy Schubert emit_flag(tOptions * opts)
608ea906c41SOllivier Robert {
6092b15cb3dSCy Schubert     tOptDesc * od = opts->pOptDesc;
6102b15cb3dSCy Schubert     int        opt_ct = opts->optCt;
611ea906c41SOllivier Robert 
612ea906c41SOllivier Robert     fputs(zOptionCase, stdout);
613ea906c41SOllivier Robert 
6142b15cb3dSCy Schubert     for (;opt_ct > 0; od++, --opt_ct) {
615ea906c41SOllivier Robert 
6162b15cb3dSCy Schubert         if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue))
617ea906c41SOllivier Robert             continue;
618ea906c41SOllivier Robert 
6192b15cb3dSCy Schubert         printf(zOptionFlag, od->optValue);
6202b15cb3dSCy Schubert         emit_action(opts, od);
621ea906c41SOllivier Robert     }
6222b15cb3dSCy Schubert     printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME);
623ea906c41SOllivier Robert }
624ea906c41SOllivier Robert 
6252b15cb3dSCy Schubert /**
6262b15cb3dSCy Schubert  *  Emit the match text for a long option.  The passed in \a name may be
6272b15cb3dSCy Schubert  *  either the enablement name or the disablement name.
6282b15cb3dSCy Schubert  *
6292b15cb3dSCy Schubert  * @param[in] name  The current name to check.
6302b15cb3dSCy Schubert  * @param[in] cod   current option descriptor
6312b15cb3dSCy Schubert  * @param[in] opts  the program options
632ea906c41SOllivier Robert  */
633ea906c41SOllivier Robert static void
6342b15cb3dSCy Schubert emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts)
635ea906c41SOllivier Robert {
6362b15cb3dSCy Schubert     char name_bf[32];
6372b15cb3dSCy Schubert     unsigned int    min_match_ct = 2;
6382b15cb3dSCy Schubert     unsigned int    max_match_ct = strlen(name) - 1;
639ea906c41SOllivier Robert 
6402b15cb3dSCy Schubert     if (max_match_ct >= sizeof(name_bf) - 1)
6412b15cb3dSCy Schubert         goto leave;
6422b15cb3dSCy Schubert 
6432b15cb3dSCy Schubert     {
6442b15cb3dSCy Schubert         tOptDesc *  od = opts->pOptDesc;
6452b15cb3dSCy Schubert         int         ct = opts->optCt;
6462b15cb3dSCy Schubert 
6472b15cb3dSCy Schubert         for (; ct-- > 0; od++) {
6482b15cb3dSCy Schubert             unsigned int match_ct = 0;
649ea906c41SOllivier Robert 
650ea906c41SOllivier Robert             /*
6512b15cb3dSCy Schubert              *  Omit the current option, Doc opts and compiled out opts.
652ea906c41SOllivier Robert              */
6532b15cb3dSCy Schubert             if ((od == cod) || SKIP_OPT(od))
654ea906c41SOllivier Robert                 continue;
655ea906c41SOllivier Robert 
656ea906c41SOllivier Robert             /*
657ea906c41SOllivier Robert              *  Check each character of the name case insensitively.
658ea906c41SOllivier Robert              *  They must not be the same.  They cannot be, because it would
659ea906c41SOllivier Robert              *  not compile correctly if they were.
660ea906c41SOllivier Robert              */
661*276da39aSCy Schubert             while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct]))
6622b15cb3dSCy Schubert                 match_ct++;
663ea906c41SOllivier Robert 
6642b15cb3dSCy Schubert             if (match_ct > min_match_ct)
6652b15cb3dSCy Schubert                 min_match_ct = match_ct;
666ea906c41SOllivier Robert 
667ea906c41SOllivier Robert             /*
668ea906c41SOllivier Robert              *  Check the disablement name, too.
669ea906c41SOllivier Robert              */
6702b15cb3dSCy Schubert             if (od->pz_DisableName == NULL)
6712b15cb3dSCy Schubert                 continue;
6722b15cb3dSCy Schubert 
6732b15cb3dSCy Schubert             match_ct = 0;
674*276da39aSCy Schubert             while (  toupper(od->pz_DisableName[match_ct])
675*276da39aSCy Schubert                   == toupper(name[match_ct]))
6762b15cb3dSCy Schubert                 match_ct++;
6772b15cb3dSCy Schubert             if (match_ct > min_match_ct)
6782b15cb3dSCy Schubert                 min_match_ct = match_ct;
679ea906c41SOllivier Robert         }
680ea906c41SOllivier Robert     }
681ea906c41SOllivier Robert 
682ea906c41SOllivier Robert     /*
6832b15cb3dSCy Schubert      *  Don't bother emitting partial matches if there is only one possible
6842b15cb3dSCy Schubert      *  partial match.
685ea906c41SOllivier Robert      */
6862b15cb3dSCy Schubert     if (min_match_ct < max_match_ct) {
6872b15cb3dSCy Schubert         char *  pz    = name_bf + min_match_ct;
6882b15cb3dSCy Schubert         int     nm_ix = min_match_ct;
689ea906c41SOllivier Robert 
6902b15cb3dSCy Schubert         memcpy(name_bf, name, min_match_ct);
691ea906c41SOllivier Robert 
692ea906c41SOllivier Robert         for (;;) {
693ea906c41SOllivier Robert             *pz = NUL;
6942b15cb3dSCy Schubert             printf(zOptionPartName, name_bf);
6952b15cb3dSCy Schubert             *pz++ = name[nm_ix++];
6962b15cb3dSCy Schubert             if (name[nm_ix] == NUL) {
697ea906c41SOllivier Robert                 *pz = NUL;
698ea906c41SOllivier Robert                 break;
699ea906c41SOllivier Robert             }
700ea906c41SOllivier Robert         }
701ea906c41SOllivier Robert     }
7022b15cb3dSCy Schubert 
7032b15cb3dSCy Schubert leave:
7042b15cb3dSCy Schubert     printf(zOptionFullName, name);
705ea906c41SOllivier Robert }
706ea906c41SOllivier Robert 
7072b15cb3dSCy Schubert /**
7082b15cb3dSCy Schubert  *  Emit GNU-standard long option handling code.
7092b15cb3dSCy Schubert  *
7102b15cb3dSCy Schubert  * @param[in] opts  the program options
711ea906c41SOllivier Robert  */
712ea906c41SOllivier Robert static void
7132b15cb3dSCy Schubert emit_long(tOptions * opts)
714ea906c41SOllivier Robert {
7152b15cb3dSCy Schubert     tOptDesc * od = opts->pOptDesc;
7162b15cb3dSCy Schubert     int        ct  = opts->optCt;
717ea906c41SOllivier Robert 
718ea906c41SOllivier Robert     fputs(zOptionCase, stdout);
719ea906c41SOllivier Robert 
720ea906c41SOllivier Robert     /*
721ea906c41SOllivier Robert      *  do each option, ...
722ea906c41SOllivier Robert      */
723ea906c41SOllivier Robert     do  {
724ea906c41SOllivier Robert         /*
725ea906c41SOllivier Robert          *  Documentation & compiled-out options
726ea906c41SOllivier Robert          */
7272b15cb3dSCy Schubert         if (SKIP_OPT(od))
728ea906c41SOllivier Robert             continue;
729ea906c41SOllivier Robert 
7302b15cb3dSCy Schubert         emit_match_expr(od->pz_Name, od, opts);
7312b15cb3dSCy Schubert         emit_action(opts, od);
732ea906c41SOllivier Robert 
733ea906c41SOllivier Robert         /*
734ea906c41SOllivier Robert          *  Now, do the same thing for the disablement version of the option.
735ea906c41SOllivier Robert          */
7362b15cb3dSCy Schubert         if (od->pz_DisableName != NULL) {
7372b15cb3dSCy Schubert             emit_match_expr(od->pz_DisableName, od, opts);
7382b15cb3dSCy Schubert             emit_inaction(opts, od);
739ea906c41SOllivier Robert         }
7402b15cb3dSCy Schubert     } while (od++, --ct > 0);
741ea906c41SOllivier Robert 
7422b15cb3dSCy Schubert     printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME);
743ea906c41SOllivier Robert }
744ea906c41SOllivier Robert 
7452b15cb3dSCy Schubert /**
7462b15cb3dSCy Schubert  * Load the previous shell script output file.  We need to preserve any
7472b15cb3dSCy Schubert  * hand-edited additions outside of the START_MARK and END_MARKs.
7482b15cb3dSCy Schubert  *
7492b15cb3dSCy Schubert  * @param[in] fname  the output file name
7502b15cb3dSCy Schubert  */
7512b15cb3dSCy Schubert static char *
7522b15cb3dSCy Schubert load_old_output(char const * fname, char const * pname)
753ea906c41SOllivier Robert {
754ea906c41SOllivier Robert     /*
755ea906c41SOllivier Robert      *  IF we cannot stat the file,
756ea906c41SOllivier Robert      *  THEN assume we are creating a new file.
757ea906c41SOllivier Robert      *       Skip the loading of the old data.
758ea906c41SOllivier Robert      */
7592b15cb3dSCy Schubert     FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG);
7602b15cb3dSCy Schubert     struct stat stbf;
7612b15cb3dSCy Schubert     char * text;
7622b15cb3dSCy Schubert     char * scan;
7632b15cb3dSCy Schubert 
7642b15cb3dSCy Schubert     if (fp == NULL)
7652b15cb3dSCy Schubert         return NULL;
766ea906c41SOllivier Robert 
767ea906c41SOllivier Robert     /*
7682b15cb3dSCy Schubert      * If we opened it, we should be able to stat it and it needs
7692b15cb3dSCy Schubert      * to be a regular file
770ea906c41SOllivier Robert      */
7712b15cb3dSCy Schubert     if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode)))
7722b15cb3dSCy Schubert         fserr_exit(pname, "fstat", fname);
773ea906c41SOllivier Robert 
7742b15cb3dSCy Schubert     scan = text = AGALOC(stbf.st_size + 1, "f data");
775ea906c41SOllivier Robert 
776ea906c41SOllivier Robert     /*
777ea906c41SOllivier Robert      *  Read in all the data as fast as our OS will let us.
778ea906c41SOllivier Robert      */
779ea906c41SOllivier Robert     for (;;) {
780*276da39aSCy Schubert         size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp);
781ea906c41SOllivier Robert         if (inct == 0)
782ea906c41SOllivier Robert             break;
783ea906c41SOllivier Robert 
7842b15cb3dSCy Schubert         stbf.st_size -= (ssize_t)inct;
785ea906c41SOllivier Robert 
7862b15cb3dSCy Schubert         if (stbf.st_size == 0)
787ea906c41SOllivier Robert             break;
7882b15cb3dSCy Schubert 
7892b15cb3dSCy Schubert         scan += inct;
790ea906c41SOllivier Robert     }
791ea906c41SOllivier Robert 
7922b15cb3dSCy Schubert     *scan = NUL;
793ea906c41SOllivier Robert     fclose(fp);
7942b15cb3dSCy Schubert 
7952b15cb3dSCy Schubert     return text;
7962b15cb3dSCy Schubert }
7972b15cb3dSCy Schubert 
7982b15cb3dSCy Schubert /**
7992b15cb3dSCy Schubert  * Open the specified output file.  If it already exists, load its
8002b15cb3dSCy Schubert  * contents and save the non-generated (hand edited) portions.
8012b15cb3dSCy Schubert  * If a "start mark" is found, everything before it is preserved leader.
8022b15cb3dSCy Schubert  * If not, the entire thing is a trailer.  Assuming the start is found,
8032b15cb3dSCy Schubert  * then everything after the end marker is the trailer.  If the end
8042b15cb3dSCy Schubert  * mark is not found, the file is actually corrupt, but we take the
8052b15cb3dSCy Schubert  * remainder to be the trailer.
8062b15cb3dSCy Schubert  *
8072b15cb3dSCy Schubert  * @param[in] fname  the output file name
8082b15cb3dSCy Schubert  */
8092b15cb3dSCy Schubert static void
8102b15cb3dSCy Schubert open_out(char const * fname, char const * pname)
8112b15cb3dSCy Schubert {
8122b15cb3dSCy Schubert 
8132b15cb3dSCy Schubert     do  {
8142b15cb3dSCy Schubert         char * txt = script_text = load_old_output(fname, pname);
8152b15cb3dSCy Schubert         char * scn;
8162b15cb3dSCy Schubert 
8172b15cb3dSCy Schubert         if (txt == NULL)
8182b15cb3dSCy Schubert             break;
8192b15cb3dSCy Schubert 
8202b15cb3dSCy Schubert         scn = strstr(txt, START_MARK);
8212b15cb3dSCy Schubert         if (scn == NULL) {
8222b15cb3dSCy Schubert             script_trailer = txt;
823ea906c41SOllivier Robert             break;
824ea906c41SOllivier Robert         }
825ea906c41SOllivier Robert 
8262b15cb3dSCy Schubert         *(scn++) = NUL;
8272b15cb3dSCy Schubert         scn = strstr(scn, END_MARK);
8282b15cb3dSCy Schubert         if (scn == NULL) {
8292b15cb3dSCy Schubert             /*
8302b15cb3dSCy Schubert              * The file is corrupt.  Set the trailer to be everything
8312b15cb3dSCy Schubert              * after the start mark. The user will need to fix it up.
8322b15cb3dSCy Schubert              */
8332b15cb3dSCy Schubert             script_trailer = txt + strlen(txt) + START_MARK_LEN + 1;
834ea906c41SOllivier Robert             break;
835ea906c41SOllivier Robert         }
836ea906c41SOllivier Robert 
837ea906c41SOllivier Robert         /*
8382b15cb3dSCy Schubert          *  Check to see if the data contains our marker.
8392b15cb3dSCy Schubert          *  If it does, then we will skip over it
840ea906c41SOllivier Robert          */
8412b15cb3dSCy Schubert         script_trailer = scn + END_MARK_LEN;
8422b15cb3dSCy Schubert         script_leader  = txt;
8432b15cb3dSCy Schubert     } while (false);
844ea906c41SOllivier Robert 
8452b15cb3dSCy Schubert     if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout)
8462b15cb3dSCy Schubert         fserr_exit(pname, "freopen", fname);
847ea906c41SOllivier Robert }
848ea906c41SOllivier Robert 
849ea906c41SOllivier Robert /*=export_func genshelloptUsage
850ea906c41SOllivier Robert  * private:
851ea906c41SOllivier Robert  * what: The usage function for the genshellopt generated program
852ea906c41SOllivier Robert  *
8532b15cb3dSCy Schubert  * arg:  + tOptions * + opts    + program options descriptor +
8542b15cb3dSCy Schubert  * arg:  + int        + exit_cd + usage text type to produce +
855ea906c41SOllivier Robert  *
856ea906c41SOllivier Robert  * doc:
857ea906c41SOllivier Robert  *  This function is used to create the usage strings for the option
858ea906c41SOllivier Robert  *  processing shell script code.  Two child processes are spawned
859ea906c41SOllivier Robert  *  each emitting the usage text in either the short (error exit)
860ea906c41SOllivier Robert  *  style or the long style.  The generated program will capture this
861ea906c41SOllivier Robert  *  and create shell script variables containing the two types of text.
862ea906c41SOllivier Robert =*/
863ea906c41SOllivier Robert void
8642b15cb3dSCy Schubert genshelloptUsage(tOptions * opts, int exit_cd)
865ea906c41SOllivier Robert {
8662b15cb3dSCy Schubert #if ! defined(HAVE_WORKING_FORK)
8672b15cb3dSCy Schubert     optionUsage(opts, exit_cd);
868ea906c41SOllivier Robert #else
869ea906c41SOllivier Robert     /*
870ea906c41SOllivier Robert      *  IF not EXIT_SUCCESS,
871ea906c41SOllivier Robert      *  THEN emit the short form of usage.
872ea906c41SOllivier Robert      */
8732b15cb3dSCy Schubert     if (exit_cd != EXIT_SUCCESS)
8742b15cb3dSCy Schubert         optionUsage(opts, exit_cd);
875ea906c41SOllivier Robert     fflush(stderr);
876ea906c41SOllivier Robert     fflush(stdout);
8772b15cb3dSCy Schubert     if (ferror(stdout) || ferror(stderr))
8782b15cb3dSCy Schubert         option_exits(EXIT_FAILURE);
879ea906c41SOllivier Robert 
880ea906c41SOllivier Robert     option_usage_fp = stdout;
881ea906c41SOllivier Robert 
882ea906c41SOllivier Robert     /*
883ea906c41SOllivier Robert      *  First, print our usage
884ea906c41SOllivier Robert      */
885ea906c41SOllivier Robert     switch (fork()) {
886ea906c41SOllivier Robert     case -1:
8872b15cb3dSCy Schubert         optionUsage(opts, EXIT_FAILURE);
888ea906c41SOllivier Robert         /* NOTREACHED */
889ea906c41SOllivier Robert 
890ea906c41SOllivier Robert     case 0:
891ea906c41SOllivier Robert         pagerState = PAGER_STATE_CHILD;
8922b15cb3dSCy Schubert         optionUsage(opts, EXIT_SUCCESS);
893ea906c41SOllivier Robert         /* NOTREACHED */
894ea906c41SOllivier Robert         _exit(EXIT_FAILURE);
895ea906c41SOllivier Robert 
896ea906c41SOllivier Robert     default:
897ea906c41SOllivier Robert     {
898ea906c41SOllivier Robert         int  sts;
899ea906c41SOllivier Robert         wait(&sts);
900ea906c41SOllivier Robert     }
901ea906c41SOllivier Robert     }
902ea906c41SOllivier Robert 
903ea906c41SOllivier Robert     /*
904ea906c41SOllivier Robert      *  Generate the pzProgName, since optionProcess() normally
905ea906c41SOllivier Robert      *  gets it from the command line
906ea906c41SOllivier Robert      */
907ea906c41SOllivier Robert     {
908ea906c41SOllivier Robert         char *  pz;
909*276da39aSCy Schubert         char ** pp = VOIDP(&(optionParseShellOptions->pzProgName));
9102b15cb3dSCy Schubert         AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
9112b15cb3dSCy Schubert         *pp = pz;
912ea906c41SOllivier Robert         while (*pz != NUL) {
913*276da39aSCy Schubert             *pz = (char)LOWER(*pz);
914ea906c41SOllivier Robert             pz++;
915ea906c41SOllivier Robert         }
916ea906c41SOllivier Robert     }
917ea906c41SOllivier Robert 
918ea906c41SOllivier Robert     /*
919ea906c41SOllivier Robert      *  Separate the makeshell usage from the client usage
920ea906c41SOllivier Robert      */
9212b15cb3dSCy Schubert     fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
922ea906c41SOllivier Robert     fflush(option_usage_fp);
923ea906c41SOllivier Robert 
924ea906c41SOllivier Robert     /*
925ea906c41SOllivier Robert      *  Now, print the client usage.
926ea906c41SOllivier Robert      */
927ea906c41SOllivier Robert     switch (fork()) {
928ea906c41SOllivier Robert     case 0:
929ea906c41SOllivier Robert         pagerState = PAGER_STATE_CHILD;
930ea906c41SOllivier Robert         /*FALLTHROUGH*/
931ea906c41SOllivier Robert     case -1:
9322b15cb3dSCy Schubert         optionUsage(optionParseShellOptions, EXIT_FAILURE);
933ea906c41SOllivier Robert 
934ea906c41SOllivier Robert     default:
935ea906c41SOllivier Robert     {
936ea906c41SOllivier Robert         int  sts;
937ea906c41SOllivier Robert         wait(&sts);
938ea906c41SOllivier Robert     }
939ea906c41SOllivier Robert     }
940ea906c41SOllivier Robert 
9412b15cb3dSCy Schubert     fflush(stdout);
9422b15cb3dSCy Schubert     if (ferror(stdout))
9432b15cb3dSCy Schubert         fserr_exit(opts->pzProgName, zwriting, zstdout_name);
9442b15cb3dSCy Schubert 
9452b15cb3dSCy Schubert     option_exits(EXIT_SUCCESS);
946ea906c41SOllivier Robert #endif
947ea906c41SOllivier Robert }
948ea906c41SOllivier Robert 
9492b15cb3dSCy Schubert /** @}
9502b15cb3dSCy Schubert  *
951ea906c41SOllivier Robert  * Local Variables:
952ea906c41SOllivier Robert  * mode: C
953ea906c41SOllivier Robert  * c-file-style: "stroustrup"
954ea906c41SOllivier Robert  * indent-tabs-mode: nil
955ea906c41SOllivier Robert  * End:
956ea906c41SOllivier Robert  * end of autoopts/makeshell.c */
957