486 lines
12 KiB
C
486 lines
12 KiB
C
|
/*++
|
||
|
|
||
|
PARSE.C
|
||
|
|
||
|
Option parser
|
||
|
|
||
|
Split from options.c 6/9/1997 by DavidCHR
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "private.h"
|
||
|
#include <malloc.h>
|
||
|
|
||
|
/* ParseOneOption:
|
||
|
|
||
|
parses a single option from argv.
|
||
|
returns: number of arguments eaten, or zero on error */
|
||
|
|
||
|
BOOL
|
||
|
ParseOneOption( int argc, // number of arguments, total
|
||
|
PCHAR *argv, // vector of arguments
|
||
|
int argi, // which argument we're to parse
|
||
|
ULONG flags, // applicable flags
|
||
|
optionStruct *options, // option structure
|
||
|
int *argsused, // arguments we've eaten
|
||
|
PBOOL pbStop, // Stop Parsing
|
||
|
PSAVEQUEUE pQueue // memory save area
|
||
|
) {
|
||
|
|
||
|
PCHAR p; // argument pointer copy
|
||
|
int opti; // which option we're examining
|
||
|
BOOL ret = FALSE; // return value
|
||
|
int tmp = 0; // temporary inbound value
|
||
|
int used = 0;
|
||
|
|
||
|
ASSERT( argc > 0 ); // there must be arguments
|
||
|
ASSERT( argv != NULL ); // the vector must exist
|
||
|
ASSERT( argi < argc ); // argument number must be INSIDE the vector
|
||
|
ASSERT( options != NULL ); // passed option structure must be valid
|
||
|
ASSERT( argsused != NULL );
|
||
|
|
||
|
p = argv[argi];
|
||
|
|
||
|
if( ISSWITCH( p[0] ) ) {
|
||
|
p++; /* skip the switch character */
|
||
|
}
|
||
|
|
||
|
OPTIONS_DEBUG("Checking option %d: \"%hs\"...", argi, p);
|
||
|
|
||
|
for (opti = 0 ; !ARRAY_TERMINATED( options+opti ) ; opti++ ) {
|
||
|
|
||
|
OPTIONS_DEBUG("against [%d]%hs...", opti, options[opti].cmd);
|
||
|
|
||
|
if ( ParseCompare( options+opti, flags, p ) ) {
|
||
|
/* we have a match! */
|
||
|
|
||
|
ret = StoreOption( options, argv, argc, argi, opti,
|
||
|
flags, &tmp, TRUE, pbStop, pQueue );
|
||
|
|
||
|
OPTIONS_DEBUG( "StoreOption returned %d, args=%d, *pbStop = %d\n",
|
||
|
ret, tmp, *pbStop );
|
||
|
|
||
|
if ( !ret ) {
|
||
|
|
||
|
/* failed to store options for some reason. This is
|
||
|
critical. */
|
||
|
|
||
|
PrintUsage( stderr, flags, options, "" );
|
||
|
|
||
|
if ( flags & OPT_FLAG_TERMINATE ) {
|
||
|
exit( -1 );
|
||
|
} else {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
OPTIONS_DEBUG( "ParseOneOption now returning TRUE, argsused = %d.\n",
|
||
|
tmp );
|
||
|
|
||
|
*argsused = tmp;
|
||
|
return ret; /* successful StoreOptions parses our one option. */
|
||
|
|
||
|
} /* if ParseCompare... */
|
||
|
|
||
|
OPTIONS_DEBUG( "nope.\n" );
|
||
|
|
||
|
} /* options for-loop */
|
||
|
|
||
|
OPTIONS_DEBUG( "did not find the option. Checking for OPT_DEFAULTs.\n" );
|
||
|
|
||
|
for (opti = 0 ; !ARRAY_TERMINATED( options+opti ) ; opti++ ) {
|
||
|
|
||
|
if ( options[opti].flags & OPT_DEFAULT ) {
|
||
|
|
||
|
/* WASBUG 73922: should check to see if the option is also an
|
||
|
OPT_SUBOPTION, then parse the suboption for OPT_DEFAULTs.
|
||
|
However, as it stands, this will just fail for OPT_SUBOPTIONS,
|
||
|
because the first pointer will probably be nonnull.
|
||
|
|
||
|
The dev enlistment doesn't contain any OPT_SUBOPTIONS, so
|
||
|
this is not an issue. */
|
||
|
|
||
|
ASSERT( ( options[ opti ].flags & OPT_MUTEX_MASK ) != OPT_SUBOPTION );
|
||
|
|
||
|
if ( *( ((POPTU) &options[opti].data)->raw_data) == NULL ) {
|
||
|
|
||
|
OPTIONS_DEBUG("Storing %hs in unused OPT_DEFAULT %hs\n",
|
||
|
argv[argi],
|
||
|
options[opti].cmd );
|
||
|
|
||
|
ret = StoreOption( options, argv, argc, argi, opti,
|
||
|
flags, &tmp, FALSE, pbStop, pQueue );
|
||
|
OPTIONS_DEBUG("OPT_DEFAULT: StoreOptions returned %d\n", ret);
|
||
|
|
||
|
if ( !ret ) {
|
||
|
PrintUsage( stderr, flags, options, "" );
|
||
|
exit( -1 );
|
||
|
}
|
||
|
|
||
|
*argsused = tmp;
|
||
|
return ret;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*argsused = 0;
|
||
|
|
||
|
if ( ( flags & OPT_FLAG_SKIP_UNKNOWNS ) ||
|
||
|
( flags & OPT_FLAG_REASSEMBLE ) ||
|
||
|
( flags & OPT_FLAG_INTERNAL_JUMPOUT )) {
|
||
|
|
||
|
return TRUE; // skip this option
|
||
|
|
||
|
} else {
|
||
|
|
||
|
fprintf(stderr, "unknown option \"%hs\".\n", argv[argi]);
|
||
|
PrintUsage(stderr, flags, options, "");
|
||
|
|
||
|
if ( flags & OPT_FLAG_TERMINATE ) {
|
||
|
exit( -1 );
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
}
|
||
|
|
||
|
ASSERT_NOTREACHED( "should be no path to this code" );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ParseOptionsEx:
|
||
|
|
||
|
initializes the option structure, which is a sentinally-terminated
|
||
|
vector of optionStructs.
|
||
|
|
||
|
argc, argv: arguments to main() (see K&R)
|
||
|
pOptionStructure: vector of optionStructs, terminated with TERMINATE_ARRAY
|
||
|
optionFlags: optional flags to control api behavior
|
||
|
ppReturnedMemory: returned handle to a list of memory to be freed before
|
||
|
program exit. Use CleanupOptionDataEx to free it.
|
||
|
|
||
|
new_arg[c,v]: if nonnull, a new argc and argv are returned here.
|
||
|
if all the options were used up, argc = 0 and argv is
|
||
|
NULL. Note that it is safe to provide pointers to the
|
||
|
original argv/argc if so desired.
|
||
|
|
||
|
The function's behavior is complex:
|
||
|
|
||
|
the function will always return FALSE on any critical error (unable to
|
||
|
allocate memory, or invalid argument). On WINNT, Last Error will be
|
||
|
set to the appropriate error.
|
||
|
|
||
|
if new_argc AND new_argv are specified,
|
||
|
ParseOptionsEx will always return TRUE unless help was called, and
|
||
|
the two parameters will be updated to reflect new values.
|
||
|
|
||
|
otherwise:
|
||
|
ParseOptionsEx will return TRUE if it was able to recognize ALL args
|
||
|
on the command line given. It will return FALSE if any of the options
|
||
|
were unknown. This will probably be what most people want.
|
||
|
*/
|
||
|
|
||
|
BOOL
|
||
|
ParseOptionsEx( int argc,
|
||
|
char **argv,
|
||
|
optionStruct *options,
|
||
|
|
||
|
ULONG flags,
|
||
|
PVOID *ppReturnedMemory,
|
||
|
int *new_argc,
|
||
|
char ***new_argv ) {
|
||
|
|
||
|
BOOL bStopParsing = FALSE;
|
||
|
BOOL ret = FALSE;
|
||
|
LONG argi; // argument index
|
||
|
LONG tmp; // temporary return variable
|
||
|
PSAVEQUEUE pSaveQueue = NULL; // memory save area
|
||
|
PCHAR *pUnknowns = NULL; // will alloc with alloca
|
||
|
int cUnknowns = 0;
|
||
|
|
||
|
flags = flags & ~OPT_FLAG_INTERNAL_RESERVED; /* mask off flags that
|
||
|
the user shouldn't set. */
|
||
|
|
||
|
if ( new_argc && new_argv &&
|
||
|
!( flags & ( OPT_FLAG_SKIP_UNKNOWNS |
|
||
|
OPT_FLAG_REASSEMBLE |
|
||
|
OPT_FLAG_TERMINATE ) ) ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "\nSetting internal jumpout flag.\n" );
|
||
|
flags |= OPT_FLAG_INTERNAL_JUMPOUT;
|
||
|
}
|
||
|
|
||
|
OPTIONS_DEBUG( "ParseOptionsEx( argc = %d\n"
|
||
|
" argv = 0x%x\n"
|
||
|
" opts = 0x%x\n"
|
||
|
" flags = 0x%x\n"
|
||
|
" ppMem = 0x%x\n"
|
||
|
" pargc = 0x%x\n"
|
||
|
" pargv = 0x%x\n",
|
||
|
|
||
|
argc, argv, options, flags, ppReturnedMemory, new_argc,
|
||
|
new_argv );
|
||
|
|
||
|
ASSERT( ppReturnedMemory != NULL );
|
||
|
|
||
|
// first, we need to ensure we have a save area.
|
||
|
|
||
|
if ( flags & OPT_FLAG_MEMORYLIST_OK ) {
|
||
|
|
||
|
pSaveQueue = (PSAVEQUEUE) *ppReturnedMemory;
|
||
|
|
||
|
} else if ( !OptionAlloc( NULL, &pSaveQueue, sizeof( SAVEQUEUE ) ) ) {
|
||
|
fprintf( stderr,
|
||
|
"ParseOptionsEx: unable to allocate save area\n" );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ASSERT( pSaveQueue != NULL );
|
||
|
|
||
|
/* We must initialize pUnknowns if the user specified command-line
|
||
|
reassembly. Otherwise, it can stay NULL. */
|
||
|
|
||
|
if ( (flags & OPT_FLAG_REASSEMBLE) && ( argc > 0 ) ) {
|
||
|
|
||
|
pUnknowns = (PCHAR *) alloca( argc * sizeof( PCHAR ) );
|
||
|
|
||
|
ASSERT( pUnknowns != NULL ); /* yes, this assertion is invalid.
|
||
|
However, there is no clean solution if
|
||
|
we run out of stack space. Something
|
||
|
else will just fail even more
|
||
|
spectacularly. */
|
||
|
}
|
||
|
|
||
|
OPTIONS_DEBUG("options are at 0x%x\n", options);
|
||
|
|
||
|
#ifdef DEBUG_OPTIONS
|
||
|
|
||
|
if (DebugFlag) {
|
||
|
|
||
|
for (argi = 0; argi < argc ; argi++ ) {
|
||
|
OPTIONS_DEBUG("option %d is %hs\n", argi, argv[argi]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
for (argi = 0 ; argi < argc ; /* NOTHING */ ) {
|
||
|
|
||
|
int tmp;
|
||
|
|
||
|
if ( bStopParsing ) { // this gets set in the PREVIOUS iteration.
|
||
|
|
||
|
OPTIONS_DEBUG( "bStopParsing is TRUE. Terminating parse run.\n");
|
||
|
|
||
|
/* WASBUG 73924: now what do we do with the unused options?
|
||
|
They get leaked. This is okay, because the app terminates. */
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( ParseOneOption( argc, argv, argi, flags, options, &tmp,
|
||
|
&bStopParsing, pSaveQueue ) ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "ParseOneOption succeeded with %d options.\n", tmp );
|
||
|
|
||
|
if ( tmp > 0 ) {
|
||
|
|
||
|
// we were able to successfully parse one or more options.
|
||
|
|
||
|
argi += tmp;
|
||
|
|
||
|
OPTIONS_DEBUG( "advancing argi by %d to %d\n", tmp, argi );
|
||
|
|
||
|
continue;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if ( flags & OPT_FLAG_REASSEMBLE ) {
|
||
|
|
||
|
ASSERT( pUnknowns != NULL );
|
||
|
ASSERT( cUnknowns < argc );
|
||
|
|
||
|
OPTIONS_DEBUG( "OPT_FLAG_REASSEMBLE: this is unknown %d\n",
|
||
|
cUnknowns );
|
||
|
|
||
|
pUnknowns[ cUnknowns ] = argv[ argi ];
|
||
|
cUnknowns ++;
|
||
|
argi ++; // skipping this option
|
||
|
|
||
|
} else if ( !( flags & OPT_FLAG_SKIP_UNKNOWNS ) ) {
|
||
|
|
||
|
if ( new_argv && new_argc ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "new argv and argc-- breakout at "
|
||
|
"argi=%d\n", argi );
|
||
|
|
||
|
break; /* we're not skipping the unknown values or
|
||
|
reassembling the command line. We're just
|
||
|
supposed to quit on unknown options. */
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/* error or unknown option, depending on our flags. Regardless,
|
||
|
an error message has already been printed. */
|
||
|
|
||
|
ret = FALSE;
|
||
|
goto cleanup;
|
||
|
|
||
|
}
|
||
|
|
||
|
} /* command-line for-loop */
|
||
|
|
||
|
/* if we make it this far, all the options were ok.
|
||
|
Check for unused OPT_NONNULLs... */
|
||
|
|
||
|
OPTIONS_DEBUG( "\n*** Searching for unused options ***\n\n" );
|
||
|
|
||
|
if (!FindUnusedOptions( options, flags, NULL, pSaveQueue ) ) {
|
||
|
|
||
|
/* unused OPT_NONNULLS are a critical error. Even if the user
|
||
|
specifies OPT_FLAG_SKIP_UNKNOWNS, he/she still told us not to
|
||
|
let the user unspecify the option. We default to returning FALSE.*/
|
||
|
|
||
|
if ( flags & OPT_FLAG_TERMINATE ) {
|
||
|
|
||
|
exit( -1 );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
ret = FALSE;
|
||
|
goto cleanup;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
OPTIONS_DEBUG( "All variables are OK. Checking reassembly flag:\n" );
|
||
|
|
||
|
if ( new_argv && new_argc ) {
|
||
|
|
||
|
int i;
|
||
|
|
||
|
// we may have skipped some of the options.
|
||
|
|
||
|
if ( flags & OPT_FLAG_REASSEMBLE ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "REASSEMBLY: " );
|
||
|
|
||
|
for( i = 0 ; argi + i < argc ; i++ ) {
|
||
|
|
||
|
/* tack arguments we never parsed ( OPT_STOP_PARSING can cause
|
||
|
this ) onto the end of the Unknown array */
|
||
|
|
||
|
OPTIONS_DEBUG( "Assembling skipped option %d (%s) as unknown %d.\n",
|
||
|
i, argv[ argi+i ], cUnknowns+i );
|
||
|
|
||
|
pUnknowns[ cUnknowns+i ] = argv[ argi+i ];
|
||
|
cUnknowns++;
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( cUnknowns > 0 ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "There were %d unknowns.\n", cUnknowns);
|
||
|
|
||
|
for ( i = 0 ; i < cUnknowns ; i++ ) {
|
||
|
|
||
|
ASSERT( pUnknowns != NULL );
|
||
|
|
||
|
(*new_argv)[i] = pUnknowns[i];
|
||
|
|
||
|
}
|
||
|
|
||
|
} else OPTIONS_DEBUG( "There were no unknowns. \n" );
|
||
|
|
||
|
(*new_argv)[cUnknowns] = NULL;
|
||
|
*new_argc = cUnknowns;
|
||
|
|
||
|
#if 0 // same outcome as if the flag didn't exist
|
||
|
} else if ( flags & OPT_FLAG_SKIP_UNKNOWNS ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "User asked us to skip unknown options.\n"
|
||
|
"zeroing argv and argc.\n" );
|
||
|
#endif
|
||
|
|
||
|
} else if ( argi != argc ) {
|
||
|
|
||
|
/* normal operation-- go until we hit unknown options.
|
||
|
|
||
|
argi is the index of the first unknown option, so we add
|
||
|
it to argv and subtract it from argc. */
|
||
|
|
||
|
*new_argv = argv+argi;
|
||
|
*new_argc = argc-argi;
|
||
|
|
||
|
OPTIONS_DEBUG( "normal operation-- parsing was halted.\n"
|
||
|
"new_argv = %d. new_argc = 0x%x.\n",
|
||
|
|
||
|
*new_argv, *new_argc );
|
||
|
} else {
|
||
|
|
||
|
*new_argv = NULL;
|
||
|
*new_argc = 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
#if 0
|
||
|
if ( new_argv && new_argc ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "Catch-all case. Zeroing argv and argc.\n" );
|
||
|
|
||
|
*new_argv = NULL;
|
||
|
*new_argc = 0;
|
||
|
|
||
|
}
|
||
|
#else
|
||
|
|
||
|
OPTIONS_DEBUG( "User didn't request new argv or argc.\n" );
|
||
|
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
OPTIONS_DEBUG( "command line survived the parser.\n" );
|
||
|
|
||
|
ret = TRUE;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
ASSERT( pSaveQueue != NULL );
|
||
|
|
||
|
if (!( flags & OPT_FLAG_MEMORYLIST_OK ) ) {
|
||
|
|
||
|
if ( ret ) {
|
||
|
|
||
|
OPTIONS_DEBUG( "Returning SaveQueue = 0x%x\n", pSaveQueue );
|
||
|
|
||
|
*ppReturnedMemory = (PVOID) pSaveQueue;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
OPTIONS_DEBUG( "function failing. cleaning up local data..." );
|
||
|
CleanupOptionDataEx( pSaveQueue );
|
||
|
OPTIONS_DEBUG( "ok.\n" );
|
||
|
|
||
|
*ppReturnedMemory = NULL;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
|
||
|
}
|