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.

1845 lines
47 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. For Internal use only!
  4. Module Name:
  5. INFSCAN
  6. globalscan.cpp
  7. Abstract:
  8. Global scanner class
  9. entry points GlobalScan::ParseArgs
  10. and GlobalScan::Scan
  11. are called from main()
  12. History:
  13. Created July 2001 - JamieHun
  14. --*/
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. GlobalScan::GlobalScan()
  18. /*++
  19. Routine Description:
  20. Initialize class variables
  21. --*/
  22. {
  23. InfFilter = INVALID_HANDLE_VALUE;
  24. ThreadCount = 0;
  25. GeneratePnfs = false;
  26. GeneratePnfsOnly = false;
  27. Pedantic = false;
  28. Trace = false;
  29. IgnoreErrors = false;
  30. TargetsToo = false;
  31. DetermineCopySections = false;
  32. LimitedSourceDisksFiles = false;
  33. BuildChangedDevices = BUILD_CHANGED_DEVICES_DISABLED;
  34. SourceFileList = INVALID_HANDLE_VALUE;
  35. NewFilter = INVALID_HANDLE_VALUE;
  36. DeviceFilterList = INVALID_HANDLE_VALUE;
  37. NextJob = Jobs.end();
  38. SpecifiedNames = false;
  39. }
  40. GlobalScan::~GlobalScan()
  41. /*++
  42. Routine Description:
  43. Release any allocated data/files
  44. --*/
  45. {
  46. if(InfFilter != INVALID_HANDLE_VALUE) {
  47. SetupCloseInfFile(InfFilter);
  48. }
  49. if(SourceFileList != INVALID_HANDLE_VALUE) {
  50. CloseHandle(SourceFileList);
  51. }
  52. if(NewFilter != INVALID_HANDLE_VALUE) {
  53. CloseHandle(NewFilter);
  54. }
  55. if(DeviceFilterList != INVALID_HANDLE_VALUE) {
  56. CloseHandle(DeviceFilterList);
  57. }
  58. }
  59. int
  60. GlobalScan::ParseVersion(LPCSTR ver)
  61. /*++
  62. Routine Description:
  63. Parse a version string into constituent parts
  64. Arguments:
  65. ver - version string as passed into argv[]
  66. Return Value:
  67. 0 on success
  68. --*/
  69. {
  70. //
  71. // <plat>.<maj>.<min>.<typ>.<suite>
  72. //
  73. PTSTR cpy = CopyString(ver);
  74. int res = Version.Parse(cpy);
  75. delete [] cpy;
  76. if(res == 4) {
  77. Usage();
  78. return 2;
  79. }
  80. return res;
  81. }
  82. int
  83. GlobalScan::ParseArgs(int argc,char *argv[])
  84. /*++
  85. Routine Description:
  86. Parse command line parameters
  87. Arguments:
  88. argc/argv as passed into main
  89. Return Value:
  90. 0 on success
  91. --*/
  92. {
  93. int i;
  94. int res;
  95. SafeString arg;
  96. for(i = 1; i < argc; i++) {
  97. if((argv[i][0] != TEXT('/')) && (argv[i][0] != TEXT('-'))) {
  98. break;
  99. }
  100. if(!argv[i][1] || (argv[i][2] && !isdigit(argv[i][2]))) {
  101. Usage();
  102. return 2;
  103. }
  104. switch(*(argv[i]+1)) {
  105. case 'B' :
  106. case 'b' :
  107. //
  108. // Take supplied textfile as list of "unchanged" build files
  109. // report a list of devices that use (copy) files that are not
  110. // part of this unchanged list
  111. //
  112. // SP build special
  113. // use in conjunction with /E
  114. //
  115. BuildChangedDevices = (DWORD)strtoul(argv[i]+2,NULL,0);
  116. if(!BuildChangedDevices) {
  117. BuildChangedDevices = BUILD_CHANGED_DEVICES_DEFAULT;
  118. }
  119. BuildChangedDevices |= BUILD_CHANGED_DEVICES_ENABLED;
  120. i++;
  121. if(i == argc) {
  122. Usage();
  123. return 2;
  124. } else {
  125. StringList list;
  126. res = LoadListFromFile(SafeStringA(argv[i]),list);
  127. if(res != 0) {
  128. return res;
  129. }
  130. StringList::iterator li;
  131. for(li = list.begin(); li != list.end(); li++) {
  132. BuildUnchangedFiles.insert(*li);
  133. }
  134. }
  135. break;
  136. case 'C' :
  137. case 'c' :
  138. //
  139. // Create Filter INF specified in the next argument
  140. //
  141. i++;
  142. if(i == argc) {
  143. Usage();
  144. return 2;
  145. }
  146. if(NewFilter == INVALID_HANDLE_VALUE) {
  147. NewFilter = CreateFileA(argv[i],
  148. GENERIC_WRITE,
  149. 0,
  150. NULL,
  151. CREATE_ALWAYS,
  152. FILE_ATTRIBUTE_NORMAL,
  153. NULL);
  154. if(NewFilter == INVALID_HANDLE_VALUE) {
  155. fprintf(stderr,"#*** Cannot open file \"%s\" for writing\n",argv[i]);
  156. return 3;
  157. }
  158. Write(NewFilter,WRITE_INF_HEADER);
  159. }
  160. break;
  161. case 'D' :
  162. case 'd' :
  163. //
  164. // Determine other copy sections
  165. //
  166. DetermineCopySections = true;
  167. break;
  168. case 'E' :
  169. case 'e' :
  170. //
  171. // Create a list of device = inf
  172. //
  173. i++;
  174. if(i == argc) {
  175. Usage();
  176. return 2;
  177. }
  178. if(DeviceFilterList == INVALID_HANDLE_VALUE) {
  179. DeviceFilterList = CreateFileA(argv[i],
  180. GENERIC_WRITE,
  181. 0,
  182. NULL,
  183. CREATE_ALWAYS,
  184. FILE_ATTRIBUTE_NORMAL,
  185. NULL);
  186. if(DeviceFilterList == INVALID_HANDLE_VALUE) {
  187. fprintf(stderr,"#*** Cannot open file \"%s\" for writing\n",argv[i]);
  188. return 3;
  189. }
  190. }
  191. break;
  192. case 'F' :
  193. case 'f' :
  194. //
  195. // Filter the list based on the INF in the next argument
  196. //
  197. i++;
  198. if(i == argc) {
  199. Usage();
  200. return 2;
  201. }
  202. FilterPath = ConvertString(argv[i]);
  203. break;
  204. case 'G':
  205. case 'g':
  206. //
  207. // generate PNF's (see also Z)
  208. //
  209. GeneratePnfs = true;
  210. break;
  211. case 'H' :
  212. case 'h' :
  213. case '?' :
  214. //
  215. // Display usage help
  216. //
  217. Usage();
  218. return 1;
  219. case 'I' :
  220. case 'i' :
  221. //
  222. // ignore
  223. //
  224. IgnoreErrors = true;
  225. break;
  226. case 'N' :
  227. case 'n' :
  228. //
  229. // named file
  230. //
  231. i++;
  232. if(i == argc) {
  233. Usage();
  234. return 2;
  235. }
  236. SpecifiedNames = true;
  237. _strlwr(argv[i]);
  238. arg = ConvertString(argv[i]);
  239. if(ExcludeInfs.find(arg) == ExcludeInfs.end()) {
  240. NamedInfList.push_back(arg);
  241. //
  242. // make sure it appears only once
  243. //
  244. ExcludeInfs.insert(arg);
  245. }
  246. break;
  247. case 'O' :
  248. case 'o' :
  249. //
  250. // override path (if an INF is in this (relative) location,
  251. // it's used instead
  252. // multiple overrides can be given
  253. // "/O ." is always assumed to be last unless explicitly given
  254. //
  255. i++;
  256. if(i == argc) {
  257. Usage();
  258. return 2;
  259. }
  260. arg = ConvertString(argv[i]);
  261. Overrides.push_back(arg);
  262. break;
  263. case 'P' :
  264. case 'p' :
  265. //
  266. // pedantic mode - INF's must match expectations in filter
  267. //
  268. Pedantic = true;
  269. break;
  270. case 'Q' :
  271. case 'q' :
  272. //
  273. // output source+target files (used in conjunction with /S
  274. //
  275. i++;
  276. if(i == argc) {
  277. Usage();
  278. return 2;
  279. }
  280. if(SourceFileList == INVALID_HANDLE_VALUE) {
  281. TargetsToo = true;
  282. SourceFileList = CreateFileA(argv[i],
  283. GENERIC_WRITE,
  284. 0,
  285. NULL,
  286. CREATE_ALWAYS,
  287. FILE_ATTRIBUTE_NORMAL,
  288. NULL);
  289. if(SourceFileList == INVALID_HANDLE_VALUE) {
  290. fprintf(stderr,"#*** Cannot open file \"%s\" for writing\n",argv[i]);
  291. return 3;
  292. }
  293. }
  294. break;
  295. case 'R' :
  296. case 'r' :
  297. //
  298. // trace
  299. //
  300. Trace = true;
  301. break;
  302. case 'S' :
  303. case 's' :
  304. //
  305. // output source file
  306. //
  307. i++;
  308. if(i == argc) {
  309. Usage();
  310. return 2;
  311. }
  312. if(SourceFileList == INVALID_HANDLE_VALUE) {
  313. SourceFileList = CreateFileA(argv[i],
  314. GENERIC_WRITE,
  315. 0,
  316. NULL,
  317. CREATE_ALWAYS,
  318. FILE_ATTRIBUTE_NORMAL,
  319. NULL);
  320. if(SourceFileList == INVALID_HANDLE_VALUE) {
  321. fprintf(stderr,"#*** Cannot open file \"%s\" for writing\n",argv[i]);
  322. return 3;
  323. }
  324. }
  325. break;
  326. case 'T':
  327. case 't':
  328. //
  329. // specify number of threads
  330. //
  331. i++;
  332. if(i == argc) {
  333. Usage();
  334. return 2;
  335. }
  336. ThreadCount = atoi(argv[i]);
  337. break;
  338. case 'V' :
  339. case 'v' :
  340. //
  341. // version
  342. //
  343. i++;
  344. if(i == argc) {
  345. Usage();
  346. return 2;
  347. }
  348. res = ParseVersion(argv[i]);
  349. if(res != 0) {
  350. return res;
  351. }
  352. break;
  353. case 'W' :
  354. case 'w' :
  355. //
  356. // include, alternative to 'N'
  357. //
  358. i++;
  359. if(i == argc) {
  360. Usage();
  361. return 2;
  362. } else {
  363. SpecifiedNames = true;
  364. StringList list;
  365. res = LoadListFromFile(SafeStringA(argv[i]),list);
  366. if(res != 0) {
  367. return res;
  368. }
  369. StringList::iterator li;
  370. for(li = list.begin(); li != list.end(); li++) {
  371. if(ExcludeInfs.find(*li) == ExcludeInfs.end()) {
  372. NamedInfList.push_back(*li);
  373. //
  374. // insert only once
  375. // stop a 2nd/subsequent insert
  376. //
  377. ExcludeInfs.insert(*li);
  378. }
  379. }
  380. }
  381. break;
  382. case 'X' :
  383. case 'x' :
  384. //
  385. // exclude, mask out future files added to list
  386. //
  387. i++;
  388. if(i == argc) {
  389. Usage();
  390. return 2;
  391. } else {
  392. StringList list;
  393. res = LoadListFromFile(SafeStringA(argv[i]),list);
  394. if(res != 0) {
  395. return res;
  396. }
  397. StringList::iterator li;
  398. for(li = list.begin(); li != list.end(); li++) {
  399. ExcludeInfs.insert(*li);
  400. }
  401. }
  402. break;
  403. case 'Y':
  404. case 'y':
  405. LimitedSourceDisksFiles = true;
  406. break;
  407. case 'Z':
  408. case 'z':
  409. //
  410. // generate PNF's only
  411. //
  412. GeneratePnfs = true;
  413. GeneratePnfsOnly = true;
  414. break;
  415. default:
  416. //
  417. // Display usage help
  418. //
  419. Usage();
  420. return 2;
  421. }
  422. }
  423. if(i < argc) {
  424. SourcePath = ConvertString(argv[i]);
  425. i++;
  426. }
  427. if(i != argc) {
  428. Usage();
  429. return 2;
  430. }
  431. return 0;
  432. }
  433. int
  434. GlobalScan::Scan()
  435. /*++
  436. Routine Description:
  437. Do the actual scanning
  438. Arguments:
  439. none
  440. Return Value:
  441. 0 on success
  442. --*/
  443. {
  444. int res;
  445. StringList ParseInfList;
  446. StringList LayoutInfList;
  447. if(Trace) {
  448. _ftprintf(stderr,TEXT("#### Obtaining list of INF files\n"));
  449. }
  450. Overrides.push_back(TEXT("."));
  451. if (!SpecifiedNames) {
  452. //
  453. // enumerate all the INF's if none were explicitly mentioned
  454. //
  455. StringList::iterator dir;
  456. for (dir = Overrides.begin(); dir != Overrides.end(); dir++) {
  457. //
  458. // for each directory
  459. //
  460. WIN32_FIND_DATA findData;
  461. HANDLE findHandle;
  462. ZeroMemory(&findData,sizeof(findData));
  463. SafeString mask;
  464. res = ExpandFullPath(*dir,TEXT("*.INF"),mask);
  465. if(res != 0) {
  466. return res;
  467. }
  468. if(Trace) {
  469. _ftprintf(stderr,TEXT("#### Scanning %s\n"),mask.c_str());
  470. }
  471. findHandle = FindFirstFile(mask.c_str(),&findData);
  472. if(findHandle == INVALID_HANDLE_VALUE) {
  473. continue;
  474. }
  475. do {
  476. if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  477. continue;
  478. }
  479. if(findData.nFileSizeHigh != 0) {
  480. continue;
  481. }
  482. if(findData.nFileSizeLow == 0) {
  483. continue;
  484. }
  485. _tcslwr(findData.cFileName);
  486. SafeString name = findData.cFileName; // saves lots of automatics
  487. if(ExcludeInfs.find(name) != ExcludeInfs.end()) {
  488. //
  489. // not allowed to process an INF with this name
  490. // (previously obtained or specifically excluded)
  491. //
  492. continue;
  493. }
  494. //
  495. // make a note of the filename so we effectively override it
  496. //
  497. ExcludeInfs.insert(name);
  498. //
  499. // make the final name
  500. //
  501. SafeString fullname;
  502. res = ExpandFullPath(*dir,name,fullname);
  503. if(res != 0) {
  504. return res;
  505. }
  506. if(name.compare(TEXT("layout.inf")) == 0) {
  507. //
  508. // looks like a (the) layout file
  509. // these are parsed ahead of other INF's
  510. //
  511. LayoutInfList.push_back(fullname);
  512. } else {
  513. ParseInfList.push_back(fullname);
  514. }
  515. } while (FindNextFile(findHandle,&findData));
  516. FindClose(findHandle);
  517. }
  518. } else {
  519. //
  520. // INF's were manually specified, find where they are located
  521. //
  522. StringList::iterator name;
  523. StringList::iterator dir;
  524. for(name = NamedInfList.begin(); name != NamedInfList.end(); name++) {
  525. SafeString fullname;
  526. res = ExpandFullPathWithOverride(*name,fullname);
  527. if(res != 0) {
  528. return res;
  529. }
  530. if(_tcsicmp(GetFileNamePart(name->c_str()),TEXT("layout.inf"))==0) {
  531. //
  532. // looks lie a layout file
  533. // parse ahead of other INF's
  534. //
  535. LayoutInfList.push_back(fullname);
  536. } else {
  537. ParseInfList.push_back(fullname);
  538. }
  539. }
  540. }
  541. if(ParseInfList.empty() && LayoutInfList.empty()) {
  542. _ftprintf(stderr,TEXT("#*** No files\n"));
  543. return 1;
  544. }
  545. if(FilterPath.length() && !GeneratePnfsOnly) {
  546. GetFullPathName(FilterPath,FilterPath);
  547. InfFilter = SetupOpenInfFile(FilterPath.c_str(),NULL,INF_STYLE_WIN4,NULL);
  548. if(InfFilter == INVALID_HANDLE_VALUE) {
  549. _ftprintf(stderr,TEXT("#*** Cannot open filter\n"));
  550. return 3;
  551. }
  552. if(Trace) {
  553. _ftprintf(stderr,TEXT("#### Loading filter %s\n"),FilterPath.c_str());
  554. }
  555. LoadFileDispositions();
  556. LoadOtherCopySections();
  557. }
  558. if((NewFilter == INVALID_HANDLE_VALUE) && !DetermineCopySections) {
  559. FileDisposition & disp = GetGuidDisposition(NULL_GUID);
  560. if(!disp.Filtered) {
  561. //
  562. // default disposition for NULL guid's
  563. //
  564. disp.FilterAction = ACTION_IGNOREINF;
  565. }
  566. }
  567. StringList::iterator i;
  568. if(GeneratePnfsOnly) {
  569. //
  570. // do nothing special with layout files because all we'll be doing
  571. // is generating PNF's
  572. // so merge them into ParseInfList
  573. //
  574. for(i = LayoutInfList.begin(); i != LayoutInfList.end() ; i++) {
  575. ParseInfList.push_back(*i);
  576. }
  577. } else {
  578. if(Trace) {
  579. _ftprintf(stderr,TEXT("#### Scanning Layout.Inf file(s) sequentially\n"));
  580. }
  581. for(i = LayoutInfList.begin(); i != LayoutInfList.end() ; i++) {
  582. SafeString &full_inf = *i;
  583. if(GeneratePnfs) {
  584. GeneratePnf(full_inf);
  585. }
  586. //
  587. // this stuff has to be done sequentially
  588. //
  589. GetFileDisposition(full_inf).FilterAction |= ACTION_EARLYLOAD; // override
  590. InfScan *pInfScan = new InfScan(this,full_inf); // may throw bad_alloc
  591. SerialJobs.push_back(pInfScan);
  592. pInfScan->ThisIsLayoutInf = true;
  593. res = pInfScan->Run();
  594. if(res == 0) {
  595. //
  596. // parse [SourceDisksFiles*]
  597. //
  598. pInfScan->PrimaryInf->Locked = true;
  599. LayoutInfs.push_back(pInfScan->PrimaryInf);
  600. if(pInfScan->PrimaryInf->LooksLikeLayoutInf) {
  601. //
  602. // parse [WinntDirectories]
  603. // (only makes sense if layout.inf extended syntax detected)
  604. //
  605. res = pInfScan->PrimaryInf->LoadWinntDirectories(GlobalDirectories);
  606. }
  607. if(NewFilter != INVALID_HANDLE_VALUE) {
  608. //
  609. // ensure we indicate this INF should always be processed
  610. // at some point we can handle this flag specially
  611. //
  612. GetFileDisposition(full_inf).FilterAction |= ACTION_EARLYLOAD;
  613. }
  614. }
  615. pInfScan->PartialCleanup();
  616. if(res != 0) {
  617. return res;
  618. }
  619. }
  620. }
  621. if(GeneratePnfs) {
  622. //
  623. // process Pnf's either now
  624. // or add them to Job list
  625. //
  626. if(Trace) {
  627. if(ThreadCount) {
  628. _ftprintf(stderr,TEXT("#### Adding Pnf generation Jobs\n"));
  629. } else {
  630. _ftprintf(stderr,TEXT("#### Generating Pnf's sequentially\n"));
  631. }
  632. }
  633. for(i = ParseInfList.begin(); i != ParseInfList.end() ; i++) {
  634. SafeString &full_inf = *i;
  635. if(ThreadCount) {
  636. //
  637. // add to job list
  638. //
  639. PnfGen *pJob = new PnfGen(full_inf); // may throw bad_alloc
  640. Jobs.push_back(pJob);
  641. } else {
  642. //
  643. // do now
  644. //
  645. GeneratePnf(full_inf);
  646. }
  647. }
  648. //
  649. // limit threads to # of INF's
  650. //
  651. if(Jobs.size() < ThreadCount) {
  652. ThreadCount = Jobs.size();
  653. }
  654. }
  655. if(!GeneratePnfsOnly) {
  656. //
  657. // process Inf's either now or queue for job processing
  658. //
  659. if(Trace) {
  660. if(ThreadCount) {
  661. _ftprintf(stderr,TEXT("#### Adding Inf scanning Jobs\n"));
  662. } else {
  663. _ftprintf(stderr,TEXT("#### Scanning Inf's sequentially\n"));
  664. }
  665. }
  666. for(i = ParseInfList.begin(); i != ParseInfList.end() ; i++) {
  667. SafeString &full_inf = *i;
  668. InfScan *pJob = new InfScan(this,full_inf); // may throw bad_alloc
  669. if(ThreadCount) {
  670. //
  671. // add to job list
  672. //
  673. Jobs.push_back(pJob);
  674. } else {
  675. //
  676. // do now
  677. //
  678. SerialJobs.push_back(pJob);
  679. res = pJob->Run();
  680. pJob->PartialCleanup();
  681. if(res != 0) {
  682. return res;
  683. }
  684. }
  685. }
  686. }
  687. if(ThreadCount) {
  688. //
  689. // use worker threads
  690. //
  691. // see if #jobs > #threads
  692. //
  693. if(Jobs.size() < ThreadCount) {
  694. ThreadCount = Jobs.size();
  695. }
  696. if(Trace) {
  697. _ftprintf(stderr,TEXT("#### Spinning %u threads\n"),ThreadCount);
  698. }
  699. //
  700. // generate the thread objects
  701. //
  702. res = GenerateThreads();
  703. if(res != 0) {
  704. return res;
  705. }
  706. //
  707. // start the threads
  708. // they will start picking up the jobs
  709. //
  710. if(Trace) {
  711. _ftprintf(stderr,TEXT("#### Starting threads\n"));
  712. }
  713. res = StartThreads();
  714. if(res != 0) {
  715. return res;
  716. }
  717. //
  718. // wait for all threads to finish
  719. //
  720. if(Trace) {
  721. _ftprintf(stderr,TEXT("#### Waiting for Jobs to finish\n"));
  722. }
  723. res = FinishThreads();
  724. if(res != 0) {
  725. return res;
  726. }
  727. }
  728. //
  729. // merge any results
  730. //
  731. if(Trace) {
  732. _ftprintf(stderr,TEXT("#### Merge results\n"));
  733. }
  734. res = FinishJobs();
  735. if(res != 0) {
  736. return res;
  737. }
  738. if(GeneratePnfsOnly) {
  739. return 0;
  740. }
  741. //
  742. // post operations
  743. //
  744. if(res == 0) {
  745. res = BuildNewInfFilter();
  746. }
  747. if(res == 0) {
  748. res = BuildDeviceInfMap();
  749. }
  750. if(res == 0) {
  751. res = BuildFinalSourceList();
  752. }
  753. if(Trace) {
  754. _ftprintf(stderr,TEXT("#### Finish\n"));
  755. }
  756. return res;
  757. }
  758. int
  759. GlobalScan::GenerateThreads()
  760. /*++
  761. Routine Description:
  762. Create required number of Job threads
  763. Threads are initially stopped
  764. Arguments:
  765. none
  766. Return Value:
  767. 0 on success
  768. --*/
  769. {
  770. int c;
  771. for(c=0; c< ThreadCount;c++) {
  772. JobThreads.push_back(JobThread(this));
  773. }
  774. return 0;
  775. }
  776. int
  777. GlobalScan::StartThreads()
  778. /*++
  779. Routine Description:
  780. Kick off the job threads to start processing the jobs
  781. Arguments:
  782. none
  783. Return Value:
  784. 0 on success
  785. --*/
  786. {
  787. //
  788. // this is the first job in the list
  789. //
  790. NextJob = Jobs.begin();
  791. //
  792. // now kick off the threads to start looking for
  793. // and processing jobs
  794. //
  795. JobThreadList::iterator i;
  796. for(i = JobThreads.begin(); i != JobThreads.end(); i++) {
  797. if(!i->Begin()) {
  798. _ftprintf(stderr,TEXT("#*** Could not start thread\n"));
  799. return 3;
  800. }
  801. }
  802. return 0;
  803. }
  804. int
  805. GlobalScan::FinishThreads()
  806. /*++
  807. Routine Description:
  808. Wait for all job threads to finish
  809. Arguments:
  810. none
  811. Return Value:
  812. 0 on success
  813. --*/
  814. {
  815. JobThreadList::iterator i;
  816. for(i = JobThreads.begin(); i != JobThreads.end(); i++) {
  817. int res = (int)(i->Wait());
  818. if(res != 0) {
  819. return res;
  820. }
  821. }
  822. return 0;
  823. }
  824. int
  825. GlobalScan::FinishJobs()
  826. /*++
  827. Routine Description:
  828. Do finish processing on each job sequentially
  829. this includes any serialized jobs
  830. Arguments:
  831. none
  832. Return Value:
  833. 0 on success
  834. --*/
  835. {
  836. JobList::iterator i;
  837. //
  838. // two passes
  839. // first PreResults to do any final prep's
  840. //
  841. for(i = SerialJobs.begin(); i != SerialJobs.end(); i++) {
  842. int res = i->PreResults();
  843. if(res != 0) {
  844. return res;
  845. }
  846. }
  847. for(i = Jobs.begin(); i != Jobs.end(); i++) {
  848. int res = i->PreResults();
  849. if(res != 0) {
  850. return res;
  851. }
  852. }
  853. //
  854. // now actual Results phase
  855. //
  856. for(i = SerialJobs.begin(); i != SerialJobs.end(); i++) {
  857. int res = i->Results();
  858. if(res != 0) {
  859. return res;
  860. }
  861. }
  862. for(i = Jobs.begin(); i != Jobs.end(); i++) {
  863. int res = i->Results();
  864. if(res != 0) {
  865. return res;
  866. }
  867. }
  868. return 0;
  869. }
  870. int
  871. GlobalScan::ExpandFullPath(const SafeString & subdir,const SafeString & name,SafeString & target)
  872. /*++
  873. Routine Description:
  874. Expand full path taking into account specified SourcePath
  875. Arguments:
  876. subdir - subdirectory of SourcePath if not ""
  877. name - filename (may be wildcard and may include a subdirectory)
  878. target - generated full path name
  879. Return Value:
  880. 0
  881. --*/
  882. {
  883. SafeString given = PathConcat(SourcePath,PathConcat(subdir,name));
  884. GetFullPathName(given,target);
  885. return 0;
  886. }
  887. int
  888. GlobalScan::ExpandFullPathWithOverride(const SafeString & name,SafeString & target)
  889. /*++
  890. Routine Description:
  891. Expand full path taking into account Overrides
  892. look for each SourcePath\<override>\name
  893. and take first found converting it into full path
  894. Arguments:
  895. name - filename (may include a subdirectory)
  896. target - generated full path name
  897. Return Value:
  898. 0
  899. --*/
  900. {
  901. StringList::iterator dir;
  902. int res;
  903. for(dir = Overrides.begin(); dir != Overrides.end(); dir++) {
  904. res = ExpandFullPath(*dir,name,target);
  905. if(res != 0) {
  906. return res;
  907. }
  908. DWORD attr = GetFileAttributes(target.c_str());
  909. if((attr == (DWORD)(-1)) || (attr & FILE_ATTRIBUTE_DIRECTORY)) {
  910. //
  911. // the named file doesn't exist in this particular
  912. // override directory
  913. //
  914. continue;
  915. }
  916. //
  917. // we found an override match
  918. //
  919. return 0;
  920. }
  921. //
  922. // since the last overrides entry is ".", just return
  923. // last generated name
  924. //
  925. return 0;
  926. }
  927. int GlobalScan::SaveForCrossInfInstallCheck(const SafeString & desc,const SafeString & src)
  928. /*++
  929. Routine Description:
  930. Not thread safe, called during PreResults
  931. Save description->src mapping
  932. for later call to CheckCrossInfInstallCheck
  933. (we can't do the check here else we'd randomly pick one inf over another
  934. to report the conflict and we want to fail both inf's)
  935. Arguments:
  936. desc - lower-case device description
  937. src - full name of INF currently being checked
  938. Return Value:
  939. 0
  940. --*/
  941. {
  942. StringToStringset::iterator i;
  943. i = GlobalInfDescriptions.find(desc);
  944. if(i == GlobalInfDescriptions.end()) {
  945. //
  946. // description doesn't exist
  947. // create a new entry desc->src
  948. //
  949. StringSet s;
  950. s.insert(src);
  951. GlobalInfDescriptions.insert(StringToStringset::value_type(desc,s));
  952. } else {
  953. //
  954. // description exists
  955. // add this src (if it doesn't already exist)
  956. //
  957. i->second.insert(src);
  958. }
  959. return 0;
  960. }
  961. int GlobalScan::SaveForCrossInfDeviceCheck(const SafeString & hwid,const SafeString & src)
  962. /*++
  963. Routine Description:
  964. Not thread safe, called during PreResults
  965. Save hwid->src mapping
  966. for later call to CheckCrossInfInstallCheck
  967. (we can't do the check here else we'd randomly pick one inf over another
  968. to report the conflict and we want to fail both inf's)
  969. Arguments:
  970. hwid - lower-case hardware ID
  971. src - full name of INF currently being checked
  972. Return Value:
  973. 0
  974. --*/
  975. {
  976. StringToStringset::iterator i;
  977. i = GlobalInfHardwareIds.find(hwid);
  978. if(i == GlobalInfHardwareIds.end()) {
  979. //
  980. // hwid doesn't exist
  981. // create a new entry desc->src
  982. //
  983. StringSet s;
  984. s.insert(src);
  985. GlobalInfHardwareIds.insert(StringToStringset::value_type(hwid,s));
  986. } else {
  987. //
  988. // hwid exists
  989. // add this src (if it doesn't already exist)
  990. //
  991. i->second.insert(src);
  992. }
  993. return 0;
  994. }
  995. int
  996. GlobalScan::CheckCrossInfInstallConflict(const SafeString & desc,const SafeString & src, bool & f,SafeString & others)
  997. /*++
  998. Routine Description:
  999. Not thread safe, called during PreResults
  1000. given a "description"
  1001. return f=true if found in some INF other than 'src'
  1002. return f=false if not found (INF added)
  1003. Arguments:
  1004. desc - lower-case device description
  1005. src - full name of INF currently being checked
  1006. f - return true if exists in another inf
  1007. others - descriptive list of conflicting INF's
  1008. Return Value:
  1009. 0
  1010. --*/
  1011. {
  1012. f = false;
  1013. StringToStringset::iterator i;
  1014. i = GlobalInfDescriptions.find(desc);
  1015. if(i == GlobalInfDescriptions.end()) {
  1016. //
  1017. // shouldn't happen, but do right thing
  1018. //
  1019. return 0;
  1020. }
  1021. StringSet & s = i->second;
  1022. StringSet::iterator ii;
  1023. //
  1024. // report any conflicts other than self
  1025. //
  1026. for(ii = s.begin(); ii != s.end(); ii++) {
  1027. SafeString & str = *ii;
  1028. if(str == src) {
  1029. continue;
  1030. }
  1031. if(f) {
  1032. others += TEXT(" & ") + str;
  1033. } else {
  1034. f = true;
  1035. others = str;
  1036. }
  1037. }
  1038. return 0;
  1039. }
  1040. int
  1041. GlobalScan::CheckCrossInfDeviceConflict(const SafeString & hwid,const SafeString & src, bool & f,SafeString & others)
  1042. /*++
  1043. Routine Description:
  1044. Not thread safe, called during PreResults
  1045. given a "hardwareId"
  1046. return f=true if found in some INF other than 'src'
  1047. return f=false if not found (INF added)
  1048. Arguments:
  1049. hwid - lower-case hardware ID
  1050. src - full name of INF currently being checked
  1051. f - return true if exists in another inf
  1052. others - descriptive list of conflicting INF's
  1053. Return Value:
  1054. 0
  1055. --*/
  1056. {
  1057. f = false;
  1058. StringToStringset::iterator i;
  1059. i = GlobalInfHardwareIds.find(hwid);
  1060. if(i == GlobalInfHardwareIds.end()) {
  1061. //
  1062. // shouldn't happen, but do right thing
  1063. //
  1064. return 0;
  1065. }
  1066. StringSet & s = i->second;
  1067. StringSet::iterator ii;
  1068. //
  1069. // report any conflicts other than self
  1070. //
  1071. for(ii = s.begin(); ii != s.end(); ii++) {
  1072. SafeString & str = *ii;
  1073. if(str == src) {
  1074. continue;
  1075. }
  1076. if(f) {
  1077. others += TEXT(" & ") + str;
  1078. } else {
  1079. f = true;
  1080. others = str;
  1081. }
  1082. }
  1083. return 0;
  1084. }
  1085. int GlobalScan::AddSourceFiles(StringList & sources)
  1086. /*++
  1087. Routine Description:
  1088. Add the list 'sources' to the known set of source files
  1089. Arguments:
  1090. sources - list of source files to add
  1091. Return Value:
  1092. 0
  1093. --*/
  1094. {
  1095. if(SourceFileList != INVALID_HANDLE_VALUE) {
  1096. StringList::iterator i;
  1097. for(i = sources.begin(); i != sources.end(); i++) {
  1098. GlobalFileSet.insert(*i);
  1099. }
  1100. }
  1101. return 0;
  1102. }
  1103. JobEntry * GlobalScan::GetNextJob()
  1104. /*++
  1105. Routine Description:
  1106. Get next job safely
  1107. Arguments:
  1108. none
  1109. Return Value:
  1110. next job or NULL
  1111. --*/
  1112. {
  1113. ProtectedSection ThisSectionIsA(BottleNeck);
  1114. if(NextJob == Jobs.end()) {
  1115. return NULL;
  1116. }
  1117. JobEntry * Job = &*NextJob;
  1118. NextJob++;
  1119. return Job;
  1120. }
  1121. int GlobalScan::LoadFileDispositions()
  1122. /*++
  1123. Routine Description:
  1124. Load global error dispositions from filter
  1125. Load file/guid top-level dispositions
  1126. (actual error tables for these are loaded on demand)
  1127. Arguments:
  1128. none
  1129. Return Value:
  1130. 0 on success
  1131. --*/
  1132. {
  1133. //
  1134. // load filter tables
  1135. //
  1136. int res;
  1137. INFCONTEXT filterContext;
  1138. if(InfFilter == INVALID_HANDLE_VALUE) {
  1139. return 0;
  1140. }
  1141. //
  1142. // load top-level filter table
  1143. //
  1144. if(SetupFindFirstLine(InfFilter,SECTION_FILEFILTERS,NULL,&filterContext)) {
  1145. do {
  1146. SafeString filename;
  1147. FileDisposition disp;
  1148. if(!MyGetStringField(&filterContext,FILEFILTERS_KEY_FILENAME,filename)) {
  1149. continue;
  1150. }
  1151. if(FileDispositions.find(filename) != FileDispositions.end()) {
  1152. continue;
  1153. }
  1154. if(!SetupGetIntField(&filterContext,FILEFILTERS_FIELD_ACTION,&disp.FilterAction)) {
  1155. disp.FilterAction = ACTION_DEFAULT;
  1156. }
  1157. MyGetStringField(&filterContext,FILEFILTERS_FIELD_SECTION,disp.FilterErrorSection);
  1158. MyGetStringField(&filterContext,FILEFILTERS_FIELD_GUID,disp.FileGuid);
  1159. disp.Filtered = true;
  1160. FileDispositions.insert(FileDispositionMap::value_type(filename,disp));
  1161. } while (SetupFindNextLine(&filterContext,&filterContext));
  1162. }
  1163. if(SetupFindFirstLine(InfFilter,SECTION_GUIDFILTERS,NULL,&filterContext)) {
  1164. do {
  1165. SafeString guid;
  1166. FileDisposition disp;
  1167. if(!MyGetStringField(&filterContext,GUIDFILTERS_KEY_GUID,guid)) {
  1168. continue;
  1169. }
  1170. if(GuidDispositions.find(guid) != GuidDispositions.end()) {
  1171. continue;
  1172. }
  1173. if(!SetupGetIntField(&filterContext,GUIDFILTERS_FIELD_ACTION,&disp.FilterAction)) {
  1174. disp.FilterAction = ACTION_DEFAULT;
  1175. }
  1176. MyGetStringField(&filterContext,GUIDFILTERS_FIELD_SECTION,disp.FilterErrorSection);
  1177. disp.Filtered = true;
  1178. GuidDispositions.insert(FileDispositionMap::value_type(guid,disp));
  1179. } while (SetupFindNextLine(&filterContext,&filterContext));
  1180. }
  1181. //
  1182. // preload global error table
  1183. //
  1184. GlobalErrorFilters.LoadFromInfSection(InfFilter,SECTION_ERRORFILTERS);
  1185. return 0;
  1186. }
  1187. int GlobalScan::LoadOtherCopySections()
  1188. /*++
  1189. Routine Description:
  1190. Load copy filters
  1191. Load file/guid top-level dispositions
  1192. (actual error tables for these are loaded on demand)
  1193. Arguments:
  1194. none
  1195. Return Value:
  1196. 0 on success
  1197. --*/
  1198. {
  1199. //
  1200. // load filter tables
  1201. //
  1202. int res;
  1203. INFCONTEXT filterContext;
  1204. if(InfFilter == INVALID_HANDLE_VALUE) {
  1205. return 0;
  1206. }
  1207. //
  1208. // load top-level filter table
  1209. //
  1210. if(SetupFindFirstLine(InfFilter,SECTION_INSTALLS,NULL,&filterContext)) {
  1211. //
  1212. // file = install[,install....]
  1213. //
  1214. do {
  1215. SafeString filename;
  1216. SafeString section;
  1217. int c;
  1218. if(!MyGetStringField(&filterContext,0,filename)) {
  1219. continue;
  1220. }
  1221. StringSet & sects = GlobalOtherInstallSections[filename];
  1222. for(c=1; MyGetStringField(&filterContext,c,section);c++) {
  1223. sects.insert(section);
  1224. }
  1225. } while(SetupFindNextLine(&filterContext,&filterContext));
  1226. }
  1227. return 0;
  1228. }
  1229. int GlobalScan::GetCopySections(const SafeString & filename,StringSet & target)
  1230. /*++
  1231. Routine Description:
  1232. Return cached list of copy sections from filter
  1233. Arguments:
  1234. filename (without path)
  1235. target - merged with list of install sections
  1236. Return Value:
  1237. 0 = success
  1238. --*/
  1239. {
  1240. StringToStringset::iterator i;
  1241. i = GlobalOtherInstallSections.find(filename);
  1242. if(i == GlobalOtherInstallSections.end()) {
  1243. return 0;
  1244. }
  1245. StringSet & sects = i->second;
  1246. StringSet::iterator ii;
  1247. for(ii = sects.begin(); ii != sects.end(); ii++) {
  1248. target.insert(*ii);
  1249. }
  1250. return 0;
  1251. }
  1252. int GlobalScan::SetCopySections(const SafeString & filename,const StringSet & sections)
  1253. /*++
  1254. Routine Description:
  1255. Not thread safe
  1256. upgrade global table with extra copy sections
  1257. Arguments:
  1258. filename (without path)
  1259. sections - list of install sections to merge in
  1260. Return Value:
  1261. 0 = success
  1262. --*/
  1263. {
  1264. StringSet::iterator i;
  1265. StringSet & sects = GlobalOtherInstallSections[filename];
  1266. for(i = sections.begin(); i != sections.end(); i++) {
  1267. sects.insert(*i);
  1268. }
  1269. return 0;
  1270. }
  1271. FileDisposition & GlobalScan::GetFileDisposition(const SafeString & pathname)
  1272. /*++
  1273. Routine Description:
  1274. Return a file disposition entry for specified file
  1275. Note that the returned structure may be modified
  1276. However the table itself is shared
  1277. Arguments:
  1278. full path or filename
  1279. Return Value:
  1280. modifiable entry
  1281. --*/
  1282. {
  1283. //
  1284. // assume filename is lower-case
  1285. // we need to get just the actual name
  1286. //
  1287. SafeString filename = GetFileNamePart(pathname);
  1288. FileDispositionMap::iterator i;
  1289. ProtectedSection ThisSectionIsA(BottleNeck);
  1290. i = FileDispositions.find(filename);
  1291. if(i == FileDispositions.end()) {
  1292. //
  1293. // create and return
  1294. //
  1295. return FileDispositions[filename];
  1296. }
  1297. return i->second;
  1298. }
  1299. FileDisposition & GlobalScan::GetGuidDisposition(const SafeString & guid)
  1300. /*++
  1301. Routine Description:
  1302. Return a guid disposition entry for specified guid
  1303. Note that the returned structure may be modified
  1304. However the table itself is shared
  1305. Arguments:
  1306. full guid {...}
  1307. Return Value:
  1308. modifiable entry
  1309. --*/
  1310. {
  1311. FileDispositionMap::iterator i;
  1312. //
  1313. // assume guid-string is lower-case
  1314. //
  1315. ProtectedSection ThisSectionIsA(BottleNeck);
  1316. i = GuidDispositions.find(guid);
  1317. if(i == GuidDispositions.end()) {
  1318. //
  1319. // create and return
  1320. //
  1321. return GuidDispositions[guid];
  1322. }
  1323. return i->second;
  1324. }
  1325. int GlobalScan::BuildFinalSourceList()
  1326. /*++
  1327. Routine Description:
  1328. If requested, build a complete list of source files
  1329. Arguments:
  1330. NONE
  1331. Return Value:
  1332. 0 on success
  1333. --*/
  1334. {
  1335. if(SourceFileList == INVALID_HANDLE_VALUE) {
  1336. return 0;
  1337. }
  1338. if(Trace) {
  1339. _ftprintf(stderr,TEXT("#### Build source list\n"));
  1340. }
  1341. StringSet::iterator i;
  1342. for(i = GlobalFileSet.begin(); i != GlobalFileSet.end(); i++) {
  1343. Write(SourceFileList,*i);
  1344. Write(SourceFileList,"\r\n");
  1345. }
  1346. return 0;
  1347. }
  1348. int GlobalScan::BuildNewInfFilter()
  1349. /*++
  1350. Routine Description:
  1351. If requested, complete a filter INF
  1352. Arguments:
  1353. NONE
  1354. Return Value:
  1355. 0 on success
  1356. --*/
  1357. {
  1358. if(NewFilter == INVALID_HANDLE_VALUE) {
  1359. return 0;
  1360. }
  1361. if(Trace) {
  1362. _ftprintf(stderr,TEXT("#### Build filter\n"));
  1363. }
  1364. FileDispositionMap::iterator i;
  1365. //
  1366. // File Dispositions table
  1367. //
  1368. Write(NewFilter,TEXT("\r\n[") SECTION_FILEFILTERS TEXT("]\r\n"));
  1369. for(i = FileDispositions.begin(); i != FileDispositions.end(); i++) {
  1370. if(i->second.Filtered) {
  1371. basic_ostringstream<TCHAR> line;
  1372. line << QuoteIt(i->first) << TEXT(" = ");
  1373. line << TEXT("0x") << setfill(TEXT('0')) << setw(8) << hex << i->second.FilterAction << setfill(TEXT(' ')) << TEXT(",");
  1374. line << QuoteIt(i->second.FilterErrorSection) << TEXT(",");
  1375. line << QuoteIt(i->second.FileGuid);
  1376. line << TEXT("\r\n");
  1377. Write(NewFilter,line.str());
  1378. }
  1379. }
  1380. //
  1381. // Guid Dispositions
  1382. //
  1383. Write(NewFilter,TEXT("\r\n[") SECTION_GUIDFILTERS TEXT("]\r\n"));
  1384. for(i = GuidDispositions.begin(); i != GuidDispositions.end(); i++) {
  1385. if(i->second.Filtered) {
  1386. basic_ostringstream<TCHAR> line;
  1387. line << QuoteIt(i->first) << TEXT(" = ");
  1388. line << TEXT("0x") << setfill(TEXT('0')) << setw(8) << hex << i->second.FilterAction << setfill(TEXT(' ')) << TEXT("\r\n");
  1389. Write(NewFilter,line.str());
  1390. }
  1391. }
  1392. //
  1393. // other copy sections
  1394. //
  1395. if(GlobalOtherInstallSections.size()) {
  1396. Write(NewFilter,TEXT("\r\n[") SECTION_INSTALLS TEXT("]\r\n"));
  1397. StringToStringset::iterator s;
  1398. for(s = GlobalOtherInstallSections.begin(); s != GlobalOtherInstallSections.end(); s++) {
  1399. StringSet & sects = s->second;
  1400. StringSet::iterator ss;
  1401. for(ss = sects.begin(); ss != sects.end(); ss++) {
  1402. basic_ostringstream<TCHAR> line;
  1403. line << QuoteIt(s->first) << TEXT(" = ");
  1404. line << QuoteIt(*ss) << TEXT("\r\n");
  1405. Write(NewFilter,line.str());
  1406. }
  1407. }
  1408. }
  1409. return 0;
  1410. }
  1411. bool GlobalScan::IsFileChanged(const SafeString & file) const
  1412. {
  1413. SafeString fnp = GetFileNamePart(file);
  1414. //
  1415. // appears to have changed
  1416. //
  1417. return BuildUnchangedFiles.find(fnp) == BuildUnchangedFiles.end();
  1418. }
  1419. int GlobalScan::BuildDeviceInfMap()
  1420. /*++
  1421. Routine Description:
  1422. Create a list of "hardwareId" = "file"
  1423. to DeviceFilterList
  1424. Return Value:
  1425. 0
  1426. --*/
  1427. {
  1428. if(DeviceFilterList == INVALID_HANDLE_VALUE) {
  1429. return 0;
  1430. }
  1431. StringToStringset::iterator i;
  1432. for(i = GlobalInfHardwareIds.begin(); i != GlobalInfHardwareIds.end(); i++) {
  1433. const SafeString &hwid = i->first;
  1434. StringSet &s = i->second;
  1435. Write(DeviceFilterList,QuoteIt(hwid));
  1436. bool f = false;
  1437. StringSet::iterator ii;
  1438. //
  1439. // report all INF's hwid appears in
  1440. //
  1441. for(ii = s.begin(); ii != s.end(); ii++) {
  1442. SafeString & str = *ii;
  1443. if(f) {
  1444. Write(DeviceFilterList,",");
  1445. } else {
  1446. Write(DeviceFilterList," = ");
  1447. f = true;
  1448. }
  1449. Write(DeviceFilterList,QuoteIt(*ii));
  1450. }
  1451. Write(DeviceFilterList,"\r\n");
  1452. }
  1453. return 0;
  1454. }
  1455. int GlobalScan::LoadListFromFile(const SafeStringA & file,StringList & list)
  1456. /*++
  1457. Routine Description:
  1458. Load a list of strings from a text file (ANSI filename)
  1459. file - name of file to load
  1460. list - returned list of strings
  1461. Return Value:
  1462. 0
  1463. --*/
  1464. {
  1465. HANDLE hFile = CreateFileA(file.c_str(),
  1466. GENERIC_READ,
  1467. FILE_SHARE_READ|FILE_SHARE_WRITE,
  1468. NULL,
  1469. OPEN_EXISTING,
  1470. FILE_ATTRIBUTE_NORMAL,
  1471. NULL);
  1472. if(hFile == INVALID_HANDLE_VALUE) {
  1473. fprintf(stderr,"#*** Cannot open file \"%s\" for reading\n",file.c_str());
  1474. return 3;
  1475. }
  1476. int res = LoadListFromFile(hFile,list);
  1477. CloseHandle(hFile);
  1478. if(res!=0) {
  1479. fprintf(stderr,"#*** Failure reading file \"%s\"\n",file.c_str());
  1480. }
  1481. return res;
  1482. }
  1483. int GlobalScan::LoadListFromFile(const SafeStringW & file,StringList & list)
  1484. /*++
  1485. Routine Description:
  1486. Load a list of strings from a text file (Unicode filename)
  1487. file - name of file to load
  1488. list - returned list of strings
  1489. Return Value:
  1490. 0
  1491. --*/
  1492. {
  1493. HANDLE hFile = CreateFileW(file.c_str(),
  1494. GENERIC_READ,
  1495. FILE_SHARE_READ|FILE_SHARE_WRITE,
  1496. NULL,
  1497. OPEN_EXISTING,
  1498. FILE_ATTRIBUTE_NORMAL,
  1499. NULL);
  1500. if(hFile == INVALID_HANDLE_VALUE) {
  1501. fprintf(stderr,"#*** Cannot open file \"%S\" for reading\n",file.c_str());
  1502. return 3;
  1503. }
  1504. int res = LoadListFromFile(hFile,list);
  1505. CloseHandle(hFile);
  1506. if(res!=0) {
  1507. fprintf(stderr,"#*** Failure reading file \"%S\"\n",file.c_str());
  1508. }
  1509. return res;
  1510. }
  1511. int GlobalScan::LoadListFromFile(HANDLE hFile,StringList & list)
  1512. /*++
  1513. Routine Description:
  1514. Load a list of strings from a text file (handle)
  1515. file - name of file to load
  1516. list - returned list of strings
  1517. Return Value:
  1518. 0
  1519. --*/
  1520. {
  1521. DWORD sz = GetFileSize(hFile,NULL);
  1522. if(sz == INVALID_FILE_SIZE) {
  1523. return 3;
  1524. }
  1525. if(sz==0) {
  1526. //
  1527. // nothing to read
  1528. //
  1529. return 0;
  1530. }
  1531. LPSTR buffer = new CHAR[sz+1];
  1532. DWORD actsz;
  1533. if(!ReadFile(hFile,buffer,sz,&actsz,NULL)) {
  1534. delete [] buffer;
  1535. return 3;
  1536. }
  1537. buffer[actsz] = 0;
  1538. while(actsz>0 && buffer[actsz-1] == ('Z'-'@'))
  1539. {
  1540. actsz--;
  1541. buffer[actsz] = 0;
  1542. }
  1543. LPSTR linestart = buffer;
  1544. while(*linestart) {
  1545. LPSTR lineend = strchr(linestart,'\n');
  1546. if(lineend==NULL) {
  1547. lineend += strlen(linestart);
  1548. } else if(lineend == linestart) {
  1549. linestart = lineend+1;
  1550. continue;
  1551. } else {
  1552. if(lineend[-1] == '\r') {
  1553. lineend[-1] = '\0';
  1554. }
  1555. if(*lineend) {
  1556. *lineend = '\0';
  1557. lineend++;
  1558. }
  1559. }
  1560. _strlwr(linestart);
  1561. list.push_back(ConvertString(linestart));
  1562. linestart = lineend;
  1563. }
  1564. delete [] buffer;
  1565. return 0;
  1566. }