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.

3343 lines
96 KiB

  1. /****************************************************************************/
  2. /* */
  3. /* WFCOPY.C - */
  4. /* */
  5. /* Windows File System File Copying Routines */
  6. /* */
  7. /****************************************************************************/
  8. #include "winfile.h"
  9. #include "winnet.h"
  10. #include "wnetcaps.h" // WNetGetCaps()
  11. #include "lfn.h"
  12. #include "wfcopy.h"
  13. #ifdef TRACECOPY
  14. #define dbg(x) DebugF x
  15. #else
  16. #define dbg(x)
  17. #endif
  18. BOOL *pbConfirmAll;
  19. CHAR szSpace[] = " ";
  20. INT ManySource;
  21. INT nCopyNumQueue; // # of items in the queue
  22. INT nCopyMaxQueue; // size of the queue
  23. PCOPYQUEUE pCopyQueue; // copy queue buffer
  24. BOOL bCopyReport; // do notifications? bogus
  25. LPSTR lpCopyBuffer; // global memory for FileCopy() buffer
  26. WORD wCopyBufferSize; // size of this buffer
  27. VOID APIENTRY wfYield(VOID);
  28. INT CopyMoveRetry(PSTR, INT);
  29. VOID CopyError(PSTR, PSTR, INT, WORD, INT);
  30. BOOL IsRootDirectory(PSTR pPath);
  31. BOOL IsDirectory(PSTR pPath);
  32. WORD ConfirmDialog(
  33. HWND hDlg, WORD dlg,
  34. LPSTR pFileDest, PLFNDTA pDTADest,
  35. LPSTR pFileSource, PLFNDTA pDTASource,
  36. BOOL bConfirmByDefault, BOOL *pbAll);
  37. VOID MergePathName(LPSTR pPath, LPSTR pName);
  38. BOOL IsInvalidPath(register LPSTR pPath);
  39. WORD GetNextPair(register PCOPYROOT pcr, LPSTR pFrom, LPSTR pToPath, LPSTR pToSpec, WORD wFunc);
  40. INT CheckMultiple(LPSTR pInput);
  41. VOID DialogEnterFileStuff(register HWND hwnd);
  42. WORD SafeFileRemove(LPSTR szFileOEM);
  43. BOOL IsWindowsFile(LPSTR szFileOEM);
  44. INT_PTR APIENTRY ReplaceDlgProc(register HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
  45. BOOL
  46. APIENTRY
  47. IsValidChar(
  48. BYTE ch,
  49. BOOL fPath
  50. )
  51. {
  52. switch (ch) {
  53. case ';': // terminator
  54. case ',': // terminator
  55. case '|': // pipe
  56. case '>': // redir
  57. case '<': // redir
  58. case '"': // quote
  59. return FALSE;
  60. case '?': // wc we only do wilds here because they're
  61. case '*': // wc legal for qualifypath
  62. case '\\': // path separator
  63. case ':': // drive colon
  64. case '/': // path sep
  65. return fPath;
  66. }
  67. // cannot be a control character or space
  68. return ch > ' ';
  69. }
  70. //--------------------------------------------------------------------------
  71. //
  72. // StripColon() -
  73. //
  74. // removes trailing colon if not a drive letter.
  75. // this is to support DOS character devices (CON:, COM1: LPT1:). DOS
  76. // can't deal with these things having a colon on the end (so we strip it).
  77. //
  78. //--------------------------------------------------------------------------
  79. PSTR
  80. StripColon(
  81. register PSTR pPath
  82. )
  83. {
  84. register INT cb = lstrlen(pPath);
  85. dbg(("StripColon(%s)\r\n",(LPSTR)pPath));
  86. {
  87. LPSTR pTailp = AnsiPrev( pPath, &(pPath[cb]) );
  88. if (cb > 2 && *pTailp == ':')
  89. *pTailp = 0;
  90. }
  91. return pPath;
  92. }
  93. /*--------------------------------------------------------------------------*/
  94. /* */
  95. /* FindFileName() - */
  96. /* */
  97. /*--------------------------------------------------------------------------*/
  98. /* Returns a pointer to the last component of a path string. */
  99. PSTR
  100. FindFileName(
  101. register PSTR pPath
  102. )
  103. {
  104. register LPSTR pT;
  105. dbg(("FindFileName(%s);\r\n",(LPSTR)pPath));
  106. for (pT=pPath; *pPath; pPath=(LPSTR)AnsiNext(pPath)) {
  107. if ((pPath[0] == '\\' || pPath[0] == ':') && pPath[1])
  108. pT = pPath+1;
  109. }
  110. return (pT);
  111. }
  112. /*--------------------------------------------------------------------------*/
  113. /* */
  114. /* AppendToPath() - */
  115. /* */
  116. /*--------------------------------------------------------------------------*/
  117. /* Appends a filename to a path. Checks the \ problem first
  118. * (which is why one can't just use lstrcat())
  119. * Also don't append a \ to : so we can have drive-relative paths...
  120. * this last bit is no longer appropriate since we qualify first!
  121. *
  122. * is this relative junk needed anymore? if not this can be
  123. * replaced with AddBackslash(); lstrcat()
  124. */
  125. VOID
  126. APIENTRY
  127. AppendToPath(
  128. PSTR pPath,
  129. PSTR pMore
  130. )
  131. {
  132. dbg(("AppendToPath(%s,%s);\r\n",(LPSTR)pPath,(LPSTR)pMore));
  133. /* Don't append a \ to empty paths. */
  134. if (*pPath) {
  135. LPSTR pPathBase = pPath;
  136. BYTE ch;
  137. while (*pPath)
  138. pPath++;
  139. ch = *AnsiPrev(pPathBase, pPath );
  140. if (ch != '\\')
  141. *pPath++='\\';
  142. }
  143. /* Skip any initial terminators on input. */
  144. while (*pMore == '\\')
  145. pMore = (LPSTR)AnsiNext(pMore);
  146. lstrcpy(pPath, pMore);
  147. }
  148. /*--------------------------------------------------------------------------*/
  149. /* */
  150. /* RemoveLast() - */
  151. /* */
  152. /*--------------------------------------------------------------------------*/
  153. /* Deletes the last component of a filename in a string. */
  154. VOID
  155. APIENTRY
  156. RemoveLast(
  157. PSTR pFile
  158. )
  159. {
  160. PSTR pT;
  161. dbg(("RemoveLast(%s);\r\n",(LPSTR)pFile));
  162. for (pT=pFile; *pFile; pFile=(LPSTR)AnsiNext(pFile)) {
  163. if (*pFile == '\\')
  164. pT = pFile;
  165. else if (*pFile == ':') {
  166. if (pFile[1] =='\\')
  167. pFile++;
  168. pT = pFile + 1;
  169. }
  170. }
  171. *pT = TEXT('\0');
  172. }
  173. // qualify a DOS (or LFN) file name based on the currently active window.
  174. // this code is not careful to not write more than MAXPATHLEN characters
  175. // into psz
  176. //
  177. // in:
  178. // psz path to be qualified (of at least MAXPATHLEN characters)
  179. // ANSI string
  180. //
  181. // out:
  182. // psz fully qualified version of input string based
  183. // on the current active window (current directory)
  184. //
  185. VOID
  186. APIENTRY
  187. QualifyPath(
  188. PSTR psz
  189. )
  190. {
  191. INT cb, nSpaceLeft;
  192. CHAR szTemp[MAXPATHLEN];
  193. INT iDrive = 0;
  194. LPSTR pOrig;
  195. BOOL flfn = FALSE;
  196. STKCHK();
  197. dbg(("QualifyPath(%s);\r\n",(LPSTR)psz));
  198. /* Save it away. */
  199. strncpy(szTemp, psz, sizeof(szTemp));
  200. CheckSlashies(szTemp);
  201. StripColon(szTemp);
  202. nSpaceLeft = MAXPATHLEN;
  203. pOrig = szTemp;
  204. if (pOrig[0] == '\\' && pOrig[1] == '\\') {
  205. // leave the \\ in the buffer so that the various parts
  206. // of the UNC path will be qualified and appended. Note
  207. // we must assume that UNCs are FAT's.
  208. psz[2] = 0;
  209. nSpaceLeft -= 3;
  210. goto GetComps;
  211. }
  212. if (pOrig[0] && pOrig[1]==':' && !IsDBCSLeadByte(pOrig[0])) {
  213. iDrive = DRIVEID(pOrig);
  214. /* Skip over the drive letter. */
  215. pOrig += 2;
  216. } else
  217. iDrive = GetSelectedDrive();
  218. flfn = IsLFNDrive(pOrig);
  219. #ifdef DEBUG
  220. if (flfn)
  221. dbg(("lfn qualify!\r\n"));
  222. else
  223. dbg(("normal qualify!\r\n"));
  224. #endif
  225. // on FAT devices, replace any illegal chars with underscores
  226. if (!flfn)
  227. {
  228. LPSTR pT;
  229. for (pT = pOrig; *pT; pT = (LPSTR)AnsiNext(pT));
  230. {
  231. if (!IsValidChar(*pT,TRUE))
  232. *pT = '_';
  233. }
  234. }
  235. if (pOrig[0]=='\\') {
  236. psz[0] = (CHAR)iDrive + (CHAR)'A';
  237. psz[1] = ':';
  238. psz[2] = '\\';
  239. psz[3] = 0;
  240. nSpaceLeft -= 4;
  241. pOrig++;
  242. } else {
  243. /* Get current dir of drive in path. Also returns drive. */
  244. GetSelectedDirectory((WORD)(iDrive+1), psz);
  245. nSpaceLeft -= (lstrlen(psz) + 1);
  246. }
  247. GetComps:
  248. while (*pOrig && nSpaceLeft > 0) {
  249. /* If the component is parent dir, go up one dir.
  250. * If its the current dir, skip it, else add it normally
  251. */
  252. if (pOrig[0] == '.') {
  253. if (pOrig[1] == '.')
  254. RemoveLast(psz);
  255. else if (pOrig[1] && pOrig[1] != '\\')
  256. goto addcomponent;
  257. while (*pOrig && *pOrig != '\\')
  258. pOrig = (LPSTR)AnsiNext(pOrig);
  259. if (*pOrig)
  260. pOrig++;
  261. } else {
  262. LPSTR pT, pTT = NULL;
  263. addcomponent:
  264. AddBackslash(psz);
  265. nSpaceLeft--;
  266. pT = psz + lstrlen(psz);
  267. if (flfn) {
  268. // copy the component
  269. while (*pOrig && *pOrig != '\\') {
  270. nSpaceLeft--;
  271. if (IsDBCSLeadByte(*pT++ = *pOrig++)) {
  272. if (nSpaceLeft <= 0) {
  273. pT--;
  274. } else {
  275. *pT++ = *pOrig++;
  276. nSpaceLeft--;
  277. }
  278. }
  279. }
  280. } else {
  281. // copy the filename (up to 8 chars)
  282. for (cb = 0; *pOrig && *pOrig != '\\' && *pOrig != '.' && nSpaceLeft > 0;) {
  283. if (cb < 8) {
  284. cb++;
  285. nSpaceLeft--;
  286. if (IsDBCSLeadByte(*pT++ = *pOrig++)) {
  287. if (nSpaceLeft <= 0) {
  288. pT--;
  289. } else {
  290. cb++;
  291. *pT++ = *pOrig++;
  292. nSpaceLeft--;
  293. }
  294. }
  295. } else {
  296. pOrig = AnsiNext(pOrig);
  297. }
  298. }
  299. // if there's an extension, copy it, up to 3 chars
  300. if (*pOrig == '.' && nSpaceLeft > 0) {
  301. *pT++ = '.';
  302. nSpaceLeft--;
  303. pOrig++;
  304. for (cb = 0; *pOrig && *pOrig != '\\' && nSpaceLeft > 0;) {
  305. if (*pOrig == '.')
  306. cb = 3;
  307. if (cb < 3) {
  308. cb++;
  309. nSpaceLeft--;
  310. if (IsDBCSLeadByte(*pT++ = *pOrig++)) {
  311. if (nSpaceLeft <= 0) {
  312. pT--;
  313. } else {
  314. cb++;
  315. *pT++ = *pOrig++;
  316. nSpaceLeft--;
  317. }
  318. }
  319. } else {
  320. pOrig = AnsiNext(pOrig);
  321. }
  322. }
  323. }
  324. }
  325. // skip the backslash
  326. if (*pOrig)
  327. pOrig++;
  328. // null terminate for next pass...
  329. *pT = 0;
  330. }
  331. }
  332. StripBackslash(psz);
  333. // remove any trailing dots
  334. if (*(psz + lstrlen(psz) - 1) == '.')
  335. *(psz + lstrlen(psz) - 1) = 0;
  336. }
  337. /*--------------------------------------------------------------------------*/
  338. /* */
  339. /* IsRootDirectory() - */
  340. /* */
  341. /*--------------------------------------------------------------------------*/
  342. BOOL
  343. IsRootDirectory(
  344. register LPSTR pPath
  345. )
  346. {
  347. if (!IsDBCSLeadByte( *pPath )) {
  348. if (!lstrcmpi(pPath+1, ":\\"))
  349. return (TRUE);
  350. if (!lstrcmpi(pPath+1, ":"))
  351. return TRUE;
  352. }
  353. if (!lstrcmpi(pPath, "\\"))
  354. return (TRUE);
  355. return (FALSE);
  356. }
  357. // returns:
  358. // TRUE if pPath is a directory, including the root and
  359. // relative paths "." and ".."
  360. // FALSE not a dir
  361. BOOL
  362. IsDirectory(
  363. PSTR pPath
  364. )
  365. {
  366. PSTR pT;
  367. CHAR szTemp[MAXPATHLEN];
  368. STKCHK();
  369. if (IsRootDirectory(pPath))
  370. return TRUE;
  371. // check for "." and ".."
  372. pT = FindFileName(pPath);
  373. if (pT[0] == '.') {
  374. if (!pT[1] || pT[1] == '.')
  375. return TRUE;
  376. }
  377. lstrcpy(szTemp, pPath);
  378. FixAnsiPathForDos(szTemp);
  379. return WFIsDir(szTemp);
  380. }
  381. //
  382. // note: this has the side effect of setting the
  383. // current drive to the new disk if it is successful
  384. //
  385. WORD
  386. APIENTRY
  387. IsTheDiskReallyThere(
  388. HWND hwnd,
  389. register LPSTR pPath,
  390. WORD wFunc
  391. )
  392. {
  393. INT i;
  394. register INT drive;
  395. CHAR szTemp[MAXPATHLEN];
  396. INT err = 0;
  397. WORD wError;
  398. STKCHK();
  399. #ifdef DEBUG
  400. {
  401. char szMsg[200];
  402. wsprintf(szMsg, "IsTheDiskReallyThere(%s)\r\n",(LPSTR)pPath);
  403. OutputDebugString(szMsg);
  404. }
  405. #endif
  406. if (pPath[1]==':' && !IsDBCSLeadByte( *pPath ))
  407. drive = DRIVEID(pPath);
  408. else
  409. return TRUE;
  410. Retry:
  411. err = SheGetDir(drive + 1, szTemp);
  412. if (err)
  413. goto DiskNotThere;
  414. return TRUE;
  415. DiskNotThere:
  416. wError = (WORD)GetExtendedError();
  417. if (wError == 0x15) {
  418. // drive not ready (no disk in the drive)
  419. LoadString(hAppInstance, IDS_COPYERROR + wFunc, szTitle, sizeof(szTitle));
  420. LoadString(hAppInstance, IDS_DRIVENOTREADY, szTemp, sizeof(szTemp));
  421. wsprintf(szMessage, szTemp, drive + 'A');
  422. if (MessageBox(hwnd, szMessage, szTitle, MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY)
  423. goto Retry;
  424. else
  425. return FALSE;
  426. } else if (wError == 0x1F) {
  427. // general failue (disk not formatted)
  428. LoadString(hAppInstance, IDS_COPYERROR + wFunc, szTitle, sizeof(szTitle));
  429. LoadString(hAppInstance, IDS_UNFORMATTED, szTemp, sizeof(szTemp));
  430. wsprintf(szMessage, szTemp, drive + 'A');
  431. if (MessageBox(hwnd, szMessage, szTitle, MB_ICONEXCLAMATION| MB_YESNO) == IDYES) {
  432. HWND hwndSave;
  433. // this is ugly: hdlgProgress is a global that is used
  434. // by the copy code and the format code. this should
  435. // be rewritten so it is not a global (hdlgProgress should
  436. // be passed to all QueryAbort() functions, etc)
  437. hwndSave = hdlgProgress;
  438. nLastDriveInd = 0;
  439. for (i = 0; i < cDrives; i++) {
  440. if (IsRemovableDrive(rgiDrive[i])) {
  441. if (rgiDrive[i] == drive)
  442. break;
  443. nLastDriveInd++;
  444. }
  445. }
  446. fFormatFlags |= FF_ONLYONE; // alow only one format
  447. if (FormatDiskette(hwnd) != TRUE) {
  448. hdlgProgress = hwndSave;
  449. return FALSE;
  450. }
  451. hdlgProgress = hwndSave;
  452. goto Retry;
  453. } else
  454. return FALSE;
  455. }
  456. LoadString(hAppInstance, IDS_COPYERROR + wFunc, szTitle, 32);
  457. LoadString(hAppInstance, IDS_NOSUCHDRIVE, szTemp, sizeof(szTemp));
  458. wsprintf(szMessage, szTemp, drive + 'A');
  459. MessageBox(hwnd, szMessage, szTitle, MB_ICONHAND);
  460. return FALSE;
  461. }
  462. VOID
  463. BuildDateLine(
  464. LPSTR szTemp,
  465. PLFNDTA plfndta
  466. )
  467. {
  468. wsprintf(szTemp, szBytes, plfndta->fd.nFileSizeLow);
  469. lstrcat(szTemp, szSpace);
  470. PutDate(&plfndta->fd.ftLastWriteTime, szTemp + lstrlen(szTemp));
  471. lstrcat(szTemp, szSpace);
  472. PutTime(&plfndta->fd.ftLastWriteTime, szTemp + lstrlen(szTemp));
  473. }
  474. typedef struct {
  475. LPSTR pFileDest;
  476. LPSTR pFileSource;
  477. PLFNDTA plfndtaDest;
  478. PLFNDTA plfndtaSrc;
  479. INT bWriteProtect;
  480. } PARAM_REPLACEDLG, *LPPARAM_REPLACEDLG;
  481. VOID
  482. SetDlgItemPath(
  483. HWND hDlg,
  484. INT id,
  485. LPSTR pszPath
  486. )
  487. {
  488. RECT rc;
  489. HDC hdc;
  490. HFONT hFont;
  491. CHAR szPath[MAXPATHLEN+1]; // can have one extra char
  492. HWND hwnd;
  493. hwnd = GetDlgItem(hDlg, id);
  494. if (!hwnd)
  495. return;
  496. lstrcpy(szPath, pszPath);
  497. GetClientRect(hwnd, &rc);
  498. hdc = GetDC(hDlg);
  499. hFont = (HANDLE)SendMessage(hwnd, WM_GETFONT, 0, 0L);
  500. if (hFont = SelectObject(hdc, hFont)) {
  501. CompactPath(hdc, szPath, (WORD)rc.right);
  502. SelectObject(hdc, hFont);
  503. }
  504. ReleaseDC(hDlg, hdc);
  505. SetWindowText(hwnd, szPath);
  506. }
  507. INT_PTR
  508. APIENTRY
  509. ReplaceDlgProc(
  510. register HWND hDlg,
  511. UINT wMsg,
  512. WPARAM wParam,
  513. LPARAM lParam
  514. )
  515. {
  516. STKCHK();
  517. switch (wMsg) {
  518. case WM_INITDIALOG:
  519. {
  520. #define lpdlgparams ((LPPARAM_REPLACEDLG)lParam)
  521. if (lpdlgparams->bWriteProtect) {
  522. LoadString(hAppInstance, IDS_WRITEPROTECTFILE, szMessage, sizeof(szMessage));
  523. SetDlgItemText(hDlg, IDD_STATUS, szMessage);
  524. }
  525. EnableWindow(GetDlgItem(hDlg, IDD_YESALL), !lpdlgparams->bWriteProtect && ManySource);
  526. lstrcpy(szMessage, lpdlgparams->pFileSource);
  527. lstrcat(szMessage, "?");
  528. SetDlgItemPath(hDlg, IDD_FROM, szMessage);
  529. if (lpdlgparams->pFileDest) {
  530. BuildDateLine(szMessage, lpdlgparams->plfndtaSrc);
  531. SetDlgItemText(hDlg, IDD_DATE2, szMessage);
  532. SetDlgItemPath(hDlg, IDD_TO, lpdlgparams->pFileDest);
  533. BuildDateLine(szMessage, lpdlgparams->plfndtaDest);
  534. SetDlgItemText(hDlg, IDD_DATE1, szMessage);
  535. }
  536. break;
  537. }
  538. case WM_COMMAND:
  539. {
  540. WORD id;
  541. id = GET_WM_COMMAND_ID(wParam, lParam);
  542. switch (id) {
  543. case IDD_HELP:
  544. goto DoHelp;
  545. case IDD_FLAGS:
  546. break;
  547. case IDD_YESALL:
  548. *pbConfirmAll = TRUE;
  549. id = IDYES;
  550. // fall through
  551. case IDYES:
  552. // fall through
  553. default: // this is IDNO and IDCANCEL
  554. EndDialog(hDlg, id);
  555. return FALSE;
  556. }
  557. }
  558. break;
  559. default:
  560. if (wMsg == wHelpMessage) {
  561. DoHelp:
  562. WFHelp(hDlg);
  563. return TRUE;
  564. } else
  565. return FALSE;
  566. }
  567. return TRUE;
  568. }
  569. WORD
  570. ConfirmDialog(
  571. HWND hDlg, WORD dlg,
  572. LPSTR pFileDest, PLFNDTA plfndtaDest,
  573. LPSTR pFileSource, PLFNDTA plfndtaSrc,
  574. BOOL bConfirmByDefault,
  575. BOOL *pbAll
  576. )
  577. {
  578. INT w;
  579. PARAM_REPLACEDLG params;
  580. params.pFileDest = pFileDest;
  581. params.pFileSource = pFileSource;
  582. params.plfndtaDest = plfndtaDest;
  583. params.plfndtaSrc = plfndtaSrc;
  584. params.bWriteProtect = FALSE;
  585. pbConfirmAll = pbAll; // set global for dialog box
  586. if (plfndtaDest->fd.dwFileAttributes & (ATTR_READONLY | ATTR_SYSTEM | ATTR_HIDDEN)) {
  587. DWORD dwSave = dwContext;
  588. dwContext = IDH_DLGFIRST + dlg;
  589. params.bWriteProtect = TRUE;
  590. w = (INT)DialogBoxParam(hAppInstance, MAKEINTRESOURCE(dlg), hDlg, ReplaceDlgProc, (LPARAM)&params);
  591. dwContext = dwSave;
  592. if (w == IDYES) {
  593. lstrcpy(szMessage, pFileDest ? (LPSTR)pFileDest : (LPSTR)pFileSource);
  594. FixAnsiPathForDos(szMessage);
  595. WFSetAttr(szMessage, plfndtaDest->fd.dwFileAttributes & ~(ATTR_READONLY|ATTR_HIDDEN|ATTR_SYSTEM));
  596. }
  597. } else if (!bConfirmByDefault || *pbConfirmAll) {
  598. w = IDYES;
  599. } else {
  600. DWORD dwSave = dwContext;
  601. dwContext = IDH_DLGFIRST + dlg;
  602. w = (INT)DialogBoxParam(hAppInstance, MAKEINTRESOURCE(dlg), hDlg, ReplaceDlgProc, (LPARAM)&params);
  603. dwContext = dwSave;
  604. }
  605. if (w == -1)
  606. w = DE_INSMEM;
  607. return (WORD)w;
  608. }
  609. /*--------------------------------------------------------------------------*/
  610. /* */
  611. /* NetCheck() - */
  612. /* */
  613. /*--------------------------------------------------------------------------*/
  614. /* check rmdirs and mkdirs with the net driver
  615. */
  616. WORD
  617. APIENTRY
  618. NetCheck(
  619. LPSTR pPath,
  620. WORD wType
  621. )
  622. {
  623. UNREFERENCED_PARAMETER(pPath);
  624. UNREFERENCED_PARAMETER(wType);
  625. return WN_SUCCESS;
  626. }
  627. /*** FIX30: This "could use some cleaning up." ***/
  628. /*--------------------------------------------------------------------------*/
  629. /* */
  630. /* MergePathName() - */
  631. /* */
  632. /*--------------------------------------------------------------------------*/
  633. /* Used to generate destination filenames given a pattern and an original
  634. * source name. ? is replaced by the corresponding character in the source,
  635. * and * is replaced by the remainder of the source name.
  636. *
  637. * pPath path with wildcards to be expanded
  638. * pName mask used to expand pName
  639. *
  640. * DBCS by 07/21/90 - Yukinin
  641. *
  642. */
  643. VOID
  644. MergePathName(
  645. LPSTR pPath,
  646. LPSTR pName
  647. )
  648. {
  649. INT i;
  650. INT cch;
  651. LPSTR pWild, p2, pEnd;
  652. BOOL bNoDir = FALSE;
  653. CHAR szWildPart[13];
  654. // if there are no wild cards the destination path does not need merging.
  655. if (!IsWild(pPath))
  656. return;
  657. if (LFNMergePath(pPath,pName))
  658. return;
  659. // copy only 8.3... this part may not be fully qualified for rename
  660. pWild = FindFileName(pPath);
  661. for (p2=szWildPart,i=0; *pWild && *pWild != '.' && i<8; i++, pWild++, p2++) {
  662. *p2 = *pWild;
  663. if (IsDBCSLeadByte(*pWild)) {
  664. if (i == 7)
  665. break;
  666. *(++p2) = *(++pWild);
  667. i++;
  668. }
  669. }
  670. while (*pWild && *pWild != '.')
  671. pWild = (LPSTR)AnsiNext(pWild);
  672. if (*pWild == '.') {
  673. *p2++ = '.';
  674. pWild++;
  675. for (i=0; *pWild && i < 3; i++, pWild++, p2++) {
  676. *p2 = *pWild;
  677. if (IsDBCSLeadByte( *pWild )) {
  678. if (i == 2)
  679. break;
  680. *(++p2) = *(++pWild);
  681. i++;
  682. }
  683. }
  684. }
  685. *p2 = 0;
  686. // szWildPart now has the 8.3 form of the wildcard mask
  687. RemoveLast(pPath);
  688. AddBackslash(pPath);
  689. for (pEnd = pPath; *pEnd; pEnd++); // point to end of string
  690. pWild = szWildPart;
  691. cch = 8;
  692. merge:
  693. for (i=0; i < cch; i+=(IsDBCSLeadByte(*pWild)?2:1), pWild=AnsiNext(pWild)) {
  694. switch (*pWild) {
  695. case '\0':
  696. case ' ':
  697. case '.':
  698. break;
  699. case '*':
  700. pWild--;
  701. /*** FALL THRU ***/
  702. case '?':
  703. if (*pName && *pName!='.')
  704. *pEnd++ = *pName++;
  705. continue;
  706. default:
  707. *pEnd++ = *pWild;
  708. if (IsDBCSLeadByte(*pWild)) {
  709. *pEnd++ = pWild[1];
  710. if (*pName && *pName != '.')
  711. pName++;
  712. }
  713. continue;
  714. }
  715. break;
  716. }
  717. while (*pName && *pName != '.')
  718. pName = AnsiNext(pName);
  719. if (*pName)
  720. pName++;
  721. while (*pWild && *pWild != '.')
  722. pWild = AnsiNext(pWild);
  723. if (*pWild)
  724. pWild++;
  725. if (*pWild) {
  726. *pEnd++ = '.';
  727. cch = 3;
  728. goto merge; // do it for the extension part now
  729. } else {
  730. if (pEnd[-1]=='.')
  731. pEnd[-1]=0;
  732. else
  733. pEnd[0] = TEXT('\0');
  734. }
  735. QualifyPath(pPath);
  736. }
  737. /*--------------------------------------------------------------------------*/
  738. /* */
  739. /* IsInvalidPath() - */
  740. /* */
  741. /*--------------------------------------------------------------------------*/
  742. /* Checks to see if a file spec is an evil character device or if it is
  743. * too long...
  744. */
  745. BOOL
  746. IsInvalidPath(
  747. register LPSTR pPath
  748. )
  749. {
  750. CHAR sz[9];
  751. INT n = 0;
  752. if (lstrlen(pPath) >= MAXPATHLEN-1)
  753. return (TRUE);
  754. pPath = FindFileName(pPath);
  755. while (*pPath && *pPath != '.' && *pPath != ':' && n < 8) {
  756. if (IsDBCSLeadByte( *pPath )) {
  757. if (n == 7)
  758. break;
  759. sz[n++] = *pPath;
  760. }
  761. sz[n++] = *pPath++;
  762. }
  763. sz[n] = TEXT('\0');
  764. if (!lstrcmpi(sz,"CON"))
  765. return (TRUE);
  766. if (!lstrcmpi(sz,"MS$MOUSE"))
  767. return (TRUE);
  768. if (!lstrcmpi(sz,"EMMXXXX0"))
  769. return (TRUE);
  770. if (!lstrcmpi(sz,"CLOCK$"))
  771. return (TRUE);
  772. return (FALSE);
  773. }
  774. PLFNDTA CurPDTA(PCOPYROOT pcr)
  775. {
  776. if (pcr->cDepth) {
  777. return (pcr->rgDTA + pcr->cDepth - 1);
  778. } else {
  779. return pcr->rgDTA;
  780. }
  781. }
  782. /*--------------------------------------------------------------------------*/
  783. /* */
  784. /* GetNextCleanup() - */
  785. /* */
  786. /*--------------------------------------------------------------------------*/
  787. VOID
  788. GetNextCleanup(
  789. PCOPYROOT pcr
  790. )
  791. {
  792. while (pcr->cDepth) {
  793. WFFindClose(CurPDTA(pcr));
  794. pcr->cDepth--;
  795. }
  796. }
  797. /* GetNameDialog
  798. *
  799. * Runs the dialog box to prompt the user for a new filename when copying
  800. * or moving from HPFS to FAT.
  801. */
  802. WORD GetNameDialog(WORD, LPSTR, LPSTR);
  803. INT_PTR APIENTRY GetNameDlgProc(HWND,UINT,WPARAM,LPARAM);
  804. WORD wDialogOp;
  805. LPSTR pszDialogFrom;
  806. LPSTR pszDialogTo;
  807. INT_PTR
  808. APIENTRY
  809. GetNameDlgProc(
  810. HWND hwnd,
  811. UINT wMsg,
  812. WPARAM wParam,
  813. LPARAM lParam
  814. )
  815. {
  816. CHAR szT[14];
  817. LPSTR p;
  818. INT i, j, cMax, fDot;
  819. UNREFERENCED_PARAMETER(lParam);
  820. switch (wMsg) {
  821. case WM_INITDIALOG:
  822. // inform the user of the old name
  823. SetDlgItemText(hwnd, IDD_FROM, pszDialogFrom);
  824. // generate a guess for the new name
  825. p = FindFileName(pszDialogFrom);
  826. for (i = j = fDot = 0, cMax = 8; *p; p++) {
  827. if (*p == '.') {
  828. // if there was a previous dot, step back to it
  829. // this way, we get the last extension
  830. if (fDot)
  831. i -= j+1;
  832. // set number of chars to 0, put the dot in
  833. j = 0;
  834. szT[i++] = '.';
  835. // remember we saw a dot and set max 3 chars.
  836. fDot = TRUE;
  837. cMax = 3;
  838. } else if (j < cMax && IsValidChar(*p,FALSE)) {
  839. if (IsDBCSLeadByte(*p)) {
  840. szT[i] = *p++;
  841. if (++j >= cMax)
  842. continue;
  843. ++i;
  844. }
  845. j++;
  846. szT[i++] = *p;
  847. }
  848. }
  849. szT[i] = 0;
  850. SetDlgItemText(hwnd, IDD_TO, szT);
  851. SendDlgItemMessage(hwnd,IDD_TO,EM_LIMITTEXT,13,0L);
  852. // directory the file will go into
  853. RemoveLast(pszDialogTo);
  854. SetDlgItemText(hwnd, IDD_DIR, pszDialogTo);
  855. break;
  856. case WM_COMMAND:
  857. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  858. case IDOK:
  859. GetDlgItemText(hwnd,IDD_TO,szT,14);
  860. AppendToPath(pszDialogTo,szT);
  861. QualifyPath(pszDialogTo);
  862. EndDialog(hwnd,IDOK);
  863. break;
  864. case IDCANCEL:
  865. EndDialog(hwnd,IDCANCEL);
  866. break;
  867. case IDD_HELP:
  868. goto DoHelp;
  869. case IDD_TO:
  870. GetDlgItemText(hwnd,IDD_TO,szT,14);
  871. for (p = szT; *p; p=AnsiNext(p))
  872. {
  873. if (!IsValidChar(*p,FALSE))
  874. break;
  875. }
  876. EnableWindow(GetDlgItem(hwnd,IDOK),((!*p) && (p != szT)));
  877. break;
  878. default:
  879. return FALSE;
  880. }
  881. break;
  882. default:
  883. if (wMsg == wHelpMessage) {
  884. DoHelp:
  885. WFHelp(hwnd);
  886. return TRUE;
  887. }
  888. return FALSE;
  889. }
  890. return TRUE;
  891. }
  892. WORD
  893. GetNameDialog(
  894. WORD wOp,
  895. LPSTR pFrom,
  896. LPSTR pTo
  897. )
  898. {
  899. WORD wRet = -1;
  900. DWORD dwSave;
  901. dwSave = dwContext;
  902. dwContext = IDH_DLGFIRST + LFNTOFATDLG;
  903. wDialogOp = wOp;
  904. pszDialogFrom = pFrom;
  905. pszDialogTo = pTo;
  906. wRet = (WORD)DialogBox(hAppInstance, MAKEINTRESOURCE(LFNTOFATDLG), hdlgProgress, GetNameDlgProc);
  907. dwContext = dwSave;
  908. return wRet;
  909. }
  910. /*============================================================================
  911. ;
  912. ; GetNextPair
  913. ;
  914. ; The following function determines the next pair of files to copy, rename,
  915. ; move, or delete.
  916. ;
  917. ; Parameters:
  918. ;
  919. ; pcr - Pointer to structure for recursing directory tree
  920. ; pFrom - Source file or directory to copy
  921. ; pToPath - Path to destination file or directory
  922. ; pToSpec - Raw destination file or directory name
  923. ; wFunc - Operation being performed. Can be one of:
  924. ;
  925. ; FUNC_DELETE - Delete files in pFrom
  926. ; FUNC_RENAME - Rename files (same directory)
  927. ; FUNC_MOVE - Move files in pFrom to pTo (different disk)
  928. ; FUNC_COPY - Copy files in pFrom to pTo
  929. ;
  930. ; Return Value: Type of operation to perform. Can be one of:
  931. ;
  932. ; OPER_ERROR - Error processing filenames
  933. ; OPER_DOFILE - Go ahead and copy, rename, or delete file
  934. ; OPER_MKDIR - Make a directory specified in pTo
  935. ; OPER_RMDIR - Remove directory
  936. ; 0 - No more files left
  937. ;
  938. ; Revision History:
  939. ;
  940. ; Modified by C. Stevens, August, 1991. Added logic so that we would call
  941. ; IsTheDiskReallyThere only once per drive. Also changed some of the code
  942. ; to minimize the number of calls which access the disk.
  943. ;
  944. ============================================================================*/
  945. WORD
  946. GetNextPair(
  947. PCOPYROOT pcr,
  948. PSTR pFrom,
  949. PSTR pToPath,
  950. PSTR pToSpec,
  951. WORD wFunc
  952. )
  953. {
  954. PSTR pT; /* Temporary pointer */
  955. WORD wOp; /* Return value (operation to perform */
  956. PLFNDTA pDTA; /* Pointer to file DTA data */
  957. CHAR szOEM[MAXPATHLEN+1]; /* OEM version of string */
  958. STKCHK();
  959. *pFrom = TEXT('\0');
  960. dbg(("GetNextPair(-,-,%s,%s,%d);\r\n", (LPSTR)pToPath, (LPSTR)pToSpec, wFunc));
  961. /* Keep recursing directory structure until we get to the bottom */
  962. while (TRUE) {
  963. dbg ((" top of loop....\r\n"));
  964. if (pcr->cDepth) {
  965. /* The directory we returned last call needs to be recursed. */
  966. pDTA = pcr->rgDTA + pcr->cDepth - 1; // use this DTA below
  967. dbg ((" pcr->cDepth=%d\r\n",pcr->cDepth));
  968. if (pcr->fRecurse && pcr->cDepth == 1 && !pcr->rgDTA[0].fd.cFileName[0])
  969. /* The last one was the recursion root. */
  970. goto BeginDirSearch;
  971. if (pcr->cDepth >= (MAXDIRDEPTH - 1)) { // reached the limit?
  972. wOp = OPER_ERROR | DE_PATHTODEEP;
  973. goto ReturnPair;
  974. }
  975. if (pcr->fRecurse && (pDTA->fd.dwFileAttributes & ATTR_DIR) &&
  976. !(pDTA->fd.dwFileAttributes & ATTR_RETURNED)) {
  977. /* Was returned on last call, begin search. */
  978. pDTA->fd.dwFileAttributes |= ATTR_RETURNED;
  979. pcr->cDepth++;
  980. pDTA++;
  981. BeginDirSearch:
  982. /* Search for all subfiles in directory. */
  983. dbg ((" BeginDirSearch\r\n"));
  984. AppendToPath (pcr->sz,szStarDotStar);
  985. goto BeginSearch;
  986. }
  987. SkipThisFile:
  988. /* Search for the next matching file. */
  989. dbg ((" SkipThisFile:\r\n"));
  990. if (!WFFindNext (pDTA)) {
  991. dbg ((" FindNext() fails\r\n"));
  992. WFFindClose (pDTA);
  993. LeaveDirectory:
  994. /* This spec has been exhausted... */
  995. pcr->cDepth--;
  996. /* Remove the child file spec. */
  997. RemoveLast (pcr->sz);
  998. RemoveLast (pcr->szDest);
  999. if (pcr->fRecurse) {
  1000. /* Tell the move/copy driver it can now delete
  1001. the source directory if necessary. */
  1002. wOp = OPER_RMDIR;
  1003. goto ReturnPair;
  1004. }
  1005. /* Not recursing, get more stuff. */
  1006. continue;
  1007. }
  1008. ProcessSearchResult:
  1009. /* Got a file or dir in the DTA which matches the wild card
  1010. originally passed in... */
  1011. dbg ((" ProcessSearchResult:\r\n"));
  1012. dbg ((" found %s\r\n",(LPSTR)pDTA->fd.cFileName));
  1013. if (pDTA->fd.dwFileAttributes & ATTR_DIR) {
  1014. /* Ignore directories if we're not recursing. */
  1015. if (!pcr->fRecurse)
  1016. goto SkipThisFile;
  1017. /* Skip the current and parent directories. */
  1018. if (pDTA->fd.cFileName[0]=='.') {
  1019. if (!pDTA->fd.cFileName[1] || pDTA->fd.cFileName[1] == '.')
  1020. goto SkipThisFile;
  1021. }
  1022. /* We need to create this directory, and then begin searching
  1023. for subfiles. */
  1024. wOp = OPER_MKDIR;
  1025. RemoveLast (pcr->sz);
  1026. OemToAnsi (pDTA->fd.cFileName,pDTA->fd.cFileName);
  1027. AppendToPath (pcr->sz,pDTA->fd.cFileName);
  1028. AppendToPath (pcr->szDest,pDTA->fd.cFileName);
  1029. goto ReturnPair;
  1030. }
  1031. if (pcr->fRecurse || !(pDTA->fd.dwFileAttributes & ATTR_DIR)) {
  1032. /* Remove the original spec. */
  1033. RemoveLast (pcr->sz);
  1034. /* Replace it. */
  1035. AppendToPath (pcr->sz,pDTA->fd.cFileName);
  1036. /* Convert to ANSI. */
  1037. pT = FindFileName (pcr->sz);
  1038. OemToAnsi (pT,pT);
  1039. /* If its a dir, tell the driver to create it
  1040. otherwise, tell the driver to "operate" on the file. */
  1041. wOp = (WORD)((pDTA->fd.dwFileAttributes & ATTR_DIR) ? OPER_RMDIR : OPER_DOFILE);
  1042. goto ReturnPair;
  1043. }
  1044. continue;
  1045. } else {
  1046. /* Read the next source spec out of the raw source string. */
  1047. pcr->fRecurse = 0;
  1048. pcr->pSource = GetNextFile (pcr->pSource,pcr->sz,sizeof(pcr->sz));
  1049. pcr->szDest[0] = 0;
  1050. if (!pcr->pSource)
  1051. return (0);
  1052. /* Fully qualify the path */
  1053. QualifyPath(pcr->sz);
  1054. /* Ensure the source disk really exists before doing anything.
  1055. Only call IsTheDiskReallyThere once for each drive letter.
  1056. Set pcr->cIsDiskThereCheck[DRIVEID] after disk has been
  1057. checked. Modified by C. Stevens, August 1991 */
  1058. if (pcr->sz[1]==':' && !pcr->cIsDiskThereCheck[DRIVEID (pcr->sz)]) {
  1059. if (!IsTheDiskReallyThere(hdlgProgress, pcr->sz, wFunc))
  1060. return (0);
  1061. pcr->cIsDiskThereCheck[DRIVEID (pcr->sz)] = 1;
  1062. }
  1063. /* Classify the input string. */
  1064. if (IsWild (pcr->sz)) {
  1065. /* Wild card... operate on all matches but not recursively. */
  1066. pcr->cDepth = 1;
  1067. pDTA = pcr->rgDTA;
  1068. pcr->pRoot = NULL;
  1069. BeginSearch:
  1070. dbg ((" BeginSearch: (on %s)\r\n",(LPSTR)pcr->sz));
  1071. /* Quit if pcr->sz gets too big. */
  1072. if (lstrlen (pcr->sz) - lstrlen (FindFileName (pcr->sz)) >= MAXPATHLEN)
  1073. goto SearchStartFail;
  1074. lstrcpy (szOEM,pcr->sz);
  1075. FixAnsiPathForDos (szOEM);
  1076. /* Search for the wildcard spec in pcr->sz. */
  1077. if (!WFFindFirst(pDTA, szOEM, ATTR_ALL)) {
  1078. SearchStartFail:
  1079. dbg((" StartSearchFail:\r\n"));
  1080. if (pcr->fRecurse) {
  1081. /* We are inside a recursive directory delete, so
  1082. instead of erroring out, go back a level */
  1083. goto LeaveDirectory;
  1084. }
  1085. lstrcpy (pFrom,pcr->sz);
  1086. /* Back up as if we completed a search. */
  1087. RemoveLast (pcr->sz);
  1088. pcr->cDepth--;
  1089. /* Find First returned an error. Return FileNotFound. */
  1090. wOp = OPER_ERROR | DE_FILENOTFOUND;
  1091. goto ReturnPair;
  1092. }
  1093. goto ProcessSearchResult;
  1094. } else {
  1095. /* This could be a file or a directory. Fill in the DTA
  1096. structure for attrib check */
  1097. if (!IsRootDirectory(pcr->sz)) {
  1098. lstrcpy(szOEM,pcr->sz);
  1099. FixAnsiPathForDos(szOEM);
  1100. if (!WFFindFirst(pcr->rgDTA, szOEM, ATTR_ALL)) {
  1101. wOp = OPER_ERROR | DE_FILENOTFOUND;
  1102. goto ReturnPair;
  1103. }
  1104. WFFindClose(pcr->rgDTA);
  1105. }
  1106. /* Now determine if its a file or a directory */
  1107. pDTA = pcr->rgDTA;
  1108. if (IsRootDirectory(pcr->sz) || (pDTA->fd.dwFileAttributes & ATTR_DIR)) {
  1109. /* Process directory */
  1110. if (wFunc == FUNC_RENAME) {
  1111. if (IsRootDirectory (pcr->sz))
  1112. wOp = OPER_ERROR | DE_ROOTDIR;
  1113. else
  1114. wOp = OPER_DOFILE;
  1115. goto ReturnPair;
  1116. }
  1117. /* Directory: operation is recursive. */
  1118. pcr->fRecurse = TRUE;
  1119. pcr->cDepth = 1;
  1120. pDTA->fd.cFileName[0] = 0;
  1121. pcr->pRoot = FindFileName (pcr->sz);
  1122. lstrcpy (pcr->szDest,pcr->pRoot);
  1123. wOp = OPER_MKDIR;
  1124. goto ReturnPair;
  1125. } else {
  1126. /* Process file */
  1127. pcr->pRoot = NULL;
  1128. wOp = OPER_DOFILE;
  1129. goto ReturnPair;
  1130. }
  1131. }
  1132. }
  1133. }
  1134. ReturnPair:
  1135. /* The source filespec has been derived into pcr->sz
  1136. that is copied to pFrom. pcr->sz and pToSpec are merged into pTo. */
  1137. dbg((" ReturnPair:\r\n"));
  1138. if (!*pFrom)
  1139. lstrcpy(pFrom,pcr->sz);
  1140. QualifyPath(pFrom);
  1141. if (wFunc != FUNC_DELETE) {
  1142. if (wFunc == FUNC_RENAME && !*pToPath) {
  1143. lstrcpy(pToPath, pFrom);
  1144. RemoveLast(pToPath);
  1145. AppendToPath(pToPath, pToSpec);
  1146. } else {
  1147. AppendToPath(pToPath,pcr->szDest);
  1148. if (wOp == OPER_MKDIR)
  1149. RemoveLast(pToPath);
  1150. AppendToPath(pToPath,pToSpec);
  1151. }
  1152. if ((wOp == OPER_MKDIR || wOp == OPER_DOFILE) &&
  1153. (!IsLFNDrive(pToPath) && IsLFNDrive(pFrom)) &&
  1154. IsLFN (FindFileName (pFrom)) &&
  1155. (IsWild(pToSpec) || IsLFN(pToSpec))) {
  1156. if (GetNameDialog(wOp, pFrom, pToPath) != IDOK)
  1157. return 0; /* User cancelled the operation, return failure */
  1158. /* Update the "to" path with the FAT name chosen by the user. */
  1159. if (wOp == OPER_MKDIR) {
  1160. RemoveLast(pcr->szDest);
  1161. AppendToPath(pcr->szDest, FindFileName(pToPath));
  1162. }
  1163. } else
  1164. MergePathName(pToPath, FindFileName(pFrom));
  1165. }
  1166. if (wOp == OPER_MKDIR) {
  1167. /* Make sure the new directory is not a subdir of the original... */
  1168. while (*pFrom && *pFrom == *pToPath) {
  1169. pFrom++;
  1170. pToPath++;
  1171. }
  1172. if (!*pFrom && (!*pToPath || *pToPath == '\\')) {
  1173. /* The two fully qualified strings are equal up to the end of the
  1174. source directory ==> the destination is a subdir.Must return
  1175. an error. */
  1176. wOp = OPER_ERROR | DE_DESTSUBTREE;
  1177. }
  1178. }
  1179. return wOp;
  1180. }
  1181. VOID
  1182. CdDotDot (
  1183. PSTR szOrig
  1184. )
  1185. {
  1186. CHAR szTemp[MAXPATHLEN];
  1187. STKCHK();
  1188. lstrcpy(szTemp, szOrig);
  1189. StripFilespec(szTemp);
  1190. SheChangeDir(szTemp);
  1191. }
  1192. /* p is a fully qualified ANSI string. */
  1193. BOOL
  1194. IsCurrentDirectory (
  1195. PSTR p
  1196. )
  1197. {
  1198. CHAR szTemp[MAXPATHLEN];
  1199. STKCHK();
  1200. SheGetDir(DRIVEID(p) + 1, szTemp);
  1201. OemToAnsi(szTemp, szTemp);
  1202. return (lstrcmpi(szTemp, p) == 0);
  1203. }
  1204. //
  1205. // test input for "multiple" filespec
  1206. //
  1207. // examples:
  1208. // 0 foo.bar (single non directory file)
  1209. // 1 *.exe (wild card)
  1210. // 1 foo.bar bletch.txt (multiple files)
  1211. // 2 c:\ (directory)
  1212. //
  1213. // note: this may hit the disk in the directory check
  1214. //
  1215. INT
  1216. CheckMultiple(
  1217. register PSTR pInput
  1218. )
  1219. {
  1220. PSTR pT;
  1221. CHAR szTemp[MAXPATHLEN];
  1222. /* Wildcards imply multiple files. */
  1223. if (IsWild(pInput))
  1224. return 1; // wild card
  1225. /* More than one thing implies multiple files. */
  1226. pT = GetNextFile(pInput, szTemp, sizeof(szTemp));
  1227. if (!pT)
  1228. return 0; // blank string
  1229. StripBackslash(szTemp);
  1230. if (IsDirectory(szTemp))
  1231. return 2; // directory
  1232. pT = GetNextFile(pT, szTemp, sizeof(szTemp));
  1233. return pT ? 1 : 0; // several files, or just one
  1234. }
  1235. /*--------------------------------------------------------------------------*/
  1236. /* */
  1237. /* DialogEnterFileStuff() - */
  1238. /* */
  1239. /*--------------------------------------------------------------------------*/
  1240. /* Prevents the user from diddling anything other than the cancel button. */
  1241. VOID
  1242. DialogEnterFileStuff(
  1243. register HWND hwnd
  1244. )
  1245. {
  1246. register HWND hwndT;
  1247. /* set the focus to the cancel button so the user can hit space or esc
  1248. */
  1249. if (hwndT = GetDlgItem(hwnd, IDCANCEL)) {
  1250. SetFocus(hwndT);
  1251. SendMessage(hwnd,DM_SETDEFID,IDCANCEL,0L);
  1252. }
  1253. /* disable the ok button and the edit controls
  1254. */
  1255. if (hwndT = GetDlgItem(hwnd, IDOK))
  1256. EnableWindow(hwndT, FALSE);
  1257. if (hwndT = GetDlgItem(hwnd, IDD_TO))
  1258. EnableWindow(hwndT, FALSE);
  1259. if (hwndT = GetDlgItem(hwnd, IDD_FROM))
  1260. EnableWindow(hwndT, FALSE);
  1261. }
  1262. /*--------------------------------------------------------------------------*/
  1263. /* */
  1264. /* Notify() - */
  1265. /* */
  1266. /*--------------------------------------------------------------------------*/
  1267. /* Sets the status dialog item in the modeless status dialog box. */
  1268. // used for both the drag drop status dialogs and the manual user
  1269. // entry dialogs so be careful what you change
  1270. VOID
  1271. Notify(
  1272. HWND hDlg,
  1273. WORD idMessage,
  1274. PSTR szFrom,
  1275. PSTR szTo
  1276. )
  1277. {
  1278. CHAR szTemp[40];
  1279. if (!bCopyReport)
  1280. return;
  1281. if (idMessage) {
  1282. LoadString(hAppInstance, idMessage, szTemp, sizeof(szTemp));
  1283. SetDlgItemText(hDlg, IDD_STATUS, szTemp);
  1284. SetDlgItemPath(hDlg, IDD_NAME, szFrom);
  1285. } else {
  1286. SetDlgItemText(hDlg, IDD_STATUS, szNULL);
  1287. SetDlgItemText(hDlg, IDD_NAME, szNULL);
  1288. }
  1289. // is this the drag/drop status dialog or the move/copy dialog
  1290. SetDlgItemPath(hDlg, IDD_TONAME, szTo);
  1291. }
  1292. //
  1293. // BOOL IsWindowsFile(LPSTR szFileOEM)
  1294. //
  1295. // this is a bit strange. kernel strips off the path info so he
  1296. // will match only on the base name of the file. so if the base
  1297. // name matches a currently open windows file we get the full
  1298. // path string and compare against that. that will tell
  1299. // us that we have a file that kernel has open.
  1300. //
  1301. // LFN: detect long names and ignore them?
  1302. BOOL
  1303. IsWindowsFile(
  1304. LPSTR szFileOEM
  1305. )
  1306. {
  1307. HANDLE hMod;
  1308. CHAR szModule[MAXPATHLEN];
  1309. STKCHK();
  1310. /* kernel can't load an lfn...
  1311. */
  1312. if (GetNameType(szFileOEM) == FILE_LONG)
  1313. return FALSE;
  1314. // kernel won't accept long paths
  1315. lstrcpy(szModule, szFileOEM);
  1316. StripPath(szModule);
  1317. hMod = GetModuleHandle(szModule);
  1318. // check for one cause that's what's returned if its MSDOS
  1319. // but it isn't really loaded because of xl 2.1c kernel hack
  1320. if (!hMod || hMod == (HANDLE)1)
  1321. return FALSE;
  1322. GetModuleFileName(hMod, szModule, sizeof(szModule));
  1323. if (!lstrcmpi(szFileOEM, szModule)) // they are both OEM & we
  1324. return TRUE; // just care about equality
  1325. else
  1326. return FALSE;
  1327. }
  1328. WORD
  1329. SafeFileRemove(
  1330. LPSTR szFileOEM
  1331. )
  1332. {
  1333. if (IsWindowsFile(szFileOEM))
  1334. return DE_WINDOWSFILE;
  1335. else
  1336. return WFRemove(szFileOEM);
  1337. }
  1338. INT
  1339. APIENTRY
  1340. WF_CreateDirectory(
  1341. HWND hwndParent,
  1342. LPSTR szDestOEM
  1343. )
  1344. {
  1345. INT ret = 0;
  1346. CHAR szTemp[MAXPATHLEN + 1]; // +1 for AddBackslash()
  1347. LPSTR p;
  1348. BOOL bCheckPath = IsRemoteDrive(DRIVEID(szDestOEM));
  1349. STKCHK();
  1350. #ifdef DEBUG
  1351. if (szDestOEM[1] != ':')
  1352. OutputDebugString("CreateDirectory() with non qualified path\r\n");
  1353. #endif
  1354. // now create the full dir tree on the destination
  1355. strncpy(szTemp, szDestOEM, sizeof(szTemp)-1);
  1356. AddBackslash(szTemp); // for the loop below
  1357. p = szTemp + 3; // assume we have 'X:\' to start
  1358. // create each part of the dir in order
  1359. while (*p) {
  1360. while (*p && *p != '\\')
  1361. p = AnsiNext(p);
  1362. if (*p) {
  1363. *p = 0;
  1364. if (!(ret = MKDir(szTemp))) {
  1365. if (bCheckPath) {
  1366. static CHAR szTempTemp[] = "temptemp.tmp";
  1367. BOOL bFoundFile = FALSE;
  1368. PSTR pEnd;
  1369. CHAR szTempFile[MAXPATHLEN+sizeof(szTempTemp)];
  1370. LFNDTA DTA;
  1371. HDC hDC;
  1372. INT fh;
  1373. /* Note that this assumes the dir has just been created,
  1374. * so it is empty (except possibly for "." and "..")
  1375. */
  1376. lstrcpy(szTempFile, szTemp);
  1377. pEnd = szTempFile + lstrlen(szTempFile);
  1378. *pEnd++ = '\\';
  1379. lstrcpy(pEnd, szTempTemp);
  1380. if (fh=_lcreat(szTempFile, 0)!= -1) {
  1381. _lclose(fh);
  1382. lstrcpy(pEnd, szStarDotStar);
  1383. if (WFFindFirst(&DTA, szTempFile, ATTR_ALL&(~ATTR_DIR))) {
  1384. do {
  1385. if (!lstrcmpi(DTA.fd.cFileName, szTempTemp)) {
  1386. bFoundFile = TRUE;
  1387. break;
  1388. }
  1389. } while (WFFindNext(&DTA)) ;
  1390. WFFindClose(&DTA);
  1391. }
  1392. lstrcpy(pEnd, szTempTemp);
  1393. WFRemove(szTempFile);
  1394. }
  1395. if (!bFoundFile) {
  1396. *(pEnd-1) = '\0';
  1397. hDC = GetDC(NULL);
  1398. CompactPath(hDC, szTempFile, (WORD)(GetSystemMetrics(SM_CXSCREEN)/2));
  1399. ReleaseDC(NULL, hDC);
  1400. LoadString(hAppInstance, IDS_CREATELONGDIR,
  1401. szTitle, sizeof(szTitle));
  1402. wsprintf(szMessage, szTitle, (LPSTR)szTempFile);
  1403. LoadString(hAppInstance, IDS_CREATELONGDIRTITLE,
  1404. szTitle, sizeof(szTitle));
  1405. if (MessageBox(hwndParent, szMessage, szTitle,
  1406. MB_ICONHAND|MB_YESNO) != IDYES) {
  1407. RMDir(szTemp);
  1408. return (DE_OPCANCELLED);
  1409. }
  1410. }
  1411. }
  1412. /* Allow the WM_FILESYSCHANGE messages to be processed
  1413. */
  1414. wfYield();
  1415. }
  1416. *p++ = '\\';
  1417. }
  1418. }
  1419. return ret; // return the last error code
  1420. }
  1421. /*============================================================================
  1422. ;
  1423. ; WFMoveCopyDriver
  1424. ;
  1425. ; The following function is the mainline function for COPYing, RENAMEing,
  1426. ; DELETEing, and MOVEing single or multiple files.
  1427. ;
  1428. ; Parameters:
  1429. ;
  1430. ; pFrom - String containing list of source specs
  1431. ; pTo - String containing destination specs
  1432. ; wFunc - Operation to be performed. Possible values are:
  1433. ; FUNC_DELETE - Delete files in pFrom
  1434. ; FUNC_RENAME - Rename files (same directory)
  1435. ; FUNC_MOVE - Move files in pFrom to pTo (different disk)
  1436. ; FUNC_COPY - Copy files in pFrom to pTo
  1437. ;
  1438. ; Return Value: A 0 indicates success.
  1439. ;
  1440. ; Modification History:
  1441. ;
  1442. ; August 1991 - Modified by C. Stevens. Added code to allow us to queue
  1443. ; calls to GetNextPair. The purpose of this is to examine as
  1444. ; many source files at once as possible. This keeps the source
  1445. ; disk spinning, so we don't suffer from having to wait for the
  1446. ; source disk to speed up every time we call GetNextPair. Also
  1447. ; see the comments for WFCopy and FileCopy. I have changed the
  1448. ; code here so we can queue the copy operations. This allows
  1449. ; us to open several source and destination files in one go,
  1450. ; minimizing seek time to the directory track.
  1451. ;
  1452. ============================================================================*/
  1453. WORD
  1454. APIENTRY
  1455. WFMoveCopyDriver(
  1456. PSTR pFrom,
  1457. PSTR pTo,
  1458. WORD wFunc
  1459. )
  1460. {
  1461. INT i; // Counter
  1462. WORD ret = 0; // Return value from WFMoveCopyDriver
  1463. PSTR pSpec; // Pointer to file spec
  1464. WORD wAttr; // File attributes
  1465. WORD oper = 0; // Disk operation being performed
  1466. CHAR szDestSpec[MAXFILENAMELEN+1]; // Dest file spec
  1467. CHAR szDest[MAXPATHLEN]; // Dest file (ANSI string)
  1468. CHAR szDestOEM[MAXPATHLEN]; // OEM version of above
  1469. CHAR szSource[MAXPATHLEN]; // Source file (ANSI string)
  1470. CHAR szSourceOEM[MAXPATHLEN]; // OEM version of above
  1471. LFNDTA DTADest; // DTA block for reporting dest errors
  1472. PLFNDTA pDTA; // DTA pointer for source errors
  1473. PCOPYROOT pcr; // Structure for searching source tree
  1474. BOOL bReplaceAll = FALSE; // Replace all flag
  1475. BOOL bSubtreeDelAll = FALSE; // Delete entire subtree flag
  1476. BOOL bDeleteAll = FALSE; // Delete all files flag
  1477. BOOL bFalse = FALSE; // For cases that aren't disableable
  1478. INT nNumQueue; // Number of calls to GetNextPair
  1479. PGETNEXTQUEUE pGetNextQueue = NULL;// Pointer to GetNextPair queue buffer
  1480. INT CurIDS = 0; // Current string displayed in status
  1481. /* Initialization stuff. Disable all file system change processing until
  1482. we're all done */
  1483. STKCHK();
  1484. bCopyReport = TRUE;
  1485. szDest[0] = szSource[0] = 0;
  1486. DisableFSC();
  1487. /* Change all '/' characters to '\' characters in dest spec */
  1488. CheckSlashies(pFrom);
  1489. bUserAbort = FALSE;
  1490. /* Check for multiple source files */
  1491. ManySource = CheckMultiple(pFrom);
  1492. /* Allocate buffer for searching the source tree */
  1493. pcr = (PCOPYROOT)LocalAlloc(LPTR, sizeof(COPYROOT));
  1494. if (!pcr) {
  1495. ret = DE_INSMEM;
  1496. goto ShowMessageBox;
  1497. }
  1498. /* Allocate a buffer so we can queue calls to GetNextPair. */
  1499. pGetNextQueue = (PGETNEXTQUEUE)LocalAlloc(LPTR, COPYMAXFILES * sizeof (GETNEXTQUEUE));
  1500. if (!pGetNextQueue) {
  1501. ret = DE_INSMEM;
  1502. goto ShowMessageBox;
  1503. }
  1504. /* Skip destination specific processing if we are deleting files */
  1505. if (wFunc != FUNC_DELETE) {
  1506. // it is an error condition if there are multiple files
  1507. // specified as the dest (but not a single directory)
  1508. pSpec = GetNextFile(pTo, szMessage, MAXPATHLEN);
  1509. if (GetNextFile(pSpec, szMessage, MAXPATHLEN) != NULL) {
  1510. // move, copy specified with multiple destinations
  1511. // not allowed, error case
  1512. ret = DE_MANYDEST;
  1513. goto ShowMessageBox;
  1514. }
  1515. lstrcpy(pTo, szMessage);
  1516. QualifyPath(pTo);
  1517. if (wFunc == FUNC_RENAME) {
  1518. // don't let them rename multiple files to one single file
  1519. if ((ManySource == 1) && !IsWild(pTo)) {
  1520. ret = DE_MANYSRC1DEST;
  1521. goto ShowMessageBox;
  1522. }
  1523. } else {
  1524. /* We are either executing FUNC_COPY or FUNC_MOVE at this point.
  1525. Check that the destination disk is there. NOTE: There's a disk
  1526. access here slowing us down. */
  1527. if (!IsTheDiskReallyThere(hdlgProgress,pTo,wFunc))
  1528. goto CancelWholeOperation;
  1529. // deal with case where directory is implicit in source
  1530. // move/copy: *.* -> c:\windows, c:\windows -> c:\temp
  1531. // or foo.bar -> c:\temp
  1532. if (!IsWild(pTo) && (ManySource || IsDirectory(pTo))) {
  1533. AddBackslash(pTo);
  1534. lstrcat(pTo, szStarDotStar);
  1535. }
  1536. }
  1537. /* FUNC_RENAME or FUNC_MOVE FUNC_COPY with a file name dest
  1538. (possibly including wildcards). Save the filespec and the path
  1539. part of the destination */
  1540. pSpec = FindFileName(pTo);
  1541. lstrcpy(szDestSpec,pSpec);
  1542. lstrcpy(szDest,pTo);
  1543. RemoveLast(szDest);
  1544. pSpec = szDest + lstrlen(szDest);
  1545. }
  1546. pcr->pSource = pFrom;
  1547. /* Disable all but the cancel button on the notify dialog */
  1548. DialogEnterFileStuff(hdlgProgress);
  1549. /* Set up arguments for queued copy commands */
  1550. lpCopyBuffer = NULL;
  1551. pCopyQueue = NULL;
  1552. while (pcr) {
  1553. /* Allow the user to abort the operation */
  1554. if (WFQueryAbort())
  1555. goto CancelWholeOperation;
  1556. /* Now queue up a bunch of GetNextPair calls. */
  1557. for (nNumQueue = 0; nNumQueue < COPYMAXFILES; nNumQueue++) {
  1558. /* Clean off the last filespec for multiple file copies */
  1559. if (wFunc != FUNC_DELETE) {
  1560. *pSpec = TEXT('\0');
  1561. }
  1562. oper = GetNextPair(pcr,szSource,szDest,szDestSpec,wFunc);
  1563. /* Check for no operation or error */
  1564. if (!oper) {
  1565. LocalFree((HANDLE)pcr);
  1566. pcr = NULL;
  1567. break;
  1568. }
  1569. if ((oper & OPER_MASK) == OPER_ERROR) {
  1570. ret = LOBYTE (oper);
  1571. oper = OPER_DOFILE;
  1572. goto ShowMessageBox;
  1573. }
  1574. pGetNextQueue[nNumQueue].nOper = oper;
  1575. lstrcpy(pGetNextQueue[nNumQueue].szSource, szSource);
  1576. lstrcpy(pGetNextQueue[nNumQueue].szDest, szDest);
  1577. pGetNextQueue[nNumQueue].SourceDTA = *CurPDTA(pcr);
  1578. }
  1579. /* Execute the queued GetNextPair calls */
  1580. for (i = 0; i < nNumQueue; i++) {
  1581. /* Allow the user to abort the operation */
  1582. if (WFQueryAbort())
  1583. goto CancelWholeOperation;
  1584. oper = (WORD)pGetNextQueue[i].nOper;
  1585. lstrcpy(szSource, pGetNextQueue[i].szSource);
  1586. lstrcpy(szDest, pGetNextQueue[i].szDest);
  1587. pDTA = &pGetNextQueue[i].SourceDTA;
  1588. dbg(("Gonna do OPER:%x FUNC:%x '%s' and '%s'.\r\n",oper,wFunc, (LPSTR)szSource, (LPSTR)szDest));
  1589. /* Fix up source spec */
  1590. lstrcpy (szSourceOEM,szSource);
  1591. FixAnsiPathForDos (szSourceOEM);
  1592. if (IsInvalidPath (szSource)) {
  1593. ret = DE_ACCESSDENIED;
  1594. goto ShowMessageBox;
  1595. }
  1596. if (wFunc != FUNC_DELETE) {
  1597. /* Fix up dest spec */
  1598. lstrcpy(szDestOEM, szDest);
  1599. FixAnsiPathForDos(szDestOEM);
  1600. if (!lstrcmpi(szSource, szDest)) {
  1601. ret = DE_SAMEFILE;
  1602. goto ShowMessageBox;
  1603. } else if (IsInvalidPath (szDest)) {
  1604. ret = DE_ACCESSDENIED | ERRORONDEST;
  1605. goto ShowMessageBox;
  1606. }
  1607. /* Check to see if we are overwriting an existing file. If so,
  1608. better confirm */
  1609. if (oper == OPER_DOFILE) {
  1610. // we can avoid this expensive call on dos 4.0 and up
  1611. // by using the extended open don't replace option
  1612. if (WFFindFirst(&DTADest, szDestOEM, ATTR_ALL)) {
  1613. WFFindClose(&DTADest);
  1614. if (wFunc == FUNC_RENAME) {
  1615. ret = DE_RENAMREPLACE;
  1616. goto ShowMessageBox;
  1617. }
  1618. // we need to check if we are trying to copy a file
  1619. // over a directory and give a reasonable error message
  1620. switch (wAttr = ConfirmDialog (hdlgProgress,CONFIRMREPLACE,
  1621. szDest,&DTADest,szSource,
  1622. pDTA,bConfirmReplace,
  1623. &bReplaceAll)) {
  1624. case IDYES: /* Perform the delete */
  1625. if ((wFunc == FUNC_MOVE) &&
  1626. (DRIVEID(szSource) == DRIVEID(szDest))) {
  1627. /* For FUNC_MOVE we need to delete the
  1628. * destination first. Do that now. */
  1629. if (DTADest.fd.dwFileAttributes & ATTR_DIR) {
  1630. if (IsCurrentDirectory(szDestOEM))
  1631. CdDotDot(szDestOEM);
  1632. switch (NetCheck(szDest, WNDN_RMDIR)) {
  1633. case WN_SUCCESS:
  1634. /* Remove directory */
  1635. ret = RMDir(szDestOEM);
  1636. break;
  1637. case WN_CONTINUE:
  1638. break;
  1639. case WN_CANCEL:
  1640. goto CancelWholeOperation;
  1641. }
  1642. } else {
  1643. ret = SafeFileRemove (szDestOEM);
  1644. }
  1645. if (ret) {
  1646. ret |= ERRORONDEST;
  1647. goto ShowMessageBox;
  1648. }
  1649. }
  1650. break;
  1651. case IDNO:
  1652. /* Don't perform operation on current file */
  1653. continue;
  1654. case IDCANCEL:
  1655. goto CancelWholeOperation;
  1656. default:
  1657. ret = (WORD) wAttr;
  1658. goto ShowMessageBox;
  1659. }
  1660. }
  1661. }
  1662. }
  1663. /* Now determine which operation to perform */
  1664. switch (oper | wFunc) {
  1665. case OPER_MKDIR | FUNC_COPY: // Create destination directory
  1666. case OPER_MKDIR | FUNC_MOVE: // Create dest, verify source delete
  1667. CurIDS = IDS_CREATINGMSG;
  1668. Notify(hdlgProgress, IDS_CREATINGMSG, szDest, szNULL);
  1669. switch (NetCheck(szDest, WNDN_MKDIR)) {
  1670. case WN_SUCCESS:
  1671. break;
  1672. case WN_CONTINUE:
  1673. goto SkipMKDir;
  1674. case WN_CANCEL:
  1675. goto CancelWholeOperation;
  1676. }
  1677. ret = (WORD)WF_CreateDirectory(hdlgProgress, szDestOEM);
  1678. if (!ret)
  1679. /* set attributes of dest to source (not including the
  1680. subdir and vollabel bits) */
  1681. WFSetAttr(szDestOEM, pDTA->fd.dwFileAttributes & ~(ATTR_DIR|ATTR_VOLUME));
  1682. // if it already exits ingore the error return
  1683. if (ret == DE_ACCESSDENIED)
  1684. ret = 0;
  1685. if (ret)
  1686. ret |= ERRORONDEST;
  1687. /* set attributes of new directory to those of the source */
  1688. SkipMKDir:
  1689. break;
  1690. case OPER_MKDIR | FUNC_DELETE:
  1691. /* Confirm removal of directory on this pass. The directories
  1692. are actually removed on the OPER_RMDIR pass */
  1693. /* We can't delete the root directory, so don't bother
  1694. confirming it */
  1695. if (IsRootDirectory(szSource))
  1696. break;
  1697. switch (wAttr = ConfirmDialog (hdlgProgress,CONFIRMRMDIR,
  1698. NULL,pDTA,szSource, NULL,
  1699. bConfirmSubDel,
  1700. &bSubtreeDelAll)) {
  1701. case IDYES:
  1702. break;
  1703. case IDNO:
  1704. case IDCANCEL:
  1705. goto CancelWholeOperation;
  1706. default:
  1707. ret = (WORD) wAttr;
  1708. goto ShowMessageBox;
  1709. }
  1710. break;
  1711. case OPER_RMDIR | FUNC_MOVE:
  1712. case OPER_RMDIR | FUNC_DELETE:
  1713. CurIDS = IDS_REMOVINGDIRMSG;
  1714. Notify(hdlgProgress, IDS_REMOVINGDIRMSG, szSource, szNULL);
  1715. if (IsRootDirectory (szSource))
  1716. break;
  1717. if (IsCurrentDirectory (szSource))
  1718. CdDotDot (szSource);
  1719. /* We already confirmed the delete at MKDIR time, so attempt
  1720. to delete the directory */
  1721. switch (NetCheck (szSource,WNDN_RMDIR)) {
  1722. case WN_SUCCESS:
  1723. ret = RMDir (szSourceOEM);
  1724. break;
  1725. case WN_CONTINUE:
  1726. break;
  1727. case WN_CANCEL:
  1728. goto CancelWholeOperation;
  1729. }
  1730. break;
  1731. case OPER_RMDIR | FUNC_COPY:
  1732. break;
  1733. case OPER_DOFILE | FUNC_COPY:
  1734. if (IsWindowsFile(szDestOEM)) {
  1735. ret = DE_WINDOWSFILE | ERRORONDEST;
  1736. break;
  1737. }
  1738. TRY_COPY_AGAIN:
  1739. /* Now try to copy the file. Do extra error processing only
  1740. in 2 cases:
  1741. 1) If a floppy is full let the user stick in a new disk
  1742. 2) If the path doesn't exist (the user typed in
  1743. and explicit path that doesn't exits) ask if
  1744. we should create it for him.
  1745. NOTE: This processing is normally done by WFCopy. But in
  1746. the case where LFN copy support is invoked, we have
  1747. to support this error condition here. Modified by
  1748. C. Stevens, August 1991 */
  1749. ret = WFCopy(szSourceOEM, szDestOEM);
  1750. if (bUserAbort)
  1751. goto CancelWholeOperation;
  1752. if ((((ret & ~ERRORONDEST) == DE_NODISKSPACE) &&
  1753. IsRemovableDrive(DRIVEID(szDestOEM))) ||
  1754. ((ret & ~ERRORONDEST) == DE_PATHNOTFOUND)) {
  1755. ret = (WORD)CopyMoveRetry(szDestOEM, (INT)ret);
  1756. if (!ret)
  1757. goto TRY_COPY_AGAIN;
  1758. else
  1759. goto CancelWholeOperation;
  1760. }
  1761. break;
  1762. case OPER_DOFILE | FUNC_RENAME:
  1763. {
  1764. CHAR save1,save2;
  1765. PSTR p;
  1766. if (CurIDS != IDS_RENAMINGMSG) {
  1767. CurIDS = IDS_RENAMINGMSG;
  1768. Notify(hdlgProgress, IDS_RENAMINGMSG, szNULL, szNULL);
  1769. }
  1770. /* Get raw source and dest paths. Check to make sure the
  1771. paths are the same */
  1772. p = FindFileName(szSource);
  1773. save1 = *p;
  1774. *p = TEXT('\0');
  1775. p = FindFileName(szDest);
  1776. save2 = *p;
  1777. *p = TEXT('\0');
  1778. ret = (WORD)lstrcmpi(szSource, szDest);
  1779. szSource[lstrlen(szSource)] = save1;
  1780. szDest[lstrlen(szDest)] = save2;
  1781. if (ret) {
  1782. ret = DE_DIFFDIR;
  1783. break;
  1784. }
  1785. goto DoMoveRename;
  1786. }
  1787. case OPER_DOFILE | FUNC_MOVE:
  1788. if (CurIDS != IDS_MOVINGMSG) {
  1789. CurIDS = IDS_MOVINGMSG;
  1790. Notify(hdlgProgress, IDS_MOVINGMSG, szNULL, szNULL);
  1791. }
  1792. DoMoveRename:
  1793. /* Don't allow the user to rename from or to the root
  1794. directory */
  1795. if (IsRootDirectory(szSource)) {
  1796. ret = DE_ROOTDIR;
  1797. break;
  1798. }
  1799. if (IsRootDirectory(szDest)) {
  1800. ret = DE_ROOTDIR | ERRORONDEST;
  1801. break;
  1802. }
  1803. if (IsCurrentDirectory(szSource))
  1804. CdDotDot(szSource);
  1805. /* Confirm the rename */
  1806. switch (wAttr = ConfirmDialog (hdlgProgress,
  1807. (WORD)(wFunc == FUNC_MOVE ?
  1808. CONFIRMMOVE : CONFIRMRENAME),
  1809. NULL,pDTA,szSource,NULL,FALSE,
  1810. (BOOL *)&bFalse)) {
  1811. case IDYES:
  1812. break;
  1813. case IDNO:
  1814. continue;
  1815. case IDCANCEL:
  1816. goto CancelWholeOperation;
  1817. default:
  1818. ret = (WORD) wAttr;
  1819. goto ShowMessageBox;
  1820. }
  1821. if (IsWindowsFile(szSourceOEM)) {
  1822. ret = DE_WINDOWSFILE;
  1823. } else {
  1824. if (DRIVEID(szSource) == DRIVEID(szDest)) {
  1825. ret = WFMove(szSourceOEM, szDestOEM);
  1826. if (!ret)
  1827. /* set attributes of dest to those of the source */
  1828. WFSetAttr(szDestOEM, pDTA->fd.dwFileAttributes);
  1829. } else {
  1830. // we must force all copies to go through
  1831. // straight so we can remove the source
  1832. // and have the
  1833. ret = WFCopy(szSourceOEM, szDestOEM);
  1834. if (!ret) {
  1835. ret = EndCopy();
  1836. if (!ret)
  1837. WFRemove(szSourceOEM);
  1838. }
  1839. if (bUserAbort)
  1840. goto CancelWholeOperation;
  1841. }
  1842. }
  1843. break;
  1844. case OPER_DOFILE | FUNC_DELETE:
  1845. if (CurIDS != IDS_DELETINGMSG) {
  1846. CurIDS = IDS_DELETINGMSG;
  1847. Notify(hdlgProgress,IDS_DELETINGMSG,szNULL, szNULL);
  1848. }
  1849. /* Confirm the delete first */
  1850. switch (wAttr = ConfirmDialog (hdlgProgress,CONFIRMDELETE,
  1851. NULL,pDTA,szSource,NULL,
  1852. bConfirmDelete,&bDeleteAll)) {
  1853. case IDYES:
  1854. break;
  1855. case IDNO:
  1856. continue;
  1857. case IDCANCEL:
  1858. goto CancelWholeOperation;
  1859. default:
  1860. ret = (WORD)wAttr;
  1861. goto ShowMessageBox;
  1862. }
  1863. /* make sure we don't delete any open windows
  1864. apps or dlls (lets hope this isn't too slow) */
  1865. ret = SafeFileRemove(szSourceOEM);
  1866. break;
  1867. default:
  1868. ret = DE_HOWDIDTHISHAPPEN; // internal error
  1869. break;
  1870. }
  1871. /* Report any errors which have occurred */
  1872. if (ret) {
  1873. ShowMessageBox:
  1874. CopyError(szSource, szDest, ret, wFunc, oper);
  1875. /* Continue the operation where one file is a windows file
  1876. in use */
  1877. if ((ret & ~ERRORONDEST) != DE_WINDOWSFILE) {
  1878. CancelWholeOperation:
  1879. /* Force a CopyAbort in case there are any files in the
  1880. copy queue */
  1881. bUserAbort = TRUE;
  1882. goto ExitLoop;
  1883. }
  1884. }
  1885. }
  1886. }
  1887. ExitLoop:
  1888. /* Copy any outstanding files in the copy queue */
  1889. if (!bUserAbort) {
  1890. if (EndCopy())
  1891. CopyAbort();
  1892. } else
  1893. CopyAbort();
  1894. // this happens in error cases where we broke out of the pcr loop
  1895. // without hitting the end
  1896. if (pcr) {
  1897. GetNextCleanup(pcr);
  1898. LocalFree((HANDLE)pcr);
  1899. }
  1900. if (pGetNextQueue)
  1901. LocalFree((HANDLE)pGetNextQueue);
  1902. /* goofy way to make sure we've gotten all the WM_FILESYSCHANGE messages */
  1903. WFQueryAbort();
  1904. EnableFSC();
  1905. return ret;
  1906. }
  1907. /*--------------------------------------------------------------------------*/
  1908. /* */
  1909. /* DMMoveCopyHelper() - */
  1910. /* */
  1911. /*--------------------------------------------------------------------------*/
  1912. /* Used by Danger Mouse to do moves and copies. */
  1913. WORD
  1914. APIENTRY
  1915. DMMoveCopyHelper(
  1916. register LPSTR pFrom,
  1917. register LPSTR pTo,
  1918. BOOL bCopy
  1919. )
  1920. {
  1921. WORD iStatus;
  1922. dbg(("DMMoveCopyHelper(%s,%s);\r\n",(LPSTR)pFrom,(LPSTR)pTo));
  1923. /* Confirm mouse operations. */
  1924. if (bConfirmMouse) {
  1925. LoadString(hAppInstance, IDS_MOUSECONFIRM, szTitle, sizeof(szTitle));
  1926. LoadString(hAppInstance,
  1927. bCopy ? IDS_COPYMOUSECONFIRM : IDS_MOVEMOUSECONFIRM,
  1928. szMessage, sizeof(szMessage));
  1929. if (MessageBox(hwndFrame, szMessage, szTitle, MB_YESNO | MB_ICONEXCLAMATION) != IDYES)
  1930. return DE_OPCANCELLED;
  1931. }
  1932. hdlgProgress = CreateDialog(hAppInstance, MAKEINTRESOURCE(DMSTATUSDLG), hwndFrame, ProgressDlgProc);
  1933. if (!hdlgProgress) {
  1934. return DE_INSMEM;
  1935. }
  1936. /* Set the destination directory in the dialog.
  1937. * use IDD_TONAME 'cause IDD_TO gets disabled....
  1938. */
  1939. // SetDlgItemText(hdlgProgress, IDD_TONAME, pTo);
  1940. /* The dialog title defaults to "Moving..." */
  1941. if (bCopy) {
  1942. LoadString(hAppInstance, IDS_COPYINGTITLE, szMessage, sizeof(szMessage));
  1943. SetWindowText(hdlgProgress, szMessage);
  1944. }
  1945. /* Display and paint the status dialog. */
  1946. EnableWindow(hwndFrame,FALSE);
  1947. ShowWindow(hdlgProgress, SW_SHOW);
  1948. UpdateWindow(hdlgProgress);
  1949. /* Move/Copy things. */
  1950. iStatus = WFMoveCopyDriver(pFrom, pTo, (WORD)(bCopy ? FUNC_COPY : FUNC_MOVE));
  1951. /* Destroy the status dialog. */
  1952. EnableWindow(hwndFrame,TRUE);
  1953. DestroyWindow(hdlgProgress);
  1954. return (iStatus);
  1955. }
  1956. WORD
  1957. APIENTRY
  1958. FileRemove(
  1959. PSTR pSpec
  1960. )
  1961. {
  1962. if (DeleteFile(pSpec))
  1963. return (WORD)0;
  1964. else
  1965. return (WORD)GetLastError();
  1966. }
  1967. WORD
  1968. APIENTRY
  1969. FileMove(
  1970. PSTR pFrom,
  1971. PSTR pTo
  1972. )
  1973. {
  1974. WORD result;
  1975. TryAgain:
  1976. if (MoveFile(pFrom, pTo))
  1977. result = 0;
  1978. else
  1979. result = (WORD)GetLastError();
  1980. // try to create the destination if it is not there
  1981. if (result == DE_PATHNOTFOUND) {
  1982. result = (WORD)CopyMoveRetry(pTo, (INT)result);
  1983. if (!result)
  1984. goto TryAgain;
  1985. else
  1986. return result;
  1987. }
  1988. return 0;
  1989. }
  1990. /*============================================================================
  1991. ;
  1992. ; FileCopy
  1993. ;
  1994. ; The following function replaces the old FileCopy function which performed
  1995. ; single file copies. This function queues copies. The function StartCopy
  1996. ; is called to initialize the copy queue if required. If the queue is full,
  1997. ; the function EndCopy is called to purge the copy queue before queueing
  1998. ; up new copy commands. Note that the function EndCopy must be called to
  1999. ; purge the copy queue.
  2000. ;
  2001. ; Parameters:
  2002. ;
  2003. ; pszSource - Fully qualified source path
  2004. ; pszDest - Fully qualifies destination path
  2005. ;
  2006. ; returns:
  2007. ; 0 success
  2008. ; dos error code for failure
  2009. ;
  2010. ============================================================================*/
  2011. WORD
  2012. APIENTRY
  2013. FileCopy(
  2014. PSTR pszSource,
  2015. PSTR pszDest
  2016. )
  2017. {
  2018. WORD ret;
  2019. if (ret = StartCopy())
  2020. return ret; // failure
  2021. // if the queue is full we must empty it first
  2022. if (nCopyNumQueue >= nCopyMaxQueue) {
  2023. // queue is full, now we empty it by really doing copies
  2024. if (ret = EndCopy())
  2025. return ret; // failure
  2026. if (ret = StartCopy())
  2027. return ret; // failure
  2028. }
  2029. // add this to the queue
  2030. lstrcpy(pCopyQueue[nCopyNumQueue].szSource, pszSource);
  2031. lstrcpy(pCopyQueue[nCopyNumQueue].szDest, pszDest);
  2032. pCopyQueue[nCopyNumQueue].hSource = -1;
  2033. pCopyQueue[nCopyNumQueue].hDest = -1;
  2034. pCopyQueue[nCopyNumQueue].ftLastWriteTime.dwLowDateTime = 0;
  2035. pCopyQueue[nCopyNumQueue].ftLastWriteTime.dwHighDateTime = 0;
  2036. nCopyNumQueue++;
  2037. return 0; // success
  2038. }
  2039. /*============================================================================
  2040. ;
  2041. ; StartCopy
  2042. ;
  2043. ; The following function is called automatically by WFCopy to initialize the
  2044. ; copy queue. The function is called each time by WFCopy, but will only
  2045. ; initialize the first time. The function allocates a buffer for reading and
  2046. ; writing, and a buffer for storing the source and destination filenames,
  2047. ; handles, and time stamps. The function EndCopy must be called to flush the
  2048. ; copy queue, and perform the actual disk transfer.
  2049. ;
  2050. ; Parameters: None
  2051. ;
  2052. ; return:
  2053. ; 0 success
  2054. ; != 0 dos error code (DE_ value)
  2055. ;
  2056. ; Written by C. Stevens, August 1991
  2057. ;
  2058. ============================================================================*/
  2059. WORD
  2060. APIENTRY
  2061. StartCopy(VOID)
  2062. {
  2063. WORD wSize; /* Buffer size */
  2064. register INT i; /* Counter */
  2065. // have we already been called?
  2066. if (lpCopyBuffer && pCopyQueue)
  2067. return 0; // success, buffers already allocated
  2068. /* Allocate and lock buffer for reading and writing */
  2069. wSize = COPYMAXBUFFERSIZE;
  2070. while (!lpCopyBuffer) {
  2071. lpCopyBuffer = GlobalAllocPtr(GHND, (DWORD)wSize);
  2072. if (!lpCopyBuffer) {
  2073. wSize /= 2;
  2074. if (wSize < COPYMINBUFFERSIZE)
  2075. return DE_INSMEM; // memory failure
  2076. }
  2077. }
  2078. wCopyBufferSize = wSize;
  2079. /* Allocate and lock buffer for copy queue. Note that magic +5 below is
  2080. because we always have stdin, stdout, stderr, and AUX files open all
  2081. the time, and we can't count them as available file handles */
  2082. // someone opens files on our psp, leave them 2 handles
  2083. // so we don't run on in the midst of copying
  2084. nCopyMaxQueue = min(SetHandleCount(11 * 2) / 2 - 1, 10);
  2085. #ifdef DEBUG
  2086. {
  2087. char buf[80];
  2088. wsprintf(buf, "SetHandleCount() -> %d\r\n", nCopyMaxQueue);
  2089. OutputDebugString(buf);
  2090. }
  2091. #endif
  2092. wSize = (WORD)(nCopyMaxQueue * sizeof(COPYQUEUEENTRY));
  2093. while (!pCopyQueue) {
  2094. pCopyQueue = (PCOPYQUEUE)LocalAlloc(LPTR,wSize);
  2095. if (!pCopyQueue) {
  2096. wSize /= 2;
  2097. if (wSize < (COPYMINFILES * sizeof(COPYQUEUEENTRY))) {
  2098. GlobalFreePtr(lpCopyBuffer);
  2099. lpCopyBuffer = NULL;
  2100. return DE_INSMEM; // memory failure
  2101. }
  2102. }
  2103. }
  2104. /* Initialize other Copy Queue variables and return success */
  2105. nCopyMaxQueue = (int) wSize / sizeof (COPYQUEUEENTRY);
  2106. nCopyNumQueue = 0;
  2107. for (i = 0; i < nCopyMaxQueue; i++) {
  2108. pCopyQueue[i].szSource[0] = 0;
  2109. pCopyQueue[i].szDest[0] = 0;
  2110. pCopyQueue[i].hSource = -1;
  2111. pCopyQueue[i].hDest = -1;
  2112. }
  2113. return 0; // success
  2114. }
  2115. // in:
  2116. // pszFile file to open/create
  2117. // wAttrib attributes to use on create
  2118. //
  2119. // returns:
  2120. // flags register (carry set on error)
  2121. // *pfh file handle or dos error code
  2122. WORD
  2123. OpenDestFile(
  2124. PSTR pszFile,
  2125. WORD wAttrib,
  2126. INT *pfh
  2127. )
  2128. {
  2129. INT fh;
  2130. WORD wStatus = 0;
  2131. OFSTRUCT ofs;
  2132. // use new extended open on dos > 4
  2133. if (wDOSversion >= 0x0400) {
  2134. if (wAttrib & ATTR_ATTRIBS)
  2135. wAttrib &= ATTR_USED;
  2136. else
  2137. wAttrib = ATTR_ARCHIVE;
  2138. {
  2139. fh = OpenFile(pszFile, &ofs,
  2140. OF_READWRITE | OF_SHARE_DENY_WRITE | OF_CREATE);
  2141. if (fh == (INT)-1) {
  2142. fh = GetLastError();
  2143. wStatus |= CARRY_FLAG;
  2144. } else {
  2145. wStatus = 0;
  2146. SetFileAttributes(pszFile, wAttrib);
  2147. }
  2148. // fh now contains a file handle or error code
  2149. }
  2150. } else {
  2151. {
  2152. fh = OpenFile(pszFile, &ofs,
  2153. OF_READWRITE | OF_SHARE_DENY_WRITE | OF_CREATE);
  2154. if (fh == (INT)-1) {
  2155. fh = GetLastError();
  2156. wStatus |= CARRY_FLAG;
  2157. } else
  2158. wStatus = 0;
  2159. }
  2160. }
  2161. *pfh = fh;
  2162. return wStatus;
  2163. }
  2164. /*============================================================================
  2165. ;
  2166. ; EndCopy
  2167. ;
  2168. ; The following function flushes the copy queue, attempting to copy all files
  2169. ; in the queue. The function ALWAYS frees global memory and flushes the
  2170. ; queue and reports it's own errors.
  2171. ;
  2172. ; strategy:
  2173. ; we will do as many operations on one drive as we can, thus
  2174. ; avoiding disk spin up time (really bad on floppies).
  2175. ;
  2176. ; Parameters: None
  2177. ;
  2178. ; returns:
  2179. ; 0 successful operation
  2180. ; != 0 dos error code (DE_OPCANCELLED) failure
  2181. ;
  2182. ; use like:
  2183. ;
  2184. ; loop {
  2185. ; ret = WFCopy();
  2186. ; if (ret) {
  2187. ; ReportError(ret);
  2188. ; goto Error;
  2189. ; }
  2190. ; }
  2191. ;
  2192. ; ret = EndCopy();
  2193. ; if (ret)
  2194. ; goto Error;
  2195. ;
  2196. ; return success;
  2197. ;
  2198. ;Error:
  2199. ; CopyAbort();
  2200. ; ReportError(ret);
  2201. ;
  2202. ============================================================================*/
  2203. WORD
  2204. APIENTRY
  2205. EndCopy(VOID)
  2206. {
  2207. INT i, j; /* Counter */
  2208. PSTR pTemp; /* Pointer to source or dest filename */
  2209. INT fh; /* File handle for DOS calls */
  2210. WORD wStatus; /* Status flags returned from DOS calls */
  2211. DWORD wRead; /* Number of bytes read from source file */
  2212. DWORD wWrite; /* Number of bytes written to destination file */
  2213. FILETIME ftLastWriteTime; /* Source file date and time */
  2214. DWORD wAttrib; /* File attributes */
  2215. #ifdef DEBUG
  2216. {
  2217. char buf[80];
  2218. wsprintf(buf, "EndCopy() nCopyNumQueue == %d\r\n", nCopyNumQueue);
  2219. OutputDebugString(buf);
  2220. }
  2221. #endif
  2222. /* Open as many source files as possible. Note we are assuming here
  2223. that nCopyNumQueue < nCopyMaxQueue. This should always be true
  2224. because WFCopy calls EndCopy to purge the queue if it becomes full.
  2225. We should never get an out of handles error opening source files or
  2226. destination files. If we do get an out of handles error opening
  2227. source files, cause a fatal error and abort the copy. */
  2228. // open all source files
  2229. Notify(hdlgProgress, IDS_OPENINGMSG, szNULL, szNULL);
  2230. for (i = 0; i < nCopyNumQueue; i++) {
  2231. if (WFQueryAbort())
  2232. return DE_OPCANCELLED;
  2233. pTemp = pCopyQueue[i].szSource;
  2234. {
  2235. OFSTRUCT ofs;
  2236. fh = OpenFile(pTemp, &ofs, OF_READ);
  2237. if (fh == (INT)-1)
  2238. fh = OpenFile(pTemp, &ofs, OF_SHARE_DENY_WRITE);
  2239. }
  2240. if (fh == (INT)-1) {
  2241. CopyError(pCopyQueue[i].szSource, pCopyQueue[i].szDest, fh, FUNC_COPY, OPER_DOFILE);
  2242. return DE_OPCANCELLED; // error already reported
  2243. } else
  2244. pCopyQueue[i].hSource = fh;
  2245. /* Get the source file date, time, and attributes if necessary */
  2246. fh = pCopyQueue[i].hSource;
  2247. if (!IsSerialDevice(fh)) {
  2248. {
  2249. FILETIME ft;
  2250. // Call DOS Get Date/Time of File.
  2251. if (GetFileTime((HANDLE)LongToHandle(fh), NULL, NULL, (LPFILETIME)&ft))
  2252. pCopyQueue[i].ftLastWriteTime = ft;
  2253. }
  2254. pTemp = pCopyQueue[i].szSource;
  2255. {
  2256. // Call DOS Get File Attributes
  2257. wAttrib = GetFileAttributes(pTemp);
  2258. if (wAttrib != (DWORD)-1)
  2259. pCopyQueue[i].wAttrib |= (wAttrib | ATTR_ATTRIBS);
  2260. }
  2261. }
  2262. }
  2263. /* Now open as many destination files as possible. If we get an out of
  2264. handles error, cause a fatal abort because we already called
  2265. Windows SetHandleCount to ensure we had enough.
  2266. Note: We are assuming the files do not exist when we try to open
  2267. them, although for DOS 4.0 and above files WILL be replaced
  2268. if they do happen to exist. */
  2269. // open all destination files
  2270. for (i = 0; i < nCopyNumQueue; i++) {
  2271. if (WFQueryAbort())
  2272. return DE_OPCANCELLED;
  2273. TryOpen:
  2274. wStatus = OpenDestFile(pCopyQueue[i].szDest, (WORD)pCopyQueue[i].wAttrib, (INT *)&fh);
  2275. if (wStatus & CARRY_FLAG) {
  2276. // error operning/creating destinaton file
  2277. if (fh == DE_PATHNOTFOUND) {
  2278. TryOpenDestAgain:
  2279. // ask the user to stick in another disk
  2280. fh = CopyMoveRetry(pCopyQueue[i].szDest, fh);
  2281. if (!fh) {
  2282. goto TryOpen;
  2283. } else {
  2284. // didn't happen, abort this copy
  2285. CopyError(pCopyQueue[i].szSource, pCopyQueue[i].szDest, (WORD)fh | ERRORONDEST, FUNC_COPY, OPER_DOFILE);
  2286. return DE_OPCANCELLED; // error already reported
  2287. }
  2288. } else {
  2289. // some other error condition
  2290. CopyError(pCopyQueue[i].szSource, pCopyQueue[i].szDest, (WORD)fh | ERRORONDEST, FUNC_COPY, OPER_DOFILE);
  2291. return DE_OPCANCELLED; // error already reported
  2292. }
  2293. } else {
  2294. pCopyQueue[i].hDest = fh; // dest file open success
  2295. }
  2296. }
  2297. /* Now copy between the open files */
  2298. for (i = 0; i < nCopyNumQueue; i++) {
  2299. Notify(hdlgProgress, IDS_COPYINGMSG, pCopyQueue[i].szSource, pCopyQueue[i].szDest);
  2300. wRead = wCopyBufferSize;
  2301. do {
  2302. if (WFQueryAbort())
  2303. return DE_OPCANCELLED;
  2304. fh = pCopyQueue[i].hSource;
  2305. {
  2306. wRead = _lread(fh, lpCopyBuffer, wCopyBufferSize);
  2307. if (wRead == (DWORD)-1) {
  2308. wStatus |= CARRY_FLAG;
  2309. wRead = GetLastError();
  2310. } else
  2311. wStatus = 0;
  2312. // wRead is either # bytes read or error code
  2313. }
  2314. if (wStatus & CARRY_FLAG) {
  2315. // Error during file read
  2316. CopyError(pCopyQueue[i].szSource, pCopyQueue[i].szDest, wRead, FUNC_COPY, OPER_DOFILE);
  2317. return DE_OPCANCELLED; // error already reported
  2318. }
  2319. fh = pCopyQueue[i].hDest;
  2320. {
  2321. // size can be zero to terminate file
  2322. wWrite = _lwrite(fh, lpCopyBuffer, wRead);
  2323. if (wWrite == (DWORD)-1) {
  2324. wStatus |= CARRY_FLAG;
  2325. wWrite = GetLastError();
  2326. } else
  2327. wStatus = 0;
  2328. // wWrite is either # bytes read or error code
  2329. }
  2330. if (wStatus & CARRY_FLAG) {
  2331. CopyError(pCopyQueue[i].szSource, pCopyQueue[i].szDest, wWrite | ERRORONDEST, FUNC_COPY, OPER_DOFILE);
  2332. return DE_OPCANCELLED; // error already reported
  2333. }
  2334. // write did not complete and removable drive?
  2335. if (wRead != wWrite) {
  2336. if (IsRemovableDrive(DRIVEID(pCopyQueue[i].szDest)) &&
  2337. (DRIVEID(pCopyQueue[i].szDest) != DRIVEID(pCopyQueue[i].szSource))) {
  2338. // destination disk must be full. delete the destination
  2339. // files, give the user the option to insert a new disk.
  2340. for (j = i; j < nCopyNumQueue; j++) {
  2341. _lclose(pCopyQueue[j].hDest);
  2342. pCopyQueue[j].hDest = -1;
  2343. pTemp = pCopyQueue[j].szDest;
  2344. DeleteFile(pTemp);
  2345. }
  2346. fh = DE_NODISKSPACE;
  2347. goto TryOpenDestAgain; // and try to create the destiations
  2348. } else {
  2349. // not removable, error condition
  2350. CopyError(pCopyQueue[i].szSource, pCopyQueue[i].szDest, DE_NODISKSPACE | ERRORONDEST, FUNC_COPY, OPER_DOFILE);
  2351. return DE_OPCANCELLED; // error already reported
  2352. }
  2353. }
  2354. // we have moved all the data, so don't delete this on
  2355. // clean up.
  2356. if (!wRead)
  2357. pCopyQueue[i].wAttrib |= ATTR_COPIED;
  2358. } while (wRead);
  2359. }
  2360. // Close all destination files, set date time attribs
  2361. Notify(hdlgProgress, IDS_CLOSINGMSG, szNULL, szNULL);
  2362. for (i = 0; i < nCopyNumQueue; i++) {
  2363. fh = pCopyQueue[i].hDest;
  2364. if (!IsSerialDevice(fh)) {
  2365. ftLastWriteTime = pCopyQueue[i].ftLastWriteTime;
  2366. if (ftLastWriteTime.dwLowDateTime &&
  2367. ftLastWriteTime.dwHighDateTime) {
  2368. SetFileTime((HANDLE)LongToHandle(fh), NULL, NULL, (LPFILETIME)&ftLastWriteTime);
  2369. }
  2370. }
  2371. _lclose(pCopyQueue[i].hDest);
  2372. pCopyQueue[i].hDest = -1;
  2373. /* Now set the file attributes if necessary */
  2374. if (wDOSversion < 0x0400) {
  2375. pTemp = pCopyQueue[i].szDest;
  2376. wAttrib = pCopyQueue[i].wAttrib;
  2377. // only set attribs if necessary (this is slow)
  2378. if (wAttrib & ATTR_ATTRIBS) {
  2379. wAttrib &= ATTR_USED;
  2380. SetFileAttributes(pTemp, wAttrib);
  2381. }
  2382. }
  2383. }
  2384. // Close all source files (and delete them if necessary)
  2385. if (pCopyQueue && (pCopyQueue[0].wAttrib & ATTR_DELSRC))
  2386. Notify(hdlgProgress, IDS_REMOVINGMSG, szNULL, szNULL);
  2387. for (i = 0; i < nCopyNumQueue; i++) {
  2388. _lclose(pCopyQueue[i].hSource);
  2389. pCopyQueue[i].hSource = -1;
  2390. if (pCopyQueue[i].wAttrib & ATTR_DELSRC) {
  2391. WFRemove(pCopyQueue[i].szSource);
  2392. }
  2393. }
  2394. if (lpCopyBuffer) {
  2395. GlobalFreePtr(lpCopyBuffer);
  2396. lpCopyBuffer = NULL;
  2397. }
  2398. if (pCopyQueue) {
  2399. LocalFree((HANDLE)pCopyQueue);
  2400. pCopyQueue = NULL;
  2401. }
  2402. nCopyMaxQueue = 0;
  2403. nCopyNumQueue = 0;
  2404. return 0; // success
  2405. }
  2406. /*============================================================================
  2407. ;
  2408. ; CopyError
  2409. ;
  2410. ; The following function reports an error during a file copy operation
  2411. ;
  2412. ; Parameters
  2413. ;
  2414. ; lpszSource - Source file name
  2415. ; lpszDest - Destination file name
  2416. ; nError - dos (or our exteneded) error code
  2417. ; 0xFFFF for special case NET error
  2418. ; wFunc - Operation being performed during error. Can be one of:
  2419. ; FUNC_DELETE - Delete files in pFrom
  2420. ; FUNC_RENAME - Rename files (same directory)
  2421. ; FUNC_MOVE - Move files in pFrom to pTo (different disk)
  2422. ; FUNC_COPY - Copy files in pFrom to pTo
  2423. ; nOper - Operation being performed. Can be one of:
  2424. ; OPER_ERROR - Error processing filenames
  2425. ; OPER_DOFILE - Go ahead and copy, rename, or delete file
  2426. ; OPER_MKDIR - Make a directory specified in pTo
  2427. ; OPER_RMDIR - Remove directory
  2428. ; 0 - No more files left
  2429. ;
  2430. ; Return Value: None
  2431. ;
  2432. ; Written by C. Stevens, August 1991
  2433. ;
  2434. ============================================================================*/
  2435. VOID
  2436. CopyError(
  2437. PSTR pszSource,
  2438. PSTR pszDest,
  2439. INT nError,
  2440. WORD wFunc,
  2441. INT nOper
  2442. )
  2443. {
  2444. CHAR szVerb[70]; /* Verb describing error */
  2445. CHAR szReason[200]; /* Reason for error */
  2446. BOOL bDest;
  2447. bDest = nError & ERRORONDEST; // was dest file cause of error
  2448. nError &= ~ERRORONDEST; // clear the dest bit
  2449. if (nError == DE_OPCANCELLED) // user abort
  2450. return;
  2451. if (!bCopyReport) // silent, don't report errors
  2452. return;
  2453. LoadString(hAppInstance, IDS_COPYERROR + wFunc, szTitle, sizeof(szTitle));
  2454. // get the verb string
  2455. if (nOper == OPER_DOFILE || !nOper) {
  2456. if (nError != 0xFFFF && bDest)
  2457. // this is bogus, this could be IDS_CREATING as well...
  2458. LoadString(hAppInstance, IDS_REPLACING, szVerb, sizeof(szVerb));
  2459. else
  2460. LoadString(hAppInstance, IDS_VERBS + wFunc, szVerb, sizeof(szVerb));
  2461. } else {
  2462. LoadString(hAppInstance, IDS_ACTIONS + (nOper >> 8), szVerb, sizeof(szVerb));
  2463. }
  2464. // get the reason string
  2465. if (nError == 0xFFFF) {
  2466. // special case LFN net error
  2467. WNetErrorText(WN_NET_ERROR, szReason, sizeof(szReason));
  2468. } else {
  2469. // transform some error cases
  2470. if (bDest) {
  2471. if (nError != DE_ACCESSDENIED && GetFreeDiskSpace((WORD)DRIVEID(pszDest)) == 0L)
  2472. nError = DE_NODISKSPACE;
  2473. } else {
  2474. if (nError == DE_ACCESSDENIED)
  2475. nError = DE_ACCESSDENIEDSRC; // soruce file access denied
  2476. }
  2477. LoadString(hAppInstance, IDS_REASONS + nError, szReason, sizeof(szReason));
  2478. }
  2479. // use the files names or "Selected files" if file list too long
  2480. if (!nOper && (lstrlen(pszSource) > 64))
  2481. LoadString(hAppInstance, IDS_SELECTEDFILES, pszSource, 32);
  2482. wsprintf(szMessage, szVerb, (LPSTR)(bDest ? pszDest : pszSource), (LPSTR)szReason);
  2483. MessageBox(hdlgProgress, szMessage, szTitle, MB_OK | MB_ICONSTOP);
  2484. }
  2485. /*============================================================================
  2486. ;
  2487. ; CopyAbort
  2488. ;
  2489. ; The following function aborts a queued copy operation. The function closes
  2490. ; all source and destination files, deleteing all destination files
  2491. ; including and following the specified index.
  2492. ;
  2493. ; Parameters:
  2494. ;
  2495. ; nIndex - Index of first destination file to delete
  2496. ;
  2497. ; Return Value: None
  2498. ;
  2499. ; Written by C. Stevens, August 1991
  2500. ;
  2501. ============================================================================*/
  2502. VOID
  2503. APIENTRY
  2504. CopyAbort(VOID)
  2505. {
  2506. INT i;
  2507. PSTR pTemp;
  2508. // close all source files
  2509. for (i = 0; i < nCopyMaxQueue; i++) {
  2510. if (pCopyQueue[i].hSource != -1)
  2511. _lclose(pCopyQueue[i].hSource);
  2512. }
  2513. // close and delete (if necessary) destination files
  2514. for (i = 0; i < nCopyMaxQueue; i++) {
  2515. if (pCopyQueue[i].hDest != -1) {
  2516. _lclose(pCopyQueue[i].hDest);
  2517. if (!(pCopyQueue[i].wAttrib & ATTR_COPIED)) {
  2518. pTemp = pCopyQueue[i].szDest;
  2519. DeleteFile(pTemp);
  2520. }
  2521. }
  2522. }
  2523. if (lpCopyBuffer) {
  2524. GlobalFreePtr(lpCopyBuffer);
  2525. lpCopyBuffer = NULL;
  2526. }
  2527. if (pCopyQueue) {
  2528. LocalFree((HANDLE)pCopyQueue);
  2529. pCopyQueue = NULL;
  2530. }
  2531. nCopyMaxQueue = 0; /* Clear other Copy Queue variables */
  2532. nCopyNumQueue = 0;
  2533. }
  2534. /*============================================================================
  2535. ;
  2536. ; CopyMoveRetry
  2537. ;
  2538. ; The following function is used to retry failed move/copy operations
  2539. ; due to out of disk situations or path not found errors
  2540. ; on the destination.
  2541. ;
  2542. ; NOTE: the destination drive must be removable or this function
  2543. ; does not make a whole lot of sense
  2544. ;
  2545. ; Parameters:
  2546. ;
  2547. ; pszDest - Fully qualified path to destination file
  2548. ; nError - Type of error which occured: DE_NODISKSPACE or DE_PATHNOTFOUND
  2549. ;
  2550. ; returns:
  2551. ; 0 success (destination path has been created)
  2552. ; != 0 dos error code including DE_OPCANCELLED
  2553. ;
  2554. ============================================================================*/
  2555. INT
  2556. CopyMoveRetry(
  2557. PSTR pszDest,
  2558. INT nError
  2559. )
  2560. {
  2561. CHAR szReason[128]; /* Error message string */
  2562. PSTR pTemp; /* Pointer into filename */
  2563. WORD wFlags; /* Message box flags */
  2564. INT result; /* Return from MessageBox call */
  2565. do { // until the destination path has been created
  2566. GetWindowText(hdlgProgress, szTitle, sizeof(szTitle));
  2567. if (nError == DE_PATHNOTFOUND) {
  2568. LoadString(hAppInstance, IDS_PATHNOTTHERE, szReason, sizeof(szReason));
  2569. /* Note the -1 below here is valid in both SBCS and DBCS because
  2570. pszDest is fully qualified and the character preceding the
  2571. file name must be a backslash */
  2572. pTemp = FindFileName(pszDest) - 1;
  2573. *pTemp = 0;
  2574. wsprintf(szMessage, szReason, (LPSTR)pszDest);
  2575. *pTemp = '\\';
  2576. wFlags = MB_ICONEXCLAMATION | MB_YESNO;
  2577. } else {
  2578. wFlags = MB_ICONEXCLAMATION | MB_RETRYCANCEL;
  2579. LoadString(hAppInstance, IDS_DESTFULL, szMessage, sizeof(szMessage));
  2580. }
  2581. result = MessageBox(hdlgProgress,szMessage,szTitle,wFlags);
  2582. if (result == IDRETRY || result == IDYES) {
  2583. // Allow the disk to be formatted
  2584. if (!IsTheDiskReallyThere(hdlgProgress, pszDest, FUNC_COPY))
  2585. return DE_OPCANCELLED;
  2586. pTemp = FindFileName(pszDest) - 1;
  2587. *pTemp = 0;
  2588. result = WF_CreateDirectory(hdlgProgress, pszDest);
  2589. *pTemp = '\\';
  2590. // only as once if creating the destionation failed
  2591. if (result == DE_OPCANCELLED)
  2592. return DE_OPCANCELLED;
  2593. if (result && (nError == DE_PATHNOTFOUND))
  2594. return result | ERRORONDEST;
  2595. } else
  2596. return DE_OPCANCELLED;
  2597. } while (result);
  2598. return 0; // success
  2599. }
  2600. BOOL
  2601. IsSerialDevice(
  2602. INT hFile
  2603. )
  2604. {
  2605. UNREFERENCED_PARAMETER(hFile);
  2606. return FALSE; // BUG BUG. How to findout if its a serialdevice
  2607. }