Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

485 lines
12 KiB

  1. /*++
  2. PARSE.C
  3. Option parser
  4. Split from options.c 6/9/1997 by DavidCHR
  5. --*/
  6. #include "private.h"
  7. #include <malloc.h>
  8. /* ParseOneOption:
  9. parses a single option from argv.
  10. returns: number of arguments eaten, or zero on error */
  11. BOOL
  12. ParseOneOption( int argc, // number of arguments, total
  13. PCHAR *argv, // vector of arguments
  14. int argi, // which argument we're to parse
  15. ULONG flags, // applicable flags
  16. optionStruct *options, // option structure
  17. int *argsused, // arguments we've eaten
  18. PBOOL pbStop, // Stop Parsing
  19. PSAVEQUEUE pQueue // memory save area
  20. ) {
  21. PCHAR p; // argument pointer copy
  22. int opti; // which option we're examining
  23. BOOL ret = FALSE; // return value
  24. int tmp = 0; // temporary inbound value
  25. int used = 0;
  26. ASSERT( argc > 0 ); // there must be arguments
  27. ASSERT( argv != NULL ); // the vector must exist
  28. ASSERT( argi < argc ); // argument number must be INSIDE the vector
  29. ASSERT( options != NULL ); // passed option structure must be valid
  30. ASSERT( argsused != NULL );
  31. p = argv[argi];
  32. if( ISSWITCH( p[0] ) ) {
  33. p++; /* skip the switch character */
  34. }
  35. OPTIONS_DEBUG("Checking option %d: \"%hs\"...", argi, p);
  36. for (opti = 0 ; !ARRAY_TERMINATED( options+opti ) ; opti++ ) {
  37. OPTIONS_DEBUG("against [%d]%hs...", opti, options[opti].cmd);
  38. if ( ParseCompare( options+opti, flags, p ) ) {
  39. /* we have a match! */
  40. ret = StoreOption( options, argv, argc, argi, opti,
  41. flags, &tmp, TRUE, pbStop, pQueue );
  42. OPTIONS_DEBUG( "StoreOption returned %d, args=%d, *pbStop = %d\n",
  43. ret, tmp, *pbStop );
  44. if ( !ret ) {
  45. /* failed to store options for some reason. This is
  46. critical. */
  47. PrintUsage( stderr, flags, options, "" );
  48. if ( flags & OPT_FLAG_TERMINATE ) {
  49. exit( -1 );
  50. } else {
  51. return FALSE;
  52. }
  53. }
  54. OPTIONS_DEBUG( "ParseOneOption now returning TRUE, argsused = %d.\n",
  55. tmp );
  56. *argsused = tmp;
  57. return ret; /* successful StoreOptions parses our one option. */
  58. } /* if ParseCompare... */
  59. OPTIONS_DEBUG( "nope.\n" );
  60. } /* options for-loop */
  61. OPTIONS_DEBUG( "did not find the option. Checking for OPT_DEFAULTs.\n" );
  62. for (opti = 0 ; !ARRAY_TERMINATED( options+opti ) ; opti++ ) {
  63. if ( options[opti].flags & OPT_DEFAULT ) {
  64. /* WASBUG 73922: should check to see if the option is also an
  65. OPT_SUBOPTION, then parse the suboption for OPT_DEFAULTs.
  66. However, as it stands, this will just fail for OPT_SUBOPTIONS,
  67. because the first pointer will probably be nonnull.
  68. The dev enlistment doesn't contain any OPT_SUBOPTIONS, so
  69. this is not an issue. */
  70. ASSERT( ( options[ opti ].flags & OPT_MUTEX_MASK ) != OPT_SUBOPTION );
  71. if ( *( ((POPTU) &options[opti].data)->raw_data) == NULL ) {
  72. OPTIONS_DEBUG("Storing %hs in unused OPT_DEFAULT %hs\n",
  73. argv[argi],
  74. options[opti].cmd );
  75. ret = StoreOption( options, argv, argc, argi, opti,
  76. flags, &tmp, FALSE, pbStop, pQueue );
  77. OPTIONS_DEBUG("OPT_DEFAULT: StoreOptions returned %d\n", ret);
  78. if ( !ret ) {
  79. PrintUsage( stderr, flags, options, "" );
  80. exit( -1 );
  81. }
  82. *argsused = tmp;
  83. return ret;
  84. }
  85. }
  86. }
  87. *argsused = 0;
  88. if ( ( flags & OPT_FLAG_SKIP_UNKNOWNS ) ||
  89. ( flags & OPT_FLAG_REASSEMBLE ) ||
  90. ( flags & OPT_FLAG_INTERNAL_JUMPOUT )) {
  91. return TRUE; // skip this option
  92. } else {
  93. fprintf(stderr, "unknown option \"%hs\".\n", argv[argi]);
  94. PrintUsage(stderr, flags, options, "");
  95. if ( flags & OPT_FLAG_TERMINATE ) {
  96. exit( -1 );
  97. }
  98. return FALSE;
  99. }
  100. ASSERT_NOTREACHED( "should be no path to this code" );
  101. }
  102. /* ParseOptionsEx:
  103. initializes the option structure, which is a sentinally-terminated
  104. vector of optionStructs.
  105. argc, argv: arguments to main() (see K&R)
  106. pOptionStructure: vector of optionStructs, terminated with TERMINATE_ARRAY
  107. optionFlags: optional flags to control api behavior
  108. ppReturnedMemory: returned handle to a list of memory to be freed before
  109. program exit. Use CleanupOptionDataEx to free it.
  110. new_arg[c,v]: if nonnull, a new argc and argv are returned here.
  111. if all the options were used up, argc = 0 and argv is
  112. NULL. Note that it is safe to provide pointers to the
  113. original argv/argc if so desired.
  114. The function's behavior is complex:
  115. the function will always return FALSE on any critical error (unable to
  116. allocate memory, or invalid argument). On WINNT, Last Error will be
  117. set to the appropriate error.
  118. if new_argc AND new_argv are specified,
  119. ParseOptionsEx will always return TRUE unless help was called, and
  120. the two parameters will be updated to reflect new values.
  121. otherwise:
  122. ParseOptionsEx will return TRUE if it was able to recognize ALL args
  123. on the command line given. It will return FALSE if any of the options
  124. were unknown. This will probably be what most people want.
  125. */
  126. BOOL
  127. ParseOptionsEx( int argc,
  128. char **argv,
  129. optionStruct *options,
  130. ULONG flags,
  131. PVOID *ppReturnedMemory,
  132. int *new_argc,
  133. char ***new_argv ) {
  134. BOOL bStopParsing = FALSE;
  135. BOOL ret = FALSE;
  136. LONG argi; // argument index
  137. LONG tmp; // temporary return variable
  138. PSAVEQUEUE pSaveQueue = NULL; // memory save area
  139. PCHAR *pUnknowns = NULL; // will alloc with alloca
  140. int cUnknowns = 0;
  141. flags = flags & ~OPT_FLAG_INTERNAL_RESERVED; /* mask off flags that
  142. the user shouldn't set. */
  143. if ( new_argc && new_argv &&
  144. !( flags & ( OPT_FLAG_SKIP_UNKNOWNS |
  145. OPT_FLAG_REASSEMBLE |
  146. OPT_FLAG_TERMINATE ) ) ) {
  147. OPTIONS_DEBUG( "\nSetting internal jumpout flag.\n" );
  148. flags |= OPT_FLAG_INTERNAL_JUMPOUT;
  149. }
  150. OPTIONS_DEBUG( "ParseOptionsEx( argc = %d\n"
  151. " argv = 0x%x\n"
  152. " opts = 0x%x\n"
  153. " flags = 0x%x\n"
  154. " ppMem = 0x%x\n"
  155. " pargc = 0x%x\n"
  156. " pargv = 0x%x\n",
  157. argc, argv, options, flags, ppReturnedMemory, new_argc,
  158. new_argv );
  159. ASSERT( ppReturnedMemory != NULL );
  160. // first, we need to ensure we have a save area.
  161. if ( flags & OPT_FLAG_MEMORYLIST_OK ) {
  162. pSaveQueue = (PSAVEQUEUE) *ppReturnedMemory;
  163. } else if ( !OptionAlloc( NULL, &pSaveQueue, sizeof( SAVEQUEUE ) ) ) {
  164. fprintf( stderr,
  165. "ParseOptionsEx: unable to allocate save area\n" );
  166. return FALSE;
  167. }
  168. ASSERT( pSaveQueue != NULL );
  169. /* We must initialize pUnknowns if the user specified command-line
  170. reassembly. Otherwise, it can stay NULL. */
  171. if ( (flags & OPT_FLAG_REASSEMBLE) && ( argc > 0 ) ) {
  172. pUnknowns = (PCHAR *) alloca( argc * sizeof( PCHAR ) );
  173. ASSERT( pUnknowns != NULL ); /* yes, this assertion is invalid.
  174. However, there is no clean solution if
  175. we run out of stack space. Something
  176. else will just fail even more
  177. spectacularly. */
  178. }
  179. OPTIONS_DEBUG("options are at 0x%x\n", options);
  180. #ifdef DEBUG_OPTIONS
  181. if (DebugFlag) {
  182. for (argi = 0; argi < argc ; argi++ ) {
  183. OPTIONS_DEBUG("option %d is %hs\n", argi, argv[argi]);
  184. }
  185. }
  186. #endif
  187. for (argi = 0 ; argi < argc ; /* NOTHING */ ) {
  188. int tmp;
  189. if ( bStopParsing ) { // this gets set in the PREVIOUS iteration.
  190. OPTIONS_DEBUG( "bStopParsing is TRUE. Terminating parse run.\n");
  191. /* WASBUG 73924: now what do we do with the unused options?
  192. They get leaked. This is okay, because the app terminates. */
  193. break;
  194. }
  195. if ( ParseOneOption( argc, argv, argi, flags, options, &tmp,
  196. &bStopParsing, pSaveQueue ) ) {
  197. OPTIONS_DEBUG( "ParseOneOption succeeded with %d options.\n", tmp );
  198. if ( tmp > 0 ) {
  199. // we were able to successfully parse one or more options.
  200. argi += tmp;
  201. OPTIONS_DEBUG( "advancing argi by %d to %d\n", tmp, argi );
  202. continue;
  203. } else {
  204. if ( flags & OPT_FLAG_REASSEMBLE ) {
  205. ASSERT( pUnknowns != NULL );
  206. ASSERT( cUnknowns < argc );
  207. OPTIONS_DEBUG( "OPT_FLAG_REASSEMBLE: this is unknown %d\n",
  208. cUnknowns );
  209. pUnknowns[ cUnknowns ] = argv[ argi ];
  210. cUnknowns ++;
  211. argi ++; // skipping this option
  212. } else if ( !( flags & OPT_FLAG_SKIP_UNKNOWNS ) ) {
  213. if ( new_argv && new_argc ) {
  214. OPTIONS_DEBUG( "new argv and argc-- breakout at "
  215. "argi=%d\n", argi );
  216. break; /* we're not skipping the unknown values or
  217. reassembling the command line. We're just
  218. supposed to quit on unknown options. */
  219. }
  220. }
  221. continue;
  222. }
  223. } else {
  224. /* error or unknown option, depending on our flags. Regardless,
  225. an error message has already been printed. */
  226. ret = FALSE;
  227. goto cleanup;
  228. }
  229. } /* command-line for-loop */
  230. /* if we make it this far, all the options were ok.
  231. Check for unused OPT_NONNULLs... */
  232. OPTIONS_DEBUG( "\n*** Searching for unused options ***\n\n" );
  233. if (!FindUnusedOptions( options, flags, NULL, pSaveQueue ) ) {
  234. /* unused OPT_NONNULLS are a critical error. Even if the user
  235. specifies OPT_FLAG_SKIP_UNKNOWNS, he/she still told us not to
  236. let the user unspecify the option. We default to returning FALSE.*/
  237. if ( flags & OPT_FLAG_TERMINATE ) {
  238. exit( -1 );
  239. } else {
  240. ret = FALSE;
  241. goto cleanup;
  242. }
  243. }
  244. OPTIONS_DEBUG( "All variables are OK. Checking reassembly flag:\n" );
  245. if ( new_argv && new_argc ) {
  246. int i;
  247. // we may have skipped some of the options.
  248. if ( flags & OPT_FLAG_REASSEMBLE ) {
  249. OPTIONS_DEBUG( "REASSEMBLY: " );
  250. for( i = 0 ; argi + i < argc ; i++ ) {
  251. /* tack arguments we never parsed ( OPT_STOP_PARSING can cause
  252. this ) onto the end of the Unknown array */
  253. OPTIONS_DEBUG( "Assembling skipped option %d (%s) as unknown %d.\n",
  254. i, argv[ argi+i ], cUnknowns+i );
  255. pUnknowns[ cUnknowns+i ] = argv[ argi+i ];
  256. cUnknowns++;
  257. }
  258. if ( cUnknowns > 0 ) {
  259. OPTIONS_DEBUG( "There were %d unknowns.\n", cUnknowns);
  260. for ( i = 0 ; i < cUnknowns ; i++ ) {
  261. ASSERT( pUnknowns != NULL );
  262. (*new_argv)[i] = pUnknowns[i];
  263. }
  264. } else OPTIONS_DEBUG( "There were no unknowns. \n" );
  265. (*new_argv)[cUnknowns] = NULL;
  266. *new_argc = cUnknowns;
  267. #if 0 // same outcome as if the flag didn't exist
  268. } else if ( flags & OPT_FLAG_SKIP_UNKNOWNS ) {
  269. OPTIONS_DEBUG( "User asked us to skip unknown options.\n"
  270. "zeroing argv and argc.\n" );
  271. #endif
  272. } else if ( argi != argc ) {
  273. /* normal operation-- go until we hit unknown options.
  274. argi is the index of the first unknown option, so we add
  275. it to argv and subtract it from argc. */
  276. *new_argv = argv+argi;
  277. *new_argc = argc-argi;
  278. OPTIONS_DEBUG( "normal operation-- parsing was halted.\n"
  279. "new_argv = %d. new_argc = 0x%x.\n",
  280. *new_argv, *new_argc );
  281. } else {
  282. *new_argv = NULL;
  283. *new_argc = 0;
  284. }
  285. } else {
  286. #if 0
  287. if ( new_argv && new_argc ) {
  288. OPTIONS_DEBUG( "Catch-all case. Zeroing argv and argc.\n" );
  289. *new_argv = NULL;
  290. *new_argc = 0;
  291. }
  292. #else
  293. OPTIONS_DEBUG( "User didn't request new argv or argc.\n" );
  294. #endif
  295. }
  296. OPTIONS_DEBUG( "command line survived the parser.\n" );
  297. ret = TRUE;
  298. cleanup:
  299. ASSERT( pSaveQueue != NULL );
  300. if (!( flags & OPT_FLAG_MEMORYLIST_OK ) ) {
  301. if ( ret ) {
  302. OPTIONS_DEBUG( "Returning SaveQueue = 0x%x\n", pSaveQueue );
  303. *ppReturnedMemory = (PVOID) pSaveQueue;
  304. } else {
  305. OPTIONS_DEBUG( "function failing. cleaning up local data..." );
  306. CleanupOptionDataEx( pSaveQueue );
  307. OPTIONS_DEBUG( "ok.\n" );
  308. *ppReturnedMemory = NULL;
  309. }
  310. }
  311. return ret;
  312. }