5 * Time-stamp: "2012-01-29 19:01:07 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
31 tOptions
* optionParseShellOptions
= NULL
;
33 static char const * shell_prog
= NULL
;
34 static char * script_leader
= NULL
;
35 static char * script_trailer
= NULL
;
37 /* = = = START-STATIC-FORWARD = = = */
39 emit_var_text(char const * prog
, char const * var
, int fdin
);
42 text_to_var(tOptions
* pOpts
, teTextTo whichVar
, tOptDesc
* pOD
);
45 emit_usage(tOptions
* pOpts
);
48 emit_setup(tOptions
* pOpts
);
51 emit_action(tOptions
* pOpts
, tOptDesc
* pOptDesc
);
54 emit_inaction(tOptions
* pOpts
, tOptDesc
* pOptDesc
);
57 emit_flag(tOptions
* pOpts
);
60 emit_match_expr(char const * pzMatchName
, tOptDesc
* pCurOpt
, tOptions
* pOpts
);
63 emitLong(tOptions
* pOpts
);
66 open_out(char const * pzFile
);
67 /* = = = END-STATIC-FORWARD = = = */
69 /*=export_func optionParseShell
72 * what: Decipher a boolean value
73 * arg: + tOptions* + pOpts + program options descriptor +
76 * Emit a shell script that will parse the command line options.
79 optionParseShell(tOptions
* pOpts
)
82 * Check for our SHELL option now.
83 * IF the output file contains the "#!" magic marker,
84 * it will override anything we do here.
86 if (HAVE_GENSHELL_OPT(SHELL
))
87 shell_prog
= GENSHELL_OPT_ARG(SHELL
);
89 else if (! ENABLED_GENSHELL_OPT(SHELL
))
92 else if ((shell_prog
= getenv("SHELL")),
95 shell_prog
= POSIX_SHELL
;
98 * Check for a specified output file
100 if (HAVE_GENSHELL_OPT(SCRIPT
))
101 open_out(GENSHELL_OPT_ARG(SCRIPT
));
107 * There are four modes of option processing.
109 switch (pOpts
->fOptSet
& (OPTPROC_LONGOPT
|OPTPROC_SHORTOPT
)) {
110 case OPTPROC_LONGOPT
:
111 fputs(LOOP_STR
, stdout
);
113 fputs(LONG_OPT_MARK
, stdout
);
114 fputs(INIT_LOPT_STR
, stdout
);
116 printf(LOPT_ARG_FMT
, pOpts
->pzPROGNAME
);
117 fputs(END_OPT_SEL_STR
, stdout
);
119 fputs(NOT_FOUND_STR
, stdout
);
123 fputs(ONLY_OPTS_LOOP
, stdout
);
124 fputs(INIT_LOPT_STR
, stdout
);
126 printf(LOPT_ARG_FMT
, pOpts
->pzPROGNAME
);
129 case OPTPROC_SHORTOPT
:
130 fputs(LOOP_STR
, stdout
);
132 fputs(FLAG_OPT_MARK
, stdout
);
133 fputs(INIT_OPT_STR
, stdout
);
135 printf(OPT_ARG_FMT
, pOpts
->pzPROGNAME
);
136 fputs(END_OPT_SEL_STR
, stdout
);
138 fputs(NOT_FOUND_STR
, stdout
);
141 case OPTPROC_LONGOPT
|OPTPROC_SHORTOPT
:
142 fputs(LOOP_STR
, stdout
);
144 fputs(LONG_OPT_MARK
, stdout
);
145 fputs(INIT_LOPT_STR
, stdout
);
147 printf(LOPT_ARG_FMT
, pOpts
->pzPROGNAME
);
148 fputs(END_OPT_SEL_STR
, stdout
);
150 fputs(FLAG_OPT_MARK
, stdout
);
151 fputs(INIT_OPT_STR
, stdout
);
153 printf(OPT_ARG_FMT
, pOpts
->pzPROGNAME
);
154 fputs(END_OPT_SEL_STR
, stdout
);
156 fputs(NOT_FOUND_STR
, stdout
);
160 printf(zLoopEnd
, pOpts
->pzPROGNAME
, END_MARK
);
161 if ((script_trailer
!= NULL
) && (*script_trailer
!= NUL
))
162 fputs(script_trailer
, stdout
);
163 else if (ENABLED_GENSHELL_OPT(SHELL
))
164 printf(SHOW_PROG_ENV
, pOpts
->pzPROGNAME
);
168 fchmod(STDOUT_FILENO
, 0755);
171 if (ferror(stdout
)) {
172 fputs(zOutputFail
, stderr
);
177 #ifdef HAVE_WORKING_FORK
179 emit_var_text(char const * prog
, char const * var
, int fdin
)
181 FILE * fp
= fdopen(fdin
, "r" FOPEN_BINARY_FLAG
);
182 int nlct
= 0; /* defer newlines and skip trailing ones */
184 printf(SET_TEXT_FMT
, prog
, var
);
201 fputs(apostrophy
, stdout
);
221 fputs(END_SET_TEXT
, stdout
);
227 * The purpose of this function is to assign "long usage", short usage
228 * and version information to a shell variable. Rather than wind our
229 * way through all the logic necessary to emit the text directly, we
230 * fork(), have our child process emit the text the normal way and
231 * capture the output in the parent process.
234 text_to_var(tOptions
* pOpts
, teTextTo whichVar
, tOptDesc
* pOD
)
236 # define _TT_(n) static char const z ## n [] = #n;
239 # define _TT_(n) z ## n ,
240 static char const * apzTTNames
[] = { TEXTTO_TABLE
};
243 #if ! defined(HAVE_WORKING_FORK)
244 printf(SET_NO_TEXT_FMT
, pOpts
->pzPROGNAME
, apzTTNames
[ whichVar
]);
251 if (pipe(pipeFd
) != 0) {
252 fprintf(stderr
, zBadPipe
, errno
, strerror(errno
));
258 fprintf(stderr
, zForkFail
, errno
, strerror(errno
), pOpts
->pzProgName
);
264 * Send both stderr and stdout to the pipe. No matter which
265 * descriptor is used, we capture the output on the read end.
267 dup2(pipeFd
[1], STDERR_FILENO
);
268 dup2(pipeFd
[1], STDOUT_FILENO
);
273 (*(pOpts
->pUsageProc
))(pOpts
, EXIT_SUCCESS
);
277 (*(pOpts
->pUsageProc
))(pOpts
, EXIT_FAILURE
);
281 if (pOD
->fOptState
& OPTST_ALLOC_ARG
) {
282 AGFREE(pOD
->optArg
.argString
);
283 pOD
->fOptState
&= ~OPTST_ALLOC_ARG
;
285 pOD
->optArg
.argString
= "c";
286 optionPrintVersion(pOpts
, pOD
);
297 emit_var_text(pOpts
->pzPROGNAME
, apzTTNames
[whichVar
], pipeFd
[0]);
303 emit_usage(tOptions
* pOpts
)
305 char zTimeBuf
[AO_NAME_SIZE
];
308 * First, switch stdout to the output file name.
309 * Then, change the program name to the one defined
310 * by the definitions (rather than the current
311 * executable name). Down case the upper cased name.
313 if (script_leader
!= NULL
)
314 fputs(script_leader
, stdout
);
320 time_t c_tim
= time(NULL
);
321 struct tm
* ptm
= localtime(&c_tim
);
322 strftime(zTimeBuf
, AO_NAME_SIZE
, TIME_FMT
, ptm
);
325 if (HAVE_GENSHELL_OPT(SCRIPT
))
326 out_nm
= GENSHELL_OPT_ARG(SCRIPT
);
327 else out_nm
= STDOUT
;
329 if ((script_leader
== NULL
) && (shell_prog
!= NULL
))
330 printf(SHELL_MAGIC
, shell_prog
);
332 printf(PREAMBLE_FMT
, START_MARK
, out_nm
, zTimeBuf
);
335 printf(END_PRE_FMT
, pOpts
->pzPROGNAME
);
338 * Get a copy of the original program name in lower case and
339 * fill in an approximation of the program name from it.
342 char * pzPN
= zTimeBuf
;
343 char const * pz
= pOpts
->pzPROGNAME
;
347 if ((*pzPN
++ = (char)tolower(*pz
++)) == NUL
)
351 pp
= (char **)(void *)&(pOpts
->pzProgPath
);
353 pp
= (char **)(void *)&(pOpts
->pzProgName
);
357 text_to_var(pOpts
, TT_LONGUSAGE
, NULL
);
358 text_to_var(pOpts
, TT_USAGE
, NULL
);
361 tOptDesc
* pOptDesc
= pOpts
->pOptDesc
;
362 int optionCt
= pOpts
->optCt
;
365 if (pOptDesc
->pOptProc
== optionPrintVersion
) {
366 text_to_var(pOpts
, TT_VERSION
, pOptDesc
);
379 emit_setup(tOptions
* pOpts
)
381 tOptDesc
* pOptDesc
= pOpts
->pOptDesc
;
382 int optionCt
= pOpts
->presetOptCt
;
384 char const * pzDefault
;
386 for (;optionCt
> 0; pOptDesc
++, --optionCt
) {
390 * Options that are either usage documentation or are compiled out
391 * are not to be processed.
393 if (SKIP_OPT(pOptDesc
) || (pOptDesc
->pz_NAME
== NULL
))
396 if (pOptDesc
->optMaxCt
> 1)
397 pzFmt
= MULTI_DEF_FMT
;
398 else pzFmt
= SGL_DEF_FMT
;
401 * IF this is an enumeration/bitmask option, then convert the value
402 * to a string before printing the default value.
404 switch (OPTST_GET_ARGTYPE(pOptDesc
->fOptState
)) {
405 case OPARG_TYPE_ENUMERATION
:
406 (*(pOptDesc
->pOptProc
))(OPTPROC_EMIT_SHELL
, pOptDesc
);
407 pzDefault
= pOptDesc
->optArg
.argString
;
411 * Numeric and membership bit options are just printed as a number.
413 case OPARG_TYPE_NUMERIC
:
414 snprintf(zVal
, sizeof(zVal
), "%d",
415 (int)pOptDesc
->optArg
.argInt
);
419 case OPARG_TYPE_MEMBERSHIP
:
420 snprintf(zVal
, sizeof(zVal
), "%lu",
421 (unsigned long)pOptDesc
->optArg
.argIntptr
);
425 case OPARG_TYPE_BOOLEAN
:
426 pzDefault
= (pOptDesc
->optArg
.argBool
) ? TRUE_STR
: FALSE_STR
;
430 if (pOptDesc
->optArg
.argString
== NULL
) {
431 if (pzFmt
== SGL_DEF_FMT
)
432 pzFmt
= SGL_NO_DEF_FMT
;
436 pzDefault
= pOptDesc
->optArg
.argString
;
439 printf(pzFmt
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
, pzDefault
);
444 emit_action(tOptions
* pOpts
, tOptDesc
* pOptDesc
)
446 if (pOptDesc
->pOptProc
== optionPrintVersion
)
447 printf(zTextExit
, pOpts
->pzPROGNAME
, VER_STR
);
449 else if (pOptDesc
->pOptProc
== optionPagedUsage
)
450 printf(zPagedUsageExit
, pOpts
->pzPROGNAME
);
452 else if (pOptDesc
->pOptProc
== optionLoadOpt
) {
453 printf(zCmdFmt
, NO_LOAD_WARN
);
454 printf(zCmdFmt
, YES_NEED_OPT_ARG
);
456 } else if (pOptDesc
->pz_NAME
== NULL
) {
458 if (pOptDesc
->pOptProc
== NULL
) {
459 printf(zCmdFmt
, NO_SAVE_OPTS
);
460 printf(zCmdFmt
, OK_NEED_OPT_ARG
);
462 printf(zTextExit
, pOpts
->pzPROGNAME
, LONG_USE_STR
);
465 if (pOptDesc
->optMaxCt
== 1)
466 printf(SGL_ARG_FMT
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
468 if ((unsigned)pOptDesc
->optMaxCt
< NOLIMIT
)
469 printf(zCountTest
, pOpts
->pzPROGNAME
,
470 pOptDesc
->pz_NAME
, pOptDesc
->optMaxCt
);
472 printf(MULTI_ARG_FMT
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
478 if (OPTST_GET_ARGTYPE(pOptDesc
->fOptState
) == OPARG_TYPE_NONE
) {
479 printf(zCantArg
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
481 } else if (pOptDesc
->fOptState
& OPTST_ARG_OPTIONAL
) {
482 printf(zMayArg
, pOpts
->pzPROGNAME
, pOptDesc
->pz_NAME
);
485 fputs(zMustArg
, stdout
);
488 fputs(zOptionEndSelect
, stdout
);
493 emit_inaction(tOptions
* pOpts
, tOptDesc
* pOptDesc
)
495 if (pOptDesc
->pOptProc
== optionLoadOpt
) {
496 printf(zCmdFmt
, NO_SUPPRESS_LOAD
);
498 } else if (pOptDesc
->optMaxCt
== 1)
499 printf(NO_SGL_ARG_FMT
, pOpts
->pzPROGNAME
,
500 pOptDesc
->pz_NAME
, pOptDesc
->pz_DisablePfx
);
502 printf(NO_MULTI_ARG_FMT
, pOpts
->pzPROGNAME
,
503 pOptDesc
->pz_NAME
, pOptDesc
->pz_DisablePfx
);
505 printf(zCmdFmt
, NO_ARG_NEEDED
);
506 fputs(zOptionEndSelect
, stdout
);
511 emit_flag(tOptions
* pOpts
)
513 tOptDesc
* pOptDesc
= pOpts
->pOptDesc
;
514 int optionCt
= pOpts
->optCt
;
516 fputs(zOptionCase
, stdout
);
518 for (;optionCt
> 0; pOptDesc
++, --optionCt
) {
520 if (SKIP_OPT(pOptDesc
))
523 if (IS_GRAPHIC_CHAR(pOptDesc
->optValue
)) {
524 printf(zOptionFlag
, pOptDesc
->optValue
);
525 emit_action(pOpts
, pOptDesc
);
528 printf(UNK_OPT_FMT
, FLAG_STR
, pOpts
->pzPROGNAME
);
533 * Emit the match text for a long option
536 emit_match_expr(char const * pzMatchName
, tOptDesc
* pCurOpt
, tOptions
* pOpts
)
538 tOptDesc
* pOD
= pOpts
->pOptDesc
;
539 int oCt
= pOpts
->optCt
;
548 * Omit the current option, Documentation opts and compiled out opts.
550 if ((pOD
== pCurOpt
) || SKIP_OPT(pOD
)){
558 * Check each character of the name case insensitively.
559 * They must not be the same. They cannot be, because it would
560 * not compile correctly if they were.
562 while ( toupper(pOD
->pz_Name
[matchCt
])
563 == toupper(pzMatchName
[matchCt
]))
570 * Check the disablement name, too.
572 if (pOD
->pz_DisableName
!= NULL
) {
574 while ( toupper(pOD
->pz_DisableName
[matchCt
])
575 == toupper(pzMatchName
[matchCt
]))
586 * IF the 'min' is all or one short of the name length,
587 * THEN the entire string must be matched.
589 if ( (pzMatchName
[min
] == NUL
)
590 || (pzMatchName
[min
+1] == NUL
) )
591 printf(zOptionFullName
, pzMatchName
);
595 for (; matchCt
<= min
; matchCt
++)
596 *pz
++ = pzMatchName
[matchCt
];
600 printf(zOptionPartName
, zName
);
601 *pz
++ = pzMatchName
[matchCt
++];
602 if (pzMatchName
[matchCt
] == NUL
) {
604 printf(zOptionFullName
, zName
);
613 * Emit GNU-standard long option handling code
616 emitLong(tOptions
* pOpts
)
618 tOptDesc
* pOD
= pOpts
->pOptDesc
;
619 int ct
= pOpts
->optCt
;
621 fputs(zOptionCase
, stdout
);
624 * do each option, ...
628 * Documentation & compiled-out options
633 emit_match_expr(pOD
->pz_Name
, pOD
, pOpts
);
634 emit_action(pOpts
, pOD
);
637 * Now, do the same thing for the disablement version of the option.
639 if (pOD
->pz_DisableName
!= NULL
) {
640 emit_match_expr(pOD
->pz_DisableName
, pOD
, pOpts
);
641 emit_inaction(pOpts
, pOD
);
643 } while (pOD
++, --ct
> 0);
645 printf(UNK_OPT_FMT
, OPTION_STR
, pOpts
->pzPROGNAME
);
650 open_out(char const * pzFile
)
661 * IF we cannot stat the file,
662 * THEN assume we are creating a new file.
663 * Skip the loading of the old data.
665 if (stat(pzFile
, &stbf
) != 0)
669 * The file must be a regular file
671 if (! S_ISREG(stbf
.st_mode
)) {
672 fprintf(stderr
, zNotFile
, pzFile
);
676 pzData
= AGALOC(stbf
.st_size
+ 1, "f data");
677 fp
= fopen(pzFile
, "r" FOPEN_BINARY_FLAG
);
679 sizeLeft
= (unsigned)stbf
.st_size
;
683 * Read in all the data as fast as our OS will let us.
686 int inct
= fread((void*)pzScan
, (size_t)1, sizeLeft
, fp
);
698 * NUL-terminate the leader and look for the trailer
702 pzScan
= strstr(pzData
, START_MARK
);
703 if (pzScan
== NULL
) {
704 script_trailer
= pzData
;
709 pzScan
= strstr(pzScan
, END_MARK
);
710 if (pzScan
== NULL
) {
711 script_trailer
= pzData
;
716 * Check to see if the data contains our marker.
717 * If it does, then we will skip over it
719 script_trailer
= pzScan
+ END_MARK_LEN
;
720 script_leader
= pzData
;
723 if (freopen(pzFile
, "w" FOPEN_BINARY_FLAG
, stdout
) != stdout
) {
724 fprintf(stderr
, zFreopenFail
, errno
, strerror(errno
));
730 /*=export_func genshelloptUsage
732 * what: The usage function for the genshellopt generated program
734 * arg: + tOptions* + pOpts + program options descriptor +
735 * arg: + int + exitCode + usage text type to produce +
738 * This function is used to create the usage strings for the option
739 * processing shell script code. Two child processes are spawned
740 * each emitting the usage text in either the short (error exit)
741 * style or the long style. The generated program will capture this
742 * and create shell script variables containing the two types of text.
745 genshelloptUsage(tOptions
* pOpts
, int exitCode
)
747 #if ! defined(HAVE_WORKING_FORK)
748 optionUsage(pOpts
, exitCode
);
751 * IF not EXIT_SUCCESS,
752 * THEN emit the short form of usage.
754 if (exitCode
!= EXIT_SUCCESS
)
755 optionUsage(pOpts
, exitCode
);
758 if (ferror(stdout
) || ferror(stderr
))
761 option_usage_fp
= stdout
;
764 * First, print our usage
768 optionUsage(pOpts
, EXIT_FAILURE
);
772 pagerState
= PAGER_STATE_CHILD
;
773 optionUsage(pOpts
, EXIT_SUCCESS
);
785 * Generate the pzProgName, since optionProcess() normally
786 * gets it from the command line
790 char ** pp
= (char **)(void *)&(optionParseShellOptions
->pzProgName
);
791 AGDUPSTR(pz
, optionParseShellOptions
->pzPROGNAME
, "prog name");
800 * Separate the makeshell usage from the client usage
802 fprintf(option_usage_fp
, zGenshell
, optionParseShellOptions
->pzProgName
);
803 fflush(option_usage_fp
);
806 * Now, print the client usage.
810 pagerState
= PAGER_STATE_CHILD
;
813 optionUsage(optionParseShellOptions
, EXIT_FAILURE
);
823 if (ferror(stdout
)) {
824 fputs(zOutputFail
, stderr
);
835 * c-file-style: "stroustrup"
836 * indent-tabs-mode: nil
838 * end of autoopts/makeshell.c */