Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1041 lines
33 KiB

  1. /*** fileutil.c - Utility routines for dealing with files
  2. *
  3. * Microsoft Confidential
  4. * Copyright (C) Microsoft Corporation 1993-1997
  5. * All Rights Reserved.
  6. *
  7. * Author:
  8. * Benjamin W. Slivka
  9. *
  10. * History:
  11. * 20-Feb-1994 bens Initial version (code from diamond.c)
  12. * 21-Feb-1994 bens Add IsWildPath()
  13. * 24-Feb-1994 bens Added tempfile functions
  14. * 23-Mar-1994 bens Added Win32<->MS-DOS file attribute mapping
  15. * 03-Jun-1994 bens VER.DLL support
  16. * 07-Jun-1994 bens Move VER.DLL stuff to filever.c
  17. * 14-Dec-1994 bens Fix bug in IsWildPath()
  18. * 02-Feb-1996 msliger Reduced bogosity in pattern matcher
  19. * 26-Feb-1997 msliger Avoid NULL deref in catDirAndFile()
  20. * 04-Mar-1997 msliger Close file before applying attributes to avoid
  21. * setting the archive bit.
  22. * 01-Apr-1997 msliger Avoid bounds error in ensureDirectory.
  23. */
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <ctype.h>
  27. #include <string.h>
  28. #include <malloc.h>
  29. #include <fcntl.h>
  30. #include <sys\types.h>
  31. #include <sys\stat.h>
  32. #include <io.h>
  33. #include <errno.h>
  34. #include <direct.h>
  35. #ifdef BIT16
  36. #include <dos.h>
  37. #else // !BIT16
  38. //** Get minimal Win32 definitions
  39. #ifndef WIN32_LEAN_AND_MEAN
  40. #define WIN32_LEAN_AND_MEAN
  41. #endif
  42. #include <windows.h>
  43. #undef ERROR // Override "#define ERROR 0" in wingdi.h
  44. #endif // !BIT16
  45. #include "types.h"
  46. #include "asrt.h"
  47. #include "error.h"
  48. #include "mem.h"
  49. #include "message.h"
  50. #include "fileutil.h"
  51. #include <fileutil.msg> // LOCALIZED for EXTRACT.EXE -- specify "cl /Ipath"
  52. /** TEMPFILE definitions
  53. *
  54. */
  55. typedef struct { /* tmp */
  56. #ifdef ASSERT
  57. SIGNATURE sig; // structure signature sigTEMPFILE
  58. #endif
  59. FILE *pfile; // Stream pointer (fopen,fread,fwrite,fclose,...)
  60. char *pszFile; // Constructed filename (MemFree to free)
  61. char *pszDesc; // Description of tempfile
  62. } TEMPFILE;
  63. typedef TEMPFILE *PTEMPFILE; /* ptmp */
  64. #ifdef ASSERT
  65. #define sigTEMPFILE MAKESIG('T','M','P','F') // TEMPFILE signature
  66. #define AssertTmp(ptmp) AssertStructure(ptmp,sigTEMPFILE);
  67. #else // !ASSERT
  68. #define AssertTmp(ptmp)
  69. #endif // !ASSERT
  70. #define PTMPfromHTMP(htmp) ((PTEMPFILE)(htmp))
  71. #define HTMPfromPTMP(ptmp) ((HTEMPFILE)(ptmp))
  72. #ifdef BIT16
  73. #define CharIncr(psz) (psz = psz + 1)
  74. #else
  75. #define CharIncr(psz) (psz = CharNextExA(CP_ACP, psz, 0))
  76. #endif
  77. /*** TmpCreate - Create a temporary file
  78. *
  79. * NOTE: See fileutil.h for entry/exit conditions.
  80. */
  81. HTEMPFILE TmpCreate(char *pszDesc, char *pszPrefix, char *pszMode, PERROR perr)
  82. {
  83. #define cTMP_RETRY 5 // Number of tempfile retries
  84. int cFailure=0;
  85. FILE *pfile=NULL;
  86. char *pszTmpName;
  87. PTEMPFILE ptmp;
  88. //** Try to create a temp file (give it 6 tries for good luck)
  89. while (pfile == NULL) {
  90. pszTmpName = _tempnam("",pszPrefix); // Get a name
  91. if (pszTmpName != NULL) {
  92. pfile = fopen(pszTmpName,pszMode); // Create the file
  93. }
  94. if (pfile == NULL) { // Name or create failed
  95. cFailure++; // Count failures
  96. if (cFailure > cTMP_RETRY) { // Failure, select error message
  97. if (pszTmpName == NULL) { // Name create failed
  98. ErrSet(perr,pszFILERR_CANT_CREATE_TMP,"%s",pszDesc);
  99. }
  100. else { // File create failed
  101. ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s",
  102. pszDesc,pszTmpName);
  103. free(pszTmpName); //BC6 cr
  104. }
  105. return NULL;
  106. }
  107. if (pszTmpName != NULL) { //BC6 cr
  108. free(pszTmpName);
  109. }
  110. }
  111. }
  112. //** File create worked, allocate our tempfile structure and fill it in
  113. if (!(ptmp = MemAlloc(sizeof(TEMPFILE)))) {
  114. ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc);
  115. goto error;
  116. }
  117. ptmp->pszFile = NULL;
  118. ptmp->pszDesc = NULL;
  119. if (!(ptmp->pszFile = MemStrDup(pszTmpName))) {
  120. ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc);
  121. goto error;
  122. }
  123. if (!(ptmp->pszDesc = MemStrDup(pszDesc))) {
  124. ErrSet(perr,pszFILERR_OUT_OF_MEMORY,"%s",pszDesc);
  125. goto error;
  126. }
  127. ptmp->pfile = pfile;
  128. SetAssertSignature(ptmp,sigTEMPFILE);
  129. free(pszTmpName); //BC6
  130. return HTMPfromPTMP(ptmp); // Success
  131. error:
  132. if (ptmp) {
  133. if (ptmp->pszDesc != NULL) {
  134. MemFree(ptmp->pszDesc);
  135. }
  136. if (ptmp->pszFile != NULL) {
  137. MemFree(ptmp->pszFile);
  138. }
  139. MemFree(ptmp);
  140. }
  141. fclose(pfile);
  142. free(pszTmpName);
  143. return NULL; // Failure
  144. } /* createTempFile() */
  145. /*** TmpGetStream - Get FILE* from HTEMPFILE, to perform I/O
  146. *
  147. * NOTE: See fileutil.h for entry/exit conditions.
  148. */
  149. FILE *TmpGetStream(HTEMPFILE htmp, PERROR perr)
  150. {
  151. PTEMPFILE ptmp;
  152. ptmp = PTMPfromHTMP(htmp);
  153. AssertTmp(ptmp);
  154. return ptmp->pfile;
  155. } /* TmpGetStream() */
  156. /*** TmpGetDescription - Get description of temporary file
  157. *
  158. * NOTE: See fileutil.h for entry/exit conditions.
  159. */
  160. char *TmpGetDescription(HTEMPFILE htmp, PERROR perr)
  161. {
  162. PTEMPFILE ptmp;
  163. ptmp = PTMPfromHTMP(htmp);
  164. AssertTmp(ptmp);
  165. return ptmp->pszDesc;
  166. } /* TmpGetDescription() */
  167. /*** TmpGetFileName - Get filename of temporary file
  168. *
  169. * NOTE: See fileutil.h for entry/exit conditions.
  170. */
  171. char *TmpGetFileName(HTEMPFILE htmp, PERROR perr)
  172. {
  173. PTEMPFILE ptmp;
  174. ptmp = PTMPfromHTMP(htmp);
  175. AssertTmp(ptmp);
  176. return ptmp->pszFile;
  177. } /* TmpGetFileName() */
  178. /*** TmpClose - Close a temporary file stream, but keep tempfile handle
  179. *
  180. * NOTE: See fileutil.h for entry/exit conditions.
  181. */
  182. BOOL TmpClose(HTEMPFILE htmp, PERROR perr)
  183. {
  184. PTEMPFILE ptmp;
  185. ptmp = PTMPfromHTMP(htmp);
  186. AssertTmp(ptmp);
  187. //** Only close if it is open
  188. if (ptmp->pfile != NULL) {
  189. if (fclose(ptmp->pfile) == EOF) { // Close it
  190. ErrSet(perr,pszFILERR_CANT_CLOSE_TMP,ptmp->pszDesc);
  191. return FALSE;
  192. }
  193. ptmp->pfile = NULL; // Remember stream is closed
  194. }
  195. return TRUE;
  196. } /* TmpClose() */
  197. /*** TmpOpen - Open the stream for a temporary file
  198. *
  199. * NOTE: See fileutil.h for entry/exit conditions.
  200. * Entry:
  201. * htmp - Handle to temp file
  202. * pszMode - Mode string passed to fopen ("wt", "wb", "rt", etc.)
  203. * perr - ERROR structure
  204. *
  205. * Exit-Success:
  206. * Returns TRUE; stream opened
  207. *
  208. * Exit-Failure:
  209. * Returns NULL; perr filled in
  210. */
  211. BOOL TmpOpen(HTEMPFILE htmp, char *pszMode, PERROR perr)
  212. {
  213. PTEMPFILE ptmp;
  214. ptmp = PTMPfromHTMP(htmp);
  215. AssertTmp(ptmp);
  216. Assert(ptmp->pfile == NULL); // Can't open if already open
  217. ptmp->pfile = fopen(ptmp->pszFile,pszMode); // Open the file
  218. if (!ptmp->pfile) {
  219. ErrSet(perr,pszFILERR_CANNOT_OPEN_TMP,"%s%s",
  220. ptmp->pszDesc,ptmp->pszFile);
  221. }
  222. return (ptmp->pfile != NULL); // Indicate success/failure
  223. } /* TmpOpen() */
  224. /*** TmpDestroy - Delete tempfil and destroy handle
  225. *
  226. * NOTE: See fileutil.h for entry/exit conditions.
  227. */
  228. BOOL TmpDestroy(HTEMPFILE htmp, PERROR perr)
  229. {
  230. PTEMPFILE ptmp;
  231. ptmp = PTMPfromHTMP(htmp);
  232. AssertTmp(ptmp);
  233. //** Make sure file is closed
  234. if (ptmp->pfile != NULL) {
  235. fclose(ptmp->pfile);
  236. }
  237. //** Delete tempfile
  238. if (remove(ptmp->pszFile) != 0) {
  239. ErrSet(perr,pszFILERR_CANT_DELETE_TMP,"%s%s",
  240. ptmp->pszDesc,ptmp->pszFile);
  241. }
  242. //** Free Memory
  243. if (ptmp->pszDesc != NULL) {
  244. MemFree(ptmp->pszDesc);
  245. }
  246. if (ptmp->pszFile != NULL) {
  247. MemFree(ptmp->pszFile);
  248. }
  249. ClearAssertSignature(ptmp);
  250. MemFree(ptmp);
  251. //** Success
  252. return TRUE;
  253. } /* TmpDestroy() */
  254. /*** getFileSize - Get size of a file
  255. *
  256. * NOTE: See fileutil.h for entry/exit conditions.
  257. */
  258. long getFileSize(char *pszFile, PERROR perr)
  259. {
  260. struct _stat statbuf; // Buffer for _stat()
  261. //** Get file status
  262. if (_stat(pszFile,&statbuf) == -1) {
  263. //** Could not get file status
  264. ErrSet(perr,pszFILERR_FILE_NOT_FOUND,"%s",pszFile);
  265. return -1;
  266. }
  267. //** Make sure it is a file
  268. if (statbuf.st_mode & (_S_IFCHR | _S_IFDIR)) { // device or directory
  269. ErrSet(perr,pszFILERR_NOT_A_FILE,"%s",pszFile);
  270. return -1;
  271. }
  272. //** Success
  273. return statbuf.st_size;
  274. } /* getFileSize() */
  275. /*** appendPathSeparator - Append a path separator only if necessary
  276. *
  277. * NOTE: See fileutil.h for entry/exit conditions.
  278. */
  279. int appendPathSeparator(char *pszPathEnd)
  280. {
  281. //** Add path separator if necessary
  282. if ((*pszPathEnd != '\0') && // Path is not empty
  283. (*pszPathEnd != chPATH_SEP1) && // Not a path separator
  284. (*pszPathEnd != chPATH_SEP2) && // Not a path separator
  285. (*pszPathEnd != chDRIVE_SEP) ) { // Not a drive separator
  286. *(++pszPathEnd) = chPATH_SEP1; // Add path separator
  287. *(++pszPathEnd) = '\0'; // Terminate path
  288. return 1; // Account for path separator
  289. }
  290. //** No separator added
  291. return 0;
  292. } /* appendPathSeparator() */
  293. /*** catDirAndFile - Concatenate a possibly empty dir and file name
  294. *
  295. * NOTE: See fileutil.h for entry/exit conditions.
  296. */
  297. BOOL catDirAndFile(char * pszResult,
  298. int cbResult,
  299. char * pszDir,
  300. char * pszFile,
  301. char * pszFileDef,
  302. PERROR perr)
  303. {
  304. int cch;
  305. char *pch;
  306. //FEATURE: 14-Feb-1994 bens Need to add pszName to say what field was bad
  307. //** Handle directory
  308. pszResult[0] = '\0'; // No filespec, yet
  309. cch = strlen(pszDir); // Get length of dir
  310. if (cch != 0) { // Have to concatenate path
  311. strcpy(pszResult,pszDir); // Copy destination dir to buffer
  312. cbResult -= cch; // Account for dir
  313. //** Add path separator if necessary, adjust remaining size
  314. cbResult -= appendPathSeparator(&(pszResult[cch-1]));
  315. if (cbResult <= 0) {
  316. ErrSet(perr,pszFILERR_PATH_TOO_LONG,"%s",pszDir);
  317. return FALSE;
  318. }
  319. }
  320. //** Append file name, using default if primary one not supplied
  321. if (*pszFile == '\0') { // Need to construct file name
  322. if ((pszFileDef == NULL) || // Don't deref NULL
  323. (*pszFileDef == '\0')) { // Empty default name, too
  324. return TRUE; // We're done!
  325. }
  326. pch = getJustFileNameAndExt(pszFileDef,perr); // Get default name
  327. if (pch == NULL) {
  328. return FALSE; // perr already filled in
  329. }
  330. }
  331. else {
  332. pch = pszFile; // Use supplied name
  333. }
  334. strcat(pszResult,pch); // Append file name
  335. cbResult -= strlen(pch); // Update remaining size
  336. if (cbResult <= 0) {
  337. ErrSet(perr,pszFILERR_PATH_TOO_LONG,"%s",pch);
  338. return FALSE;
  339. }
  340. //** Success
  341. return TRUE;
  342. } /* catDirAndFile() */
  343. /*** ensureDirectory - Ensure directory exists (creating as needed)
  344. *
  345. * NOTE: See fileutil.h for entry/exit conditions.
  346. */
  347. BOOL ensureDirectory(char *pszPath, BOOL fHasFileName, PERROR perr)
  348. {
  349. char achDir[cbFILE_NAME_MAX]; // Partial directory buffer
  350. int cErrors;
  351. int cch;
  352. int cchNoPathSep;
  353. int i; // Temp file name count
  354. int fh; // File handle
  355. char *pch;
  356. char *pchCurr; // Current path separator
  357. char *pchNext; // Next path separator
  358. //** Find first path separator, if any.
  359. // NOTE: Have to handle case of "d:foo" specially!
  360. //
  361. for (pch=pszPath;
  362. *pch && // Not end of string
  363. (*pch!=chPATH_SEP1) && // Not path separator 1
  364. (*pch!=chPATH_SEP2) && // Not path separator 2
  365. ((*pch!=chDRIVE_SEP) || ((*(pch+1)==chPATH_SEP1) || // Not "d:\"
  366. (*(pch+1)==chPATH_SEP2)));
  367. CharIncr(pch)) {
  368. ; //
  369. }
  370. //** Set correct starting point for first directory component (if any)
  371. achDir[0] = '\0'; // Assume current directory
  372. if ((*pch == '\0') && // No path separators
  373. fHasFileName) { // Just a file name
  374. //** Do nothing; for loop below will be skipped because *pch == \0
  375. }
  376. else {
  377. //** Have to consider whole path
  378. pch = pszPath; // Need to ensure directories
  379. }
  380. //** Make sure directories on path exist (create them all)
  381. // We need to identify successive components and create directory
  382. // tree one component at a time. Since the directory may already
  383. // exist, we do the final test of creating a file there to make
  384. // sure we can do the write.
  385. for (pchCurr=pch, pchNext=pch; // Process path components
  386. *pchNext && *pchCurr; // Until no more //BC6
  387. pchCurr=pchNext+1) { // Skip over last path separator
  388. //** Find next path separator
  389. for (pch=pchCurr;
  390. *pch &&
  391. (*pch!=chPATH_SEP1) &&
  392. (*pch!=chPATH_SEP2) &&
  393. ((*pch!=chDRIVE_SEP) || ((*(pch+1)==chPATH_SEP1) || // Not "d:\"
  394. (*(pch+1)==chPATH_SEP2)));
  395. CharIncr(pch)) {
  396. ; //
  397. }
  398. pchNext = pch; // Location of next path separator
  399. //** Don't process last component if caller said it was a filename
  400. if ((*pchNext != '\0') || !fHasFileName) {
  401. //** We have a partial path; make sure directory exists
  402. cch = (int)(pchNext - pszPath); // Length of partial path
  403. if ((cch>0) &&
  404. ((*pchNext == chDRIVE_SEP) || (*(pchNext-1) == chDRIVE_SEP))) {
  405. //** Have "d:xxx" or "d:\xxx", so need to include ":" or "\"!
  406. cch++;
  407. }
  408. strncpy(achDir,pszPath,cch);
  409. achDir[cch] = '\0'; // Terminate path
  410. _mkdir(achDir); // Ignore any error
  411. }
  412. }
  413. //** Check for special case of root directory: "\" or "\xxx.yyy"
  414. if ((strlen(achDir) == 0) &&
  415. (strlen(pszPath) > 0) &&
  416. ((*pszPath == chPATH_SEP1) || (*pszPath == chPATH_SEP2))) {
  417. achDir[0] = *pszPath;
  418. achDir[1] = '\0';
  419. }
  420. //** Make sure there is an appropriate separator
  421. cch = strlen(achDir);
  422. cchNoPathSep = cch; // For error reporting
  423. if (cch > 0)
  424. {
  425. cch += appendPathSeparator(&(achDir[cch-1]));
  426. }
  427. //** Make sure we can write to the directory
  428. // achDir = Has path of directory to test
  429. cErrors = 0; // No errors, so far
  430. for (i=0; i<999; i++) {
  431. //** Form full file name
  432. sprintf(&achDir[cch],"CAB%5.5d.TMP",GetCurrentProcessId()+i);
  433. //** Make sure file does not exist, and can be created and written to
  434. fh = _open(achDir,
  435. _O_CREAT | _O_EXCL | _O_RDWR, // Must not exist, read/write
  436. _S_IREAD | _S_IWRITE); // Read & Write permission
  437. //** Figure out what happened
  438. if (fh == -1) {
  439. switch (errno) {
  440. case EACCES: // Was a dir, or read-only
  441. cErrors++;
  442. if (cErrors < 5) { // Tolerate this a few times
  443. continue; // Try next temp file name
  444. }
  445. achDir[cchNoPathSep] = '\0'; // Remove temp file name
  446. ErrSet(perr,pszFILERR_DIR_NOT_WRITEABLE,"%s",achDir);
  447. return FALSE;
  448. case EEXIST: // File already exists -- good sign!
  449. continue; // Try next temp file name
  450. case EMFILE: // Out of file handles
  451. achDir[cchNoPathSep] = '\0'; // Remove temp file name
  452. ErrSet(perr,pszFILERR_NO_MORE_FILE_HANDLES,"%s",achDir);
  453. return FALSE;
  454. case EINVAL: // oflag and/or pmode args are bad
  455. if (_doserrno == ERROR_DELETE_PENDING) {
  456. continue;
  457. }
  458. // fall through
  459. case ENOENT: // File/Path not found
  460. default:
  461. printf("EnsureDirectory: Cant create file: %s, errno=%d, _doserrno=%d, GLE=%d\n",
  462. achDir, errno, _doserrno, GetLastError() );
  463. achDir[cchNoPathSep] = '\0'; // Remove temp file name
  464. ErrSet(perr,pszFILERR_CANT_MAKE_DIR,"%s%d%d",
  465. achDir, errno, _doserrno);
  466. return FALSE;
  467. }
  468. }
  469. //** File was created, close it, delete it, and we're golden
  470. _close(fh); // Done with file
  471. _unlink(achDir); // Get rid of it
  472. return TRUE; // Success
  473. }
  474. //** Ran out of temp file names
  475. achDir[cchNoPathSep] = '\0'; // Remove temp file name
  476. ErrSet(perr,pszFILERR_OUT_OF_TMP_FILE_NAMES,"%d%s",i,achDir);
  477. return FALSE;
  478. } /* ensureDirectory() */
  479. /*** ensureFile - Ensure a file can be created
  480. *
  481. * NOTE: See fileutil.h for entry/exit conditions.
  482. */
  483. BOOL ensureFile(char *pszFile, char *pszDesc, PERROR perr)
  484. {
  485. int fh;
  486. //** Make sure directory is present
  487. if (!ensureDirectory(pszFile,TRUE,perr)) {
  488. //** Override error message with more meaningful one
  489. ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s",pszDesc,pszFile);
  490. return FALSE;
  491. }
  492. //** Make sure file can be created
  493. fh = _open(pszFile,
  494. _O_CREAT | _O_RDWR, // Create if necessary, read/write
  495. _S_IREAD | _S_IWRITE); // Read & Write permission
  496. if (fh == -1) {
  497. switch (errno) {
  498. case EMFILE: // Out of file handles
  499. ErrSet(perr,pszFILERR_NO_MORE_FILE_HANDLES,"%s",pszFile);
  500. return FALSE;
  501. case EACCES: // Was a dir, or read-only
  502. case ENOENT: // File/Path not found
  503. case EINVAL: // oflag and/or pmode args are bad
  504. default:
  505. ErrSet(perr,pszFILERR_CANT_CREATE_FILE,"%s%s",pszDesc,pszFile);
  506. return FALSE;
  507. }
  508. }
  509. //** File was created; close it, delete it, and we're golden
  510. _close(fh); // Done with file
  511. _unlink(pszFile); // Get rid of it
  512. return TRUE;
  513. } /* ensureFile() */
  514. /*** getJustFileNameAndExt - Get last component in filespec
  515. *
  516. * NOTE: See fileutil.h for entry/exit conditions.
  517. */
  518. char *getJustFileNameAndExt(char *pszPath, PERROR perr)
  519. {
  520. char *pch=pszPath;
  521. char *pchStart=pszPath; // Assume filespec is just a name[.ext]
  522. //** Find last path separator
  523. while (*pch) {
  524. switch (*pch) {
  525. case chPATH_SEP1:
  526. case chPATH_SEP2:
  527. case chDRIVE_SEP:
  528. pchStart = pch+1; // Name starts after path/drive separator
  529. break;
  530. }
  531. CharIncr(pch); // Check next character
  532. }
  533. //** Make sure file name is not empty
  534. if (*pchStart == '\0') { // Empty file name
  535. ErrSet(perr,pszFILERR_EMPTY_FILE_NAME,"%s",pszPath);
  536. return NULL; // Failure
  537. }
  538. else {
  539. return pchStart; // Success
  540. }
  541. } /* getJustFileNameAndExt() */
  542. /*** IsWildMatch - Test filespec against wild card specification
  543. *
  544. * NOTE: See fileutil.h for entry/exit conditions.
  545. */
  546. BOOL IsWildMatch(char *pszPath, char *pszWild, PERROR perr)
  547. {
  548. char chNext;
  549. char *psz;
  550. char *psz1; // Walks through filespec
  551. char *psz2; // Walks through pattern
  552. // 10/24/96 jforbes Make *.* match everything, even i.have.many.dots
  553. if (!strcmp(pszWild, pszALL_FILES))
  554. return TRUE;
  555. psz1 = pszPath; // Filespec to test
  556. psz2 = pszWild; // Test pattern
  557. // While there is pattern to account for keep going
  558. while (*psz2) {
  559. switch (*psz2) { // Handle wild card chars in pattern
  560. case chWILD_RUN:
  561. //** Find next non-wildcard character => treat run of */? as 1 *
  562. for (psz=psz2+1;
  563. (*psz == chWILD_RUN) || (*psz == chWILD_CHAR);
  564. CharIncr(psz)) {
  565. ; //** Skip through pattern string
  566. }
  567. //** *psz is either EOL, or not a wildcard
  568. chNext = *psz; // Character to terminate run
  569. //** Span until run terminates -- either
  570. while ((*psz1 != '\0') && // Don't run off filespec
  571. (*psz1 != chNext) && // Stop at run terminator
  572. (*psz1 != chNAME_EXT_SEP)) { // "." not allowed to match
  573. CharIncr(psz1);
  574. }
  575. //** At this point, we've matched as much as we could;
  576. // If there is a failure, the next iteration through the
  577. // loop will find it; So, just update the pattern position.
  578. psz2 = psz;
  579. break;
  580. case chWILD_CHAR:
  581. if (*psz1 == chNAME_EXT_SEP) { // Match anything but "."
  582. return FALSE; // Found point of mismatch
  583. }
  584. if (*psz1)
  585. CharIncr(psz1); // Next position in filespec
  586. CharIncr(psz2); // Next position in pattern
  587. break;
  588. case chNAME_EXT_SEP:
  589. if (*psz1 == chNAME_EXT_SEP) {
  590. psz1++;
  591. CharIncr(psz2);
  592. } else if (*psz1 == '\0') {
  593. CharIncr(psz2);
  594. } else {
  595. return FALSE;
  596. }
  597. break;
  598. default:
  599. if (toupper(*psz1) != toupper(*psz2)) { // Still match
  600. return FALSE; // Found point of mismatch
  601. }
  602. if (*psz1)
  603. CharIncr(psz1); // Next position in filespec
  604. CharIncr(psz2); // Next position in pattern
  605. break;
  606. }
  607. }
  608. //** We have a match if *both* strings were fully consumed
  609. return ((*psz1 == '\0') && (*psz2 == '\0'));
  610. } /* IsWildMatch() */
  611. #pragma optimize("",off) // Avoid optimizer warning on in-line ASM
  612. /*** IsPathRemovable - See if path refers to a removable media drive
  613. *
  614. * NOTE: See fileutil.h for entry/exit conditions.
  615. */
  616. BOOL IsPathRemovable(char *pszPath, char *pchDrive)
  617. {
  618. char ach[4]="x:\\"; // Buffer for "x:\"
  619. BOOL fRemovable;
  620. char iDrive;
  621. //** Get drive for path
  622. if ((strlen(pszPath) >= 2) &&
  623. isalpha(pszPath[0]) &&
  624. (pszPath[1] == chDRIVE_SEP)) {
  625. iDrive = toupper(pszPath[0]) - 'A' + 1;
  626. }
  627. else {
  628. iDrive = (char)_getdrive();
  629. }
  630. *pchDrive = 'A' + iDrive - 1; // Return drive letter
  631. #ifdef BIT16
  632. //** Do it the MS-DOS way
  633. _asm {
  634. mov fRemovable,0 ; Assume not removable
  635. mov bl,iDrive ; (0=default; 1=A, ...)
  636. mov ax,4408h ; IOCTL Get Removable Media
  637. int 21h ; Call MS-DOS
  638. jc not_removable ; Error, assume not removable
  639. or ax,ax ; Test removability flag
  640. jne not_removable
  641. mov fRemovable,1 ; Drive is removable
  642. not_removable:
  643. }
  644. #else // !BIT16
  645. //** Do it the Win32 way
  646. ach[0] = *pchDrive; // Construct path to root of drive to test
  647. fRemovable = GetDriveType(ach) == DRIVE_REMOVABLE;
  648. #endif
  649. return fRemovable; // Return removability
  650. } /* IsPathRemovable() */
  651. #pragma optimize("",on) // Restore previous Optimization settings
  652. /*** GetFileTimeAndAttr - Get date, time, and attributes from a file
  653. *
  654. * NOTE: See fileutil.h for entry/exit conditions.
  655. */
  656. BOOL GetFileTimeAndAttr(PFILETIMEATTR pfta, char *pszFile, PERROR perr)
  657. {
  658. #ifdef BIT16
  659. //** Do it the MS-DOS way
  660. int hf;
  661. hf = _open(pszFile, _O_RDONLY | _O_BINARY);
  662. if (hf == -1) {
  663. ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile);
  664. return FALSE;
  665. }
  666. //WARNING: 30-Mar-1994 bens Ignore errors???
  667. _dos_getftime(hf,&pfta->date,&pfta->time);
  668. _close(hf);
  669. _dos_getfileattr(pszFile,&pfta->attr);
  670. return TRUE;
  671. #else // !BIT16
  672. //** Do it the Win32 way
  673. BOOL rc;
  674. FILETIME ft;
  675. FILETIME ftUTC; // Win32 returns Universal Time Code
  676. HANDLE hfQuery;
  677. hfQuery = CreateFile(pszFile, // open again with Win32
  678. GENERIC_READ, // Just to read
  679. FILE_SHARE_READ,// Coexist with previous open
  680. NULL, // No security
  681. OPEN_EXISTING, // Must exist
  682. 0L, // We're not setting any attributes
  683. NULL); // No template handle
  684. if (hfQuery == INVALID_HANDLE_VALUE) {
  685. ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile);
  686. return FALSE;
  687. }
  688. //** Get date/time and convert it
  689. rc = GetFileTime(hfQuery,NULL,NULL,&ftUTC);
  690. rc |= FileTimeToLocalFileTime(&ftUTC,&ft); // Apply timezone
  691. rc |= FileTimeToDosDateTime(&ft,&pfta->date,&pfta->time);
  692. CloseHandle(hfQuery);
  693. //** Get attributes and convert them
  694. pfta->attr = AttrFATFromAttr32(GetFileAttributes(pszFile));
  695. if (!rc) {
  696. ErrSet(perr,pszFILERR_CANNOT_GET_FILE_INFO,"%s",pszFile);
  697. return FALSE;
  698. }
  699. return TRUE;
  700. #endif
  701. } /* GetFileTimeAndAttr() */
  702. /*** SetFileTimeAndAttr - Set date, time, and attributes of a file
  703. *
  704. * NOTE: See fileutil.h for entry/exit conditions.
  705. */
  706. BOOL SetFileTimeAndAttr(char *pszFile, PFILETIMEATTR pfta, PERROR perr)
  707. {
  708. #ifdef BIT16
  709. //** Do it the MS-DOS way
  710. int hf;
  711. hf = _open(pszFile,_O_WRONLY | _O_BINARY);
  712. if (hf == -1) {
  713. ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszFile);
  714. return FALSE;
  715. }
  716. _dos_setftime(hf,pfta->date,pfta->time);
  717. _close(hf);
  718. _dos_setfileattr(pszFile,pfta->attr);
  719. return TRUE;
  720. #else // !BIT16
  721. //** Do it the Win32 way
  722. HANDLE hfSet;
  723. FILETIME ft;
  724. FILETIME ftUTC; // Win32 needs Universal Time Code
  725. BOOL rc;
  726. hfSet = CreateFile(pszFile, // open with Win32
  727. GENERIC_WRITE, // Need to be able to modify properties
  728. 0, // Deny all
  729. NULL, // No security
  730. OPEN_EXISTING, // Must exist
  731. 0L, // We're not setting any attributes
  732. NULL); // No template handle
  733. if (hfSet == INVALID_HANDLE_VALUE) {
  734. // Changed this to retry because NT 4.0 occasionally returns a
  735. // sharing violation even though we've just closed the file.
  736. // Although this always worked in testing, also made it non-fatal so
  737. // that if the retry doesn't work it won't abort the extraction.
  738. Sleep(100); // give OS opportunity to sort it out
  739. hfSet = CreateFile(pszFile, // open with Win32
  740. GENERIC_WRITE, // Need to be able to modify properties
  741. 0, // Deny all
  742. NULL, // No security
  743. OPEN_EXISTING, // Must exist
  744. 0L, // We're not setting any attributes
  745. NULL); // No template handle
  746. if (hfSet == INVALID_HANDLE_VALUE) {
  747. return TRUE;
  748. }
  749. }
  750. rc = DosDateTimeToFileTime(pfta->date,pfta->time,&ft);
  751. rc |= LocalFileTimeToFileTime(&ft, &ftUTC); // Apply timezone
  752. rc |= SetFileTime(hfSet,NULL,NULL,&ftUTC);
  753. CloseHandle(hfSet);
  754. rc |= SetFileAttributes(pszFile,Attr32FromAttrFAT(pfta->attr));
  755. if (!rc) {
  756. ErrSet(perr,pszFILERR_CANNOT_SET_FILE_INFO,"%s",pszFile);
  757. return FALSE;
  758. }
  759. return TRUE;
  760. #endif // !BIT16
  761. } /* SetFileTimeAndAttr() */
  762. /*** CopyOneFile - Make a faithful copy of a file
  763. *
  764. * NOTE: See fileutil.h for entry/exit conditions.
  765. */
  766. BOOL CopyOneFile(char *pszDst,
  767. char *pszSrc,
  768. BOOL fCopy,
  769. UINT cbBuffer,
  770. PFNOVERRIDEFILEPROPERTIES pfnofp,
  771. void *pv,
  772. PERROR perr)
  773. {
  774. UINT cbRead;
  775. UINT cbWritten;
  776. BOOL fSuccess = FALSE; // Assume failure
  777. FILETIMEATTR fta;
  778. int hfDst = -1;
  779. int hfSrc = -1;
  780. char *pbuf = NULL;
  781. //** Open source
  782. hfSrc = _open(pszSrc, _O_RDONLY | _O_BINARY);
  783. if (hfSrc == -1) {
  784. ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszSrc);
  785. goto cleanup;
  786. }
  787. //** Get file date, time, and attributes for source file
  788. if (!GetFileTimeAndAttr(&fta,pszSrc,perr)) {
  789. goto cleanup;
  790. }
  791. //** Permit caller to override date/time/attr
  792. if (pfnofp != NULL) {
  793. if (!(*pfnofp)(&fta,pv,perr)) { // Call override function
  794. goto cleanup; // Error, go cleanup
  795. }
  796. }
  797. //** Early out if we were just merging file date/time/attr values
  798. if (!fCopy) {
  799. fSuccess = TRUE; // Success
  800. goto cleanup; // Go close source and exit
  801. }
  802. //** Get copy buffer
  803. if (!(pbuf = MemAlloc(cbBuffer))) {
  804. ErrSet(perr,pszFILERR_NO_MEMORY_FOR_BUFFER,"%s%s",pszSrc,pszDst);
  805. goto cleanup;
  806. }
  807. //** Open destination
  808. hfDst = _open(pszDst,
  809. _O_BINARY | _O_WRONLY | _O_CREAT | _O_TRUNC, // No translation, R/W
  810. _S_IREAD | _S_IWRITE); // Attributes when file is closed
  811. if (hfDst == -1) {
  812. ErrSet(perr,pszFILERR_OPEN_FAILED,"%s",pszDst);
  813. goto cleanup;
  814. }
  815. //** Copy data
  816. while (!_eof(hfSrc)) {
  817. //** Read chunk
  818. cbRead = _read(hfSrc,pbuf,cbBuffer);
  819. if (cbRead == -1) {
  820. ErrSet(perr,pszFILERR_READ_FILE,"%s",pszSrc);
  821. goto cleanup;
  822. }
  823. else if (cbRead != 0) { // Not at EOF
  824. //** Write it
  825. cbWritten = _write(hfDst,pbuf,cbRead);
  826. if (cbWritten != cbRead) {
  827. ErrSet(perr,pszFILERR_WRITE_FILE,"%s",pszSrc);
  828. goto cleanup;
  829. }
  830. }
  831. }
  832. //** Done copying, close destination file handle
  833. _close(hfDst);
  834. hfDst = -1; // Avoid unnecessary close in cleanup
  835. //** Set file date, time, and attributes
  836. if (!SetFileTimeAndAttr(pszDst,&fta,perr)) {
  837. goto cleanup;
  838. }
  839. //** Success!
  840. fSuccess = TRUE;
  841. cleanup:
  842. if (hfDst != -1) {
  843. _close(hfDst);
  844. }
  845. if (hfSrc != -1) {
  846. _close(hfSrc);
  847. }
  848. if (pbuf) {
  849. MemFree(pbuf);
  850. }
  851. return fSuccess;
  852. } /* CopyOneFile() */
  853. #ifndef BIT16
  854. //** Win32 stuff
  855. /*** Attr32FromAttrFAT - Convert FAT file attributes to Win32 form
  856. *
  857. * NOTE: See fileutil.h for entry/exit conditions.
  858. */
  859. DWORD Attr32FromAttrFAT(WORD attrMSDOS)
  860. {
  861. //** Quick out for normal file special case
  862. if (attrMSDOS == _A_NORMAL) {
  863. return FILE_ATTRIBUTE_NORMAL;
  864. }
  865. //** Otherwise, mask off read-only, hidden, system, and archive bits
  866. // NOTE: These bits are in the same places in MS-DOS and Win32!
  867. //
  868. Assert(_A_RDONLY == FILE_ATTRIBUTE_READONLY);
  869. Assert(_A_HIDDEN == FILE_ATTRIBUTE_HIDDEN);
  870. Assert(_A_SYSTEM == FILE_ATTRIBUTE_SYSTEM);
  871. Assert(_A_ARCH == FILE_ATTRIBUTE_ARCHIVE);
  872. return attrMSDOS & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH);
  873. }
  874. /*** AttrFATFromAttr32 - Convert Win32 file attributes to FAT form
  875. *
  876. * NOTE: See fileutil.h for entry/exit conditions.
  877. */
  878. WORD AttrFATFromAttr32(DWORD attr32)
  879. {
  880. //** Quick out for normal file special case
  881. if (attr32 & FILE_ATTRIBUTE_NORMAL) {
  882. return _A_NORMAL;
  883. }
  884. //** Otherwise, mask off read-only, hidden, system, and archive bits
  885. // NOTE: These bits are in the same places in MS-DOS and Win32!
  886. //
  887. Assert(_A_RDONLY == FILE_ATTRIBUTE_READONLY);
  888. Assert(_A_HIDDEN == FILE_ATTRIBUTE_HIDDEN);
  889. Assert(_A_SYSTEM == FILE_ATTRIBUTE_SYSTEM);
  890. Assert(_A_ARCH == FILE_ATTRIBUTE_ARCHIVE);
  891. return ((WORD)attr32) & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH);
  892. }
  893. #endif // !BIT16