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.

2096 lines
42 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. inf.c
  5. Abstract:
  6. This module implements functions to access the parsed INF.
  7. Author:
  8. Sunil Pai (sunilp) 13-Nov-1991
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #include <string.h>
  14. #include <ctype.h>
  15. #define ISSPACE(x) (((x) == TEXT(' ')) || ((x) == TEXT('\t')) || ((x) == TEXT('\r')))
  16. #define STRNCPY(s1,s2,n) CopyMemory((s1),(s2),(n)*sizeof(WCHAR))
  17. // what follows was alpar.h
  18. //
  19. // EXPORTED BY THE PARSER AND USED BY BOTH THE PARSER AND
  20. // THE INF HANDLING COMPONENTS
  21. //
  22. // typedefs exported
  23. //
  24. typedef struct _value {
  25. struct _value *pNext;
  26. PTSTR pName;
  27. BOOL IsStringId;
  28. } XVALUE, *PXVALUE;
  29. typedef struct _line {
  30. struct _line *pNext;
  31. PTSTR pName;
  32. PXVALUE pValue;
  33. } LINE, *PLINE;
  34. typedef struct _section {
  35. struct _section *pNext;
  36. PTSTR pName;
  37. PLINE pLine;
  38. } SECTION, *PSECTION;
  39. typedef struct _inf {
  40. PSECTION pSection;
  41. } INF, *PINF;
  42. DWORD
  43. ParseInfBuffer(
  44. PTSTR Buffer,
  45. DWORD Size,
  46. PVOID *Handle
  47. );
  48. //
  49. // DEFINES USED FOR THE PARSER INTERNALLY
  50. //
  51. //
  52. // typedefs used
  53. //
  54. typedef enum _tokentype {
  55. TOK_EOF,
  56. TOK_EOL,
  57. TOK_LBRACE,
  58. TOK_RBRACE,
  59. TOK_STRING,
  60. TOK_STRING_ID,
  61. TOK_EQUAL,
  62. TOK_COMMA,
  63. TOK_ERRPARSE,
  64. TOK_ERRNOMEM
  65. } TOKENTYPE, *PTOKENTTYPE;
  66. typedef struct _token {
  67. TOKENTYPE Type;
  68. PTSTR pValue;
  69. } TOKEN, *PTOKEN;
  70. //
  71. // Routine defines
  72. //
  73. DWORD
  74. DnAppendSection(
  75. IN PTSTR pSectionName
  76. );
  77. DWORD
  78. DnAppendLine(
  79. IN PTSTR pLineKey
  80. );
  81. DWORD
  82. DnAppendValue(
  83. IN PTSTR pValueString,
  84. IN BOOL IsStringId
  85. );
  86. TOKEN
  87. DnGetToken(
  88. IN OUT PTSTR *Stream,
  89. IN PTSTR MaxStream
  90. );
  91. BOOL
  92. IsStringTerminator(
  93. IN TCHAR ch
  94. );
  95. BOOL
  96. IsQStringTerminator(
  97. IN TCHAR ch,
  98. IN TCHAR term
  99. );
  100. // what follows was alinf.c
  101. //
  102. // Internal Routine Declarations for freeing inf structure members
  103. //
  104. VOID
  105. FreeSectionList (
  106. IN PSECTION pSection
  107. );
  108. VOID
  109. FreeLineList (
  110. IN PLINE pLine
  111. );
  112. VOID
  113. FreeValueList (
  114. IN PXVALUE pValue
  115. );
  116. //
  117. // Internal Routine declarations for searching in the INF structures
  118. //
  119. PXVALUE
  120. SearchValueInLine(
  121. IN PLINE pLine,
  122. IN unsigned ValueIndex
  123. );
  124. PLINE
  125. SearchLineInSectionByKey(
  126. IN PSECTION pSection,
  127. IN LPCTSTR Key
  128. );
  129. PLINE
  130. SearchLineInSectionByIndex(
  131. IN PSECTION pSection,
  132. IN unsigned LineIndex
  133. );
  134. PSECTION
  135. SearchSectionByName(
  136. IN PINF pINF,
  137. IN LPCTSTR SectionName
  138. );
  139. BOOL
  140. ProcessStringSection(
  141. PINF pINF
  142. );
  143. DWORD
  144. LoadInfFile(
  145. IN LPCTSTR Filename,
  146. IN BOOL OemCodepage,
  147. OUT PVOID *InfHandle
  148. )
  149. /*++
  150. Routine Description:
  151. Arguments:
  152. Filename - supplies win32 filename of inf file to be loaded.
  153. OemCodepage - if TRUE amd the file named by Filename is not
  154. Unicode text, then the file is assumed to be in the OEM
  155. codepage (otherwise it's in the ANSI codepage).
  156. InfHandle - if successful, receives a handle to be used with
  157. subsequent inf operations.
  158. Return Value:
  159. ERROR_FILE_NOT_FOUND - file does not exist or error opening it.
  160. ERROR_INVALID_DATA - syntax error in inf file.
  161. ERROR_READ_FAULT - unable to read file.
  162. ERROR_NOT_ENOUGH_MEMORY - mem alloc failed
  163. NO_ERROR - file read and parsed.
  164. --*/
  165. {
  166. DWORD err;
  167. DWORD FileSize;
  168. HANDLE FileHandle;
  169. HANDLE MappingHandle;
  170. PVOID BaseAddress;
  171. BOOL IsUnicode;
  172. DWORD ParseCount;
  173. PVOID ParseBuffer;
  174. //
  175. // Open and map the inf file.
  176. //
  177. err = MapFileForRead(Filename,&FileSize,&FileHandle,&MappingHandle,&BaseAddress);
  178. if(err != NO_ERROR) {
  179. err = ERROR_FILE_NOT_FOUND;
  180. goto c0;
  181. }
  182. //
  183. // Determine whether the file is unicode. If it's got the byte order mark
  184. // then it's unicode, otherwise call the IsTextUnicode API. We do it this way
  185. // because IsTextUnicode always returns FALSE on Win95 so we need to break out
  186. // the BOM to detect Unicode files on Win95.
  187. //
  188. if((FileSize >= sizeof(WCHAR)) && (*(PWCHAR)BaseAddress == 0xfeff)) {
  189. IsUnicode = 2;
  190. } else {
  191. IsUnicode = IsTextUnicode(BaseAddress,FileSize,NULL) ? 1 : 0;
  192. }
  193. #ifdef UNICODE
  194. if(IsUnicode) {
  195. //
  196. // Copy into local buffer, skipping BOM if necessary.
  197. //
  198. ParseBuffer = MALLOC(FileSize);
  199. if(!ParseBuffer) {
  200. err = ERROR_NOT_ENOUGH_MEMORY;
  201. goto c1;
  202. }
  203. try {
  204. CopyMemory(
  205. ParseBuffer,
  206. (PUCHAR)BaseAddress + ((IsUnicode == 2) ? sizeof(WCHAR) : 0),
  207. FileSize - ((IsUnicode == 2) ? sizeof(WCHAR) : 0)
  208. );
  209. } except(EXCEPTION_EXECUTE_HANDLER) {
  210. err = ERROR_READ_FAULT;
  211. }
  212. ParseCount = (FileSize / sizeof(WCHAR)) - ((IsUnicode == 2) ? 1 : 0);
  213. } else {
  214. //
  215. // Convert to Unicode.
  216. //
  217. // Allocate a buffer large enough to hold the maximum sized unicode
  218. // equivalent of the multibyte text. This size occurs when all chars
  219. // in the file are single-byte and thus double in size when converted.
  220. //
  221. ParseBuffer = MALLOC(FileSize * sizeof(WCHAR));
  222. if(!ParseBuffer) {
  223. err = ERROR_NOT_ENOUGH_MEMORY;
  224. goto c1;
  225. }
  226. try {
  227. ParseCount = MultiByteToWideChar(
  228. OemCodepage ? CP_OEMCP : CP_ACP,
  229. MB_PRECOMPOSED,
  230. BaseAddress,
  231. FileSize,
  232. ParseBuffer,
  233. FileSize
  234. );
  235. if(!ParseCount) {
  236. //
  237. // Assume inpage i/o error
  238. //
  239. err = ERROR_READ_FAULT;
  240. }
  241. } except(EXCEPTION_EXECUTE_HANDLER) {
  242. err = ERROR_READ_FAULT;
  243. }
  244. }
  245. #else
  246. if(IsUnicode) {
  247. //
  248. // Text is unicode but internal routines want ansi. Convert here.
  249. //
  250. // Maximum required buffer is when each unicode char ends up as
  251. // a double-byte char.
  252. //
  253. ParseBuffer = MALLOC(FileSize);
  254. if(!ParseBuffer) {
  255. err = ERROR_NOT_ENOUGH_MEMORY;
  256. goto c1;
  257. }
  258. try {
  259. ParseCount = WideCharToMultiByte(
  260. CP_ACP,
  261. 0,
  262. (PWCHAR)BaseAddress + ((IsUnicode == 2) ? 1 : 0),
  263. (FileSize / sizeof(WCHAR)) - ((IsUnicode == 2) ? 1 : 0),
  264. ParseBuffer,
  265. FileSize,
  266. NULL,
  267. NULL
  268. );
  269. if(!ParseCount) {
  270. //
  271. // Assume inpage i/o error
  272. //
  273. err = ERROR_READ_FAULT;
  274. }
  275. } except(EXCEPTION_EXECUTE_HANDLER) {
  276. err = ERROR_READ_FAULT;
  277. }
  278. } else {
  279. //
  280. // Text is not unicode. It might be OEM though and could thus still
  281. // require translation.
  282. //
  283. ParseCount = FileSize;
  284. ParseBuffer = MALLOC(FileSize);
  285. if(!ParseBuffer) {
  286. err = ERROR_NOT_ENOUGH_MEMORY;
  287. goto c1;
  288. }
  289. try {
  290. CopyMemory(ParseBuffer,BaseAddress,FileSize);
  291. } except(EXCEPTION_EXECUTE_HANDLER) {
  292. err = ERROR_READ_FAULT;
  293. }
  294. if(err != NO_ERROR) {
  295. goto c2;
  296. }
  297. if(OemCodepage && (GetOEMCP() != GetACP())) {
  298. OemToCharBuff(ParseBuffer,ParseBuffer,ParseCount);
  299. }
  300. }
  301. #endif
  302. if(err != NO_ERROR) {
  303. goto c2;
  304. }
  305. err = ParseInfBuffer(ParseBuffer,ParseCount,InfHandle);
  306. c2:
  307. FREE(ParseBuffer);
  308. c1:
  309. UnmapFile(MappingHandle,BaseAddress);
  310. CloseHandle(FileHandle);
  311. c0:
  312. return(err);
  313. }
  314. VOID
  315. UnloadInfFile(
  316. IN PVOID InfHandle
  317. )
  318. /*++
  319. Routine Description:
  320. Unload a file previously loaded by LoadInfFile().
  321. Arguments:
  322. InfHandle - supplies a habdle previously returned by a successful
  323. call to LoadInfFile().
  324. Return Value:
  325. None.
  326. --*/
  327. {
  328. PINF pINF;
  329. pINF = InfHandle;
  330. FreeSectionList(pINF->pSection);
  331. FREE(pINF);
  332. }
  333. VOID
  334. FreeSectionList (
  335. IN PSECTION pSection
  336. )
  337. /*++
  338. Routine Description:
  339. Arguments:
  340. Return Value:
  341. --*/
  342. {
  343. PSECTION Next;
  344. while(pSection) {
  345. Next = pSection->pNext;
  346. FreeLineList(pSection->pLine);
  347. if(pSection->pName) {
  348. FREE(pSection->pName);
  349. }
  350. FREE(pSection);
  351. pSection = Next;
  352. }
  353. }
  354. VOID
  355. FreeLineList(
  356. IN PLINE pLine
  357. )
  358. /*++
  359. Routine Description:
  360. Arguments:
  361. Return Value:
  362. --*/
  363. {
  364. PLINE Next;
  365. while(pLine) {
  366. Next = pLine->pNext;
  367. FreeValueList(pLine->pValue);
  368. if(pLine->pName) {
  369. FREE(pLine->pName);
  370. }
  371. FREE(pLine);
  372. pLine = Next;
  373. }
  374. }
  375. VOID
  376. FreeValueList (
  377. IN PXVALUE pValue
  378. )
  379. /*++
  380. Routine Description:
  381. Arguments:
  382. Return Value:
  383. --*/
  384. {
  385. PXVALUE Next;
  386. while(pValue) {
  387. Next = pValue->pNext;
  388. if(pValue->pName) {
  389. FREE(pValue->pName);
  390. }
  391. FREE(pValue);
  392. pValue = Next;
  393. }
  394. }
  395. //
  396. // searches for the existance of a particular section,
  397. // returns line count (-1 if not found)
  398. //
  399. LONG
  400. InfGetSectionLineCount(
  401. IN PVOID INFHandle,
  402. IN PTSTR SectionName
  403. )
  404. /*++
  405. Routine Description:
  406. Arguments:
  407. Return Value:
  408. --*/
  409. {
  410. PSECTION pSection;
  411. PLINE pLine;
  412. LONG count;
  413. //
  414. // if search for section fails return failure
  415. //
  416. if ((pSection = SearchSectionByName(INFHandle,SectionName)) == NULL) {
  417. return(-1);
  418. }
  419. for(count=0,pLine=pSection->pLine; pLine; pLine=pLine->pNext) {
  420. count++;
  421. }
  422. return(count);
  423. }
  424. //
  425. // given section name, line number and index return the value.
  426. //
  427. LPCTSTR
  428. InfGetFieldByIndex(
  429. IN PVOID INFHandle,
  430. IN LPCTSTR SectionName,
  431. IN unsigned LineIndex,
  432. IN unsigned ValueIndex
  433. )
  434. /*++
  435. Routine Description:
  436. Arguments:
  437. Return Value:
  438. --*/
  439. {
  440. PSECTION pSection;
  441. PLINE pLine;
  442. PXVALUE pValue;
  443. if((pSection = SearchSectionByName(
  444. (PINF)INFHandle,
  445. SectionName
  446. ))
  447. == NULL)
  448. return(NULL);
  449. if((pLine = SearchLineInSectionByIndex(
  450. pSection,
  451. LineIndex
  452. ))
  453. == NULL)
  454. return(NULL);
  455. if((pValue = SearchValueInLine(
  456. pLine,
  457. ValueIndex
  458. ))
  459. == NULL)
  460. return(NULL);
  461. return (pValue->pName);
  462. }
  463. BOOL
  464. InfDoesLineExistInSection(
  465. IN PVOID INFHandle,
  466. IN LPCTSTR SectionName,
  467. IN LPCTSTR Key
  468. )
  469. /*++
  470. Routine Description:
  471. Arguments:
  472. Return Value:
  473. --*/
  474. {
  475. PSECTION pSection;
  476. if((pSection = SearchSectionByName(
  477. (PINF)INFHandle,
  478. SectionName
  479. ))
  480. == NULL) {
  481. return( FALSE );
  482. }
  483. if (SearchLineInSectionByKey(pSection, Key) == NULL) {
  484. return( FALSE );
  485. }
  486. return( TRUE );
  487. }
  488. BOOL
  489. InfDoesEntryExistInSection (
  490. IN PVOID INFHandle,
  491. IN LPCTSTR SectionName,
  492. IN LPCTSTR Entry
  493. )
  494. /*++
  495. Routine Description:
  496. Arguments:
  497. Return Value:
  498. --*/
  499. {
  500. PSECTION pSection;
  501. PLINE pLine;
  502. PXVALUE pValue;
  503. PCTSTR pEntryName;
  504. if((pSection = SearchSectionByName(
  505. (PINF)INFHandle,
  506. SectionName
  507. ))
  508. == NULL) {
  509. return( FALSE );
  510. }
  511. pLine = pSection->pLine;
  512. while (pLine) {
  513. pEntryName = pLine->pName ?
  514. pLine->pName :
  515. pLine->pValue ?
  516. pLine->pValue->pName :
  517. NULL;
  518. if (pEntryName && !lstrcmpi (pEntryName, Entry)) {
  519. return TRUE;
  520. }
  521. pLine = pLine->pNext;
  522. }
  523. return FALSE;
  524. }
  525. LPCTSTR
  526. InfGetLineKeyName(
  527. IN PVOID INFHandle,
  528. IN LPCTSTR SectionName,
  529. IN unsigned LineIndex
  530. )
  531. {
  532. PSECTION pSection;
  533. PLINE pLine;
  534. pSection = SearchSectionByName((PINF)INFHandle,SectionName);
  535. if(pSection == NULL) {
  536. return(NULL);
  537. }
  538. pLine = SearchLineInSectionByIndex(pSection,LineIndex);
  539. if(pLine == NULL) {
  540. return(NULL);
  541. }
  542. return(pLine->pName);
  543. }
  544. //
  545. // given section name, key and index return the value
  546. //
  547. LPCTSTR
  548. InfGetFieldByKey(
  549. IN PVOID INFHandle,
  550. IN LPCTSTR SectionName,
  551. IN LPCTSTR Key,
  552. IN unsigned ValueIndex
  553. )
  554. /*++
  555. Routine Description:
  556. Arguments:
  557. Return Value:
  558. --*/
  559. {
  560. PSECTION pSection;
  561. PLINE pLine;
  562. PXVALUE pValue;
  563. if((pSection = SearchSectionByName(
  564. (PINF)INFHandle,
  565. SectionName
  566. ))
  567. == NULL)
  568. return(NULL);
  569. if((pLine = SearchLineInSectionByKey(
  570. pSection,
  571. Key
  572. ))
  573. == NULL)
  574. return(NULL);
  575. if((pValue = SearchValueInLine(
  576. pLine,
  577. ValueIndex
  578. ))
  579. == NULL)
  580. return(NULL);
  581. return (pValue->pName);
  582. }
  583. PXVALUE
  584. SearchValueInLine(
  585. IN PLINE pLine,
  586. IN unsigned ValueIndex
  587. )
  588. /*++
  589. Routine Description:
  590. Arguments:
  591. Return Value:
  592. --*/
  593. {
  594. PXVALUE pValue;
  595. unsigned i;
  596. if (pLine == NULL)
  597. return (NULL);
  598. pValue = pLine->pValue;
  599. for (i = 0; (i < ValueIndex) && (pValue = pValue->pNext); i++)
  600. ;
  601. return pValue;
  602. }
  603. PLINE
  604. SearchLineInSectionByKey(
  605. IN PSECTION pSection,
  606. IN LPCTSTR Key
  607. )
  608. /*++
  609. Routine Description:
  610. Arguments:
  611. Return Value:
  612. --*/
  613. {
  614. PLINE pLine;
  615. if (pSection == NULL || Key == NULL) {
  616. return (NULL);
  617. }
  618. pLine = pSection->pLine;
  619. while(pLine && ((pLine->pName == NULL) || lstrcmpi(pLine->pName, Key))) {
  620. pLine = pLine->pNext;
  621. }
  622. return pLine;
  623. }
  624. PLINE
  625. SearchLineInSectionByIndex(
  626. IN PSECTION pSection,
  627. IN unsigned LineIndex
  628. )
  629. /*++
  630. Routine Description:
  631. Arguments:
  632. Return Value:
  633. --*/
  634. {
  635. PLINE pLine;
  636. unsigned i;
  637. //
  638. // Validate the parameters passed in
  639. //
  640. if(pSection == NULL) {
  641. return (NULL);
  642. }
  643. //
  644. // find the start of the line list in the section passed in
  645. //
  646. pLine = pSection->pLine;
  647. //
  648. // traverse down the current line list to the LineIndex th line
  649. //
  650. for (i = 0; (i < LineIndex) && (pLine = pLine->pNext); i++) {
  651. ;
  652. }
  653. //
  654. // return the Line found
  655. //
  656. return pLine;
  657. }
  658. PSECTION
  659. SearchSectionByName(
  660. IN PINF pINF,
  661. IN LPCTSTR SectionName
  662. )
  663. /*++
  664. Routine Description:
  665. Arguments:
  666. Return Value:
  667. --*/
  668. {
  669. PSECTION pSection;
  670. //
  671. // validate the parameters passed in
  672. //
  673. if (pINF == NULL || SectionName == NULL) {
  674. return (NULL);
  675. }
  676. //
  677. // find the section list
  678. //
  679. pSection = pINF->pSection;
  680. //
  681. // traverse down the section list searching each section for the section
  682. // name mentioned
  683. //
  684. while (pSection && lstrcmpi(pSection->pName, SectionName)) {
  685. pSection = pSection->pNext;
  686. }
  687. //
  688. // return the section at which we stopped (either NULL or the section
  689. // which was found
  690. //
  691. return pSection;
  692. }
  693. // what follows was alparse.c
  694. //
  695. // Globals used to make building the lists easier
  696. //
  697. PINF pINF;
  698. PSECTION pSectionRecord;
  699. PLINE pLineRecord;
  700. PXVALUE pValueRecord;
  701. //
  702. // Globals used by the token parser
  703. //
  704. // string terminators are the whitespace characters (isspace: space, tab,
  705. // linefeed, formfeed, vertical tab, carriage return) or the chars given below
  706. TCHAR StringTerminators[] = { TEXT('['),
  707. TEXT(']'),
  708. TEXT('='),
  709. TEXT(','),
  710. TEXT('\"'),
  711. TEXT(' '),
  712. TEXT('\t'),
  713. TEXT('\n'),
  714. TEXT('\f'),
  715. TEXT('\v'),
  716. TEXT('\r'),
  717. TEXT('\032')
  718. };
  719. unsigned NumberOfTerminators = sizeof(StringTerminators)/sizeof(TCHAR);
  720. //
  721. // quoted string terminators allow some of the regular terminators to
  722. // appear as characters
  723. TCHAR QStringTerminators[] = { TEXT('\n'),
  724. TEXT('\f'),
  725. TEXT('\v'),
  726. TEXT('\r'),
  727. TEXT('\032')
  728. };
  729. unsigned QNumberOfTerminators = sizeof(QStringTerminators)/sizeof(TCHAR);
  730. //
  731. // Main parser routine
  732. //
  733. DWORD
  734. ParseInfBuffer(
  735. PTSTR Buffer,
  736. DWORD Size,
  737. PVOID *Handle
  738. )
  739. /*++
  740. Routine Description:
  741. Given a character buffer containing the INF file, this routine parses
  742. the INF into an internal form with Section records, Line records and
  743. Value records.
  744. If this module is compiler for unicode, the input is assumed to be
  745. a bufferful of unicode characters.
  746. Arguments:
  747. Buffer - contains to ptr to a buffer containing the INF file
  748. Size - contains the size of the buffer in characters.
  749. Handle - receives INF handle ptr to be used in subsequent INF calls.
  750. Return Value:
  751. Win32 error code indicating outcome. One of NO_ERROR, ERROR_INVALID_DATA,
  752. or ERROR_NOT_ENOUGH_MEMORY.
  753. --*/
  754. {
  755. LPTSTR Stream, MaxStream, pchSectionName, pchValue;
  756. unsigned State, InfLine;
  757. TOKEN Token;
  758. BOOL Done;
  759. BOOL Error;
  760. DWORD ErrorCode;
  761. BOOL IsStringId;
  762. //
  763. // Initialise the globals
  764. //
  765. pINF = NULL;
  766. pSectionRecord = NULL;
  767. pLineRecord = NULL;
  768. pValueRecord = NULL;
  769. //
  770. // Get INF record
  771. //
  772. pINF = MALLOC(sizeof(INF));
  773. if(!pINF) {
  774. return(ERROR_NOT_ENOUGH_MEMORY);
  775. }
  776. pINF->pSection = NULL;
  777. //
  778. // Set initial state
  779. //
  780. State = 1;
  781. InfLine = 1;
  782. Stream = Buffer;
  783. MaxStream = Buffer + Size;
  784. Done = FALSE;
  785. Error = FALSE;
  786. ErrorCode = NO_ERROR;
  787. IsStringId = FALSE;
  788. pchSectionName = NULL;
  789. pchValue = NULL;
  790. //
  791. // Enter token processing loop
  792. //
  793. if (Size == 0)
  794. return ERROR_INVALID_DATA;
  795. while (!Done) {
  796. Token = DnGetToken(&Stream, MaxStream);
  797. if(Token.Type == TOK_ERRNOMEM){
  798. Error = Done = TRUE;
  799. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  800. break;
  801. }
  802. switch (State) {
  803. //
  804. // STATE1: Start of file, this state remains till first
  805. // section is found
  806. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_LBRACE
  807. case 1:
  808. switch (Token.Type) {
  809. case TOK_EOL:
  810. break;
  811. case TOK_EOF:
  812. Done = TRUE;
  813. break;
  814. case TOK_LBRACE:
  815. State = 2;
  816. break;
  817. default:
  818. Error = Done = TRUE;
  819. ErrorCode = ERROR_INVALID_DATA;
  820. break;
  821. }
  822. break;
  823. //
  824. // STATE 2: Section LBRACE has been received, expecting STRING
  825. //
  826. // Valid Tokens: TOK_STRING
  827. //
  828. case 2:
  829. switch (Token.Type) {
  830. case TOK_STRING:
  831. State = 3;
  832. pchSectionName = Token.pValue;
  833. break;
  834. default:
  835. Error = Done = TRUE;
  836. ErrorCode = ERROR_INVALID_DATA;
  837. break;
  838. }
  839. break;
  840. //
  841. // STATE 3: Section Name received, expecting RBRACE
  842. //
  843. // Valid Tokens: TOK_RBRACE
  844. //
  845. case 3:
  846. switch (Token.Type) {
  847. case TOK_RBRACE:
  848. State = 4;
  849. break;
  850. default:
  851. Error = Done = TRUE;
  852. ErrorCode = ERROR_INVALID_DATA;
  853. break;
  854. }
  855. break;
  856. //
  857. // STATE 4: Section Definition Complete, expecting EOL
  858. //
  859. // Valid Tokens: TOK_EOL, TOK_EOF
  860. //
  861. case 4:
  862. switch (Token.Type) {
  863. case TOK_EOL:
  864. if ((ErrorCode = DnAppendSection(pchSectionName)) != NO_ERROR)
  865. Error = Done = TRUE;
  866. else {
  867. pchSectionName = NULL;
  868. State = 5;
  869. }
  870. break;
  871. case TOK_EOF:
  872. if ((ErrorCode = DnAppendSection(pchSectionName)) != NO_ERROR)
  873. Error = Done = TRUE;
  874. else {
  875. pchSectionName = NULL;
  876. Done = TRUE;
  877. }
  878. break;
  879. default:
  880. Error = Done = TRUE;
  881. ErrorCode = ERROR_INVALID_DATA;
  882. break;
  883. }
  884. break;
  885. //
  886. // STATE 5: Expecting Section Lines
  887. //
  888. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_STRING, TOK_LBRACE
  889. //
  890. case 5:
  891. switch (Token.Type) {
  892. case TOK_EOL:
  893. break;
  894. case TOK_EOF:
  895. Done = TRUE;
  896. break;
  897. case TOK_STRING_ID:
  898. IsStringId = TRUE;
  899. case TOK_STRING:
  900. pchValue = Token.pValue;
  901. State = 6;
  902. break;
  903. case TOK_LBRACE:
  904. State = 2;
  905. break;
  906. default:
  907. Error = Done = TRUE;
  908. ErrorCode = ERROR_INVALID_DATA;
  909. break;
  910. }
  911. break;
  912. //
  913. // STATE 6: String returned, not sure whether it is key or value
  914. //
  915. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA, TOK_EQUAL
  916. //
  917. case 6:
  918. switch (Token.Type) {
  919. case TOK_EOL:
  920. if ( (ErrorCode = DnAppendLine(NULL)) != NO_ERROR ||
  921. (ErrorCode = DnAppendValue(pchValue,IsStringId)) !=NO_ERROR ) {
  922. Error = Done = TRUE;
  923. } else {
  924. pchValue = NULL;
  925. State = 5;
  926. }
  927. break;
  928. case TOK_EOF:
  929. if ( (ErrorCode = DnAppendLine(NULL)) != NO_ERROR ||
  930. (ErrorCode = DnAppendValue(pchValue,IsStringId)) !=NO_ERROR ) {
  931. Error = Done = TRUE;
  932. } else {
  933. pchValue = NULL;
  934. Done = TRUE;
  935. }
  936. break;
  937. case TOK_COMMA:
  938. if ( (ErrorCode = DnAppendLine(NULL)) != NO_ERROR ||
  939. (ErrorCode = DnAppendValue(pchValue,IsStringId)) !=NO_ERROR ) {
  940. Error = Done = TRUE;
  941. } else {
  942. pchValue = NULL;
  943. State = 7;
  944. }
  945. break;
  946. case TOK_EQUAL:
  947. if ( (ErrorCode = DnAppendLine(pchValue)) !=NO_ERROR)
  948. Error = Done = TRUE;
  949. else {
  950. pchValue = NULL;
  951. State = 8;
  952. }
  953. break;
  954. default:
  955. Error = Done = TRUE;
  956. ErrorCode = ERROR_INVALID_DATA;
  957. break;
  958. }
  959. IsStringId = FALSE;
  960. break;
  961. //
  962. // STATE 7: Comma received, Expecting another string
  963. //
  964. // Valid Tokens: TOK_STRING, TOK_EOL, TOK_COMMA
  965. //
  966. case 7:
  967. switch (Token.Type) {
  968. case TOK_EOL:
  969. //
  970. // this is the end of the line, after a comma
  971. //
  972. State = 5;
  973. //
  974. // fall through
  975. //
  976. case TOK_COMMA:
  977. Token.pValue = DupString(TEXT(""));
  978. if(!Token.pValue) {
  979. Error = Done = TRUE;
  980. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  981. } else {
  982. ErrorCode = DnAppendValue(Token.pValue,FALSE);
  983. if(ErrorCode != NO_ERROR) {
  984. Error = Done = TRUE;
  985. }
  986. }
  987. break;
  988. case TOK_STRING_ID:
  989. IsStringId = TRUE;
  990. case TOK_STRING:
  991. if ((ErrorCode = DnAppendValue(Token.pValue,IsStringId)) != NO_ERROR) {
  992. Error = Done = TRUE;
  993. } else {
  994. State = 9;
  995. }
  996. IsStringId = FALSE;
  997. break;
  998. default:
  999. Error = Done = TRUE;
  1000. ErrorCode = ERROR_INVALID_DATA;
  1001. break;
  1002. }
  1003. break;
  1004. //
  1005. // STATE 8: Equal received, Expecting another string
  1006. //
  1007. // Valid Tokens: TOK_STRING TOK_EOL, TOK_EOF
  1008. //
  1009. case 8:
  1010. switch (Token.Type) {
  1011. case TOK_STRING_ID:
  1012. IsStringId = TRUE;
  1013. case TOK_STRING:
  1014. if ((ErrorCode = DnAppendValue(Token.pValue,IsStringId)) != NO_ERROR) {
  1015. Error = Done = TRUE;
  1016. } else {
  1017. State = 9;
  1018. }
  1019. IsStringId = FALSE;
  1020. break;
  1021. case TOK_EOL:
  1022. case TOK_EOF:
  1023. Token.pValue = DupString(TEXT(""));
  1024. if(!Token.pValue) {
  1025. Error = Done = TRUE;
  1026. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  1027. } else {
  1028. if ((ErrorCode = DnAppendValue(Token.pValue,FALSE)) != NO_ERROR) {
  1029. Error = Done = TRUE;
  1030. } else {
  1031. State = 5;
  1032. }
  1033. }
  1034. IsStringId = FALSE;
  1035. break;
  1036. default:
  1037. Error = Done = TRUE;
  1038. ErrorCode = ERROR_INVALID_DATA;
  1039. break;
  1040. }
  1041. break;
  1042. //
  1043. // STATE 9: String received after equal, value string
  1044. //
  1045. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA
  1046. //
  1047. case 9:
  1048. switch (Token.Type) {
  1049. case TOK_EOL:
  1050. State = 5;
  1051. break;
  1052. case TOK_EOF:
  1053. Done = TRUE;
  1054. break;
  1055. case TOK_COMMA:
  1056. State = 7;
  1057. break;
  1058. default:
  1059. Error = Done = TRUE;
  1060. ErrorCode = ERROR_INVALID_DATA;
  1061. break;
  1062. }
  1063. break;
  1064. //
  1065. // STATE 10: Value string definitely received
  1066. //
  1067. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA
  1068. //
  1069. case 10:
  1070. switch (Token.Type) {
  1071. case TOK_EOL:
  1072. State =5;
  1073. break;
  1074. case TOK_EOF:
  1075. Done = TRUE;
  1076. break;
  1077. case TOK_COMMA:
  1078. State = 7;
  1079. break;
  1080. default:
  1081. Error = Done = TRUE;
  1082. ErrorCode = ERROR_INVALID_DATA;
  1083. break;
  1084. }
  1085. break;
  1086. default:
  1087. Error = Done = TRUE;
  1088. ErrorCode = ERROR_INVALID_DATA;
  1089. break;
  1090. } // end switch(State)
  1091. if (Error) {
  1092. UnloadInfFile(pINF);
  1093. if(pchSectionName) {
  1094. FREE(pchSectionName);
  1095. }
  1096. if(pchValue) {
  1097. FREE(pchValue);
  1098. }
  1099. pINF = NULL;
  1100. }
  1101. else {
  1102. //
  1103. // Keep track of line numbers so that we can display Errors
  1104. //
  1105. if (Token.Type == TOK_EOL)
  1106. InfLine++;
  1107. }
  1108. } // End while
  1109. if(!Error) {
  1110. ProcessStringSection( pINF );
  1111. *Handle = pINF;
  1112. }
  1113. return(Error ? ErrorCode : NO_ERROR);
  1114. }
  1115. DWORD
  1116. DnAppendSection(
  1117. IN PTSTR pSectionName
  1118. )
  1119. /*++
  1120. Routine Description:
  1121. This appends a new section to the section list in the current INF.
  1122. All further lines and values pertain to this new section, so it resets
  1123. the line list and value lists too.
  1124. Arguments:
  1125. pSectionName - Name of the new section. ( [SectionName] )
  1126. Return Value:
  1127. NO_ERROR - if successful.
  1128. ERROR_INVALID_DATA - if invalid parameters passed in or the INF buffer not
  1129. initialised
  1130. --*/
  1131. {
  1132. PSECTION pNewSection;
  1133. //
  1134. // See if we already have a section by this name. If so we want
  1135. // to merge sections.
  1136. //
  1137. for(pNewSection=pINF->pSection; pNewSection; pNewSection=pNewSection->pNext) {
  1138. if(pNewSection->pName && !lstrcmpi(pNewSection->pName,pSectionName)) {
  1139. break;
  1140. }
  1141. }
  1142. if(pNewSection) {
  1143. //
  1144. // Set pLineRecord to point to the list line currently in the section.
  1145. //
  1146. for(pLineRecord = pNewSection->pLine;
  1147. pLineRecord && pLineRecord->pNext;
  1148. pLineRecord = pLineRecord->pNext)
  1149. ;
  1150. } else {
  1151. //
  1152. // Allocate memory for the new section
  1153. //
  1154. pNewSection = MALLOC(sizeof(SECTION));
  1155. if(!pNewSection) {
  1156. return(ERROR_NOT_ENOUGH_MEMORY);
  1157. }
  1158. //
  1159. // initialise the new section
  1160. //
  1161. pNewSection->pNext = NULL;
  1162. pNewSection->pLine = NULL;
  1163. pNewSection->pName = pSectionName;
  1164. //
  1165. // link it in
  1166. //
  1167. pNewSection->pNext = pINF->pSection;
  1168. pINF->pSection = pNewSection;
  1169. //
  1170. // reset the current line record
  1171. //
  1172. pLineRecord = NULL;
  1173. }
  1174. pSectionRecord = pNewSection;
  1175. pValueRecord = NULL;
  1176. return NO_ERROR;
  1177. }
  1178. DWORD
  1179. DnAppendLine(
  1180. IN PTSTR pLineKey
  1181. )
  1182. /*++
  1183. Routine Description:
  1184. This appends a new line to the line list in the current section.
  1185. All further values pertain to this new line, so it resets
  1186. the value list too.
  1187. Arguments:
  1188. pLineKey - Key to be used for the current line, this could be NULL.
  1189. Return Value:
  1190. NO_ERROR - if successful.
  1191. ERROR_INVALID_DATA - if invalid parameters passed in or current section not
  1192. initialised
  1193. --*/
  1194. {
  1195. PLINE pNewLine;
  1196. //
  1197. // Allocate memory for the new Line
  1198. //
  1199. pNewLine = MALLOC(sizeof(LINE));
  1200. if(!pNewLine) {
  1201. return(ERROR_NOT_ENOUGH_MEMORY);
  1202. }
  1203. //
  1204. // Link it in
  1205. //
  1206. pNewLine->pNext = NULL;
  1207. pNewLine->pValue = NULL;
  1208. pNewLine->pName = pLineKey;
  1209. if (pLineRecord == NULL) {
  1210. pSectionRecord->pLine = pNewLine;
  1211. }
  1212. else {
  1213. pLineRecord->pNext = pNewLine;
  1214. }
  1215. pLineRecord = pNewLine;
  1216. //
  1217. // Reset the current value record
  1218. //
  1219. pValueRecord = NULL;
  1220. return NO_ERROR;
  1221. }
  1222. DWORD
  1223. DnAppendValue(
  1224. IN PTSTR pValueString,
  1225. IN BOOL IsStringId
  1226. )
  1227. /*++
  1228. Routine Description:
  1229. This appends a new value to the value list in the current line.
  1230. Arguments:
  1231. pValueString - The value string to be added.
  1232. Return Value:
  1233. NO_ERROR - if successful.
  1234. ERROR_INVALID_DATA - if invalid parameters passed in or current line not
  1235. initialised.
  1236. --*/
  1237. {
  1238. PXVALUE pNewValue;
  1239. //
  1240. // Allocate memory for the new value record
  1241. //
  1242. pNewValue = MALLOC(sizeof(XVALUE));
  1243. if(!pNewValue) {
  1244. return(ERROR_NOT_ENOUGH_MEMORY);
  1245. }
  1246. //
  1247. // Link it in.
  1248. //
  1249. pNewValue->pNext = NULL;
  1250. pNewValue->pName = pValueString;
  1251. pNewValue->IsStringId = IsStringId;
  1252. if (pValueRecord == NULL)
  1253. pLineRecord->pValue = pNewValue;
  1254. else
  1255. pValueRecord->pNext = pNewValue;
  1256. pValueRecord = pNewValue;
  1257. return NO_ERROR;
  1258. }
  1259. TOKEN
  1260. DnGetToken(
  1261. IN OUT PTSTR *Stream,
  1262. IN PTSTR MaxStream
  1263. )
  1264. /*++
  1265. Routine Description:
  1266. This function returns the Next token from the configuration stream.
  1267. Arguments:
  1268. Stream - Supplies the address of the configuration stream. Returns
  1269. the address of where to start looking for tokens within the
  1270. stream.
  1271. MaxStream - Supplies the address of the last character in the stream.
  1272. Return Value:
  1273. TOKEN - Returns the next token
  1274. --*/
  1275. {
  1276. PTSTR pch, pchStart, pchNew;
  1277. unsigned Length;
  1278. TOKEN Token;
  1279. //
  1280. // Skip whitespace (except for eol)
  1281. //
  1282. pch = *Stream;
  1283. while (pch < MaxStream && *pch != TEXT('\n') && (ISSPACE(*pch) || (*pch == TEXT('\032'))))
  1284. pch++;
  1285. //
  1286. // Check for comments and remove them
  1287. //
  1288. if (pch < MaxStream &&
  1289. ((*pch == TEXT(';')) || (*pch == TEXT('#'))
  1290. || (*pch == TEXT('/') && pch+1 < MaxStream && *(pch+1) == TEXT('/'))))
  1291. while (pch < MaxStream && *pch != TEXT('\n'))
  1292. pch++;
  1293. //
  1294. // Check to see if EOF has been reached, set the token to the right
  1295. // value
  1296. //
  1297. if ( pch >= MaxStream ) {
  1298. *Stream = pch;
  1299. Token.Type = TOK_EOF;
  1300. Token.pValue = NULL;
  1301. return Token;
  1302. }
  1303. switch (*pch) {
  1304. case TEXT('[') :
  1305. pch++;
  1306. Token.Type = TOK_LBRACE;
  1307. Token.pValue = NULL;
  1308. break;
  1309. case TEXT(']') :
  1310. pch++;
  1311. Token.Type = TOK_RBRACE;
  1312. Token.pValue = NULL;
  1313. break;
  1314. case TEXT('=') :
  1315. pch++;
  1316. Token.Type = TOK_EQUAL;
  1317. Token.pValue = NULL;
  1318. break;
  1319. case TEXT(',') :
  1320. pch++;
  1321. Token.Type = TOK_COMMA;
  1322. Token.pValue = NULL;
  1323. break;
  1324. case TEXT('\n') :
  1325. pch++;
  1326. Token.Type = TOK_EOL;
  1327. Token.pValue = NULL;
  1328. break;
  1329. case TEXT('%'):
  1330. pch++;
  1331. //
  1332. // determine percented string
  1333. //
  1334. pchStart = pch;
  1335. while (pch < MaxStream && !IsQStringTerminator(*pch,TEXT('%'))) {
  1336. pch++;
  1337. }
  1338. if (pch >=MaxStream || *pch != TEXT('%')) {
  1339. Token.Type = TOK_ERRPARSE;
  1340. Token.pValue = NULL;
  1341. }
  1342. else {
  1343. Length = (unsigned)((PUCHAR)pch - (PUCHAR)pchStart);
  1344. if(pchNew = MALLOC(Length + sizeof(TCHAR))){
  1345. Length /= sizeof(TCHAR);
  1346. lstrcpyn(pchNew,pchStart,Length+1);
  1347. pchNew[Length] = 0;
  1348. Token.Type = TOK_STRING_ID;
  1349. Token.pValue = pchNew;
  1350. pch++; // advance past the percent
  1351. }else{
  1352. Token.Type = TOK_ERRNOMEM;
  1353. Token.pValue = NULL;
  1354. }
  1355. }
  1356. break;
  1357. case TEXT('\"'):
  1358. pch++;
  1359. //
  1360. // determine quoted string
  1361. //
  1362. pchStart = pch;
  1363. while (pch < MaxStream && !IsQStringTerminator(*pch,TEXT('\"'))) {
  1364. pch++;
  1365. }
  1366. if (pch >=MaxStream || *pch != TEXT('\"')) {
  1367. Token.Type = TOK_ERRPARSE;
  1368. Token.pValue = NULL;
  1369. }
  1370. else {
  1371. Length = (unsigned)((PUCHAR)pch - (PUCHAR)pchStart);
  1372. if( pchNew = MALLOC(Length + sizeof(TCHAR))){
  1373. Length /= sizeof(TCHAR);
  1374. lstrcpyn(pchNew,pchStart,Length+1);
  1375. pchNew[Length] = 0;
  1376. Token.Type = TOK_STRING;
  1377. Token.pValue = pchNew;
  1378. pch++; // advance past the quote
  1379. }else{
  1380. Token.Type = TOK_ERRNOMEM;
  1381. Token.pValue = NULL;
  1382. }
  1383. }
  1384. break;
  1385. default:
  1386. //
  1387. // determine regular string
  1388. //
  1389. pchStart = pch;
  1390. while (pch < MaxStream && !IsStringTerminator(*pch))
  1391. pch++;
  1392. if (pch == pchStart) {
  1393. pch++;
  1394. Token.Type = TOK_ERRPARSE;
  1395. Token.pValue = NULL;
  1396. }
  1397. else {
  1398. Length = (unsigned)((PUCHAR)pch - (PUCHAR)pchStart);
  1399. if( pchNew = MALLOC(Length + sizeof(TCHAR)) ){
  1400. Length /= sizeof(TCHAR);
  1401. lstrcpyn(pchNew,pchStart,Length+1);
  1402. pchNew[Length] = 0;
  1403. Token.Type = TOK_STRING;
  1404. Token.pValue = pchNew;
  1405. }else{
  1406. Token.Type = TOK_ERRNOMEM;
  1407. Token.pValue = NULL;
  1408. }
  1409. }
  1410. break;
  1411. }
  1412. *Stream = pch;
  1413. return (Token);
  1414. }
  1415. BOOL
  1416. IsStringTerminator(
  1417. TCHAR ch
  1418. )
  1419. /*++
  1420. Routine Description:
  1421. This routine tests whether the given character terminates a quoted
  1422. string.
  1423. Arguments:
  1424. ch - The current character.
  1425. Return Value:
  1426. TRUE if the character is a quoted string terminator, FALSE otherwise.
  1427. --*/
  1428. {
  1429. unsigned i;
  1430. //
  1431. // one of the string terminator array
  1432. //
  1433. for (i = 0; i < NumberOfTerminators; i++) {
  1434. if (ch == StringTerminators[i]) {
  1435. return (TRUE);
  1436. }
  1437. }
  1438. return FALSE;
  1439. }
  1440. BOOL
  1441. IsQStringTerminator(
  1442. TCHAR ch,
  1443. TCHAR term
  1444. )
  1445. /*++
  1446. Routine Description:
  1447. This routine tests whether the given character terminates a quoted
  1448. string.
  1449. Arguments:
  1450. ch - The current character.
  1451. Return Value:
  1452. TRUE if the character is a quoted string terminator, FALSE otherwise.
  1453. --*/
  1454. {
  1455. unsigned i;
  1456. //
  1457. // one of quoted string terminators array
  1458. //
  1459. for (i = 0; i < QNumberOfTerminators; i++) {
  1460. if (ch == QStringTerminators[i] || ch == term) {
  1461. return (TRUE);
  1462. }
  1463. }
  1464. return FALSE;
  1465. }
  1466. typedef struct _STRING_ENTRY {
  1467. LPCTSTR StringId;
  1468. LPCTSTR StringValue;
  1469. } STRING_ENTRY, *PSTRING_ENTRY;
  1470. BOOL
  1471. ProcessStringSection(
  1472. PINF pINF
  1473. )
  1474. /*++
  1475. Routine Description:
  1476. This routine processes the strings sections on the
  1477. specified inf file. The processing scans all values
  1478. in the inf and replaces any string ids that are
  1479. referenced.
  1480. Arguments:
  1481. pINF - pointer to the specified inf structure
  1482. Return Value:
  1483. TRUE if the strings section is processed properly
  1484. --*/
  1485. {
  1486. PSTRING_ENTRY StringTable;
  1487. DWORD StringTableCount;
  1488. DWORD LineCount;
  1489. DWORD i;
  1490. LPCTSTR StringId;
  1491. LPCTSTR StringValue;
  1492. PSECTION pSection;
  1493. PLINE pLine;
  1494. PXVALUE pValue;
  1495. LineCount = InfGetSectionLineCount( pINF, TEXT("Strings") );
  1496. if (LineCount == 0 || LineCount == 0xffffffff) {
  1497. return FALSE;
  1498. }
  1499. StringTable = (PSTRING_ENTRY) MALLOC( LineCount * sizeof(STRING_ENTRY) );
  1500. if (StringTable == NULL) {
  1501. return FALSE;
  1502. }
  1503. StringTableCount = 0;
  1504. for (i=0; i<LineCount; i++) {
  1505. StringId = InfGetLineKeyName( pINF, TEXT("Strings"), i );
  1506. StringValue = InfGetFieldByIndex( pINF, TEXT("Strings"), i, 0 );
  1507. if (StringId && StringValue) {
  1508. StringTable[i].StringId = StringId;
  1509. StringTable[i].StringValue = StringValue;
  1510. StringTableCount += 1;
  1511. }
  1512. }
  1513. pSection = pINF->pSection;
  1514. while(pSection) {
  1515. pLine = pSection->pLine;
  1516. while(pLine) {
  1517. pValue = pLine->pValue;
  1518. while(pValue) {
  1519. if (pValue->IsStringId) {
  1520. for (i=0; i<StringTableCount; i++) {
  1521. if (_tcsicmp( StringTable[i].StringId, pValue->pName ) == 0) {
  1522. FREE(pValue->pName);
  1523. pValue->pName = DupString( (PTSTR)StringTable[i].StringValue );
  1524. break;
  1525. }
  1526. }
  1527. }
  1528. pValue = pValue->pNext;
  1529. }
  1530. pLine = pLine->pNext;
  1531. }
  1532. pSection = pSection->pNext;
  1533. }
  1534. FREE( StringTable );
  1535. return TRUE;
  1536. }
  1537. BOOL
  1538. EnumFirstInfLine (
  1539. OUT PINF_ENUM InfEnum,
  1540. IN PVOID InfHandle,
  1541. IN PCTSTR InfSection
  1542. )
  1543. {
  1544. ZeroMemory (InfEnum, sizeof (INF_ENUM));
  1545. InfEnum->SectionName = DupString (InfSection);
  1546. InfEnum->InfHandle = InfHandle;
  1547. InfEnum->LineIndex = (unsigned) -1;
  1548. return EnumNextInfLine (InfEnum);
  1549. }
  1550. BOOL
  1551. EnumNextInfLine (
  1552. IN OUT PINF_ENUM InfEnum
  1553. )
  1554. {
  1555. if (!InfEnum->InfHandle) {
  1556. return FALSE;
  1557. }
  1558. if (!InfEnum->SectionName) {
  1559. return FALSE;
  1560. }
  1561. InfEnum->LineIndex++;
  1562. InfEnum->FieldZeroData = InfGetFieldByIndex (
  1563. InfEnum->InfHandle,
  1564. InfEnum->SectionName,
  1565. InfEnum->LineIndex,
  1566. 0
  1567. );
  1568. if (!InfEnum->FieldZeroData) {
  1569. AbortInfLineEnum (InfEnum);
  1570. return FALSE;
  1571. }
  1572. return TRUE;
  1573. }
  1574. VOID
  1575. AbortInfLineEnum (
  1576. IN PINF_ENUM InfEnum // ZEROED
  1577. )
  1578. {
  1579. if (InfEnum->SectionName) {
  1580. FREE ((PVOID) InfEnum->SectionName);
  1581. }
  1582. ZeroMemory (InfEnum, sizeof (INF_ENUM));
  1583. }