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.

787 lines
20 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. expand.c
  5. Abstract:
  6. This module implements the file expand command.
  7. Author:
  8. Mike Sliger (msliger) 29-Apr-1999
  9. Revision History:
  10. --*/
  11. #include "cmdcons.h"
  12. #pragma hdrstop
  13. //
  14. // structure tunneled thru SpExpandFile to carry info to/from callback
  15. //
  16. typedef struct {
  17. LPWSTR FileSpec;
  18. BOOLEAN DisplayFiles;
  19. BOOLEAN MatchedAnyFiles;
  20. ULONG NumberOfFilesDone;
  21. BOOLEAN UserAborted;
  22. BOOLEAN OverwriteExisting;
  23. } EXPAND_CONTEXT;
  24. BOOLEAN
  25. pRcCheckForBreak( VOID );
  26. EXPAND_CALLBACK_RESULT
  27. pRcExpandCallback(
  28. EXPAND_CALLBACK_MESSAGE Message,
  29. PWSTR FileName,
  30. PLARGE_INTEGER FileSize,
  31. PLARGE_INTEGER FileTime,
  32. ULONG FileAttributes,
  33. PVOID UserData
  34. );
  35. BOOL
  36. pRcPatternMatch(
  37. LPWSTR pszString,
  38. LPWSTR pszPattern,
  39. IN BOOL fImplyDotAtEnd
  40. );
  41. ULONG
  42. RcCmdExpand(
  43. IN PTOKENIZED_LINE TokenizedLine
  44. )
  45. {
  46. PLINE_TOKEN Token;
  47. LPWSTR Arg;
  48. LPWSTR SrcFile = NULL;
  49. LPWSTR DstFile = NULL;
  50. LPWSTR FileSpec = NULL;
  51. LPWSTR SrcNtFile = NULL;
  52. LPWSTR DstNtPath = NULL;
  53. LPWSTR s;
  54. IO_STATUS_BLOCK IoStatusBlock;
  55. UNICODE_STRING UnicodeString;
  56. HANDLE Handle;
  57. OBJECT_ATTRIBUTES Obja;
  58. NTSTATUS Status;
  59. LPWSTR YesNo;
  60. WCHAR Text[3];
  61. IO_STATUS_BLOCK status_block;
  62. FILE_BASIC_INFORMATION fileInfo;
  63. WCHAR * pos;
  64. ULONG CopyFlags = 0;
  65. BOOLEAN DisplayFileList = FALSE;
  66. BOOLEAN OverwriteExisting = NoCopyPrompt;
  67. EXPAND_CONTEXT Context;
  68. ASSERT(TokenizedLine->TokenCount >= 1);
  69. if (RcCmdParseHelp( TokenizedLine, MSG_EXPAND_HELP )) {
  70. goto exit;
  71. }
  72. //
  73. // Parse command line
  74. //
  75. for( Token = TokenizedLine->Tokens->Next;
  76. Token != NULL;
  77. Token = Token->Next ) {
  78. Arg = Token->String;
  79. if(( Arg[0] == L'-' ) || ( Arg[0] == L'/' )) {
  80. switch( Arg[1] ) {
  81. case L'F':
  82. case L'f':
  83. if(( Arg[2] == L':' ) && ( FileSpec == NULL )) {
  84. FileSpec = &Arg[3];
  85. } else {
  86. RcMessageOut(MSG_SYNTAX_ERROR);
  87. goto exit;
  88. }
  89. break;
  90. case L'D':
  91. case L'd':
  92. if ( Arg[2] == L'\0' ) {
  93. DisplayFileList = TRUE;
  94. } else {
  95. RcMessageOut(MSG_SYNTAX_ERROR);
  96. goto exit;
  97. }
  98. break;
  99. case L'Y':
  100. case L'y':
  101. if ( Arg[2] == L'\0' ) {
  102. OverwriteExisting = TRUE;
  103. } else {
  104. RcMessageOut(MSG_SYNTAX_ERROR);
  105. goto exit;
  106. }
  107. break;
  108. default:
  109. RcMessageOut(MSG_SYNTAX_ERROR);
  110. goto exit;
  111. }
  112. } else if( SrcFile == NULL ) {
  113. SrcFile = Arg;
  114. } else if( DstFile == NULL ) {
  115. DstFile = SpDupStringW( Arg );
  116. } else {
  117. RcMessageOut(MSG_SYNTAX_ERROR);
  118. goto exit;
  119. }
  120. }
  121. if(( SrcFile == NULL ) ||
  122. (( DstFile != NULL ) && ( DisplayFileList == TRUE ))) {
  123. RcMessageOut(MSG_SYNTAX_ERROR);
  124. goto exit;
  125. }
  126. if ( RcDoesPathHaveWildCards( SrcFile )) {
  127. RcMessageOut(MSG_DIR_WILDCARD_NOT_SUPPORTED);
  128. goto exit;
  129. }
  130. //
  131. // Translate the source name to the NT namespace
  132. //
  133. if (!RcFormFullPath( SrcFile, _CmdConsBlock->TemporaryBuffer, TRUE )) {
  134. RcMessageOut(MSG_INVALID_PATH);
  135. goto exit;
  136. }
  137. SrcNtFile = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  138. if ( !DisplayFileList ) {
  139. BOOLEAN OnRemovableMedia;
  140. //
  141. // Create a destination path name when the user did not
  142. // provide one. We use the current drive and directory.
  143. //
  144. if( DstFile == NULL ) {
  145. RcGetCurrentDriveAndDir( _CmdConsBlock->TemporaryBuffer );
  146. DstFile = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  147. }
  148. //
  149. // create the destination paths
  150. //
  151. if (!RcFormFullPath( DstFile, _CmdConsBlock->TemporaryBuffer, FALSE )) {
  152. RcMessageOut(MSG_INVALID_PATH);
  153. goto exit;
  154. }
  155. if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,FALSE,FALSE)) {
  156. RcMessageOut(MSG_ACCESS_DENIED);
  157. goto exit;
  158. }
  159. if (!RcFormFullPath( DstFile, _CmdConsBlock->TemporaryBuffer, TRUE )) {
  160. RcMessageOut(MSG_INVALID_PATH);
  161. goto exit;
  162. }
  163. DstNtPath = SpDupStringW( _CmdConsBlock->TemporaryBuffer );
  164. //
  165. // check for removable media
  166. //
  167. Status = RcIsFileOnRemovableMedia(DstNtPath, &OnRemovableMedia);
  168. if (AllowRemovableMedia == FALSE && (!NT_SUCCESS(Status) || OnRemovableMedia)) {
  169. RcMessageOut(MSG_ACCESS_DENIED);
  170. goto exit;
  171. }
  172. }
  173. //
  174. // setup context for callbacks
  175. //
  176. RtlZeroMemory(&Context, sizeof(Context));
  177. Context.FileSpec = FileSpec;
  178. Context.DisplayFiles = DisplayFileList;
  179. Context.OverwriteExisting = OverwriteExisting;
  180. if ( DisplayFileList ) {
  181. pRcEnableMoreMode();
  182. }
  183. Status = SpExpandFile( SrcNtFile, DstNtPath, pRcExpandCallback, &Context );
  184. pRcDisableMoreMode();
  185. if( !NT_SUCCESS(Status) && !Context.UserAborted ) {
  186. RcNtError( Status, MSG_CANT_EXPAND_FILE );
  187. } else if (( Context.NumberOfFilesDone == 0 ) &&
  188. ( Context.MatchedAnyFiles == FALSE ) &&
  189. ( Context.FileSpec != NULL )) {
  190. RcMessageOut( MSG_EXPAND_NO_MATCH, Context.FileSpec, SrcFile );
  191. }
  192. if ( Context.MatchedAnyFiles ) {
  193. if ( DisplayFileList ) {
  194. RcMessageOut( MSG_EXPAND_SHOWN, Context.NumberOfFilesDone );
  195. } else {
  196. RcMessageOut( MSG_EXPAND_COUNT, Context.NumberOfFilesDone );
  197. }
  198. }
  199. exit:
  200. if( SrcNtFile ) {
  201. SpMemFree( SrcNtFile );
  202. }
  203. if( DstFile ) {
  204. SpMemFree( DstFile );
  205. }
  206. if( DstNtPath ) {
  207. SpMemFree( DstNtPath );
  208. }
  209. return 1;
  210. }
  211. EXPAND_CALLBACK_RESULT
  212. pRcExpandCallback(
  213. EXPAND_CALLBACK_MESSAGE Message,
  214. PWSTR FileName,
  215. PLARGE_INTEGER FileSize,
  216. PLARGE_INTEGER FileTime,
  217. ULONG FileAttributes,
  218. PVOID UserData
  219. )
  220. {
  221. EXPAND_CONTEXT * Context = (EXPAND_CONTEXT * ) UserData;
  222. LPWSTR YesNo;
  223. EXPAND_CALLBACK_RESULT rc;
  224. WCHAR Text[3];
  225. switch ( Message )
  226. {
  227. case EXPAND_COPY_FILE:
  228. //
  229. // Watch for Ctl-C or ESC while processing
  230. //
  231. if ( pRcCheckForBreak() ) {
  232. Context->UserAborted = TRUE;
  233. return( EXPAND_ABORT );
  234. }
  235. //
  236. // See if filename matches filespec pattern, if any
  237. //
  238. if ( Context->FileSpec != NULL ) {
  239. //
  240. // To be "*.*"-friendly, we need to know if there is a real
  241. // dot in the last element of the string to be matched
  242. //
  243. BOOL fAllowImpliedDot = TRUE;
  244. LPWSTR p;
  245. for ( p = FileName; *p != L'\0'; p++ ) {
  246. if ( *p == L'.' ) {
  247. fAllowImpliedDot = FALSE;
  248. } else if ( *p == L'\\' ) {
  249. fAllowImpliedDot = TRUE;
  250. }
  251. }
  252. if ( !pRcPatternMatch( FileName,
  253. Context->FileSpec,
  254. fAllowImpliedDot )) {
  255. //
  256. // File doesn't match given spec: skip it
  257. //
  258. return( EXPAND_SKIP_THIS_FILE );
  259. }
  260. }
  261. Context->MatchedAnyFiles = TRUE; // don't report "no matches"
  262. if ( Context->DisplayFiles ) {
  263. //
  264. // We're just listing file names, and we must do it now, because
  265. // we're going to tell ExpandFile to skip this one, so this will
  266. // be the last we here about it.
  267. //
  268. WCHAR LineOut[50];
  269. WCHAR *p;
  270. //
  271. // Format the date and time, which go first.
  272. //
  273. RcFormatDateTime(FileTime,LineOut);
  274. RcTextOut(LineOut);
  275. //
  276. // 2 spaces for separation
  277. //
  278. RcTextOut(L" ");
  279. //
  280. // File attributes.
  281. //
  282. p = LineOut;
  283. *p++ = L'-';
  284. if(FileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
  285. *p++ = L'a';
  286. } else {
  287. *p++ = L'-';
  288. }
  289. if(FileAttributes & FILE_ATTRIBUTE_READONLY) {
  290. *p++ = L'r';
  291. } else {
  292. *p++ = L'-';
  293. }
  294. if(FileAttributes & FILE_ATTRIBUTE_HIDDEN) {
  295. *p++ = L'h';
  296. } else {
  297. *p++ = L'-';
  298. }
  299. if(FileAttributes & FILE_ATTRIBUTE_SYSTEM) {
  300. *p++ = L's';
  301. } else {
  302. *p++ = L'-';
  303. }
  304. *p++ = L'-';
  305. *p++ = L'-';
  306. *p++ = L'-';
  307. *p = 0;
  308. RcTextOut(LineOut);
  309. //
  310. // 2 spaces for separation
  311. //
  312. RcTextOut(L" ");
  313. //
  314. // Now, put the size in there. Right justified and space padded
  315. // up to 8 chars. Otherwise unjustified or padded.
  316. //
  317. RcFormat64BitIntForOutput(FileSize->QuadPart,LineOut,TRUE);
  318. if(FileSize->QuadPart > 99999999i64) {
  319. RcTextOut(LineOut);
  320. } else {
  321. RcTextOut(LineOut+11); // outputs 8 chars
  322. }
  323. RcTextOut(L" ");
  324. //
  325. // Finally, put the filename on the line.
  326. //
  327. if( !RcTextOut( FileName ) || !RcTextOut( L"\r\n" )) {
  328. Context->UserAborted = TRUE;
  329. return( EXPAND_ABORT ); /* user aborted display output */
  330. }
  331. Context->NumberOfFilesDone++;
  332. return( EXPAND_SKIP_THIS_FILE );
  333. } // end if DisplayFiles
  334. //
  335. // This file qualified, and we're not just displaying, so tell
  336. // ExpandFile to do it.
  337. //
  338. return( EXPAND_COPY_THIS_FILE );
  339. case EXPAND_COPIED_FILE:
  340. //
  341. // Notification that a file has been copied successfully.
  342. //
  343. RcMessageOut( MSG_EXPANDED, FileName);
  344. Context->NumberOfFilesDone++;
  345. return( EXPAND_NO_ERROR );
  346. case EXPAND_QUERY_OVERWRITE:
  347. //
  348. // Query for approval to overwrite an existing file.
  349. //
  350. if ( Context->OverwriteExisting ) {
  351. return( EXPAND_COPY_THIS_FILE );
  352. }
  353. rc = EXPAND_SKIP_THIS_FILE;
  354. YesNo = SpRetreiveMessageText( ImageBase, MSG_YESNOALLQUIT, NULL, 0 );
  355. if ( YesNo ) {
  356. RcMessageOut( MSG_COPY_OVERWRITE_QUIT, FileName );
  357. if( RcLineIn( Text, 2 ) ) {
  358. if (( Text[0] == YesNo[2] ) || ( Text[0] == YesNo[3] )) {
  359. //
  360. // Yes, we may overwrite this file
  361. //
  362. rc = EXPAND_COPY_THIS_FILE;
  363. } else if (( Text[0] == YesNo[4] ) || ( Text[0] == YesNo[5] )) {
  364. //
  365. // All, we may overwrite this file, and don't prompt again
  366. //
  367. Context->OverwriteExisting = TRUE;
  368. rc = EXPAND_COPY_THIS_FILE;
  369. } else if (( Text[0] == YesNo[6] ) || ( Text[0] == YesNo[7] )) {
  370. //
  371. // No, and stop too.
  372. //
  373. Context->UserAborted = TRUE;
  374. rc = EXPAND_ABORT;
  375. }
  376. }
  377. SpMemFree( YesNo );
  378. }
  379. return( rc );
  380. case EXPAND_NOTIFY_MULTIPLE:
  381. //
  382. // We're being advised that the source contains multiple files.
  383. // If we don't have a selective filespec, we'll abort.
  384. //
  385. if ( Context->FileSpec == NULL ) {
  386. RcMessageOut( MSG_FILESPEC_REQUIRED );
  387. Context->UserAborted = TRUE;
  388. return ( EXPAND_ABORT );
  389. }
  390. return ( EXPAND_CONTINUE );
  391. case EXPAND_NOTIFY_CANNOT_EXPAND:
  392. //
  393. // We're being advised that the source file format is not
  394. // recognized. We display the file name and abort.
  395. //
  396. RcMessageOut( MSG_CANT_EXPAND_FILE, FileName );
  397. Context->UserAborted = TRUE;
  398. return ( EXPAND_ABORT );
  399. case EXPAND_NOTIFY_CREATE_FAILED:
  400. //
  401. // We're being advised that the current target file cannot be
  402. // created. We display the file name and abort.
  403. //
  404. RcMessageOut( MSG_EXPAND_FAILED, FileName );
  405. Context->UserAborted = TRUE;
  406. return ( EXPAND_ABORT );
  407. default:
  408. //
  409. // Ignore any unexpected callback.
  410. //
  411. return( EXPAND_NO_ERROR );
  412. }
  413. }
  414. BOOLEAN
  415. pRcCheckForBreak( VOID )
  416. {
  417. while ( SpInputIsKeyWaiting() ) {
  418. ULONG Key = SpInputGetKeypress();
  419. switch ( Key ) {
  420. case ASCI_ETX:
  421. case ASCI_ESC:
  422. RcMessageOut( MSG_BREAK );
  423. return TRUE;
  424. default:
  425. break;
  426. }
  427. }
  428. return FALSE;
  429. }
  430. //
  431. // pRcPatternMatch() & helpers
  432. //
  433. #define WILDCARD L'*' /* zero or more of any character */
  434. #define WILDCHAR L'?' /* one of any character (does not match END) */
  435. #define END L'\0' /* terminal character */
  436. #define DOT L'.' /* may be implied at end ("hosts" matches "*.") */
  437. static int __inline Lower(c)
  438. {
  439. if ((c >= L'A') && (c <= L'Z'))
  440. {
  441. return(c + (L'a' - L'A'));
  442. }
  443. else
  444. {
  445. return(c);
  446. }
  447. }
  448. static int __inline CharacterMatch(WCHAR chCharacter, WCHAR chPattern)
  449. {
  450. if (Lower(chCharacter) == Lower(chPattern))
  451. {
  452. return(TRUE);
  453. }
  454. else
  455. {
  456. return(FALSE);
  457. }
  458. }
  459. BOOL
  460. pRcPatternMatch(
  461. LPWSTR pszString,
  462. LPWSTR pszPattern,
  463. IN BOOL fImplyDotAtEnd
  464. )
  465. {
  466. /* RECURSIVE */
  467. //
  468. // This function does not deal with 8.3 conventions which might
  469. // be expected for filename comparisons. (In an 8.3 environment,
  470. // "alongfilename.html" would match "alongfil.htm")
  471. //
  472. // This code is NOT MBCS-enabled
  473. //
  474. for ( ; ; )
  475. {
  476. switch (*pszPattern)
  477. {
  478. case END:
  479. //
  480. // Reached end of pattern, so we're done. Matched if
  481. // end of string, no match if more string remains.
  482. //
  483. return(*pszString == END);
  484. case WILDCHAR:
  485. //
  486. // Next in pattern is a wild character, which matches
  487. // anything except end of string. If we reach the end
  488. // of the string, the implied DOT would also match.
  489. //
  490. if (*pszString == END)
  491. {
  492. if (fImplyDotAtEnd == TRUE)
  493. {
  494. fImplyDotAtEnd = FALSE;
  495. }
  496. else
  497. {
  498. return(FALSE);
  499. }
  500. }
  501. else
  502. {
  503. pszString++;
  504. }
  505. pszPattern++;
  506. break;
  507. case WILDCARD:
  508. //
  509. // Next in pattern is a wildcard, which matches anything.
  510. // Find the required character that follows the wildcard,
  511. // and search the string for it. At each occurence of the
  512. // required character, try to match the remaining pattern.
  513. //
  514. // There are numerous equivalent patterns in which multiple
  515. // WILDCARD and WILDCHAR are adjacent. We deal with these
  516. // before our search for the required character.
  517. //
  518. // Each WILDCHAR burns one non-END from the string. An END
  519. // means we have a match. Additional WILDCARDs are ignored.
  520. //
  521. for ( ; ; )
  522. {
  523. pszPattern++;
  524. if (*pszPattern == END)
  525. {
  526. return(TRUE);
  527. }
  528. else if (*pszPattern == WILDCHAR)
  529. {
  530. if (*pszString == END)
  531. {
  532. if (fImplyDotAtEnd == TRUE)
  533. {
  534. fImplyDotAtEnd = FALSE;
  535. }
  536. else
  537. {
  538. return(FALSE);
  539. }
  540. }
  541. else
  542. {
  543. pszString++;
  544. }
  545. }
  546. else if (*pszPattern != WILDCARD)
  547. {
  548. break;
  549. }
  550. }
  551. //
  552. // Now we have a regular character to search the string for.
  553. //
  554. while (*pszString != END)
  555. {
  556. //
  557. // For each match, use recursion to see if the remainder
  558. // of the pattern accepts the remainder of the string.
  559. // If it does not, continue looking for other matches.
  560. //
  561. if (CharacterMatch(*pszString, *pszPattern) == TRUE)
  562. {
  563. if (pRcPatternMatch(pszString + 1, pszPattern + 1, fImplyDotAtEnd) == TRUE)
  564. {
  565. return(TRUE);
  566. }
  567. }
  568. pszString++;
  569. }
  570. //
  571. // Reached end of string without finding required character
  572. // which followed the WILDCARD. If the required character
  573. // is a DOT, consider matching the implied DOT.
  574. //
  575. // Since the remaining string is empty, the only pattern which
  576. // could match after the DOT would be zero or more WILDCARDs,
  577. // so don't bother with recursion.
  578. //
  579. if ((*pszPattern == DOT) && (fImplyDotAtEnd == TRUE))
  580. {
  581. pszPattern++;
  582. while (*pszPattern != END)
  583. {
  584. if (*pszPattern != WILDCARD)
  585. {
  586. return(FALSE);
  587. }
  588. pszPattern++;
  589. }
  590. return(TRUE);
  591. }
  592. //
  593. // Reached end of the string without finding required character.
  594. //
  595. return(FALSE);
  596. break;
  597. default:
  598. //
  599. // Nothing special about the pattern character, so it
  600. // must match source character.
  601. //
  602. if (CharacterMatch(*pszString, *pszPattern) == FALSE)
  603. {
  604. if ((*pszPattern == DOT) &&
  605. (*pszString == END) &&
  606. (fImplyDotAtEnd == TRUE))
  607. {
  608. fImplyDotAtEnd = FALSE;
  609. }
  610. else
  611. {
  612. return(FALSE);
  613. }
  614. }
  615. if (*pszString != END)
  616. {
  617. pszString++;
  618. }
  619. pszPattern++;
  620. }
  621. }
  622. }