Source code of Windows XP (NT5)
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.

963 lines
29 KiB

  1. /*++
  2. Copyright (c) 1990-2001 Microsoft Corporation
  3. Module Name:
  4. Argument
  5. Abstract:
  6. Argument processing for the XCopy directory copy utility
  7. Author:
  8. Ramon Juan San Andres (ramonsa) 01-May-1991
  9. Notes:
  10. The arguments accepted by the XCopy utility are:
  11. Source directory.- Source path.
  12. Dest. directory.- Destination path.
  13. Archive switch.- Copy files that have their archive bit set
  14. Date.- Copy files modified on or after the specifiec
  15. date.
  16. Empty switch.- Copy directories even if empty. Subdir switch
  17. must also be set.
  18. Modify switch.- Same as Archive switch, but turns off archive
  19. bit in the source file after copying.
  20. Prompt switch.- Prompts before copying each file.
  21. Subdir switch.- Copies also subdirectories, unless they are empty.
  22. (Empty directories are copied if the Empty switch
  23. is set).
  24. Verify switch.- Verifies each copy.
  25. Wait switch.- Wait before starting to copy the files.
  26. Owner switch. - Copy ownership and permissions
  27. Audit switch. - Copy auditing information.
  28. Revision History:
  29. --*/
  30. #include "ulib.hxx"
  31. #include "arg.hxx"
  32. #include "arrayit.hxx"
  33. #include "dir.hxx"
  34. #include "xcopy.hxx"
  35. #include "stringar.hxx"
  36. #include "file.hxx"
  37. #include "filestrm.hxx"
  38. //
  39. // Switch characters. Used for maintaining DOS5 compatibility when
  40. // displaying error messages
  41. //
  42. #define SWITCH_CHARACTERS "dDaAeEmMpPsSvVwW?"
  43. //
  44. // Static variables
  45. //
  46. PARRAY LexArray;
  47. PPATH_ARGUMENT FirstPathArgument = NULL;
  48. PPATH_ARGUMENT FirstQuotedPathArgument = NULL;
  49. PPATH_ARGUMENT SecondPathArgument = NULL;
  50. PPATH_ARGUMENT SecondQuotedPathArgument = NULL;
  51. PFLAG_ARGUMENT ArchiveArgument = NULL;
  52. PTIMEINFO_ARGUMENT DateArgument = NULL;
  53. PFLAG_ARGUMENT DecryptArgument = NULL;
  54. PFLAG_ARGUMENT EmptyArgument = NULL;
  55. PFLAG_ARGUMENT ModifyArgument = NULL;
  56. PFLAG_ARGUMENT PromptArgument = NULL;
  57. PFLAG_ARGUMENT OverWriteArgument = NULL;
  58. PFLAG_ARGUMENT NotOverWriteArgument = NULL;
  59. PFLAG_ARGUMENT SubdirArgument = NULL;
  60. PFLAG_ARGUMENT VerifyArgument = NULL;
  61. PFLAG_ARGUMENT WaitArgument = NULL;
  62. PFLAG_ARGUMENT HelpArgument = NULL;
  63. PFLAG_ARGUMENT ContinueArgument = NULL;
  64. PFLAG_ARGUMENT IntelligentArgument = NULL;
  65. PFLAG_ARGUMENT VerboseArgument = NULL;
  66. PFLAG_ARGUMENT OldArgument = NULL;
  67. PFLAG_ARGUMENT HiddenArgument = NULL;
  68. PFLAG_ARGUMENT ReadOnlyArgument = NULL;
  69. PFLAG_ARGUMENT SilentArgument = NULL;
  70. PFLAG_ARGUMENT NoCopyArgument = NULL;
  71. PFLAG_ARGUMENT StructureArgument = NULL;
  72. PFLAG_ARGUMENT UpdateArgument = NULL;
  73. PFLAG_ARGUMENT CopyAttrArgument = NULL;
  74. PFLAG_ARGUMENT UseShortArgument = NULL;
  75. PFLAG_ARGUMENT RestartableArgument = NULL;
  76. PFLAG_ARGUMENT OwnerArgument = NULL;
  77. PFLAG_ARGUMENT AuditArgument = NULL;
  78. PSTRING_ARGUMENT ExclusionListArgument = NULL;
  79. PSTRING_ARGUMENT InvalidSwitchArgument = NULL;
  80. BOOLEAN HelpSwitch;
  81. //
  82. // Prototypes
  83. //
  84. VOID
  85. XCOPY::SetArguments(
  86. )
  87. /*++
  88. Routine Description:
  89. Obtains the arguments for the XCopy utility
  90. Arguments:
  91. None.
  92. Return Value:
  93. None.
  94. Notes:
  95. --*/
  96. {
  97. PATH_ARGUMENT LocalFirstPathArgument;
  98. PATH_ARGUMENT LocalFirstQuotedPathArgument;
  99. PATH_ARGUMENT LocalSecondPathArgument;
  100. PATH_ARGUMENT LocalSecondQuotedPathArgument;
  101. FLAG_ARGUMENT LocalArchiveArgument;
  102. TIMEINFO_ARGUMENT LocalDateArgument;
  103. FLAG_ARGUMENT LocalOldArgument;
  104. FLAG_ARGUMENT LocalDecryptArgument;
  105. FLAG_ARGUMENT LocalEmptyArgument;
  106. FLAG_ARGUMENT LocalModifyArgument;
  107. FLAG_ARGUMENT LocalPromptArgument;
  108. FLAG_ARGUMENT LocalOverWriteArgument;
  109. FLAG_ARGUMENT LocalNotOverWriteArgument;
  110. FLAG_ARGUMENT LocalSubdirArgument;
  111. FLAG_ARGUMENT LocalVerifyArgument;
  112. FLAG_ARGUMENT LocalWaitArgument;
  113. FLAG_ARGUMENT LocalHelpArgument;
  114. FLAG_ARGUMENT LocalContinueArgument;
  115. FLAG_ARGUMENT LocalIntelligentArgument;
  116. FLAG_ARGUMENT LocalVerboseArgument;
  117. FLAG_ARGUMENT LocalHiddenArgument;
  118. FLAG_ARGUMENT LocalReadOnlyArgument;
  119. FLAG_ARGUMENT LocalSilentArgument;
  120. FLAG_ARGUMENT LocalNoCopyArgument;
  121. FLAG_ARGUMENT LocalStructureArgument;
  122. FLAG_ARGUMENT LocalUpdateArgument;
  123. FLAG_ARGUMENT LocalCopyAttrArgument;
  124. FLAG_ARGUMENT LocalUseShortArgument;
  125. FLAG_ARGUMENT LocalRestartableArgument;
  126. FLAG_ARGUMENT LocalOwnerArgument;
  127. FLAG_ARGUMENT LocalAuditArgument;
  128. STRING_ARGUMENT LocalExclusionListArgument;
  129. STRING_ARGUMENT LocalInvalidSwitchArgument;
  130. ARRAY LocalLexArray;
  131. //
  132. // Set the static global pointers
  133. //
  134. FirstPathArgument = &LocalFirstPathArgument;
  135. FirstQuotedPathArgument = &LocalFirstQuotedPathArgument;
  136. SecondPathArgument = &LocalSecondPathArgument;
  137. SecondQuotedPathArgument = &LocalSecondQuotedPathArgument;
  138. ArchiveArgument = &LocalArchiveArgument;
  139. DateArgument = &LocalDateArgument;
  140. OldArgument = &LocalOldArgument;
  141. DecryptArgument = &LocalDecryptArgument;
  142. EmptyArgument = &LocalEmptyArgument;
  143. ModifyArgument = &LocalModifyArgument;
  144. PromptArgument = &LocalPromptArgument;
  145. OverWriteArgument = &LocalOverWriteArgument;
  146. NotOverWriteArgument = &LocalNotOverWriteArgument;
  147. SubdirArgument = &LocalSubdirArgument;
  148. VerifyArgument = &LocalVerifyArgument;
  149. WaitArgument = &LocalWaitArgument;
  150. HelpArgument = &LocalHelpArgument;
  151. ContinueArgument = &LocalContinueArgument;
  152. IntelligentArgument = &LocalIntelligentArgument;
  153. VerboseArgument = &LocalVerboseArgument;
  154. HiddenArgument = &LocalHiddenArgument;
  155. ReadOnlyArgument = &LocalReadOnlyArgument;
  156. SilentArgument = &LocalSilentArgument;
  157. NoCopyArgument = &LocalNoCopyArgument;
  158. StructureArgument = &LocalStructureArgument;
  159. UpdateArgument = &LocalUpdateArgument;
  160. CopyAttrArgument = &LocalCopyAttrArgument;
  161. UseShortArgument = &LocalUseShortArgument;
  162. ExclusionListArgument = &LocalExclusionListArgument;
  163. InvalidSwitchArgument = &LocalInvalidSwitchArgument;
  164. LexArray = &LocalLexArray;
  165. RestartableArgument = &LocalRestartableArgument;
  166. OwnerArgument = &LocalOwnerArgument;
  167. AuditArgument = &LocalAuditArgument;
  168. //
  169. // Parse the arguments
  170. //
  171. GetArgumentsCmd();
  172. //
  173. // Verify the arguments
  174. //
  175. CheckArgumentConsistency();
  176. LocalLexArray.DeleteAllMembers();
  177. }
  178. VOID
  179. GetSourceAndDestinationPath(
  180. IN OUT PPATH_ARGUMENT FirstPathArgument,
  181. IN OUT PPATH_ARGUMENT FirstQuotedPathArgument,
  182. IN OUT PPATH_ARGUMENT SecondPathArgument,
  183. IN OUT PPATH_ARGUMENT SecondQuotedPathArgument,
  184. IN OUT PARGUMENT_LEXEMIZER ArgLex,
  185. OUT PPATH* SourcePath,
  186. OUT PPATH* DestinationPath
  187. )
  188. /*++
  189. Routine Description:
  190. This routine computes the Source and Destination path from
  191. the given list of arguments.
  192. Arguments:
  193. FirstPathArgument - Supplies the first unquoted path argument.
  194. FirstQuotedPathArgument - Supplies the first quoted path argument.
  195. SecondPathArgument - Supplies the second unquoted path argument.
  196. SecondQuotedPathArgument - Supplies the second quoted path argument.
  197. ArgLex - Supplies the argument lexemizer.
  198. SourcePath - Returns the source path.
  199. DestinationPath - Returns the destination path.
  200. Return Value:
  201. None.
  202. --*/
  203. {
  204. BOOLEAN f, qf, s, qs;
  205. PPATH_ARGUMENT source, destination;
  206. ULONG i;
  207. PWSTRING string, qstring;
  208. f = FirstPathArgument->IsValueSet();
  209. qf = FirstQuotedPathArgument->IsValueSet();
  210. s = SecondPathArgument->IsValueSet();
  211. qs = SecondQuotedPathArgument->IsValueSet();
  212. source = NULL;
  213. destination = NULL;
  214. *SourcePath = NULL;
  215. *DestinationPath = NULL;
  216. if (f && !qf && s && !qs) {
  217. source = FirstPathArgument;
  218. destination = SecondPathArgument;
  219. } else if (!f && qf && !s && qs) {
  220. source = FirstQuotedPathArgument;
  221. destination = SecondQuotedPathArgument;
  222. } else if (f && qf && !s && !qs) {
  223. string = FirstPathArgument->GetLexeme();
  224. qstring = FirstQuotedPathArgument->GetLexeme();
  225. for (i = 0; i < ArgLex->QueryLexemeCount(); i++) {
  226. if (!ArgLex->GetLexemeAt(i)->Strcmp(string)) {
  227. source = FirstPathArgument;
  228. destination = FirstQuotedPathArgument;
  229. break;
  230. }
  231. if (!ArgLex->GetLexemeAt(i)->Strcmp(qstring)) {
  232. source = FirstQuotedPathArgument;
  233. destination = FirstPathArgument;
  234. break;
  235. }
  236. }
  237. } else if (f && !qf && !s && !qs) {
  238. source = FirstPathArgument;
  239. } else if (!f && qf && !s && !qs) {
  240. source = FirstQuotedPathArgument;
  241. }
  242. if (source) {
  243. if (!(*SourcePath = NEW PATH) ||
  244. !(*SourcePath)->Initialize(source->GetPath(),
  245. VerboseArgument->IsValueSet())) {
  246. *SourcePath = NULL;
  247. }
  248. }
  249. if (destination) {
  250. if (!(*DestinationPath = NEW PATH) ||
  251. !(*DestinationPath)->Initialize(destination->GetPath(),
  252. VerboseArgument->IsValueSet())) {
  253. *DestinationPath = NULL;
  254. }
  255. }
  256. }
  257. VOID
  258. XCOPY::GetArgumentsCmd(
  259. )
  260. /*++
  261. Routine Description:
  262. Obtains the arguments from the Command line
  263. Arguments:
  264. None.
  265. Return Value:
  266. None.
  267. --*/
  268. {
  269. ARRAY ArgArray;
  270. PATH_ARGUMENT ProgramNameArgument;
  271. DSTRING CmdLine;
  272. DSTRING InvalidParms;
  273. WCHAR Ch;
  274. PWSTRING InvalidSwitch;
  275. PARGUMENT_LEXEMIZER ArgLex;
  276. //
  277. // Prepare for parsing
  278. //
  279. if (//
  280. // Initialize the arguments
  281. //
  282. !(CmdLine.Initialize( GetCommandLine() )) ||
  283. !(ArgArray.Initialize( 15, 15 )) ||
  284. !(ProgramNameArgument.Initialize( "*" )) ||
  285. !(FirstPathArgument->Initialize( "*", FALSE )) ||
  286. !(FirstQuotedPathArgument->Initialize( "\"*\"", FALSE )) ||
  287. !(SecondPathArgument->Initialize( "*", FALSE)) ||
  288. !(SecondQuotedPathArgument->Initialize( "\"*\"", FALSE)) ||
  289. !(ArchiveArgument->Initialize( "/A" )) ||
  290. !(DateArgument->Initialize( "/D:*" )) ||
  291. !(OldArgument->Initialize( "/D" )) ||
  292. !(DecryptArgument->Initialize( "/G" )) ||
  293. !(EmptyArgument->Initialize( "/E" )) ||
  294. !(ModifyArgument->Initialize( "/M" )) ||
  295. !(PromptArgument->Initialize( "/P" )) ||
  296. !(OverWriteArgument->Initialize( "/Y" )) ||
  297. !(NotOverWriteArgument->Initialize( "/-Y" )) ||
  298. !(SubdirArgument->Initialize( "/S" )) ||
  299. !(VerifyArgument->Initialize( "/V" )) ||
  300. !(WaitArgument->Initialize( "/W" )) ||
  301. !(HelpArgument->Initialize( "/?" )) ||
  302. !(ContinueArgument->Initialize( "/C" )) ||
  303. !(IntelligentArgument->Initialize( "/I" )) ||
  304. !(VerboseArgument->Initialize( "/F" )) ||
  305. !(HiddenArgument->Initialize( "/H" )) ||
  306. !(ReadOnlyArgument->Initialize( "/R" )) ||
  307. !(SilentArgument->Initialize( "/Q" )) ||
  308. !(NoCopyArgument->Initialize( "/L" )) ||
  309. !(StructureArgument->Initialize( "/T" )) ||
  310. !(UpdateArgument->Initialize( "/U" )) ||
  311. !(CopyAttrArgument->Initialize( "/K" )) ||
  312. !(UseShortArgument->Initialize( "/N" )) ||
  313. !(RestartableArgument->Initialize( "/Z" )) ||
  314. !(OwnerArgument->Initialize( "/O" )) ||
  315. !(AuditArgument->Initialize( "/X" )) ||
  316. !(ExclusionListArgument->Initialize("/EXCLUDE:*")) ||
  317. !(InvalidSwitchArgument->Initialize( "/*" )) ||
  318. //
  319. // Put the arguments in the argument array
  320. //
  321. !(ArgArray.Put( &ProgramNameArgument )) ||
  322. !(ArgArray.Put( ArchiveArgument )) ||
  323. !(ArgArray.Put( DateArgument )) ||
  324. !(ArgArray.Put( OldArgument )) ||
  325. !(ArgArray.Put( DecryptArgument )) ||
  326. !(ArgArray.Put( EmptyArgument )) ||
  327. !(ArgArray.Put( ModifyArgument )) ||
  328. !(ArgArray.Put( PromptArgument )) ||
  329. !(ArgArray.Put( OverWriteArgument )) ||
  330. !(ArgArray.Put( NotOverWriteArgument )) ||
  331. !(ArgArray.Put( SubdirArgument )) ||
  332. !(ArgArray.Put( VerifyArgument )) ||
  333. !(ArgArray.Put( WaitArgument )) ||
  334. !(ArgArray.Put( HelpArgument )) ||
  335. !(ArgArray.Put( ContinueArgument )) ||
  336. !(ArgArray.Put( IntelligentArgument )) ||
  337. !(ArgArray.Put( VerboseArgument )) ||
  338. !(ArgArray.Put( HiddenArgument )) ||
  339. !(ArgArray.Put( ReadOnlyArgument )) ||
  340. !(ArgArray.Put( SilentArgument )) ||
  341. !(ArgArray.Put( RestartableArgument )) ||
  342. !(ArgArray.Put( OwnerArgument )) ||
  343. !(ArgArray.Put( AuditArgument )) ||
  344. !(ArgArray.Put( NoCopyArgument )) ||
  345. !(ArgArray.Put( StructureArgument )) ||
  346. !(ArgArray.Put( UpdateArgument )) ||
  347. !(ArgArray.Put( CopyAttrArgument )) ||
  348. !(ArgArray.Put( UseShortArgument )) ||
  349. !(ArgArray.Put( ExclusionListArgument )) ||
  350. !(ArgArray.Put( InvalidSwitchArgument )) ||
  351. !(ArgArray.Put( FirstQuotedPathArgument )) ||
  352. !(ArgArray.Put( SecondQuotedPathArgument )) ||
  353. !(ArgArray.Put( FirstPathArgument )) ||
  354. !(ArgArray.Put( SecondPathArgument ))
  355. ) {
  356. DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR);
  357. }
  358. //
  359. // Parse the arguments
  360. //
  361. ArgLex = ParseArguments( &CmdLine, &ArgArray );
  362. if ( InvalidSwitchArgument->IsValueSet() ) {
  363. InvalidSwitch = InvalidSwitchArgument->GetString();
  364. InvalidParms.Initialize( SWITCH_CHARACTERS );
  365. Ch = InvalidSwitch->QueryChAt(0);
  366. if ( Ch == 'd' || Ch == 'D' ) {
  367. Ch = InvalidSwitch->QueryChAt(1);
  368. if ( Ch == INVALID_CHAR ) {
  369. DisplayMessageAndExit( XCOPY_ERROR_INVALID_NUMBER_PARAMETERS,
  370. NULL,
  371. EXIT_MISC_ERROR );
  372. } else if ( Ch != ':' || InvalidSwitch->QueryChCount() == 2 ) {
  373. DisplayMessageAndExit( XCOPY_ERROR_INVALID_SWITCH_SWITCH,
  374. InvalidSwitchArgument->GetLexeme(),
  375. EXIT_MISC_ERROR );
  376. }
  377. } else if ( Ch == '/' ) {
  378. Ch = InvalidSwitch->QueryChAt(1);
  379. if ( Ch == ':' && InvalidSwitchArgument->GetString()->QueryChAt(2) == INVALID_CHAR ) {
  380. InvalidSwitchArgument->GetLexeme()->Truncate(1);
  381. }
  382. }
  383. Ch = InvalidSwitch->QueryChAt(0);
  384. if ( InvalidParms.Strchr( Ch ) != INVALID_CHNUM ) {
  385. DisplayMessageAndExit( XCOPY_ERROR_INVALID_PARAMETER,
  386. InvalidSwitchArgument->GetLexeme(),
  387. EXIT_MISC_ERROR );
  388. } else {
  389. DisplayMessageAndExit( XCOPY_ERROR_INVALID_SWITCH_SWITCH,
  390. InvalidSwitchArgument->GetLexeme(),
  391. EXIT_MISC_ERROR );
  392. }
  393. }
  394. //
  395. // Set the switches
  396. //
  397. _EmptySwitch = EmptyArgument->QueryFlag();
  398. _ModifySwitch = ModifyArgument->QueryFlag();
  399. //
  400. // ModifySwitch implies ArchiveSwitch
  401. //
  402. if ( _ModifySwitch ) {
  403. _ArchiveSwitch = TRUE;
  404. } else {
  405. _ArchiveSwitch = ArchiveArgument->QueryFlag();
  406. }
  407. //
  408. // Set the switches
  409. //
  410. _PromptSwitch = PromptArgument->QueryFlag();
  411. _OverWriteSwitch = QueryOverWriteSwitch();
  412. _SubdirSwitch = SubdirArgument->QueryFlag();
  413. _VerifySwitch = VerifyArgument->QueryFlag();
  414. _WaitSwitch = WaitArgument->QueryFlag();
  415. _ContinueSwitch = ContinueArgument->QueryFlag();
  416. _IntelligentSwitch = IntelligentArgument->QueryFlag();
  417. _CopyIfOldSwitch = OldArgument->QueryFlag();
  418. _DecryptSwitch = DecryptArgument->QueryFlag();
  419. _VerboseSwitch = VerboseArgument->QueryFlag();
  420. _HiddenSwitch = HiddenArgument->QueryFlag();
  421. _ReadOnlySwitch = ReadOnlyArgument->QueryFlag();
  422. _SilentSwitch = SilentArgument->QueryFlag();
  423. _DontCopySwitch = NoCopyArgument->QueryFlag();
  424. _StructureOnlySwitch= StructureArgument->QueryFlag();
  425. _UpdateSwitch = UpdateArgument->QueryFlag();
  426. _CopyAttrSwitch = CopyAttrArgument->QueryFlag();
  427. _UseShortSwitch = UseShortArgument->QueryFlag();
  428. _RestartableSwitch = RestartableArgument->QueryFlag();
  429. _OwnerSwitch = OwnerArgument->QueryFlag();
  430. _AuditSwitch = AuditArgument->QueryFlag();
  431. HelpSwitch = HelpArgument->QueryFlag();
  432. //
  433. // Set the source and destination paths. Argument checking is
  434. // done somewhere else, so it is ok. to set the source path to
  435. // NULL here.
  436. //
  437. GetSourceAndDestinationPath(FirstPathArgument,
  438. FirstQuotedPathArgument,
  439. SecondPathArgument,
  440. SecondQuotedPathArgument,
  441. ArgLex,
  442. &_SourcePath,
  443. &_DestinationPath);
  444. DELETE(ArgLex);
  445. //
  446. // Set the date argument
  447. //
  448. if ( DateArgument->IsValueSet() ) {
  449. if ((_Date = NEW TIMEINFO) == NULL ) {
  450. DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR );
  451. }
  452. _Date->Initialize( DateArgument->GetTimeInfo() );
  453. //
  454. // The command-line date argument is specified in local time so
  455. // that it corresponds to the output of 'dir'. We want to compare
  456. // it to file timestamps that we query from the file system, so
  457. // convert the argument from local to universal time.
  458. //
  459. _Date->ConvertToUTC();
  460. } else {
  461. _Date = NULL;
  462. }
  463. if( ExclusionListArgument->IsValueSet() ) {
  464. InitializeExclusionList( ExclusionListArgument->GetString() );
  465. }
  466. }
  467. PARGUMENT_LEXEMIZER
  468. XCOPY::ParseArguments(
  469. IN PWSTRING CmdLine,
  470. OUT PARRAY ArgArray
  471. )
  472. /*++
  473. Routine Description:
  474. Parses a group of arguments
  475. Arguments:
  476. CmdLine - Supplies pointer to a command line to parse
  477. ArgArray - Supplies pointer to array of arguments
  478. Return Value:
  479. Returns the argument lexemizer used which then needs to be freed
  480. by the client.
  481. Notes:
  482. --*/
  483. {
  484. PARGUMENT_LEXEMIZER ArgLex;
  485. //
  486. // Initialize lexeme array and the lexemizer.
  487. //
  488. if ( !(ArgLex = NEW ARGUMENT_LEXEMIZER) ||
  489. !(LexArray->Initialize( 9, 9 )) ||
  490. !(ArgLex->Initialize( LexArray )) ) {
  491. DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY,
  492. NULL,
  493. EXIT_MISC_ERROR );
  494. }
  495. //
  496. // Set our parsing preferences
  497. //
  498. ArgLex->PutMultipleSwitch( "/?ABMDPSEVWCIFHRQLKTUNZOXY" );
  499. ArgLex->PutSwitches( "/" );
  500. ArgLex->SetCaseSensitive( FALSE );
  501. ArgLex->PutSeparators( " \t" );
  502. ArgLex->PutStartQuotes( "\"" );
  503. ArgLex->PutEndQuotes( "\"" );
  504. ArgLex->SetAllowSwitchGlomming( TRUE );
  505. ArgLex->SetNoSpcBetweenDstAndSwitch( TRUE );
  506. //
  507. // Parse the arguments
  508. //
  509. if ( !(ArgLex->PrepareToParse( CmdLine ))) {
  510. DisplayMessageAndExit( XCOPY_ERROR_PARSE,
  511. NULL,
  512. EXIT_MISC_ERROR );
  513. }
  514. if ( !ArgLex->DoParsing( ArgArray ) ) {
  515. DisplayMessageAndExit( XCOPY_ERROR_INVALID_NUMBER_PARAMETERS,
  516. NULL,
  517. EXIT_MISC_ERROR );
  518. }
  519. return ArgLex;
  520. }
  521. VOID
  522. XCOPY::CheckArgumentConsistency (
  523. )
  524. /*++
  525. Routine Description:
  526. Checks the consistency of the arguments
  527. Arguments:
  528. none
  529. Return Value:
  530. none
  531. Notes:
  532. --*/
  533. {
  534. PFSN_DIRECTORY DirSrc = NULL;
  535. PFSN_DIRECTORY DirDst = NULL;
  536. PWSTRING DevSrc = NULL;
  537. PWSTRING DevDst = NULL;
  538. PATH PathSrc, PathSrc1;
  539. PATH PathDst, PathDst1;
  540. DSTRING Slash;
  541. if ( HelpSwitch ) {
  542. //
  543. // Help requested
  544. //
  545. Usage();
  546. DisplayMessageAndExit( 0,
  547. NULL,
  548. EXIT_NORMAL );
  549. }
  550. //
  551. // Make sure that we have a source path
  552. //
  553. if ( _SourcePath == NULL ) {
  554. DisplayMessageAndExit( XCOPY_ERROR_INVALID_NUMBER_PARAMETERS,
  555. NULL,
  556. EXIT_MISC_ERROR );
  557. }
  558. //
  559. // The empty switch implies Subdir switch (note that DOS
  560. // requires Subdir switch explicitly).
  561. //
  562. //
  563. if ( _EmptySwitch ) {
  564. _SubdirSwitch = TRUE;
  565. }
  566. //
  567. // The StructureOnly switch imples the subdir switch
  568. //
  569. if ( _StructureOnlySwitch ) {
  570. _SubdirSwitch = TRUE;
  571. }
  572. //
  573. // Copying audit info implies copying the rest of the security
  574. // info.
  575. //
  576. _OwnerSwitch = _OwnerSwitch || _AuditSwitch;
  577. //
  578. // Restartable copy is not available with security because
  579. // secure copy uses BackupRead/Write instead of CopyFileEx.
  580. //
  581. if (_OwnerSwitch && _RestartableSwitch) {
  582. DisplayMessageAndExit( XCOPY_ERROR_Z_X_CONFLICT, NULL, EXIT_MISC_ERROR );
  583. }
  584. //
  585. // If destination path is null, then the destination path is the
  586. // current directory
  587. //
  588. if ( _DestinationPath == NULL ) {
  589. if ( ((_DestinationPath = NEW PATH) == NULL ) ||
  590. !_DestinationPath->Initialize( (LPWSTR)L".", TRUE ) ) {
  591. DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR );
  592. }
  593. }
  594. _DestinationPath->TruncateNameAtColon();
  595. if ( !PathSrc1.Initialize( _SourcePath, TRUE ) ||
  596. !PathDst1.Initialize( _DestinationPath, TRUE ) ||
  597. !(DevSrc = PathSrc1.QueryDevice()) ||
  598. !(DevDst = PathDst1.QueryDevice()) ||
  599. !PathSrc.Initialize( DevSrc ) ||
  600. !PathDst.Initialize( DevDst ) ||
  601. !Slash.Initialize( "\\" ) ||
  602. !PathSrc.AppendBase( &Slash ) ||
  603. !PathDst.AppendBase( &Slash ) ||
  604. !(DirSrc = SYSTEM::QueryDirectory( &PathSrc )) ||
  605. !(DirDst = SYSTEM::QueryDirectory( &PathDst )) ) {
  606. DisplayMessageAndExit( XCOPY_ERROR_INVALID_DRIVE, NULL, EXIT_MISC_ERROR );
  607. }
  608. DELETE( DevSrc );
  609. DELETE( DevDst );
  610. DELETE( DirSrc );
  611. DELETE( DirDst );
  612. }
  613. BOOLEAN
  614. XCOPY::AddToExclusionList(
  615. IN PWSTRING ExclusionListFileName
  616. )
  617. /*++
  618. Routine Description:
  619. This method adds the contents of the specified file to
  620. the exclusion list.
  621. Arguments:
  622. ExclusionListFileName -- Supplies the name of a file which
  623. contains the exclusion list.
  624. Return Value:
  625. TRUE upon successful completion.
  626. --*/
  627. {
  628. PATH ExclusionPath;
  629. PDSTRING String;
  630. PFSN_FILE File;
  631. PFILE_STREAM Stream;
  632. CHNUM Position;
  633. DebugPtrAssert( ExclusionListFileName );
  634. if( !ExclusionPath.Initialize( ExclusionListFileName ) ||
  635. (File = SYSTEM::QueryFile( &ExclusionPath )) == NULL ||
  636. (Stream = File->QueryStream( READ_ACCESS )) == NULL ) {
  637. DisplayMessageAndExit( MSG_COMP_UNABLE_TO_READ,
  638. ExclusionListFileName,
  639. EXIT_MISC_ERROR );
  640. }
  641. while( !Stream->IsAtEnd() &&
  642. (String = NEW DSTRING) != NULL &&
  643. Stream->ReadLine ( String ) ) {
  644. if( String->QueryChCount() == 0 ) {
  645. continue;
  646. }
  647. // Convert the string to upper-case and remove
  648. // trailing whitespace (blanks and tabs).
  649. //
  650. String->Strupr();
  651. Position = String->QueryChCount() - 1;
  652. while( Position != 0 &&
  653. (String->QueryChAt( Position ) == ' ' ||
  654. String->QueryChAt( Position ) == '\t') ) {
  655. Position -= 1;
  656. }
  657. if( String->QueryChAt( Position ) != ' ' &&
  658. String->QueryChAt( Position ) != '\t' ) {
  659. Position++;
  660. }
  661. if( Position != String->QueryChCount() ) {
  662. String->Truncate( Position );
  663. }
  664. if( String->QueryChCount() != 0 && !_ExclusionList->Put( String ) ) {
  665. DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY,
  666. NULL,
  667. EXIT_MISC_ERROR );
  668. }
  669. }
  670. DELETE( Stream );
  671. DELETE( File );
  672. return TRUE;
  673. }
  674. BOOLEAN
  675. XCOPY::InitializeExclusionList(
  676. IN PWSTRING ListOfFiles
  677. )
  678. /*++
  679. Routine Description:
  680. This method reads the exclusion list and initializes the
  681. exclusion list array.
  682. Arguments:
  683. ListOfFiles -- Supplies a string containing a list of file
  684. names, separated by '+' (e.g. file1+file2+file3)
  685. Return Value:
  686. TRUE upon successful completion.
  687. --*/
  688. {
  689. DSTRING CurrentName;
  690. CHNUM LastPosition, Position;
  691. DebugPtrAssert( ListOfFiles );
  692. if( (_ExclusionList = NEW STRING_ARRAY) == NULL ||
  693. !_ExclusionList->Initialize() ||
  694. (_Iterator = _ExclusionList->QueryIterator()) == NULL ) {
  695. DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY, NULL, EXIT_MISC_ERROR );
  696. }
  697. LastPosition = 0;
  698. while( LastPosition != ListOfFiles->QueryChCount() ) {
  699. Position = ListOfFiles->Strchr( '+', LastPosition );
  700. if( Position == INVALID_CHNUM ) {
  701. Position = ListOfFiles->QueryChCount();
  702. }
  703. if( Position != LastPosition ) {
  704. if( !CurrentName.Initialize( ListOfFiles,
  705. LastPosition,
  706. Position - LastPosition ) ) {
  707. DisplayMessageAndExit( XCOPY_ERROR_NO_MEMORY,
  708. NULL,
  709. EXIT_MISC_ERROR );
  710. }
  711. AddToExclusionList( &CurrentName );
  712. }
  713. // Advance past any separators.
  714. //
  715. while( Position < ListOfFiles->QueryChCount() &&
  716. ListOfFiles->QueryChAt( Position ) == '+' ) {
  717. Position += 1;
  718. }
  719. LastPosition = Position;
  720. }
  721. return TRUE;
  722. }
  723. BOOLEAN
  724. XCOPY::QueryOverWriteSwitch(
  725. )
  726. {
  727. PCHAR env;
  728. DSTRING env_str;
  729. if (OverWriteArgument->IsValueSet() && NotOverWriteArgument->IsValueSet()) {
  730. return (OverWriteArgument->QueryArgPos() > NotOverWriteArgument->QueryArgPos());
  731. } else if (OverWriteArgument->IsValueSet())
  732. return OverWriteArgument->QueryFlag();
  733. else if (NotOverWriteArgument->IsValueSet())
  734. return !NotOverWriteArgument->QueryFlag();
  735. else {
  736. env = getenv("COPYCMD");
  737. if (env == NULL)
  738. return FALSE; // use default
  739. else {
  740. if (!env_str.Initialize(env))
  741. return FALSE; // to be on the safe side
  742. if (env_str.Stricmp(OverWriteArgument->GetPattern()) == 0)
  743. return TRUE;
  744. return FALSE;
  745. }
  746. }
  747. }