5 * Time-stamp: "2012-04-07 09:03:16 bkorb"
7 * This module will interpret the options set in the tOptions
8 * structure and create a Bourne shell script capable of parsing them.
10 * This file is part of AutoOpts, a companion to AutoGen.
11 * AutoOpts is free software.
12 * AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
14 * AutoOpts is available under any one of two licenses. The license
15 * in use must be one of these two and the choice is under the control
16 * of the user of the license.
18 * The GNU Lesser General Public License, version 3 or later
19 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
21 * The Modified Berkeley Software Distribution License
22 * See the file "COPYING.mbsd"
24 * These files have the following md5sums:
26 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
27 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
28 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
33 /* Work around problem reported in
34 <http://permalink.gmane.org/gmane.comp.lib.gnulib.bugs/15755>.*/
35 #if GETTIMEOFDAY_CLOBBERS_LOCALTIME
39 tOptions
* optionParseShellOptions
= NULL
;
41 static char const * shell_prog
= NULL
;
42 static char * script_leader
= NULL
;
43 static char * script_trailer
= NULL
;
45 /* = = = START-STATIC-FORWARD = = = */
47 emit_var_text(char const * prog
, char const * var
, int fdin
);
50 text_to_var(tOptions
* pOpts
, teTextTo whichVar
, tOptDesc
* pOD
);
53 emit_usage(tOptions
* pOpts
);
56 emit_setup(tOptions
* pOpts
);
59 emit_action(tOptions
* pOpts
, tOptDesc
* pOptDesc
);
62 emit_inaction(tOptions
* pOpts
, tOptDesc
* pOptDesc
);
65 emit_flag(tOptions
* pOpts
);
68 emit_match_expr(char const * pzMatchName
, tOptDesc
* pCurOpt
, tOptions
* pOpts
);
71 emitLong(tOptions
* pOpts
);
74 open_out(char const * pzFile
);
75 /* = = = END-STATIC-FORWARD = = = */
77 /*=export_func optionParseShell
80 * what: Decipher a boolean value
81 * arg: + tOptions* + pOpts + program options descriptor +
84 * Emit a shell script that will parse the command line options.
87 optionParseShell(tOptions
* pOpts
)
90 * Check for our SHELL option now.
91 * IF the output file contains the "#!" magic marker,
92 * it will override anything we do here.
94 if (HAVE_GENSHELL_OPT(SHELL
))
95 shell_prog
= GENSHELL_OPT_ARG(SHELL
);
97 else if (! ENABLED_GENSHELL_OPT(SHELL
))
100 else if ((shell_prog
= getenv("SHELL")),
103 shell_prog
= POSIX_SHELL
;
106 * Check for a specified output file
108 if (HAVE_GENSHELL_OPT(SCRIPT
))
109 open_out(GENSHELL_OPT_ARG(SCRIPT
));
115 * There are four modes of option processing.
117 switch (pOpts
->fOptSet
& (OPTPROC_LONGOPT
|OPTPROC_SHORTOPT
)) {
118 case OPTPROC_LONGOPT
:
119 fputs(LOOP_STR
, stdout
);
121 fputs(LONG_OPT_MARK
, stdout
);
122 fputs(INIT_LOPT_STR
, stdout
);
124 printf(LOPT_ARG_FMT
, pOpts
->pzPROGNAME
);
125 fputs(END_OPT_SEL_STR
, stdout
);
127 fputs(NOT_FOUND_STR
, stdout
);
131 fputs(ONLY_OPTS_LOOP
, stdout
);
132 fputs(INIT_LOPT_STR
, stdout
);
134 printf(LOPT_ARG_FMT
, pOpts
->pzPROGNAME
);
137 case OPTPROC_SHORTOPT
:
138 fputs(LOOP_STR
, stdout
);
140 fputs(FLAG_OPT_MARK
, stdout
);
141 fputs(INIT_OPT_STR
, stdout
);
143 printf(OPT_ARG_FMT
, pOpts
->pzPROGNAME
);
144 fputs(END_OPT_SEL_STR
, stdout
);
146 fputs(NOT_FOUND_STR
, stdout
);
149 case OPTPROC_LONGOPT
|OPTPROC_SHORTOPT
:
150 fputs(LOOP_STR
, stdout
);
152 fputs(LONG_OPT_MARK
, stdout
);
153 fputs(INIT_LOPT_STR
, stdout
);
155 printf(LOPT_ARG_FMT
, pOpts
->pzPROGNAME
);
156 fputs(END_OPT_SEL_STR
, stdout
);
158 fputs(FLAG_OPT_MARK
, stdout
);
159 fputs(INIT_OPT_STR
, stdout
);
161 printf(OPT_ARG_FMT
, pOpts
->pzPROGNAME
);
162 fputs(END_OPT_SEL_STR
, stdout
);
164 fputs(NOT_FOUND_STR
, stdout
);
168 printf(zLoopEnd
, pOpts
->pzPROGNAME
, END_MARK
);
169 if ((script_trailer
!= NULL
) && (*script_trailer
!= NUL
))
170 fputs(script_trailer
, stdout
);
171 else if (ENABLED_GENSHELL_OPT(SHELL
))
172 printf(SHOW_PROG_ENV
, pOpts
->pzPROGNAME
);
175 fchmod(STDOUT_FILENO
, 0755);
179 if (ferror(stdout
)) {
180 fputs(zOutputFail
, stderr
);
185 #ifdef HAVE_WORKING_FORK
187 emit_var_text(char const * prog
, char const * var
, int fdin
)
189 FILE * fp
= fdopen(fdin
, "r" FOPEN_BINARY_FLAG
);
190 int nlct
= 0; /* defer newlines and skip trailing ones */
192 printf(SET_TEXT_FMT
, prog
, var
);
209 fputs(apostrophy
, stdout
);
229 fputs(END_SET_TEXT
, stdout
);
235 * The purpose of this function is to assign "long usage", short usage
236 * and version information to a shell variable. Rather than wind our
237 * way through all the logic necessary to emit the text directly, we
238 * fork(), have our child process emit the text the normal way and
239 * capture the output in the parent process.
242 text_to_var(tOptions
* pOpts
, teTextTo whichVar
, tOptDesc
* pOD
)
244 # define _TT_(n) static char const z ## n [] = #n;
247 # define _TT_(n) z ## n ,
248 static char const * apzTTNames
[] = { TEXTTO_TABLE
};
251 #if ! defined(HAVE_WORKING_FORK)
252 printf(SET_NO_TEXT_FMT
, pOpts
->pzPROGNAME
, apzTTNames
[ whichVar
]);
259 if (pipe(pipeFd
) != 0) {
260 fprintf(stderr
, zBadPipe
, errno
, strerror(errno
));
266 fprintf(stderr
, zForkFail
, errno
, strerror(errno
), pOpts
->pzProgName
);
272 * Send both stderr and stdout to the pipe. No matter which
273 * descriptor is used, we capture the output on the read end.
275 dup2(pipeFd
[1], STDERR_FILENO
);
276 dup2(pipeFd
[1], STDOUT_FILENO
);
281 (*(pOpts
->pUsageProc
))(pOpts
, EXIT_SUCCESS
);
285 (*(pOpts
->pUsageProc
))(pOpts
, EXIT_FAILURE
);
289 if (pOD
->fOptState
& OPTST_ALLOC_ARG
) {
290 AGFREE(pOD
->optArg
.argString
);
291 pOD
->fOptState
&= ~OPTST_ALLOC_ARG
;
293 pOD
->optArg
.argString
= "c";
294 optionPrintVersion(pOpts
, pOD
);
305 emit_var_text(pOpts
->pzPROGNAME
, apzTTNames
[whichVar
], pipeFd
[0]);
311 emit_usage(tOptions
* pOpts
)
313 char zTimeBuf
[AO_NAME_SIZE
];
316 * First, switch stdout to the output file name.
317 * Then, change the program name to the one defined
318 * by the definitions (rather than the current
319 * executable name). Down case the upper cased name.
321 if (script_leader
!= NULL
)
322 fputs(script_leader
, stdout
);
328 time_t c_tim
= time(NULL
);
329 struct tm
* ptm
= localtime(&c_tim
);
330 strftime(zTimeBuf
, AO_NAME_SIZE
, TIME_FMT
, ptm
);
333 if (HAVE_GENSHELL_OPT(SCRIPT
))
334 out_nm
= GENSHELL_OPT_ARG(SCRIPT
);
335 else out_nm
= STDOUT
;
337 if ((script_leader
== NULL
) && (shell_prog
!= NULL
))
338 printf(SHELL_MAGIC
, shell_prog
);
340 printf(PREAMBLE_FMT
, START_MARK
, out_nm
, zTimeBuf
);
343 printf(END_PRE_FMT
, pOpts
->pzPROGNAME
);
346 * Get a copy of the original program name in lower case and
347 * fill in an approximation of the program name from it.
350 char * pzPN
= zTimeBuf
;
351 char const * pz
= pOpts
->pzPROGNAME
;
355 if ((*pzPN
++ = (char)tolower(*pz
++)) == NUL
)
359 pp
= (char **)(void *)&(pOpts
->pzProgPath
);
361 pp
= (char **)(void *)&(pOpts
->pzProgName
);
365 text_to_var(pOpts
, TT_LONGUSAGE
, NULL
);
366 text_to_var(pOpts
, TT_USAGE
, NULL
);
369 tOptDesc
* pOptDesc
= pOpts
->pOptDesc
;
370 int optionCt
= pOpts
->optCt
;
373 if (pOptDesc
->pOptProc
== optionPrintVersion
) {
374 text_to_var(pOpts
, TT_VERSION
, pOptDesc
);
387 emit_setup(tOptions
* pOpts
)
389 tOptDesc
* pOptDesc
= pOpts
->pOptDesc
;
390 int optionCt
= pOpts
->presetOptCt
;
392 char const * pzDefault
;
394 for (;optionCt
> 0; pOptDesc
++, --optionCt
) {
398 * Options that are either usage documentation or are compiled out
399 * are not to be processed.
401 if (SKIP_OPT(pOptDesc
) || (pOptDesc
->pz_NAME
== NULL
))
404 if (pOptDesc
->optMaxCt
> 1)
405 pzFmt
= MULTI_DEF_FMT
;
406 else pzFmt
= SGL_DEF_FMT
;
409 * IF this is an enumeration/bitmask option, then convert the value
410 * to a string before printing the default value.
412 switch (OPTST_GET_ARGTYPE(pOptDesc
->fOptState
)) {
413 case OPARG_TYPE_ENUMERATION
:
414 (*(pOptDesc
->pOptProc
))(OPTPROC_EMIT_SHELL
, pOptDesc
);
415 pzDefault
= pOptDesc
->optArg
.argString
;
419 * Numeric and membership bit options are just printed as a number.
421 case OPARG_TYPE_NUMERIC
:
422 snprintf(zVal
, sizeof(zVal
), "%d",
423 (int)pOptDesc
->optArg
.argInt
);
427 case OPARG_TYPE_MEMBERSHIP
:
428 snprintf(zVal
, sizeof(zVal
), "%lu",
429 (unsigned long)pOptDesc
->optArg
.argIntptr
);
433 case OPARG_TYPE_BOOLEAN
:
434 pzDefault
= (pOptDesc
->optArg
.argBool
) ? TRUE_STR
: FALSE_STR
;
438 if (pOptDesc
->optArg
.argString
== NULL
) {
439 if (pzFmt
== SGL_DEF_FMT
)
440 pzFmt
= SGL_NO_DEF_FMT
;
444 pzDefault
= pOptDesc
->optArg
.argString
;
447 printf(pzFmt
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
, pzDefault
);
452 emit_action(tOptions
* pOpts
, tOptDesc
* pOptDesc
)
454 if (pOptDesc
->pOptProc
== optionPrintVersion
)
455 printf(zTextExit
, pOpts
->pzPROGNAME
, VER_STR
);
457 else if (pOptDesc
->pOptProc
== optionPagedUsage
)
458 printf(zPagedUsageExit
, pOpts
->pzPROGNAME
);
460 else if (pOptDesc
->pOptProc
== optionLoadOpt
) {
461 printf(zCmdFmt
, NO_LOAD_WARN
);
462 printf(zCmdFmt
, YES_NEED_OPT_ARG
);
464 } else if (pOptDesc
->pz_NAME
== NULL
) {
466 if (pOptDesc
->pOptProc
== NULL
) {
467 printf(zCmdFmt
, NO_SAVE_OPTS
);
468 printf(zCmdFmt
, OK_NEED_OPT_ARG
);
470 printf(zTextExit
, pOpts
->pzPROGNAME
, LONG_USE_STR
);
473 if (pOptDesc
->optMaxCt
== 1)
474 printf(SGL_ARG_FMT
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
476 if ((unsigned)pOptDesc
->optMaxCt
< NOLIMIT
)
477 printf(zCountTest
, pOpts
->pzPROGNAME
,
478 pOptDesc
->pz_NAME
, pOptDesc
->optMaxCt
);
480 printf(MULTI_ARG_FMT
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
486 if (OPTST_GET_ARGTYPE(pOptDesc
->fOptState
) == OPARG_TYPE_NONE
) {
487 printf(zCantArg
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
489 } else if (pOptDesc
->fOptState
& OPTST_ARG_OPTIONAL
) {
490 printf(zMayArg
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
493 fputs(zMustArg
, stdout
);
496 fputs(zOptionEndSelect
, stdout
);
501 emit_inaction(tOptions
* pOpts
, tOptDesc
* pOptDesc
)
503 if (pOptDesc
->pOptProc
== optionLoadOpt
) {
504 printf(zCmdFmt
, NO_SUPPRESS_LOAD
);
506 } else if (pOptDesc
->optMaxCt
== 1)
507 printf(NO_SGL_ARG_FMT
, pOpts
->pzPROGNAME
,
508 pOptDesc
->pz_NAME
, pOptDesc
->pz_DisablePfx
);
510 printf(NO_MULTI_ARG_FMT
, pOpts
->pzPROGNAME
,
511 pOptDesc
->pz_NAME
, pOptDesc
->pz_DisablePfx
);
513 printf(zCmdFmt
, NO_ARG_NEEDED
);
514 fputs(zOptionEndSelect
, stdout
);
519 emit_flag(tOptions
* pOpts
)
521 tOptDesc
* pOptDesc
= pOpts
->pOptDesc
;
522 int optionCt
= pOpts
->optCt
;
524 fputs(zOptionCase
, stdout
);
526 for (;optionCt
> 0; pOptDesc
++, --optionCt
) {
528 if (SKIP_OPT(pOptDesc
))
531 if (IS_GRAPHIC_CHAR(pOptDesc
->optValue
)) {
532 printf(zOptionFlag
, pOptDesc
->optValue
);
533 emit_action(pOpts
, pOptDesc
);
536 printf(UNK_OPT_FMT
, FLAG_STR
, pOpts
->pzPROGNAME
);
541 * Emit the match text for a long option
544 emit_match_expr(char const * pzMatchName
, tOptDesc
* pCurOpt
, tOptions
* pOpts
)
546 tOptDesc
* pOD
= pOpts
->pOptDesc
;
547 int oCt
= pOpts
->optCt
;
556 * Omit the current option, Documentation opts and compiled out opts.
558 if ((pOD
== pCurOpt
) || SKIP_OPT(pOD
)){
566 * Check each character of the name case insensitively.
567 * They must not be the same. They cannot be, because it would
568 * not compile correctly if they were.
570 while ( toupper(pOD
->pz_Name
[matchCt
])
571 == toupper(pzMatchName
[matchCt
]))
578 * Check the disablement name, too.
580 if (pOD
->pz_DisableName
!= NULL
) {
582 while ( toupper(pOD
->pz_DisableName
[matchCt
])
583 == toupper(pzMatchName
[matchCt
]))
594 * IF the 'min' is all or one short of the name length,
595 * THEN the entire string must be matched.
597 if ( (pzMatchName
[min
] == NUL
)
598 || (pzMatchName
[min
+1] == NUL
) )
599 printf(zOptionFullName
, pzMatchName
);
603 for (; matchCt
<= min
; matchCt
++)
604 *pz
++ = pzMatchName
[matchCt
];
608 printf(zOptionPartName
, zName
);
609 *pz
++ = pzMatchName
[matchCt
++];
610 if (pzMatchName
[matchCt
] == NUL
) {
612 printf(zOptionFullName
, zName
);
621 * Emit GNU-standard long option handling code
624 emitLong(tOptions
* pOpts
)
626 tOptDesc
* pOD
= pOpts
->pOptDesc
;
627 int ct
= pOpts
->optCt
;
629 fputs(zOptionCase
, stdout
);
632 * do each option, ...
636 * Documentation & compiled-out options
641 emit_match_expr(pOD
->pz_Name
, pOD
, pOpts
);
642 emit_action(pOpts
, pOD
);
645 * Now, do the same thing for the disablement version of the option.
647 if (pOD
->pz_DisableName
!= NULL
) {
648 emit_match_expr(pOD
->pz_DisableName
, pOD
, pOpts
);
649 emit_inaction(pOpts
, pOD
);
651 } while (pOD
++, --ct
> 0);
653 printf(UNK_OPT_FMT
, OPTION_STR
, pOpts
->pzPROGNAME
);
658 open_out(char const * pzFile
)
669 * IF we cannot stat the file,
670 * THEN assume we are creating a new file.
671 * Skip the loading of the old data.
673 if (stat(pzFile
, &stbf
) != 0)
677 * The file must be a regular file
679 if (! S_ISREG(stbf
.st_mode
)) {
680 fprintf(stderr
, zNotFile
, pzFile
);
684 pzData
= AGALOC(stbf
.st_size
+ 1, "f data");
685 fp
= fopen(pzFile
, "r" FOPEN_BINARY_FLAG
);
687 sizeLeft
= (unsigned)stbf
.st_size
;
691 * Read in all the data as fast as our OS will let us.
694 int inct
= fread((void*)pzScan
, (size_t)1, sizeLeft
, fp
);
706 * NUL-terminate the leader and look for the trailer
710 pzScan
= strstr(pzData
, START_MARK
);
711 if (pzScan
== NULL
) {
712 script_trailer
= pzData
;
717 pzScan
= strstr(pzScan
, END_MARK
);
718 if (pzScan
== NULL
) {
719 script_trailer
= pzData
;
724 * Check to see if the data contains our marker.
725 * If it does, then we will skip over it
727 script_trailer
= pzScan
+ END_MARK_LEN
;
728 script_leader
= pzData
;
731 if (freopen(pzFile
, "w" FOPEN_BINARY_FLAG
, stdout
) != stdout
) {
732 fprintf(stderr
, zFreopenFail
, errno
, strerror(errno
));
738 /*=export_func genshelloptUsage
740 * what: The usage function for the genshellopt generated program
742 * arg: + tOptions* + pOpts + program options descriptor +
743 * arg: + int + exitCode + usage text type to produce +
746 * This function is used to create the usage strings for the option
747 * processing shell script code. Two child processes are spawned
748 * each emitting the usage text in either the short (error exit)
749 * style or the long style. The generated program will capture this
750 * and create shell script variables containing the two types of text.
753 genshelloptUsage(tOptions
* pOpts
, int exitCode
)
755 #if ! defined(HAVE_WORKING_FORK)
756 optionUsage(pOpts
, exitCode
);
759 * IF not EXIT_SUCCESS,
760 * THEN emit the short form of usage.
762 if (exitCode
!= EXIT_SUCCESS
)
763 optionUsage(pOpts
, exitCode
);
766 if (ferror(stdout
) || ferror(stderr
))
769 option_usage_fp
= stdout
;
772 * First, print our usage
776 optionUsage(pOpts
, EXIT_FAILURE
);
780 pagerState
= PAGER_STATE_CHILD
;
781 optionUsage(pOpts
, EXIT_SUCCESS
);
793 * Generate the pzProgName, since optionProcess() normally
794 * gets it from the command line
798 char ** pp
= (char **)(void *)&(optionParseShellOptions
->pzProgName
);
799 AGDUPSTR(pz
, optionParseShellOptions
->pzPROGNAME
, "prog name");
808 * Separate the makeshell usage from the client usage
810 fprintf(option_usage_fp
, zGenshell
, optionParseShellOptions
->pzProgName
);
811 fflush(option_usage_fp
);
814 * Now, print the client usage.
818 pagerState
= PAGER_STATE_CHILD
;
821 optionUsage(optionParseShellOptions
, EXIT_FAILURE
);
831 if (ferror(stdout
)) {
832 fputs(zOutputFail
, stderr
);
843 * c-file-style: "stroustrup"
844 * indent-tabs-mode: nil
846 * end of autoopts/makeshell.c */