5 * This module will interpret the options set in the tOptions
6 * structure and create a Bourne shell script capable of parsing them.
12 * This file is part of AutoOpts, a companion to AutoGen.
13 * AutoOpts is free software.
14 * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
16 * AutoOpts is available under any one of two licenses. The license
17 * in use must be one of these two and the choice is under the control
18 * of the user of the license.
20 * The GNU Lesser General Public License, version 3 or later
21 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
23 * The Modified Berkeley Software Distribution License
24 * See the file "COPYING.mbsd"
26 * These files have the following sha256 sums:
28 * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
29 * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
30 * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
33 static inline unsigned char to_uchar (char ch) { return ch; }
35 #define UPPER(_c) (toupper(to_uchar(_c)))
36 #define LOWER(_c) (tolower(to_uchar(_c)))
38 /* = = = START-STATIC-FORWARD = = = */
40 emit_var_text(char const * prog, char const * var, int fdin);
43 text_to_var(tOptions * opts, teTextTo which, tOptDesc * od);
46 emit_usage(tOptions * opts);
49 emit_wrapup(tOptions * opts);
52 emit_setup(tOptions * opts);
55 emit_action(tOptions * opts, tOptDesc * od);
58 emit_inaction(tOptions * opts, tOptDesc * od);
61 emit_flag(tOptions * opts);
64 emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts);
67 emit_long(tOptions * opts);
70 load_old_output(char const * fname, char const * pname);
73 open_out(char const * fname, char const * pname);
74 /* = = = END-STATIC-FORWARD = = = */
77 option_exits(int exit_code)
80 printf("\nexit %d\n", exit_code);
85 ao_bug(char const * msg)
87 fprintf(stderr, zao_bug_msg, msg);
88 option_exits(EX_SOFTWARE);
92 fserr_warn(char const * prog, char const * op, char const * fname)
94 fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno),
99 fserr_exit(char const * prog, char const * op, char const * fname)
101 fserr_warn(prog, op, fname);
102 option_exits(EXIT_FAILURE);
105 /*=export_func optionParseShell
108 * what: Decipher a boolean value
109 * arg: + tOptions * + pOpts + program options descriptor +
112 * Emit a shell script that will parse the command line options.
115 optionParseShell(tOptions * opts)
118 * Check for our SHELL option now.
119 * IF the output file contains the "#!" magic marker,
120 * it will override anything we do here.
122 if (HAVE_GENSHELL_OPT(SHELL))
123 shell_prog = GENSHELL_OPT_ARG(SHELL);
125 else if (! ENABLED_GENSHELL_OPT(SHELL))
128 else if ((shell_prog = getenv("SHELL")),
131 shell_prog = POSIX_SHELL;
134 * Check for a specified output file
136 if (HAVE_GENSHELL_OPT(SCRIPT))
137 open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName);
143 * There are four modes of option processing.
145 switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
146 case OPTPROC_LONGOPT:
147 fputs(LOOP_STR, stdout);
149 fputs(LONG_OPT_MARK, stdout);
150 fputs(INIT_LOPT_STR, stdout);
152 printf(LOPT_ARG_FMT, opts->pzPROGNAME);
153 fputs(END_OPT_SEL_STR, stdout);
155 fputs(NOT_FOUND_STR, stdout);
159 fputs(ONLY_OPTS_LOOP, stdout);
160 fputs(INIT_LOPT_STR, stdout);
162 printf(LOPT_ARG_FMT, opts->pzPROGNAME);
165 case OPTPROC_SHORTOPT:
166 fputs(LOOP_STR, stdout);
168 fputs(FLAG_OPT_MARK, stdout);
169 fputs(INIT_OPT_STR, stdout);
171 printf(OPT_ARG_FMT, opts->pzPROGNAME);
172 fputs(END_OPT_SEL_STR, stdout);
174 fputs(NOT_FOUND_STR, stdout);
177 case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
178 fputs(LOOP_STR, stdout);
180 fputs(LONG_OPT_MARK, stdout);
181 fputs(INIT_LOPT_STR, stdout);
183 printf(LOPT_ARG_FMT, opts->pzPROGNAME);
184 fputs(END_OPT_SEL_STR, stdout);
186 fputs(FLAG_OPT_MARK, stdout);
187 fputs(INIT_OPT_STR, stdout);
189 printf(OPT_ARG_FMT, opts->pzPROGNAME);
190 fputs(END_OPT_SEL_STR, stdout);
192 fputs(NOT_FOUND_STR, stdout);
197 if ((script_trailer != NULL) && (*script_trailer != NUL))
198 fputs(script_trailer, stdout);
199 else if (ENABLED_GENSHELL_OPT(SHELL))
200 printf(SHOW_PROG_ENV, opts->pzPROGNAME);
203 fchmod(STDOUT_FILENO, 0755);
208 fserr_exit(opts->pzProgName, zwriting, zstdout_name);
211 script_leader = NULL;
212 script_trailer = NULL;
216 #ifdef HAVE_WORKING_FORK
218 * Print the value of "var" to a file descriptor.
219 * The "fdin" is the read end of a pipe to a forked process that
220 * is writing usage text to it. We read that text in and re-emit
221 * to standard out, formatting it so that it is assigned to a
224 * @param[in] prog The capitalized, c-variable-formatted program name
225 * @param[in] var a similarly formatted type name
226 * (LONGUSAGE, USAGE or VERSION)
227 * @param[in] fdin the input end of a pipe
230 emit_var_text(char const * prog, char const * var, int fdin)
232 FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
233 int nlct = 0; /* defer newlines and skip trailing ones */
235 printf(SET_TEXT_FMT, prog, var);
252 fputs(apostrophe, stdout);
272 fputs(END_SET_TEXT, stdout);
277 * The purpose of this function is to assign "long usage", short usage
278 * and version information to a shell variable. Rather than wind our
279 * way through all the logic necessary to emit the text directly, we
280 * fork(), have our child process emit the text the normal way and
281 * capture the output in the parent process.
283 * @param[in] opts the program options
284 * @param[in] which what to print: long usage, usage or version
285 * @param[in] od for TT_VERSION, it is the version option
288 text_to_var(tOptions * opts, teTextTo which, tOptDesc * od)
290 # define _TT_(n) static char const z ## n [] = #n;
293 # define _TT_(n) z ## n ,
294 static char const * ttnames[] = { TEXTTO_TABLE };
297 #if ! defined(HAVE_WORKING_FORK)
298 printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]);
305 if (pipe(fdpair) != 0)
306 fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe);
310 fserr_exit(opts->pzProgName, "fork", opts->pzProgName);
315 * Send both stderr and stdout to the pipe. No matter which
316 * descriptor is used, we capture the output on the read end.
318 dup2(fdpair[1], STDERR_FILENO);
319 dup2(fdpair[1], STDOUT_FILENO);
324 (*(opts->pUsageProc))(opts, EXIT_SUCCESS);
328 (*(opts->pUsageProc))(opts, EXIT_FAILURE);
332 if (od->fOptState & OPTST_ALLOC_ARG) {
333 AGFREE(od->optArg.argString);
334 od->fOptState &= ~OPTST_ALLOC_ARG;
336 od->optArg.argString = "c";
337 optionPrintVersion(opts, od);
341 option_exits(EXIT_FAILURE);
350 emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]);
355 * capture usage text in shell variables.
359 emit_usage(tOptions * opts)
361 char tm_nm_buf[AO_NAME_SIZE];
364 * First, switch stdout to the output file name.
365 * Then, change the program name to the one defined
366 * by the definitions (rather than the current
367 * executable name). Down case the upper cased name.
369 if (script_leader != NULL)
370 fputs(script_leader, stdout);
376 time_t c_tim = time(NULL);
377 struct tm * ptm = localtime(&c_tim);
378 strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm );
381 if (HAVE_GENSHELL_OPT(SCRIPT))
382 out_nm = GENSHELL_OPT_ARG(SCRIPT);
383 else out_nm = STDOUT;
385 if ((script_leader == NULL) && (shell_prog != NULL))
386 printf(SHELL_MAGIC, shell_prog);
388 printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf);
391 printf(END_PRE_FMT, opts->pzPROGNAME);
394 * Get a copy of the original program name in lower case and
395 * fill in an approximation of the program name from it.
398 char * pzPN = tm_nm_buf;
399 char const * pz = opts->pzPROGNAME;
402 /* Copy the program name into the time/name buffer */
404 if ((*pzPN++ = (char)tolower((unsigned char)*pz++)) == NUL)
408 pp = VOIDP(&(opts->pzProgPath));
410 pp = VOIDP(&(opts->pzProgName));
414 text_to_var(opts, TT_LONGUSAGE, NULL);
415 text_to_var(opts, TT_USAGE, NULL);
418 tOptDesc * pOptDesc = opts->pOptDesc;
419 int optionCt = opts->optCt;
422 if (pOptDesc->pOptProc == optionPrintVersion) {
423 text_to_var(opts, TT_VERSION, pOptDesc);
435 emit_wrapup(tOptions * opts)
437 tOptDesc * od = opts->pOptDesc;
438 int opt_ct = opts->presetOptCt;
441 printf(FINISH_LOOP, opts->pzPROGNAME);
442 for (;opt_ct > 0; od++, --opt_ct) {
444 * Options that are either usage documentation or are compiled out
445 * are not to be processed.
447 if (SKIP_OPT(od) || (od->pz_NAME == NULL))
451 * do not presence check if there is no minimum/must-set
453 if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0))
456 if (od->optMaxCt > 1)
458 else fmt = CHK_ONE_REQUIRED;
461 int min = (od->optMinCt == 0) ? 1 : od->optMinCt;
462 printf(fmt, opts->pzPROGNAME, od->pz_NAME, min);
465 fputs(END_MARK, stdout);
469 emit_setup(tOptions * opts)
471 tOptDesc * od = opts->pOptDesc;
472 int opt_ct = opts->presetOptCt;
474 char const * def_val;
476 for (;opt_ct > 0; od++, --opt_ct) {
477 char int_val_buf[32];
480 * Options that are either usage documentation or are compiled out
481 * are not to be processed.
483 if (SKIP_OPT(od) || (od->pz_NAME == NULL))
486 if (od->optMaxCt > 1)
488 else fmt = SGL_DEF_FMT;
491 * IF this is an enumeration/bitmask option, then convert the value
492 * to a string before printing the default value.
494 switch (OPTST_GET_ARGTYPE(od->fOptState)) {
495 case OPARG_TYPE_ENUMERATION:
496 (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od );
497 def_val = od->optArg.argString;
501 * Numeric and membership bit options are just printed as a number.
503 case OPARG_TYPE_NUMERIC:
504 snprintf(int_val_buf, sizeof(int_val_buf), "%d",
505 (int)od->optArg.argInt);
506 def_val = int_val_buf;
509 case OPARG_TYPE_MEMBERSHIP:
510 snprintf(int_val_buf, sizeof(int_val_buf), "%lu",
511 (unsigned long)od->optArg.argIntptr);
512 def_val = int_val_buf;
515 case OPARG_TYPE_BOOLEAN:
516 def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR;
520 if (od->optArg.argString == NULL) {
521 if (fmt == SGL_DEF_FMT)
522 fmt = SGL_NO_DEF_FMT;
526 def_val = od->optArg.argString;
529 printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val);
534 emit_action(tOptions * opts, tOptDesc * od)
536 if (od->pOptProc == optionPrintVersion)
537 printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR);
539 else if (od->pOptProc == optionPagedUsage)
540 printf(PAGE_USAGE_TEXT, opts->pzPROGNAME);
542 else if (od->pOptProc == optionLoadOpt) {
543 printf(LVL3_CMD, NO_LOAD_WARN);
544 printf(LVL3_CMD, YES_NEED_OPT_ARG);
546 } else if (od->pz_NAME == NULL) {
548 if (od->pOptProc == NULL) {
549 printf(LVL3_CMD, NO_SAVE_OPTS);
550 printf(LVL3_CMD, OK_NEED_OPT_ARG);
552 printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR);
555 if (od->optMaxCt == 1)
556 printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
558 if ((unsigned)od->optMaxCt < NOLIMIT)
559 printf(CHK_MAX_COUNT, opts->pzPROGNAME,
560 od->pz_NAME, od->optMaxCt);
562 printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
568 if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) {
569 printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
570 printf(LVL3_CMD, NO_ARG_NEEDED);
572 } else if (od->fOptState & OPTST_ARG_OPTIONAL) {
573 printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
574 printf(LVL3_CMD, OK_NEED_OPT_ARG);
577 printf(LVL3_CMD, YES_NEED_OPT_ARG);
580 fputs(zOptionEndSelect, stdout);
584 emit_inaction(tOptions * opts, tOptDesc * od)
586 if (od->pOptProc == optionLoadOpt) {
587 printf(LVL3_CMD, NO_SUPPRESS_LOAD);
589 } else if (od->optMaxCt == 1)
590 printf(NO_SGL_ARG_FMT, opts->pzPROGNAME,
591 od->pz_NAME, od->pz_DisablePfx);
593 printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME,
594 od->pz_NAME, od->pz_DisablePfx);
596 printf(LVL3_CMD, NO_ARG_NEEDED);
597 fputs(zOptionEndSelect, stdout);
601 * recognize flag options. These go at the end.
602 * At the end, emit code to handle options we don't recognize.
604 * @param[in] opts the program options
607 emit_flag(tOptions * opts)
609 tOptDesc * od = opts->pOptDesc;
610 int opt_ct = opts->optCt;
612 fputs(zOptionCase, stdout);
614 for (;opt_ct > 0; od++, --opt_ct) {
616 if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue))
619 printf(zOptionFlag, od->optValue);
620 emit_action(opts, od);
622 printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME);
626 * Emit the match text for a long option. The passed in \a name may be
627 * either the enablement name or the disablement name.
629 * @param[in] name The current name to check.
630 * @param[in] cod current option descriptor
631 * @param[in] opts the program options
634 emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts)
637 unsigned int min_match_ct = 2;
638 unsigned int max_match_ct = strlen(name) - 1;
640 if (max_match_ct >= sizeof(name_bf) - 1)
644 tOptDesc * od = opts->pOptDesc;
645 int ct = opts->optCt;
647 for (; ct-- > 0; od++) {
648 unsigned int match_ct = 0;
651 * Omit the current option, Doc opts and compiled out opts.
653 if ((od == cod) || SKIP_OPT(od))
657 * Check each character of the name case insensitively.
658 * They must not be the same. They cannot be, because it would
659 * not compile correctly if they were.
661 while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct]))
664 if (match_ct > min_match_ct)
665 min_match_ct = match_ct;
668 * Check the disablement name, too.
670 if (od->pz_DisableName == NULL)
674 while ( toupper((unsigned char)od->pz_DisableName[match_ct])
675 == toupper((unsigned char)name[match_ct]))
677 if (match_ct > min_match_ct)
678 min_match_ct = match_ct;
683 * Don't bother emitting partial matches if there is only one possible
686 if (min_match_ct < max_match_ct) {
687 char * pz = name_bf + min_match_ct;
688 int nm_ix = min_match_ct;
690 memcpy(name_bf, name, min_match_ct);
694 printf(zOptionPartName, name_bf);
695 *pz++ = name[nm_ix++];
696 if (name[nm_ix] == NUL) {
704 printf(zOptionFullName, name);
708 * Emit GNU-standard long option handling code.
710 * @param[in] opts the program options
713 emit_long(tOptions * opts)
715 tOptDesc * od = opts->pOptDesc;
716 int ct = opts->optCt;
718 fputs(zOptionCase, stdout);
721 * do each option, ...
725 * Documentation & compiled-out options
730 emit_match_expr(od->pz_Name, od, opts);
731 emit_action(opts, od);
734 * Now, do the same thing for the disablement version of the option.
736 if (od->pz_DisableName != NULL) {
737 emit_match_expr(od->pz_DisableName, od, opts);
738 emit_inaction(opts, od);
740 } while (od++, --ct > 0);
742 printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME);
746 * Load the previous shell script output file. We need to preserve any
747 * hand-edited additions outside of the START_MARK and END_MARKs.
749 * @param[in] fname the output file name
752 load_old_output(char const * fname, char const * pname)
755 * IF we cannot stat the file,
756 * THEN assume we are creating a new file.
757 * Skip the loading of the old data.
759 FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG);
768 * If we opened it, we should be able to stat it and it needs
769 * to be a regular file
771 if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode)))
772 fserr_exit(pname, "fstat", fname);
774 scan = text = AGALOC(stbf.st_size + 1, "f data");
777 * Read in all the data as fast as our OS will let us.
780 size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp);
784 stbf.st_size -= (ssize_t)inct;
786 if (stbf.st_size == 0)
799 * Open the specified output file. If it already exists, load its
800 * contents and save the non-generated (hand edited) portions.
801 * If a "start mark" is found, everything before it is preserved leader.
802 * If not, the entire thing is a trailer. Assuming the start is found,
803 * then everything after the end marker is the trailer. If the end
804 * mark is not found, the file is actually corrupt, but we take the
805 * remainder to be the trailer.
807 * @param[in] fname the output file name
810 open_out(char const * fname, char const * pname)
814 char * txt = script_text = load_old_output(fname, pname);
820 scn = strstr(txt, START_MARK);
822 script_trailer = txt;
827 scn = strstr(scn, END_MARK);
830 * The file is corrupt. Set the trailer to be everything
831 * after the start mark. The user will need to fix it up.
833 script_trailer = txt + strlen(txt) + START_MARK_LEN + 1;
838 * Check to see if the data contains our marker.
839 * If it does, then we will skip over it
841 script_trailer = scn + END_MARK_LEN;
845 if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout)
846 fserr_exit(pname, "freopen", fname);
849 /*=export_func genshelloptUsage
851 * what: The usage function for the genshellopt generated program
853 * arg: + tOptions * + opts + program options descriptor +
854 * arg: + int + exit_cd + usage text type to produce +
857 * This function is used to create the usage strings for the option
858 * processing shell script code. Two child processes are spawned
859 * each emitting the usage text in either the short (error exit)
860 * style or the long style. The generated program will capture this
861 * and create shell script variables containing the two types of text.
864 genshelloptUsage(tOptions * opts, int exit_cd)
866 #if ! defined(HAVE_WORKING_FORK)
867 optionUsage(opts, exit_cd);
870 * IF not EXIT_SUCCESS,
871 * THEN emit the short form of usage.
873 if (exit_cd != EXIT_SUCCESS)
874 optionUsage(opts, exit_cd);
877 if (ferror(stdout) || ferror(stderr))
878 option_exits(EXIT_FAILURE);
880 option_usage_fp = stdout;
883 * First, print our usage
887 optionUsage(opts, EXIT_FAILURE);
891 pagerState = PAGER_STATE_CHILD;
892 optionUsage(opts, EXIT_SUCCESS);
904 * Generate the pzProgName, since optionProcess() normally
905 * gets it from the command line
909 char ** pp = VOIDP(&(optionParseShellOptions->pzProgName));
910 AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
913 *pz = (char)LOWER(*pz);
919 * Separate the makeshell usage from the client usage
921 fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
922 fflush(option_usage_fp);
925 * Now, print the client usage.
929 pagerState = PAGER_STATE_CHILD;
932 optionUsage(optionParseShellOptions, EXIT_FAILURE);
943 fserr_exit(opts->pzProgName, zwriting, zstdout_name);
945 option_exits(EXIT_SUCCESS);
953 * c-file-style: "stroustrup"
954 * indent-tabs-mode: nil
956 * end of autoopts/makeshell.c */