1125 lines
30 KiB
C
1125 lines
30 KiB
C
/* $NetBSD: makeshell.c,v 1.3 2007/06/24 16:55:13 kardel Exp $ */
|
|
|
|
|
|
/*
|
|
* 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( (int)*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( (int)pOD->pz_Name[matchCt] )
|
|
== toupper( (int)pzMatchName[matchCt] ))
|
|
matchCt++;
|
|
|
|
if (matchCt > min)
|
|
min = matchCt;
|
|
|
|
/*
|
|
* Check the disablement name, too.
|
|
*/
|
|
if (pOD->pz_DisableName != NULL) {
|
|
matchCt = 0;
|
|
while ( toupper( (int)pOD->pz_DisableName[matchCt] )
|
|
== toupper( (int)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( (int)*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 */
|