windows-nt/Source/XPSP1/NT/ds/security/tools/keytab2/opt/parse.c
2020-09-26 16:20:57 +08:00

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;
}