/*++ PARSE.C Option parser Split from options.c 6/9/1997 by DavidCHR --*/ #include "private.h" #include /* 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; }