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.

1810 lines
62 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. ForFiles.c
  5. Abstract:
  6. This file finds files present in a directory and subdirectory and
  7. calls appropriate function to perform the rest of task.
  8. Author:
  9. V Vijaya Bhaskar
  10. Revision History:
  11. 14-Jun-2001 : Created by V Vijaya Bhaskar ( Wipro Technologies ).
  12. --*/
  13. #include "Global.h"
  14. #include "FileDate.h"
  15. #include "ExecCommand.h"
  16. #include "Forfiles.h"
  17. PStore_Path_Name g_pPathName = NULL ; // Holds path name from where started .
  18. PStore_Path_Name g_pFollowPathName = NULL ; // Holds information about a subdirectory .
  19. LPWSTR g_lpszFileToSearch = NULL; // Holds information about directories and subdirectories .
  20. LPWSTR g_lpszStartPath = NULL ;
  21. /******************************************************************************
  22. ** Function Prototypes **
  23. ******************************************************************************/
  24. BOOL
  25. ProcessOptions(
  26. IN DWORD argc ,
  27. IN LPCWSTR *argv ,
  28. OUT LPWSTR lpszPathName ,
  29. OUT LPWSTR lpszSearchMask ,
  30. OUT LPWSTR lpszCommand ,
  31. OUT LPWSTR lpszDate ,
  32. OUT BOOL *pbRecurse ,
  33. OUT BOOL *pbUsage ,
  34. OUT BOOL *pbSearchFilter
  35. ) ;
  36. BOOL
  37. DisplayUsage(
  38. IN DWORD dwStartUsage ,
  39. IN DWORD dwEndUsage
  40. ) ;
  41. BOOL
  42. DisplayMatchedFiles(
  43. IN LPWSTR lpszPathName ,
  44. IN LPWSTR lpszSearchMask ,
  45. IN LPWSTR lpszCommand ,
  46. IN Valid_File_Date vfdValidFileDate ,
  47. IN DWORD dwDateGreater ,
  48. IN BOOL bRecurse ,
  49. IN BOOL bSearchFilter
  50. ) ;
  51. BOOL
  52. Push(
  53. IN LPWSTR lpszPathName
  54. ) ;
  55. BOOL
  56. Pop(
  57. void
  58. ) ;
  59. BOOL
  60. DisplayFile(
  61. IN OUT BOOL *pbHeader ,
  62. IN LPWSTR lpszPathName ,
  63. IN DWORD dwDateGreater ,
  64. IN LPWSTR lpszCommand ,
  65. IN Valid_File_Date vfdValidFileDate ,
  66. IN OUT BOOL *pbReturn ,
  67. IN LPWSTR lpszSearchMask ,
  68. IN BOOL bRecurse
  69. ) ;
  70. BOOL
  71. FindAndReplaceString(
  72. IN OUT LPWSTR lpszString,
  73. IN LPWSTR lpszFlag
  74. ) ;
  75. BOOL
  76. InitStartPath(
  77. LPWSTR lpszPathName,
  78. LPWSTR lpszCommand
  79. ) ;
  80. BOOL
  81. CheckDateLocalized(
  82. LPWSTR lpwszDate,
  83. DWORD* pdwDateFormat,
  84. LPWSTR lpszDateSep
  85. );
  86. BOOL
  87. PatternMatch(
  88. IN LPWSTR szPat,
  89. IN LPWSTR szFile
  90. );
  91. /*************************************************************************
  92. /* Function Definition starts from here . **
  93. *************************************************************************/
  94. DWORD
  95. __cdecl _tmain(
  96. IN DWORD argc ,
  97. IN LPCWSTR argv[]
  98. )
  99. /*++
  100. Routine Description:
  101. This is the main entry point to this code . Input supplied is
  102. read and appropriate function is called to achieve the functionality .
  103. Arguments:
  104. [ IN ] argc - Contains number of arguments passed at command prompt .
  105. [ IN ] argv - Contains value of each argument in string format .
  106. Return value:
  107. 0 if tool succedded and 1 if tool failed .
  108. --*/
  109. {
  110. // Variables to be passed to other functions .
  111. DWORD dwDateGreater = 2 ;
  112. DWORD dwOldErrorMode = 0;
  113. Valid_File_Date vfdValidFileDate ;
  114. // Variables required to hold command line inputs .
  115. WCHAR szPathName[ MAX_STRING_LENGTH * 2 ] ;
  116. WCHAR szCommand[ MAX_STRING_LENGTH ] ;
  117. BOOL bRecurse = FALSE;
  118. BOOL bUsage = FALSE;
  119. BOOL bSearchFilter = TRUE ;
  120. // Variables required to hold command line inputs .
  121. // Variables to be passed to other functions but are to be
  122. // removed or freed when they are not needed .
  123. LPWSTR lpszDate = NULL ;
  124. LPWSTR lpszSearchMask = NULL ;
  125. // Initialize to zero.
  126. SecureZeroMemory( &vfdValidFileDate, sizeof( Valid_File_Date ) );
  127. SecureZeroMemory( szPathName, MAX_STRING_LENGTH * 2 * sizeof( WCHAR ) );
  128. SecureZeroMemory( szCommand, MAX_STRING_LENGTH * sizeof( WCHAR ) );
  129. // Allocate memory to these variables .
  130. ASSIGN_MEMORY( lpszDate , WCHAR , MAX_STRING_LENGTH ) ;
  131. ASSIGN_MEMORY( lpszSearchMask , WCHAR , MAX_STRING_LENGTH ) ;
  132. // Check whether memory allocation was successfully .
  133. if( ( NULL == lpszSearchMask ) || ( NULL == lpszDate ) )
  134. { // Memory Allocation Failed .
  135. DISPLAY_MEMORY_ALLOC_FAIL();
  136. FREE_MEMORY( lpszDate ) ;
  137. FREE_MEMORY( lpszSearchMask ) ;
  138. ReleaseGlobals() ;
  139. return EXIT_FAILURE ; // 1 errorlevel
  140. }
  141. dwOldErrorMode = SetErrorMode( SEM_FAILCRITICALERRORS );
  142. // Check out whether arguments passed are valid and is the syntax right .
  143. if( FALSE == ProcessOptions( argc, argv, szPathName, lpszSearchMask, szCommand, lpszDate,
  144. &bRecurse, &bUsage, &bSearchFilter ) )
  145. { // Some error occured , free memory allocated , and exit .
  146. FREE_MEMORY( lpszDate ) ;
  147. FREE_MEMORY( lpszSearchMask ) ;
  148. ReleaseGlobals() ;
  149. SetErrorMode( dwOldErrorMode );
  150. return EXIT_FAILURE ; // 1 errorlevel
  151. }
  152. // Check whether /? was specified at command prompt .
  153. if( TRUE == bUsage )
  154. { // Free variable , and display help .
  155. // Since 'dwDateGreater' is no more used, it is used for
  156. // return value only in this block.
  157. dwDateGreater = EXIT_SUCCESS;
  158. if( FALSE == DisplayUsage( IDS_HELP_START , IDS_HELP_END ) )
  159. {
  160. dwDateGreater = EXIT_FAILURE;
  161. }
  162. FREE_MEMORY( lpszDate ) ;
  163. FREE_MEMORY( lpszSearchMask ) ;
  164. ReleaseGlobals() ;
  165. SetErrorMode( dwOldErrorMode );
  166. return dwDateGreater ; // 0 or 1 errorlevel.
  167. }
  168. if( TRUE == SetCurrentDirectory( szPathName ) )
  169. { // Sets process directory to supplied directory .
  170. if( TRUE == InitStartPath( szPathName, szCommand ) )
  171. {// Start path is intialized.
  172. if( TRUE == Push( szPathName ) )
  173. { // Push the current directory .
  174. // 'bUsage' is not needed anymore. Can be used for other purpose.
  175. bUsage = TRUE;
  176. if( 0 != StringLength( lpszDate, 0 ) )
  177. {
  178. bUsage = ValidDateForFile( &dwDateGreater , &vfdValidFileDate , lpszDate );
  179. }
  180. if( TRUE == bUsage )
  181. { // Get date from where to display files .
  182. FREE_MEMORY( lpszDate ) ;
  183. if( TRUE == DisplayMatchedFiles( szPathName , lpszSearchMask , szCommand ,
  184. vfdValidFileDate , dwDateGreater , bRecurse , bSearchFilter ) )
  185. {
  186. FREE_MEMORY( g_lpszStartPath ) ;
  187. FREE_MEMORY( lpszSearchMask ) ;
  188. ReleaseStoreCommand();
  189. ReleaseGlobals() ;
  190. SetErrorMode( dwOldErrorMode );
  191. return EXIT_SUCCESS ;
  192. }
  193. }
  194. }
  195. }
  196. }
  197. else
  198. { // Path supplied was wrong .
  199. dwDateGreater = GetLastError();
  200. switch( dwDateGreater )
  201. {
  202. case ERROR_BAD_NET_NAME:
  203. case ERROR_BAD_NETPATH:
  204. SetLastError( ERROR_INVALID_NAME ) ;
  205. SaveLastError() ;
  206. DISPLAY_GET_REASON() ;
  207. break;
  208. case ERROR_ACCESS_DENIED:
  209. SetLastError( ERROR_ACCESS_DENIED );
  210. SaveLastError() ;
  211. DISPLAY_GET_REASON() ;
  212. break;
  213. case ERROR_INVALID_NAME:
  214. SetLastError( ERROR_DIRECTORY );
  215. SaveLastError() ;
  216. DISPLAY_GET_REASON() ;
  217. break;
  218. default:
  219. ShowMessageEx( stderr, 2, FALSE, L"%1 %2",TAG_ERROR_DISPLAY,
  220. ERROR_DIRECTORY_INVALID ) ;
  221. }
  222. }
  223. // Free nodes in a linked list .
  224. while( NULL != g_pPathName )
  225. {
  226. // More than one node is present .
  227. g_pFollowPathName = g_pPathName ;
  228. g_pPathName = g_pFollowPathName->NextNode ;
  229. FREE_MEMORY( g_pFollowPathName->pszDirName ) ;
  230. FREE_MEMORY( g_pFollowPathName ) ;
  231. }
  232. FREE_MEMORY( g_lpszStartPath ) ;
  233. FREE_MEMORY( lpszSearchMask ) ;
  234. FREE_MEMORY( lpszDate ) ;
  235. ReleaseStoreCommand();
  236. ReleaseGlobals() ;
  237. SetErrorMode( dwOldErrorMode );
  238. return EXIT_FAILURE ;
  239. }
  240. BOOL
  241. ProcessOptions(
  242. IN DWORD argc ,
  243. IN LPCWSTR *argv ,
  244. OUT LPWSTR lpszPathName ,
  245. OUT LPWSTR lpszSearchMask ,
  246. OUT LPWSTR lpszCommand ,
  247. OUT LPWSTR lpszDate ,
  248. OUT BOOL *pbRecurse ,
  249. OUT BOOL *pbUsage ,
  250. OUT BOOL *pbSearchFilter
  251. )
  252. /*++
  253. Routine Description:
  254. Arguments supplied at command line are checked in this function for syntax
  255. or boundary or invalid command etc .
  256. Arguments:
  257. [ IN ] argc - Contains number of arguments passed at command prompt .
  258. [ IN ] *argv - Contains value of each argument in tring format .
  259. [ OUT ] lpszPathName - Contain path of a directory , if /pa option
  260. is specified .
  261. [ OUT ] lpszSearchMask - Contain search mask with which a file is to be
  262. searched , if /m option is specified .
  263. [ OUT ] lpszCommand - Contain command to execute , if /c option is specified .
  264. [ OUT ] lpszDate - Contain date , if /d option is specifed .
  265. [ OUT ] *pbRecurse - Whether to recurse into subdirectories ,
  266. if /sd option specifed .
  267. [ OUT ] *pbUsage - Display help usage , if /? is specifed .
  268. [ OUT ] *pbSearchFilter- Search Filter /m option is specified or not .
  269. Return value:
  270. TRUE if syntax and arguments supplied to option are right else FALSE .
  271. --*/
  272. {
  273. // local variables
  274. LPWSTR lpCharTemp = NULL ; // Pointer To Memory Location .
  275. PTCMDPARSER2 pcmdOption = NULL;
  276. TCMDPARSER2 cmdOptions[ MAX_OPTIONS ];
  277. // If user supplied file name is of 255 characters , then need some extra
  278. // space for copying the directory into it .
  279. WCHAR lpszTemp[ MAX_STRING_LENGTH * 2 ];
  280. if( ( NULL == argv ) ||
  281. ( NULL == lpszPathName ) ||
  282. ( NULL == lpszSearchMask ) ||
  283. ( NULL == lpszCommand ) ||
  284. ( NULL == lpszDate ) ||
  285. ( NULL == pbRecurse ) ||
  286. ( NULL == pbUsage ) ||
  287. ( NULL == pbSearchFilter ) )
  288. {
  289. SetLastError( ERROR_INVALID_PARAMETER );
  290. SaveLastError() ;
  291. DISPLAY_GET_REASON() ;
  292. return FALSE ;
  293. }
  294. // prepare the command options
  295. SecureZeroMemory( cmdOptions, sizeof( TCMDPARSER2 ) * MAX_OPTIONS );
  296. SecureZeroMemory( lpszTemp, MAX_STRING_LENGTH * 2 * sizeof( WCHAR ) );
  297. // -?
  298. pcmdOption = &cmdOptions[ OI_USAGE ] ;
  299. StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
  300. pcmdOption->dwType = CP_TYPE_BOOLEAN;
  301. pcmdOption->pwszOptions = OPTION_USAGE;
  302. pcmdOption->pwszFriendlyName = NULL;
  303. pcmdOption->pwszValues = NULL;
  304. pcmdOption->dwFlags = CP2_USAGE;
  305. pcmdOption->dwCount = 1;
  306. pcmdOption->dwActuals = 0;
  307. pcmdOption->pValue = pbUsage;
  308. pcmdOption->dwLength = 0;
  309. pcmdOption->pFunction = NULL;
  310. pcmdOption->pFunctionData = NULL;
  311. pcmdOption->dwReserved = 0;
  312. pcmdOption->pReserved1 = NULL;
  313. pcmdOption->pReserved2 = NULL;
  314. pcmdOption->pReserved3 = NULL;
  315. // -p
  316. pcmdOption = &cmdOptions[ OI_PATH ] ;
  317. StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
  318. pcmdOption->dwType = CP_TYPE_TEXT;
  319. pcmdOption->pwszOptions = OPTION_PATH;
  320. pcmdOption->pwszFriendlyName = NULL;
  321. pcmdOption->pwszValues = NULL;
  322. pcmdOption->dwFlags = CP2_VALUE_TRIMINPUT|CP2_VALUE_NONULL;
  323. pcmdOption->dwCount = 1;
  324. pcmdOption->dwActuals = 0;
  325. pcmdOption->pValue = lpszPathName;
  326. pcmdOption->dwLength = ( MAX_STRING_LENGTH * 2 );
  327. pcmdOption->pFunction = NULL;
  328. pcmdOption->pFunctionData = NULL;
  329. pcmdOption->dwReserved = 0;
  330. pcmdOption->pReserved1 = NULL;
  331. pcmdOption->pReserved2 = NULL;
  332. pcmdOption->pReserved3 = NULL;
  333. // -m
  334. pcmdOption = &cmdOptions[ OI_SEARCHMASK ] ;
  335. StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
  336. pcmdOption->dwType = CP_TYPE_TEXT;
  337. pcmdOption->pwszOptions = OPTION_SEARCHMASK;
  338. pcmdOption->pwszFriendlyName = NULL;
  339. pcmdOption->pwszValues = NULL;
  340. pcmdOption->dwFlags = CP2_VALUE_TRIMINPUT|CP2_VALUE_NONULL;
  341. pcmdOption->dwCount = 1;
  342. pcmdOption->dwActuals = 0;
  343. pcmdOption->pValue = lpszSearchMask;
  344. pcmdOption->dwLength = MAX_STRING_LENGTH;
  345. pcmdOption->pFunction = NULL;
  346. pcmdOption->pFunctionData = NULL;
  347. pcmdOption->dwReserved = 0;
  348. pcmdOption->pReserved1 = NULL;
  349. pcmdOption->pReserved2 = NULL;
  350. pcmdOption->pReserved3 = NULL;
  351. // -c
  352. pcmdOption = &cmdOptions[ OI_COMMAND ] ;
  353. StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
  354. pcmdOption->dwType = CP_TYPE_TEXT;
  355. pcmdOption->pwszOptions = OPTION_COMMAND;
  356. pcmdOption->pwszFriendlyName = NULL;
  357. pcmdOption->pwszValues = NULL;
  358. pcmdOption->dwFlags = CP2_VALUE_TRIMINPUT|CP2_VALUE_NONULL;
  359. pcmdOption->dwCount = 1;
  360. pcmdOption->dwActuals = 0;
  361. pcmdOption->pValue = lpszCommand;
  362. pcmdOption->dwLength = MAX_STRING_LENGTH;
  363. pcmdOption->pFunction = NULL;
  364. pcmdOption->pFunctionData = NULL;
  365. pcmdOption->dwReserved = 0;
  366. pcmdOption->pReserved1 = NULL;
  367. pcmdOption->pReserved2 = NULL;
  368. pcmdOption->pReserved3 = NULL;
  369. // -d
  370. pcmdOption = &cmdOptions[ OI_DATE ] ;
  371. StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
  372. pcmdOption->dwType = CP_TYPE_TEXT;
  373. pcmdOption->pwszOptions = OPTION_DATE;
  374. pcmdOption->pwszFriendlyName = NULL;
  375. pcmdOption->pwszValues = NULL;
  376. pcmdOption->dwFlags = CP2_VALUE_TRIMINPUT|CP2_VALUE_NONULL;
  377. pcmdOption->dwCount = 1;
  378. pcmdOption->dwActuals = 0;
  379. pcmdOption->pValue = lpszDate;
  380. /*************************************************************
  381. ** If '+' or '-' is not specified then one character buffer **
  382. ** extra is needed. That's why 1 less buffer is passed. If **
  383. ** -1 is removed then overflow of buffer occurs which **
  384. ** incorrect information. **
  385. *************************************************************/
  386. pcmdOption->dwLength = MAX_STRING_LENGTH - 1;
  387. pcmdOption->pFunction = NULL;
  388. pcmdOption->pFunctionData = NULL;
  389. pcmdOption->dwReserved = 0;
  390. pcmdOption->pReserved1 = NULL;
  391. pcmdOption->pReserved2 = NULL;
  392. pcmdOption->pReserved3 = NULL;
  393. // -s
  394. pcmdOption = &cmdOptions[ OI_RECURSE ] ;
  395. StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
  396. pcmdOption->dwType = CP_TYPE_BOOLEAN;
  397. pcmdOption->pwszOptions = OPTION_RECURSE;
  398. pcmdOption->pwszFriendlyName = NULL;
  399. pcmdOption->pwszValues = NULL;
  400. pcmdOption->dwFlags = 0;
  401. pcmdOption->dwCount = 1;
  402. pcmdOption->dwActuals = 0;
  403. pcmdOption->pValue = pbRecurse;
  404. pcmdOption->dwLength = 0;
  405. pcmdOption->pFunction = NULL;
  406. pcmdOption->pFunctionData = NULL;
  407. pcmdOption->dwReserved = 0;
  408. pcmdOption->pReserved1 = NULL;
  409. pcmdOption->pReserved2 = NULL;
  410. pcmdOption->pReserved3 = NULL;
  411. // Do the parsing of supplied input .
  412. if( FALSE == DoParseParam2( argc , argv , -1, SIZE_OF_ARRAY( cmdOptions ),
  413. cmdOptions, 0 ) )
  414. { // Invalid synatx .
  415. DISPLAY_GET_REASON() ;
  416. return FALSE ;
  417. }
  418. // If /? is specified .
  419. if( TRUE == *pbUsage )
  420. {
  421. if( argc > 2 ) // If some other option is specified with the /? .
  422. {
  423. ShowMessageEx( stderr, 3, FALSE, L"%1 %2%3",TAG_ERROR_DISPLAY,
  424. ERROR_INVALID_SYNTAX, ERROR_DISPLAY_HELP ) ;
  425. return FALSE ;
  426. }
  427. else
  428. { // No need of furthur checking , display Help .
  429. return TRUE ;
  430. }
  431. }
  432. // Empty path is not valid
  433. if( 0 == cmdOptions[ OI_PATH ].dwActuals )
  434. {
  435. StringCopy( lpszPathName, _T( "." ), MAX_STRING_LENGTH );
  436. }
  437. /******************************************************************************
  438. /* If option not specified then add a default value if required . **
  439. /*****************************************************************************/
  440. // If UNC path is specified then display error and return.
  441. if( ( _T( '\\' ) == lpszPathName[ 0 ] ) &&
  442. ( _T( '\\' ) == lpszPathName[ 1 ] ))
  443. {
  444. // Check whether specified path is in \\machine\share format.
  445. lpCharTemp = FindAChar( ( lpszPathName + 2 ), _T('\\') );
  446. if( ( NULL == lpCharTemp ) ||
  447. ( ( _T( '\0' ) == lpCharTemp[ 1 ] ) ||
  448. ( _T( '\\' ) == lpCharTemp[ 1 ] ) ) )
  449. {
  450. SetLastError( ERROR_DIRECTORY );
  451. SaveLastError();
  452. DISPLAY_GET_REASON() ;
  453. return FALSE;
  454. }
  455. ShowMessageEx( stderr, 2, FALSE, L"%1 %2", TAG_ERROR_DISPLAY,
  456. ERROR_UNC_PATH_NAME );
  457. return FALSE;
  458. }
  459. else
  460. {
  461. // Check does path name have more than '\' in the specified string.
  462. // Check does path name have any '/' in the specified string.
  463. if( ( NULL != FindSubString( lpszPathName, _T("...") ) ) ||
  464. ( NULL != FindSubString( lpszPathName, DOUBLE_SLASH ) ) ||
  465. ( NULL != FindSubString( lpszPathName, _T( "/" ) ) ) )
  466. {
  467. SetLastError( ERROR_DIRECTORY );
  468. SaveLastError();
  469. DISPLAY_GET_REASON() ;
  470. return FALSE;
  471. }
  472. }
  473. // Check Whether -m Is Specified At Command Prompt , If Not Initialize It To "*"
  474. if( 0 == cmdOptions[ OI_SEARCHMASK ].dwActuals )
  475. {
  476. StringCopy( lpszSearchMask , DEFAULT_SEARCH_MASK, MAX_STRING_LENGTH ) ;
  477. *pbSearchFilter = FALSE ;
  478. }
  479. // Check whether -c is specified at command prompt.
  480. // If not initialize it to "cmd /c echo @file".
  481. if( 0 == cmdOptions[ OI_COMMAND ].dwActuals )
  482. {
  483. StringCopy( lpszCommand , DEFAULT_COMMAND, MAX_STRING_LENGTH ) ;
  484. }
  485. else
  486. {
  487. // Replace Any Hex Value In String To An ASCII Character .
  488. if( FALSE == ReplaceHexToChar( lpszCommand ) )
  489. { // Error is displayed by the called function.
  490. return FALSE;
  491. }
  492. // All flags are converted to lower case.
  493. if( ( FALSE == FindAndReplaceString( lpszCommand, FILE_NAME ) ) ||
  494. ( FALSE == FindAndReplaceString( lpszCommand, FILE_WITHOUT_EXT ) ) ||
  495. ( FALSE == FindAndReplaceString( lpszCommand, EXTENSION ) ) ||
  496. ( FALSE == FindAndReplaceString( lpszCommand, FILE_PATH ) ) ||
  497. ( FALSE == FindAndReplaceString( lpszCommand, RELATIVE_PATH ) ) ||
  498. ( FALSE == FindAndReplaceString( lpszCommand, IS_DIRECTORY ) ) ||
  499. ( FALSE == FindAndReplaceString( lpszCommand, FILE_SIZE ) ) ||
  500. ( FALSE == FindAndReplaceString( lpszCommand, FILE_DATE ) ) ||
  501. ( FALSE == FindAndReplaceString( lpszCommand, FILE_TIME ) ) )
  502. { // Error is displayed by the called function.
  503. return FALSE;
  504. }
  505. }
  506. // Check whether -d is specified.
  507. if( 0 != cmdOptions[ OI_DATE ].dwActuals )
  508. {
  509. // First character must be '+' or '-' .
  510. if( ( PLUS != *lpszDate ) && ( MINUS != *lpszDate ) )
  511. {
  512. StringCopy( lpszTemp, lpszDate, MAX_STRING_LENGTH * 2 );
  513. StringCopy( lpszDate, L"+", MAX_STRING_LENGTH );
  514. StringConcat( lpszDate, lpszTemp, MAX_STRING_LENGTH );
  515. }
  516. // Now string length of 'lpszDate' should be more than 1.
  517. if( ( ( ( PLUS != *lpszDate ) && ( MINUS != *lpszDate ) ) ||
  518. ( 1 >= StringLength( lpszDate, 0 ) ) ) )
  519. { // Invalid Date Specified .
  520. DISPLAY_INVALID_DATE();
  521. return FALSE ;
  522. }
  523. if( FALSE == CheckDateLocalized( lpszDate, NULL, NULL ) )
  524. { // Error is displayed by the called function.
  525. return FALSE ;
  526. }
  527. if( NULL != FindAChar( ( lpszDate + 1 ), _T('/') ) )
  528. { // 'lpszDate' is in '{+|-}MM/dd/yyyy' format.
  529. return TRUE;
  530. }
  531. }
  532. return TRUE ;
  533. }
  534. BOOL
  535. DisplayMatchedFiles(
  536. IN LPWSTR lpszPathName ,
  537. IN LPWSTR lpszSearchMask ,
  538. IN LPWSTR lpszCommand ,
  539. IN Valid_File_Date vfdValidFileDate ,
  540. IN DWORD dwDateGreater ,
  541. IN BOOL bRecurse ,
  542. IN BOOL bSearchFilter
  543. )
  544. /*++
  545. Routine Description:
  546. Path to search for a file is retrived and passed to functions
  547. for furthur processing.
  548. Arguments:
  549. [ IN ] lpszPathName - Contains path of a directory from where files
  550. matching a criteria are to be displayed .
  551. [ IN ] lpszSearchMask - Contains search mask with which a file is to be
  552. searched .
  553. [ IN ] lpszCommand - Contains command to execute .
  554. [ IN ] vfdValidFileDate - Contains a date files created before or after
  555. this date are to be displayed .
  556. [ IN ] dwDateGreater - File created before or after is decided by this
  557. variable .
  558. [ IN ] bRecurse - Whether to recurse into subdirectories .
  559. [ IN ] bSearchFilter - Whether search filter was specified at command
  560. prompt or not .
  561. Return value:
  562. TRUE if succeded in displaying the the obtained files else FALSE .
  563. --*/
  564. {
  565. BOOL bHeader = FALSE ; // Check for whether first item is displayed.
  566. DWORD dwLength = 0; // Length of reallocted string.
  567. BOOL bReturn = FALSE; // Contains return value.
  568. // Loop until data strycture( stack) has no item left in it .
  569. while( NULL != g_pPathName )
  570. {
  571. // Pop a directory from stack which has to be traveresed .
  572. if( FALSE == Pop( ) )
  573. { // Control should come here only when linkedlist have no node to POP .
  574. FREE_MEMORY( g_lpszFileToSearch ) ; // Error message is already displayed .
  575. return FALSE ;
  576. }
  577. // Copy path name to variable which is the only source to get the current working directory .
  578. StringCopy( lpszPathName , g_lpszFileToSearch, MAX_STRING_LENGTH * 2 ) ;
  579. // Sets process directory to supplied directory .
  580. if( FALSE == SetCurrentDirectory( lpszPathName ) )
  581. {
  582. if( ERROR_ACCESS_DENIED == GetLastError())
  583. {
  584. ShowMessageEx( stderr, 6 , FALSE, L"%1 %2%3%4%5%6", TAG_ERROR_DISPLAY,
  585. TAG_ERROR_ACCESS_DENIED, DOUBLE_QUOTES_TO_DISPLAY,
  586. lpszPathName, DOUBLE_QUOTES_TO_DISPLAY, APPEND_AT_END ) ;
  587. }
  588. else
  589. {
  590. SaveLastError() ;
  591. DISPLAY_GET_REASON() ;
  592. }
  593. FREE_MEMORY( g_lpszFileToSearch ) ;
  594. continue ;
  595. }
  596. dwLength = StringLength( g_lpszFileToSearch, 0 ) +
  597. StringLength( lpszSearchMask, 0 ) + EXTRA_MEM ;
  598. // Reallocate to copy search mask to original buffer .
  599. REALLOC_MEMORY( g_lpszFileToSearch , WCHAR , dwLength ) ;
  600. if( NULL == g_lpszFileToSearch )
  601. {
  602. DISPLAY_MEMORY_ALLOC_FAIL() ;
  603. return FALSE ;
  604. }
  605. StringConcat( g_lpszFileToSearch , DEFAULT_SEARCH_MASK, dwLength ) ;
  606. if( FALSE == DisplayFile( &bHeader , lpszPathName , dwDateGreater ,
  607. lpszCommand , vfdValidFileDate , &bReturn ,
  608. lpszSearchMask , bRecurse ) )
  609. {
  610. FREE_MEMORY( g_lpszFileToSearch ) ;
  611. return FALSE ;
  612. }
  613. // Free memory.
  614. FREE_MEMORY( g_lpszFileToSearch ) ;
  615. }
  616. // If nothing is displayed on to the stdout then display error stderr .
  617. if( FALSE == bHeader )
  618. {
  619. // If some search criteria is specified.
  620. if( NO_RESTRICTION_DATE != dwDateGreater )
  621. {
  622. ShowMessageEx( stderr, 2, FALSE, L"%1 %2", TAG_ERROR_DISPLAY,
  623. ERROR_CRITERIA_MISMATCHED ) ;
  624. }
  625. else
  626. { // Search criteria is 'search mask' only.
  627. if( TRUE == bSearchFilter )
  628. {
  629. ShowMessageEx( stderr, 6, FALSE, L"%1 %2%3%4%5%6", TAG_ERROR_DISPLAY,
  630. ERROR_NOFILE_FOUND, DOUBLE_QUOTES_TO_DISPLAY,
  631. _X3(lpszSearchMask), DOUBLE_QUOTES_TO_DISPLAY,
  632. ERROR_NOFILE_FOUND1 ) ;
  633. }
  634. else
  635. {
  636. // Displays output as invalid handle , changed to file not found .
  637. switch( GetLastError() )
  638. {
  639. case ERROR_NO_MORE_FILES:
  640. case ERROR_INVALID_HANDLE:
  641. SetLastError( ERROR_FILE_NOT_FOUND ) ;
  642. SaveLastError() ;
  643. DISPLAY_GET_REASON() ;
  644. break;
  645. default:
  646. SaveLastError() ;
  647. DISPLAY_GET_REASON() ;
  648. }
  649. }
  650. }
  651. bReturn = FALSE ;
  652. }
  653. FREE_MEMORY( g_lpszFileToSearch ) ;
  654. return bReturn ;
  655. }
  656. BOOL
  657. DisplayFile(
  658. IN OUT BOOL *pbHeader ,
  659. IN LPWSTR lpszPathName ,
  660. IN DWORD dwDateGreater ,
  661. IN LPWSTR lpszCommand ,
  662. IN Valid_File_Date vfdValidFileDate ,
  663. IN OUT BOOL *pbReturn ,
  664. IN LPWSTR lpszSearchMask ,
  665. IN BOOL bRecurse
  666. )
  667. /*++
  668. Routine Description:
  669. Find subdirectories and files present in a directory and is passed to
  670. functions for furthur processing, such as , file was created between
  671. specified date or not , and replace the flags present in command
  672. with appropriate values etc.
  673. Arguments:
  674. [ IN OUT ] *pbHeader - Contains value to display errormessage if
  675. nothing is displayed .
  676. [ IN ] lpszPathName - Contains path of a directory from where files
  677. matching a criteria are to be displayed .
  678. [ IN ] dwDateGreater - File created before or after is decided by this
  679. variable .
  680. [ IN ] lpszCommand - Contains command to execute .
  681. [ IN ] vfdValidFileDate - Contains a date files created before or after
  682. this date are to be displayed .
  683. [ IN OUT ] *pbReturn - Contains exit value .
  684. [ IN ] lpszSearchMask - Contains search mask with which a file is to be
  685. searched .
  686. [ IN ] bRecurse - Contains TRUE when child directories are also to
  687. be searched else FALSE.
  688. Return value:
  689. TRUE if succeded in executing the command and finding a file falling in
  690. range of specified date else FALSE .
  691. --*/
  692. {
  693. HANDLE hFindFile = NULL ; // Handle to a file .
  694. WIN32_FIND_DATA wfdFindFile ; // Structure keeping information about the found file .
  695. DWORD dwLength = 0;
  696. if( ( NULL == pbHeader ) ||
  697. ( NULL == pbReturn ) ||
  698. ( NULL == lpszPathName ) ||
  699. ( NULL == lpszSearchMask ) ||
  700. ( NULL == lpszCommand ) )
  701. {
  702. SetLastError( ERROR_INVALID_PARAMETER );
  703. SaveLastError() ;
  704. DISPLAY_GET_REASON() ;
  705. return FALSE ;
  706. }
  707. SecureZeroMemory( &wfdFindFile , sizeof( WIN32_FIND_DATA ) ) ;
  708. // From here onwards directory and file information should be displayed .
  709. if( INVALID_HANDLE_VALUE !=
  710. ( hFindFile = FindFirstFile( g_lpszFileToSearch , &wfdFindFile ) ) )
  711. {
  712. do // Loop until files are present in the directory to display .
  713. {
  714. // Check whether files are "." or "..".
  715. if( ( 0 == StringCompare( wfdFindFile.cFileName , SINGLE_DOT, TRUE, 0 ) ) ||
  716. ( 0 == StringCompare( wfdFindFile.cFileName , DOUBLE_DOT, TRUE, 0 ) ) )
  717. {
  718. continue ;
  719. }
  720. // Check again whether obtained handle points to a directory or file .
  721. // If directory then check whether files in subdir are to be displayed .
  722. if( ( TRUE == bRecurse ) &&
  723. ( 0 != ( wfdFindFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) )
  724. {
  725. dwLength = StringLength( lpszPathName, 0 ) +
  726. StringLength( wfdFindFile.cFileName, 0 ) + EXTRA_MEM ;
  727. // Reallocate memory .
  728. REALLOC_MEMORY( g_lpszFileToSearch , WCHAR , dwLength ) ;
  729. if( NULL == g_lpszFileToSearch )
  730. { // Reallocation failed .
  731. DISPLAY_MEMORY_ALLOC_FAIL() ;
  732. CLOSE_FILE_HANDLE( hFindFile ) ;
  733. return FALSE ;
  734. }
  735. // Copy Path, Concat FileName, Concat '\' as it is required .
  736. StringCopy( g_lpszFileToSearch , lpszPathName, dwLength ) ;
  737. StringConcat( g_lpszFileToSearch , wfdFindFile.cFileName, dwLength ) ;
  738. StringConcat( g_lpszFileToSearch , SINGLE_SLASH, dwLength ) ;
  739. // Copy current path name and store it .
  740. if( FALSE == Push( g_lpszFileToSearch ) )
  741. { // Control comes here when memory allocation fails .
  742. CLOSE_FILE_HANDLE( hFindFile ) ;
  743. return FALSE ;
  744. } // Push Is Over .
  745. }
  746. // Check out whether the file matches pattern specified and if yes then
  747. // file obtained is created on a valid date as specified by user.
  748. if( ( TRUE == PatternMatch( lpszSearchMask, wfdFindFile.cFileName ) ) &&
  749. ( ( NO_RESTRICTION_DATE == dwDateGreater ) ||
  750. ( TRUE == FileDateValid( dwDateGreater , vfdValidFileDate ,
  751. wfdFindFile.ftLastWriteTime ) ) ) )
  752. {
  753. // Execute a command specified at command prompt .
  754. // Reallocate memory .
  755. dwLength = StringLength( lpszCommand, 0 ) + EXTRA_MEM;
  756. REALLOC_MEMORY( g_lpszFileToSearch , WCHAR , dwLength ) ;
  757. if( NULL == g_lpszFileToSearch )
  758. { // Reallocation failed .
  759. DISPLAY_MEMORY_ALLOC_FAIL() ;
  760. CLOSE_FILE_HANDLE( hFindFile ) ;
  761. return FALSE ;
  762. }
  763. // Contains original command to execute .
  764. StringCopy( g_lpszFileToSearch , lpszCommand, dwLength ) ;
  765. // Value can be anything , Filename , Extension name , PathName etc.
  766. if( TRUE == ReplaceTokensWithValidValue( lpszPathName ,
  767. wfdFindFile ) )
  768. { // Tokens are replaced , know execute this command .
  769. if( FALSE == *pbHeader )
  770. {
  771. ShowMessage( stdout , _T( "\n" ) ) ;
  772. }
  773. if( TRUE == ExecuteCommand( ) )
  774. {
  775. *pbReturn = TRUE ;
  776. }
  777. // Make header TRUE because it tell us :
  778. // a) No need to display header .
  779. // b) If FindFirstFile() returns invalidHandle then ,
  780. // display error if Handle == FALSE .
  781. *pbHeader = TRUE ;
  782. }
  783. else
  784. { // Failed to replace tokens , might be memory insuffcient .
  785. *pbReturn = FALSE ;
  786. CLOSE_FILE_HANDLE( hFindFile ) ;
  787. return FALSE ;
  788. }
  789. }
  790. // Continue till no files are present to display.
  791. } while( 0 != FindNextFile( hFindFile , &wfdFindFile ) ) ;
  792. }
  793. CLOSE_FILE_HANDLE( hFindFile ) ; // Close open find file handle .
  794. g_pFollowPathName = NULL ;
  795. return TRUE ;
  796. }
  797. BOOL
  798. Push(
  799. IN LPWSTR szPathName
  800. )
  801. /*++
  802. Routine Description:
  803. Store the path of obtained subdirectory .
  804. Arguments:
  805. [ IN ] szPathName - Contains path of a subdirectory .
  806. Return value:
  807. TRUE if succedded in storing a path else FALSE if failed to get memory.
  808. --*/
  809. {
  810. // Get a temporary variable .
  811. PStore_Path_Name pAddPathName = NULL;
  812. DWORD dwLength = 0;
  813. if( NULL == szPathName )
  814. {
  815. SetLastError( ERROR_INVALID_PARAMETER );
  816. SaveLastError() ;
  817. DISPLAY_GET_REASON() ;
  818. return FALSE ;
  819. }
  820. // Assign memory To Temporary Variable .
  821. ASSIGN_MEMORY( pAddPathName , struct __STORE_PATH_NAME , 1 ) ;
  822. if( NULL == pAddPathName ) // Check memory allocation is successful.
  823. { // Memory allocation is unsuccessful .
  824. DISPLAY_MEMORY_ALLOC_FAIL() ;
  825. return FALSE ;
  826. }
  827. dwLength = StringLength( szPathName, 0 ) + EXTRA_MEM ;
  828. // Assign memory to string variable which is going to store full path name
  829. // of a valid directory .
  830. ASSIGN_MEMORY( pAddPathName->pszDirName , WCHAR , dwLength ) ;
  831. if( NULL == pAddPathName->pszDirName ) // Check memory allocation is successful.
  832. { // Memory allocation was unsuccessful .
  833. DISPLAY_MEMORY_ALLOC_FAIL() ;
  834. FREE_MEMORY( pAddPathName ) ;
  835. return FALSE ;
  836. }
  837. // Copy path name to memory allocated string variable .
  838. StringCopy( ( LPWSTR ) pAddPathName->pszDirName , szPathName, dwLength ) ;
  839. pAddPathName->NextNode = NULL ; // Assign null , had only one subdirectory stored.
  840. // Check global variable is NULL or not .
  841. if( NULL == g_pPathName )
  842. { // Add memory to store path of subdirectory .
  843. g_pPathName = pAddPathName ;
  844. g_pFollowPathName = g_pPathName ;
  845. }
  846. else
  847. {
  848. if( NULL == g_pFollowPathName )
  849. { // Store first obtained subdirectory .
  850. pAddPathName->NextNode = g_pPathName ;
  851. g_pPathName = pAddPathName ;
  852. g_pFollowPathName = g_pPathName ;
  853. }
  854. else
  855. {
  856. // Stroe subdirectory in the middle
  857. pAddPathName->NextNode = g_pFollowPathName->NextNode ;
  858. g_pFollowPathName->NextNode = pAddPathName ;
  859. g_pFollowPathName = pAddPathName ;
  860. }
  861. }
  862. return TRUE ;
  863. }
  864. BOOL
  865. Pop(
  866. void
  867. )
  868. /*++
  869. Routine Description:
  870. Get a subdirectory which has to be searched for a file matching a user
  871. specified criteria .
  872. Arguments:
  873. Return value:
  874. TRUE if successful in getting a path else FALSE if failed to get memory or
  875. if no path is stored .
  876. --*/
  877. {
  878. // Linked list has more than 1 node .
  879. PStore_Path_Name pDelPathName = g_pPathName ;
  880. DWORD dwLength = 0;
  881. // Check whether linked list is having any nodes .
  882. if( NULL == g_pPathName )
  883. { // No nodes present , return False ,
  884. // Should not happen ever . Control should not come here .
  885. DISPLAY_MEMORY_ALLOC_FAIL() ;
  886. return FALSE ;
  887. }
  888. dwLength = StringLength( g_pPathName->pszDirName, 0 ) + EXTRA_MEM;
  889. // Realloc memory and give buffer space in which path name can fix .
  890. ASSIGN_MEMORY( g_lpszFileToSearch , WCHAR , dwLength ) ;
  891. if( NULL == g_lpszFileToSearch )
  892. { // Memory reallocation failed .
  893. DISPLAY_MEMORY_ALLOC_FAIL() ;
  894. return FALSE ;
  895. }
  896. g_pPathName = pDelPathName->NextNode ;
  897. // Memory allocation successful. Copy pathname to the buffer.
  898. StringCopy( g_lpszFileToSearch, pDelPathName->pszDirName, dwLength ) ;
  899. // Free node.
  900. FREE_MEMORY( pDelPathName->pszDirName ) ;
  901. FREE_MEMORY( pDelPathName ) ;
  902. return TRUE ;
  903. }
  904. BOOL
  905. DisplayUsage(
  906. IN DWORD dwStartUsage ,
  907. IN DWORD dwEndUsage
  908. )
  909. /*++
  910. Routine Description:
  911. This function displays help on this tool .
  912. Arguments:
  913. [ IN ] dwStartUsage - Start Resource String ID in Resiurce file for help usage .
  914. [ IN ] dwEndUsage - End Resource String ID in Resiurce file for help usage .
  915. Return value:
  916. If success returns TRUE else FALSE .
  917. --*/
  918. {
  919. DWORD dwLoop = 0 ;
  920. WCHAR wszDisplayStr[ 256 ]; // Contains string to display.
  921. WCHAR wszDateFormat[ 20 ]; // Contains date format w.r.t locale.
  922. WCHAR wszString[ 5 ]; // Contains date seperator w.r.t locale.
  923. WCHAR wszDateDisplay[ 50 ]; // Contains date w.r.t locale for examples in help.
  924. WCHAR wszStaticDateDisplay[ 50 ]; // Contains date w.r.t locale for examples in help.
  925. SYSTEMTIME sysTimeAndDate ;
  926. DWORD dwDateFormat = 0 ;
  927. SecureZeroMemory( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ) * sizeof( WCHAR ) );
  928. SecureZeroMemory( wszDateFormat, SIZE_OF_ARRAY( wszDateFormat ) * sizeof( WCHAR ) );
  929. SecureZeroMemory( wszString, SIZE_OF_ARRAY( wszString ) * sizeof( WCHAR ) );
  930. SecureZeroMemory( wszDateDisplay, SIZE_OF_ARRAY( wszDateDisplay ) * sizeof( WCHAR ) );
  931. SecureZeroMemory( wszStaticDateDisplay, SIZE_OF_ARRAY( wszStaticDateDisplay ) * sizeof( WCHAR ) );
  932. SecureZeroMemory( &sysTimeAndDate, sizeof( SYSTEMTIME ) );
  933. if( FALSE == CheckDateLocalized( NULL, &dwDateFormat, wszString ) )
  934. { // Error is displayed by the called function.
  935. return FALSE;
  936. }
  937. // 'void' is returned so need to check return value.
  938. GetLocalTime( &sysTimeAndDate );
  939. // Fill up all the strings with required values.
  940. switch( dwDateFormat )
  941. {
  942. // 'MM/yyyy/dd' format.
  943. case 1:
  944. StringCchPrintfW( wszDateFormat, SIZE_OF_ARRAY( wszDateFormat ),
  945. FORMAT_1, wszString, wszString );
  946. StringCchPrintfW( wszStaticDateDisplay, SIZE_OF_ARRAY( wszStaticDateDisplay ),
  947. L"01%s2001%s01", wszString, wszString );
  948. StringCchPrintfW( wszDateDisplay, SIZE_OF_ARRAY( wszDateDisplay ),
  949. DATE_FORMAT, sysTimeAndDate.wMonth, wszString,
  950. sysTimeAndDate.wYear, wszString,
  951. sysTimeAndDate.wDay );
  952. break;
  953. // 'dd/MM/yyyy' format.
  954. case 2:
  955. StringCchPrintfW( wszDateFormat, SIZE_OF_ARRAY( wszDateFormat ),
  956. FORMAT_2, wszString, wszString );
  957. StringCchPrintfW( wszStaticDateDisplay, SIZE_OF_ARRAY( wszStaticDateDisplay ),
  958. L"01%s01%s2001", wszString, wszString );
  959. StringCchPrintfW( wszDateDisplay, SIZE_OF_ARRAY( wszDateDisplay ),
  960. DATE_FORMAT, sysTimeAndDate.wDay, wszString,
  961. sysTimeAndDate.wMonth, wszString,
  962. sysTimeAndDate.wYear );
  963. break;
  964. // 'dd/yyyy/MM' format.
  965. case 3:
  966. StringCchPrintfW( wszDateFormat, SIZE_OF_ARRAY( wszDateFormat ),
  967. FORMAT_3, wszString, wszString );
  968. StringCchPrintfW( wszStaticDateDisplay, SIZE_OF_ARRAY( wszStaticDateDisplay ),
  969. L"01%s2001%s01", wszString, wszString );
  970. StringCchPrintfW( wszDateDisplay, SIZE_OF_ARRAY( wszDateDisplay ),
  971. DATE_FORMAT, sysTimeAndDate.wDay, wszString,
  972. sysTimeAndDate.wYear, wszString,
  973. sysTimeAndDate.wMonth );
  974. break;
  975. // 'yyyy/dd/MM' format.
  976. case 4:
  977. StringCchPrintfW( wszDateFormat, SIZE_OF_ARRAY( wszDateFormat ),
  978. FORMAT_4, wszString, wszString );
  979. StringCchPrintfW( wszStaticDateDisplay, SIZE_OF_ARRAY( wszStaticDateDisplay ),
  980. L"2001%s01%s01", wszString, wszString );
  981. StringCchPrintfW( wszDateDisplay, SIZE_OF_ARRAY( wszDateDisplay ),
  982. DATE_FORMAT, sysTimeAndDate.wYear, wszString,
  983. sysTimeAndDate.wDay, wszString,
  984. sysTimeAndDate.wMonth );
  985. break;
  986. // 'yyyy/MM/dd' format.
  987. case 5:
  988. StringCchPrintfW( wszDateFormat, SIZE_OF_ARRAY( wszDateFormat ),
  989. FORMAT_5, wszString, wszString );
  990. StringCchPrintfW( wszStaticDateDisplay, SIZE_OF_ARRAY( wszStaticDateDisplay ),
  991. L"2001%s01%s01", wszString, wszString );
  992. StringCchPrintfW( wszDateDisplay, SIZE_OF_ARRAY( wszDateDisplay ),
  993. DATE_FORMAT, sysTimeAndDate.wYear, wszString,
  994. sysTimeAndDate.wMonth, wszString,
  995. sysTimeAndDate.wDay );
  996. break;
  997. // 'MM/dd/yyyy' format.
  998. default:
  999. StringCchPrintfW( wszDateFormat, SIZE_OF_ARRAY( wszDateFormat ),
  1000. FORMAT_0, wszString, wszString );
  1001. StringCchPrintfW( wszStaticDateDisplay, SIZE_OF_ARRAY( wszStaticDateDisplay ),
  1002. L"01%s01%s2001", wszString, wszString );
  1003. StringCchPrintfW( wszDateDisplay, SIZE_OF_ARRAY( wszDateDisplay ),
  1004. DATE_FORMAT, sysTimeAndDate.wMonth, wszString,
  1005. sysTimeAndDate.wDay, wszString,
  1006. sysTimeAndDate.wYear );
  1007. break;
  1008. }
  1009. // Keep on displaying the help.
  1010. for( dwLoop = dwStartUsage ; dwLoop <= dwEndUsage ; dwLoop++ )
  1011. {
  1012. switch( dwLoop )
  1013. {
  1014. case IDS_HELP_SYNTAX2 :
  1015. SecureZeroMemory( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ) );
  1016. StringCchPrintfW( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ),
  1017. GetResString( dwLoop ), wszDateFormat );
  1018. ShowMessage( stdout , _X(wszDisplayStr) ) ;
  1019. break;
  1020. case IDS_HELP_D1:
  1021. case IDS_HELP_D2:
  1022. case IDS_HELP_D3:
  1023. case IDS_HELP_D4:
  1024. case IDS_HELP_D5:
  1025. case IDS_HELP_D6:
  1026. case IDS_HELP_D7:
  1027. case IDS_HELP_D8:
  1028. case IDS_HELP_D9:
  1029. case IDS_HELP_D10:
  1030. SecureZeroMemory( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ) );
  1031. StringCchPrintfW( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ),
  1032. GetResString( dwLoop ), wszDateFormat );
  1033. ShowMessage( stdout , _X(wszDisplayStr) ) ;
  1034. break;
  1035. case IDS_HELP_E8:
  1036. SecureZeroMemory( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ) );
  1037. StringCchPrintfW( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ),
  1038. GetResString( dwLoop ),
  1039. wszStaticDateDisplay );
  1040. ShowMessage( stdout , _X(wszDisplayStr) ) ;
  1041. break;
  1042. case IDS_HELP_E10:
  1043. SecureZeroMemory( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ) );
  1044. StringCchPrintfW( wszDisplayStr, SIZE_OF_ARRAY( wszDisplayStr ),
  1045. GetResString( dwLoop ), wszDateDisplay );
  1046. ShowMessage( stdout , _X(wszDisplayStr) ) ;
  1047. break;
  1048. default:
  1049. ShowMessage( stdout , GetResString( dwLoop ) ) ;
  1050. }
  1051. }
  1052. // Successful.
  1053. return TRUE;
  1054. }
  1055. BOOL
  1056. FindAndReplaceString(
  1057. IN OUT LPWSTR lpszString,
  1058. IN LPWSTR lpszFlag
  1059. )
  1060. /*++
  1061. Routine Description:
  1062. This function finds flags ( Eg: @file, @path etc.) given by user in any case
  1063. and converts them to lowercase.
  1064. Arguments:
  1065. [ IN ] lpszString - String in which to replace flags to lowercase .
  1066. [ IN ] lpszFlag - Flag to be replaced .
  1067. Return value:
  1068. Returns FALSE if memory allocation fails else returns TRUE .
  1069. --*/
  1070. {
  1071. DWORD dwLength = 0 ;
  1072. DWORD dwIndex = 0 ;
  1073. LPWSTR lpszTemp = NULL ;
  1074. LPWSTR lpszDup = NULL ;
  1075. // Keep record of position or index from where to start next search.
  1076. #ifdef _WIN64
  1077. __int64 dwLocation = 0 ;
  1078. #else
  1079. DWORD dwLocation = 0 ;
  1080. #endif
  1081. if( ( NULL == lpszString ) ||
  1082. ( NULL == lpszFlag ) )
  1083. {
  1084. SetLastError( ERROR_INVALID_PARAMETER );
  1085. SaveLastError() ;
  1086. DISPLAY_GET_REASON() ;
  1087. return FALSE ;
  1088. }
  1089. // Get a duplicate string.
  1090. lpszDup = StrDup( lpszString );
  1091. if( NULL == lpszDup )
  1092. {
  1093. DISPLAY_MEMORY_ALLOC_FAIL() ;
  1094. return FALSE ;
  1095. }
  1096. // Convert duplicate string to lowercase.
  1097. CharLower( lpszDup );
  1098. lpszTemp = lpszDup ;
  1099. // Get Length of duplicate string.
  1100. dwLength = StringLength( lpszFlag, 0 ) ;
  1101. // Loop until all the strings "FLAG" are note replaced.
  1102. // Here string replaced is of original string, duplicate string
  1103. // used to get the index or location of flag.
  1104. while( NULL != ( lpszTemp = FindSubString( lpszTemp, lpszFlag ) ) )
  1105. {
  1106. // Get the index from where the "FLAG" is starting.
  1107. dwLocation = lpszTemp - lpszDup ;
  1108. // Add length of the flag to the string pointer to get ready
  1109. // for next iteration.
  1110. lpszTemp += dwLength ;
  1111. // Check is the character to be replaced is in uppercase.
  1112. for( dwIndex = 1 ; dwIndex < dwLength ; dwIndex++ )
  1113. {
  1114. // Character to be replaced is in uppercase.
  1115. if( ( 65 <= (DWORD)*( lpszString + dwLocation + dwIndex ) ) &&
  1116. ( 90 >= (DWORD)*( lpszString + dwLocation + dwIndex ) ) )
  1117. {
  1118. // Add 32 to convert uppercase letter to lowercase.
  1119. *( lpszString + dwLocation + dwIndex ) += 32 ;
  1120. }
  1121. }
  1122. }
  1123. LocalFree( lpszDup );
  1124. return TRUE ;
  1125. }
  1126. BOOL
  1127. InitStartPath(
  1128. LPWSTR lpszPathName,
  1129. LPWSTR lpszCommand
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. This function copies start path to global variable.
  1134. Arguments:
  1135. [ IN ] lpszPathName - Current process path.
  1136. [ IN ] lpszCommand - Command to execute.
  1137. Return value:
  1138. Returns FALSE if memory allocation fails else returns TRUE .
  1139. --*/
  1140. {
  1141. DWORD dwLength = 0;
  1142. if( ( NULL == lpszPathName ) ||
  1143. ( NULL == lpszCommand ) )
  1144. {
  1145. SetLastError( ERROR_INVALID_PARAMETER );
  1146. SaveLastError() ;
  1147. DISPLAY_GET_REASON() ;
  1148. return FALSE ;
  1149. }
  1150. // If not specified then get current directory path.
  1151. if( 0 == GetCurrentDirectory( ( MAX_STRING_LENGTH * 2 ) , lpszPathName ) )
  1152. {
  1153. SaveLastError() ;
  1154. DISPLAY_GET_REASON() ;
  1155. return FALSE ;
  1156. }
  1157. if( _T( '\\' ) != lpszPathName[ StringLength( lpszPathName, 0 ) - 1 ] )
  1158. { // String should contain a '\' in end. EX: "c:\windows\"
  1159. StringConcat( lpszPathName, _T( "\\" ), MAX_STRING_LENGTH * 2 );
  1160. }
  1161. // Set only if '@relpath' is specified in the command to execute.
  1162. if( NULL != FindSubString( lpszCommand , RELATIVE_PATH ) )
  1163. {
  1164. dwLength = StringLength( lpszPathName, 0 ) + EXTRA_MEM;
  1165. // Allocate memory to global variable .
  1166. ASSIGN_MEMORY( g_lpszStartPath , WCHAR , dwLength ) ;
  1167. // Check whether memory allocation was successfully .
  1168. if( NULL == g_lpszStartPath )
  1169. { // Memory Allocation Failed .
  1170. DISPLAY_MEMORY_ALLOC_FAIL() ;
  1171. return FALSE ;
  1172. }
  1173. // Copied path to global variable .
  1174. StringCopy( g_lpszStartPath , lpszPathName, dwLength ) ;
  1175. }
  1176. return TRUE;
  1177. }
  1178. BOOL
  1179. CheckDateLocalized(
  1180. LPWSTR lpwszDate,
  1181. DWORD* pdwDateFormat,
  1182. LPWSTR lpszDateSep
  1183. )
  1184. /*++
  1185. Routine Description:
  1186. This function converts a date in accordance with locale to mm/dd/yyyy format.
  1187. If date is in {+|-}dd format then some validation is also done.
  1188. Arguments:
  1189. [ IN ] lpwszDate - Contains date.
  1190. [ OUT ] pdwDateFormat - Contains date format being used by current locale.
  1191. [ OUT ] lpszDateSep - Contains seperator.
  1192. Return value:
  1193. Return FALSE if date does not have the separator as locale settings.
  1194. Return TRUE if date is converted to MM/dd/yyyy format successfully.
  1195. --*/
  1196. {
  1197. WCHAR wszString[ MAX_STRING_LENGTH ];
  1198. LCID lcidCurrentUserLocale = 0 ; // Stores current user locale.
  1199. BOOL bLocaleChanged = TRUE;
  1200. LPWSTR lpTemp = NULL;
  1201. LPWSTR lpTemp1 = NULL;
  1202. DWORD dwInteger = 0 ;
  1203. if((( NULL == lpwszDate ) && ( NULL == pdwDateFormat ) && ( NULL == lpszDateSep )) ||
  1204. (( NULL == lpwszDate ) && ( NULL != pdwDateFormat ) && ( NULL == lpszDateSep )) ||
  1205. (( NULL == lpwszDate ) && ( NULL == pdwDateFormat ) && ( NULL != lpszDateSep )))
  1206. {
  1207. SetLastError( ERROR_INVALID_PARAMETER );
  1208. SaveLastError() ;
  1209. DISPLAY_GET_REASON() ;
  1210. return FALSE ;
  1211. }
  1212. SecureZeroMemory( wszString, MAX_STRING_LENGTH * sizeof( WCHAR ) );
  1213. // verify whether console supports the current locale 100% or not
  1214. lcidCurrentUserLocale = GetSupportedUserLocale( &bLocaleChanged ) ;
  1215. // Get date seperator.
  1216. dwInteger = GetLocaleInfo( lcidCurrentUserLocale, LOCALE_SDATE, wszString,
  1217. SIZE_OF_ARRAY( wszString ));
  1218. if( 0 == dwInteger )
  1219. {
  1220. SaveLastError() ;
  1221. DISPLAY_GET_REASON() ;
  1222. return FALSE ;
  1223. }
  1224. // Date seperator is obtained.
  1225. if( NULL != lpszDateSep )
  1226. { // Get date seperator.
  1227. // Date seperator
  1228. StringCopy( lpszDateSep, wszString, 5 );
  1229. }
  1230. else
  1231. {
  1232. // Date seperator is known.
  1233. // Check whether the string contains any
  1234. lpTemp = FindSubString( ( lpwszDate + 1 ), wszString );
  1235. // Replace locale date seperator by '/'.
  1236. if( NULL == lpTemp )
  1237. {
  1238. // Check whether only number is present or some string is present.
  1239. if( FALSE == IsNumeric( lpwszDate, 10, TRUE ) )
  1240. {
  1241. DISPLAY_INVALID_DATE();
  1242. return FALSE ;
  1243. }
  1244. return TRUE;
  1245. }
  1246. else
  1247. {
  1248. *lpTemp = _T( '/' );
  1249. if( 1 < StringLength( wszString, 0 ) )
  1250. {
  1251. StringCopy( ( lpTemp + 1 ), ( lpTemp + StringLength( wszString, 0 ) ),
  1252. 1 + StringLength( ( lpTemp + StringLength( wszString, 0 ) ),
  1253. 0 ) );
  1254. }
  1255. lpTemp1 = FindSubString( lpTemp, wszString );
  1256. if( NULL == lpTemp1 )
  1257. {
  1258. DISPLAY_INVALID_DATE();
  1259. return FALSE ;
  1260. }
  1261. else
  1262. {
  1263. *lpTemp1 = _T( '/' );
  1264. if( 1 < StringLength( wszString, 0 ) )
  1265. {
  1266. StringCopy( ( lpTemp1 + 1 ), ( lpTemp1 + StringLength( wszString, 0 ) ),
  1267. 1 + StringLength( ( lpTemp1 + StringLength( wszString, 0 ) ),
  1268. 0 ) );
  1269. }
  1270. }
  1271. }
  1272. }
  1273. // Get type of date format. 'wszString' should not contain characters more than 80.
  1274. dwInteger = GetLocaleInfo( lcidCurrentUserLocale, LOCALE_IDATE, wszString,
  1275. SIZE_OF_ARRAY( wszString ));
  1276. if( 0 == dwInteger )
  1277. {
  1278. SaveLastError() ;
  1279. DISPLAY_GET_REASON() ;
  1280. return FALSE ;
  1281. }
  1282. // Jump to type of date format.
  1283. switch( wszString[ 0 ] )
  1284. {
  1285. case _T( '0' ):
  1286. dwInteger = GetLocaleInfo( lcidCurrentUserLocale, LOCALE_SSHORTDATE,
  1287. wszString, SIZE_OF_ARRAY( wszString ));
  1288. if( 0 == dwInteger )
  1289. {
  1290. SaveLastError() ;
  1291. DISPLAY_GET_REASON() ;
  1292. return FALSE ;
  1293. }
  1294. lpTemp = StrPBrkW( wszString, L"dy" );
  1295. if( NULL == lpTemp )
  1296. {
  1297. DISPLAY_INVALID_DATE();
  1298. return FALSE ;
  1299. }
  1300. if( _T( 'y' ) == lpTemp[ 0 ] )
  1301. {
  1302. if( NULL != pdwDateFormat )
  1303. {
  1304. *pdwDateFormat = 1;
  1305. return TRUE;
  1306. }
  1307. // Will work only for MM/yyyy/dd.
  1308. StringCopy( wszString, lpwszDate, MAX_STRING_LENGTH );
  1309. // Get dd from MM/yyyy/dd.
  1310. lpTemp = StrRChrW( lpwszDate, NULL, L'/' );
  1311. lpTemp1 = FindAChar( lpwszDate, _T( '/' ) );
  1312. if( ( NULL == lpTemp ) || ( NULL == lpTemp1 ) )
  1313. {
  1314. DISPLAY_INVALID_DATE();
  1315. return FALSE ;
  1316. }
  1317. StringCopy( ( lpTemp1 + 1 ),( lpTemp + 1 ),
  1318. 1 + StringLength( ( lpTemp1 + 1 ), 0 ) );
  1319. StringConcat( lpwszDate, _T( "/" ), MAX_STRING_LENGTH );
  1320. // Now date string is MM/dd/
  1321. lpTemp = StrRChrW( wszString, NULL, _T( '/' ) );
  1322. if( NULL == lpTemp )
  1323. {
  1324. DISPLAY_INVALID_DATE();
  1325. return FALSE ;
  1326. }
  1327. *lpTemp = _T( '\0' );
  1328. // Copy 'yyyy'.
  1329. dwInteger = StringLength( wszString, 0 );
  1330. StringConcat( lpwszDate, ( wszString + dwInteger - 4 ),
  1331. MAX_STRING_LENGTH );
  1332. }
  1333. else
  1334. {
  1335. if( NULL != pdwDateFormat )
  1336. {
  1337. *pdwDateFormat = 0;
  1338. return TRUE;
  1339. }
  1340. }
  1341. return TRUE;
  1342. case _T( '1' ):
  1343. dwInteger = GetLocaleInfo( lcidCurrentUserLocale, LOCALE_SSHORTDATE,
  1344. wszString, SIZE_OF_ARRAY( wszString ));
  1345. if( 0 == dwInteger )
  1346. {
  1347. SaveLastError() ;
  1348. DISPLAY_GET_REASON() ;
  1349. return FALSE ;
  1350. }
  1351. lpTemp = StrPBrkW( wszString, L"My" );
  1352. if( NULL == lpTemp )
  1353. {
  1354. DISPLAY_INVALID_DATE();
  1355. return FALSE ;
  1356. }
  1357. if( _T( 'M' ) == lpTemp[ 0 ] )
  1358. {
  1359. if( NULL != pdwDateFormat )
  1360. {
  1361. *pdwDateFormat = 2;
  1362. return TRUE;
  1363. }
  1364. // Will work only for dd/MM/yyyy
  1365. StringCopy( wszString, lpwszDate, MAX_STRING_LENGTH );
  1366. // Get
  1367. // Pointing at "yyyy"
  1368. lpTemp = StrRChrW( wszString, NULL, _T( '/' ) );
  1369. // Pointing at "MM"
  1370. lpTemp1 = FindAChar( wszString, _T( '/' ) );
  1371. if( ( NULL == lpTemp ) || ( NULL == lpTemp1 ) )
  1372. {
  1373. DISPLAY_INVALID_DATE();
  1374. return FALSE ;
  1375. }
  1376. StringCopy( ( lpwszDate + 1 ), ( lpTemp1 + 1 ), MAX_STRING_LENGTH - 1 );
  1377. StringCopy( lpTemp1, lpTemp,
  1378. MAX_STRING_LENGTH - (DWORD)(DWORD_PTR)(lpTemp1 - wszString));
  1379. lpTemp = FindAChar( lpwszDate, _T( '/' ) );
  1380. if( NULL == lpTemp )
  1381. {
  1382. DISPLAY_INVALID_DATE();
  1383. return FALSE ;
  1384. }
  1385. StringCopy( ( lpTemp + 1 ), ( wszString + 1 ),
  1386. MAX_STRING_LENGTH - (DWORD)(DWORD_PTR)(lpTemp - lpwszDate));
  1387. }
  1388. else
  1389. {
  1390. if( NULL != pdwDateFormat )
  1391. {
  1392. *pdwDateFormat = 3;
  1393. return TRUE;
  1394. }
  1395. // Will work only for dd/yyyy/MM
  1396. StringCopy( wszString, lpwszDate, MAX_STRING_LENGTH );
  1397. // Get MM.
  1398. lpTemp = StrRChr( wszString, NULL, _T( '/' ) );
  1399. if( NULL == lpTemp )
  1400. {
  1401. DISPLAY_INVALID_DATE();
  1402. return FALSE ;
  1403. }
  1404. StringCopy( ( lpwszDate + 1 ), ( lpTemp + 1 ), MAX_STRING_LENGTH - 1 );
  1405. *lpTemp = _T( '\0' );
  1406. // Date string is MM.
  1407. StringConcat( lpwszDate , _T( "/" ), MAX_STRING_LENGTH );
  1408. StringConcat( lpwszDate, ( wszString + 1 ), MAX_STRING_LENGTH );
  1409. }
  1410. return TRUE;
  1411. case _T( '2' ):
  1412. dwInteger = GetLocaleInfo( lcidCurrentUserLocale, LOCALE_SSHORTDATE,
  1413. wszString, SIZE_OF_ARRAY( wszString ));
  1414. if( 0 == dwInteger )
  1415. {
  1416. SaveLastError() ;
  1417. DISPLAY_GET_REASON() ;
  1418. return FALSE ;
  1419. }
  1420. lpTemp = StrPBrkW( wszString, L"Md" );
  1421. if( NULL == lpTemp )
  1422. {
  1423. DISPLAY_INVALID_DATE();
  1424. return FALSE ;
  1425. }
  1426. // Make modification to the date string.
  1427. if( _T( 'd' ) == lpTemp[ 0 ] )
  1428. {
  1429. if( NULL != pdwDateFormat )
  1430. {
  1431. *pdwDateFormat = 4;
  1432. return TRUE;
  1433. }
  1434. // Will work only for yyyy/dd/MM.
  1435. StringCopy( wszString, lpwszDate, MAX_STRING_LENGTH );
  1436. // Get MM
  1437. lpTemp = StrRChr( wszString, NULL, _T( '/' ) );
  1438. if( NULL == lpTemp )
  1439. {
  1440. DISPLAY_INVALID_DATE();
  1441. return FALSE ;
  1442. }
  1443. StringCopy( ( lpwszDate + 1 ), ( lpTemp + 1 ), MAX_STRING_LENGTH -1 );
  1444. StringConcat( ( lpwszDate + 1 ), _T( "/" ), MAX_STRING_LENGTH );
  1445. *lpTemp = _T( '\0' ) ;
  1446. // Get dd, date string contains "yyyy/dd" only.
  1447. lpTemp = StrRChr( wszString, NULL, _T( '/' ) );
  1448. if( NULL == lpTemp )
  1449. {
  1450. DISPLAY_INVALID_DATE();
  1451. return FALSE ;
  1452. }
  1453. StringConcat( lpwszDate, ( lpTemp + 1 ), MAX_STRING_LENGTH );
  1454. StringConcat( ( lpwszDate + 1 ), _T( "/" ), MAX_STRING_LENGTH );
  1455. *lpTemp = _T( '\0' ) ;
  1456. // Get
  1457. StringConcat( lpwszDate, ( wszString + 1 ), MAX_STRING_LENGTH );
  1458. }
  1459. else
  1460. {
  1461. if( NULL != pdwDateFormat )
  1462. {
  1463. *pdwDateFormat = 5;
  1464. return TRUE;
  1465. }
  1466. // Will work only for yyyy/MM/dd.
  1467. StringCopy( wszString, lpwszDate, MAX_STRING_LENGTH );
  1468. StringCopy( lpwszDate + 1, ( wszString + 6 ),MAX_STRING_LENGTH - 1 );
  1469. wszString[ 5 ] = _T( '\0' );
  1470. StringConcat( lpwszDate, _T( "/" ), MAX_STRING_LENGTH );
  1471. StringConcat( lpwszDate, ( wszString + 1 ), MAX_STRING_LENGTH );
  1472. }
  1473. return TRUE;
  1474. default:
  1475. DISPLAY_INVALID_DATE();
  1476. return FALSE ;
  1477. }
  1478. }
  1479. BOOL
  1480. PatternMatch(
  1481. IN LPWSTR szPat,
  1482. IN LPWSTR szFile
  1483. )
  1484. /*++
  1485. Routine Description : This routine is used to check whether file is mathced against
  1486. pattern or not.
  1487. [ IN ] szPat : A string variable pattern against which the file name to be matched.
  1488. [ IN ] szFile : A pattern string which specifies the file name to be matched.
  1489. Return Value : BOOL
  1490. Returns successfully if function is success other wise return failure.
  1491. --*/
  1492. {
  1493. if( ( NULL == szPat ) ||
  1494. ( NULL == szFile ) )
  1495. {
  1496. SetLastError( ERROR_INVALID_PARAMETER );
  1497. SaveLastError() ;
  1498. DISPLAY_GET_REASON() ;
  1499. return FALSE ;
  1500. }
  1501. // Apply recursive pattern match.
  1502. switch( *szPat )
  1503. {
  1504. case '\0':
  1505. return ( *szFile == L'\0' );
  1506. case '?':
  1507. return ( ( *szFile != L'\0' ) && PatternMatch( szPat + 1, szFile + 1 ) );
  1508. case '*':
  1509. do
  1510. {
  1511. if( TRUE == PatternMatch( szPat + 1, szFile ) )
  1512. {
  1513. return TRUE;
  1514. }
  1515. } while( *szFile++ );
  1516. return FALSE;
  1517. default:
  1518. return ( ( toupper( *szFile ) == toupper( *szPat ) ) &&
  1519. PatternMatch( szPat + 1, szFile + 1 ) );
  1520. }
  1521. }
  1522. LPWSTR
  1523. FindAChar(
  1524. IN LPWSTR szString,
  1525. IN WCHAR wCharToFind
  1526. )
  1527. /*++
  1528. Routine Description : This routine is used to find a case sensitive
  1529. character in a string.
  1530. [ IN ] szString : String in which to search for a character.
  1531. [ IN ] wCharTofind : Char to search for.
  1532. Return Value : LPWSTR
  1533. If found a character then memory location of that character will
  1534. be returned else NULL is returned.
  1535. --*/
  1536. {
  1537. if( NULL == szString )
  1538. {
  1539. return NULL;
  1540. }
  1541. return ( StrChrW( szString, wCharToFind ) );
  1542. }
  1543. LPWSTR
  1544. FindSubString(
  1545. IN LPWSTR szString,
  1546. IN LPWSTR szSubString
  1547. )
  1548. /*++
  1549. Routine Description : This routine is used to find a case sensitive
  1550. substring in a string.
  1551. [ IN ] szString : String in which to search for a substring.
  1552. [ IN ] szSubString : SubString to search for.
  1553. Return Value : LPWSTR
  1554. If found a character then memory location of that character will
  1555. be returned else NULL is returned.
  1556. --*/
  1557. {
  1558. if( ( NULL == szString ) ||
  1559. ( NULL == szSubString ) )
  1560. {
  1561. return NULL;
  1562. }
  1563. return ( StrStrW( szString, szSubString ) );
  1564. }