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