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.

887 lines
22 KiB

  1. /*
  2. * copy.c - Copy routine for WinDosSetup
  3. * Todd Laney
  4. *
  5. * Modification History:
  6. *
  7. * 6/03/91 Vlads Change copy process to incorporate new Install API
  8. *
  9. * 3/24/89 Toddla Wrote it
  10. *
  11. *
  12. * notes:
  13. * we now use the LZCopy stuff for compression
  14. * we now set the crit error handler ourselves so CHECKFLOPPY is
  15. * NOT defined
  16. */
  17. #include <windows.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include <mmsystem.h>
  22. #include "drivers.h"
  23. #include "sulib.h"
  24. //#include <ver.h>
  25. #define MAX_COPY_ATTEMPTS 15
  26. /*
  27. * Maximum number of install disks we support
  28. */
  29. #define MAX_DISKS 100
  30. /*
  31. * Flags for VerInstallFile
  32. */
  33. #define FORCEABLE_FLAGS (VIF_MISMATCH + VIF_SRCOLD + VIF_DIFFLANG + VIF_DIFFTYPE + VIF_DIFFCODEPG )
  34. /**********************************************************************
  35. *
  36. * Local function prototypes.
  37. *
  38. **********************************************************************/
  39. // Retrieve disk path for logical disk
  40. BOOL GetDiskPath(LPTSTR Disk, LPTSTR szPath);
  41. // Convert VIF_... to ERROR... return codes
  42. UINT ConvertFlagToValue(DWORD dwFlags);
  43. // Do the work of trying to copy a file
  44. LONG TryCopy(LPTSTR szSrc, // Full source file path
  45. LPTSTR szLogSrc, // Logical source name
  46. LPTSTR szDestPath,// Destination path
  47. FPFNCOPY fpfnCopy); // Callback routine
  48. #ifdef CHECK_FLOPPY
  49. BOOL NEAR IsDiskInDrive(int iDisk);
  50. #endif
  51. // GLOBAL VARIABLES
  52. // directory where windows will be setup to
  53. TCHAR szSetupPath[MAX_PATH];
  54. // directory where the root of the setup disks are!
  55. TCHAR szDiskPath[MAX_PATH];
  56. // Name of driver being copied (or oemsetup.inf)
  57. TCHAR szDrv[120];
  58. /*
  59. * global vars used by DosCopy
  60. */
  61. static LPTSTR lpBuf = NULL; // copy buffer
  62. static int iBuf = 0; // usage count
  63. static UINT nBufSize;
  64. BOOL bRetry = FALSE;
  65. BOOL bQueryExist;
  66. extern BOOL bCopyEvenIfOlder; // From DRIVERS.C
  67. BOOL DefCopyCallback(int msg, DWORD_PTR n, LPTSTR szFile)
  68. {
  69. return FC_IGNORE;
  70. }
  71. /* UINT FileCopy (szSource, szDir, fpfnCopy, UINT fCopy)
  72. *
  73. * This function will copy a group of files to a single destination
  74. *
  75. * ENTRY:
  76. *
  77. * szSourc : pointer to a SETUP.INF section
  78. * szDir : pointer to a string containing the target DIR
  79. * fpfnCopy : callback function used to notify called of copy status
  80. * fCopy : flags
  81. *
  82. * FC_SECTION - szSource is a section name
  83. * FC_LIST - szSource is a pointer to a char **foo;
  84. * FC_LISTTYPE - szSource is a pointer to a char *foo[];
  85. * FC_FILE - szSource is a file name.
  86. * FC_QUALIFIED - szSource is a fully qualified file name.
  87. * FC_DEST_QUALIFIED - szDir is fully qualified. Don't expand this.
  88. * FC_CALLBACK_WITH_VER - call back if file exists and report version information.
  89. *
  90. * NOTES:
  91. * if szSource points to a string of the form '#name' the section
  92. * named by 'name' will be used as the source files
  93. *
  94. * the first field of each line in the secion is used as the name of the
  95. * source file. A file name has the following form:
  96. *
  97. * #:name
  98. *
  99. * # - Disk number containing file 1-9,A-Z
  100. * name - name of the file, may be a wild card expression
  101. *
  102. * Format for copy status function
  103. *
  104. * BOOL FAR PASCAL CopyStatus(int msg, int n, LPSTR szFile)
  105. *
  106. * msg:
  107. * COPY_ERROR error occured while copying file(s)
  108. * n is the DOS error number
  109. * szFile is the file that got the error
  110. * return: TRUE ok, FALSE abort copy
  111. *
  112. * COPY_STATUS Called each time a new file is copied
  113. * n is the percent done
  114. * szFile is the file being copied
  115. * return: TRUE ok, FALSE abort copy
  116. *
  117. * COPY_INSERTDISK Please tell the user to insert a disk
  118. * n is the disk needed ('1' - '9')
  119. * return: TRUE try again, FALSE abort copy
  120. *
  121. * COPY_QUERYCOPY Should this file be copied?
  122. * n line index in SETUP.INF section (0 based)
  123. * szFile is the line from section
  124. * return: TRUE copy it, FALSE dont copy
  125. *
  126. * COPY_START Sent before any files are copied
  127. *
  128. * COPY_END Sent after all files have been copied
  129. * n is dos error if copy failed
  130. *
  131. * COPY_EXISTS Sent if the FC_CALL_ON_EXIST bit was set
  132. * and the file exists at the destination
  133. * given for the filecopy.
  134. *
  135. *
  136. * EXIT: returns TRUE if successful, FALSE if failure.
  137. *
  138. */
  139. UINT FileCopy (LPTSTR szSource, LPTSTR szDir, FPFNCOPY fpfnCopy, UINT fCopy)
  140. {
  141. int err = ERROR_SUCCESS; // Return code from this routine
  142. TCHAR szPath[MAX_PATH];
  143. TCHAR szLogSrc[MAX_PATH];
  144. TCHAR szSrc[MAX_PATH];
  145. LPTSTR pFileBegin; // First file
  146. LPTSTR * List; // Handle lists of files
  147. LPTSTR * ListHead;
  148. int nDisk; // The disk we're on
  149. int cntFiles = 0; // How many files we've got to do
  150. if (fpfnCopy == NULL) {
  151. fpfnCopy = DefCopyCallback;
  152. }
  153. if (!szSource || !*szSource || !szDir || !*szDir) {
  154. return ERROR_FILE_NOT_FOUND;
  155. }
  156. /*
  157. * fix up the drive in the destination
  158. */
  159. if ( fCopy & FC_DEST_QUALIFIED ) {
  160. lstrcpy(szPath, szDir);
  161. fCopy &= ~FC_DEST_QUALIFIED;
  162. } else {
  163. ExpandFileName(szDir, szPath);
  164. }
  165. if (szSource[0] == TEXT('#') && fCopy == FC_FILE) {
  166. fCopy = FC_SECTION;
  167. ++szSource;
  168. }
  169. switch (fCopy) {
  170. case FC_SECTION:
  171. {
  172. szSource = infFindSection(NULL,szSource);
  173. /*
  174. * We are called even when the section doesn't exist
  175. */
  176. if (szSource == NULL) {
  177. return ERROR_SUCCESS;
  178. }
  179. fCopy = FC_LIST;
  180. }
  181. // fall through to FC_LIST
  182. case FC_LIST:
  183. pFileBegin = szSource;
  184. cntFiles = infLineCount(szSource);
  185. break;
  186. case FC_LISTTYPE:
  187. ListHead = List = (LPTSTR far *)szSource;
  188. pFileBegin = *ListHead;
  189. while ( *List++ ) // Count files to be copied.
  190. ++cntFiles;
  191. break;
  192. case FC_FILE:
  193. case FC_QUALIFIED:
  194. default:
  195. pFileBegin = szSource;
  196. cntFiles = 1;
  197. }
  198. /*
  199. * walk all files in the list and call TryCopy ....
  200. *
  201. * NOTES:
  202. * we must walk file list sorted by disk number.
  203. * we should use the disk that is currently inserted.
  204. * we should do a find first/find next on the files????
  205. * we need to check for errors.
  206. * we need to ask the user to insert disk in drive.
  207. *
  208. */
  209. (*fpfnCopy)(COPY_START,0,NULL);
  210. /*
  211. * Go through all possible disks: 1 to 100 and A to Z (26)
  212. */
  213. for (nDisk = 1;
  214. err == ERROR_SUCCESS && (cntFiles > 0) &&
  215. (nDisk <= MAX_DISKS + 'Z' - 'A' + 1);
  216. nDisk++)
  217. {
  218. TCHAR Disk[10]; // Maximum string is "100:"
  219. LPTSTR pFile;
  220. int FileNumber; // Which file in the list we're on
  221. // (to pass to callback)
  222. pFile = pFileBegin; // Start at first file
  223. List = ListHead; // Handled chained lists
  224. FileNumber = 0; // Informational for callback - gives
  225. // which file in list we're on
  226. /*
  227. * Work out the string representing our disk letter
  228. */
  229. if (nDisk > MAX_DISKS) {
  230. Disk[0] = TEXT('A') + nDisk - MAX_DISKS - 1;
  231. Disk[1] = TEXT('\0');
  232. } else {
  233. _itow(nDisk, Disk, 10);
  234. }
  235. wcscat(Disk, TEXT(":"));
  236. for (;
  237. err == ERROR_SUCCESS && pFile;
  238. FileNumber++,
  239. pFile = fCopy == FC_LISTTYPE ? *(++List) :
  240. fCopy == FC_LIST ? infNextLine(pFile) :
  241. NULL)
  242. {
  243. /*
  244. * We have to reset high bit of first byte because it could be set
  245. * by translating service in OEM setup to show that file name was
  246. * mapped
  247. */
  248. *pFile = toascii(*pFile);
  249. /*
  250. * should we copy this file?
  251. * copy the files in disk order.
  252. */
  253. if (_wcsnicmp(pFile, Disk, wcslen(Disk)) == 0 || // File has disk
  254. // number and we're
  255. // on that disk
  256. RemoveDiskId(pFile) == pFile &&
  257. nDisk == 1 && *pFile || // First disk and
  258. // no disk number
  259. fCopy == FC_QUALIFIED) { // Fully qualified
  260. /*
  261. * done with a file. decrement count.
  262. */
  263. cntFiles--;
  264. lstrcpy(szDrv, RemoveDiskId(pFile));
  265. switch ((*fpfnCopy)(COPY_QUERYCOPY, FileNumber, pFile))
  266. {
  267. case CopyCurrent: // Skip
  268. continue;
  269. case CopyNeither:
  270. err = ERROR_FILE_EXISTS; // File already exists
  271. case CopyNew:
  272. break;
  273. default:
  274. break;
  275. }
  276. /*
  277. * Pick up bad return code from switch
  278. */
  279. if (err != ERROR_SUCCESS) {
  280. break;
  281. }
  282. /*
  283. * now we convert logical dest into a physical
  284. * (unless FC_QUALIFIED)
  285. */
  286. infParseField(pFile, 1, szLogSrc); // logical source
  287. if ( fCopy != FC_QUALIFIED ) {
  288. ExpandFileName(szLogSrc, szSrc); // full physical source
  289. } else {
  290. lstrcpy(szSrc,szLogSrc);
  291. }
  292. /*
  293. * Attempt copy
  294. */
  295. err = TryCopy(szSrc, // Qualified Source file
  296. szLogSrc, // Logical source file name (with disk #)
  297. szPath, // Path for directory to install in
  298. fpfnCopy); // Copy callback function
  299. /*
  300. * If failed to find file try the windows directory
  301. */
  302. if (err != ERROR_SUCCESS) {
  303. break;
  304. }
  305. } /* End if dor if DoCopy */
  306. }
  307. }
  308. (*fpfnCopy)(COPY_END,err,NULL);
  309. return err;
  310. }
  311. /**********************************************************************
  312. *
  313. * TryCopy
  314. *
  315. * Copy a single file from source to destination using the VerInstallFile
  316. * API - interpreting the return code as :
  317. *
  318. * ERROR_SUCCESS - OK
  319. * Other - failure type
  320. *
  321. **********************************************************************/
  322. LONG TryCopy(LPTSTR szSrc, // Full expanded source file path
  323. LPTSTR szLogSrc, // Logical source name
  324. LPTSTR szDestPath, // Destination path
  325. FPFNCOPY fpfnCopy) // Callback routine
  326. {
  327. DWORD wTmpLen;
  328. DWORD dwRetFlags;
  329. TCHAR szTempFile[MAX_PATH];
  330. TCHAR szErrFile[MAX_PATH];
  331. TCHAR DriversPath[MAX_PATH];
  332. BOOL bRetVal; // Return code from callback
  333. LPTSTR szFile;
  334. TCHAR szSrcPath[MAX_PATH];
  335. int iAttemptCount;
  336. WORD wVerFlags;
  337. LONG err;
  338. /*
  339. * Fix up destination if file is a kernel driver
  340. */
  341. if (IsFileKernelDriver(szSrc) && szDestPath)
  342. {
  343. wcscpy(DriversPath, szDestPath);
  344. wcscat(DriversPath, TEXT("\\drivers"));
  345. szDestPath = DriversPath;
  346. }
  347. /*
  348. * Create file name from current string
  349. */
  350. szFile = FileName(szSrc);
  351. lstrcpy(szSrcPath, szSrc);
  352. StripPathName(szSrcPath);
  353. for(iAttemptCount = 0, wVerFlags = 0 ;
  354. iAttemptCount <= MAX_COPY_ATTEMPTS;
  355. iAttemptCount++) {
  356. HCURSOR hcurPrev; // Saved cursor state
  357. // Central operation - attempt to install file szFile in directory
  358. // pointed by szPath from directory pointed by szSrc
  359. // If operation will fail but with possibility to force install
  360. // in last parameter buffer we will have temporary file name ==>
  361. // therefore we can avoid excessive copying.
  362. // NOTE: now szFile consists of only file name and other buffers
  363. // only path names.
  364. wTmpLen = MAX_PATH;
  365. hcurPrev = SetCursor(LoadCursor(NULL,IDC_WAIT));
  366. dwRetFlags = VerInstallFile(wVerFlags,
  367. (LPTSTR) szFile,
  368. (LPTSTR) szFile,
  369. (LPTSTR) szSrcPath,
  370. (LPTSTR) szDestPath,
  371. (LPTSTR) szDestPath,
  372. (LPTSTR) szTempFile,
  373. (LPDWORD) &wTmpLen);
  374. SetCursor(hcurPrev);
  375. /*
  376. * Operation failed if at least one bit of return flags is non-zero
  377. * That is unusual but defined so in Version API.
  378. */
  379. if ( !dwRetFlags )
  380. return ERROR_SUCCESS; // If no errors - goto next file
  381. /*
  382. * If flag MISMATCH is set - install can be forced and we have
  383. * temporary file in destination subdirectory
  384. */
  385. if ( dwRetFlags & VIF_MISMATCH ) {
  386. if ( (dwRetFlags & VIF_SRCOLD) && (!bCopyEvenIfOlder) ) {
  387. /*
  388. * If we need not call back with question - automatically
  389. * force install with same parameters.
  390. * michaele, *only* if src file is *newer* than dst file
  391. */
  392. DeleteFile(szTempFile);
  393. return ERROR_SUCCESS;
  394. }
  395. /*
  396. * If we need not call back with question - automatically
  397. * force install with same parameters.
  398. */
  399. wVerFlags |= VIFF_FORCEINSTALL;
  400. iAttemptCount--; // Make sure we get another go.
  401. continue;
  402. } /* End if MISMATCH */
  403. /*
  404. * If real error occured - call back with error file info
  405. * In all dialogs we use our error codes - so I will convert
  406. * flags returned from Ver API to ours.
  407. */
  408. err = ConvertFlagToValue(dwRetFlags);
  409. /*
  410. * If source path or file is nor readable - try to change disk
  411. */
  412. if ( dwRetFlags & VIF_CANNOTREADSRC )
  413. {
  414. /*
  415. * Now new path in szSrc so I deleted logic for creating it
  416. */
  417. if (RemoveDiskId(szLogSrc) == szLogSrc)
  418. /*
  419. * if disk # not provided, default to 1
  420. */
  421. bRetVal = (*fpfnCopy)(COPY_INSERTDISK, (DWORD_PTR)"1", szSrcPath);
  422. else
  423. bRetVal = (*fpfnCopy)(COPY_INSERTDISK, (DWORD_PTR)szLogSrc, szSrcPath);
  424. switch (bRetVal)
  425. {
  426. case FC_RETRY:
  427. continue; // and try again...
  428. case FC_ABORT:
  429. return ERROR_FILE_NOT_FOUND;
  430. case FC_IGNORE:
  431. break;
  432. }
  433. }
  434. ExpandFileName(szLogSrc, szErrFile);
  435. #if WINDOWSDIR
  436. if (!*bWindowsDir &&
  437. err != FC_ERROR_LOADED_DRIVER &&
  438. err != ERROR_DISK_FULL)
  439. {
  440. GetWindowsDirectory(szPath, MAX_PATH);
  441. *bWindowsDir = TRUE;
  442. continue;
  443. }
  444. #endif // WINDOWSDIR
  445. switch ((*fpfnCopy)(COPY_ERROR, err, szErrFile)) {
  446. case FC_IGNORE:
  447. return ERROR_SUCCESS;
  448. case FC_RETRY:
  449. break;
  450. case FC_ABORT:
  451. return ERROR_FILE_NOT_FOUND;
  452. }
  453. } // End of attempts
  454. return err;
  455. }
  456. /* BOOL GetDiskPath(Disk, szPath)
  457. *
  458. * This function will retrive the full path name for a logical disk
  459. *
  460. * The code reads the [disks] section of SETUP.INF and looks for
  461. * n = path where n is the disk char. NOTE the disk '0' defaults to
  462. * the root windows directory.
  463. *
  464. * ENTRY:
  465. *
  466. * cDisk : what disk to find 0-9,A-Z
  467. * szPath : buffer to hold disk path
  468. *
  469. * Returns :
  470. * TRUE if a disk path was found
  471. * FALSE if there was no disk specified (ie no ':'
  472. *
  473. */
  474. BOOL GetDiskPath(LPTSTR Disk, LPTSTR szPath)
  475. {
  476. TCHAR ach[MAX_PATH];
  477. TCHAR szBuf[MAX_PATH];
  478. int i;
  479. /*
  480. * Check to see if there is actually a disk id.
  481. * If not return FALSE
  482. */
  483. if (RemoveDiskId(Disk) == Disk) {
  484. return FALSE;
  485. }
  486. /*
  487. * Create our copy of the disk id
  488. */
  489. for (i = 0; Disk[i] != TEXT(':'); i++) {
  490. ach[i] = Disk[i];
  491. }
  492. ach[i] = TEXT('\0');
  493. /*
  494. * Zero disk letter means windows setup directory
  495. */
  496. if (_wcsicmp(ach, TEXT("0")) == 0) {
  497. /*
  498. * return the windows setup directory
  499. */
  500. lstrcpy(szPath,szSetupPath);
  501. return TRUE;
  502. }
  503. /*
  504. * now look in the [disks] section for a full path name
  505. *
  506. * This is a pretty bogus concept and is not supported
  507. * in win 32 style disks section [Source Media Descriptions]
  508. */
  509. if ( !infGetProfileString(NULL,DISK_SECT,ach,szPath) &&
  510. !infGetProfileString(NULL,OEMDISK_SECT,ach,szPath)) {
  511. lstrcpy(szPath, szDiskPath);
  512. } else {
  513. infParseField(szPath,1,szPath);
  514. /*
  515. * is the path relative? is so prepend the szDiskPath
  516. */
  517. if (szPath[0] == TEXT('.') || szPath[0] == TEXT('\0')) {
  518. lstrcpy(szBuf,szDiskPath);
  519. catpath(szBuf,szPath);
  520. lstrcpy(szPath,szBuf);
  521. }
  522. }
  523. return TRUE;
  524. }
  525. /* BOOL FAR PASCAL ExpandFileName(LPSTR szFile, LPTSTR szPath)
  526. *
  527. * This function will retrive the full path name for a file
  528. * it will expand, logical disk letters to pyshical ones
  529. * will use current disk and directory if non specifed.
  530. *
  531. * if the drive specifed is 0-9, it will expand the drive into a
  532. * full pathname using GetDiskPath()
  533. *
  534. * IE 0:system ==> c:windows\system
  535. * 1:foo.txt a:\foo.txt
  536. *
  537. * ENTRY:
  538. *
  539. * szFile : File name to expand
  540. * szPath : buffer to hold full file name
  541. *
  542. */
  543. BOOL ExpandFileName(LPTSTR szFile, LPTSTR szPath)
  544. {
  545. TCHAR szBuf[MAX_PATH*2];
  546. if (GetDiskPath(szFile, szBuf)) {
  547. lstrcpy(szPath,szBuf);
  548. if (szFile[2])
  549. catpath(szPath,szFile + 2);
  550. } else {
  551. lstrcpy(szPath,szFile);
  552. }
  553. return TRUE;
  554. }
  555. void catpath(LPTSTR path, LPTSTR sz)
  556. {
  557. //
  558. // Remove any drive letters from the directory to append
  559. //
  560. sz = RemoveDiskId(sz);
  561. //
  562. // Remove any current directories ".\" from directory to append
  563. //
  564. while (sz[0] == TEXT('.') && SLASH(sz[1]))
  565. sz += 2;
  566. //
  567. // Dont append a NULL string or a single "."
  568. //
  569. if (*sz && ! (sz[0] == TEXT('.') && sz[1] == 0))
  570. {
  571. // Add a slash separator if necessary.
  572. if ((! SLASH(path[lstrlen(path) - 1])) && // slash at end of path
  573. ((path[lstrlen(path) - 1]) != TEXT(':')) && // colon at end of path
  574. (! SLASH(sz[0]))) // slash at beginning of file
  575. lstrcat(path, CHSEPSTR);
  576. lstrcat(path, sz);
  577. }
  578. }
  579. /*
  580. * Return a pointer to the file name part of a string
  581. */
  582. LPTSTR FileName(LPTSTR szPath)
  583. {
  584. LPTSTR sz;
  585. for (sz=szPath; *sz; sz++)
  586. ;
  587. for (; sz>=szPath && !SLASH(*sz) && *sz!=TEXT(':'); sz--)
  588. ;
  589. return ++sz;
  590. }
  591. /*
  592. * Return the portion of a file name following the disk (ie anything
  593. * before the colon).
  594. * If there is no colon just return a pointer to the original string
  595. */
  596. LPTSTR RemoveDiskId(LPTSTR szPath)
  597. {
  598. LPTSTR sz;
  599. for (sz = szPath; *sz; sz++) {
  600. if (*sz == TEXT(':')) {
  601. return sz + 1;
  602. }
  603. }
  604. return szPath;
  605. }
  606. LPTSTR StripPathName(LPTSTR szPath)
  607. {
  608. LPTSTR sz;
  609. sz = FileName(szPath);
  610. if (sz > szPath+1 && SLASH(sz[-1]) && sz[-2] != TEXT(':'))
  611. sz--;
  612. *sz = 0;
  613. return szPath;
  614. }
  615. /*
  616. * See if a file is a kernel driver. Unfortunately the VersionInfo APIs
  617. * don't seem coded up to take care of this at the moment so we just check
  618. * to see if the file extension is ".SYS"
  619. */
  620. BOOL IsFileKernelDriver(LPTSTR szPath)
  621. {
  622. TCHAR drive[MAX_PATH];
  623. TCHAR dir[MAX_PATH];
  624. TCHAR fname[MAX_PATH];
  625. TCHAR ext[MAX_PATH];
  626. lsplitpath(szPath, drive, dir, fname, ext);
  627. return !_wcsicmp(ext, TEXT(".sys"));
  628. }
  629. /**************************************************************************
  630. *
  631. * This function converts returned flags from Ver API to the numerical
  632. * error codes used in SETUP.
  633. *
  634. ***************************************************************************/
  635. UINT ConvertFlagToValue(DWORD dwFlags)
  636. {
  637. if ( ! dwFlags )
  638. return(NO_ERROR);
  639. if ( dwFlags & VIF_CANNOTREADSRC )
  640. return(ERROR_FILE_NOT_FOUND);
  641. if ( dwFlags & VIF_OUTOFMEMORY )
  642. return(ERROR_OUTOFMEMORY);
  643. if ( dwFlags & VIF_ACCESSVIOLATION )
  644. return(ERROR_ACCESS_DENIED);
  645. if ( dwFlags & VIF_SHARINGVIOLATION )
  646. return(ERROR_SHARING_VIOLATION);
  647. if ( dwFlags & VIF_FILEINUSE)
  648. return(FC_ERROR_LOADED_DRIVER);
  649. return(ERROR_CANNOT_COPY); // General error
  650. }
  651. #ifdef CHECK_FLOPPY
  652. /*--------------------------------------------------------------------------
  653. IsValidDiskette() -
  654. --------------------------------------------------------------------------*/
  655. #define CBSECTORSIZE 512
  656. #define INT13_READ 2
  657. BOOL IsValidDiskette(int iDrive)
  658. {
  659. TCHAR buf[CBSECTORSIZE];
  660. iDrive |= 0x0020; // make lower case
  661. iDrive -= 'a'; // A = 0, B = 1, etc. for BIOS stuff
  662. return MyReadWriteSector(buf, INT13_READ, iDrive, 0, 0, 1);
  663. }
  664. /* BOOL IsDiskInDrive(char cDisk)
  665. *
  666. * Is the specifed disk in the drive
  667. *
  668. * ENTRY:
  669. *
  670. * cDisk : what disk required to be in the drive (logical)
  671. *
  672. * return TRUE if the specifed disk is in the drive
  673. * FALSE if the wrong disk is in the drive or disk error
  674. *
  675. */
  676. BOOL IsDiskInDrive(int iDisk)
  677. {
  678. if ((iDisk >= 'A' && iDisk <= 'Z') ||
  679. (iDisk >= 'a' && iDisk <= 'z'))
  680. {
  681. if (DosRemoveable(iDisk))
  682. {
  683. if (!IsValidDiskette(iDisk))
  684. return FALSE;
  685. }
  686. return TRUE;
  687. }
  688. return TRUE; // for non drive letters assume a path
  689. // and thus always in.
  690. }
  691. #endif