diff options
Diffstat (limited to 'src/libopts/makeshell.c')
-rw-r--r-- | src/libopts/makeshell.c | 707 |
1 files changed, 406 insertions, 301 deletions
diff --git a/src/libopts/makeshell.c b/src/libopts/makeshell.c index 79edb7022b..f22e9f0b84 100644 --- a/src/libopts/makeshell.c +++ b/src/libopts/makeshell.c @@ -2,14 +2,16 @@ /** * \file makeshell.c * - * Time-stamp: "2012-04-07 09:03:16 bkorb" - * * This module will interpret the options set in the tOptions * structure and create a Bourne shell script capable of parsing them. * + * @addtogroup autoopts + * @{ + */ +/* * This file is part of AutoOpts, a companion to AutoGen. * AutoOpts is free software. - * AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved + * AutoOpts is Copyright (C) 1992-2013 by Bruce Korb - all rights reserved * * AutoOpts is available under any one of two licenses. The license * in use must be one of these two and the choice is under the control @@ -21,11 +23,11 @@ * The Modified Berkeley Software Distribution License * See the file "COPYING.mbsd" * - * These files have the following md5sums: + * These files have the following sha256 sums: * - * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 - * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 - * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd + * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 + * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 + * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd */ tOptions * optionParseShellOptions = NULL; @@ -33,39 +35,60 @@ tOptions * optionParseShellOptions = NULL; static char const * shell_prog = NULL; static char * script_leader = NULL; static char * script_trailer = NULL; +static char * script_text = NULL; /* = = = START-STATIC-FORWARD = = = */ static void emit_var_text(char const * prog, char const * var, int fdin); static void -text_to_var(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD); +text_to_var(tOptions * opts, teTextTo which, tOptDesc * od); static void -emit_usage(tOptions * pOpts); +emit_usage(tOptions * opts); static void -emit_setup(tOptions * pOpts); +emit_wrapup(tOptions * opts); static void -emit_action(tOptions * pOpts, tOptDesc* pOptDesc); +emit_setup(tOptions * opts); static void -emit_inaction(tOptions * pOpts, tOptDesc* pOptDesc); +emit_action(tOptions * opts, tOptDesc * od); static void -emit_flag(tOptions * pOpts); +emit_inaction(tOptions * opts, tOptDesc * od); static void -emit_match_expr(char const * pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts); +emit_flag(tOptions * opts); static void -emitLong(tOptions * pOpts); +emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts); static void -open_out(char const * pzFile); +emit_long(tOptions * opts); + +static char * +load_old_output(char const * fname, char const * pname); + +static void +open_out(char const * fname, char const * pname); /* = = = END-STATIC-FORWARD = = = */ +LOCAL void +fserr_warn(char const * prog, char const * op, char const * fname) +{ + fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno), + op, fname); +} + +LOCAL void +fserr_exit(char const * prog, char const * op, char const * fname) +{ + fserr_warn(prog, op, fname); + exit(EXIT_FAILURE); +} + /*=export_func optionParseShell * private: * @@ -76,7 +99,7 @@ open_out(char const * pzFile); * Emit a shell script that will parse the command line options. =*/ void -optionParseShell(tOptions * pOpts) +optionParseShell(tOptions * opts) { /* * Check for our SHELL option now. @@ -98,22 +121,22 @@ optionParseShell(tOptions * pOpts) * Check for a specified output file */ if (HAVE_GENSHELL_OPT(SCRIPT)) - open_out(GENSHELL_OPT_ARG(SCRIPT)); - - emit_usage(pOpts); - emit_setup(pOpts); + open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName); + + emit_usage(opts); + emit_setup(opts); /* * There are four modes of option processing. */ - switch (pOpts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) { + switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) { case OPTPROC_LONGOPT: fputs(LOOP_STR, stdout); fputs(LONG_OPT_MARK, stdout); fputs(INIT_LOPT_STR, stdout); - emitLong(pOpts); - printf(LOPT_ARG_FMT, pOpts->pzPROGNAME); + emit_long(opts); + printf(LOPT_ARG_FMT, opts->pzPROGNAME); fputs(END_OPT_SEL_STR, stdout); fputs(NOT_FOUND_STR, stdout); @@ -122,8 +145,8 @@ optionParseShell(tOptions * pOpts) case 0: fputs(ONLY_OPTS_LOOP, stdout); fputs(INIT_LOPT_STR, stdout); - emitLong(pOpts); - printf(LOPT_ARG_FMT, pOpts->pzPROGNAME); + emit_long(opts); + printf(LOPT_ARG_FMT, opts->pzPROGNAME); break; case OPTPROC_SHORTOPT: @@ -131,8 +154,8 @@ optionParseShell(tOptions * pOpts) fputs(FLAG_OPT_MARK, stdout); fputs(INIT_OPT_STR, stdout); - emit_flag(pOpts); - printf(OPT_ARG_FMT, pOpts->pzPROGNAME); + emit_flag(opts); + printf(OPT_ARG_FMT, opts->pzPROGNAME); fputs(END_OPT_SEL_STR, stdout); fputs(NOT_FOUND_STR, stdout); @@ -143,38 +166,53 @@ optionParseShell(tOptions * pOpts) fputs(LONG_OPT_MARK, stdout); fputs(INIT_LOPT_STR, stdout); - emitLong(pOpts); - printf(LOPT_ARG_FMT, pOpts->pzPROGNAME); + emit_long(opts); + printf(LOPT_ARG_FMT, opts->pzPROGNAME); fputs(END_OPT_SEL_STR, stdout); fputs(FLAG_OPT_MARK, stdout); fputs(INIT_OPT_STR, stdout); - emit_flag(pOpts); - printf(OPT_ARG_FMT, pOpts->pzPROGNAME); + emit_flag(opts); + printf(OPT_ARG_FMT, opts->pzPROGNAME); fputs(END_OPT_SEL_STR, stdout); fputs(NOT_FOUND_STR, stdout); break; } - printf(zLoopEnd, pOpts->pzPROGNAME, END_MARK); + emit_wrapup(opts); if ((script_trailer != NULL) && (*script_trailer != NUL)) fputs(script_trailer, stdout); else if (ENABLED_GENSHELL_OPT(SHELL)) - printf(SHOW_PROG_ENV, pOpts->pzPROGNAME); + printf(SHOW_PROG_ENV, opts->pzPROGNAME); #ifdef HAVE_FCHMOD fchmod(STDOUT_FILENO, 0755); #endif fclose(stdout); - if (ferror(stdout)) { - fputs(zOutputFail, stderr); - exit(EXIT_FAILURE); - } + if (ferror(stdout)) + fserr_exit(opts->pzProgName, zwriting, zstdout_name); + + AGFREE(script_text); + script_leader = NULL; + script_trailer = NULL; + script_text = NULL; } #ifdef HAVE_WORKING_FORK +/** + * Print the value of "var" to a file descriptor. + * The "fdin" is the read end of a pipe to a forked process that + * is writing usage text to it. We read that text in and re-emit + * to standard out, formatting it so that it is assigned to a + * shell variable. + * + * @param[in] prog The capitalized, c-variable-formatted program name + * @param[in] var a similarly formatted type name + * (LONGUSAGE, USAGE or VERSION) + * @param[in] fdin the input end of a pipe + */ static void emit_var_text(char const * prog, char const * var, int fdin) { @@ -198,11 +236,11 @@ emit_var_text(char const * prog, char const * var, int fdin) fputc(NL, stdout); nlct--; } - fputs(apostrophy, stdout); + fputs(apostrophe, stdout); break; case EOF: - goto endCharLoop; + goto done; default: while (nlct > 0) { @@ -212,97 +250,102 @@ emit_var_text(char const * prog, char const * var, int fdin) fputc(ch, stdout); break; } - } endCharLoop:; + } done:; fclose(fp); -skip_text: + skip_text: fputs(END_SET_TEXT, stdout); } - #endif -/* +/** * The purpose of this function is to assign "long usage", short usage * and version information to a shell variable. Rather than wind our * way through all the logic necessary to emit the text directly, we * fork(), have our child process emit the text the normal way and * capture the output in the parent process. + * + * @param[in] opts the program options + * @param[in] which what to print: long usage, usage or version + * @param[in] od for TT_VERSION, it is the version option */ static void -text_to_var(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD) +text_to_var(tOptions * opts, teTextTo which, tOptDesc * od) { # define _TT_(n) static char const z ## n [] = #n; TEXTTO_TABLE # undef _TT_ # define _TT_(n) z ## n , - static char const * apzTTNames[] = { TEXTTO_TABLE }; + static char const * ttnames[] = { TEXTTO_TABLE }; # undef _TT_ #if ! defined(HAVE_WORKING_FORK) - printf(SET_NO_TEXT_FMT, pOpts->pzPROGNAME, apzTTNames[ whichVar]); + printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]); #else - int pipeFd[2]; + int fdpair[2]; fflush(stdout); fflush(stderr); - if (pipe(pipeFd) != 0) { - fprintf(stderr, zBadPipe, errno, strerror(errno)); - exit(EXIT_FAILURE); - } + if (pipe(fdpair) != 0) + fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe); switch (fork()) { case -1: - fprintf(stderr, zForkFail, errno, strerror(errno), pOpts->pzProgName); - exit(EXIT_FAILURE); - break; + fserr_exit(opts->pzProgName, "fork", opts->pzProgName); + /* NOTREACHED */ case 0: /* * Send both stderr and stdout to the pipe. No matter which * descriptor is used, we capture the output on the read end. */ - dup2(pipeFd[1], STDERR_FILENO); - dup2(pipeFd[1], STDOUT_FILENO); - close(pipeFd[0]); + dup2(fdpair[1], STDERR_FILENO); + dup2(fdpair[1], STDOUT_FILENO); + close(fdpair[0]); - switch (whichVar) { + switch (which) { case TT_LONGUSAGE: - (*(pOpts->pUsageProc))(pOpts, EXIT_SUCCESS); + (*(opts->pUsageProc))(opts, EXIT_SUCCESS); /* NOTREACHED */ case TT_USAGE: - (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE); + (*(opts->pUsageProc))(opts, EXIT_FAILURE); /* NOTREACHED */ case TT_VERSION: - if (pOD->fOptState & OPTST_ALLOC_ARG) { - AGFREE(pOD->optArg.argString); - pOD->fOptState &= ~OPTST_ALLOC_ARG; + if (od->fOptState & OPTST_ALLOC_ARG) { + AGFREE(od->optArg.argString); + od->fOptState &= ~OPTST_ALLOC_ARG; } - pOD->optArg.argString = "c"; - optionPrintVersion(pOpts, pOD); + od->optArg.argString = "c"; + optionPrintVersion(opts, od); /* NOTREACHED */ default: exit(EXIT_FAILURE); + /* NOTREACHED */ } + /* NOTREACHED */ default: - close(pipeFd[1]); + close(fdpair[1]); } - emit_var_text(pOpts->pzPROGNAME, apzTTNames[whichVar], pipeFd[0]); + emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]); #endif } - +/** + * capture usage text in shell variables. + * + */ static void -emit_usage(tOptions * pOpts) +emit_usage(tOptions * opts) { - char zTimeBuf[AO_NAME_SIZE]; + char tm_nm_buf[AO_NAME_SIZE]; /* * First, switch stdout to the output file name. @@ -319,7 +362,7 @@ emit_usage(tOptions * pOpts) { time_t c_tim = time(NULL); struct tm * ptm = localtime(&c_tim); - strftime(zTimeBuf, AO_NAME_SIZE, TIME_FMT, ptm ); + strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm ); } if (HAVE_GENSHELL_OPT(SCRIPT)) @@ -329,41 +372,42 @@ emit_usage(tOptions * pOpts) if ((script_leader == NULL) && (shell_prog != NULL)) printf(SHELL_MAGIC, shell_prog); - printf(PREAMBLE_FMT, START_MARK, out_nm, zTimeBuf); + printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf); } - printf(END_PRE_FMT, pOpts->pzPROGNAME); + printf(END_PRE_FMT, opts->pzPROGNAME); /* * Get a copy of the original program name in lower case and * fill in an approximation of the program name from it. */ { - char * pzPN = zTimeBuf; - char const * pz = pOpts->pzPROGNAME; + char * pzPN = tm_nm_buf; + char const * pz = opts->pzPROGNAME; char ** pp; + /* Copy the program name into the time/name buffer */ for (;;) { if ((*pzPN++ = (char)tolower(*pz++)) == NUL) break; } - pp = (char **)(void *)&(pOpts->pzProgPath); - *pp = zTimeBuf; - pp = (char **)(void *)&(pOpts->pzProgName); - *pp = zTimeBuf; + pp = (char **)(void *)&(opts->pzProgPath); + *pp = tm_nm_buf; + pp = (char **)(void *)&(opts->pzProgName); + *pp = tm_nm_buf; } - text_to_var(pOpts, TT_LONGUSAGE, NULL); - text_to_var(pOpts, TT_USAGE, NULL); + text_to_var(opts, TT_LONGUSAGE, NULL); + text_to_var(opts, TT_USAGE, NULL); { - tOptDesc* pOptDesc = pOpts->pOptDesc; - int optionCt = pOpts->optCt; + tOptDesc* pOptDesc = opts->pOptDesc; + int optionCt = opts->optCt; for (;;) { if (pOptDesc->pOptProc == optionPrintVersion) { - text_to_var(pOpts, TT_VERSION, pOptDesc); + text_to_var(opts, TT_VERSION, pOptDesc); break; } @@ -374,249 +418,289 @@ emit_usage(tOptions * pOpts) } } +static void +emit_wrapup(tOptions * opts) +{ + tOptDesc * od = opts->pOptDesc; + int opt_ct = opts->presetOptCt; + char const * fmt; + + printf(FINISH_LOOP, opts->pzPROGNAME); + for (;opt_ct > 0; od++, --opt_ct) { + /* + * Options that are either usage documentation or are compiled out + * are not to be processed. + */ + if (SKIP_OPT(od) || (od->pz_NAME == NULL)) + continue; + + /* + * do not presence check if there is no minimum/must-set + */ + if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0)) + continue; + + if (od->optMaxCt > 1) + fmt = CHK_MIN_COUNT; + else fmt = CHK_ONE_REQUIRED; + + { + int min = (od->optMinCt == 0) ? 1 : od->optMinCt; + printf(fmt, opts->pzPROGNAME, od->pz_NAME, min); + } + } + fputs(END_MARK, stdout); +} static void -emit_setup(tOptions * pOpts) +emit_setup(tOptions * opts) { - tOptDesc * pOptDesc = pOpts->pOptDesc; - int optionCt = pOpts->presetOptCt; - char const * pzFmt; - char const * pzDefault; + tOptDesc * od = opts->pOptDesc; + int opt_ct = opts->presetOptCt; + char const * fmt; + char const * def_val; - for (;optionCt > 0; pOptDesc++, --optionCt) { - char zVal[32]; + for (;opt_ct > 0; od++, --opt_ct) { + char int_val_buf[32]; /* * Options that are either usage documentation or are compiled out * are not to be processed. */ - if (SKIP_OPT(pOptDesc) || (pOptDesc->pz_NAME == NULL)) + if (SKIP_OPT(od) || (od->pz_NAME == NULL)) continue; - if (pOptDesc->optMaxCt > 1) - pzFmt = MULTI_DEF_FMT; - else pzFmt = SGL_DEF_FMT; + if (od->optMaxCt > 1) + fmt = MULTI_DEF_FMT; + else fmt = SGL_DEF_FMT; /* * IF this is an enumeration/bitmask option, then convert the value * to a string before printing the default value. */ - switch (OPTST_GET_ARGTYPE(pOptDesc->fOptState)) { + switch (OPTST_GET_ARGTYPE(od->fOptState)) { case OPARG_TYPE_ENUMERATION: - (*(pOptDesc->pOptProc))(OPTPROC_EMIT_SHELL, pOptDesc ); - pzDefault = pOptDesc->optArg.argString; + (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od ); + def_val = od->optArg.argString; break; /* * Numeric and membership bit options are just printed as a number. */ case OPARG_TYPE_NUMERIC: - snprintf(zVal, sizeof(zVal), "%d", - (int)pOptDesc->optArg.argInt); - pzDefault = zVal; + snprintf(int_val_buf, sizeof(int_val_buf), "%d", + (int)od->optArg.argInt); + def_val = int_val_buf; break; case OPARG_TYPE_MEMBERSHIP: - snprintf(zVal, sizeof(zVal), "%lu", - (unsigned long)pOptDesc->optArg.argIntptr); - pzDefault = zVal; + snprintf(int_val_buf, sizeof(int_val_buf), "%lu", + (unsigned long)od->optArg.argIntptr); + def_val = int_val_buf; break; case OPARG_TYPE_BOOLEAN: - pzDefault = (pOptDesc->optArg.argBool) ? TRUE_STR : FALSE_STR; + def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR; break; default: - if (pOptDesc->optArg.argString == NULL) { - if (pzFmt == SGL_DEF_FMT) - pzFmt = SGL_NO_DEF_FMT; - pzDefault = NULL; + if (od->optArg.argString == NULL) { + if (fmt == SGL_DEF_FMT) + fmt = SGL_NO_DEF_FMT; + def_val = NULL; } else - pzDefault = pOptDesc->optArg.argString; + def_val = od->optArg.argString; } - printf(pzFmt, pOpts->pzPROGNAME, pOptDesc->pz_NAME, pzDefault); + printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val); } } static void -emit_action(tOptions * pOpts, tOptDesc* pOptDesc) +emit_action(tOptions * opts, tOptDesc * od) { - if (pOptDesc->pOptProc == optionPrintVersion) - printf(zTextExit, pOpts->pzPROGNAME, VER_STR); + if (od->pOptProc == optionPrintVersion) + printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR); - else if (pOptDesc->pOptProc == optionPagedUsage) - printf(zPagedUsageExit, pOpts->pzPROGNAME); + else if (od->pOptProc == optionPagedUsage) + printf(PAGE_USAGE_TEXT, opts->pzPROGNAME); - else if (pOptDesc->pOptProc == optionLoadOpt) { - printf(zCmdFmt, NO_LOAD_WARN); - printf(zCmdFmt, YES_NEED_OPT_ARG); + else if (od->pOptProc == optionLoadOpt) { + printf(LVL3_CMD, NO_LOAD_WARN); + printf(LVL3_CMD, YES_NEED_OPT_ARG); - } else if (pOptDesc->pz_NAME == NULL) { + } else if (od->pz_NAME == NULL) { - if (pOptDesc->pOptProc == NULL) { - printf(zCmdFmt, NO_SAVE_OPTS); - printf(zCmdFmt, OK_NEED_OPT_ARG); + if (od->pOptProc == NULL) { + printf(LVL3_CMD, NO_SAVE_OPTS); + printf(LVL3_CMD, OK_NEED_OPT_ARG); } else - printf(zTextExit, pOpts->pzPROGNAME, LONG_USE_STR); + printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR); } else { - if (pOptDesc->optMaxCt == 1) - printf(SGL_ARG_FMT, pOpts->pzPROGNAME, pOptDesc->pz_NAME); + if (od->optMaxCt == 1) + printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); else { - if ((unsigned)pOptDesc->optMaxCt < NOLIMIT) - printf(zCountTest, pOpts->pzPROGNAME, - pOptDesc->pz_NAME, pOptDesc->optMaxCt); + if ((unsigned)od->optMaxCt < NOLIMIT) + printf(CHK_MAX_COUNT, opts->pzPROGNAME, + od->pz_NAME, od->optMaxCt); - printf(MULTI_ARG_FMT, pOpts->pzPROGNAME, pOptDesc->pz_NAME); + printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); } /* * Fix up the args. */ - if (OPTST_GET_ARGTYPE(pOptDesc->fOptState) == OPARG_TYPE_NONE) { - printf(zCantArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME); + if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) { + printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); + printf(LVL3_CMD, NO_ARG_NEEDED); - } else if (pOptDesc->fOptState & OPTST_ARG_OPTIONAL) { - printf(zMayArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME); + } else if (od->fOptState & OPTST_ARG_OPTIONAL) { + printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); + printf(LVL3_CMD, OK_NEED_OPT_ARG); } else { - fputs(zMustArg, stdout); + printf(LVL3_CMD, YES_NEED_OPT_ARG); } } fputs(zOptionEndSelect, stdout); } - static void -emit_inaction(tOptions * pOpts, tOptDesc* pOptDesc) +emit_inaction(tOptions * opts, tOptDesc * od) { - if (pOptDesc->pOptProc == optionLoadOpt) { - printf(zCmdFmt, NO_SUPPRESS_LOAD); + if (od->pOptProc == optionLoadOpt) { + printf(LVL3_CMD, NO_SUPPRESS_LOAD); - } else if (pOptDesc->optMaxCt == 1) - printf(NO_SGL_ARG_FMT, pOpts->pzPROGNAME, - pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx); + } else if (od->optMaxCt == 1) + printf(NO_SGL_ARG_FMT, opts->pzPROGNAME, + od->pz_NAME, od->pz_DisablePfx); else - printf(NO_MULTI_ARG_FMT, pOpts->pzPROGNAME, - pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx); + printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME, + od->pz_NAME, od->pz_DisablePfx); - printf(zCmdFmt, NO_ARG_NEEDED); + printf(LVL3_CMD, NO_ARG_NEEDED); fputs(zOptionEndSelect, stdout); } - +/** + * recognize flag options. These go at the end. + * At the end, emit code to handle options we don't recognize. + * + * @param[in] opts the program options + */ static void -emit_flag(tOptions * pOpts) +emit_flag(tOptions * opts) { - tOptDesc* pOptDesc = pOpts->pOptDesc; - int optionCt = pOpts->optCt; + tOptDesc* od = opts->pOptDesc; + int opt_ct = opts->optCt; fputs(zOptionCase, stdout); - for (;optionCt > 0; pOptDesc++, --optionCt) { + for (;opt_ct > 0; od++, --opt_ct) { - if (SKIP_OPT(pOptDesc)) + if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue)) continue; - if (IS_GRAPHIC_CHAR(pOptDesc->optValue)) { - printf(zOptionFlag, pOptDesc->optValue); - emit_action(pOpts, pOptDesc); - } + printf(zOptionFlag, od->optValue); + emit_action(opts, od); } - printf(UNK_OPT_FMT, FLAG_STR, pOpts->pzPROGNAME); + printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME); } - -/* - * Emit the match text for a long option +/** + * Emit the match text for a long option. The passed in \a name may be + * either the enablement name or the disablement name. + * + * @param[in] name The current name to check. + * @param[in] cod current option descriptor + * @param[in] opts the program options */ static void -emit_match_expr(char const * pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts) +emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts) { - tOptDesc* pOD = pOpts->pOptDesc; - int oCt = pOpts->optCt; - int min = 1; - char zName[ 256 ]; - char* pz = zName; - - for (;;) { - int matchCt = 0; - - /* - * Omit the current option, Documentation opts and compiled out opts. - */ - if ((pOD == pCurOpt) || SKIP_OPT(pOD)){ - if (--oCt <= 0) - break; - pOD++; - continue; - } + char name_bf[32]; + unsigned int min_match_ct = 2; + unsigned int max_match_ct = strlen(name) - 1; - /* - * Check each character of the name case insensitively. - * They must not be the same. They cannot be, because it would - * not compile correctly if they were. - */ - while ( toupper(pOD->pz_Name[matchCt]) - == toupper(pzMatchName[matchCt])) - matchCt++; - - if (matchCt > min) - min = matchCt; - - /* - * Check the disablement name, too. - */ - if (pOD->pz_DisableName != NULL) { - matchCt = 0; - while ( toupper(pOD->pz_DisableName[matchCt]) - == toupper(pzMatchName[matchCt])) - matchCt++; - if (matchCt > min) - min = matchCt; + if (max_match_ct >= sizeof(name_bf) - 1) + goto leave; + + { + tOptDesc * od = opts->pOptDesc; + int ct = opts->optCt; + + for (; ct-- > 0; od++) { + unsigned int match_ct = 0; + + /* + * Omit the current option, Doc opts and compiled out opts. + */ + if ((od == cod) || SKIP_OPT(od)) + continue; + + /* + * Check each character of the name case insensitively. + * They must not be the same. They cannot be, because it would + * not compile correctly if they were. + */ + while (toupper(od->pz_Name[match_ct]) == toupper(name[match_ct])) + match_ct++; + + if (match_ct > min_match_ct) + min_match_ct = match_ct; + + /* + * Check the disablement name, too. + */ + if (od->pz_DisableName == NULL) + continue; + + match_ct = 0; + while ( toupper(od->pz_DisableName[match_ct]) + == toupper(name[match_ct])) + match_ct++; + if (match_ct > min_match_ct) + min_match_ct = match_ct; } - if (--oCt <= 0) - break; - pOD++; } /* - * IF the 'min' is all or one short of the name length, - * THEN the entire string must be matched. + * Don't bother emitting partial matches if there is only one possible + * partial match. */ - if ( (pzMatchName[min ] == NUL) - || (pzMatchName[min+1] == NUL) ) - printf(zOptionFullName, pzMatchName); + if (min_match_ct < max_match_ct) { + char * pz = name_bf + min_match_ct; + int nm_ix = min_match_ct; - else { - int matchCt = 0; - for (; matchCt <= min; matchCt++) - *pz++ = pzMatchName[matchCt]; + memcpy(name_bf, name, min_match_ct); for (;;) { *pz = NUL; - printf(zOptionPartName, zName); - *pz++ = pzMatchName[matchCt++]; - if (pzMatchName[matchCt] == NUL) { + printf(zOptionPartName, name_bf); + *pz++ = name[nm_ix++]; + if (name[nm_ix] == NUL) { *pz = NUL; - printf(zOptionFullName, zName); break; } } } -} +leave: + printf(zOptionFullName, name); +} -/* - * Emit GNU-standard long option handling code +/** + * Emit GNU-standard long option handling code. + * + * @param[in] opts the program options */ static void -emitLong(tOptions * pOpts) +emit_long(tOptions * opts) { - tOptDesc* pOD = pOpts->pOptDesc; - int ct = pOpts->optCt; + tOptDesc * od = opts->pOptDesc; + int ct = opts->optCt; fputs(zOptionCase, stdout); @@ -627,88 +711,113 @@ emitLong(tOptions * pOpts) /* * Documentation & compiled-out options */ - if (SKIP_OPT(pOD)) + if (SKIP_OPT(od)) continue; - emit_match_expr(pOD->pz_Name, pOD, pOpts); - emit_action(pOpts, pOD); + emit_match_expr(od->pz_Name, od, opts); + emit_action(opts, od); /* * Now, do the same thing for the disablement version of the option. */ - if (pOD->pz_DisableName != NULL) { - emit_match_expr(pOD->pz_DisableName, pOD, pOpts); - emit_inaction(pOpts, pOD); + if (od->pz_DisableName != NULL) { + emit_match_expr(od->pz_DisableName, od, opts); + emit_inaction(opts, od); } - } while (pOD++, --ct > 0); + } while (od++, --ct > 0); - printf(UNK_OPT_FMT, OPTION_STR, pOpts->pzPROGNAME); + printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME); } - -static void -open_out(char const * pzFile) +/** + * Load the previous shell script output file. We need to preserve any + * hand-edited additions outside of the START_MARK and END_MARKs. + * + * @param[in] fname the output file name + */ +static char * +load_old_output(char const * fname, char const * pname) { - FILE* fp; - char* pzData = NULL; + /* + * IF we cannot stat the file, + * THEN assume we are creating a new file. + * Skip the loading of the old data. + */ + FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG); struct stat stbf; + char * text; + char * scan; - do { - char* pzScan; - size_t sizeLeft; + if (fp == NULL) + return NULL; - /* - * IF we cannot stat the file, - * THEN assume we are creating a new file. - * Skip the loading of the old data. - */ - if (stat(pzFile, &stbf) != 0) + /* + * If we opened it, we should be able to stat it and it needs + * to be a regular file + */ + if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode))) + fserr_exit(pname, "fstat", fname); + + scan = text = AGALOC(stbf.st_size + 1, "f data"); + + /* + * Read in all the data as fast as our OS will let us. + */ + for (;;) { + size_t inct = fread((void*)scan, 1, (size_t)stbf.st_size, fp); + if (inct == 0) break; - /* - * The file must be a regular file - */ - if (! S_ISREG(stbf.st_mode)) { - fprintf(stderr, zNotFile, pzFile); - exit(EXIT_FAILURE); - } + stbf.st_size -= (ssize_t)inct; - pzData = AGALOC(stbf.st_size + 1, "f data"); - fp = fopen(pzFile, "r" FOPEN_BINARY_FLAG); + if (stbf.st_size == 0) + break; - sizeLeft = (unsigned)stbf.st_size; - pzScan = pzData; + scan += inct; + } - /* - * Read in all the data as fast as our OS will let us. - */ - for (;;) { - int inct = fread((void*)pzScan, (size_t)1, sizeLeft, fp); - if (inct == 0) - break; + *scan = NUL; + fclose(fp); - pzScan += inct; - sizeLeft -= inct; + return text; +} - if (sizeLeft == 0) - break; - } +/** + * Open the specified output file. If it already exists, load its + * contents and save the non-generated (hand edited) portions. + * If a "start mark" is found, everything before it is preserved leader. + * If not, the entire thing is a trailer. Assuming the start is found, + * then everything after the end marker is the trailer. If the end + * mark is not found, the file is actually corrupt, but we take the + * remainder to be the trailer. + * + * @param[in] fname the output file name + */ +static void +open_out(char const * fname, char const * pname) +{ - /* - * NUL-terminate the leader and look for the trailer - */ - *pzScan = NUL; - fclose(fp); - pzScan = strstr(pzData, START_MARK); - if (pzScan == NULL) { - script_trailer = pzData; + do { + char * txt = script_text = load_old_output(fname, pname); + char * scn; + + if (txt == NULL) + break; + + scn = strstr(txt, START_MARK); + if (scn == NULL) { + script_trailer = txt; break; } - *(pzScan++) = NUL; - pzScan = strstr(pzScan, END_MARK); - if (pzScan == NULL) { - script_trailer = pzData; + *(scn++) = NUL; + scn = strstr(scn, END_MARK); + if (scn == NULL) { + /* + * The file is corrupt. Set the trailer to be everything + * after the start mark. The user will need to fix it up. + */ + script_trailer = txt + strlen(txt) + START_MARK_LEN + 1; break; } @@ -716,23 +825,20 @@ open_out(char const * pzFile) * Check to see if the data contains our marker. * If it does, then we will skip over it */ - script_trailer = pzScan + END_MARK_LEN; - script_leader = pzData; + script_trailer = scn + END_MARK_LEN; + script_leader = txt; } while (false); - if (freopen(pzFile, "w" FOPEN_BINARY_FLAG, stdout) != stdout) { - fprintf(stderr, zFreopenFail, errno, strerror(errno)); - exit(EXIT_FAILURE); - } + if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout) + fserr_exit(pname, "freopen", fname); } - /*=export_func genshelloptUsage * private: * what: The usage function for the genshellopt generated program * - * arg: + tOptions* + pOpts + program options descriptor + - * arg: + int + exitCode + usage text type to produce + + * arg: + tOptions* + opts + program options descriptor + + * arg: + int + exit_cd + usage text type to produce + * * doc: * This function is used to create the usage strings for the option @@ -742,17 +848,17 @@ open_out(char const * pzFile) * and create shell script variables containing the two types of text. =*/ void -genshelloptUsage(tOptions * pOpts, int exitCode) +genshelloptUsage(tOptions * opts, int exit_cd) { #if ! defined(HAVE_WORKING_FORK) - optionUsage(pOpts, exitCode); + optionUsage(opts, exit_cd); #else /* * IF not EXIT_SUCCESS, * THEN emit the short form of usage. */ - if (exitCode != EXIT_SUCCESS) - optionUsage(pOpts, exitCode); + if (exit_cd != EXIT_SUCCESS) + optionUsage(opts, exit_cd); fflush(stderr); fflush(stdout); if (ferror(stdout) || ferror(stderr)) @@ -765,12 +871,12 @@ genshelloptUsage(tOptions * pOpts, int exitCode) */ switch (fork()) { case -1: - optionUsage(pOpts, EXIT_FAILURE); + optionUsage(opts, EXIT_FAILURE); /* NOTREACHED */ case 0: pagerState = PAGER_STATE_CHILD; - optionUsage(pOpts, EXIT_SUCCESS); + optionUsage(opts, EXIT_SUCCESS); /* NOTREACHED */ _exit(EXIT_FAILURE); @@ -791,7 +897,7 @@ genshelloptUsage(tOptions * pOpts, int exitCode) AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name"); *pp = pz; while (*pz != NUL) { - *pz = tolower(*pz); + *pz = (char)tolower(*pz); pz++; } } @@ -820,16 +926,15 @@ genshelloptUsage(tOptions * pOpts, int exitCode) } fflush(stdout); - if (ferror(stdout)) { - fputs(zOutputFail, stderr); - exit(EXIT_FAILURE); - } + if (ferror(stdout)) + fserr_exit(opts->pzProgName, zwriting, zstdout_name); exit(EXIT_SUCCESS); #endif } -/* +/** @} + * * Local Variables: * mode: C * c-file-style: "stroustrup" |