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.

1604 lines
40 KiB

  1. #ifndef WIN32
  2. #define RC_INVOKED
  3. #endif
  4. #include <windows.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <stdio.h>
  8. #include <stddef.h>
  9. #include <stdlib.h>
  10. #include <stdarg.h>
  11. #include <limits.h>
  12. #include <malloc.h>
  13. #include <errno.h>
  14. #include <ctype.h>
  15. #include <signal.h>
  16. #include <string.h>
  17. #include <time.h>
  18. #include <conio.h>
  19. #include <io.h>
  20. #include <sys\types.h>
  21. #include <sys\stat.h>
  22. #include <imagehlp.h>
  23. #define READ_BUFFER_SIZE (16 * 1024 * sizeof(DWORD)) // 64k blocks
  24. #define CHECK_NAME "\\chkfile.chk"
  25. LPSTR
  26. atolx(
  27. LPSTR psz,
  28. LPDWORD pul);
  29. DWORD
  30. ParseCheckFile (
  31. VOID
  32. );
  33. HANDLE
  34. PortFindFirstFile(
  35. LPSTR FindPattern,
  36. BOOL fNormal,
  37. LPSTR FindName,
  38. LPDWORD FindSize);
  39. BOOL
  40. PortFindNextFile(
  41. HANDLE hFind,
  42. BOOL fNormal,
  43. LPSTR FindName,
  44. LPDWORD FindSize);
  45. VOID
  46. PortFindClose(
  47. HANDLE hFind
  48. );
  49. UINT ProcessCheckFile(LPINT pcfiles);
  50. VOID Usage(VOID);
  51. VOID __cdecl crerror(LPSTR pszfmt, ...);
  52. LPSTR *ProcessParameters(INT *pargc, LPSTR argv[]);
  53. LPSTR ProcessArgBuf(LPSTR pszargs);
  54. BOOL OpenCheckFile(VOID);
  55. BOOL ProcessEntry(LPSTR pszFullPath, LPSTR pszRelPath);
  56. BOOL FindEntry(LPSTR pszRelPath, PULONG pSum);
  57. DWORD MissingEntries(VOID);
  58. VOID ReadCheckHeader(FILE *pf);
  59. VOID RecursiveCheckHeader(void);
  60. VOID WriteCheckHeader(FILE *pf);
  61. LPSTR iscomment(LPSTR psz);
  62. LPSTR ismatch(LPSTR psz, LPSTR pszcompact);
  63. LPSTR iscomment(LPSTR psz);
  64. LPSTR AddDirectory(LPSTR psz);
  65. BOOL AddEntry(LPSTR psz, BOOL frequired);
  66. BOOL AddComponent(LPSTR pszdir, LPSTR pszpat, BOOL fdir, BOOL frequired);
  67. LPSTR ReadDirectory(LPSTR pszdir);
  68. #define CHECKSTRLEN(psz, cbmax) \
  69. if (strlen(psz) > cbmax) { \
  70. crerror("String overflow at line %u (%s)", __LINE__, psz); \
  71. exit(4); \
  72. }
  73. #define DEFAULTROOT "nt"
  74. //
  75. // Defined parsed check file entry structure and table storage.
  76. //
  77. typedef struct _CHECK_FILE_ENTRY {
  78. struct _CHECK_FILE_ENTRY *Next;
  79. DWORD Sum;
  80. WORD Length;
  81. CHAR *Name;
  82. } CHECK_FILE_ENTRY, *PCHECK_FILE_ENTRY;
  83. #define CHECK_ENTRY_TABLE_SIZE 4096
  84. CHECK_FILE_ENTRY CheckEntryTable[CHECK_ENTRY_TABLE_SIZE];
  85. //
  86. // Define root of parsed check file list.
  87. //
  88. CHECK_FILE_ENTRY CheckEntryRoot;
  89. struct component_s {
  90. struct component_s *pcmNext; // next in linked list
  91. BOOL fDir; // TRUE if directory
  92. BOOL fFound; // TRUE if found
  93. BOOL fRequired; // TRUE if must exist
  94. CHAR achPat[1]; // path component (subdir or pattern)
  95. };
  96. struct checkpath_s {
  97. struct checkpath_s *pcpNext; // next in linked list
  98. struct component_s *pcmPat; // subdirectories and file patterns
  99. CHAR achDir[1]; // root relative directory path
  100. };
  101. struct checkpath_s *pcpPaths = NULL;
  102. DWORD cbCheck;
  103. LPSTR pszCheckFileName = NULL; // input/output check file path
  104. LPSTR pszLogFileName = NULL; // error log file path
  105. FILE *pfCheck = NULL; // input/output check stdio file pointer
  106. FILE *pfLog; // error log file pointer
  107. LPSTR pszCheck = NULL; // input check file contents
  108. LPSTR RootOfTree = DEFAULTROOT;
  109. BOOL fInProgress = FALSE;
  110. UINT cbProgress = 0;
  111. BOOL fAll = FALSE;
  112. BOOL fCommand = FALSE;
  113. BOOL fGenerateCheck = FALSE;
  114. BOOL fNoArgs = FALSE;
  115. BOOL fRecurse = FALSE;
  116. BOOL fPrintMissing = TRUE;
  117. BOOL fPrintExtra = TRUE;
  118. DWORD fCdCheck;
  119. CHAR OutputLine[512];
  120. DWORD ReadBuffer[READ_BUFFER_SIZE / sizeof(DWORD) + 1];
  121. //
  122. // this table must be in alphabetical order !!!
  123. //
  124. LPSTR pszDefaultDir =
  125. "#directory start\n"
  126. #if defined(i386)
  127. "*.\n"
  128. "*.com\n"
  129. #endif
  130. #if defined(MIPS) || defined(_ALPHA_)
  131. "*.dll\n"
  132. "*.exe\n"
  133. #endif
  134. #if defined(PPC)
  135. "*.exe\n"
  136. #endif
  137. "?\\*.*\n"
  138. "?\\dump\\ optional\n"
  139. "?\\dump\\*.* optional\n"
  140. "?\\idw\\ optional\n"
  141. "?\\idw\\*.* optional\n"
  142. "?\\idw\\setup\\ optional\n"
  143. "?\\idw\\setup\\*.* optional\n"
  144. "?\\km\\ optional\n"
  145. "?\\km\\*.* optional\n"
  146. "?\\km\\symbols\\ optional\n"
  147. "?\\km\\symbols\\dll\\ optional\n"
  148. "?\\km\\symbols\\dll\\*.* optional\n"
  149. "?\\km\\symbols\\sys\\ optional\n"
  150. "?\\km\\symbols\\sys\\*.* optional\n"
  151. "?\\km\\system32\\ optional\n"
  152. "?\\km\\system32\\*.* optional\n"
  153. "?\\km\\system32\\drivers\\ optional\n"
  154. "?\\km\\system32\\drivers\\*.* optional\n"
  155. "?\\mstools\\ optional\n"
  156. "?\\mstools\\*.* optional\n"
  157. "?\\nws\\ optional\n"
  158. "?\\nws\\*.* optional\n"
  159. "?\\symbols\\*.* optional\n"
  160. "?\\symbols\\acm\\*.* optional\n"
  161. "?\\symbols\\com\\*.* optional\n"
  162. "?\\symbols\\cpl\\*.* optional\n"
  163. "?\\symbols\\dll\\*.* optional\n"
  164. "?\\symbols\\drv\\*.* optional\n"
  165. "?\\symbols\\exe\\*.* optional\n"
  166. "?\\symbols\\scr\\*.* optional\n"
  167. "?\\symbols\\sys\\*.* optional\n"
  168. "?\\system\\*.*\n"
  169. "?\\system32\\*.*\n"
  170. "?\\system32\\config\\*.*\n"
  171. "?\\system32\\dhcp\\*.* optional\n"
  172. "?\\system32\\drivers\\*.*\n"
  173. "?\\system32\\drivers\\etc\\*.*\n"
  174. #ifdef i386
  175. "?\\system32\\os2\\ optional\n"
  176. "?\\system32\\os2\\dll\\ optional\n"
  177. "?\\system32\\os2\\dll\\*.* optional\n"
  178. #endif
  179. "?\\system32\\ras\\*.* optional\n"
  180. "?\\system32\\spool\\ optional\n"
  181. "?\\system32\\spool\\drivers\\ optional\n"
  182. "?\\system32\\spool\\prtprocs\\ optional\n"
  183. #ifdef MIPS
  184. "?\\system32\\spool\\prtprocs\\w32mips\\ optional\n"
  185. "?\\system32\\spool\\prtprocs\\w32mips\\*.dll optional\n"
  186. #endif
  187. #ifdef _ALPHA_
  188. "?\\system32\\spool\\prtprocs\\w32alpha\\ optional\n"
  189. "?\\system32\\spool\\prtprocs\\w32alpha\\*.dll optional\n"
  190. #endif
  191. #ifdef i386
  192. "?\\system32\\spool\\prtprocs\\w32x86\\ optional\n"
  193. "?\\system32\\spool\\prtprocs\\w32x86\\*.dll optional\n"
  194. #endif
  195. #ifdef PPC
  196. "?\\system32\\spool\\prtprocs\\w32ppc\\ optional\n"
  197. "?\\system32\\spool\\prtprocs\\w32ppc\\*.dll optional\n"
  198. #endif
  199. "?\\system32\\wins\\*.* optional\n"
  200. "?\\ui\\ optional\n"
  201. "?\\ui\\*.* optional\n"
  202. "?\\ui\\dump\\ optional\n"
  203. "?\\ui\\dump\\*.* optional\n"
  204. "?\\ui\\symbols\\ optional\n"
  205. "?\\ui\\symbols\\cpl\\ optional\n"
  206. "?\\ui\\symbols\\cpl\\*.* optional\n"
  207. "?\\ui\\symbols\\dll\\ optional\n"
  208. "?\\ui\\symbols\\dll\\*.* optional\n"
  209. "?\\ui\\symbols\\exe\\ optional\n"
  210. "?\\ui\\symbols\\exe\\*.* optional\n"
  211. "?\\ui\\system32\\ optional\n"
  212. "?\\ui\\system32\\*.* optional\n"
  213. #ifdef i386
  214. "?\\wdl\\ optional\n"
  215. "?\\wdl\\video\\ optional\n"
  216. "?\\wdl\\video\\avga\\ optional\n"
  217. "?\\wdl\\video\\avga\\*.* optional\n"
  218. #endif
  219. "#directory end\n"
  220. "";
  221. VOID
  222. CdCheck()
  223. {
  224. #if 0
  225. LPSTR line=NULL;
  226. LPSTR psz;
  227. CHAR partialname[256];
  228. CHAR fullname[256];
  229. char flatname[256];
  230. DWORD ChkFileSum,ChkFileSize;
  231. LPSTR FilePart;
  232. DWORD ActualSize, ActualSum;
  233. FILE *pf = NULL;
  234. //
  235. // We are checking the CD. Read the entire checkfile
  236. // and cross check each entry against contents of the
  237. // CD
  238. //
  239. line = pszCheck;
  240. for ( line = pszCheck; line != NULL ; line = strchr(line, '\n')) {
  241. if (line >= pszCheck + cbCheck - 1) {
  242. line = pszCheck;
  243. }
  244. if (*line == '\n') {
  245. line++;
  246. }
  247. if (*line == '\0') {
  248. break;
  249. }
  250. if (*line == '\n') {
  251. continue; // skip used entries & empty lines
  252. }
  253. psz = line;
  254. while (*psz == ' ' || *psz == '\t') {
  255. psz++; // skip leading whitespace
  256. }
  257. if (*psz == '\n') {
  258. continue; // skip empty line
  259. }
  260. //
  261. // psz points to name sum size
  262. //
  263. sscanf(psz,"%s %x %x",partialname,&ChkFileSum,&ChkFileSize);
  264. GetFullPathName(partialname,sizeof(fullname),fullname,&FilePart);
  265. strcpy(flatname,RootOfTree);
  266. strcat(flatname,"\\");
  267. strcat(flatname,FilePart);
  268. pf = fopen(flatname, "rb");
  269. if (pf == NULL) {
  270. strcpy(flatname,RootOfTree);
  271. strcpy(flatname+2,"\\mstools\\bin");
  272. strcat(flatname,RootOfTree+2);
  273. strcat(flatname,"\\");
  274. strcat(flatname,FilePart);
  275. pf = fopen(flatname, "rb");
  276. if (pf == NULL) {
  277. if ( strstr(partialname,"idw\\") ) {
  278. goto nextone;
  279. }
  280. if ( strstr(partialname,"dump\\") ) {
  281. goto nextone;
  282. }
  283. crerror("Cannot open file(%d): %s", errno, FilePart);
  284. goto nextone;
  285. }
  286. }
  287. ActualSize = _filelength(_fileno(pf));
  288. if (ActualSize == 0xffffffff) {
  289. crerror("Cannot determine size of file: %s %d", FilePart, errno);
  290. fclose(pf);
  291. goto nextone;
  292. }
  293. if (ActualSize != ChkFileSize) {
  294. crerror("Size differs (actual %lx, expected %lx): %s",
  295. ActualSize,
  296. ChkFileSize,
  297. FilePart);
  298. fclose(pf);
  299. goto nextone;
  300. }
  301. // ActualSum = CheckSumFile(pf, flatname, flatname, &ActualSize);
  302. if (ActualSum != ChkFileSum) {
  303. crerror("Sum differs (actual %lx, expected %lx): %s",
  304. ActualSum,
  305. ChkFileSum,
  306. FilePart);
  307. }
  308. nextone:;
  309. }
  310. #endif /* 0 */
  311. }
  312. INT __cdecl
  313. main(
  314. INT argc,
  315. LPSTR argv[]
  316. )
  317. {
  318. UINT rc;
  319. pfLog = stderr;
  320. //
  321. // Initialize check file entry root list entry.
  322. //
  323. CheckEntryRoot.Next = NULL;
  324. argv = ProcessParameters(&argc, argv);
  325. if (fCommand) {
  326. pfCheck = stdout;
  327. rc = 0;
  328. while (argc > 1) {
  329. argc--;
  330. argv++;
  331. _strlwr(*argv);
  332. if (!ProcessEntry(*argv, *argv)) {
  333. rc++;
  334. }
  335. }
  336. } else {
  337. long l;
  338. time_t t = time(NULL);
  339. INT cfiles;
  340. // If we are generating a check file, then generate it.
  341. // Otherwise just check the release.
  342. rc = ProcessCheckFile(&cfiles);
  343. l = (long)(time(NULL) - t);
  344. printf("\n%3u files: %lu:%02lu\n", cfiles, l/60, l % 60);
  345. }
  346. exit(rc);
  347. return rc;
  348. }
  349. LPSTR pszUsage =
  350. "usage: checkrel [-?] display this message\n"
  351. " [-a] process all files\n"
  352. " [-c] command line contains file names to sum\n"
  353. " [-f chkfile] input/output check file override\n"
  354. " [-g] generate check file\n"
  355. " [-l logfile] stderr log file\n"
  356. " [-n] suppress check file arguments\n"
  357. " [-r pathname] root path override\n"
  358. " [-R] recursive file check\n"
  359. " [-m] master cdrom check\n"
  360. " [-i] don't warn about missing files\n"
  361. " [-x] don't warn about extra files\n"
  362. "";
  363. VOID
  364. Usage(VOID)
  365. {
  366. fprintf(stderr, pszUsage);
  367. exit(1);
  368. }
  369. VOID
  370. __cdecl
  371. crerror(
  372. LPSTR pszfmt,
  373. ...
  374. )
  375. {
  376. va_list argptr;
  377. va_start(argptr, pszfmt);
  378. if (fInProgress && pfLog == stderr) {
  379. printf("\r%*s\r", cbProgress, ""); // clear line
  380. fflush(stdout);
  381. fInProgress = FALSE;
  382. }
  383. fprintf(pfLog, "CheckRel: ");
  384. vfprintf(pfLog, pszfmt, argptr);
  385. fprintf(pfLog, "\n");
  386. }
  387. LPSTR *
  388. ProcessParameters(INT *pargc, LPSTR argv[])
  389. {
  390. CHAR cswitch, c, *p;
  391. while (*pargc > 1) {
  392. --(*pargc);
  393. p = *++argv;
  394. if ((cswitch = *p) == '/' || cswitch == '-') {
  395. while (c = *++p) {
  396. switch (c) {
  397. case '?':
  398. Usage();
  399. case 'm': fCdCheck++; break;
  400. case 'a': fAll++; break;
  401. case 'c': fCommand++; break;
  402. case 'g': fGenerateCheck++; break;
  403. case 'n': fNoArgs++; break;
  404. case 'i': fPrintMissing = FALSE; break;
  405. case 'x': fPrintExtra = FALSE; break;
  406. case 'R': fRecurse++; break;
  407. case 'f':
  408. if (p[1] == '\0' && --(*pargc)) {
  409. ++argv;
  410. if (pszCheckFileName == NULL) {
  411. pszCheckFileName = *argv;
  412. break;
  413. }
  414. crerror("Check file specified twice: -f %s -f %s",
  415. pszCheckFileName,
  416. *argv);
  417. Usage();
  418. }
  419. Usage();
  420. case 'l':
  421. if (p[1] == '\0' && --(*pargc)) {
  422. ++argv;
  423. if (pszLogFileName == NULL) {
  424. pfLog = fopen(*argv, "wt");
  425. if (pfLog == NULL) {
  426. pfLog = stderr;
  427. crerror("Cannot open %s (%d)", *argv, errno);
  428. exit(2);
  429. }
  430. pszLogFileName = *argv;
  431. break;
  432. }
  433. crerror("Log file specified twice: -l %s -l %s",
  434. pszLogFileName,
  435. *argv);
  436. Usage();
  437. }
  438. Usage();
  439. case 'r':
  440. if (p[1] == '\0' && --(*pargc)) {
  441. ++argv;
  442. RootOfTree = _strdup(*argv);
  443. if (RootOfTree == NULL) {
  444. crerror("Out of memory for tree root");
  445. exit(2);
  446. }
  447. break;
  448. }
  449. Usage();
  450. default:
  451. crerror("Invalid switch: -%c", c);
  452. Usage();
  453. }
  454. }
  455. } else if (fCommand) {
  456. (*pargc)++;
  457. argv--;
  458. break;
  459. } else {
  460. crerror("Extra argument: %s", p);
  461. Usage();
  462. }
  463. }
  464. if (fCommand || fRecurse) {
  465. fGenerateCheck = TRUE;
  466. fAll = TRUE;
  467. }
  468. return(argv);
  469. }
  470. LPSTR
  471. ProcessArgBuf(LPSTR pszargs)
  472. {
  473. UINT i;
  474. INT argc;
  475. LPSTR pb;
  476. LPSTR psz;
  477. LPSTR *ppsz;
  478. LPSTR argv[20];
  479. CHAR achbuf[512];
  480. ppsz = argv;
  481. *ppsz++ = "Check File";
  482. psz = achbuf;
  483. if ((pb = strchr(pszargs, '\n')) != NULL) {
  484. pb++;
  485. while (*pszargs == ' ' || *pszargs == '\t') {
  486. pszargs++; // skip leading white space
  487. }
  488. if (*pszargs == '-') {
  489. for (;;) {
  490. i = strcspn(pszargs, " \t\n");
  491. *ppsz++ = psz;
  492. if (ppsz - argv + 1 >= sizeof(argv)/sizeof(argv[0])) {
  493. crerror("Too many file args (%d)", ppsz - argv);
  494. exit(2);
  495. }
  496. if (psz - achbuf + i + 2 >= sizeof(achbuf)) {
  497. crerror("Too many file arg chars (%d)", sizeof(achbuf));
  498. exit(2);
  499. }
  500. strncpy(psz, pszargs, i);
  501. psz += i;
  502. *psz++ = '\0';
  503. if (pszargs[i] == '\n') {
  504. break;
  505. }
  506. pszargs += i + 1;
  507. while (*pszargs == ' ' || *pszargs == '\t') {
  508. pszargs++; // skip leading white space
  509. }
  510. }
  511. *ppsz = NULL;
  512. argc = (INT)(ppsz - argv);
  513. if (!fNoArgs) {
  514. if (fGenerateCheck) {
  515. printf("Check file arguments:");
  516. for (ppsz = &argv[1]; *ppsz != NULL; ppsz++) {
  517. printf(" %s", *ppsz);
  518. }
  519. printf("\n");
  520. }
  521. ProcessParameters(&argc, argv);
  522. }
  523. } else {
  524. pb = NULL;
  525. }
  526. }
  527. return(pb);
  528. }
  529. UINT
  530. ProcessCheckFile(
  531. LPINT pcfiles
  532. )
  533. {
  534. HANDLE hFind;
  535. DWORD FindSize;
  536. UINT cbFindPattern;
  537. LPSTR FindPattern;
  538. LPSTR pszRelPath;
  539. LPSTR pszFile;
  540. struct checkpath_s *pcp;
  541. struct component_s *pcm;
  542. CHAR FindName[MAX_PATH];
  543. INT i;
  544. *pcfiles = 0;
  545. if (!OpenCheckFile()) {
  546. return(1);
  547. }
  548. cbFindPattern = MAX_PATH + strlen(".") + 1;
  549. FindPattern = malloc(cbFindPattern + 1);
  550. if (FindPattern == NULL) {
  551. crerror("Process: memory allocation (%d bytes) failed",
  552. cbFindPattern + 1);
  553. return(1);
  554. }
  555. //
  556. // Set address of relative path.
  557. //
  558. pszRelPath = &FindPattern[strlen(".") + 1];
  559. i = 0;
  560. for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
  561. i = (i & ~31) + 32;
  562. //
  563. // Build the initial find pattern.
  564. //
  565. sprintf(FindPattern,
  566. "%s\\%s%s",
  567. ".",
  568. pcp->achDir,
  569. *pcp->achDir ? "\\" : "");
  570. CHECKSTRLEN(FindPattern, cbFindPattern);
  571. //
  572. // Point past directory in find pattern.
  573. //
  574. pszFile = &FindPattern[strlen(FindPattern)];
  575. for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
  576. i++;
  577. if (pcm->fDir) {
  578. continue; // process only file patterns
  579. }
  580. if (!fAll && *pcm->achPat == '\0') {
  581. continue; // skip entry if no search pattern
  582. }
  583. // Complete FindPattern: "c:\nt\system32\*.exe"
  584. if (fAll)
  585. strcpy(pszFile, "*.*");
  586. else if (pcm->achPat)
  587. strcpy(pszFile, pcm->achPat);
  588. else
  589. *pcm->achPat = '\0';
  590. CHECKSTRLEN(FindPattern, cbFindPattern);
  591. hFind = PortFindFirstFile(FindPattern, TRUE, FindName, &FindSize);
  592. if (hFind == INVALID_HANDLE_VALUE) {
  593. if (pcm->fRequired) {
  594. crerror("Missing files: %s", pszRelPath);
  595. }
  596. } else {
  597. do {
  598. // append file name to FindPattern: "c:\nt\driver\foo.sys"
  599. _strlwr(FindName);
  600. strcpy(pszFile, FindName);
  601. CHECKSTRLEN(FindPattern, cbFindPattern);
  602. if (fAll && strcmp(FindPattern, pszCheckFileName) == 0) {
  603. continue;
  604. }
  605. *pcfiles += 1;
  606. if (!ProcessEntry(FindPattern,
  607. pszRelPath)) {
  608. crerror("ProcessEntry failed");
  609. return(1);
  610. }
  611. } while (PortFindNextFile(hFind, TRUE, FindName, &FindSize));
  612. PortFindClose(hFind);
  613. }
  614. // if ignoring the supplied extensions, skip redundant patterns
  615. if (fAll) {
  616. break;
  617. }
  618. }
  619. strcpy(pszFile, "*.*"); // search for all directories
  620. CHECKSTRLEN(FindPattern, cbFindPattern);
  621. for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
  622. if (pcm->fDir) { // process only directories
  623. pcm->fFound = FALSE;
  624. }
  625. }
  626. hFind = PortFindFirstFile(FindPattern, FALSE, FindName, &FindSize);
  627. *pszFile = '\0';
  628. if (hFind != INVALID_HANDLE_VALUE) {
  629. do {
  630. if (strcmp(FindName, ".") == 0 ||
  631. strcmp(FindName, "..") == 0) {
  632. continue;
  633. }
  634. _strlwr(FindName);
  635. for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
  636. if (pcm->fDir && strcmp(FindName, pcm->achPat) == 0) {
  637. pcm->fFound = TRUE;
  638. break;
  639. }
  640. }
  641. if (pcm == NULL && fPrintExtra) {
  642. crerror("Extra directory: %s%s", pszRelPath, FindName);
  643. }
  644. } while (PortFindNextFile(hFind, FALSE, FindName, &FindSize));
  645. PortFindClose(hFind);
  646. }
  647. for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
  648. if (pcm->fDir && !pcm->fFound && fPrintMissing) {
  649. crerror("Missing directory: %s%s", pszRelPath, pcm->achPat);
  650. }
  651. }
  652. }
  653. if (!fGenerateCheck && MissingEntries()) {
  654. return(1);
  655. }
  656. if (fInProgress) {
  657. printf("\n");
  658. fInProgress = FALSE;
  659. }
  660. return(0);
  661. }
  662. BOOL
  663. OpenCheckFile(
  664. VOID
  665. )
  666. {
  667. UINT cbCheckName;
  668. // If the check file name wasn't given, then construct it.
  669. if (pszCheckFileName == NULL) {
  670. cbCheckName = strlen(".") + 1 + strlen(CHECK_NAME);
  671. pszCheckFileName = malloc(cbCheckName + 1);
  672. if (pszCheckFileName == NULL) {
  673. crerror("Open: Out of memory (%d bytes)", cbCheckName + 1);
  674. exit(2);
  675. }
  676. sprintf(pszCheckFileName, "%s\\%s", ".", CHECK_NAME);
  677. }
  678. if (fRecurse) {
  679. RecursiveCheckHeader();
  680. } else if (fGenerateCheck) {
  681. ReadCheckHeader(NULL);
  682. }
  683. pfCheck = fopen(pszCheckFileName, fGenerateCheck||fRecurse? "wt" : "rt");
  684. if (pfCheck == NULL) {
  685. crerror("Cannot open %s (%d)", pszCheckFileName, errno);
  686. return(FALSE);
  687. }
  688. if (fGenerateCheck) {
  689. WriteCheckHeader(pfCheck);
  690. } else {
  691. ReadCheckHeader(pfCheck);
  692. if (fCdCheck) {
  693. CdCheck();
  694. return FALSE;
  695. }
  696. }
  697. return(TRUE);
  698. }
  699. VOID
  700. ReadCheckHeader(
  701. FILE *pf
  702. )
  703. {
  704. DWORD cb;
  705. UINT cbread, cbactual;
  706. LPSTR pb;
  707. if (pf == NULL) {
  708. cbCheck = strlen(pszDefaultDir) + 1;
  709. pszCheck = pszDefaultDir;
  710. } else {
  711. cbCheck = _filelength(_fileno(pfCheck)) + 1;
  712. if ((DWORD) (size_t) cbCheck != cbCheck) {
  713. crerror("Open: check file too large (%ld bytes)", cbCheck);
  714. exit(2);
  715. }
  716. pszCheck = malloc((size_t) cbCheck);
  717. if (pszCheck == NULL) {
  718. crerror("Open: memory allocation (%ld bytes) failed", cbCheck);
  719. exit(2);
  720. }
  721. pb = pszCheck;
  722. cb = cbCheck - 1;
  723. while (cb) {
  724. cbread = (cb >= READ_BUFFER_SIZE)? READ_BUFFER_SIZE : (UINT) cb;
  725. cbactual = fread(pb, 1, cbread, pfCheck);
  726. if (cbread > cbactual) {
  727. cb -= cbread - cbactual;
  728. cbCheck -= cbread - cbactual;
  729. }
  730. pb += cbactual;
  731. cb -= cbactual;
  732. }
  733. *pb = '\0';
  734. }
  735. while ((pb = iscomment(pszCheck)) != NULL ||
  736. (pb = ProcessArgBuf(pszCheck)) != NULL) {
  737. pszCheck = pb; // skip comment or parm line
  738. }
  739. if ((pb = ReadDirectory(pszCheck)) != NULL) {
  740. pszCheck = pb; // skip directory lines
  741. } else if (ReadDirectory(pszDefaultDir) == NULL) {
  742. crerror("Bad internal data structure directory format");
  743. exit(1);
  744. }
  745. }
  746. LPSTR
  747. ReadDirectory(
  748. LPSTR pszdir
  749. )
  750. {
  751. LPSTR pb;
  752. if ((pb = ismatch(pszdir, "#directorystart")) == NULL) {
  753. return(NULL);
  754. }
  755. pszdir = pb; // skip "start" line
  756. while ((pb = ismatch(pszdir, "#directoryend")) == NULL) {
  757. if ((pb = iscomment(pszdir)) == NULL &&
  758. (pb = AddDirectory(pszdir)) == NULL) {
  759. return(NULL);
  760. }
  761. pszdir = pb;
  762. }
  763. return(pb);
  764. }
  765. LPSTR
  766. iscomment(
  767. LPSTR psz
  768. )
  769. {
  770. while (*psz == ' ' || *psz == '\t') {
  771. psz++;
  772. }
  773. if (*psz == '\n' || *psz == '/' && psz[1] == '/') {
  774. psz += strcspn(psz, "\n");
  775. if (*psz == '\n') {
  776. psz++;
  777. }
  778. return(psz); // return start of next line
  779. }
  780. return(NULL); // not a comment
  781. }
  782. LPSTR
  783. ismatch(
  784. LPSTR psz,
  785. LPSTR pszcompact
  786. )
  787. {
  788. while (*psz) {
  789. if (*psz == ' ' || *psz == '\t') {
  790. psz++;
  791. continue;
  792. }
  793. if (*psz != *pszcompact) {
  794. break;
  795. }
  796. psz++;
  797. pszcompact++;
  798. }
  799. if (*psz != '\n' || *pszcompact != '\0') {
  800. return(NULL);
  801. }
  802. return(psz + 1);
  803. }
  804. LPSTR
  805. AddDirectory(
  806. LPSTR psz
  807. )
  808. {
  809. LPSTR pb;
  810. BOOL frequired;
  811. INT i, ch;
  812. if ((pb = strchr(psz, '\n')) == NULL) {
  813. crerror("Directory data error");
  814. return(NULL);
  815. }
  816. while (*psz == ' ' || *psz == '\t') {
  817. psz++;
  818. }
  819. frequired = TRUE;
  820. i = strcspn(psz, " \t\n");
  821. ch = psz[i];
  822. psz[i] = '\0';
  823. if (ch != '\n') {
  824. frequired = !ismatch(psz + i + 1, "optional");
  825. }
  826. if (!AddEntry(psz, frequired)) {
  827. psz[i] = (char)ch;
  828. return(NULL);
  829. }
  830. return(pb + 1);
  831. }
  832. BOOL
  833. AddEntry(LPSTR psz,
  834. BOOL frequired
  835. )
  836. {
  837. BOOL f, fdir, freq1;
  838. INT i;
  839. CHAR chsep;
  840. CHAR achdir[MAX_PATH];
  841. CHAR FullPath[MAX_PATH];
  842. //
  843. // If the leading character is ?, then prepend the name of the NT tree
  844. // to the directory name.
  845. //
  846. if (*psz == '?') {
  847. strcpy(&FullPath[0], RootOfTree);
  848. strcat(&FullPath[0], psz + 1);
  849. psz = &FullPath[0];
  850. }
  851. achdir[0] = '\0';
  852. do {
  853. i = strcspn(psz, "\\");
  854. chsep = psz[i];
  855. psz[i] = '\0';
  856. fdir = freq1 = TRUE;
  857. if (chsep == '\0' || psz[i + 1] == '\0') {
  858. if (chsep == '\0') {
  859. fdir = FALSE; // at end & no trailing pathsep
  860. }
  861. freq1 = frequired; // at end.
  862. }
  863. f = AddComponent(achdir, psz, fdir, freq1);
  864. if (achdir[0] != '\0') {
  865. strcat(achdir, "\\");
  866. }
  867. strcat(achdir, psz);
  868. psz[i] = chsep;
  869. if (!f) {
  870. return(FALSE);
  871. }
  872. psz += i + 1;
  873. } while(chsep != '\0' && *psz != '\0');
  874. return(TRUE);
  875. }
  876. //struct component_s {
  877. // struct component_s *pcmNext; // next in linked list
  878. // BOOL fDir; // TRUE if directory
  879. // BOOL fRequired; // TRUE if must exist
  880. // CHAR achPat[1]; // path component (subdir or pattern)
  881. //};
  882. //
  883. //struct checkpath_s {
  884. // struct checkpath_s *pcpNext; // next in linked list
  885. // struct component_s *pcmPat; // subdirectories and file patterns
  886. // CHAR achDir[1]; // root relative directory path
  887. //};
  888. BOOL
  889. AddComponent(
  890. LPSTR pszdir,
  891. LPSTR pszpat,
  892. BOOL fdir,
  893. BOOL frequired
  894. )
  895. {
  896. struct checkpath_s *pcp;
  897. struct checkpath_s *pcplast;
  898. struct component_s *pcm;
  899. struct component_s *pcmlast;
  900. INT r;
  901. INT t = 0;
  902. pcplast = NULL;
  903. for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
  904. pcplast = pcp;
  905. if ((r = strcmp(pszdir, pcp->achDir)) <= 0) {
  906. break;
  907. }
  908. }
  909. if (pcp == NULL || r) {
  910. pcp = malloc(sizeof(*pcp) + strlen(pszdir));
  911. if (pcp == NULL) {
  912. crerror("AddComponent: out of memory");
  913. exit(2);
  914. }
  915. if (pcplast == NULL) {
  916. t |= 1;
  917. pcp->pcpNext = NULL;
  918. pcpPaths = pcp;
  919. } else {
  920. t |= 2;
  921. pcp->pcpNext = pcplast->pcpNext;
  922. pcplast->pcpNext = pcp;
  923. }
  924. pcp->pcmPat = NULL;
  925. strcpy(pcp->achDir, pszdir);
  926. }
  927. pcmlast = NULL;
  928. if (pszpat != NULL) {
  929. for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
  930. pcmlast = pcm;
  931. if ((r = strcmp(pszpat, pcm->achPat)) <= 0) {
  932. break;
  933. }
  934. }
  935. }
  936. if (pcm == NULL || r) {
  937. if (pszpat != NULL)
  938. pcm = malloc(sizeof(*pcm) + strlen(pszpat));
  939. else
  940. pcm = malloc(sizeof(*pcm));
  941. if (pcm == NULL) {
  942. crerror("AddComponent: out of memory");
  943. exit(2);
  944. }
  945. if (pcmlast == NULL) {
  946. t |= 4;
  947. pcm->pcmNext = NULL;
  948. pcp->pcmPat = pcm;
  949. } else {
  950. t |= 8;
  951. pcm->pcmNext = pcmlast->pcmNext;
  952. pcmlast->pcmNext = pcm;
  953. }
  954. pcm->fDir = fdir;
  955. pcm->fFound = FALSE;
  956. pcm->fRequired = frequired;
  957. if (pszpat == NULL)
  958. *pcm->achPat = '\000';
  959. else
  960. strcpy(pcm->achPat, pszpat);
  961. }
  962. if (!frequired) {
  963. pcm->fRequired = frequired;
  964. }
  965. return(TRUE);
  966. }
  967. VOID
  968. WriteCheckHeader(FILE *pf)
  969. {
  970. struct checkpath_s *pcp;
  971. struct component_s *pcm;
  972. INT ccol;
  973. CHAR achpath[MAX_PATH];
  974. CHAR *psz;
  975. CHAR SavedChar;
  976. if (fAll) {
  977. fprintf(pf,
  978. "-%s\n\n",
  979. fAll? "a" : "");
  980. }
  981. fprintf(pf, "#directory start\n");
  982. for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
  983. for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
  984. sprintf(achpath,
  985. "%s%s%s%s",
  986. pcp->achDir,
  987. *pcp->achDir? "\\" : "",
  988. pcm->achPat,
  989. pcm->fDir? "\\" : "");
  990. psz = strchr(achpath, '\\');
  991. if (psz == NULL) {
  992. fprintf(pf, achpath);
  993. } else {
  994. psz -= 1;
  995. SavedChar = *psz;
  996. *psz = '?';
  997. fprintf(pf, psz);
  998. *psz = SavedChar;
  999. }
  1000. if (!pcm->fRequired) {
  1001. ccol = strlen(achpath);
  1002. fprintf(pf, " optional");
  1003. }
  1004. fprintf(pf, "\n");
  1005. }
  1006. }
  1007. fprintf(pf, "#directory end\n\n");
  1008. }
  1009. BOOL
  1010. ProcessEntry(
  1011. LPSTR pszFullPath,
  1012. LPSTR pszRelPath
  1013. )
  1014. {
  1015. ULONG CheckSum;
  1016. ULONG HeaderSum;
  1017. ULONG FileSum;
  1018. FILE *pf = NULL;
  1019. UINT cbLine;
  1020. CHAR *psz;
  1021. ULONG Status;
  1022. if (!fGenerateCheck) {
  1023. if (!FindEntry(pszRelPath, &FileSum)) {
  1024. if (fPrintExtra) {
  1025. crerror("Extra file: %s", pszRelPath);
  1026. }
  1027. return TRUE;
  1028. }
  1029. }
  1030. //
  1031. // Compute checksum of file.
  1032. //
  1033. Status = MapFileAndCheckSum(pszFullPath, &HeaderSum, &CheckSum);
  1034. if (Status != CHECKSUM_SUCCESS) {
  1035. crerror("Cannot open or map file %s", pszFullPath);
  1036. return TRUE;
  1037. }
  1038. if (fGenerateCheck) {
  1039. cbLine = sprintf(OutputLine,
  1040. "%s %lx\n",
  1041. pszRelPath,
  1042. CheckSum);
  1043. CHECKSTRLEN(OutputLine, sizeof(OutputLine));
  1044. psz = strchr(OutputLine, '\\');
  1045. if (fCommand || psz == NULL) {
  1046. fwrite(OutputLine, 1, cbLine, pfCheck);
  1047. } else {
  1048. psz -= 1;
  1049. *psz = '?';
  1050. fwrite(psz, 1, (size_t)(cbLine - (psz - OutputLine)), pfCheck);
  1051. }
  1052. }
  1053. if (!fGenerateCheck) {
  1054. if (CheckSum != FileSum) {
  1055. crerror("Sum differs (actual %lx, expected %lx): %s",
  1056. CheckSum,
  1057. FileSum,
  1058. pszRelPath);
  1059. }
  1060. }
  1061. return TRUE;
  1062. }
  1063. BOOL
  1064. FindEntry(
  1065. LPSTR pszRelPath,
  1066. PULONG FileSum
  1067. )
  1068. {
  1069. PCHECK_FILE_ENTRY LastEntry;
  1070. WORD Length;
  1071. PCHECK_FILE_ENTRY NextEntry;
  1072. //
  1073. // If this is the first trip through this code, then reset to the
  1074. // beginning of the check file.
  1075. //
  1076. if (CheckEntryRoot.Next == NULL) {
  1077. if (ParseCheckFile() == 0) {
  1078. return FALSE;
  1079. }
  1080. }
  1081. //
  1082. // Compute the length of the specified file name and loop through
  1083. // check file list for a matching entry.
  1084. //
  1085. Length = (WORD)strlen(pszRelPath);
  1086. LastEntry = &CheckEntryRoot;
  1087. NextEntry = LastEntry->Next;
  1088. do {
  1089. //
  1090. // If the length and the file name match, then remove the entry from
  1091. // the list and return the file size and check sum value.
  1092. //
  1093. if (NextEntry->Length == Length) {
  1094. if (strncmp(pszRelPath, NextEntry->Name, Length) == 0) {
  1095. LastEntry->Next = NextEntry->Next;
  1096. *FileSum = NextEntry->Sum;
  1097. return TRUE;
  1098. }
  1099. }
  1100. LastEntry = NextEntry;
  1101. NextEntry = NextEntry->Next;
  1102. } while (NextEntry != NULL);
  1103. //
  1104. // The specified file is not in the check file.
  1105. //
  1106. return FALSE;
  1107. }
  1108. DWORD
  1109. MissingEntries(
  1110. VOID
  1111. )
  1112. {
  1113. DWORD Count = 0;
  1114. PCHECK_FILE_ENTRY NextEntry;
  1115. //
  1116. // Scan through the check file list and display an error message for
  1117. // each missing file.
  1118. //
  1119. if (fPrintMissing) {
  1120. NextEntry = CheckEntryRoot.Next;
  1121. while (NextEntry != NULL) {
  1122. crerror("Missing file: %s", NextEntry->Name);
  1123. Count += 1;
  1124. NextEntry = NextEntry->Next;
  1125. }
  1126. }
  1127. return Count;
  1128. }
  1129. DWORD
  1130. ParseCheckFile(
  1131. VOID
  1132. )
  1133. {
  1134. DWORD Count = 0;
  1135. LPSTR pszline;
  1136. LPSTR psz;
  1137. PCHECK_FILE_ENTRY LastEntry;
  1138. WORD Length;
  1139. PCHECK_FILE_ENTRY NextEntry;
  1140. WORD SizeOfRoot;
  1141. DWORD Sum;
  1142. //
  1143. // If the check file contains no entries, then return.
  1144. //
  1145. if (*pszCheck != '\n') {
  1146. return Count;
  1147. }
  1148. //
  1149. // Scan through the check file and parse each file name, checksum, and
  1150. // size field.
  1151. //
  1152. SizeOfRoot = (WORD)strlen(RootOfTree);
  1153. LastEntry = &CheckEntryRoot;
  1154. for (pszline = pszCheck; pszline != NULL; pszline = strchr(pszline, '\n')) {
  1155. //
  1156. // Skip over the new line and search for the blank separator between
  1157. // the file name and the checksum.
  1158. //
  1159. pszline += 1;
  1160. psz = strchr(pszline, ' ');
  1161. //
  1162. // If there is no blank separator, then the end of the check file has
  1163. // been reached.
  1164. //
  1165. if (psz == NULL) {
  1166. return Count;
  1167. }
  1168. //
  1169. // Compute the length and checksum of the file entry.
  1170. //
  1171. Length = (short)(psz - pszline);
  1172. psz = atolx(psz + 1, &Sum);
  1173. //
  1174. // Allocate a check file entry for the specified file and insert it
  1175. // at the end of the check file entry list.
  1176. //
  1177. Count += 1;
  1178. if (Count > CHECK_ENTRY_TABLE_SIZE) {
  1179. crerror("Checkrel: Check Entry Table Overflow");
  1180. return 0;
  1181. }
  1182. NextEntry = &CheckEntryTable[Count - 1];
  1183. NextEntry->Next = NULL;
  1184. NextEntry->Sum = Sum;
  1185. //
  1186. // Form the file name from the NT root name and the specified path.
  1187. //
  1188. pszline[Length] = '\0';
  1189. if (*pszline == '?') {
  1190. pszline += 1;
  1191. NextEntry->Name = (CHAR *)malloc(SizeOfRoot + Length);
  1192. if (NextEntry->Name == NULL) {
  1193. crerror("Checkrel: failure to allocate check file entry");
  1194. return Count;
  1195. }
  1196. strcpy(NextEntry->Name, RootOfTree);
  1197. strcat(NextEntry->Name, pszline);
  1198. Length += SizeOfRoot - 1;
  1199. } else {
  1200. NextEntry->Name = pszline;
  1201. }
  1202. NextEntry->Length = Length;
  1203. LastEntry->Next = NextEntry;
  1204. LastEntry = NextEntry;
  1205. pszline = psz;
  1206. }
  1207. return Count;
  1208. }
  1209. LPSTR
  1210. atolx(
  1211. LPSTR psz,
  1212. LPDWORD pul)
  1213. {
  1214. DWORD ul;
  1215. char ch;
  1216. ul = 0;
  1217. while (isxdigit(*psz)) {
  1218. ch = *psz++;
  1219. if (isdigit(ch)) {
  1220. ch += 0 - '0';
  1221. } else if (islower(ch)) {
  1222. ch += 10 - 'a';
  1223. } else {
  1224. ch += 10 - 'A';
  1225. }
  1226. ul = (ul << 4) + ch;
  1227. }
  1228. *pul = ul;
  1229. return(psz);
  1230. }
  1231. VOID
  1232. RecursiveCheckHeader()
  1233. {
  1234. HANDLE hFind;
  1235. DWORD FindSize;
  1236. UINT cbFindPattern;
  1237. LPSTR FindPattern;
  1238. LPSTR pszRelPath;
  1239. LPSTR pszFile;
  1240. struct checkpath_s *pcp;
  1241. struct component_s *pcm;
  1242. CHAR FindName[MAX_PATH];
  1243. INT i;
  1244. cbFindPattern = strlen(".") + MAX_PATH;
  1245. FindPattern = malloc(cbFindPattern + 1);
  1246. if (FindPattern == NULL) {
  1247. crerror("Process: memory allocation (%d bytes) failed",
  1248. cbFindPattern + 1);
  1249. return;
  1250. }
  1251. // Set relative path pointer into FindPattern: "driver\elnkii.sys"
  1252. pszRelPath = &FindPattern[strlen(RootOfTree) + 1];
  1253. AddComponent(".", NULL, TRUE, TRUE);
  1254. i = 0;
  1255. for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
  1256. i = (i & ~31) + 32;
  1257. // Build Initial FindPattern directory path: "c:\nt\"
  1258. sprintf(FindPattern,
  1259. "%s\\%s%s",
  1260. ".",
  1261. pcp->achDir,
  1262. *pcp->achDir? "\\" : "");
  1263. CHECKSTRLEN(FindPattern, cbFindPattern);
  1264. // point past directory in FindPattern: "c:\nt\system32\"
  1265. pszFile = &FindPattern[strlen(FindPattern)];
  1266. strcpy(pszFile, "*.*"); // search for all directories
  1267. CHECKSTRLEN(FindPattern, cbFindPattern);
  1268. hFind = PortFindFirstFile(FindPattern, FALSE, FindName, &FindSize);
  1269. *pszFile = '\0';
  1270. if (hFind != INVALID_HANDLE_VALUE) {
  1271. do {
  1272. if (strcmp(FindName, ".") == 0 ||
  1273. strcmp(FindName, "..") == 0) {
  1274. continue;
  1275. }
  1276. _strlwr(FindName);
  1277. for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
  1278. if (pcm->fDir && strcmp(FindName, pcm->achPat) == 0) {
  1279. pcm->fFound = TRUE;
  1280. break;
  1281. }
  1282. }
  1283. if (pcm == NULL) {
  1284. AddComponent(FindName, NULL, TRUE, TRUE);
  1285. }
  1286. } while (PortFindNextFile(hFind, FALSE, FindName, &FindSize));
  1287. PortFindClose(hFind);
  1288. }
  1289. }
  1290. if (fInProgress) {
  1291. printf("\n");
  1292. fInProgress = FALSE;
  1293. }
  1294. return;
  1295. }
  1296. #define ATTRMATCH(fnormal, attr) \
  1297. (!fNormal ^ ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0))
  1298. HANDLE
  1299. PortFindFirstFile(LPSTR FindPattern,
  1300. BOOL fNormal,
  1301. LPSTR FindName,
  1302. LPDWORD FindSize)
  1303. {
  1304. HANDLE hFind;
  1305. WIN32_FIND_DATA wfd;
  1306. hFind = FindFirstFile(FindPattern, &wfd);
  1307. if (hFind != INVALID_HANDLE_VALUE) {
  1308. if (!ATTRMATCH(fNormal, wfd.dwFileAttributes)) {
  1309. if (!PortFindNextFile(hFind,
  1310. fNormal,
  1311. FindName,
  1312. FindSize)) {
  1313. FindClose(hFind);
  1314. return(INVALID_HANDLE_VALUE);
  1315. }
  1316. } else {
  1317. strcpy(FindName, wfd.cFileName);
  1318. *FindSize = wfd.nFileSizeLow;
  1319. }
  1320. }
  1321. return(hFind);
  1322. }
  1323. BOOL
  1324. PortFindNextFile(HANDLE hFind,
  1325. BOOL fNormal,
  1326. LPSTR FindName,
  1327. LPDWORD FindSize)
  1328. {
  1329. BOOL b;
  1330. WIN32_FIND_DATA wfd;
  1331. do {
  1332. b = FindNextFile(hFind, &wfd);
  1333. } while (b && !ATTRMATCH(fNormal, wfd.dwFileAttributes));
  1334. if (b) {
  1335. strcpy(FindName, wfd.cFileName);
  1336. *FindSize = wfd.nFileSizeLow;
  1337. }
  1338. return(b);
  1339. }
  1340. VOID
  1341. PortFindClose(HANDLE hFind)
  1342. {
  1343. FindClose(hFind);
  1344. }