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.

2457 lines
78 KiB

  1. // ****************************************************************************
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // Module Name:
  6. //
  7. // CmdLineParser.c
  8. //
  9. // Abstract:
  10. //
  11. // This modules implements parsing of command line arguments for the
  12. // specified options
  13. //
  14. // Author:
  15. //
  16. // Sunil G.V.N. Murali ([email protected]) 1-Sep-2000
  17. //
  18. // Revision History:
  19. //
  20. // Sunil G.V.N. Murali ([email protected]) 1-Sep-2000 : Created It.
  21. //
  22. // ****************************************************************************
  23. #include "pch.h"
  24. #include "cmdline.h"
  25. #include "CmdLineRes.h"
  26. // permanent indexes to the temporary buffers
  27. #define INDEX_TEMP_NONE 0
  28. #define INDEX_TEMP_SPLITOPTION 1
  29. #define INDEX_TEMP_SPLITVALUE 2
  30. #define INDEX_TEMP_SAVEDATA 3
  31. #define INDEX_TEMP_USAGEHELPER 4
  32. #define INDEX_TEMP_MAINOPTION 5
  33. //
  34. // defines / constants / enumerations
  35. //
  36. // constants
  37. const WCHAR cwszOptionChars[ 3 ] = L"-/";
  38. const CHAR cszParserSignature[ 8 ] = "PARSER2";
  39. // version resource specific structures
  40. typedef struct __tagLanguageAndCodePage {
  41. WORD wLanguage;
  42. WORD wCodePage;
  43. } TTRANSLATE, *PTTRANSLATE;
  44. // error messages
  45. #define ERROR_USAGEHELPER GetResString( IDS_ERROR_CMDPARSER_USAGEHELPER )
  46. #define ERROR_NULLVALUE GetResString( IDS_ERROR_CMDPARSER_NULLVALUE )
  47. #define ERROR_DEFAULT_NULLVALUE GetResString( IDS_ERROR_CMDPARSER_DEFAULT_NULLVALUE )
  48. #define ERROR_VALUE_EXPECTED GetResString( IDS_ERROR_CMDPARSER_VALUE_EXPECTED )
  49. #define ERROR_NOTINLIST GetResString( IDS_ERROR_CMDPARSER_NOTINLIST )
  50. #define ERROR_DEFAULT_NOTINLIST GetResString( IDS_ERROR_CMDPARSER_DEFAULT_NOTINLIST )
  51. #define ERROR_INVALID_NUMERIC GetResString( IDS_ERROR_CMDPARSER_INVALID_NUMERIC )
  52. #define ERROR_DEFAULT_INVALID_NUMERIC GetResString( IDS_ERROR_CMDPARSER_DEFAULT_INVALID_NUMERIC )
  53. #define ERROR_INVALID_FLOAT GetResString( IDS_ERROR_CMDPARSER_INVALID_FLOAT )
  54. #define ERROR_DEFAULT_INVALID_FLOAT GetResString( IDS_ERROR_CMDPARSER_DEFAULT_INVALID_FLOAT )
  55. #define ERROR_LENGTH_EXCEEDED GetResString( IDS_ERROR_CMDPARSER_LENGTH_EXCEEDED_EX )
  56. #define ERROR_DEFAULT_LENGTH_EXCEEDED GetResString( IDS_ERROR_CMDPARSER_DEFAULT_LENGTH_EXCEEDED_EX )
  57. #define ERROR_INVALID_OPTION GetResString( IDS_ERROR_CMDPARSER_INVALID_OPTION )
  58. #define ERROR_OPTION_REPEATED GetResString( IDS_ERROR_CMDPARSER_OPTION_REPEATED )
  59. #define ERROR_DEFAULT_OPTION_REPEATED GetResString( IDS_ERROR_CMDPARSER_DEFAULT_OPTION_REPEATED )
  60. #define ERROR_MANDATORY_OPTION_MISSING GetResString( IDS_ERROR_CMDPARSER_MANDATORY_OPTION_MISSING )
  61. #define ERROR_DEFAULT_OPTION_MISSING GetResString( IDS_ERROR_CMDPARSER_DEFAULT_OPTION_MISSING )
  62. #define ERROR_VALUENOTALLOWED GetResString( IDS_ERROR_CMDPARSER_VALUENOTALLOWED )
  63. //
  64. // custom macros
  65. #define REASON_VALUE_NOTINLIST( value, option, helptext ) \
  66. if ( option == NULL || lstrlen( option ) == 0 ) \
  67. { \
  68. SetReason2( 2, \
  69. ERROR_DEFAULT_NOTINLIST, \
  70. _X( value ), _X2( helptext ) ); \
  71. } \
  72. else \
  73. { \
  74. SetReason2( 3, ERROR_NOTINLIST, \
  75. _X( value ), _X2( option ), _X3( helptext ) ); \
  76. } \
  77. 1
  78. #define REASON_NULLVALUE( option, helptext ) \
  79. if ( option == NULL || lstrlen( option ) == 0 ) \
  80. { \
  81. SetReason2( 1, ERROR_DEFAULT_NULLVALUE, _X( helptext ) ); \
  82. } \
  83. else \
  84. { \
  85. SetReason2( 2, \
  86. ERROR_NULLVALUE, _X( option ), _X2( helptext ) ); \
  87. } \
  88. 1
  89. #define REASON_VALUE_EXPECTED( option, helptext ) \
  90. if ( option == NULL || lstrlen( option ) == 0 ) \
  91. { \
  92. UNEXPECTED_ERROR(); \
  93. SaveLastError(); \
  94. } \
  95. else \
  96. { \
  97. SetReason2( 2, \
  98. ERROR_VALUE_EXPECTED, _X( option ), _X2( helptext ) ); \
  99. } \
  100. 1
  101. #define REASON_INVALID_NUMERIC( option, helptext ) \
  102. if ( option == NULL || lstrlen( option ) == 0 ) \
  103. { \
  104. SetReason2( 1, \
  105. ERROR_DEFAULT_INVALID_NUMERIC, _X( helptext ) ); \
  106. } \
  107. else \
  108. { \
  109. SetReason2( 2, \
  110. ERROR_INVALID_NUMERIC, _X( option ), _X2( helptext ) ); \
  111. } \
  112. 1
  113. #define REASON_INVALID_FLOAT( option, helptext ) \
  114. if ( option == NULL || lstrlen( option ) == 0 ) \
  115. { \
  116. SetReason2( 1, \
  117. ERROR_DEFAULT_INVALID_FLOAT, _X( helptext ) ); \
  118. } \
  119. else \
  120. { \
  121. SetReason2( 2, \
  122. ERROR_INVALID_FLOAT, _X( option ), _X2( helptext ) ); \
  123. } \
  124. 1
  125. #define REASON_LENGTH_EXCEEDED( option, length ) \
  126. if ( option == NULL || lstrlen( option ) == 0 ) \
  127. { \
  128. SetReason2( 1, ERROR_DEFAULT_LENGTH_EXCEEDED, length ); \
  129. } \
  130. else \
  131. { \
  132. SetReason2( 2, \
  133. ERROR_LENGTH_EXCEEDED, _X( option ), length ); \
  134. } \
  135. 1
  136. #define REASON_OPTION_REPEATED( option, count, helptext ) \
  137. if ( option == NULL || lstrlen( option ) == 0 ) \
  138. { \
  139. SetReason2( 2, \
  140. ERROR_DEFAULT_OPTION_REPEATED, count, helptext ); \
  141. } \
  142. else \
  143. { \
  144. SetReason2( 3, \
  145. ERROR_OPTION_REPEATED, _X( option ), count, helptext ); \
  146. } \
  147. 1
  148. #define REASON_MANDATORY_OPTION_MISSING( option, helptext ) \
  149. if ( option == NULL || lstrlen( option ) == 0 ) \
  150. { \
  151. SetReason2( 1, \
  152. ERROR_DEFAULT_OPTION_MISSING, helptext ); \
  153. } \
  154. else \
  155. { \
  156. SetReason2( 2, \
  157. ERROR_MANDATORY_OPTION_MISSING, \
  158. _X( option ), helptext ); \
  159. } \
  160. 1
  161. #define REASON_VALUENOTALLOWED( option, helptext ) \
  162. if ( option == NULL || lstrlen( option ) == 0 ) \
  163. { \
  164. UNEXPECTED_ERROR(); \
  165. SaveLastError(); \
  166. } \
  167. else \
  168. { \
  169. SetReason2( 2, \
  170. ERROR_VALUENOTALLOWED, _X( option ), _X2( helptext ) ); \
  171. } \
  172. 1
  173. //
  174. // internal structures
  175. //
  176. typedef struct __tagMatchOptionInfo
  177. {
  178. LPWSTR pwszOption;
  179. LPWSTR pwszValue;
  180. } TMATCHOPTION_INFO;
  181. typedef struct __tagParserSaveData
  182. {
  183. DWORD dwIncrement;
  184. LONG lDefaultIndex;
  185. LPCWSTR pwszUsageHelper;
  186. PTCMDPARSER2 pcmdparser;
  187. } TPARSERSAVE_DATA;
  188. //
  189. // private functions ... used only within this file
  190. //
  191. BOOL IsOption( LPCWSTR pwszOption );
  192. BOOL IsValueNeeded( DWORD dwType );
  193. LPCWSTR PrepareUsageHelperText( LPCWSTR pwszOption );
  194. LPCWSTR ExtractMainOption( LPCWSTR pwszOptions, DWORD dwReserved );
  195. BOOL VerifyParserOptions( LONG* plDefaultIndex,
  196. DWORD dwCount, PTCMDPARSER2 pcmdOptions );
  197. BOOL ParseAndSaveOptionValue( LPCWSTR pwszOption,
  198. LPCWSTR pwszValue, TPARSERSAVE_DATA* pSaveData );
  199. LONG MatchOption( DWORD dwOptions,
  200. PTCMDPARSER2 pcmdOptions, LPCWSTR pwszOption );
  201. LONG MatchOptionEx( DWORD dwOptions, PTCMDPARSER2 pcmdOptions,
  202. LPCWSTR pwszOption, TMATCHOPTION_INFO* pMatchInfo );
  203. BOOL Parser1FromParser2Stub( LPCWSTR pwszOption,
  204. LPCWSTR pwszValue,
  205. LPVOID pData, DWORD* pdwIncrement );
  206. BOOL ReleaseAllocatedMemory( DWORD dwOptionsCount, PTCMDPARSER2 pcmdOptions );
  207. //
  208. // implementation
  209. //
  210. __inline
  211. LPWSTR
  212. GetParserTempBuffer( IN DWORD dwIndexNumber,
  213. IN LPCWSTR pwszText,
  214. IN DWORD dwLength,
  215. IN BOOL bNullify )
  216. /*++
  217. Routine Description:
  218. since every file will need the temporary buffers -- in order to see
  219. that their buffers wont be override with other functions, we are
  220. creating seperate buffer space a for each file
  221. this function will provide an access to those internal buffers and also
  222. safe guards the file buffer boundaries
  223. Arguments:
  224. [ in ] dwIndexNumber - file specific index number
  225. [ in ] pwszText - default text that needs to be copied into
  226. temporary buffer
  227. [ in ] dwLength - Length of the temporary buffer that is required
  228. Ignored when pwszText is specified
  229. [ in ] bNullify - Informs whether to clear the buffer or not
  230. before giving the temporary buffer
  231. Return Value:
  232. NULL - when any failure occurs
  233. NOTE: do not rely on GetLastError to know the reason
  234. for the failure.
  235. success - return memory address of the requested size
  236. NOTE:
  237. ----
  238. if pwszText and dwLength both are NULL, then we treat that the caller
  239. is asking for the reference of the buffer and we return the buffer address.
  240. In this call, there wont be any memory allocations -- if the requested index
  241. doesn't exist, we return as failure
  242. Also, the buffer returned by this function need not released by the caller.
  243. While exiting from the tool, all the memory will be freed automatically by
  244. the ReleaseGlobals functions.
  245. --*/
  246. {
  247. if ( dwIndexNumber >= TEMP_CMDLINEPARSER_C_COUNT )
  248. {
  249. return NULL;
  250. }
  251. // check if caller is requesting existing buffer contents
  252. if ( pwszText == NULL && dwLength == 0 && bNullify == FALSE )
  253. {
  254. // yes -- we need to pass the existing buffer contents
  255. return GetInternalTemporaryBufferRef(
  256. dwIndexNumber + INDEX_TEMP_CMDLINEPARSER_C );
  257. }
  258. // ...
  259. return GetInternalTemporaryBuffer(
  260. dwIndexNumber + INDEX_TEMP_CMDLINEPARSER_C, pwszText, dwLength, bNullify );
  261. }
  262. BOOL IsOption( IN LPCWSTR pwszOption )
  263. /*++
  264. Routine Description:
  265. Checks whether the passed argument starts with the option character
  266. or not -- currently the we treat the string as option if they start with
  267. "-" and "/" .
  268. Arguments:
  269. [ in ] pwszOption - string value
  270. Return Value:
  271. FALSE - 1. if the parameter is invalid
  272. 2. if the string doesn't start with option character
  273. To differentiate between the case 1 and case 2 call
  274. GetLastError() and check for ERROR_INVALID_PARAMETER.
  275. TRUE - if the string starts with option character
  276. --*/
  277. {
  278. // clear error
  279. CLEAR_LAST_ERROR();
  280. // check the input value
  281. if ( pwszOption == NULL )
  282. {
  283. INVALID_PARAMETER();
  284. return FALSE;
  285. }
  286. // check whether the string starts with '-' or '/' character
  287. if ( lstrlen( pwszOption ) > 1 &&
  288. FindChar2( cwszOptionChars, pwszOption[ 0 ], TRUE, 0 ) != -1 )
  289. {
  290. return TRUE; // string value is an option
  291. }
  292. // this is not an option
  293. return FALSE;
  294. }
  295. BOOL IsValueNeeded( DWORD dwType )
  296. /*++
  297. Routine Description:
  298. Checks whether the supported data type requires argument for the
  299. option or not.
  300. Arguments:
  301. [ in ] dwType - specifies one of the CP_TYPE_xxxx values
  302. Return Value:
  303. TRUE - if the supported data type requires argument for the option
  304. FALSE - if the data type passed is not supported (or) if the
  305. option of the requested type doesn't require argument.
  306. NOTE: Do not rely on GetLastError() for detecting the reason
  307. for the failure.
  308. --*/
  309. {
  310. switch( dwType )
  311. {
  312. case CP_TYPE_TEXT:
  313. case CP_TYPE_NUMERIC:
  314. case CP_TYPE_UNUMERIC:
  315. case CP_TYPE_FLOAT:
  316. case CP_TYPE_DOUBLE:
  317. return TRUE;
  318. case CP_TYPE_DATE:
  319. case CP_TYPE_TIME:
  320. case CP_TYPE_DATETIME:
  321. return FALSE;
  322. case CP_TYPE_BOOLEAN:
  323. return FALSE;
  324. case CP_TYPE_CUSTOM:
  325. // actually -- we dont know -- but for now, simply say yes
  326. return TRUE;
  327. default:
  328. return FALSE;
  329. }
  330. }
  331. LPCWSTR PrepareUsageHelperText( LPCWSTR pwszOption )
  332. /*++
  333. Routine Description:
  334. Extracts tool name from the executable module's version resource
  335. and prepares usage help text -- for ex. if the tool name is
  336. eventcreate.exe, this function will generate the text as
  337. Type "EVENTCREATE /?" for usage.
  338. Since some tools requires option to be displayed along with the
  339. tool name, this function accepts a parameter which specifies
  340. that extra option -- if that extra option is present the message looks
  341. like:
  342. Type "SCHTASKS /CREATE /?" for usage.
  343. If the message need not have the extra option information,
  344. the caller just needs to pass NULL as parameter to this function.
  345. Arguments:
  346. [ in ] pwszOption - option that needs to be shown along with
  347. the error text. If option need not be shown,
  348. pass NULL for this argument.
  349. Return Value:
  350. NULL - this will be returned when anything goes wrong. Use
  351. GetLastError() to know what went wrong
  352. on success - formatted usage error text will be returned
  353. --*/
  354. {
  355. // local variables
  356. DWORD dw = 0;
  357. UINT dwSize = 0;
  358. UINT dwTranslateSize = 0;
  359. LPWSTR pwszTemp = NULL;
  360. LPWSTR pwszBuffer = NULL;
  361. LPWSTR pwszUtilityName = NULL;
  362. LPVOID pVersionInfo = NULL;
  363. LPWSTR pwszExeName = NULL;
  364. PTTRANSLATE pTranslate = NULL;
  365. // clear last error
  366. CLEAR_LAST_ERROR();
  367. //
  368. // try to get the current running module name
  369. //
  370. // we dont know whether GetModuleFileName will terminate
  371. // the module name or not -- also, if the length of the buffer is not
  372. // sufficient, GetModuleFileName will truncate the file name -- keeping
  373. // all these scenarios in mind, we will loop in the GetModuleFileName
  374. // until we make sure that we have the complete the executable name
  375. // which is also null terminated
  376. // init
  377. dw = 0;
  378. dwSize = _MAX_PATH;
  379. // ...
  380. do
  381. {
  382. // get the buffer
  383. dwSize += (dw == 0) ? 0 : _MAX_PATH;
  384. pwszExeName = GetParserTempBuffer( 0, NULL, dwSize, TRUE );
  385. if ( pwszExeName == NULL )
  386. {
  387. OUT_OF_MEMORY();
  388. return NULL;
  389. }
  390. // get the module name
  391. dw = GetModuleFileName( NULL, pwszExeName, dwSize );
  392. if ( dw == 0 )
  393. {
  394. return NULL;
  395. }
  396. } while (dw >= dwSize - 1);
  397. // get the version information size
  398. dwSize = GetFileVersionInfoSize( pwszExeName, 0 );
  399. if ( dwSize == 0 )
  400. {
  401. // tool might have encountered error (or)
  402. // tool doesn't have version information
  403. // but version information is mandatory for us
  404. // so, just exit
  405. if ( GetLastError() == NO_ERROR )
  406. {
  407. INVALID_PARAMETER();
  408. }
  409. // ...
  410. return NULL;
  411. }
  412. // allocate memory for the version resource
  413. // take some 10 bytes extra -- for safety purposes
  414. dwSize += 10;
  415. pVersionInfo = AllocateMemory( dwSize );
  416. if ( pVersionInfo == NULL )
  417. {
  418. return NULL;
  419. }
  420. // now get the version information
  421. if ( GetFileVersionInfo( pwszExeName, 0,
  422. dwSize, pVersionInfo ) == FALSE )
  423. {
  424. FreeMemory( &pVersionInfo );
  425. return NULL;
  426. }
  427. // get the translation info
  428. if ( VerQueryValue( pVersionInfo,
  429. L"\\VarFileInfo\\Translation",
  430. (LPVOID*) &pTranslate, &dwTranslateSize ) == FALSE )
  431. {
  432. FreeMemory( &pVersionInfo );
  433. return NULL;
  434. }
  435. // get the buffer to store the translation array format string
  436. pwszBuffer = GetParserTempBuffer( 0, NULL, 64, TRUE );
  437. // try to get the internal name of the tool for each language and code page.
  438. pwszUtilityName = NULL;
  439. for( dw = 0; dw < ( dwTranslateSize / sizeof( TTRANSLATE ) ); dw++ )
  440. {
  441. // prepare the format string to get the localized the version info
  442. StringCchPrintfW( pwszBuffer, 64,
  443. L"\\StringFileInfo\\%04x%04x\\InternalName",
  444. pTranslate[ dw ].wLanguage, pTranslate[ dw ].wCodePage );
  445. // retrieve file description for language and code page "i".
  446. if ( VerQueryValue( pVersionInfo, pwszBuffer,
  447. (LPVOID*) &pwszUtilityName, &dwSize ) == FALSE )
  448. {
  449. // we cannot decide the failure based on the result of this
  450. // function failure -- we will decide about this
  451. // after terminating from the 'for' loop
  452. // for now, make the pwszExeName to NULL -- this will
  453. // enable us to decide the result
  454. pwszUtilityName = NULL;
  455. }
  456. else
  457. {
  458. // successfully retrieved the internal name
  459. break;
  460. }
  461. }
  462. // check whether we got the executable name or not -- if not, error
  463. if ( pwszUtilityName == NULL )
  464. {
  465. FreeMemory( &pVersionInfo );
  466. return NULL;
  467. }
  468. // check whether filename has .EXE as extension or not
  469. // also the file name should be more than 4 characters (including extension)
  470. if ( StringLength( pwszUtilityName, 0 ) <= 4 )
  471. {
  472. // some thing wrong -- version resource should include the internal name
  473. FreeMemory( &pVersionInfo );
  474. UNEXPECTED_ERROR();
  475. return NULL;
  476. }
  477. else if ( FindString2( pwszUtilityName, L".EXE", TRUE, 0 ) != -1 )
  478. {
  479. // now put null character -- this is to trim the extension
  480. pwszUtilityName[ lstrlen( pwszUtilityName ) - lstrlen( L".EXE" ) ] = cwchNullChar;
  481. }
  482. // determine the size we need for
  483. if ( pwszOption != NULL )
  484. {
  485. // "length of utility name + 1 (space) + length of option" + 10 buffer (for safety)
  486. dwSize = lstrlen( pwszUtilityName ) + lstrlen( pwszOption ) + 11;
  487. // get the temporary buffer for that
  488. if ( (pwszTemp = GetParserTempBuffer( 0, NULL, dwSize, TRUE )) == NULL )
  489. {
  490. FreeMemory( &pVersionInfo );
  491. OUT_OF_MEMORY();
  492. return NULL;
  493. }
  494. // ...
  495. StringCchPrintfW( pwszTemp, dwSize, L"%s %s", pwszUtilityName, pwszOption );
  496. // now remap the utility name pointer to the temp pointer
  497. pwszUtilityName = pwszTemp;
  498. }
  499. else
  500. {
  501. // get the temporary buffer with this utilty name
  502. if ( (pwszTemp = GetParserTempBuffer( 0, pwszUtilityName, 0, FALSE )) == NULL )
  503. {
  504. FreeMemory( &pVersionInfo );
  505. OUT_OF_MEMORY();
  506. return NULL;
  507. }
  508. // now remap the utility name pointer to the temp pointer
  509. pwszUtilityName = pwszTemp;
  510. }
  511. // convert the utility name into uppercase
  512. CharUpper( pwszUtilityName );
  513. // get the temporary buffer
  514. // NOTE: we will restrict this to 80 characters only -- this itself is
  515. // too high memory for this simple text string
  516. pwszBuffer = GetParserTempBuffer( INDEX_TEMP_USAGEHELPER, NULL, 80, TRUE );
  517. if ( pwszBuffer == NULL )
  518. {
  519. FreeMemory( &pVersionInfo );
  520. OUT_OF_MEMORY();
  521. return FALSE;
  522. }
  523. // prepare the text now
  524. // NOTE: look -- we are passing 79 only in _snwprintf
  525. StringCchPrintfW( pwszBuffer, 80, ERROR_USAGEHELPER, pwszUtilityName );
  526. // releast the memory allocated for version information
  527. FreeMemory( &pVersionInfo );
  528. // return the text
  529. return pwszBuffer;
  530. }
  531. LPCWSTR ExtractMainOption( LPCWSTR pwszOptions, DWORD dwReserved )
  532. /*++
  533. Routine Description:
  534. Our command line parser can handle multiple names for the single option.
  535. But while displaying error messages, it will be weird if we display all
  536. those options when some error occured. To eliminate that, this function
  537. will identify how many options are present in the given options list and
  538. if it finds multiple options, it will extract the first option in the list
  539. and returns to the caller otherwise if it finds only one argument, this
  540. function will just return the option as it is.
  541. Arguments:
  542. [ in ] pwszOptions - List of options seperated by "|" character
  543. [ in ] dwReserved - reserved for future use
  544. Return Value:
  545. NULL - on failure. Call GetLastError() function to know the
  546. cause for the failure.
  547. on success - the first option the list of supplied options will be
  548. returned. If there is only one option, then the same will
  549. be returned.
  550. --*/
  551. {
  552. // local variables
  553. LONG lIndex = 0;
  554. LPWSTR pwszBuffer = NULL;
  555. // clear last error
  556. CLEAR_LAST_ERROR();
  557. // check the input
  558. if ( pwszOptions == NULL || dwReserved != 0 )
  559. {
  560. INVALID_PARAMETER();
  561. return NULL;
  562. }
  563. // search for the option seperator
  564. lIndex = FindChar2( pwszOptions, L'|', TRUE, 0 );
  565. if ( lIndex == -1 )
  566. {
  567. // there are no multiple options
  568. CLEAR_LAST_ERROR();
  569. lIndex = StringLength( pwszOptions, 0 );
  570. }
  571. // get the temporary buffer
  572. // NOTE: get the buffer with more characters to fit in
  573. pwszBuffer = GetParserTempBuffer( INDEX_TEMP_MAINOPTION, NULL, lIndex + 5, TRUE );
  574. if ( pwszBuffer == NULL )
  575. {
  576. OUT_OF_MEMORY();
  577. return NULL;
  578. }
  579. // now extract the main option
  580. // NOTE: observe the (lIndex + 2) in the StringConcat function call
  581. // that plays the trick of extracting the main option
  582. StringCopy( pwszBuffer, L"/", lIndex + 1 );
  583. StringConcat( pwszBuffer, pwszOptions, lIndex + 2 );
  584. // return
  585. return pwszBuffer;
  586. }
  587. BOOL VerifyParserOptions( LONG* plDefaultIndex,
  588. DWORD dwCount,
  589. PTCMDPARSER2 pcmdOptions )
  590. /*++
  591. Routine Description:
  592. Checks the validity of the parsing instructions passed by the caller.
  593. Arguments:
  594. [ out ] plDefaultIndex - Updates the variable with default option
  595. index.
  596. [ in ] dwCount - Specifies the count of parser structures
  597. passed to this function.
  598. [ in ] pcmdoptions - array of parser structures
  599. Return Value:
  600. TRUE - if all the data passed to this function is valid
  601. FALSE - if any of the data is not correct. This also sets the last
  602. error ERROR_INVALID_PARAMETER.
  603. --*/
  604. {
  605. // local variables
  606. DWORD dw = 0;
  607. DWORD64 dwFlags = 0;
  608. BOOL bUsage = FALSE;
  609. PTCMDPARSER2 pcmdparser = NULL;
  610. // clear last error
  611. CLEAR_LAST_ERROR();
  612. // check the input
  613. if ( dwCount != 0 && pcmdOptions == NULL )
  614. {
  615. INVALID_PARAMETER();
  616. return FALSE;
  617. }
  618. // ...
  619. if ( plDefaultIndex == NULL )
  620. {
  621. INVALID_PARAMETER();
  622. return FALSE;
  623. }
  624. else
  625. {
  626. *plDefaultIndex = -1;
  627. }
  628. // loop thru each option data and verify
  629. for( dw = 0; dw < dwCount; dw++ )
  630. {
  631. pcmdparser = pcmdOptions + dw;
  632. // safety check
  633. if ( pcmdparser == NULL )
  634. {
  635. UNEXPECTED_ERROR();
  636. return FALSE;
  637. }
  638. // verify the signature
  639. if ( StringCompareA( pcmdparser->szSignature,
  640. cszParserSignature, TRUE, 0 ) != 0 )
  641. {
  642. INVALID_PARAMETER();
  643. return FALSE;
  644. }
  645. // ...
  646. dwFlags = pcmdparser->dwFlags;
  647. if ( pcmdparser->dwReserved != 0 ||
  648. pcmdparser->pReserved1 != NULL ||
  649. pcmdparser->pReserved2 != NULL ||
  650. pcmdparser->pReserved3 != NULL )
  651. {
  652. INVALID_PARAMETER();
  653. return FALSE;
  654. }
  655. // check the contents of pwszOptions
  656. // this can be NULL (or) empty only when dwFlags has CP2_DEFAULT
  657. if ( ((dwFlags & CP2_DEFAULT) == 0) &&
  658. (pcmdparser->pwszOptions == NULL ||
  659. lstrlen( pcmdparser->pwszOptions ) == 0) )
  660. {
  661. INVALID_PARAMETER();
  662. return FALSE;
  663. }
  664. // usage flag can be specified only for boolean types
  665. if ( (dwFlags & CP2_USAGE) && pcmdparser->dwType != CP_TYPE_BOOLEAN )
  666. {
  667. INVALID_PARAMETER();
  668. return FALSE;
  669. }
  670. // CP2_USAGE can be specified only once
  671. if ( dwFlags & CP2_USAGE )
  672. {
  673. if ( bUsage == TRUE )
  674. {
  675. // help switch can be specified only once
  676. INVALID_PARAMETER();
  677. return FALSE;
  678. }
  679. else
  680. {
  681. bUsage = TRUE;
  682. }
  683. }
  684. // CP2_DEFAULT can be specified only once
  685. if ( dwFlags & CP2_DEFAULT )
  686. {
  687. if ( *plDefaultIndex != -1 )
  688. {
  689. // default switch can be specified only once
  690. INVALID_PARAMETER();
  691. return FALSE;
  692. }
  693. else
  694. {
  695. *plDefaultIndex = (LONG) dw;
  696. }
  697. }
  698. // CP2_VALUE_OPTIONAL is not allowed along with
  699. // CP2_MODE_VALUES
  700. // if ( (dwFlags & CP2_VALUE_OPTIONAL) && (dwFlags & CP2_MODE_VALUES) )
  701. // {
  702. // INVALID_PARAMETER();
  703. // return FALSE;
  704. // }
  705. // CP2_USAGE and CP2_DEFAULT cannot be specified on the same index
  706. if ( (dwFlags & CP2_USAGE) && (dwFlags & CP2_DEFAULT) )
  707. {
  708. INVALID_PARAMETER();
  709. return FALSE;
  710. }
  711. // check the data type
  712. switch( pcmdparser->dwType )
  713. {
  714. case CP_TYPE_TEXT:
  715. {
  716. if ( dwFlags & CP2_ALLOCMEMORY )
  717. {
  718. // mode should not be any array
  719. if ( (dwFlags & CP2_MODE_ARRAY) || pcmdparser->pValue != NULL )
  720. {
  721. INVALID_PARAMETER();
  722. return FALSE;
  723. }
  724. // check the length attribute
  725. if ( pcmdparser->dwLength != 0 && pcmdparser->dwLength < 2 )
  726. {
  727. INVALID_PARAMETER();
  728. return FALSE;
  729. }
  730. }
  731. else
  732. {
  733. if ( pcmdparser->pValue == NULL )
  734. {
  735. // invalid memory reference
  736. INVALID_PARAMETER();
  737. return FALSE;
  738. }
  739. if ( dwFlags & CP2_MODE_ARRAY )
  740. {
  741. if ( IsValidArray( *((PTARRAY) pcmdparser->pValue) )== FALSE )
  742. {
  743. INVALID_PARAMETER();
  744. return FALSE;
  745. }
  746. }
  747. }
  748. if ( (dwFlags & CP2_MODE_VALUES) &&
  749. (pcmdparser->pwszValues == NULL) )
  750. {
  751. INVALID_PARAMETER();
  752. return FALSE;
  753. }
  754. if ( (dwFlags & CP2_MODE_ARRAY) == 0 )
  755. {
  756. if ( pcmdparser->dwCount != 1 ||
  757. (dwFlags & CP2_VALUE_NODUPLICATES) ||
  758. ( ((dwFlags & CP2_ALLOCMEMORY) == 0) &&
  759. pcmdparser->dwLength < 2 ) )
  760. {
  761. INVALID_PARAMETER();
  762. return FALSE;
  763. }
  764. }
  765. // ...
  766. break;
  767. }
  768. case CP_TYPE_NUMERIC:
  769. case CP_TYPE_UNUMERIC:
  770. case CP_TYPE_FLOAT:
  771. case CP_TYPE_DOUBLE:
  772. {
  773. // currently not implemented
  774. if ( dwFlags & CP2_MODE_VALUES )
  775. {
  776. SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
  777. return FALSE;
  778. }
  779. if ( (dwFlags & CP2_ALLOCMEMORY) ||
  780. (dwFlags & CP2_VALUE_TRIMINPUT) ||
  781. (dwFlags & CP2_VALUE_NONULL) )
  782. {
  783. // memory allocation will not be accepted for
  784. // these data types
  785. INVALID_PARAMETER();
  786. return FALSE;
  787. }
  788. // check the pointer
  789. if ( pcmdparser->pValue == NULL )
  790. {
  791. // invalid memory reference
  792. INVALID_PARAMETER();
  793. return FALSE;
  794. }
  795. // if the value acceptance mode is array, check that
  796. if ( dwFlags & CP2_MODE_ARRAY )
  797. {
  798. if ( IsValidArray( *((PTARRAY) pcmdparser->pValue) ) == FALSE )
  799. {
  800. INVALID_PARAMETER();
  801. return FALSE;
  802. }
  803. }
  804. else if ( (pcmdparser->dwCount > 1) ||
  805. (dwFlags & CP2_VALUE_NODUPLICATES) )
  806. {
  807. INVALID_PARAMETER();
  808. return FALSE;
  809. }
  810. if ( (dwFlags & CP2_MODE_VALUES) &&
  811. pcmdparser->pwszValues == NULL )
  812. {
  813. INVALID_PARAMETER();
  814. return FALSE;
  815. }
  816. // ...
  817. break;
  818. }
  819. case CP_TYPE_CUSTOM:
  820. {
  821. if ( pcmdparser->pFunction == NULL )
  822. {
  823. INVALID_PARAMETER();
  824. return FALSE;
  825. }
  826. // if the custom function data is NULL, assign the current
  827. // object itself to it
  828. if ( pcmdparser->pFunctionData == NULL )
  829. {
  830. pcmdparser->pFunctionData = pcmdparser;
  831. }
  832. // ...
  833. break;
  834. }
  835. case CP_TYPE_DATE:
  836. case CP_TYPE_TIME:
  837. case CP_TYPE_DATETIME:
  838. // currently not supported
  839. SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
  840. return FALSE;
  841. case CP_TYPE_BOOLEAN:
  842. {
  843. // no flags are allowed for this type
  844. if ( (dwFlags & CP2_MODE_MASK) || (dwFlags & CP2_VALUE_MASK) )
  845. {
  846. INVALID_PARAMETER();
  847. return FALSE;
  848. }
  849. // CP2_USAGE and CP2_CASESENSITIVE
  850. // are the only two flags that can be associated with this
  851. // type of options
  852. if ( dwFlags & ( ~(CP2_USAGE | CP2_CASESENSITIVE) ) )
  853. {
  854. INVALID_PARAMETER();
  855. return FALSE;
  856. }
  857. // ...
  858. break;
  859. }
  860. default:
  861. INVALID_PARAMETER();
  862. return FALSE;
  863. }
  864. // init the actuals to 0
  865. pcmdparser->dwActuals = 0;
  866. }
  867. // everything went fine -- success
  868. return TRUE;
  869. }
  870. BOOL ParseAndSaveOptionValue( LPCWSTR pwszOption,
  871. LPCWSTR pwszValue,
  872. TPARSERSAVE_DATA* pSaveData )
  873. /*++
  874. Routine Description:
  875. Processes the value and saves the data back into the memory location
  876. passed by the caller via PARSER structure.
  877. Arguments:
  878. [ in ] pwszOption - option specified at the command prompt
  879. [ in ] pwszValue - value that needs to be assciated with option.
  880. Return Value:
  881. --*/
  882. {
  883. // local variables
  884. LONG lIndex = 0, lValue = 0;
  885. DWORD dwLength = 0, dwValue = 0;
  886. float fValue = 0.0f;
  887. double dblValue = 0.0f;
  888. BOOL bSigned = FALSE;
  889. DWORD64 dwFlags = 0;
  890. LPVOID pvData = NULL;
  891. LPWSTR pwszBuffer = NULL;
  892. LPCWSTR pwszOptionValues = NULL;
  893. LPCWSTR pwszUsageHelper = NULL;
  894. PTCMDPARSER2 pcmdparser = NULL;
  895. // clear last error
  896. CLEAR_LAST_ERROR();
  897. // check the input
  898. if ( pSaveData == NULL )
  899. {
  900. INVALID_PARAMETER();
  901. SaveLastError();
  902. return FALSE;
  903. }
  904. // extract the structure data into local variables
  905. pcmdparser = pSaveData->pcmdparser;
  906. pwszUsageHelper = pSaveData->pwszUsageHelper;
  907. if ( pcmdparser == NULL || pwszUsageHelper == NULL )
  908. {
  909. INVALID_PARAMETER();
  910. SaveLastError();
  911. return FALSE;
  912. }
  913. // ...
  914. pvData = pcmdparser->pValue;
  915. dwFlags = pcmdparser->dwFlags;
  916. dwLength = pcmdparser->dwLength;
  917. pwszOptionValues = pcmdparser->pwszValues;
  918. // except for the boolean types, for all the other types,
  919. // the value for an option is mandatory except when optional flag is
  920. // explicitly specified
  921. if ( pcmdparser->dwType != CP_TYPE_BOOLEAN )
  922. {
  923. if ( pwszValue == NULL &&
  924. (dwFlags & CP2_VALUE_OPTIONAL) == 0 )
  925. {
  926. REASON_VALUE_EXPECTED( pwszOption, pwszUsageHelper );
  927. INVALID_SYNTAX();
  928. return FALSE;
  929. }
  930. }
  931. // pwszOption can be NULL only if dwFlags contains CP2_DEFAULT
  932. if ( pwszOption == NULL && ((dwFlags & CP2_DEFAULT) == 0) )
  933. {
  934. INVALID_PARAMETER();
  935. SaveLastError();
  936. return FALSE;
  937. }
  938. // determine whether we can make use of the friendly name
  939. if ( pwszOption == NULL ||
  940. (pcmdparser->pwszFriendlyName != NULL &&
  941. pcmdparser->dwType != CP_TYPE_CUSTOM) )
  942. {
  943. pwszOption = pcmdparser->pwszFriendlyName;
  944. }
  945. switch( pcmdparser->dwType )
  946. {
  947. case CP_TYPE_TEXT:
  948. {
  949. // check whether we need to trim the string
  950. if ( pwszValue != NULL &&
  951. (dwFlags & (CP2_MODE_VALUES | CP2_VALUE_TRIMINPUT)) )
  952. {
  953. if ( (pwszBuffer = GetParserTempBuffer(
  954. INDEX_TEMP_SAVEDATA,
  955. pwszValue, 0, FALSE )) == NULL )
  956. {
  957. OUT_OF_MEMORY();
  958. SaveLastError();
  959. return FALSE;
  960. }
  961. // trim the contents
  962. pwszValue = TrimString2( pwszBuffer, L" \t", TRIM_ALL );
  963. if ( GetLastError() != NO_ERROR )
  964. {
  965. // unexpected error occured
  966. SaveLastError();
  967. return FALSE;
  968. }
  969. }
  970. // check whether the value is in the allowed list -- if needed
  971. if ( dwFlags & CP2_MODE_VALUES )
  972. {
  973. // check the value for NULL
  974. if ( pwszValue == NULL )
  975. {
  976. // CP2_MODE_VALUES takes the precedence over CP2_VALUE_OPTIONAL
  977. // INVALID_SYNTAX();
  978. // SaveLastError();
  979. return TRUE;
  980. }
  981. if ( InString( pwszValue, pwszOptionValues, TRUE ) == FALSE )
  982. {
  983. REASON_VALUE_NOTINLIST( pwszValue, pwszOption, pwszUsageHelper );
  984. INVALID_SYNTAX();
  985. return FALSE;
  986. }
  987. }
  988. // check the pwszValue argument -- if it is null,
  989. // just return as success -- this is 'coz the current argument
  990. // has value optional flag
  991. if ( pwszValue == NULL )
  992. {
  993. return TRUE;
  994. }
  995. // check for non-null (if requested)
  996. if ( (dwFlags & CP2_VALUE_NONULL) && lstrlen( pwszValue ) == 0 )
  997. {
  998. REASON_NULLVALUE( pwszOption, pwszUsageHelper );
  999. INVALID_SYNTAX();
  1000. return FALSE;
  1001. }
  1002. // check the mode of the input
  1003. if ( dwFlags & CP2_MODE_ARRAY )
  1004. {
  1005. // if the mode is array, add to the array
  1006. // but before adding check whether duplicates
  1007. // has to be eliminated or not
  1008. lIndex = -1;
  1009. if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
  1010. {
  1011. // check whether current value already exists in the list or not
  1012. lIndex =
  1013. DynArrayFindString(
  1014. *((PTARRAY) pvData), pwszValue, TRUE, 0 );
  1015. }
  1016. // now add the value to array only if the item doesn't exist in list
  1017. if ( lIndex == -1 )
  1018. {
  1019. if ( DynArrayAppendString( *((PTARRAY) pvData),
  1020. pwszValue, 0 ) == -1 )
  1021. {
  1022. OUT_OF_MEMORY();
  1023. SaveLastError();
  1024. return FALSE;
  1025. }
  1026. }
  1027. }
  1028. else
  1029. {
  1030. // do the length check
  1031. // NOTE: user should specify the value which is one character
  1032. // less than the length allowed
  1033. if ( dwLength != 0 && lstrlen( pwszValue ) >= (LONG) dwLength )
  1034. {
  1035. REASON_LENGTH_EXCEEDED( pwszOption, dwLength - 1 );
  1036. INVALID_SYNTAX();
  1037. return FALSE;
  1038. }
  1039. // allocate memory if requested
  1040. if ( dwFlags & CP2_ALLOCMEMORY )
  1041. {
  1042. dwLength = lstrlen( pwszValue ) + 1;
  1043. pvData = AllocateMemory( dwLength * sizeof( WCHAR ) );
  1044. if ( pvData == NULL )
  1045. {
  1046. OUT_OF_MEMORY();
  1047. SaveLastError();
  1048. return FALSE;
  1049. }
  1050. // ...
  1051. pcmdparser->pValue = pvData;
  1052. }
  1053. // else just do copy
  1054. StringCopy( ( LPWSTR ) pvData, pwszValue, dwLength );
  1055. }
  1056. // break from the switch ... case
  1057. break;
  1058. }
  1059. case CP_TYPE_NUMERIC:
  1060. case CP_TYPE_UNUMERIC:
  1061. {
  1062. // ...
  1063. bSigned = (pcmdparser->dwType == CP_TYPE_NUMERIC);
  1064. // check the pwszValue argument -- if it is null,
  1065. // just return as success -- this is 'coz the current argument
  1066. // has value optional flag
  1067. if ( pwszValue == NULL )
  1068. {
  1069. return TRUE;
  1070. }
  1071. // check whether the value is numeric or not
  1072. if ( StringLength(pwszValue,0) == 0 || IsNumeric( pwszValue, 10, bSigned ) == FALSE )
  1073. {
  1074. //
  1075. // error ... non numeric value
  1076. // but, this option might have an optional value
  1077. // check that flag
  1078. if ( dwFlags & CP2_VALUE_OPTIONAL )
  1079. {
  1080. // yes -- this option takes an optional value
  1081. // so, the next one could be possibly a default
  1082. // option -- we need to confirm this -- 'coz this is
  1083. // very very rare occassion -- but we still need to handle it
  1084. if ( pSaveData->lDefaultIndex != -1 )
  1085. {
  1086. // yes -- the value might be a default argument
  1087. // update the increment accordingly
  1088. pSaveData->dwIncrement = 1;
  1089. return TRUE;
  1090. }
  1091. }
  1092. // all the tests failed -- so
  1093. // set the reason for the failure and return
  1094. REASON_INVALID_NUMERIC( pwszOption, pwszUsageHelper );
  1095. INVALID_SYNTAX();
  1096. return FALSE;
  1097. }
  1098. // convert the values
  1099. if ( bSigned == TRUE )
  1100. {
  1101. lValue = AsLong( pwszValue, 10 );
  1102. }
  1103. else
  1104. {
  1105. dwValue = AsLong( pwszValue, 10 );
  1106. }
  1107. // ***************************************************
  1108. // *** NEED TO ADD THE RANGE CHECKING LOGIC HERE ***
  1109. // ***************************************************
  1110. // check the mode of the input
  1111. if ( dwFlags & CP2_MODE_ARRAY )
  1112. {
  1113. // if the mode is array, add to the array
  1114. // but before adding check whether duplicates
  1115. // has to be eliminated or not
  1116. lIndex = -1;
  1117. if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
  1118. {
  1119. // check whether current value already exists in the list or not
  1120. if ( bSigned == TRUE )
  1121. {
  1122. lIndex = DynArrayFindLong( *((PTARRAY) pvData), lValue );
  1123. }
  1124. else
  1125. {
  1126. lIndex = DynArrayFindDWORD( *((PTARRAY) pvData), dwValue );
  1127. }
  1128. }
  1129. // now add the value to array only if the item doesn't exist in list
  1130. if ( lIndex == -1 )
  1131. {
  1132. if ( bSigned == TRUE )
  1133. {
  1134. lIndex = DynArrayAppendLong( *((PTARRAY) pvData), lValue );
  1135. }
  1136. else
  1137. {
  1138. lIndex = DynArrayAppendDWORD( *((PTARRAY) pvData), dwValue );
  1139. }
  1140. if ( lIndex == -1 )
  1141. {
  1142. OUT_OF_MEMORY();
  1143. SaveLastError();
  1144. return FALSE;
  1145. }
  1146. }
  1147. }
  1148. else
  1149. {
  1150. // else just assign
  1151. if ( bSigned == TRUE )
  1152. {
  1153. *( ( LONG* ) pvData ) = lValue;
  1154. }
  1155. else
  1156. {
  1157. *( ( DWORD* ) pvData ) = dwValue;
  1158. }
  1159. }
  1160. // break from the switch ... case
  1161. break;
  1162. }
  1163. case CP_TYPE_FLOAT:
  1164. case CP_TYPE_DOUBLE:
  1165. {
  1166. // check the pwszValue argument -- if it is null,
  1167. // just return as success -- this is 'coz the current argument
  1168. // has value optional flag
  1169. if ( pwszValue == NULL )
  1170. {
  1171. return TRUE;
  1172. }
  1173. // check whether the value is numeric or not
  1174. if ( IsFloatingPoint( pwszValue ) == FALSE )
  1175. {
  1176. //
  1177. // error ... non floating point value
  1178. // but, this option might have an optional value
  1179. // check that flag
  1180. if ( dwFlags & CP2_VALUE_OPTIONAL )
  1181. {
  1182. // yes -- this option takes an optional value
  1183. // so, the next one could be possibly a default
  1184. // option -- we need to confirm this -- 'coz this is
  1185. // very very rare occassion -- but we still need to handle it
  1186. if ( pSaveData->lDefaultIndex != -1 )
  1187. {
  1188. // yes -- the value might be a default argument
  1189. // update the increment accordingly
  1190. pSaveData->dwIncrement = 1;
  1191. return TRUE;
  1192. }
  1193. }
  1194. // all the tests failed -- so
  1195. // set the reason for the failure and return
  1196. REASON_INVALID_FLOAT( pwszOption, pwszUsageHelper );
  1197. INVALID_SYNTAX();
  1198. return FALSE;
  1199. }
  1200. // convert the values
  1201. if ( pcmdparser->dwType == CP_TYPE_FLOAT )
  1202. {
  1203. fValue = (float) AsFloat( pwszValue );
  1204. }
  1205. else
  1206. {
  1207. dblValue = AsFloat( pwszValue );
  1208. }
  1209. // ***************************************************
  1210. // *** NEED TO ADD THE RANGE CHECKING LOGIC HERE ***
  1211. // ***************************************************
  1212. // check the mode of the input
  1213. if ( dwFlags & CP2_MODE_ARRAY )
  1214. {
  1215. // if the mode is array, add to the array
  1216. // but before adding check whether duplicates
  1217. // has to be eliminated or not
  1218. lIndex = -1;
  1219. if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
  1220. {
  1221. // check whether current value already exists in the list or not
  1222. if ( pcmdparser->dwType == CP_TYPE_FLOAT )
  1223. {
  1224. lIndex = DynArrayFindFloat( *((PTARRAY) pvData), fValue );
  1225. }
  1226. else
  1227. {
  1228. lIndex = DynArrayFindDouble( *((PTARRAY) pvData), dblValue );
  1229. }
  1230. }
  1231. // now add the value to array only if the item doesn't exist in list
  1232. if ( lIndex == -1 )
  1233. {
  1234. if ( pcmdparser->dwType == CP_TYPE_FLOAT )
  1235. {
  1236. lIndex = DynArrayAppendFloat( *((PTARRAY) pvData), fValue );
  1237. }
  1238. else
  1239. {
  1240. lIndex = DynArrayAppendDouble( *((PTARRAY) pvData), dblValue );
  1241. }
  1242. if ( lIndex == -1 )
  1243. {
  1244. OUT_OF_MEMORY();
  1245. SaveLastError();
  1246. return FALSE;
  1247. }
  1248. }
  1249. }
  1250. else
  1251. {
  1252. // else just assign
  1253. if ( pcmdparser->dwType == CP_TYPE_FLOAT )
  1254. {
  1255. *( ( float* ) pvData ) = fValue;
  1256. }
  1257. else
  1258. {
  1259. *( ( double* ) pvData ) = dblValue;
  1260. }
  1261. }
  1262. // break from the switch ... case
  1263. break;
  1264. }
  1265. case CP_TYPE_CUSTOM:
  1266. {
  1267. // call the custom function
  1268. // and result itself is return value of this function
  1269. return ( *pcmdparser->pFunction)( pwszOption,
  1270. pwszValue, pcmdparser->pFunctionData, &pSaveData->dwIncrement );
  1271. // ...
  1272. break;
  1273. }
  1274. case CP_TYPE_DATE:
  1275. case CP_TYPE_TIME:
  1276. case CP_TYPE_DATETIME:
  1277. {
  1278. // break from the switch ... case
  1279. break;
  1280. }
  1281. case CP_TYPE_BOOLEAN:
  1282. {
  1283. // it is compulsory that the pwszValue should point to NULL
  1284. if ( pwszValue != NULL )
  1285. {
  1286. REASON_VALUENOTALLOWED( pwszOption, pwszUsageHelper );
  1287. INVALID_SYNTAX();
  1288. return FALSE;
  1289. }
  1290. *( ( BOOL* ) pvData ) = TRUE;
  1291. // break from the switch ... case
  1292. break;
  1293. }
  1294. default:
  1295. // nothing -- but should be failure
  1296. {
  1297. INVALID_PARAMETER();
  1298. SaveLastError();
  1299. return FALSE;
  1300. }
  1301. }
  1302. // everything went fine -- success
  1303. return TRUE;
  1304. }
  1305. LONG MatchOption( DWORD dwOptions,
  1306. PTCMDPARSER2 pcmdOptions,
  1307. LPCWSTR pwszOption )
  1308. /*++
  1309. Routine Description:
  1310. Arguments:
  1311. Return Value:
  1312. --*/
  1313. {
  1314. // local variables
  1315. DWORD dw = 0;
  1316. BOOL bIgnoreCase = FALSE;
  1317. PTCMDPARSER2 pcmdparser = NULL;
  1318. // clear last error
  1319. CLEAR_LAST_ERROR();
  1320. // check the input value
  1321. if ( dwOptions == 0 || pcmdOptions == NULL || pwszOption == NULL )
  1322. {
  1323. INVALID_PARAMETER();
  1324. return -1;
  1325. }
  1326. // check whether the passed argument is an option or not.
  1327. // option : starts with '-' or '/'
  1328. if ( IsOption( pwszOption ) == FALSE )
  1329. {
  1330. SetLastError( ERROR_NOT_FOUND );
  1331. return -1;
  1332. }
  1333. // parse thru the list of options and return the appropriate option id
  1334. for( dw = 0; dw < dwOptions; dw++ )
  1335. {
  1336. pcmdparser = pcmdOptions + dw;
  1337. // safety check
  1338. if ( pcmdparser == NULL )
  1339. {
  1340. UNEXPECTED_ERROR();
  1341. SaveLastError();
  1342. return FALSE;
  1343. }
  1344. // determine the case-sensitivity choice
  1345. bIgnoreCase = (pcmdparser->dwFlags & CP2_CASESENSITIVE) ? FALSE : TRUE;
  1346. // ...
  1347. if ( pcmdparser->pwszOptions != NULL &&
  1348. lstrlen( pcmdparser->pwszOptions ) > 0 )
  1349. {
  1350. // find the appropriate option entry in parser list
  1351. if ( InString( pwszOption + 1,
  1352. pcmdparser->pwszOptions, bIgnoreCase) == TRUE )
  1353. {
  1354. return dw; // option matched
  1355. }
  1356. }
  1357. }
  1358. // here we know that option is not found
  1359. SetLastError( ERROR_NOT_FOUND );
  1360. return -1;
  1361. }
  1362. LONG MatchOptionEx( DWORD dwOptions, PTCMDPARSER2 pcmdOptions,
  1363. LPCWSTR pwszOption, TMATCHOPTION_INFO* pMatchInfo )
  1364. /*++
  1365. Routine Description:
  1366. Arguments:
  1367. Return Value:
  1368. --*/
  1369. {
  1370. // local variables
  1371. LONG lValueLength = 0;
  1372. LONG lOptionLength = 0;
  1373. // clear the last error
  1374. CLEAR_LAST_ERROR();
  1375. // check input
  1376. if ( dwOptions == 0 || pcmdOptions == NULL ||
  1377. pwszOption == NULL || pMatchInfo == NULL )
  1378. {
  1379. INVALID_PARAMETER();
  1380. return -1;
  1381. }
  1382. // init
  1383. pMatchInfo->pwszOption = NULL;
  1384. pMatchInfo->pwszValue = NULL;
  1385. // search for ':' seperator
  1386. lOptionLength = FindChar2( pwszOption, L':', TRUE, 0 );
  1387. if ( lOptionLength == -1 )
  1388. {
  1389. return -1;
  1390. }
  1391. // determine the length of value argument
  1392. lValueLength = lstrlen( pwszOption ) - lOptionLength - 1;
  1393. //
  1394. // get the buffers for option and value
  1395. // ( while taking memory, add some buffer to the required length )
  1396. // option
  1397. pMatchInfo->pwszOption = GetParserTempBuffer(
  1398. INDEX_TEMP_SPLITOPTION, NULL, lOptionLength + 5, TRUE );
  1399. if ( pMatchInfo->pwszOption == NULL )
  1400. {
  1401. OUT_OF_MEMORY();
  1402. return -1;
  1403. }
  1404. // value
  1405. pMatchInfo->pwszValue = GetParserTempBuffer(
  1406. INDEX_TEMP_SPLITVALUE, NULL, lValueLength + 5, TRUE );
  1407. if ( pMatchInfo->pwszValue == NULL )
  1408. {
  1409. OUT_OF_MEMORY();
  1410. return -1;
  1411. }
  1412. // copy the values into appropriate buffers (+1 for null character)
  1413. StringCopy( pMatchInfo->pwszOption, pwszOption, lOptionLength + 1 );
  1414. if ( lValueLength != 0 )
  1415. {
  1416. StringCopy( pMatchInfo->pwszValue,
  1417. pwszOption + lOptionLength + 1, lValueLength + 1 );
  1418. }
  1419. // search for the match and return the same result
  1420. return MatchOption( dwOptions, pcmdOptions, pMatchInfo->pwszOption );
  1421. }
  1422. BOOL Parser1FromParser2Stub( LPCWSTR pwszOption,
  1423. LPCWSTR pwszValue,
  1424. LPVOID pData, DWORD* pdwIncrement )
  1425. /*++
  1426. Routine Description:
  1427. Arguments:
  1428. Return Value:
  1429. --*/
  1430. {
  1431. // local variables
  1432. LPCWSTR pwszUsageText = NULL;
  1433. PTCMDPARSER pcmdparser = NULL;
  1434. // check the input
  1435. // NOTE: we dont care about the pwszOption and pwszValue
  1436. if ( pData == NULL || pdwIncrement == NULL )
  1437. {
  1438. INVALID_PARAMETER();
  1439. SaveLastError();
  1440. return FALSE;
  1441. }
  1442. // both pwszOption and pwszValue cannot be NULL at the same time
  1443. if ( pwszOption == NULL && pwszValue == NULL )
  1444. {
  1445. INVALID_PARAMETER();
  1446. SaveLastError();
  1447. return FALSE;
  1448. }
  1449. // extract the "version 1" structure
  1450. pcmdparser = (PTCMDPARSER) pData;
  1451. // in "version 1" of command line parsing, pwszValue and pwszOption
  1452. // should not be NULL -- that means, those both should point to some data
  1453. // but "version 2", pwszOption and pwszValue can be NULL -- but whether
  1454. // they can be NULL or not depends on the data type and the requirement
  1455. // so, in order to port the old code successfully, we need to do the
  1456. // necessary substitutions
  1457. // check the option
  1458. if ( pwszOption == NULL )
  1459. {
  1460. // this means the value is of CP_DEFAULT -- check that
  1461. if ( (pcmdparser->dwFlags & CP_DEFAULT) == 0 )
  1462. {
  1463. // this case -- since the option is not marked as default
  1464. // the option should not be NULL
  1465. INVALID_PARAMETER();
  1466. SaveLastError();
  1467. return FALSE;
  1468. }
  1469. // let value and option point to the same contents(address)
  1470. // this is how the "version 1" used to treat
  1471. pwszOption = pwszValue;
  1472. }
  1473. // now check the value
  1474. else if ( pwszValue == NULL )
  1475. {
  1476. // in "version 1" the value field should never to NULL
  1477. // especially when dealing with CUSTOM types
  1478. // but to be on safe side, check whether user informed that
  1479. // the value is optional
  1480. if ( pcmdparser->dwFlags & CP_VALUE_MANDATORY )
  1481. {
  1482. // get the usage help text
  1483. pwszUsageText = GetParserTempBuffer(
  1484. INDEX_TEMP_USAGEHELPER, NULL, 0, FALSE );
  1485. if ( pwszUsageText == NULL )
  1486. {
  1487. UNEXPECTED_ERROR();
  1488. SaveLastError();
  1489. return FALSE;
  1490. }
  1491. // set the error
  1492. REASON_VALUE_EXPECTED( pwszOption, pwszUsageText );
  1493. INVALID_SYNTAX();
  1494. return FALSE;
  1495. }
  1496. }
  1497. // update the incrementer as 2 --
  1498. // this is default for custom function in "version 1"
  1499. *pdwIncrement = 2;
  1500. // call the custom function and return the value
  1501. return ( *pcmdparser->pFunction)( pwszOption, pwszValue, pcmdparser->pFunctionData );
  1502. }
  1503. BOOL ReleaseAllocatedMemory( DWORD dwOptionsCount,
  1504. PTCMDPARSER2 pcmdOptions )
  1505. /*++
  1506. Routine Description:
  1507. Arguments:
  1508. Return Value:
  1509. --*/
  1510. {
  1511. // local variables
  1512. DWORD dw = 0;
  1513. DWORD dwLastError = 0;
  1514. LPCWSTR pwszReason = NULL;
  1515. PTCMDPARSER2 pcmdparser = NULL;
  1516. // check the input
  1517. if ( dwOptionsCount == 0 || pcmdOptions == NULL )
  1518. {
  1519. return FALSE;
  1520. }
  1521. // save the last error and error text
  1522. dwLastError = GetLastError();
  1523. pwszReason = GetParserTempBuffer( 0, GetReason(), 0, FALSE );
  1524. if ( pwszReason == NULL )
  1525. {
  1526. return FALSE;
  1527. }
  1528. // free the memory allocated by parser for CP2_ALLOCMEMORY
  1529. for( dw = 0; dw < dwOptionsCount; dw++ )
  1530. {
  1531. pcmdparser = pcmdOptions + dw;
  1532. if ( pcmdparser->pValue != NULL &&
  1533. (pcmdparser->dwFlags & CP2_ALLOCMEMORY) )
  1534. {
  1535. FreeMemory( &pcmdparser->pValue );
  1536. }
  1537. }
  1538. // ...
  1539. SetReason( pwszReason );
  1540. SetLastError( dwLastError );
  1541. // return success
  1542. return TRUE;
  1543. }
  1544. //
  1545. // public functions ... exposed to external world
  1546. //
  1547. BOOL DoParseParam2( DWORD dwCount,
  1548. LPCWSTR argv[],
  1549. LONG lSubOptionIndex,
  1550. DWORD dwOptionsCount,
  1551. PTCMDPARSER2 pcmdOptions,
  1552. DWORD dwReserved )
  1553. /*++
  1554. Routine Description:
  1555. Arguments:
  1556. Return Value:
  1557. --*/
  1558. {
  1559. // local variables
  1560. DWORD dw = 0;
  1561. BOOL bUsage = FALSE;
  1562. BOOL bResult = FALSE;
  1563. DWORD dwIncrement = 0;
  1564. LONG lIndex = 0;
  1565. LONG lDefaultIndex = 0;
  1566. LPCWSTR pwszValue = NULL;
  1567. LPCWSTR pwszOption = NULL;
  1568. LPCWSTR pwszUsageText = NULL;
  1569. PTCMDPARSER2 pcmdparser = NULL;
  1570. TMATCHOPTION_INFO matchoptioninfo;
  1571. TPARSERSAVE_DATA parsersavedata;
  1572. // clear the last error
  1573. CLEAR_LAST_ERROR();
  1574. // check the input value
  1575. if ( dwCount == 0 || argv == NULL ||
  1576. dwOptionsCount == 0 || pcmdOptions == NULL )
  1577. {
  1578. SetLastError( ERROR_INVALID_PARAMETER );
  1579. SaveLastError();
  1580. return FALSE;
  1581. }
  1582. // dwReserved should be 0 ( ZERO )
  1583. if ( dwReserved != 0 )
  1584. {
  1585. SetLastError( ERROR_INVALID_PARAMETER );
  1586. SaveLastError();
  1587. return FALSE;
  1588. }
  1589. // check for version compatibility
  1590. if ( IsWin2KOrLater() == FALSE )
  1591. {
  1592. SetReason( ERROR_OS_INCOMPATIBLE );
  1593. SetLastError( ERROR_OLD_WIN_VERSION );
  1594. return FALSE;
  1595. }
  1596. // initialize the global data structure
  1597. if ( InitGlobals() == FALSE )
  1598. {
  1599. SaveLastError();
  1600. return FALSE;
  1601. }
  1602. // utility name retrieval block -- also, prepares the usage helper text
  1603. // TYPE "<utility name> <option> -?" for usage."
  1604. if ( lSubOptionIndex != -1 )
  1605. {
  1606. // validate the sub-option index value
  1607. if ( lSubOptionIndex >= (LONG) dwOptionsCount )
  1608. {
  1609. INVALID_PARAMETER();
  1610. SaveLastError();
  1611. return FALSE;
  1612. }
  1613. // extract the main option
  1614. pwszOption = ExtractMainOption( pcmdOptions[ lSubOptionIndex ].pwszOptions, 0 );
  1615. if ( pwszOption == NULL )
  1616. {
  1617. SaveLastError();
  1618. return FALSE;
  1619. }
  1620. }
  1621. // prepare the usage helper text
  1622. pwszUsageText = PrepareUsageHelperText( pwszOption );
  1623. if ( pwszUsageText == NULL )
  1624. {
  1625. SaveLastError();
  1626. return FALSE;
  1627. }
  1628. // verify the options information
  1629. if ( VerifyParserOptions( &lDefaultIndex, dwOptionsCount, pcmdOptions ) == FALSE )
  1630. {
  1631. SaveLastError();
  1632. return FALSE;
  1633. }
  1634. // Note: though the array starts at index 0 in C, the value at the array
  1635. // index 0 in a command line is the executable name ... so leave
  1636. // and parse the command line from the second parameter
  1637. // i.e; array index 1
  1638. bUsage = FALSE;
  1639. for( dw = 1; dw < dwCount; dw += dwIncrement )
  1640. {
  1641. // init
  1642. dwIncrement = 2;
  1643. pwszOption = argv[ dw ];
  1644. pwszValue = ( (dw + 1) < dwCount ) ? argv[ dw + 1 ] : NULL;
  1645. // find the appropriate the option match
  1646. lIndex = MatchOption( dwOptionsCount, pcmdOptions, pwszOption );
  1647. // check the result
  1648. if ( lIndex == -1 )
  1649. {
  1650. // value might have specified along with the option
  1651. // with ':' as seperator -- check that out
  1652. lIndex = MatchOptionEx( dwOptionsCount,
  1653. pcmdOptions, pwszOption, &matchoptioninfo );
  1654. // check the result
  1655. if ( lIndex == -1 )
  1656. {
  1657. // option is not found - now check atleast
  1658. // default option is present or not -- if it is not found, then error
  1659. if ( lDefaultIndex == -1 )
  1660. {
  1661. SetReason2( 2, ERROR_INVALID_OPTION, pwszOption, pwszUsageText );
  1662. ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
  1663. INVALID_SYNTAX();
  1664. return FALSE;
  1665. }
  1666. else
  1667. {
  1668. // this should be default argument
  1669. lIndex = lDefaultIndex;
  1670. // since we know that the current argument
  1671. // is a default argumen
  1672. // treat the option as value and option as NULL
  1673. pwszValue = pwszOption;
  1674. pwszOption = NULL;
  1675. }
  1676. }
  1677. else
  1678. {
  1679. // option is matched -- it is seperated with ':'
  1680. // so, extract the information from the structure
  1681. pwszOption = matchoptioninfo.pwszOption;
  1682. pwszValue = matchoptioninfo.pwszValue;
  1683. }
  1684. // since the value is in-directly specified along with the option
  1685. // (or as default) we need to process the next argument -- so, update the
  1686. // incrementer accordingly
  1687. dwIncrement = 1;
  1688. }
  1689. // ...
  1690. pcmdparser = pcmdOptions + lIndex;
  1691. // safety check
  1692. if ( pcmdparser == NULL )
  1693. {
  1694. UNEXPECTED_ERROR();
  1695. SaveLastError();
  1696. return FALSE;
  1697. }
  1698. // scoop into the next argument which we are assuming as
  1699. // as the value for the current -- but do this only if the
  1700. // current option type needs that
  1701. if ( pwszValue != NULL && dwIncrement == 2 )
  1702. {
  1703. if ( IsValueNeeded( pcmdparser->dwType ) == TRUE )
  1704. {
  1705. lIndex = MatchOption( dwOptionsCount, pcmdOptions, pwszValue );
  1706. if ( lIndex == -1 )
  1707. {
  1708. lIndex = MatchOptionEx( dwOptionsCount,
  1709. pcmdOptions, pwszValue, &matchoptioninfo );
  1710. }
  1711. // check the result
  1712. if ( lIndex != -1 )
  1713. {
  1714. // so, the next argument cannot be the value for this
  1715. // option -- so ...
  1716. pwszValue = NULL;
  1717. dwIncrement = 1;
  1718. }
  1719. }
  1720. else
  1721. {
  1722. pwszValue = NULL;
  1723. dwIncrement = 1;
  1724. }
  1725. }
  1726. // update the parser data structure
  1727. parsersavedata.pcmdparser = pcmdparser;
  1728. parsersavedata.dwIncrement = dwIncrement;
  1729. parsersavedata.lDefaultIndex = lDefaultIndex;
  1730. parsersavedata.pwszUsageHelper = pwszUsageText;
  1731. // try to save the data
  1732. bResult = ParseAndSaveOptionValue( pwszOption, pwszValue, &parsersavedata );
  1733. // get the increment value -- it might have changed by the data parser
  1734. dwIncrement = parsersavedata.dwIncrement;
  1735. // now check the result
  1736. if ( bResult == FALSE )
  1737. {
  1738. // return
  1739. ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
  1740. return FALSE;
  1741. }
  1742. // check the current option repetition trigger
  1743. if ( pcmdparser->dwCount != 0 &&
  1744. pcmdparser->dwCount == pcmdparser->dwActuals )
  1745. {
  1746. REASON_OPTION_REPEATED( pwszOption,
  1747. pcmdparser->dwCount, pwszUsageText );
  1748. ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
  1749. INVALID_SYNTAX();
  1750. return FALSE;
  1751. }
  1752. // update the option repetition count
  1753. pcmdparser->dwActuals++;
  1754. // check if the usage option is choosed
  1755. if ( pcmdparser->dwFlags & CP2_USAGE )
  1756. {
  1757. bUsage = TRUE;
  1758. }
  1759. }
  1760. // atlast check whether the mandatory options has come or not
  1761. // NOTE: checking of mandatory options will be done only if help is not requested
  1762. if ( bUsage == FALSE )
  1763. {
  1764. for( dw = 0; dw < dwOptionsCount; dw++ )
  1765. {
  1766. // check whether the option has come or not if it is mandatory
  1767. pcmdparser = pcmdOptions + dw;
  1768. // safety check
  1769. if ( pcmdparser == NULL )
  1770. {
  1771. UNEXPECTED_ERROR();
  1772. SaveLastError();
  1773. ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
  1774. return FALSE;
  1775. }
  1776. if ( (pcmdparser->dwFlags & CP2_MANDATORY) && pcmdparser->dwActuals == 0 )
  1777. {
  1778. //
  1779. // mandatory option not exist ... fail
  1780. // ...
  1781. pwszOption = pcmdparser->pwszOptions;
  1782. if ( pwszOption == NULL )
  1783. {
  1784. if ( (pcmdparser->dwFlags & CP2_DEFAULT) == 0 )
  1785. {
  1786. UNEXPECTED_ERROR();
  1787. SaveLastError();
  1788. ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
  1789. return FALSE;
  1790. }
  1791. else
  1792. {
  1793. pwszOption = pcmdparser->pwszFriendlyName;
  1794. }
  1795. }
  1796. else
  1797. {
  1798. // check if user specified any friendly name for this option
  1799. if ( pcmdparser->pwszFriendlyName == NULL )
  1800. {
  1801. pwszOption = ExtractMainOption( pwszOption, 0 );
  1802. if ( pwszOption == NULL )
  1803. {
  1804. SaveLastError();
  1805. ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
  1806. return FALSE;
  1807. }
  1808. }
  1809. else
  1810. {
  1811. pwszOption = pcmdparser->pwszFriendlyName;
  1812. }
  1813. }
  1814. // set the reason for the failure and return
  1815. REASON_MANDATORY_OPTION_MISSING( pwszOption, pwszUsageText );
  1816. ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
  1817. INVALID_SYNTAX();
  1818. return FALSE;
  1819. }
  1820. }
  1821. }
  1822. // parsing complete -- success
  1823. CLEAR_LAST_ERROR();
  1824. return TRUE;
  1825. }
  1826. BOOL DoParseParam( DWORD dwCount,
  1827. LPCTSTR argv[],
  1828. DWORD dwOptionsCount,
  1829. PTCMDPARSER pcmdOptions )
  1830. /*++
  1831. Routine Description:
  1832. Arguments:
  1833. Return Value:
  1834. --*/
  1835. {
  1836. // local variables
  1837. DWORD dw = 0;
  1838. BOOL bResult = FALSE;
  1839. LONG lMainOption = -1;
  1840. DWORD dwLastError = 0;
  1841. LPCWSTR pwszReason = NULL;
  1842. PTCMDPARSER pcmdparser = NULL;
  1843. PTCMDPARSER2 pcmdparser2 = NULL;
  1844. PTCMDPARSER2 pcmdOptions2 = NULL;
  1845. // check the input
  1846. if ( dwCount == 0 || argv == NULL ||
  1847. dwOptionsCount == 0 || pcmdOptions == NULL )
  1848. {
  1849. INVALID_PARAMETER();
  1850. SaveLastError();
  1851. return FALSE;
  1852. }
  1853. // allocate new structure
  1854. pcmdOptions2 = (PTCMDPARSER2) AllocateMemory( dwOptionsCount * sizeof( TCMDPARSER2 ) );
  1855. if ( pcmdOptions2 == NULL )
  1856. {
  1857. OUT_OF_MEMORY();
  1858. SaveLastError();
  1859. return FALSE;
  1860. }
  1861. // update the new structure with the data we have
  1862. for( dw = 0; dw < dwOptionsCount; dw++ )
  1863. {
  1864. // version 1
  1865. pcmdparser = pcmdOptions + dw;
  1866. if ( pcmdparser == NULL )
  1867. {
  1868. UNEXPECTED_ERROR();
  1869. SaveLastError();
  1870. return FALSE;
  1871. }
  1872. // version 2
  1873. pcmdparser2 = pcmdOptions2 + dw;
  1874. if ( pcmdparser2 == NULL )
  1875. {
  1876. UNEXPECTED_ERROR();
  1877. SaveLastError();
  1878. return FALSE;
  1879. }
  1880. // copy the signature
  1881. StringCopyA( pcmdparser2->szSignature,
  1882. cszParserSignature, SIZE_OF_ARRAY( pcmdparser2->szSignature ) );
  1883. // first init the version 2 structure with defaults (except signature)
  1884. pcmdparser2->dwType = 0;
  1885. pcmdparser2->dwFlags = 0;
  1886. pcmdparser2->dwCount = 0;
  1887. pcmdparser2->dwActuals = 0;
  1888. pcmdparser2->pwszOptions = NULL;
  1889. pcmdparser2->pwszFriendlyName = NULL;
  1890. pcmdparser2->pwszValues = NULL;
  1891. pcmdparser2->pValue = NULL;
  1892. pcmdparser2->pFunction = NULL;
  1893. pcmdparser2->pFunctionData = NULL;
  1894. pcmdparser2->dwReserved = 0;
  1895. pcmdparser2->pReserved1 = NULL;
  1896. pcmdparser2->pReserved2 = NULL;
  1897. pcmdparser2->pReserved3 = NULL;
  1898. //
  1899. // option information
  1900. pcmdparser2->pwszOptions = pcmdparser->szOption;
  1901. //
  1902. // value information
  1903. pcmdparser2->pValue = pcmdparser->pValue;
  1904. //
  1905. // type information
  1906. pcmdparser2->dwType = (pcmdparser->dwFlags & CP_TYPE_MASK);
  1907. if ( pcmdparser2->dwType == 0 )
  1908. {
  1909. // NONE
  1910. // this is how BOOLEAN type is specified in version 1
  1911. pcmdparser2->dwType = CP_TYPE_BOOLEAN;
  1912. }
  1913. //
  1914. // length information
  1915. if ( pcmdparser2->dwType == CP_TYPE_TEXT )
  1916. {
  1917. // MAX_STRING_LENGTH is what users assumed
  1918. // as maximum length allowed
  1919. pcmdparser2->dwLength = MAX_STRING_LENGTH;
  1920. }
  1921. //
  1922. // option values
  1923. if ( pcmdparser->dwFlags & CP_MODE_VALUES )
  1924. {
  1925. pcmdparser2->pwszValues = pcmdparser->szValues;
  1926. }
  1927. //
  1928. // count
  1929. pcmdparser2->dwCount = pcmdparser->dwCount;
  1930. //
  1931. // function
  1932. if ( pcmdparser2->dwType == CP_TYPE_CUSTOM )
  1933. {
  1934. //
  1935. // we play bit trick here
  1936. // since the prototype of call back function for version 2 is
  1937. // changed, we can't pass the version 1 prototype directly to
  1938. // the version 2 -- to handle this special case, we will write
  1939. // intermediate function as part of this migration which will
  1940. // act a stub and pass the version 1 structure as data parameter to
  1941. // the version 2 structure
  1942. pcmdparser2->pFunction = Parser1FromParser2Stub;
  1943. pcmdparser2->pFunctionData = pcmdparser;
  1944. // if the "version 1" structure has its data as NULL
  1945. // assign it as its own -- that is how "version 1" used to do
  1946. if ( pcmdparser->pFunctionData == NULL )
  1947. {
  1948. pcmdparser->pFunctionData = pcmdparser;
  1949. }
  1950. }
  1951. //
  1952. // flags
  1953. // CP_VALUE_MANDATORY, CP_IGNOREVALUE flags are discarded
  1954. if ( pcmdparser->dwFlags & CP_MODE_ARRAY )
  1955. {
  1956. pcmdparser2->dwFlags |= CP2_MODE_ARRAY;
  1957. }
  1958. if ( pcmdparser->dwFlags & CP_MODE_VALUES )
  1959. {
  1960. pcmdparser2->dwFlags |= CP2_MODE_VALUES;
  1961. }
  1962. if ( pcmdparser->dwFlags & CP_VALUE_OPTIONAL )
  1963. {
  1964. pcmdparser2->dwFlags |= CP2_VALUE_OPTIONAL;
  1965. }
  1966. if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
  1967. {
  1968. pcmdparser2->dwFlags |= CP2_VALUE_NODUPLICATES;
  1969. }
  1970. if ( pcmdparser->dwFlags & CP_VALUE_NOLENGTHCHECK )
  1971. {
  1972. // actually, in "version 2" this is flag is discarded
  1973. // but make sure that the type for this flag is specified
  1974. // with data type as custom (or) mode array
  1975. if ( ( pcmdparser2->dwType != CP_TYPE_CUSTOM ) &&
  1976. ( pcmdparser2->dwFlags & CP2_MODE_ARRAY ) == 0 )
  1977. {
  1978. INVALID_PARAMETER();
  1979. SaveLastError();
  1980. return FALSE;
  1981. }
  1982. }
  1983. if ( pcmdparser->dwFlags & CP_MAIN_OPTION )
  1984. {
  1985. // the functionality of this flag is handled differently
  1986. // in "version 2" -- so memorize the index of the current option
  1987. lMainOption = dw;
  1988. }
  1989. if ( pcmdparser->dwFlags & CP_USAGE )
  1990. {
  1991. pcmdparser2->dwFlags |= CP2_USAGE;
  1992. }
  1993. if ( pcmdparser->dwFlags & CP_DEFAULT )
  1994. {
  1995. pcmdparser2->dwFlags |= CP2_DEFAULT;
  1996. }
  1997. if ( pcmdparser->dwFlags & CP_MANDATORY )
  1998. {
  1999. pcmdparser2->dwFlags |= CP2_MANDATORY;
  2000. }
  2001. if ( pcmdparser->dwFlags & CP_CASESENSITIVE )
  2002. {
  2003. pcmdparser2->dwFlags |= CP2_CASESENSITIVE;
  2004. }
  2005. }
  2006. // "version 2" structure is ready to use --
  2007. // call the "version 2" of parser
  2008. bResult = DoParseParam2( dwCount, argv, lMainOption,
  2009. dwOptionsCount, pcmdOptions2, 0 );
  2010. // update the "version 1" structure with "version 2" structure data
  2011. for( dw = 0; dw < dwOptionsCount; dw++ )
  2012. {
  2013. // version 1
  2014. pcmdparser = pcmdOptions + dw;
  2015. if ( pcmdparser == NULL )
  2016. {
  2017. UNEXPECTED_ERROR();
  2018. SaveLastError();
  2019. return FALSE;
  2020. }
  2021. // version 2
  2022. pcmdparser2 = pcmdOptions2 + dw;
  2023. if ( pcmdparser2 == NULL )
  2024. {
  2025. UNEXPECTED_ERROR();
  2026. SaveLastError();
  2027. return FALSE;
  2028. }
  2029. // update the actuals
  2030. pcmdparser->dwActuals = pcmdparser2->dwActuals;
  2031. }
  2032. // release the memory that is allocated for "version 2" structure
  2033. // but since the "FreeMemory" will clear the last error
  2034. // we need to save that information before releasing the memory
  2035. dwLastError = GetLastError();
  2036. pwszReason = GetParserTempBuffer( 0, GetReason(), 0, FALSE );
  2037. // now free the memory
  2038. FreeMemory( &pcmdOptions2 );
  2039. // now, check whether we successfully saved the last error or not
  2040. // if not, return out of memory
  2041. if ( pwszReason != NULL )
  2042. {
  2043. SetReason( pwszReason );
  2044. SetLastError( dwLastError );
  2045. }
  2046. else
  2047. {
  2048. bResult = FALSE;
  2049. OUT_OF_MEMORY();
  2050. SaveLastError();
  2051. }
  2052. // return
  2053. return bResult;
  2054. }
  2055. LONG GetOptionCount( LPCWSTR pwszOption,
  2056. DWORD dwCount,
  2057. PTCMDPARSER pcmdOptions )
  2058. /*++
  2059. Routine Description:
  2060. Arguments:
  2061. Return Value:
  2062. --*/
  2063. {
  2064. // local variables
  2065. DWORD dw;
  2066. PTCMDPARSER pcp = NULL;
  2067. // check the input value
  2068. if ( pwszOption == NULL || pcmdOptions == NULL )
  2069. {
  2070. SetLastError( ERROR_INVALID_PARAMETER );
  2071. SaveLastError();
  2072. return -1;
  2073. }
  2074. // traverse thru the loop and find out how many times, the option has repeated at cmd prompt
  2075. for( dw = 0; dw < dwCount; dw++ )
  2076. {
  2077. // get the option information and check whether we are looking for this option or not
  2078. // if the option is matched, return the no. of times the option is repeated at the command prompt
  2079. pcp = pcmdOptions + dw;
  2080. if ( StringCompare( pcp->szOption, pwszOption, TRUE, 0 ) == 0 )
  2081. return pcp->dwActuals;
  2082. }
  2083. // this will / shouldn't occur
  2084. return -1;
  2085. }