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