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