diff options
Diffstat (limited to 'contrib/ntp/libopts/makeshell.c')
-rw-r--r-- | contrib/ntp/libopts/makeshell.c | 1122 |
1 files changed, 1122 insertions, 0 deletions
diff --git a/contrib/ntp/libopts/makeshell.c b/contrib/ntp/libopts/makeshell.c new file mode 100644 index 0000000..8447d45 --- /dev/null +++ b/contrib/ntp/libopts/makeshell.c @@ -0,0 +1,1122 @@ + +/* + * $Id: makeshell.c,v 4.20 2007/02/04 17:44:12 bkorb Exp $ + * Time-stamp: "2007-01-27 06:05:45 bkorb" + * + * This module will interpret the options set in the tOptions + * structure and create a Bourne shell script capable of parsing them. + */ + +/* + * Automated Options copyright 1992-2007 Bruce Korb + * + * Automated Options is free software. + * You may redistribute it and/or modify it under the terms of the + * GNU General Public License, as published by the Free Software + * Foundation; either version 2, or (at your option) any later version. + * + * Automated Options is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Automated Options. See the file "COPYING". If not, + * write to: The Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * As a special exception, Bruce Korb gives permission for additional + * uses of the text contained in his release of AutoOpts. + * + * The exception is that, if you link the AutoOpts library with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the AutoOpts library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by Bruce Korb under + * the name AutoOpts. If you copy code from other sources under the + * General Public License into a copy of AutoOpts, as the General Public + * License permits, the exception does not apply to the code that you add + * in this way. To avoid misleading anyone as to the status of such + * modified files, you must delete this exception notice from them. + * + * If you write modifications of your own for AutoOpts, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + */ + +tOptions* pShellParseOptions = NULL; + +/* * * * * * * * * * * * * * * * * * * * * + * + * Setup Format Strings + */ +tSCC zStartMarker[] = +"# # # # # # # # # # -- do not modify this marker --\n#\n" +"# DO NOT EDIT THIS SECTION"; + +tSCC zPreamble[] = +"%s OF %s\n#\n" +"# From here to the next `-- do not modify this marker --',\n" +"# the text has been generated %s\n"; + +tSCC zEndPreamble[] = +"# From the %s option definitions\n#\n"; + +tSCC zMultiDef[] = "\n" +"if test -z \"${%1$s_%2$s}\"\n" +"then\n" +" %1$s_%2$s_CT=0\n" +"else\n" +" %1$s_%2$s_CT=1\n" +" %1$s_%2$s_1=\"${%1$s_%2$s}\"\n" +"fi\n" +"export %1$s_%2$s_CT"; + +tSCC zSingleDef[] = "\n" +"%1$s_%2$s=\"${%1$s_%2$s-'%3$s'}\"\n" +"%1$s_%2$s_set=false\n" +"export %1$s_%2$s\n"; + +tSCC zSingleNoDef[] = "\n" +"%1$s_%2$s=\"${%1$s_%2$s}\"\n" +"%1$s_%2$s_set=false\n" +"export %1$s_%2$s\n"; + +/* * * * * * * * * * * * * * * * * * * * * + * + * LOOP START + * + * The loop may run in either of two modes: + * all options are named options (loop only) + * regular, marked option processing. + */ +tSCC zLoopCase[] = "\n" +"OPT_PROCESS=true\n" +"OPT_ARG=\"$1\"\n\n" +"while ${OPT_PROCESS} && [ $# -gt 0 ]\ndo\n" +" OPT_ELEMENT=''\n" +" OPT_ARG_VAL=''\n\n" + /* + * 'OPT_ARG' may or may not match the current $1 + */ +" case \"${OPT_ARG}\" in\n" +" -- )\n" +" OPT_PROCESS=false\n" +" shift\n" +" ;;\n\n"; + +tSCC zLoopOnly[] = "\n" +"OPT_ARG=\"$1\"\n\n" +"while [ $# -gt 0 ]\ndo\n" +" OPT_ELEMENT=''\n" +" OPT_ARG_VAL=''\n\n" +" OPT_ARG=\"${1}\"\n"; + +/* * * * * * * * * * * * * * * * + * + * CASE SELECTORS + * + * If the loop runs as a regular option loop, + * then we must have selectors for each acceptable option + * type (long option, flag character and non-option) + */ +tSCC zLongSelection[] = +" --* )\n"; + +tSCC zFlagSelection[] = +" -* )\n"; + +tSCC zEndSelection[] = +" ;;\n\n"; + +tSCC zNoSelection[] = +" * )\n" +" OPT_PROCESS=false\n" +" ;;\n" +" esac\n\n"; + +/* * * * * * * * * * * * * * * * + * + * LOOP END + */ +tSCC zLoopEnd[] = +" if [ -n \"${OPT_ARG_VAL}\" ]\n" +" then\n" +" eval %1$s_${OPT_NAME}${OPT_ELEMENT}=\"'${OPT_ARG_VAL}'\"\n" +" export %1$s_${OPT_NAME}${OPT_ELEMENT}\n" +" fi\n" +"done\n\n" +"unset OPT_PROCESS || :\n" +"unset OPT_ELEMENT || :\n" +"unset OPT_ARG || :\n" +"unset OPT_ARG_NEEDED || :\n" +"unset OPT_NAME || :\n" +"unset OPT_CODE || :\n" +"unset OPT_ARG_VAL || :\n%2$s"; + +tSCC zTrailerMarker[] = "\n" +"# # # # # # # # # #\n#\n" +"# END OF AUTOMATED OPTION PROCESSING\n" +"#\n# # # # # # # # # # -- do not modify this marker --\n"; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * OPTION SELECTION + */ +tSCC zOptionCase[] = +" case \"${OPT_CODE}\" in\n"; + +tSCC zOptionPartName[] = +" '%s' | \\\n"; + +tSCC zOptionFullName[] = +" '%s' )\n"; + +tSCC zOptionFlag[] = +" '%c' )\n"; + +tSCC zOptionEndSelect[] = +" ;;\n\n"; + +tSCC zOptionUnknown[] = +" * )\n" +" echo Unknown %s: \"${OPT_CODE}\" >&2\n" +" echo \"$%s_USAGE_TEXT\"\n" +" exit 1\n" +" ;;\n" +" esac\n\n"; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * OPTION PROCESSING + * + * Formats for emitting the text for handling particular options + */ +tSCC zTextExit[] = +" echo \"$%s_%s_TEXT\"\n" +" exit 0\n"; + +tSCC zPagedUsageExit[] = +" echo \"$%s_LONGUSAGE_TEXT\" | ${PAGER-more}\n" +" exit 0\n"; + +tSCC zCmdFmt[] = +" %s\n"; + +tSCC zCountTest[] = +" if [ $%1$s_%2$s_CT -ge %3$d ] ; then\n" +" echo Error: more than %3$d %2$s options >&2\n" +" echo \"$%1$s_USAGE_TEXT\"\n" +" exit 1 ; fi\n"; + +tSCC zMultiArg[] = +" %1$s_%2$s_CT=`expr ${%1$s_%2$s_CT} + 1`\n" +" OPT_ELEMENT=\"_${%1$s_%2$s_CT}\"\n" +" OPT_NAME='%2$s'\n"; + +tSCC zSingleArg[] = +" if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n" +" echo Error: duplicate %2$s option >&2\n" +" echo \"$%1$s_USAGE_TEXT\"\n" +" exit 1 ; fi\n" +" %1$s_%2$s_set=true\n" +" OPT_NAME='%2$s'\n"; + +tSCC zNoMultiArg[] = +" %1$s_%2$s_CT=0\n" +" OPT_ELEMENT=''\n" +" %1$s_%2$s='%3$s'\n" +" export %1$s_%2$s\n" +" OPT_NAME='%2$s'\n"; + +tSCC zNoSingleArg[] = +" if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n" +" echo Error: duplicate %2$s option >&2\n" +" echo \"$%1$s_USAGE_TEXT\"\n" +" exit 1 ; fi\n" +" %1$s_%2$s_set=true\n" +" %1$s_%2$s='%3$s'\n" +" export %1$s_%2$s\n" +" OPT_NAME='%2$s'\n"; + +tSCC zMayArg[] = +" eval %1$s_%2$s${OPT_ELEMENT}=true\n" +" export %1$s_%2$s${OPT_ELEMENT}\n" +" OPT_ARG_NEEDED=OK\n"; + +tSCC zMustArg[] = +" OPT_ARG_NEEDED=YES\n"; + +tSCC zCantArg[] = +" eval %1$s_%2$s${OPT_ELEMENT}=true\n" +" export %1$s_%2$s${OPT_ELEMENT}\n" +" OPT_ARG_NEEDED=NO\n"; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * LONG OPTION PROCESSING + * + * Formats for emitting the text for handling long option types + */ +tSCC zLongOptInit[] = +" OPT_CODE=`echo \"X${OPT_ARG}\"|sed 's/^X-*//'`\n" +" shift\n" +" OPT_ARG=\"$1\"\n\n" +" case \"${OPT_CODE}\" in *=* )\n" +" OPT_ARG_VAL=`echo \"${OPT_CODE}\"|sed 's/^[^=]*=//'`\n" +" OPT_CODE=`echo \"${OPT_CODE}\"|sed 's/=.*$//'` ;; esac\n\n"; + +tSCC zLongOptArg[] = +" case \"${OPT_ARG_NEEDED}\" in\n" +" NO )\n" +" OPT_ARG_VAL=''\n" +" ;;\n\n" +" YES )\n" +" if [ -z \"${OPT_ARG_VAL}\" ]\n" +" then\n" +" if [ $# -eq 0 ]\n" +" then\n" +" echo No argument provided for ${OPT_NAME} option >&2\n" +" echo \"$%s_USAGE_TEXT\"\n" +" exit 1\n" +" fi\n\n" +" OPT_ARG_VAL=\"${OPT_ARG}\"\n" +" shift\n" +" OPT_ARG=\"$1\"\n" +" fi\n" +" ;;\n\n" +" OK )\n" +" if [ -z \"${OPT_ARG_VAL}\" ] && [ $# -gt 0 ]\n" +" then\n" +" case \"${OPT_ARG}\" in -* ) ;; * )\n" +" OPT_ARG_VAL=\"${OPT_ARG}\"\n" +" shift\n" +" OPT_ARG=\"$1\" ;; esac\n" +" fi\n" +" ;;\n" +" esac\n"; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * FLAG OPTION PROCESSING + * + * Formats for emitting the text for handling flag option types + */ +tSCC zFlagOptInit[] = +" OPT_CODE=`echo \"X${OPT_ARG}\" | sed 's/X-\\(.\\).*/\\1/'`\n" +" OPT_ARG=` echo \"X${OPT_ARG}\" | sed 's/X-.//'`\n\n"; + +tSCC zFlagOptArg[] = +" case \"${OPT_ARG_NEEDED}\" in\n" +" NO )\n" +" if [ -n \"${OPT_ARG}\" ]\n" +" then\n" +" OPT_ARG=-\"${OPT_ARG}\"\n" +" else\n" +" shift\n" +" OPT_ARG=\"$1\"\n" +" fi\n" +" ;;\n\n" +" YES )\n" +" if [ -n \"${OPT_ARG}\" ]\n" +" then\n" +" OPT_ARG_VAL=\"${OPT_ARG}\"\n\n" +" else\n" +" if [ $# -eq 0 ]\n" +" then\n" +" echo No argument provided for ${OPT_NAME} option >&2\n" +" echo \"$%s_USAGE_TEXT\"\n" +" exit 1\n" +" fi\n" +" shift\n" +" OPT_ARG_VAL=\"$1\"\n" +" fi\n\n" +" shift\n" +" OPT_ARG=\"$1\"\n" +" ;;\n\n" +" OK )\n" +" if [ -n \"${OPT_ARG}\" ]\n" +" then\n" +" OPT_ARG_VAL=\"${OPT_ARG}\"\n" +" shift\n" +" OPT_ARG=\"$1\"\n\n" +" else\n" +" shift\n" +" if [ $# -gt 0 ]\n" +" then\n" +" case \"$1\" in -* ) ;; * )\n" +" OPT_ARG_VAL=\"$1\"\n" +" shift ;; esac\n" +" OPT_ARG=\"$1\"\n" +" fi\n" +" fi\n" +" ;;\n" +" esac\n"; + +tSCC* pzShell = NULL; +static char* pzLeader = NULL; +static char* pzTrailer = NULL; + +/* = = = START-STATIC-FORWARD = = = */ +/* static forward declarations maintained by :mkfwd */ +static void +textToVariable( tOptions* pOpts, teTextTo whichVar, tOptDesc* pOD ); + +static void +emitUsage( tOptions* pOpts ); + +static void +emitSetup( tOptions* pOpts ); + +static void +printOptionAction( tOptions* pOpts, tOptDesc* pOptDesc ); + +static void +printOptionInaction( tOptions* pOpts, tOptDesc* pOptDesc ); + +static void +emitFlag( tOptions* pOpts ); + +static void +emitMatchExpr( tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts ); + +static void +emitLong( tOptions* pOpts ); + +static void +openOutput( char const* pzFile ); +/* = = = END-STATIC-FORWARD = = = */ + +/*=export_func optionParseShell + * private: + * + * what: Decipher a boolean value + * arg: + tOptions* + pOpts + program options descriptor + + * + * doc: + * Emit a shell script that will parse the command line options. +=*/ +void +optionParseShell( tOptions* pOpts ) +{ + /* + * Check for our SHELL option now. + * IF the output file contains the "#!" magic marker, + * it will override anything we do here. + */ + if (HAVE_OPT( SHELL )) + pzShell = OPT_ARG( SHELL ); + + else if (! ENABLED_OPT( SHELL )) + pzShell = NULL; + + else if ((pzShell = getenv( "SHELL" )), + pzShell == NULL) + + pzShell = "/bin/sh"; + + /* + * Check for a specified output file + */ + if (HAVE_OPT( SCRIPT )) + openOutput( OPT_ARG( SCRIPT )); + + emitUsage( pOpts ); + emitSetup( pOpts ); + + /* + * There are four modes of option processing. + */ + switch (pOpts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) { + case OPTPROC_LONGOPT: + fputs( zLoopCase, stdout ); + + fputs( zLongSelection, stdout ); + fputs( zLongOptInit, stdout ); + emitLong( pOpts ); + printf( zLongOptArg, pOpts->pzPROGNAME ); + fputs( zEndSelection, stdout ); + + fputs( zNoSelection, stdout ); + break; + + case 0: + fputs( zLoopOnly, stdout ); + fputs( zLongOptInit, stdout ); + emitLong( pOpts ); + printf( zLongOptArg, pOpts->pzPROGNAME ); + break; + + case OPTPROC_SHORTOPT: + fputs( zLoopCase, stdout ); + + fputs( zFlagSelection, stdout ); + fputs( zFlagOptInit, stdout ); + emitFlag( pOpts ); + printf( zFlagOptArg, pOpts->pzPROGNAME ); + fputs( zEndSelection, stdout ); + + fputs( zNoSelection, stdout ); + break; + + case OPTPROC_LONGOPT|OPTPROC_SHORTOPT: + fputs( zLoopCase, stdout ); + + fputs( zLongSelection, stdout ); + fputs( zLongOptInit, stdout ); + emitLong( pOpts ); + printf( zLongOptArg, pOpts->pzPROGNAME ); + fputs( zEndSelection, stdout ); + + fputs( zFlagSelection, stdout ); + fputs( zFlagOptInit, stdout ); + emitFlag( pOpts ); + printf( zFlagOptArg, pOpts->pzPROGNAME ); + fputs( zEndSelection, stdout ); + + fputs( zNoSelection, stdout ); + break; + } + + printf( zLoopEnd, pOpts->pzPROGNAME, zTrailerMarker ); + if ((pzTrailer != NULL) && (*pzTrailer != '\0')) + fputs( pzTrailer, stdout ); + else if (ENABLED_OPT( SHELL )) + printf( "\nenv | grep '^%s_'\n", pOpts->pzPROGNAME ); + + fflush( stdout ); + fchmod( STDOUT_FILENO, 0755 ); + fclose( stdout ); +} + + +static void +textToVariable( tOptions* pOpts, teTextTo whichVar, tOptDesc* pOD ) +{ +# define _TT_(n) tSCC z ## n [] = #n; + TEXTTO_TABLE +# undef _TT_ +# define _TT_(n) z ## n , + static char const* apzTTNames[] = { TEXTTO_TABLE }; +# undef _TT_ + +#if defined(__windows__) && !defined(__CYGWIN__) + printf( "%1$s_%2$s_TEXT='no %2$s text'\n", + pOpts->pzPROGNAME, apzTTNames[ whichVar ]); +#else + int nlHoldCt = 0; + int pipeFd[2]; + FILE* fp; + + printf( "%s_%s_TEXT='", pOpts->pzPROGNAME, apzTTNames[ whichVar ]); + fflush( stdout ); + + if (pipe( pipeFd ) != 0) { + fprintf( stderr, zBadPipe, errno, strerror( errno )); + exit( EXIT_FAILURE ); + } + + switch (fork()) { + case -1: + fprintf( stderr, zForkFail, errno, strerror(errno), pOpts->pzProgName); + exit( EXIT_FAILURE ); + break; + + case 0: + dup2( pipeFd[1], STDERR_FILENO ); + dup2( pipeFd[1], STDOUT_FILENO ); + close( pipeFd[0] ); + + switch (whichVar) { + case TT_LONGUSAGE: + (*(pOpts->pUsageProc))( pOpts, EXIT_SUCCESS ); + /* NOTREACHED */ + exit( EXIT_FAILURE ); + + case TT_USAGE: + (*(pOpts->pUsageProc))( pOpts, EXIT_FAILURE ); + /* NOTREACHED */ + exit( EXIT_FAILURE ); + + case TT_VERSION: + if (pOD->fOptState & OPTST_ALLOC_ARG) { + AGFREE(pOD->optArg.argString); + pOD->fOptState &= ~OPTST_ALLOC_ARG; + } + pOD->optArg.argString = "c"; + optionPrintVersion( pOpts, pOD ); + /* NOTREACHED */ + + default: + exit( EXIT_FAILURE ); + } + + default: + close( pipeFd[1] ); + fp = fdopen( pipeFd[0], "r" FOPEN_BINARY_FLAG ); + } + + for (;;) { + int ch = fgetc( fp ); + switch (ch) { + + case '\n': + nlHoldCt++; + break; + + case '\'': + while (nlHoldCt > 0) { + fputc( '\n', stdout ); + nlHoldCt--; + } + fputs( "'\\''", stdout ); + break; + + case EOF: + goto endCharLoop; + + default: + while (nlHoldCt > 0) { + fputc( '\n', stdout ); + nlHoldCt--; + } + fputc( ch, stdout ); + break; + } + } endCharLoop:; + + fputs( "'\n\n", stdout ); + close( pipeFd[0] ); +#endif +} + + +static void +emitUsage( tOptions* pOpts ) +{ + char zTimeBuf[ AO_NAME_SIZE ]; + + /* + * First, switch stdout to the output file name. + * Then, change the program name to the one defined + * by the definitions (rather than the current + * executable name). Down case the upper cased name. + */ + if (pzLeader != NULL) + fputs( pzLeader, stdout ); + + { + tSCC zStdout[] = "stdout"; + tCC* pzOutName; + + { + time_t curTime = time( NULL ); + struct tm* pTime = localtime( &curTime ); + strftime(zTimeBuf, AO_NAME_SIZE, "%A %B %e, %Y at %r %Z", pTime ); + } + + if (HAVE_OPT( SCRIPT )) + pzOutName = OPT_ARG( SCRIPT ); + else pzOutName = zStdout; + + if ((pzLeader == NULL) && (pzShell != NULL)) + printf( "#! %s\n", pzShell ); + + printf( zPreamble, zStartMarker, pzOutName, zTimeBuf ); + } + + /* + * Get a copy of the original program name in lower case + */ + { + char* pzPN = zTimeBuf; + tCC* pz = pOpts->pzPROGNAME; + for (;;) { + if ((*pzPN++ = tolower( *pz++ )) == '\0') + break; + } + } + + printf( zEndPreamble, pOpts->pzPROGNAME ); + + pOpts->pzProgPath = pOpts->pzProgName = zTimeBuf; + textToVariable( pOpts, TT_LONGUSAGE, NULL ); + textToVariable( pOpts, TT_USAGE, NULL ); + + { + tOptDesc* pOptDesc = pOpts->pOptDesc; + int optionCt = pOpts->optCt; + + for (;;) { + if (pOptDesc->pOptProc == optionPrintVersion) { + textToVariable( pOpts, TT_VERSION, pOptDesc ); + break; + } + + if (--optionCt <= 0) + break; + pOptDesc++; + } + } +} + + +static void +emitSetup( tOptions* pOpts ) +{ + tOptDesc* pOptDesc = pOpts->pOptDesc; + int optionCt = pOpts->presetOptCt; + char const* pzFmt; + char const* pzDefault; + + for (;optionCt > 0; pOptDesc++, --optionCt) { + char zVal[16]; + + /* + * Options that are either usage documentation or are compiled out + * are not to be processed. + */ + if (SKIP_OPT(pOptDesc) || (pOptDesc->pz_NAME == NULL)) + continue; + + if (pOptDesc->optMaxCt > 1) + pzFmt = zMultiDef; + else pzFmt = zSingleDef; + + /* + * 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)) { + case OPARG_TYPE_ENUMERATION: + (*(pOptDesc->pOptProc))( (tOptions*)2UL, pOptDesc ); + pzDefault = pOptDesc->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; + break; + + case OPARG_TYPE_MEMBERSHIP: + snprintf( zVal, sizeof( zVal ), "%lu", + (unsigned long)pOptDesc->optArg.argIntptr ); + pzDefault = zVal; + break; + + case OPARG_TYPE_BOOLEAN: + pzDefault = (pOptDesc->optArg.argBool) ? "true" : "false"; + break; + + default: + if (pOptDesc->optArg.argString == NULL) { + if (pzFmt == zSingleDef) + pzFmt = zSingleNoDef; + pzDefault = NULL; + } + else + pzDefault = pOptDesc->optArg.argString; + } + + printf( pzFmt, pOpts->pzPROGNAME, pOptDesc->pz_NAME, pzDefault ); + } +} + + +static void +printOptionAction( tOptions* pOpts, tOptDesc* pOptDesc ) +{ + if (pOptDesc->pOptProc == optionPrintVersion) + printf( zTextExit, pOpts->pzPROGNAME, "VERSION" ); + + else if (pOptDesc->pOptProc == optionPagedUsage) + printf( zPagedUsageExit, pOpts->pzPROGNAME ); + + else if (pOptDesc->pOptProc == optionLoadOpt) { + printf( zCmdFmt, "echo 'Warning: Cannot load options files' >&2" ); + printf( zCmdFmt, "OPT_ARG_NEEDED=YES" ); + + } else if (pOptDesc->pz_NAME == NULL) { + + if (pOptDesc->pOptProc == NULL) { + printf( zCmdFmt, "echo 'Warning: Cannot save options files' " + ">&2" ); + printf( zCmdFmt, "OPT_ARG_NEEDED=OK" ); + } else + printf( zTextExit, pOpts->pzPROGNAME, "LONGUSAGE" ); + + } else { + if (pOptDesc->optMaxCt == 1) + printf( zSingleArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); + else { + if ((unsigned)pOptDesc->optMaxCt < NOLIMIT) + printf( zCountTest, pOpts->pzPROGNAME, + pOptDesc->pz_NAME, pOptDesc->optMaxCt ); + + printf( zMultiArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); + } + + /* + * Fix up the args. + */ + if (OPTST_GET_ARGTYPE(pOptDesc->fOptState) == OPARG_TYPE_NONE) { + printf( zCantArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); + + } else if (pOptDesc->fOptState & OPTST_ARG_OPTIONAL) { + printf( zMayArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); + + } else { + fputs( zMustArg, stdout ); + } + } + fputs( zOptionEndSelect, stdout ); +} + + +static void +printOptionInaction( tOptions* pOpts, tOptDesc* pOptDesc ) +{ + if (pOptDesc->pOptProc == optionLoadOpt) { + printf( zCmdFmt, "echo 'Warning: Cannot suppress the loading of " + "options files' >&2" ); + + } else if (pOptDesc->optMaxCt == 1) + printf( zNoSingleArg, pOpts->pzPROGNAME, + pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx ); + else + printf( zNoMultiArg, pOpts->pzPROGNAME, + pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx ); + + printf( zCmdFmt, "OPT_ARG_NEEDED=NO" ); + fputs( zOptionEndSelect, stdout ); +} + + +static void +emitFlag( tOptions* pOpts ) +{ + tOptDesc* pOptDesc = pOpts->pOptDesc; + int optionCt = pOpts->optCt; + + fputs( zOptionCase, stdout ); + + for (;optionCt > 0; pOptDesc++, --optionCt) { + + if (SKIP_OPT(pOptDesc)) + continue; + + if (isprint( pOptDesc->optValue )) { + printf( zOptionFlag, pOptDesc->optValue ); + printOptionAction( pOpts, pOptDesc ); + } + } + printf( zOptionUnknown, "flag", pOpts->pzPROGNAME ); +} + + +/* + * Emit the match text for a long option + */ +static void +emitMatchExpr( tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts ) +{ + 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; + } + + /* + * 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 (--oCt <= 0) + break; + pOD++; + } + + /* + * IF the 'min' is all or one short of the name length, + * THEN the entire string must be matched. + */ + if ( (pzMatchName[min ] == NUL) + || (pzMatchName[min+1] == NUL) ) + printf( zOptionFullName, pzMatchName ); + + else { + int matchCt = 0; + for (; matchCt <= min; matchCt++) + *pz++ = pzMatchName[matchCt]; + + for (;;) { + *pz = NUL; + printf( zOptionPartName, zName ); + *pz++ = pzMatchName[matchCt++]; + if (pzMatchName[matchCt] == NUL) { + *pz = NUL; + printf( zOptionFullName, zName ); + break; + } + } + } +} + + +/* + * Emit GNU-standard long option handling code + */ +static void +emitLong( tOptions* pOpts ) +{ + tOptDesc* pOD = pOpts->pOptDesc; + int ct = pOpts->optCt; + + fputs( zOptionCase, stdout ); + + /* + * do each option, ... + */ + do { + /* + * Documentation & compiled-out options + */ + if (SKIP_OPT(pOD)) + continue; + + emitMatchExpr( pOD->pz_Name, pOD, pOpts ); + printOptionAction( pOpts, pOD ); + + /* + * Now, do the same thing for the disablement version of the option. + */ + if (pOD->pz_DisableName != NULL) { + emitMatchExpr( pOD->pz_DisableName, pOD, pOpts ); + printOptionInaction( pOpts, pOD ); + } + } while (pOD++, --ct > 0); + + printf( zOptionUnknown, "option", pOpts->pzPROGNAME ); +} + + +static void +openOutput( char const* pzFile ) +{ + FILE* fp; + char* pzData = NULL; + struct stat stbf; + + do { + char* pzScan; + size_t sizeLeft; + + /* + * 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) + break; + + /* + * The file must be a regular file + */ + if (! S_ISREG( stbf.st_mode )) { + fprintf( stderr, zNotFile, pzFile ); + exit( EXIT_FAILURE ); + } + + pzData = AGALOC(stbf.st_size + 1, "file data"); + fp = fopen( pzFile, "r" FOPEN_BINARY_FLAG ); + + sizeLeft = (unsigned)stbf.st_size; + pzScan = pzData; + + /* + * 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; + + pzScan += inct; + sizeLeft -= inct; + + if (sizeLeft == 0) + break; + } + + /* + * NUL-terminate the leader and look for the trailer + */ + *pzScan = '\0'; + fclose( fp ); + pzScan = strstr( pzData, zStartMarker ); + if (pzScan == NULL) { + pzTrailer = pzData; + break; + } + + *(pzScan++) = NUL; + pzScan = strstr( pzScan, zTrailerMarker ); + if (pzScan == NULL) { + pzTrailer = pzData; + break; + } + + /* + * Check to see if the data contains + * our marker. If it does, then we will skip over it + */ + pzTrailer = pzScan + sizeof( zTrailerMarker ) - 1; + pzLeader = pzData; + } while (AG_FALSE); + + freopen( pzFile, "w" FOPEN_BINARY_FLAG, stdout ); +} + + +/*=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 + + * + * doc: + * This function is used to create the usage strings for the option + * processing shell script code. Two child processes are spawned + * each emitting the usage text in either the short (error exit) + * style or the long style. The generated program will capture this + * and create shell script variables containing the two types of text. +=*/ +void +genshelloptUsage( tOptions* pOpts, int exitCode ) +{ +#if defined(__windows__) && !defined(__CYGWIN__) + optionUsage( pOpts, exitCode ); +#else + /* + * IF not EXIT_SUCCESS, + * THEN emit the short form of usage. + */ + if (exitCode != EXIT_SUCCESS) + optionUsage( pOpts, exitCode ); + fflush( stderr ); + fflush( stdout ); + + option_usage_fp = stdout; + + /* + * First, print our usage + */ + switch (fork()) { + case -1: + optionUsage( pOpts, EXIT_FAILURE ); + /*NOTREACHED*/ + _exit( EXIT_FAILURE ); + + case 0: + pagerState = PAGER_STATE_CHILD; + optionUsage( pOpts, EXIT_SUCCESS ); + /*NOTREACHED*/ + _exit( EXIT_FAILURE ); + + default: + { + int sts; + wait( &sts ); + } + } + + /* + * Generate the pzProgName, since optionProcess() normally + * gets it from the command line + */ + { + char* pz; + AGDUPSTR( pz, pShellParseOptions->pzPROGNAME, "program name" ); + pShellParseOptions->pzProgName = pz; + while (*pz != NUL) { + *pz = tolower( *pz ); + pz++; + } + } + + /* + * Separate the makeshell usage from the client usage + */ + fprintf( option_usage_fp, zGenshell, pShellParseOptions->pzProgName ); + fflush( option_usage_fp ); + + /* + * Now, print the client usage. + */ + switch (fork()) { + case 0: + pagerState = PAGER_STATE_CHILD; + /*FALLTHROUGH*/ + case -1: + optionUsage( pShellParseOptions, EXIT_FAILURE ); + + default: + { + int sts; + wait( &sts ); + } + } + + exit( EXIT_SUCCESS ); +#endif +} + +/* + * Local Variables: + * mode: C + * c-file-style: "stroustrup" + * indent-tabs-mode: nil + * End: + * end of autoopts/makeshell.c */ |