Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

644 lines
21 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. escpeinf.c
  5. Abstract:
  6. This module filters an inf for use by the ESCAPE tool. Default file
  7. security can be set in the inx file using wildcard rules, and this
  8. program expands them, creating a valid file for ESCAPE. This is
  9. necessary because ESCAPE does not support wildcards in the file section
  10. See below for more information.
  11. Note: ESCAPE was the proposed name of the Security Configuration Engine when this
  12. was written, but that name too was dropped. They still haven't come up with
  13. a good name for it.
  14. Author:
  15. Sandy Coyne (scoyne) February 29, 2000
  16. Revision History:
  17. --*/
  18. /*
  19. Usage: escpeinf.exe <U|C> <codepage> <input file> <output file> <layout.inf>
  20. Parameter info is printed when you run the program without all the right arguments
  21. layout.inf is the system-wide layout.inf, already built for the local
  22. language, arch., etc.
  23. The input file to this program consists of an inf, already sent through
  24. the filters for architecture, language and product. This inf is an input
  25. file for ESCAPE, with one exception: In the [File Security] section, some
  26. entries may have wildcards for filenames. Following those lines, if
  27. desired, is a list of files that should be excluded from matching that
  28. wildcard. This list is in the form of exception lines.
  29. Example:
  30. [File Security]
  31. "%SystemDirectory%\*",2,"D:P(A;;GRGX;;;BU)(A;;GRGX;;;PU)(A;;GA;;;BA)(A;;GA;;;SY)"
  32. Exception="*.ini"
  33. Exception="config.nt"
  34. This wildcard line will be replaced with an enumeration of all matching files
  35. that are copied by text-mode setup, as specified by the listing in layout.inf
  36. */
  37. #include <windows.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <tchar.h>
  41. #include <string.h>
  42. #include <locale.h>
  43. #include <mbctype.h>
  44. #include <inflib.h>
  45. #include <sputils.h>
  46. #include <wild.c>
  47. // Define program result codes (returned from main()).
  48. #define SUCCESS 0
  49. #define FAILURE 1
  50. #define MAX_INF_LINE_LENGTH 256
  51. #define MAX_EXCEPTIONS 256
  52. #define ESCPEINF_VERSION "1.4"
  53. //#define ESCPEINF_DEBUG
  54. enum {
  55. UPGRADE,
  56. CLEAN_INSTALL
  57. } GlobalMode;
  58. typedef struct _EXCEPTIONS {
  59. TCHAR s[MAX_INF_LINE_LENGTH];
  60. } EXCEPTIONS;
  61. typedef struct _MYPARAM {
  62. FILE *OutputFile;
  63. EXCEPTIONS *ExceptionList;
  64. DWORD Num;
  65. PTSTR WildCard,
  66. RealPath,
  67. LayoutPath,
  68. SecurityString;
  69. } MYPARAM, *PMYPARAM;
  70. BOOL ProcessFileSecuritySection(FILE *InputFile, FILE *OutputFile,
  71. PLAYOUT_CONTEXT LayoutContexta);
  72. BOOL GetFields(PTSTR InfLine, PTSTR FileName, DWORD *Num, PTSTR SecurityString);
  73. void ExpandWildCard(PTSTR FileName, DWORD Num, PTSTR SecurityString,
  74. EXCEPTIONS ExceptionList[], FILE *OutputFile,
  75. PLAYOUT_CONTEXT LayoutContext);
  76. void FindExceptions(FILE *InputFile, EXCEPTIONS ExceptionList[]);
  77. void PrintUsage(LPTSTR FileName)
  78. {
  79. _ftprintf(stderr, TEXT("ESCAPE Inf file pre-processor, version %s\n"),
  80. TEXT(ESCPEINF_VERSION));
  81. _ftprintf(stderr, TEXT("\tFor Microsoft internal use only. "));
  82. _ftprintf(stderr, TEXT("Contact Sandy Coyne with questions.\n"));
  83. _ftprintf(stderr,
  84. TEXT("Usage: %s <U|C> <codepage> <input file> <output file> <layout.inf>\n"),
  85. FileName);
  86. _ftprintf(stderr, TEXT("\tU = Upgrade\n"));
  87. _ftprintf(stderr, TEXT("\tC = Clean Install\n"));
  88. _ftprintf(stderr,
  89. TEXT("\t<codepage> specifies the codepage to use when accessing input and\n"));
  90. _ftprintf(stderr,
  91. TEXT("\t\toutput files. Translation of layout.inf is not affected by\n"));
  92. _ftprintf(stderr,
  93. TEXT("\t\tthis option. You may specify \"none\" to open the input\n"));
  94. _ftprintf(stderr,
  95. TEXT("\t\tand output files as Unicode files with no codepage translation.\n"));
  96. _ftprintf(stderr, TEXT("\tAll fields are required.\n"));
  97. }
  98. int __cdecl _tmain(IN int argc, IN LPTSTR argv[])
  99. {
  100. FILE *InputFile,
  101. *OutputFile;
  102. LPTSTR LocaleString;
  103. TCHAR InfLine[MAX_INF_LINE_LENGTH],
  104. CodePage[10];
  105. PLAYOUT_CONTEXT LayoutContext;
  106. BOOL bUnicodeIO;
  107. int result = FAILURE;
  108. if(!pSetupInitializeUtils()) {
  109. _ftprintf(stderr, TEXT("Initialize failed\n"));
  110. return FAILURE;
  111. }
  112. // Print Usage information
  113. if (argc != 6)
  114. {
  115. PrintUsage(argv[0]);
  116. goto fail;
  117. }
  118. if (_tcsicmp(argv[1], TEXT("U")) == 0)
  119. {
  120. GlobalMode = UPGRADE;
  121. }
  122. else if (_tcsicmp(argv[1], TEXT("C")) == 0)
  123. {
  124. GlobalMode = CLEAN_INSTALL;
  125. }
  126. else
  127. {
  128. PrintUsage(argv[0]);
  129. goto fail;
  130. }
  131. if (_tcsicmp(argv[2], TEXT("None")) == 0)
  132. {
  133. bUnicodeIO = TRUE;
  134. #ifdef ESCPEINF_DEBUG
  135. _ftprintf(stderr, TEXT("Using Unicode I/O\n"));
  136. #endif
  137. }
  138. else
  139. {
  140. _stprintf(CodePage, TEXT(".%.7s"), argv[2]);
  141. LocaleString = _tsetlocale(LC_ALL, CodePage);
  142. if (LocaleString == NULL)
  143. {
  144. _ftprintf(stderr, TEXT("Invalid CodePage: \"%s\"\n"), argv[2]);
  145. #ifdef ESCPEINF_DEBUG
  146. _ftprintf(stderr, TEXT("Invalid argument to setlocale: \"%s\"\n"), CodePage);
  147. #endif
  148. goto fail;
  149. }
  150. else
  151. {
  152. #ifdef ESCPEINF_DEBUG
  153. _ftprintf(stderr, TEXT("Locale set to: \"%s\"\n"), LocaleString);
  154. #endif
  155. bUnicodeIO = FALSE;
  156. }
  157. }
  158. // Begin to Open Input, Output and Layout files
  159. if (bUnicodeIO)
  160. {
  161. InputFile = _tfopen(argv[3], TEXT("rb"));
  162. }
  163. else
  164. {
  165. InputFile = _tfopen(argv[3], TEXT("rt"));
  166. }
  167. if (InputFile == NULL)
  168. {
  169. _ftprintf(stderr, TEXT("Error opening Input file: %s\n"), argv[3]);
  170. goto fail;
  171. }
  172. #ifdef ESCPEINF_DEBUG
  173. _ftprintf(stderr, TEXT("Opened Input file: %s\n"), argv[3]);
  174. #endif
  175. rewind(InputFile);
  176. if (bUnicodeIO)
  177. {
  178. OutputFile = _tfopen(argv[4], TEXT("wb"));
  179. }
  180. else
  181. {
  182. OutputFile = _tfopen(argv[4], TEXT("wt"));
  183. }
  184. if (OutputFile == NULL)
  185. {
  186. _ftprintf(stderr, TEXT("Error opening Output file: %s\n"), argv[4]);
  187. fclose(InputFile);
  188. goto fail;
  189. }
  190. #ifdef ESCPEINF_DEBUG
  191. _ftprintf(stderr, TEXT("Opened Output file: %s\n"), argv[4]);
  192. #endif
  193. LayoutContext = BuildLayoutInfContext(argv[5], LAYOUTPLATFORMS_ALL, 0);
  194. if (LayoutContext == NULL)
  195. {
  196. _ftprintf(stderr, TEXT("Error opening Layout file: %s\n"), argv[5]);
  197. _ftprintf(stderr, TEXT("Did you remember to specify a path to the file\n"));
  198. fclose(InputFile); fclose(OutputFile);
  199. goto fail;
  200. }
  201. else
  202. {
  203. #ifdef ESCPEINF_DEBUG
  204. _ftprintf(stderr, TEXT("Opened Layout file: %s\n"), argv[5]);
  205. #endif
  206. }
  207. // Input, Output and Layout files are open
  208. while ((_fgetts(InfLine, MAX_INF_LINE_LENGTH, InputFile)) != NULL)
  209. {
  210. _fputts(InfLine, OutputFile);
  211. if (!_tcscmp(InfLine, TEXT("[File Security]\n")))
  212. if (!ProcessFileSecuritySection(InputFile, OutputFile,
  213. LayoutContext))
  214. {
  215. // If this happens, ProcessFileSecuritySection() has already
  216. // printed an error.
  217. fclose(InputFile); fclose(OutputFile);
  218. CloseLayoutInfContext(LayoutContext);
  219. goto fail;
  220. }
  221. }
  222. if (!feof(InputFile))
  223. {
  224. _ftprintf(stderr, TEXT("Error: Did not reach Input EOF.\n"));
  225. fclose(InputFile); fclose(OutputFile);
  226. CloseLayoutInfContext(LayoutContext);
  227. goto fail;
  228. }
  229. fclose(InputFile);
  230. fclose(OutputFile);
  231. CloseLayoutInfContext(LayoutContext);
  232. #ifdef ESCPEINF_DEBUG
  233. _ftprintf(stderr, TEXT("escpeinf.exe completed successfully\n"));
  234. #endif
  235. result = SUCCESS;
  236. fail:
  237. pSetupUninitializeUtils();
  238. return result;
  239. }
  240. BOOL CALLBACK MyCallback(IN PLAYOUT_CONTEXT Context,
  241. IN PCTSTR FileName,
  242. IN PFILE_LAYOUTINFORMATION LayoutInformation,
  243. IN PVOID ExtraData,
  244. IN UINT ExtraDataSize,
  245. IN OUT DWORD_PTR vpParam)
  246. {
  247. PMYPARAM Param;
  248. BOOL bIsException = FALSE;
  249. int i = 0;
  250. TCHAR FileName_l[MAX_INF_LINE_LENGTH],
  251. TargetFileName_l[MAX_INF_LINE_LENGTH];
  252. if (vpParam)
  253. Param = (PMYPARAM)vpParam;
  254. // Quit now if we don't need to worry about this file at all.
  255. if ((GlobalMode == UPGRADE) &&
  256. (LayoutInformation->UpgradeDisposition == 3))
  257. return TRUE;
  258. if ((GlobalMode == CLEAN_INSTALL) &&
  259. (LayoutInformation->CleanInstallDisposition == 3))
  260. return TRUE;
  261. // Since wildcard compares are case sensitive, I lowercase everything
  262. // before comparing. Wildcard compares happen between the two filename
  263. // variables modified here, WildCard, and ExceptionList. WildCard and
  264. // ExceptionList were previouslt lowercased.
  265. _tcscpy(FileName_l, FileName);
  266. _tcscpy(TargetFileName_l, LayoutInformation->TargetFileName);
  267. _tcslwr(FileName_l);
  268. _tcslwr(TargetFileName_l);
  269. if (_tcslen(TargetFileName_l) > 0) // Do we use the long name?
  270. {
  271. if (_tcsicmp(Param->LayoutPath, LayoutInformation->Directory) == 0)
  272. { // Then it's a file in the right directory
  273. if (IsNameInExpressionPrivate(Param->WildCard, TargetFileName_l))
  274. { // Then it matches our wildcard
  275. while ((_tcslen(Param->ExceptionList[i].s) > 0) && !bIsException)
  276. { // Checking to see if it's an exception...
  277. if (IsNameInExpressionPrivate(Param->ExceptionList[i].s,
  278. TargetFileName_l))
  279. {
  280. bIsException = TRUE; // This must be initialized FALSE
  281. }
  282. i += 1;
  283. }
  284. if (!bIsException)
  285. { // Then we actually want to put it in our output
  286. #ifdef ESCPEINF_DEBUG
  287. _ftprintf(stderr, TEXT("Match: %s(%s) in %s\n"),
  288. FileName, LayoutInformation->TargetFileName,
  289. LayoutInformation->Directory);
  290. #endif
  291. _ftprintf(Param->OutputFile, TEXT("\"%s\\%s\",%d,%s\n"),
  292. Param->RealPath,
  293. LayoutInformation->TargetFileName, Param->Num,
  294. Param->SecurityString);
  295. }
  296. }
  297. }
  298. }
  299. else // We use the short name
  300. {
  301. if (_tcsicmp(Param->LayoutPath, LayoutInformation->Directory) == 0)
  302. { // Then it's a file in the right directory
  303. if (IsNameInExpressionPrivate(Param->WildCard, FileName_l))
  304. { // Then it matches our wildcard
  305. while ((_tcslen(Param->ExceptionList[i].s) > 0) && !bIsException)
  306. { // Checking to see if it's an exception...
  307. if (IsNameInExpressionPrivate(Param->ExceptionList[i].s,
  308. FileName_l))
  309. {
  310. bIsException = TRUE; // This must be initialized FALSE
  311. }
  312. i += 1;
  313. }
  314. if (!bIsException)
  315. { // Then we actually want to put it in our output
  316. #ifdef ESCPEINF_DEBUG
  317. _ftprintf(stderr, TEXT("Match: %s in %s\n"),
  318. FileName, LayoutInformation->Directory);
  319. #endif
  320. _ftprintf(Param->OutputFile, TEXT("\"%s\\%s\",%d,%s\n"),
  321. Param->RealPath, FileName, Param->Num,
  322. Param->SecurityString);
  323. }
  324. }
  325. }
  326. }
  327. return TRUE;
  328. }
  329. BOOL ProcessFileSecuritySection(FILE *InputFile, FILE *OutputFile,
  330. PLAYOUT_CONTEXT LayoutContext)
  331. {
  332. TCHAR InfLine[MAX_INF_LINE_LENGTH],
  333. FileName[MAX_INF_LINE_LENGTH],
  334. SecurityString[MAX_INF_LINE_LENGTH];
  335. BOOL bValidFields;
  336. DWORD Num;
  337. int i;
  338. EXCEPTIONS ExceptionList[MAX_EXCEPTIONS];
  339. #ifdef ESCPEINF_DEBUG
  340. _ftprintf(stderr, TEXT("Found [File Security] section.\n"));
  341. #endif
  342. while ((_fgetts(InfLine, MAX_INF_LINE_LENGTH, InputFile)) != NULL)
  343. {
  344. if (GetFields(InfLine, FileName, &Num, SecurityString) &&
  345. DoesNameContainWildCards(FileName))
  346. { // We found a line containing proper formatting and a wildcard
  347. // in the filename
  348. #ifdef ESCPEINF_DEBUG
  349. _ftprintf(stderr, TEXT("Wildcard line: %s"), InfLine);
  350. #endif
  351. // First find the exceptions to the wildcard
  352. FindExceptions(InputFile, ExceptionList);
  353. ExpandWildCard(FileName, Num, SecurityString, ExceptionList,
  354. OutputFile, LayoutContext);
  355. }
  356. else
  357. {
  358. _fputts(InfLine, OutputFile);
  359. if (_tcsncmp(InfLine, TEXT("["), 1) == 0)
  360. {
  361. #ifdef ESCPEINF_DEBUG
  362. _ftprintf(stderr, TEXT("End of File Security section.\n"));
  363. #endif
  364. return TRUE;
  365. }
  366. }
  367. }
  368. return TRUE; // No Error
  369. }
  370. // GetFields assumes the input line is in this format:
  371. // "filename",number,"securitystring"\n
  372. // It extracts the filename and stores it in FileName. The quotes are removed
  373. // from filename. It extracts and stores the number and security string if
  374. // the filename is found. Quotes are not stripped from the security string.
  375. // On error, it sets FileName to zero length, and returns FALSE.
  376. BOOL GetFields(PTSTR InfLine, PTSTR FileName, DWORD *Num, PTSTR SecurityString)
  377. {
  378. int i = 0; // Pointer to current location in FileName
  379. // Leave now if the line does not contain a filename
  380. if ((InfLine[0] == (TCHAR)'[') ||
  381. (InfLine[0] == (TCHAR)'\n') ||
  382. (InfLine[0] == (TCHAR)';'))
  383. {
  384. FileName[0] = (TCHAR)'\0'; // Set the end-of-string NULL marker to clear the string
  385. return FALSE;
  386. }
  387. // Check if line starts with a quotation mark
  388. if (InfLine[0] == (TCHAR)'\"')
  389. {
  390. // Copy everything until the next quotation mark
  391. while ((InfLine[i+1] != (TCHAR)'\"') && (InfLine[i+1] != (TCHAR)'\0'))
  392. {
  393. FileName[i] = InfLine[i+1];
  394. i += 1;
  395. }
  396. FileName[i] = (TCHAR)'\0'; // Set the end-of-string NULL marker
  397. i += 1; // So we can use i without the +1 to access InfLine from now on
  398. }
  399. else // if the filename is not in quotes
  400. {
  401. FileName[0] = (TCHAR)'\0'; // Set the end-of-string NULL marker to clear the string
  402. return FALSE;
  403. }
  404. // It is posible that we left the above while loop because of premature end-of-line condtion.
  405. // If this is the case, clear the filename string and return.
  406. if (InfLine[i] == (TCHAR)'\0')
  407. {
  408. #ifdef ESCPEINF_DEBUG
  409. _ftprintf(stderr, TEXT("Reached End-of-Line without finding a filename\n"));
  410. _ftprintf(stderr, TEXT("Problem line is <%s>\n"), InfLine);
  411. _ftprintf(stderr, TEXT("Problem filename is <%s>\n"), FileName);
  412. #endif
  413. FileName[0] = (TCHAR)'\0'; // Set the end-of-string NULL marker to clear the string
  414. return FALSE;
  415. }
  416. else i++; // Once we know that we aren't at the end-of-string, we can do this safely.
  417. // Now, however, we may be pointing to a zero-length string.
  418. if (_tcslen(FileName) <= 3)
  419. {
  420. #ifdef ESCPEINF_DEBUG
  421. _ftprintf(stderr, TEXT("Unexpected Result: Filename \"%s\" is only %d characters.\n"), FileName, i);
  422. #endif
  423. return FALSE;
  424. }
  425. #ifdef ESCPEINF_DEBUG
  426. //_ftprintf(stderr, TEXT("Found filename : %s\n"), FileName);
  427. #endif
  428. // Read the other two fields. If we got this far, we must have found a valid filename,
  429. // so we just assume that the other two are there.
  430. if (_stscanf(&InfLine[i], TEXT(",%ld,%s"), Num, SecurityString) != 2)
  431. { // then there was an error
  432. #ifdef ESCPEINF_DEBUG
  433. _ftprintf(stderr, TEXT("Error reading Num and Security String from line.\n"));
  434. _ftprintf(stderr, TEXT("Problem line is: %s\n"), &InfLine[i]);
  435. #endif
  436. FileName[0] = (TCHAR)'\0'; // Set the end-of-string NULL marker to clear the string
  437. return FALSE;
  438. }
  439. #ifdef ESCPEINF_DEBUG
  440. //_ftprintf(stderr, TEXT("Found rest of line : %lu,%s\n"), *Num, SecurityString);
  441. #endif
  442. return TRUE;
  443. }
  444. void ExpandWildCard(PTSTR FileName, DWORD Num, PTSTR SecurityString,
  445. EXCEPTIONS ExceptionList[], FILE *OutputFile,
  446. PLAYOUT_CONTEXT LayoutContext)
  447. {
  448. MYPARAM Param;
  449. int PathPos = 0;
  450. int NamePos = 0;
  451. int LastSlash = 0;
  452. TCHAR WildCard[MAX_INF_LINE_LENGTH],
  453. RealPath[MAX_INF_LINE_LENGTH],
  454. LayoutPath[MAX_INF_LINE_LENGTH];
  455. while (FileName[NamePos] != (TCHAR)'\0')
  456. {
  457. if (FileName[NamePos] == (TCHAR)'\\')
  458. {
  459. LastSlash = NamePos;
  460. }
  461. NamePos += 1;
  462. }
  463. if (NamePos == (LastSlash + 1))
  464. return; // What? No filename? This should never happen.
  465. _tcsncpy(RealPath, FileName, LastSlash);
  466. RealPath[LastSlash] = (TCHAR)'\0';
  467. _tcscpy(WildCard, &FileName[LastSlash + 1]);
  468. _tcslwr(WildCard);
  469. #ifdef ESCPEINF_DEBUG
  470. _ftprintf(stderr, TEXT("Looking up Wildcard: %s\nin: %s\n"),
  471. WildCard, RealPath);
  472. #endif
  473. if (_tcsnicmp(RealPath, TEXT("%SystemDirectory%"), 17) == 0)
  474. {
  475. _tcscpy(LayoutPath, TEXT("System32"));
  476. _tcscpy(&LayoutPath[8], &RealPath[17]);
  477. }
  478. else if (_tcsnicmp(RealPath, TEXT("%SystemDir%"), 11) == 0)
  479. {
  480. _tcscpy(LayoutPath, TEXT("System32"));
  481. _tcscpy(&LayoutPath[8], &RealPath[11]);
  482. }
  483. else if (_tcsnicmp(RealPath, TEXT("%SystemRoot%"), 12) == 0)
  484. {
  485. if (LastSlash == 12)
  486. _tcscpy(LayoutPath, TEXT("\\"));
  487. else
  488. _tcscpy(LayoutPath, &RealPath[13]);
  489. }
  490. else
  491. {
  492. _ftprintf(stderr, TEXT("Path is unlikely to be in Layout.inf: %s\n"),
  493. RealPath);
  494. _tcscpy(LayoutPath, RealPath);
  495. }
  496. #ifdef ESCPEINF_DEBUG
  497. _ftprintf(stderr, TEXT("Path in Layout.inf terms is: %s\n"), LayoutPath);
  498. #endif
  499. Param.OutputFile = OutputFile;
  500. Param.ExceptionList = ExceptionList;
  501. Param.Num = Num;
  502. Param.WildCard = WildCard;
  503. Param.RealPath = RealPath,
  504. Param.LayoutPath = LayoutPath;
  505. Param.SecurityString= SecurityString;
  506. EnumerateLayoutInf(LayoutContext, MyCallback, (DWORD_PTR)&Param);
  507. return;
  508. }
  509. void FindExceptions(FILE *InputFile, EXCEPTIONS ExceptionList[])
  510. {
  511. long FilePosition;
  512. int NumExceptions = 0,
  513. i;
  514. TCHAR InfLine[MAX_INF_LINE_LENGTH];
  515. do
  516. {
  517. i = 0;
  518. FilePosition = ftell(InputFile); // Save file pointer
  519. if (_fgetts(InfLine, MAX_INF_LINE_LENGTH, InputFile) != NULL)
  520. {
  521. if ((_tcsnicmp(InfLine, TEXT("Exception=\""), 11) == 0) ||
  522. (_tcsnicmp(InfLine, TEXT("Exception:\""), 11) == 0))
  523. {
  524. while ((InfLine[i+11] != (TCHAR)'\"') &&
  525. (InfLine[i+11] != (TCHAR)'\0'))
  526. {
  527. ExceptionList[NumExceptions].s[i] = InfLine[i+11];
  528. i += 1;
  529. }
  530. ExceptionList[NumExceptions].s[i] = (TCHAR)'\0';
  531. _tcslwr(ExceptionList[NumExceptions].s);
  532. #ifdef ESCPEINF_DEBUG
  533. _ftprintf(stderr, TEXT("Exception found: %s\n"),
  534. ExceptionList[NumExceptions].s);
  535. #endif
  536. if (InfLine[i+11] == (TCHAR)'\0')
  537. { // then we hit end of line without closing our quotes
  538. _ftprintf(stderr, TEXT("Warning: Invalid Exception line.\n"));
  539. _ftprintf(stderr, TEXT("Problem line is: %s\n"), InfLine);
  540. }
  541. else NumExceptions += 1;
  542. }
  543. }
  544. }
  545. while (i > 0);
  546. ExceptionList[NumExceptions].s[0] = (TCHAR)'\0';
  547. if (fseek(InputFile, FilePosition, SEEK_SET) != 0) // Restore file pointer
  548. {
  549. _ftprintf(stderr, TEXT("Warning: Cannot seek within INF file! "));
  550. _ftprintf(stderr, TEXT("One line may be lost!\n"), InfLine);
  551. }
  552. // File pointer now points to the first line that is not an Exception line.
  553. return;
  554. }