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.

3247 lines
80 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. sptxtfil.c
  5. Abstract:
  6. Routines to load and extract information from
  7. setup text files.
  8. Author:
  9. Ted Miller (tedm) 4-Aug-1993
  10. Revision History:
  11. --*/
  12. #include "spprecmp.h"
  13. #pragma hdrstop
  14. #include <setupapi.h>
  15. BOOLEAN HandleLineContinueChars = TRUE;
  16. //
  17. // We often need an empty string while processing the inf files. Rather
  18. // than incur the overhead of allocating memory for an empty string, we'll
  19. // just point to this empty string for all cases.
  20. //
  21. PWSTR CommonStrings[11] =
  22. { (PWSTR)(L"0"),
  23. (PWSTR)(L"1"),
  24. (PWSTR)(L"2"),
  25. (PWSTR)(L"3"),
  26. (PWSTR)(L"4"),
  27. (PWSTR)(L"5"),
  28. (PWSTR)(L"6"),
  29. (PWSTR)(L"7"),
  30. (PWSTR)(L"8"),
  31. (PWSTR)(L"9"),
  32. (PWSTR)(L"")
  33. };
  34. PVOID
  35. ParseInfBuffer(
  36. PWCHAR Buffer,
  37. ULONG Size,
  38. PULONG ErrorLine
  39. );
  40. NTSTATUS
  41. SppWriteTextToFile(
  42. IN PVOID Handle,
  43. IN PWSTR String
  44. );
  45. BOOLEAN
  46. pSpAdjustRootAndSubkeySpec(
  47. IN PVOID SifHandle,
  48. IN LPCWSTR RootKeySpec,
  49. IN LPCWSTR SubkeySpec,
  50. IN HANDLE HKLM_SYSTEM,
  51. IN HANDLE HKLM_SOFTWARE,
  52. IN HANDLE HKCU,
  53. IN HANDLE HKR,
  54. OUT HANDLE *RootKey,
  55. OUT LPWSTR Subkey
  56. );
  57. NTSTATUS
  58. SpLoadSetupTextFile(
  59. IN PWCHAR Filename, OPTIONAL
  60. IN PVOID Image, OPTIONAL
  61. IN ULONG ImageSize, OPTIONAL
  62. OUT PVOID *Handle,
  63. OUT PULONG ErrorLine,
  64. IN BOOLEAN ClearScreen,
  65. IN BOOLEAN ScreenNotReady
  66. )
  67. /*++
  68. Routine Description:
  69. Load a setup text file into memory.
  70. Arguments:
  71. Filename - If specified, supplies full filename (in NT namespace)
  72. of the file to be loaded. Oneof Image or Filename must be specified.
  73. Image - If specified, supplies a pointer to an image of the file
  74. already in memory. One of Image or Filename must be specified.
  75. ImageSize - if Image is specified, then this parameter supplies the
  76. size of the buffer pointed to by Image. Ignored otherwise.
  77. Handle - receives handle to loaded file, which can be
  78. used in subsequent calls to other text file services.
  79. ErrorLine - receives line number of syntax error, if parsing fails.
  80. ClearScreen - supplies boolean value indicating whether to clear the
  81. screen.
  82. ScreenNotReady - Indicates that this function was invoked during the initialization
  83. of setupdd.sys, and that the screen is not ready yet for output.
  84. If this flag is set, then this function will not clear the screen or update
  85. the status line.
  86. Return Value:
  87. STATUS_SUCCESS - file was read and parsed successfully.
  88. In this case, Handle is filled in.
  89. STATUS_UNSUCCESSFUL - syntax error in file. In this case, ErrorLine
  90. is filled in.
  91. STATUS_NO_MEMORY - unable to allocate memory while parsing.
  92. STATUS_IN_PAGE_ERROR - i/o error while reading the file.
  93. --*/
  94. {
  95. HANDLE hFile;
  96. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  97. IO_STATUS_BLOCK IoStatusBlock;
  98. UNICODE_STRING FilenameU;
  99. OBJECT_ATTRIBUTES oa;
  100. PWCHAR pText;
  101. ULONG cbText;
  102. HANDLE hSection;
  103. PVOID UnmapAddress;
  104. PWCHAR UniText = NULL;
  105. BOOLEAN LoadFromFile;
  106. // Set Errorline to zero to take care of default failure case
  107. if(ErrorLine)
  108. *ErrorLine=0;
  109. //
  110. // Argument validation -- one of Filename or Image must be specified,
  111. // but not both.
  112. //
  113. ASSERT(!(Filename && Image));
  114. ASSERT(Filename || Image);
  115. LoadFromFile = (BOOLEAN)(Filename != NULL);
  116. if(ScreenNotReady) {
  117. ClearScreen = FALSE;
  118. }
  119. if(ClearScreen) {
  120. CLEAR_CLIENT_SCREEN();
  121. }
  122. if(LoadFromFile) {
  123. if(!ScreenNotReady) {
  124. SpDisplayStatusText(
  125. SP_STAT_LOADING_SIF,
  126. DEFAULT_STATUS_ATTRIBUTE,
  127. wcsrchr(Filename,L'\\')+1
  128. );
  129. }
  130. //
  131. // Open the file.
  132. //
  133. RtlInitUnicodeString(&FilenameU,Filename);
  134. InitializeObjectAttributes(&oa,&FilenameU,OBJ_CASE_INSENSITIVE,NULL,NULL);
  135. Status = ZwCreateFile(
  136. &hFile,
  137. FILE_GENERIC_READ,
  138. &oa,
  139. &IoStatusBlock,
  140. NULL,
  141. FILE_ATTRIBUTE_NORMAL,
  142. FILE_SHARE_READ,
  143. FILE_OPEN,
  144. 0,
  145. NULL,
  146. 0
  147. );
  148. if(!NT_SUCCESS(Status)) {
  149. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpLoadSetupTextFile: unable to open file %ws (%lx)\n",Filename,Status));
  150. goto ltf0;
  151. }
  152. //
  153. // Get the file size.
  154. //
  155. Status = SpGetFileSize(hFile,&cbText);
  156. if(!NT_SUCCESS(Status)) {
  157. goto ltf1;
  158. }
  159. //
  160. // Map the file.
  161. //
  162. Status = SpMapEntireFile(hFile,&hSection,&pText,FALSE);
  163. if(!NT_SUCCESS(Status)) {
  164. goto ltf1;
  165. }
  166. UnmapAddress = pText;
  167. } else {
  168. if(!ScreenNotReady) {
  169. SpDisplayStatusText(SP_STAT_PROCESSING_SIF,DEFAULT_STATUS_ATTRIBUTE);
  170. }
  171. pText = Image;
  172. cbText = ImageSize;
  173. Status = STATUS_SUCCESS; // We are now ready to go to the next block.
  174. }
  175. //
  176. // See if we think the file is Unicode. We think it's Unicode
  177. // if it's even length and starts with the Unicode text marker.
  178. //
  179. try {
  180. if((*pText == 0xfeff) && !(cbText & 1)) {
  181. //
  182. // Assume it's already unicode.
  183. //
  184. pText++;
  185. cbText -= sizeof(WCHAR);
  186. } else {
  187. //
  188. // It's not Unicode. Convert it from OEM to Unicode.
  189. //
  190. // Allocate a buffer large enough to hold the maximum
  191. // unicode text. This max size occurs when
  192. // every character is single-byte, and this size is
  193. // equal to exactly double the size of the single-byte text.
  194. //
  195. if(UniText = SpMemAllocEx(cbText*sizeof(WCHAR),'1teS', PagedPool)) {
  196. Status = RtlOemToUnicodeN(
  197. UniText, // output: newly allocatd buffer
  198. cbText * sizeof(WCHAR), // max size of output
  199. &cbText, // receives # bytes in unicode text
  200. (PUCHAR)pText, // input: oem text (mapped file)
  201. cbText // size of input
  202. );
  203. if(NT_SUCCESS(Status)) {
  204. pText = UniText; // Use newly converted Unicode text
  205. }
  206. } else {
  207. Status = STATUS_NO_MEMORY;
  208. }
  209. }
  210. } except(IN_PAGE_ERROR) {
  211. Status = STATUS_IN_PAGE_ERROR;
  212. }
  213. //
  214. // Process the file.
  215. //
  216. if(NT_SUCCESS(Status)) {
  217. try {
  218. if((*Handle = ParseInfBuffer(pText,cbText,ErrorLine)) == (PVOID)NULL) {
  219. Status = STATUS_UNSUCCESSFUL;
  220. } else {
  221. Status = STATUS_SUCCESS;
  222. }
  223. } except(IN_PAGE_ERROR) {
  224. Status = STATUS_IN_PAGE_ERROR;
  225. }
  226. }
  227. //
  228. // Free the unicode text buffer if we allocated it.
  229. //
  230. if(UniText) {
  231. SpMemFree(UniText);
  232. }
  233. //
  234. // Unmap the file.
  235. //
  236. //ltf2:
  237. if(LoadFromFile) {
  238. SpUnmapFile(hSection,UnmapAddress);
  239. }
  240. ltf1:
  241. //
  242. // Close the file.
  243. //
  244. if(LoadFromFile) {
  245. ZwClose(hFile);
  246. }
  247. ltf0:
  248. return(Status);
  249. }
  250. //
  251. // [Strings] section types.
  252. //
  253. typedef enum {
  254. StringsSectionNone,
  255. StringsSectionPlain,
  256. StringsSectionLoosePrimaryMatch,
  257. StringsSectionExactPrimaryMatch,
  258. StringsSectionExactMatch
  259. } StringsSectionType;
  260. typedef struct _TEXTFILE_VALUE {
  261. struct _TEXTFILE_VALUE *pNext;
  262. PWCHAR pName;
  263. } TEXTFILE_VALUE, *PTEXTFILE_VALUE;
  264. typedef struct _TEXTFILE_LINE {
  265. struct _TEXTFILE_LINE *pNext;
  266. PWCHAR pName;
  267. PTEXTFILE_VALUE pValue;
  268. } TEXTFILE_LINE, *PTEXTFILE_LINE;
  269. typedef struct _TEXTFILE_SECTION {
  270. struct _TEXTFILE_SECTION *pNext;
  271. PWCHAR pName;
  272. PTEXTFILE_LINE pLine;
  273. PTEXTFILE_LINE PreviouslyFoundLine;
  274. } TEXTFILE_SECTION, *PTEXTFILE_SECTION;
  275. typedef struct _TEXTFILE {
  276. PTEXTFILE_SECTION pSection;
  277. PTEXTFILE_SECTION PreviouslyFoundSection;
  278. PTEXTFILE_SECTION StringsSection;
  279. StringsSectionType StringsSectionType;
  280. } TEXTFILE, *PTEXTFILE;
  281. //
  282. // DEFINES USED FOR THE PARSER INTERNALLY
  283. //
  284. //
  285. // typedefs used
  286. //
  287. typedef enum _tokentype {
  288. TOK_EOF, // 0
  289. TOK_EOL, // 1
  290. TOK_LBRACE, // 2
  291. TOK_RBRACE, // 3
  292. TOK_STRING, // 4
  293. TOK_EQUAL, // 5
  294. TOK_COMMA, // 6
  295. TOK_ERRPARSE, // 7
  296. TOK_ERRNOMEM // 8
  297. } TOKENTYPE, *PTOKENTTYPE;
  298. typedef struct _token {
  299. TOKENTYPE Type;
  300. PWCHAR pValue;
  301. } TOKEN, *PTOKEN;
  302. //
  303. // Routine defines
  304. //
  305. NTSTATUS
  306. SpAppendSection(
  307. IN PWCHAR pSectionName
  308. );
  309. NTSTATUS
  310. SpAppendLine(
  311. IN PWCHAR pLineKey
  312. );
  313. NTSTATUS
  314. SpAppendValue(
  315. IN PWCHAR pValueString
  316. );
  317. TOKEN
  318. SpGetToken(
  319. IN OUT PWCHAR *Stream,
  320. IN PWCHAR MaxStream,
  321. IN OUT PULONG LineNumber
  322. );
  323. // what follows was alinf.c
  324. //
  325. // Internal Routine Declarations for freeing inf structure members
  326. //
  327. VOID
  328. FreeSectionList (
  329. IN PTEXTFILE_SECTION pSection
  330. );
  331. VOID
  332. FreeLineList (
  333. IN PTEXTFILE_LINE pLine
  334. );
  335. VOID
  336. FreeValueList (
  337. IN PTEXTFILE_VALUE pValue
  338. );
  339. //
  340. // Internal Routine declarations for searching in the INF structures
  341. //
  342. PTEXTFILE_VALUE
  343. SearchValueInLine(
  344. IN PTEXTFILE_LINE pLine,
  345. IN ULONG ValueIndex
  346. );
  347. PTEXTFILE_LINE
  348. SearchLineInSectionByKey(
  349. IN PTEXTFILE_SECTION pSection,
  350. IN PWCHAR Key
  351. );
  352. PTEXTFILE_LINE
  353. SearchLineInSectionByIndex(
  354. IN PTEXTFILE_SECTION pSection,
  355. IN ULONG LineIndex
  356. );
  357. PTEXTFILE_SECTION
  358. SearchSectionByName(
  359. IN PTEXTFILE pINF,
  360. IN LPCWSTR SectionName
  361. );
  362. PWCHAR
  363. SpProcessForSimpleStringSub(
  364. IN PTEXTFILE pInf,
  365. IN PWCHAR String
  366. );
  367. PVOID
  368. SpNewSetupTextFile(
  369. VOID
  370. )
  371. {
  372. PTEXTFILE pFile;
  373. pFile = SpMemAllocEx(sizeof(TEXTFILE),'2teS', PagedPool);
  374. RtlZeroMemory(pFile,sizeof(TEXTFILE));
  375. return(pFile);
  376. }
  377. VOID
  378. SpAddLineToSection(
  379. IN PVOID Handle,
  380. IN PWSTR SectionName,
  381. IN PWSTR KeyName, OPTIONAL
  382. IN PWSTR Values[],
  383. IN ULONG ValueCount
  384. )
  385. {
  386. PTEXTFILE_SECTION pSection;
  387. PTEXTFILE_LINE pLine;
  388. PTEXTFILE_VALUE pValue,PrevVal;
  389. PTEXTFILE pFile;
  390. ULONG v;
  391. ULONG nameLength;
  392. pFile = (PTEXTFILE)Handle;
  393. //
  394. // If the section doesn't exist, create it.
  395. //
  396. pSection = SearchSectionByName(pFile,SectionName);
  397. if(!pSection) {
  398. nameLength = (wcslen(SectionName) + 1) * sizeof(WCHAR);
  399. pSection = SpMemAllocEx(sizeof(TEXTFILE_SECTION) + nameLength,'3teS', PagedPool);
  400. RtlZeroMemory(pSection,sizeof(TEXTFILE_SECTION));
  401. pSection->pNext = pFile->pSection;
  402. pFile->pSection = pSection;
  403. pSection->pName = (PWCHAR)(pSection + 1);
  404. RtlCopyMemory( pSection->pName, SectionName, nameLength );
  405. }
  406. //
  407. // if the line already exists, then overwrite it
  408. //
  409. if (KeyName && (pLine = SearchLineInSectionByKey(pSection,KeyName))) {
  410. FreeValueList(pLine->pValue);
  411. } else {
  412. //
  413. // Create a structure for the line in the section.
  414. //
  415. if (KeyName) {
  416. nameLength = (wcslen(KeyName) + 1) * sizeof(WCHAR);
  417. } else {
  418. nameLength = 0;
  419. }
  420. pLine = SpMemAllocEx(sizeof(TEXTFILE_LINE) + nameLength,'4teS', PagedPool);
  421. RtlZeroMemory(pLine,sizeof(TEXTFILE_LINE));
  422. pLine->pNext = pSection->pLine;
  423. pSection->pLine = pLine;
  424. if(KeyName) {
  425. pLine->pName = (PWCHAR)(pLine + 1);
  426. RtlCopyMemory( pLine->pName, KeyName, nameLength );
  427. }
  428. }
  429. //
  430. // Create value entries for each specified value.
  431. // These must be kept in the order they were specified.
  432. //
  433. for(v=0; v<ValueCount; v++) {
  434. nameLength = (wcslen(Values[v]) + 1) * sizeof(WCHAR);
  435. pValue = SpMemAllocEx(sizeof(TEXTFILE_VALUE) + nameLength,'5teS', PagedPool);
  436. RtlZeroMemory(pValue,sizeof(TEXTFILE_VALUE));
  437. pValue->pName = (PWCHAR)(pValue + 1);
  438. RtlCopyMemory( pValue->pName, Values[v], nameLength );
  439. if(v == 0) {
  440. pLine->pValue = pValue;
  441. } else {
  442. PrevVal->pNext = pValue;
  443. }
  444. PrevVal = pValue;
  445. }
  446. }
  447. NTSTATUS
  448. SpWriteSetupTextFile(
  449. IN PVOID Handle,
  450. IN PWSTR FilenamePart1,
  451. IN PWSTR FilenamePart2, OPTIONAL
  452. IN PWSTR FilenamePart3 OPTIONAL
  453. )
  454. {
  455. IO_STATUS_BLOCK IoStatusBlock;
  456. UNICODE_STRING UnicodeString;
  457. OBJECT_ATTRIBUTES Obja;
  458. NTSTATUS Status;
  459. HANDLE hFile;
  460. PWSTR p;
  461. PTEXTFILE pFile;
  462. PTEXTFILE_SECTION pSection;
  463. PTEXTFILE_LINE pLine;
  464. PTEXTFILE_VALUE pValue;
  465. //
  466. // Do this because it takes care of read-only attributes, etc.
  467. // Do it before starting to use TemporaryBuffer.
  468. //
  469. SpDeleteFile(FilenamePart1,FilenamePart2,FilenamePart3);
  470. p = TemporaryBuffer;
  471. wcscpy(p,FilenamePart1);
  472. if(FilenamePart2) {
  473. SpConcatenatePaths(p,FilenamePart2);
  474. }
  475. if(FilenamePart3) {
  476. SpConcatenatePaths(p,FilenamePart3);
  477. }
  478. INIT_OBJA(&Obja,&UnicodeString, p);
  479. Status = ZwCreateFile(
  480. &hFile,
  481. FILE_ALL_ACCESS,
  482. &Obja,
  483. &IoStatusBlock,
  484. NULL,
  485. FILE_ATTRIBUTE_NORMAL,
  486. 0, // no sharing
  487. FILE_OVERWRITE_IF,
  488. FILE_SYNCHRONOUS_IO_ALERT | FILE_NON_DIRECTORY_FILE,
  489. NULL,
  490. 0
  491. );
  492. if(!NT_SUCCESS(Status)) {
  493. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open .sif file %ws (%lx)\n",p, Status ));
  494. return(Status);
  495. }
  496. //
  497. // Write out the file contents.
  498. //
  499. pFile = (PTEXTFILE)Handle;
  500. for(pSection=pFile->pSection; pSection; pSection=pSection->pNext) {
  501. swprintf(p,L"[%s]\r\n",pSection->pName);
  502. Status = SppWriteTextToFile( hFile, p );
  503. if(!NT_SUCCESS(Status)) {
  504. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SppWriteTextToFile() failed. Status = %lx \n", Status));
  505. goto wtf1;
  506. }
  507. for(pLine=pSection->pLine; pLine; pLine=pLine->pNext) {
  508. wcscpy( p, L"" );
  509. //
  510. // Write the keyname if there is one.
  511. //
  512. if(pLine->pName) {
  513. BOOLEAN AddDoubleQuotes;
  514. AddDoubleQuotes = (wcschr(pLine->pName, (WCHAR)' ') == NULL)? FALSE : TRUE;
  515. if( AddDoubleQuotes ) {
  516. wcscat(p,L"\"");
  517. }
  518. wcscat(p,pLine->pName);
  519. if( AddDoubleQuotes ) {
  520. wcscat(p,L"\"");
  521. }
  522. wcscat(p,L" = ");
  523. }
  524. for(pValue=pLine->pValue; pValue; pValue=pValue->pNext) {
  525. if(pValue != pLine->pValue) {
  526. wcscat(p,L",");
  527. }
  528. wcscat(p,L"\"");
  529. wcscat(p,pValue->pName);
  530. wcscat(p,L"\"");
  531. }
  532. if(!pLine->pValue) {
  533. wcscat(p,L"\"\"");
  534. }
  535. wcscat(p,L"\r\n");
  536. Status = SppWriteTextToFile( hFile, p );
  537. if(!NT_SUCCESS(Status)) {
  538. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SppWriteTextToFile() failed. Status = %lx \n", Status));
  539. goto wtf1;
  540. }
  541. }
  542. }
  543. wtf1:
  544. ZwClose(hFile);
  545. return(Status);
  546. }
  547. BOOLEAN
  548. SpFreeTextFile(
  549. IN PVOID Handle
  550. )
  551. /*++
  552. Routine Description:
  553. Frees a text file.
  554. Arguments:
  555. Return Value:
  556. TRUE.
  557. --*/
  558. {
  559. PTEXTFILE pINF;
  560. ASSERT(Handle);
  561. //
  562. // cast the buffer into an INF structure
  563. //
  564. pINF = (PTEXTFILE)Handle;
  565. FreeSectionList(pINF->pSection);
  566. //
  567. // free the inf structure too
  568. //
  569. SpMemFree(pINF);
  570. return(TRUE);
  571. }
  572. VOID
  573. FreeSectionList (
  574. IN PTEXTFILE_SECTION pSection
  575. )
  576. /*++
  577. Routine Description:
  578. Arguments:
  579. Return Value:
  580. --*/
  581. {
  582. PTEXTFILE_SECTION Next;
  583. while(pSection) {
  584. Next = pSection->pNext;
  585. FreeLineList(pSection->pLine);
  586. if ((pSection->pName != (PWCHAR)(pSection + 1)) && (pSection->pName != NULL)) {
  587. SpMemFree(pSection->pName);
  588. }
  589. SpMemFree(pSection);
  590. pSection = Next;
  591. }
  592. }
  593. VOID
  594. FreeLineList (
  595. IN PTEXTFILE_LINE pLine
  596. )
  597. /*++
  598. Routine Description:
  599. Arguments:
  600. Return Value:
  601. --*/
  602. {
  603. PTEXTFILE_LINE Next;
  604. while(pLine) {
  605. Next = pLine->pNext;
  606. FreeValueList(pLine->pValue);
  607. if ((pLine->pName != (PWCHAR)(pLine + 1)) && (pLine->pName != NULL)) {
  608. SpMemFree(pLine->pName);
  609. }
  610. SpMemFree(pLine);
  611. pLine = Next;
  612. }
  613. }
  614. VOID
  615. FreeValueList (
  616. IN PTEXTFILE_VALUE pValue
  617. )
  618. /*++
  619. Routine Description:
  620. Arguments:
  621. Return Value:
  622. --*/
  623. {
  624. PTEXTFILE_VALUE Next;
  625. while(pValue) {
  626. Next = pValue->pNext;
  627. if ((pValue->pName != (PWCHAR)(pValue + 1)) && (pValue->pName != NULL)) {
  628. SpMemFree(pValue->pName);
  629. }
  630. SpMemFree(pValue);
  631. pValue = Next;
  632. }
  633. }
  634. BOOLEAN
  635. SpSearchTextFileSection (
  636. IN PVOID Handle,
  637. IN PWCHAR SectionName
  638. )
  639. /*++
  640. Routine Description:
  641. Searches for the existance of a particular section.
  642. Arguments:
  643. Return Value:
  644. --*/
  645. {
  646. return((BOOLEAN)(SearchSectionByName((PTEXTFILE)Handle,SectionName) != NULL));
  647. }
  648. PWCHAR
  649. SpGetSectionLineIndex(
  650. IN PVOID Handle,
  651. IN LPCWSTR SectionName,
  652. IN ULONG LineIndex,
  653. IN ULONG ValueIndex
  654. )
  655. /*++
  656. Routine Description:
  657. Given section name, line number and index return the value.
  658. Arguments:
  659. Return Value:
  660. --*/
  661. {
  662. PTEXTFILE_SECTION pSection;
  663. PTEXTFILE_LINE pLine;
  664. PTEXTFILE_VALUE pValue;
  665. if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
  666. return(NULL);
  667. }
  668. if((pLine = SearchLineInSectionByIndex(pSection,LineIndex)) == NULL) {
  669. return(NULL);
  670. }
  671. if((pValue = SearchValueInLine(pLine,ValueIndex)) == NULL) {
  672. return(NULL);
  673. }
  674. return(SpProcessForSimpleStringSub(Handle,pValue->pName));
  675. }
  676. BOOLEAN
  677. SpGetSectionKeyExists (
  678. IN PVOID Handle,
  679. IN PWCHAR SectionName,
  680. IN PWCHAR Key
  681. )
  682. /*++
  683. Routine Description:
  684. Arguments:
  685. Return Value:
  686. --*/
  687. {
  688. PTEXTFILE_SECTION pSection;
  689. if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
  690. return(FALSE);
  691. }
  692. if(SearchLineInSectionByKey(pSection,Key) == NULL) {
  693. return(FALSE);
  694. }
  695. return(TRUE);
  696. }
  697. PWCHAR
  698. SpGetKeyName(
  699. IN PVOID Handle,
  700. IN LPCWSTR SectionName,
  701. IN ULONG LineIndex
  702. )
  703. {
  704. PTEXTFILE_SECTION pSection;
  705. PTEXTFILE_LINE pLine;
  706. pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName);
  707. if(pSection == NULL) {
  708. return(NULL);
  709. }
  710. pLine = SearchLineInSectionByIndex(pSection,LineIndex);
  711. if(pLine == NULL) {
  712. return(NULL);
  713. }
  714. return(pLine->pName);
  715. }
  716. PWCHAR
  717. SpGetSectionKeyIndex (
  718. IN PVOID Handle,
  719. IN PWCHAR SectionName,
  720. IN PWCHAR Key,
  721. IN ULONG ValueIndex
  722. )
  723. /*++
  724. Routine Description:
  725. Given section name, key and index return the value
  726. Arguments:
  727. Return Value:
  728. --*/
  729. {
  730. PTEXTFILE_SECTION pSection;
  731. PTEXTFILE_LINE pLine;
  732. PTEXTFILE_VALUE pValue;
  733. if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
  734. return(NULL);
  735. }
  736. if((pLine = SearchLineInSectionByKey(pSection,Key)) == NULL) {
  737. return(NULL);
  738. }
  739. if((pValue = SearchValueInLine(pLine,ValueIndex)) == NULL) {
  740. return(NULL);
  741. }
  742. return(SpProcessForSimpleStringSub(Handle,pValue->pName));
  743. }
  744. ULONG
  745. SpGetKeyIndex(
  746. IN PVOID Handle,
  747. IN PWCHAR SectionName,
  748. IN PWCHAR KeyName
  749. )
  750. {
  751. ULONG Result = -1;
  752. if (SectionName && KeyName) {
  753. ULONG MaxLines = SpCountLinesInSection(Handle, SectionName);
  754. ULONG Index;
  755. PWSTR CurrKey = 0;
  756. for (Index=0; Index < MaxLines; Index++) {
  757. CurrKey = SpGetKeyName(Handle, SectionName, Index);
  758. if (CurrKey && !wcscmp(CurrKey, KeyName)) {
  759. Result = Index;
  760. break;
  761. }
  762. }
  763. }
  764. return Result;
  765. }
  766. ULONG
  767. SpCountLinesInSection(
  768. IN PVOID Handle,
  769. IN PWCHAR SectionName
  770. )
  771. {
  772. PTEXTFILE_SECTION pSection;
  773. PTEXTFILE_LINE pLine;
  774. ULONG Count;
  775. if((pSection = SearchSectionByName((PTEXTFILE)Handle,SectionName)) == NULL) {
  776. return(0);
  777. }
  778. for(pLine = pSection->pLine, Count = 0;
  779. pLine;
  780. pLine = pLine->pNext, Count++
  781. );
  782. return(Count);
  783. }
  784. PTEXTFILE_VALUE
  785. SearchValueInLine(
  786. IN PTEXTFILE_LINE pLine,
  787. IN ULONG ValueIndex
  788. )
  789. /*++
  790. Routine Description:
  791. Arguments:
  792. Return Value:
  793. --*/
  794. {
  795. PTEXTFILE_VALUE pValue;
  796. ULONG i;
  797. if(pLine == NULL) {
  798. return(NULL);
  799. }
  800. pValue = pLine->pValue;
  801. for(i=0; (i<ValueIndex) && (pValue=pValue->pNext); i++) {
  802. ;
  803. }
  804. return pValue;
  805. }
  806. PTEXTFILE_LINE
  807. SearchLineInSectionByKey(
  808. IN PTEXTFILE_SECTION pSection,
  809. IN PWCHAR Key
  810. )
  811. /*++
  812. Routine Description:
  813. Arguments:
  814. Return Value:
  815. --*/
  816. {
  817. PTEXTFILE_LINE pLine,pFirstSearchedLine;
  818. //
  819. // Start at the line where we left off in the last search.
  820. //
  821. pLine = pFirstSearchedLine = pSection->PreviouslyFoundLine;
  822. while(pLine && ((pLine->pName == NULL) || _wcsicmp(pLine->pName,Key))) {
  823. pLine = pLine->pNext;
  824. }
  825. //
  826. // If we haven't found it yet, wrap around to the beginning of the section.
  827. //
  828. if(!pLine) {
  829. pLine = pSection->pLine;
  830. while(pLine && (pLine != pFirstSearchedLine)) {
  831. if(pLine->pName && !_wcsicmp(pLine->pName,Key)) {
  832. break;
  833. }
  834. pLine = pLine->pNext;
  835. }
  836. //
  837. // If we wrapped around to the first line we searched,
  838. // then we didn't find the line we're looking for.
  839. //
  840. if(pLine == pFirstSearchedLine) {
  841. pLine = NULL;
  842. }
  843. }
  844. //
  845. // If we found the line, save it away so we can resume the
  846. // search from that point the next time we are called.
  847. //
  848. if(pLine) {
  849. pSection->PreviouslyFoundLine = pLine;
  850. }
  851. return pLine;
  852. }
  853. PTEXTFILE_LINE
  854. SearchLineInSectionByIndex(
  855. IN PTEXTFILE_SECTION pSection,
  856. IN ULONG LineIndex
  857. )
  858. /*++
  859. Routine Description:
  860. Arguments:
  861. Return Value:
  862. --*/
  863. {
  864. PTEXTFILE_LINE pLine;
  865. ULONG i;
  866. //
  867. // Validate the parameters passed in
  868. //
  869. if(pSection == NULL) {
  870. return(NULL);
  871. }
  872. //
  873. // find the start of the line list in the section passed in
  874. //
  875. pLine = pSection->pLine;
  876. //
  877. // traverse down the current line list to the LineIndex th line
  878. //
  879. for(i=0; (i<LineIndex) && (pLine = pLine->pNext); i++) {
  880. ;
  881. }
  882. //
  883. // return the Line found
  884. //
  885. return pLine;
  886. }
  887. PTEXTFILE_SECTION
  888. SearchSectionByName(
  889. IN PTEXTFILE pINF,
  890. IN LPCWSTR SectionName
  891. )
  892. {
  893. PTEXTFILE_SECTION pSection,pFirstSearchedSection;
  894. //
  895. // find the section list
  896. //
  897. pSection = pFirstSearchedSection = pINF->PreviouslyFoundSection;
  898. //
  899. // traverse down the section list searching each section for the section
  900. // name mentioned
  901. //
  902. while(pSection && _wcsicmp(pSection->pName,SectionName)) {
  903. pSection = pSection->pNext;
  904. }
  905. //
  906. // If we didn't find it so far, search the beginning of the file.
  907. //
  908. if(!pSection) {
  909. pSection = pINF->pSection;
  910. while(pSection && (pSection != pFirstSearchedSection)) {
  911. if(pSection->pName && !_wcsicmp(pSection->pName,SectionName)) {
  912. break;
  913. }
  914. pSection = pSection->pNext;
  915. }
  916. //
  917. // If we wrapped around to the first section we searched,
  918. // then we didn't find the section we're looking for.
  919. //
  920. if(pSection == pFirstSearchedSection) {
  921. pSection = NULL;
  922. }
  923. }
  924. if(pSection) {
  925. pINF->PreviouslyFoundSection = pSection;
  926. }
  927. //
  928. // return the section at which we stopped (either NULL or the section
  929. // which was found).
  930. //
  931. return pSection;
  932. }
  933. PWCHAR
  934. SpProcessForSimpleStringSub(
  935. IN PTEXTFILE pInf,
  936. IN PWCHAR String
  937. )
  938. {
  939. UINT Len;
  940. PWCHAR ReturnString;
  941. PTEXTFILE_SECTION pSection;
  942. PTEXTFILE_LINE pLine;
  943. //
  944. // Assume no substitution necessary.
  945. //
  946. ReturnString = String;
  947. //
  948. // If it starts and end with % then look it up in the
  949. // strings section. Note the initial check before doing a
  950. // wcslen, to preserve performance in the 99% case where
  951. // there is no substitution.
  952. //
  953. if((String[0] == L'%') && ((Len = wcslen(String)) > 2) && (String[Len-1] == L'%')
  954. && (pSection = pInf->StringsSection)) {
  955. for(pLine = pSection->pLine; pLine; pLine=pLine->pNext) {
  956. if(pLine->pName
  957. && !_wcsnicmp(pLine->pName,String+1,Len-2)
  958. && (pLine->pName[Len-2] == 0)) {
  959. break;
  960. }
  961. }
  962. if(pLine && pLine->pValue && pLine->pValue->pName) {
  963. ReturnString = pLine->pValue->pName;
  964. }
  965. }
  966. return(ReturnString);
  967. }
  968. VOID
  969. SpProcessForStringSubs(
  970. IN PVOID SifHandle,
  971. IN LPCWSTR StringIn,
  972. OUT LPWSTR StringOut,
  973. IN ULONG BufferSizeChars
  974. )
  975. {
  976. LPCWSTR In,q;
  977. LPWSTR Out,p;
  978. WCHAR Str[511];
  979. ULONG Len,i;
  980. WCHAR *End;
  981. In = StringIn;
  982. Out = StringOut;
  983. End = Out + BufferSizeChars;
  984. while(*In) {
  985. if(*In == L'%') {
  986. //
  987. // Double % in input ==> single % in output
  988. //
  989. if(*(++In) == L'%') {
  990. if(Out < End) {
  991. *Out++ = L'%';
  992. }
  993. In++;
  994. } else {
  995. //
  996. // Look for terminating %.
  997. //
  998. if(p = wcschr(In,L'%')) {
  999. //
  1000. // Get value to substitute. If we can't find the value,
  1001. // put the whole string like %abc% in there.
  1002. //
  1003. Len = (ULONG)(p - In);
  1004. if(Len > ((sizeof(Str)/sizeof(WCHAR))-1)) {
  1005. //
  1006. // We can't handle substitutions for tokens this long.
  1007. // We'll just bail in this case, and copy over the token as-is.
  1008. //
  1009. q = NULL;
  1010. } else {
  1011. RtlCopyMemory(Str,In-1,(Len+2)*sizeof(WCHAR));
  1012. Str[Len+2] = 0;
  1013. q = SpProcessForSimpleStringSub(SifHandle,Str);
  1014. if(q == Str) {
  1015. q = NULL;
  1016. }
  1017. }
  1018. if(q) {
  1019. Len = wcslen(q);
  1020. for(i=0; i<Len; i++) {
  1021. if(Out < End) {
  1022. *Out++ = q[i];
  1023. }
  1024. }
  1025. In = p+1;
  1026. } else {
  1027. //
  1028. // Len is the length of the internal part (the abc in %abc%).
  1029. //
  1030. if(Out < End) {
  1031. *Out++ = L'%';
  1032. }
  1033. for(i=0; i<=Len; i++, In++) {
  1034. if(Out < End) {
  1035. *Out++ = *In;
  1036. }
  1037. }
  1038. }
  1039. } else {
  1040. //
  1041. // No terminating %. So we have something like %abc.
  1042. // Want to put %abc in the output. Put the % in here
  1043. // manually and then just let subsequent passes
  1044. // through the loop copy the rest of the chars.
  1045. //
  1046. if(Out < End) {
  1047. *Out++ = L'%';
  1048. }
  1049. }
  1050. }
  1051. } else {
  1052. //
  1053. // Plain char.
  1054. //
  1055. if(Out < End) {
  1056. *Out++ = *In;
  1057. }
  1058. In++;
  1059. }
  1060. }
  1061. *Out = 0;
  1062. }
  1063. //
  1064. // Globals used to make building the lists easier
  1065. //
  1066. PTEXTFILE pINF;
  1067. PTEXTFILE_SECTION pSectionRecord;
  1068. PTEXTFILE_LINE pLineRecord;
  1069. PTEXTFILE_VALUE pValueRecord;
  1070. //
  1071. // Globals used by the token parser
  1072. //
  1073. // string terminators are the whitespace characters (isspace: space, tab,
  1074. // linefeed, formfeed, vertical tab, carriage return) or the chars given below
  1075. WCHAR StringTerminators[] = L"[]=,\t \"\n\f\v\r";
  1076. PWCHAR QStringTerminators = StringTerminators+6;
  1077. //
  1078. // Main parser routine
  1079. //
  1080. PVOID
  1081. ParseInfBuffer(
  1082. PWCHAR Buffer,
  1083. ULONG Size,
  1084. PULONG ErrorLine
  1085. )
  1086. /*++
  1087. Routine Description:
  1088. Given a character buffer containing the INF file, this routine parses
  1089. the INF into an internal form with Section records, Line records and
  1090. Value records.
  1091. Arguments:
  1092. Buffer - contains to ptr to a buffer containing the INF file
  1093. Size - contains the size of the buffer in bytes.
  1094. ErrorLine - if a parse error occurs, this variable receives the line
  1095. number of the line containing the error.
  1096. Return Value:
  1097. PVOID - INF handle ptr to be used in subsequent INF calls.
  1098. --*/
  1099. {
  1100. PWCHAR Stream, MaxStream, pchSectionName = NULL, pchValue = NULL;
  1101. ULONG State, InfLine;
  1102. TOKEN Token;
  1103. BOOLEAN Done;
  1104. BOOLEAN Error;
  1105. NTSTATUS ErrorCode;
  1106. //
  1107. // Initialise the globals
  1108. //
  1109. pINF = NULL;
  1110. pSectionRecord = NULL;
  1111. pLineRecord = NULL;
  1112. pValueRecord = NULL;
  1113. //
  1114. // Get INF record
  1115. //
  1116. if((pINF = SpMemAllocEx(sizeof(TEXTFILE),'6teS', PagedPool)) == NULL) {
  1117. return NULL;
  1118. }
  1119. RtlZeroMemory(pINF,sizeof(TEXTFILE));
  1120. //
  1121. // Set initial state
  1122. //
  1123. State = 1;
  1124. InfLine = 1;
  1125. Stream = Buffer;
  1126. MaxStream = Buffer + (Size/sizeof(WCHAR));
  1127. Done = FALSE;
  1128. Error = FALSE;
  1129. //
  1130. // Enter token processing loop
  1131. //
  1132. while (!Done) {
  1133. Token = SpGetToken(&Stream, MaxStream, &InfLine);
  1134. switch (State) {
  1135. //
  1136. // STATE1: Start of file, this state remains till first
  1137. // section is found
  1138. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_LBRACE, TOK_STRING
  1139. // TOK_STRING occurs when reading dblspace.ini
  1140. //
  1141. case 1:
  1142. switch (Token.Type) {
  1143. case TOK_EOL:
  1144. break;
  1145. case TOK_EOF:
  1146. Done = TRUE;
  1147. break;
  1148. case TOK_LBRACE:
  1149. State = 2;
  1150. break;
  1151. case TOK_STRING:
  1152. pchSectionName = SpMemAllocEx( ( wcslen( DBLSPACE_SECTION ) + 1 )*sizeof( WCHAR ),'7teS', PagedPool );
  1153. if( pchSectionName == NULL ) {
  1154. Error = Done = TRUE;
  1155. ErrorCode = STATUS_NO_MEMORY;
  1156. }
  1157. wcscpy( pchSectionName, DBLSPACE_SECTION );
  1158. pchValue = Token.pValue;
  1159. if ((ErrorCode = SpAppendSection(pchSectionName)) != STATUS_SUCCESS) {
  1160. Error = Done = TRUE;
  1161. ErrorCode = STATUS_UNSUCCESSFUL;
  1162. } else {
  1163. pchSectionName = NULL;
  1164. State = 6;
  1165. }
  1166. break;
  1167. default:
  1168. Error = Done = TRUE;
  1169. ErrorCode = STATUS_UNSUCCESSFUL;
  1170. break;
  1171. }
  1172. break;
  1173. //
  1174. // STATE 2: Section LBRACE has been received, expecting STRING or RBRACE
  1175. //
  1176. // Valid Tokens: TOK_STRING, TOK_RBRACE
  1177. //
  1178. case 2:
  1179. switch (Token.Type) {
  1180. case TOK_STRING:
  1181. State = 3;
  1182. pchSectionName = Token.pValue;
  1183. break;
  1184. case TOK_RBRACE:
  1185. State = 4;
  1186. pchSectionName = CommonStrings[10];
  1187. break;
  1188. default:
  1189. Error = Done = TRUE;
  1190. ErrorCode = STATUS_UNSUCCESSFUL;
  1191. break;
  1192. }
  1193. break;
  1194. //
  1195. // STATE 3: Section Name received, expecting RBRACE
  1196. //
  1197. // Valid Tokens: TOK_RBRACE
  1198. //
  1199. case 3:
  1200. switch (Token.Type) {
  1201. case TOK_RBRACE:
  1202. State = 4;
  1203. break;
  1204. default:
  1205. Error = Done = TRUE;
  1206. ErrorCode = STATUS_UNSUCCESSFUL;
  1207. break;
  1208. }
  1209. break;
  1210. //
  1211. // STATE 4: Section Definition Complete, expecting EOL
  1212. //
  1213. // Valid Tokens: TOK_EOL, TOK_EOF
  1214. //
  1215. case 4:
  1216. switch (Token.Type) {
  1217. case TOK_EOL:
  1218. if ((ErrorCode = SpAppendSection(pchSectionName)) != STATUS_SUCCESS)
  1219. Error = Done = TRUE;
  1220. else {
  1221. pchSectionName = NULL;
  1222. State = 5;
  1223. }
  1224. break;
  1225. case TOK_EOF:
  1226. if ((ErrorCode = SpAppendSection(pchSectionName)) != STATUS_SUCCESS)
  1227. Error = Done = TRUE;
  1228. else {
  1229. pchSectionName = NULL;
  1230. Done = TRUE;
  1231. }
  1232. break;
  1233. default:
  1234. Error = Done = TRUE;
  1235. ErrorCode = STATUS_UNSUCCESSFUL;
  1236. break;
  1237. }
  1238. break;
  1239. //
  1240. // STATE 5: Expecting Section Lines
  1241. //
  1242. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_STRING, TOK_LBRACE
  1243. //
  1244. case 5:
  1245. switch (Token.Type) {
  1246. case TOK_EOL:
  1247. break;
  1248. case TOK_EOF:
  1249. Done = TRUE;
  1250. break;
  1251. case TOK_STRING:
  1252. pchValue = Token.pValue;
  1253. State = 6;
  1254. break;
  1255. case TOK_LBRACE:
  1256. State = 2;
  1257. break;
  1258. default:
  1259. Error = Done = TRUE;
  1260. ErrorCode = STATUS_UNSUCCESSFUL;
  1261. break;
  1262. }
  1263. break;
  1264. //
  1265. // STATE 6: String returned, not sure whether it is key or value
  1266. //
  1267. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA, TOK_EQUAL
  1268. //
  1269. case 6:
  1270. switch (Token.Type) {
  1271. case TOK_EOL:
  1272. if ( (ErrorCode = SpAppendLine(NULL)) != STATUS_SUCCESS ||
  1273. (ErrorCode = SpAppendValue(pchValue)) !=STATUS_SUCCESS )
  1274. Error = Done = TRUE;
  1275. else {
  1276. pchValue = NULL;
  1277. State = 5;
  1278. }
  1279. break;
  1280. case TOK_EOF:
  1281. if ( (ErrorCode = SpAppendLine(NULL)) != STATUS_SUCCESS ||
  1282. (ErrorCode = SpAppendValue(pchValue)) !=STATUS_SUCCESS )
  1283. Error = Done = TRUE;
  1284. else {
  1285. pchValue = NULL;
  1286. Done = TRUE;
  1287. }
  1288. break;
  1289. case TOK_COMMA:
  1290. if ( (ErrorCode = SpAppendLine(NULL)) != STATUS_SUCCESS ||
  1291. (ErrorCode = SpAppendValue(pchValue)) !=STATUS_SUCCESS )
  1292. Error = Done = TRUE;
  1293. else {
  1294. pchValue = NULL;
  1295. State = 7;
  1296. }
  1297. break;
  1298. case TOK_EQUAL:
  1299. if ( (ErrorCode = SpAppendLine(pchValue)) != STATUS_SUCCESS)
  1300. Error = Done = TRUE;
  1301. else {
  1302. pchValue = NULL;
  1303. State = 8;
  1304. }
  1305. break;
  1306. default:
  1307. Error = Done = TRUE;
  1308. ErrorCode = STATUS_UNSUCCESSFUL;
  1309. break;
  1310. }
  1311. break;
  1312. //
  1313. // STATE 7: Comma received, Expecting another string
  1314. // Also allow a comma to indicate an empty value ie x = 1,,2
  1315. //
  1316. // Valid Tokens: TOK_STRING TOK_COMMA
  1317. //
  1318. case 7:
  1319. switch (Token.Type) {
  1320. case TOK_COMMA:
  1321. Token.pValue = CommonStrings[10];
  1322. if ((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS)
  1323. Error = Done = TRUE;
  1324. //
  1325. // State stays at 7 because we are expecting a string
  1326. //
  1327. break;
  1328. case TOK_STRING:
  1329. if ((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS)
  1330. Error = Done = TRUE;
  1331. else
  1332. State = 9;
  1333. break;
  1334. default:
  1335. Error = Done = TRUE;
  1336. ErrorCode = STATUS_UNSUCCESSFUL;
  1337. break;
  1338. }
  1339. break;
  1340. //
  1341. // STATE 8: Equal received, Expecting another string
  1342. // If none, assume there is a single empty string on the RHS
  1343. //
  1344. // Valid Tokens: TOK_STRING, TOK_EOL, TOK_EOF
  1345. //
  1346. case 8:
  1347. switch (Token.Type) {
  1348. case TOK_EOF:
  1349. Token.pValue = CommonStrings[10];
  1350. if((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS) {
  1351. Error = TRUE;
  1352. }
  1353. Done = TRUE;
  1354. break;
  1355. case TOK_EOL:
  1356. Token.pValue = CommonStrings[10];
  1357. if((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS) {
  1358. Error = TRUE;
  1359. Done = TRUE;
  1360. } else {
  1361. State = 5;
  1362. }
  1363. break;
  1364. case TOK_STRING:
  1365. if ((ErrorCode = SpAppendValue(Token.pValue)) != STATUS_SUCCESS)
  1366. Error = Done = TRUE;
  1367. else
  1368. State = 9;
  1369. break;
  1370. default:
  1371. Error = Done = TRUE;
  1372. ErrorCode = STATUS_UNSUCCESSFUL;
  1373. break;
  1374. }
  1375. break;
  1376. //
  1377. // STATE 9: String received after equal, value string
  1378. //
  1379. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA
  1380. //
  1381. case 9:
  1382. switch (Token.Type) {
  1383. case TOK_EOL:
  1384. State = 5;
  1385. break;
  1386. case TOK_EOF:
  1387. Done = TRUE;
  1388. break;
  1389. case TOK_COMMA:
  1390. State = 7;
  1391. break;
  1392. default:
  1393. Error = Done = TRUE;
  1394. ErrorCode = STATUS_UNSUCCESSFUL;
  1395. break;
  1396. }
  1397. break;
  1398. //
  1399. // STATE 10: Value string definitely received
  1400. //
  1401. // Valid Tokens: TOK_EOL, TOK_EOF, TOK_COMMA
  1402. //
  1403. case 10:
  1404. switch (Token.Type) {
  1405. case TOK_EOL:
  1406. State =5;
  1407. break;
  1408. case TOK_EOF:
  1409. Done = TRUE;
  1410. break;
  1411. case TOK_COMMA:
  1412. State = 7;
  1413. break;
  1414. default:
  1415. Error = Done = TRUE;
  1416. ErrorCode = STATUS_UNSUCCESSFUL;
  1417. break;
  1418. }
  1419. break;
  1420. default:
  1421. Error = Done = TRUE;
  1422. ErrorCode = STATUS_UNSUCCESSFUL;
  1423. break;
  1424. } // end switch(State)
  1425. if (Error) {
  1426. switch (ErrorCode) {
  1427. case STATUS_UNSUCCESSFUL:
  1428. *ErrorLine = InfLine;
  1429. break;
  1430. case STATUS_NO_MEMORY:
  1431. //SpxOutOfMemory();
  1432. break;
  1433. default:
  1434. break;
  1435. }
  1436. SpFreeTextFile(pINF);
  1437. if(pchSectionName) {
  1438. SpMemFree(pchSectionName);
  1439. }
  1440. if(pchValue) {
  1441. SpMemFree(pchValue);
  1442. }
  1443. pINF = NULL;
  1444. }
  1445. else {
  1446. //
  1447. // Keep track of line numbers so that we can display Errors
  1448. //
  1449. if(Token.Type == TOK_EOL) {
  1450. InfLine++;
  1451. }
  1452. }
  1453. } // End while
  1454. if(pINF) {
  1455. PTEXTFILE_SECTION p;
  1456. pINF->PreviouslyFoundSection = pINF->pSection;
  1457. for(p=pINF->pSection; p; p=p->pNext) {
  1458. p->PreviouslyFoundLine = p->pLine;
  1459. }
  1460. }
  1461. return(pINF);
  1462. }
  1463. NTSTATUS
  1464. SpAppendSection(
  1465. IN PWCHAR pSectionName
  1466. )
  1467. /*++
  1468. Routine Description:
  1469. This appends a new section to the section list in the current INF.
  1470. All further lines and values pertain to this new section, so it resets
  1471. the line list and value lists too.
  1472. Arguments:
  1473. pSectionName - Name of the new section. ( [SectionName] )
  1474. Return Value:
  1475. STATUS_SUCCESS if successful.
  1476. STATUS_NO_MEMORY if memory allocation failed.
  1477. STATUS_UNSUCCESSFUL if invalid parameters passed in or the INF buffer not
  1478. initialised
  1479. --*/
  1480. {
  1481. PTEXTFILE_SECTION pNewSection;
  1482. StringsSectionType type;
  1483. WCHAR *p;
  1484. USHORT Id;
  1485. USHORT ThreadLang;
  1486. //
  1487. // Check to see if INF initialised and the parameter passed in is valid
  1488. //
  1489. ASSERT(pINF);
  1490. ASSERT(pSectionName);
  1491. if((pINF == NULL) || (pSectionName == NULL)) {
  1492. return STATUS_UNSUCCESSFUL;
  1493. }
  1494. //
  1495. // See if we already have a section by this name. If so we want
  1496. // to merge sections.
  1497. //
  1498. for(pNewSection=pINF->pSection; pNewSection; pNewSection=pNewSection->pNext) {
  1499. if(pNewSection->pName && !_wcsicmp(pNewSection->pName,pSectionName)) {
  1500. break;
  1501. }
  1502. }
  1503. if(pNewSection) {
  1504. //
  1505. // Set pLineRecord to point to the list line currently in the section.
  1506. //
  1507. for(pLineRecord = pNewSection->pLine;
  1508. pLineRecord && pLineRecord->pNext;
  1509. pLineRecord = pLineRecord->pNext)
  1510. ;
  1511. } else {
  1512. //
  1513. // Allocate memory for the new section and initialize the structure.
  1514. //
  1515. if((pNewSection = SpMemAllocEx(sizeof(TEXTFILE_SECTION),'8teS', PagedPool)) == NULL) {
  1516. return STATUS_NO_MEMORY;
  1517. }
  1518. RtlZeroMemory(pNewSection,sizeof(TEXTFILE_SECTION));
  1519. pNewSection->pName = pSectionName;
  1520. //
  1521. // Link it in. Also track the [strings] sections in order of desirability:
  1522. //
  1523. // 1) Strings.xxxx where xxxx is the language id part of the current thread locale.
  1524. // 2) Strings.xxxx where xxxx is the primary language id part of the thread locale.
  1525. // 3) Stirngs.xxxx where the primary language id part of xxxx matches the
  1526. // primary language id part of the thread locale.
  1527. // 4) Plain old [Strings].
  1528. //
  1529. pNewSection->pNext = pINF->pSection;
  1530. pINF->pSection = pNewSection;
  1531. if(!_wcsnicmp(pSectionName,L"Strings",7)) {
  1532. type = StringsSectionNone;
  1533. if(pSectionName[7] == L'.') {
  1534. //
  1535. // The langid part must be in the form of 4 hex digits.
  1536. //
  1537. Id = (USHORT)SpStringToLong(pSectionName+8,&p,16);
  1538. if((p == pSectionName+8+5) && (*p == 0)) {
  1539. ThreadLang = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
  1540. if(ThreadLang == Id) {
  1541. type = StringsSectionExactMatch;
  1542. } else {
  1543. if(Id == PRIMARYLANGID(ThreadLang)) {
  1544. type = StringsSectionExactPrimaryMatch;
  1545. } else {
  1546. if(PRIMARYLANGID(Id) == PRIMARYLANGID(ThreadLang)) {
  1547. type = StringsSectionLoosePrimaryMatch;
  1548. }
  1549. }
  1550. }
  1551. }
  1552. } else {
  1553. if(!pSectionName[7]) {
  1554. type = StringsSectionPlain;
  1555. }
  1556. }
  1557. if(type > pINF->StringsSectionType) {
  1558. pINF->StringsSection = pNewSection;
  1559. }
  1560. }
  1561. //
  1562. // reset the current line record
  1563. //
  1564. pLineRecord = NULL;
  1565. }
  1566. pSectionRecord = pNewSection;
  1567. pValueRecord = NULL;
  1568. return STATUS_SUCCESS;
  1569. }
  1570. NTSTATUS
  1571. SpAppendLine(
  1572. IN PWCHAR pLineKey
  1573. )
  1574. /*++
  1575. Routine Description:
  1576. This appends a new line to the line list in the current section.
  1577. All further values pertain to this new line, so it resets
  1578. the value list too.
  1579. Arguments:
  1580. pLineKey - Key to be used for the current line, this could be NULL.
  1581. Return Value:
  1582. STATUS_SUCCESS if successful.
  1583. STATUS_NO_MEMORY if memory allocation failed.
  1584. STATUS_UNSUCCESSFUL if invalid parameters passed in or current section not
  1585. initialised
  1586. --*/
  1587. {
  1588. PTEXTFILE_LINE pNewLine;
  1589. //
  1590. // Check to see if current section initialised
  1591. //
  1592. ASSERT(pSectionRecord);
  1593. if(pSectionRecord == NULL) {
  1594. return STATUS_UNSUCCESSFUL;
  1595. }
  1596. //
  1597. // Allocate memory for the new Line
  1598. //
  1599. if((pNewLine = SpMemAllocEx(sizeof(TEXTFILE_LINE),'9teS', PagedPool)) == NULL) {
  1600. return STATUS_NO_MEMORY;
  1601. }
  1602. //
  1603. // Link it in
  1604. //
  1605. pNewLine->pNext = NULL;
  1606. pNewLine->pValue = NULL;
  1607. pNewLine->pName = pLineKey;
  1608. if (pLineRecord == NULL) {
  1609. pSectionRecord->pLine = pNewLine;
  1610. } else {
  1611. pLineRecord->pNext = pNewLine;
  1612. }
  1613. pLineRecord = pNewLine;
  1614. //
  1615. // Reset the current value record
  1616. //
  1617. pValueRecord = NULL;
  1618. return STATUS_SUCCESS;
  1619. }
  1620. NTSTATUS
  1621. SpAppendValue(
  1622. IN PWCHAR pValueString
  1623. )
  1624. /*++
  1625. Routine Description:
  1626. This appends a new value to the value list in the current line.
  1627. Arguments:
  1628. pValueString - The value string to be added.
  1629. Return Value:
  1630. STATUS_SUCCESS if successful.
  1631. STATUS_NO_MEMORY if memory allocation failed.
  1632. STATUS_UNSUCCESSFUL if invalid parameters passed in or current line not
  1633. initialised.
  1634. --*/
  1635. {
  1636. PTEXTFILE_VALUE pNewValue;
  1637. //
  1638. // Check to see if current line record has been initialised and
  1639. // the parameter passed in is valid
  1640. //
  1641. ASSERT(pLineRecord);
  1642. ASSERT(pValueString);
  1643. if((pLineRecord == NULL) || (pValueString == NULL)) {
  1644. return STATUS_UNSUCCESSFUL;
  1645. }
  1646. //
  1647. // Allocate memory for the new value record
  1648. //
  1649. if((pNewValue = SpMemAllocEx(sizeof(TEXTFILE_VALUE),'ateS', PagedPool)) == NULL) {
  1650. return STATUS_NO_MEMORY;
  1651. }
  1652. //
  1653. // Link it in.
  1654. //
  1655. pNewValue->pNext = NULL;
  1656. pNewValue->pName = pValueString;
  1657. if (pValueRecord == NULL) {
  1658. pLineRecord->pValue = pNewValue;
  1659. } else {
  1660. pValueRecord->pNext = pNewValue;
  1661. }
  1662. pValueRecord = pNewValue;
  1663. return STATUS_SUCCESS;
  1664. }
  1665. TOKEN
  1666. SpGetToken(
  1667. IN OUT PWCHAR *Stream,
  1668. IN PWCHAR MaxStream,
  1669. IN OUT PULONG LineNumber
  1670. )
  1671. /*++
  1672. Routine Description:
  1673. This function returns the Next token from the configuration stream.
  1674. Arguments:
  1675. Stream - Supplies the address of the configuration stream. Returns
  1676. the address of where to start looking for tokens within the
  1677. stream.
  1678. MaxStream - Supplies the address of the last character in the stream.
  1679. Return Value:
  1680. TOKEN - Returns the next token
  1681. --*/
  1682. {
  1683. PWCHAR pch, pchStart, pchNew;
  1684. ULONG Length;
  1685. TOKEN Token;
  1686. ULONG QuotedQuotes;
  1687. //
  1688. // Skip whitespace (except for eol)
  1689. //
  1690. pch = *Stream;
  1691. restart:
  1692. while((pch < MaxStream) && (*pch != '\n') && SpIsSpace(*pch)) {
  1693. pch++;
  1694. }
  1695. //
  1696. // Check for comments and remove them
  1697. //
  1698. if((pch < MaxStream) && ((*pch == L';') || (*pch == L'#'))) {
  1699. while((pch < MaxStream) && (*pch != L'\n')) {
  1700. pch++;
  1701. }
  1702. }
  1703. //
  1704. // Check to see if EOF has been reached, set the token to the right
  1705. // value
  1706. //
  1707. if((pch >= MaxStream) || (*pch == 26)) {
  1708. *Stream = pch;
  1709. Token.Type = TOK_EOF;
  1710. Token.pValue = NULL;
  1711. return Token;
  1712. }
  1713. switch (*pch) {
  1714. case L'[' :
  1715. pch++;
  1716. Token.Type = TOK_LBRACE;
  1717. Token.pValue = NULL;
  1718. break;
  1719. case L']' :
  1720. pch++;
  1721. Token.Type = TOK_RBRACE;
  1722. Token.pValue = NULL;
  1723. break;
  1724. case L'=' :
  1725. pch++;
  1726. Token.Type = TOK_EQUAL;
  1727. Token.pValue = NULL;
  1728. break;
  1729. case L',' :
  1730. pch++;
  1731. Token.Type = TOK_COMMA;
  1732. Token.pValue = NULL;
  1733. break;
  1734. case L'\n' :
  1735. pch++;
  1736. Token.Type = TOK_EOL;
  1737. Token.pValue = NULL;
  1738. break;
  1739. case L'\"':
  1740. pch++;
  1741. //
  1742. // Determine quoted string. Within a quoted string, two double-quotes
  1743. // in a row are replaced by a single double-quote. In the normal case
  1744. // the number of characters in the string equals the number of
  1745. // characters in the input field (minus 2 for the quotes) so we
  1746. // preserve performance for that case. In the case where we have
  1747. // quoted quotes, we have to filter the string from the input file
  1748. // into our internal buffer to get rid of some quote chars.
  1749. //
  1750. // Note that in the txtsetup.sif case, setupldr has replaced all
  1751. // terminating quote chars with nul chars in the image we're parsing,
  1752. // so we have to deal with that here.
  1753. //
  1754. pchStart = pch;
  1755. QuotedQuotes = 0;
  1756. morequotedstring:
  1757. while((pch < MaxStream) && *pch && !wcschr(QStringTerminators,*pch)) {
  1758. pch++;
  1759. }
  1760. if(((pch+1) < MaxStream) && (*pch == L'\"') && (*(pch+1) == L'\"')) {
  1761. QuotedQuotes++;
  1762. pch += 2;
  1763. goto morequotedstring;
  1764. }
  1765. if((pch >= MaxStream) || ((*pch != L'\"') && *pch)) {
  1766. Token.Type = TOK_ERRPARSE;
  1767. Token.pValue = NULL;
  1768. } else {
  1769. Length = (ULONG)(((PUCHAR)pch - (PUCHAR)pchStart)/sizeof(WCHAR));
  1770. if ((pchNew = SpMemAllocEx(((Length + 1) - QuotedQuotes) * sizeof(WCHAR),'bteS', PagedPool)) == NULL) {
  1771. Token.Type = TOK_ERRNOMEM;
  1772. Token.pValue = NULL;
  1773. } else {
  1774. if(Length) { // Null quoted strings are allowed
  1775. if(QuotedQuotes) {
  1776. for(Length=0; pchStart<pch; pchStart++) {
  1777. if((pchNew[Length++] = *pchStart) == L'\"') {
  1778. //
  1779. // The only way this could happen is if there's
  1780. // another double-quote char after this one, since
  1781. // otherwise this would have terminated the string.
  1782. //
  1783. pchStart++;
  1784. }
  1785. }
  1786. } else {
  1787. wcsncpy(pchNew,pchStart,Length);
  1788. }
  1789. }
  1790. pchNew[Length] = 0;
  1791. Token.Type = TOK_STRING;
  1792. Token.pValue = pchNew;
  1793. }
  1794. pch++; // advance past the quote
  1795. }
  1796. break;
  1797. default:
  1798. //
  1799. // Check to see if we have a line continuation,
  1800. // which is \ followed by only whitespace on the line
  1801. //
  1802. pchStart = pch;
  1803. if((*pch == L'\\') && (HandleLineContinueChars)) {
  1804. pch++;
  1805. //
  1806. // Keep skipping until we hit the end of the file,
  1807. // or the newline, or a non-space character.
  1808. //
  1809. while((pch < MaxStream) && (*pch != L'\n') && SpIsSpace(*pch)) {
  1810. pch++;
  1811. }
  1812. if(pch < MaxStream) {
  1813. if(*pch == L'\n') {
  1814. //
  1815. // No non-space chars between the \ and the end of the line.
  1816. // Ignore the newline.
  1817. //
  1818. pch++;
  1819. *LineNumber = *LineNumber + 1;
  1820. goto restart;
  1821. } else {
  1822. //
  1823. // Not line continuation.
  1824. // Reset the input to the start of the field.
  1825. //
  1826. pch = pchStart;
  1827. }
  1828. }
  1829. }
  1830. //
  1831. // determine regular string
  1832. //
  1833. pchStart = pch;
  1834. while((pch < MaxStream) && (wcschr(StringTerminators,*pch) == NULL)) {
  1835. pch++;
  1836. }
  1837. if (pch == pchStart) {
  1838. pch++;
  1839. Token.Type = TOK_ERRPARSE;
  1840. Token.pValue = NULL;
  1841. }
  1842. else {
  1843. ULONG i;
  1844. Length = (ULONG)(((PUCHAR)pch - (PUCHAR)pchStart)/sizeof(WCHAR));
  1845. //
  1846. // Check for a common string...
  1847. //
  1848. for( i = 0; i < sizeof(CommonStrings)/sizeof(PWSTR); i++ ) {
  1849. if( !_wcsnicmp( pchStart, CommonStrings[i], Length ) ) {
  1850. break;
  1851. }
  1852. }
  1853. if( i < sizeof(CommonStrings)/sizeof(PWSTR) ) {
  1854. //
  1855. // Hit...
  1856. //
  1857. Token.Type = TOK_STRING;
  1858. Token.pValue = CommonStrings[i];
  1859. } else if((pchNew = SpMemAllocEx((Length + 1) * sizeof(WCHAR),'cteS', PagedPool)) == NULL) {
  1860. Token.Type = TOK_ERRNOMEM;
  1861. Token.pValue = NULL;
  1862. }
  1863. else {
  1864. wcsncpy(pchNew, pchStart, Length);
  1865. pchNew[Length] = 0;
  1866. Token.Type = TOK_STRING;
  1867. Token.pValue = pchNew;
  1868. }
  1869. }
  1870. break;
  1871. }
  1872. *Stream = pch;
  1873. return (Token);
  1874. }
  1875. #if DBG
  1876. VOID
  1877. pSpDumpTextFileInternals(
  1878. IN PVOID Handle
  1879. )
  1880. {
  1881. PTEXTFILE pInf = Handle;
  1882. PTEXTFILE_SECTION pSection;
  1883. PTEXTFILE_LINE pLine;
  1884. PTEXTFILE_VALUE pValue;
  1885. for(pSection = pInf->pSection; pSection; pSection = pSection->pNext) {
  1886. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "Section: [%ws]\r\n",pSection->pName));
  1887. for(pLine = pSection->pLine; pLine; pLine = pLine->pNext) {
  1888. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, " [%ws] = ",pLine->pName ? pLine->pName : L"(none)"));
  1889. for(pValue = pLine->pValue; pValue; pValue = pValue->pNext) {
  1890. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "[%ws] ",pValue->pName));
  1891. }
  1892. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "\n"));
  1893. }
  1894. }
  1895. }
  1896. #endif
  1897. PWSTR
  1898. SpGetKeyNameByValue(
  1899. IN PVOID Inf,
  1900. IN PWSTR SectionName,
  1901. IN PWSTR Value
  1902. )
  1903. /*++
  1904. Routine Description:
  1905. Determines the key name of a given value in a given section.
  1906. Arguments:
  1907. Inf - Handle to an inf file (txtsetup.sif or winnt.sif).
  1908. SectionName - Supplies the name of the section
  1909. Value - Supplies the string to be matched (eg. "Digital DECpc AXP 150")
  1910. Return Value:
  1911. NULL - No match was found.
  1912. PWSTR - Pointer to the canonical shortname of the component.
  1913. --*/
  1914. {
  1915. ULONG i;
  1916. PWSTR SearchName;
  1917. //
  1918. // If this is not an OEM component, then enumerate the entries in the
  1919. // section in txtsetup.sif
  1920. //
  1921. for (i=0;;i++) {
  1922. SearchName = SpGetSectionLineIndex(Inf,
  1923. SectionName,
  1924. i,
  1925. 0);
  1926. if (SearchName==NULL) {
  1927. //
  1928. // we have enumerated the entire section without finding a
  1929. // match, return failure.
  1930. //
  1931. return(NULL);
  1932. }
  1933. if (_wcsicmp(Value, SearchName) == 0) {
  1934. //
  1935. // we have a match
  1936. //
  1937. break;
  1938. }
  1939. }
  1940. //
  1941. // i is the index into the section of the short machine name
  1942. //
  1943. return(SpGetKeyName(Inf,
  1944. SectionName,
  1945. i));
  1946. }
  1947. ULONG
  1948. SpCountSectionsInFile(
  1949. IN PVOID Handle
  1950. )
  1951. {
  1952. PTEXTFILE_SECTION pSection;
  1953. PTEXTFILE pFile;
  1954. ULONG Count;
  1955. pFile = (PTEXTFILE)Handle;
  1956. for(pSection=pFile->pSection, Count = 0;
  1957. pSection;
  1958. pSection = pSection->pNext, Count++
  1959. );
  1960. return(Count);
  1961. }
  1962. PWSTR
  1963. SpGetSectionName(
  1964. IN PVOID Handle,
  1965. IN ULONG Index
  1966. )
  1967. {
  1968. PTEXTFILE_SECTION pSection;
  1969. PTEXTFILE pFile;
  1970. ULONG Count;
  1971. PWSTR SectionName;
  1972. pFile = (PTEXTFILE)Handle;
  1973. for(pSection=pFile->pSection, Count = 0;
  1974. pSection && (Count < Index);
  1975. pSection = pSection->pNext, Count++
  1976. );
  1977. return( (pSection != NULL)? pSection->pName : NULL );
  1978. }
  1979. NTSTATUS
  1980. SppWriteTextToFile(
  1981. IN PVOID Handle,
  1982. IN PWSTR String
  1983. )
  1984. {
  1985. NTSTATUS Status;
  1986. IO_STATUS_BLOCK IoStatusBlock;
  1987. PCHAR OemText;
  1988. OemText = SpToOem( String );
  1989. Status = ZwWriteFile( Handle,
  1990. NULL,
  1991. NULL,
  1992. NULL,
  1993. &IoStatusBlock,
  1994. OemText,
  1995. strlen( OemText ),
  1996. NULL,
  1997. NULL );
  1998. SpMemFree( OemText );
  1999. return( Status );
  2000. }
  2001. NTSTATUS
  2002. SpProcessAddRegSection(
  2003. IN PVOID SifHandle,
  2004. IN LPCWSTR SectionName,
  2005. IN HANDLE HKLM_SYSTEM,
  2006. IN HANDLE HKLM_SOFTWARE,
  2007. IN HANDLE HKCU,
  2008. IN HANDLE HKR
  2009. )
  2010. {
  2011. LPCWSTR p;
  2012. WCHAR *q;
  2013. ULONG Flags;
  2014. HKEY RootKey;
  2015. PTEXTFILE_SECTION pSection;
  2016. PTEXTFILE_LINE pLine;
  2017. PTEXTFILE_VALUE pValue;
  2018. LPWSTR buffer;
  2019. ULONG DataType;
  2020. LPCWSTR ValueName;
  2021. PVOID Data;
  2022. ULONG DataSize;
  2023. ULONG len;
  2024. HANDLE hkey;
  2025. UNICODE_STRING UnicodeString;
  2026. OBJECT_ATTRIBUTES ObjectAttributes;
  2027. NTSTATUS Status;
  2028. KEY_VALUE_BASIC_INFORMATION BasicInfo;
  2029. BOOLEAN b;
  2030. WCHAR c;
  2031. //
  2032. // Find the section.
  2033. //
  2034. pSection = SearchSectionByName(SifHandle,SectionName);
  2035. if(!pSection) {
  2036. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: addreg section %ws missing\n",SectionName));
  2037. return(STATUS_UNSUCCESSFUL);
  2038. }
  2039. for(pLine=pSection->pLine; pLine; pLine=pLine->pNext) {
  2040. buffer = TemporaryBuffer;
  2041. //
  2042. // 0th field is HKCU, HKLM, HKCR, or HKR.
  2043. // 1st field is subkey name, which may be empty.
  2044. //
  2045. if(pValue = pLine->pValue) {
  2046. b = pSpAdjustRootAndSubkeySpec(
  2047. SifHandle,
  2048. pValue->pName,
  2049. pValue->pNext ? pValue->pNext->pName : L"",
  2050. HKLM_SYSTEM,
  2051. HKLM_SOFTWARE,
  2052. HKCU,
  2053. HKR,
  2054. &RootKey,
  2055. buffer
  2056. );
  2057. if(!b) {
  2058. return(STATUS_UNSUCCESSFUL);
  2059. }
  2060. } else {
  2061. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: missing root key spec in section %ws\n",SectionName));
  2062. return(STATUS_UNSUCCESSFUL);
  2063. }
  2064. //
  2065. // Advance past key path to free area in buffer
  2066. //
  2067. buffer += wcslen(buffer) + 1;
  2068. //
  2069. // 2nd field is value name, which may be empty.
  2070. //
  2071. if(pValue = pValue->pNext) {
  2072. pValue = pValue->pNext;
  2073. }
  2074. if(pValue) {
  2075. p = pValue->pName;
  2076. pValue = pValue->pNext;
  2077. } else {
  2078. p = L"";
  2079. }
  2080. SpProcessForStringSubs(SifHandle,p,buffer,sizeof(TemporaryBuffer));
  2081. ValueName = buffer;
  2082. //
  2083. // Advance past value name to free area in buffer -
  2084. // align on DWORD boundary.
  2085. //
  2086. buffer += wcslen(buffer) + 1;
  2087. buffer = ALIGN_UP_POINTER(buffer,DWORD);
  2088. //
  2089. // 3rd field is flags.
  2090. //
  2091. if(pValue) {
  2092. SpProcessForStringSubs(SifHandle,pValue->pName,buffer,sizeof(TemporaryBuffer));
  2093. Flags = (ULONG)SpStringToLong(buffer,NULL,0);
  2094. pValue = pValue->pNext;
  2095. } else {
  2096. Flags = 0;
  2097. }
  2098. //
  2099. // 4th field and beyond is data, whose interpretation depends on
  2100. // the flags.
  2101. //
  2102. switch(Flags & FLG_ADDREG_TYPE_MASK) {
  2103. case FLG_ADDREG_TYPE_SZ:
  2104. DataType = REG_SZ;
  2105. break;
  2106. case FLG_ADDREG_TYPE_MULTI_SZ:
  2107. DataType = REG_MULTI_SZ;
  2108. break;
  2109. case FLG_ADDREG_TYPE_EXPAND_SZ:
  2110. DataType = REG_EXPAND_SZ;
  2111. break;
  2112. case FLG_ADDREG_TYPE_BINARY:
  2113. DataType = REG_BINARY;
  2114. break;
  2115. case FLG_ADDREG_TYPE_DWORD:
  2116. DataType = REG_DWORD;
  2117. break;
  2118. case FLG_ADDREG_TYPE_NONE:
  2119. DataType = REG_NONE;
  2120. break;
  2121. default:
  2122. //
  2123. // If the FLG_ADDREG_BINVALUETYPE is set, then the highword
  2124. // can contain just about any random reg data type ordinal value.
  2125. //
  2126. if(Flags & FLG_ADDREG_BINVALUETYPE) {
  2127. //
  2128. // Disallow the following reg data types:
  2129. //
  2130. // REG_NONE, REG_SZ, REG_EXPAND_SZ, REG_MULTI_SZ
  2131. //
  2132. DataType = (ULONG)HIWORD(Flags);
  2133. if((DataType < REG_BINARY) || (DataType == REG_MULTI_SZ)) {
  2134. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: bogus flags value 0x%lx in addreg section %ws\n",Flags,SectionName));
  2135. return(STATUS_UNSUCCESSFUL);
  2136. }
  2137. } else {
  2138. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: bogus flags value 0x%lx in addreg section %ws\n",Flags,SectionName));
  2139. return(STATUS_UNSUCCESSFUL);
  2140. }
  2141. }
  2142. //
  2143. // Don't support append flag for now.
  2144. //
  2145. if(Flags & FLG_ADDREG_APPEND) {
  2146. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: FLG_ADDREG_APPEND not supported\n"));
  2147. return(STATUS_UNSUCCESSFUL);
  2148. }
  2149. Data = buffer;
  2150. DataSize = 0;
  2151. switch(DataType) {
  2152. case REG_MULTI_SZ:
  2153. //
  2154. // Each remaining field is a member of the multi_sz
  2155. //
  2156. while(pValue) {
  2157. SpProcessForStringSubs(SifHandle,pValue->pName,buffer,sizeof(TemporaryBuffer));
  2158. len = wcslen(buffer);
  2159. buffer = buffer + len + 1;
  2160. DataSize += (len+1)*sizeof(WCHAR);
  2161. pValue = pValue->pNext;
  2162. }
  2163. *buffer = 0;
  2164. DataSize += sizeof(WCHAR);
  2165. break;
  2166. case REG_DWORD:
  2167. //
  2168. // Support specification as 4 bytes of binary data or as a dword.
  2169. //
  2170. *(PULONG)buffer = 0;
  2171. if(pValue) {
  2172. if(pValue->pNext
  2173. && pValue->pNext->pNext
  2174. && pValue->pNext->pNext->pNext
  2175. && !pValue->pNext->pNext->pNext->pNext) {
  2176. goto binarytype;
  2177. }
  2178. SpProcessForStringSubs(
  2179. SifHandle,
  2180. pValue->pName,
  2181. buffer,
  2182. sizeof(TemporaryBuffer)
  2183. );
  2184. *(PULONG)buffer = (ULONG)SpStringToLong(buffer,NULL,0);
  2185. }
  2186. buffer += sizeof(ULONG) / sizeof(WCHAR);
  2187. DataSize = sizeof(ULONG);
  2188. break;
  2189. case REG_SZ:
  2190. case REG_EXPAND_SZ:
  2191. p = pValue ? pValue->pName : L"";
  2192. SpProcessForStringSubs(SifHandle,p,buffer,sizeof(TemporaryBuffer));
  2193. len = wcslen(buffer);
  2194. DataSize = (len+1)*sizeof(WCHAR);
  2195. buffer = buffer + len + 1;
  2196. break;
  2197. case REG_BINARY:
  2198. default:
  2199. binarytype:
  2200. //
  2201. // All other types are specified in binary format.
  2202. //
  2203. while(pValue) {
  2204. // Advance past value name to free area in buffer -
  2205. // align on DWORD boundary.
  2206. //
  2207. q = buffer + 1 + sizeof(DWORD);
  2208. q = (LPWSTR) ((DWORD_PTR)q & (~((DWORD_PTR) sizeof(DWORD) - 1)));
  2209. SpProcessForStringSubs(SifHandle,pValue->pName,q,sizeof(TemporaryBuffer));
  2210. *(PUCHAR)buffer = (UCHAR)SpStringToLong(q,NULL,16);
  2211. pValue = pValue->pNext;
  2212. DataSize++;
  2213. buffer = (PWSTR) ((PUCHAR)buffer + 1);
  2214. }
  2215. break;
  2216. }
  2217. //
  2218. // Open/create the key if it's a subkey, otherwise just use
  2219. // the root key itself.
  2220. //
  2221. if(*TemporaryBuffer) {
  2222. InitializeObjectAttributes(
  2223. &ObjectAttributes,
  2224. &UnicodeString,
  2225. OBJ_CASE_INSENSITIVE,
  2226. RootKey,
  2227. NULL
  2228. );
  2229. //
  2230. // Multilevel key create, one component at a time.
  2231. //
  2232. q = TemporaryBuffer;
  2233. if(*q == L'\\') {
  2234. q++;
  2235. }
  2236. do {
  2237. if(q = wcschr(q,L'\\')) {
  2238. c = *q;
  2239. *q = 0;
  2240. } else {
  2241. c = 0;
  2242. }
  2243. RtlInitUnicodeString(&UnicodeString,TemporaryBuffer);
  2244. Status = ZwCreateKey(
  2245. &hkey,
  2246. READ_CONTROL | KEY_SET_VALUE,
  2247. &ObjectAttributes,
  2248. 0,
  2249. NULL,
  2250. REG_OPTION_NON_VOLATILE,
  2251. &len
  2252. );
  2253. if(!NT_SUCCESS(Status)) {
  2254. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: addreg: ZwCreateKey(%ws) failed %lx\n",TemporaryBuffer,Status));
  2255. return(Status);
  2256. }
  2257. if(c) {
  2258. *q = c;
  2259. q++;
  2260. //
  2261. // The call to ZwClose is in here so that we retain a handle
  2262. // to the final full key. We close it later, below.
  2263. //
  2264. ZwClose(hkey);
  2265. }
  2266. } while(c);
  2267. RtlInitUnicodeString(&UnicodeString,ValueName);
  2268. //
  2269. // If the key already existed and the noclobber flag is set,
  2270. // then leave the default value alone.
  2271. //
  2272. if(len == REG_OPENED_EXISTING_KEY) {
  2273. if((Flags & FLG_ADDREG_NOCLOBBER) && (*ValueName == 0)) {
  2274. //
  2275. // Nothing to do.
  2276. //
  2277. ZwClose(hkey);
  2278. continue;
  2279. } else if (Flags & FLG_ADDREG_DELVAL) {
  2280. //
  2281. // If this flag is set, ignore value data and delete the value.
  2282. //
  2283. ZwDeleteValueKey(hkey,&UnicodeString);
  2284. }
  2285. }
  2286. } else {
  2287. hkey = RootKey;
  2288. RtlInitUnicodeString(&UnicodeString,ValueName);
  2289. }
  2290. if(!(Flags & FLG_ADDREG_KEYONLY)) {
  2291. //
  2292. // If the noclobber flag is set, see if the value exists
  2293. // and if so leave it alone. To see if the value exists,
  2294. // we attempt to get basic information on the key and pass in
  2295. // a buffer that's large enough only for the fixed part of the
  2296. // basic info structure. If this is successful or reports buffer overflow,
  2297. // then the key exists. Otherwise it doesn't exist.
  2298. //
  2299. if(Flags & FLG_ADDREG_NOCLOBBER) {
  2300. Status = ZwQueryValueKey(
  2301. hkey,
  2302. &UnicodeString,
  2303. KeyValueBasicInformation,
  2304. &BasicInfo,
  2305. sizeof(BasicInfo),
  2306. &len
  2307. );
  2308. if(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW)) {
  2309. Status = STATUS_SUCCESS;
  2310. } else {
  2311. Status = ZwSetValueKey(hkey,&UnicodeString,0,DataType,Data,DataSize);
  2312. }
  2313. } else {
  2314. Status = ZwSetValueKey(hkey,&UnicodeString,0,DataType,Data,DataSize);
  2315. }
  2316. }
  2317. if(hkey != RootKey) {
  2318. ZwClose(hkey);
  2319. }
  2320. if(!NT_SUCCESS(Status)) {
  2321. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: addreg: status %lx adding value %ws to %ws\n",Status,ValueName,TemporaryBuffer));
  2322. return(Status);
  2323. }
  2324. }
  2325. return(STATUS_SUCCESS);
  2326. }
  2327. NTSTATUS
  2328. SpProcessDelRegSection(
  2329. IN PVOID SifHandle,
  2330. IN LPCWSTR SectionName,
  2331. IN HANDLE HKLM_SYSTEM,
  2332. IN HANDLE HKLM_SOFTWARE,
  2333. IN HANDLE HKCU,
  2334. IN HANDLE HKR
  2335. )
  2336. {
  2337. LPWSTR KeyPath;
  2338. LPWSTR ValueName;
  2339. PTEXTFILE_SECTION pSection;
  2340. PTEXTFILE_LINE pLine;
  2341. PTEXTFILE_VALUE pValue;
  2342. HKEY RootKey;
  2343. NTSTATUS Status;
  2344. HANDLE hkey;
  2345. UNICODE_STRING UnicodeString;
  2346. OBJECT_ATTRIBUTES ObjectAttributes;
  2347. BOOLEAN b;
  2348. //
  2349. // Allocate a temporary buffer. The registry delnode routine
  2350. // uses TemporaryBuffer so we can't use that.
  2351. //
  2352. KeyPath = SpMemAllocEx(1000,'dteS', PagedPool);
  2353. ValueName = SpMemAllocEx(1000,'eteS', PagedPool);
  2354. //
  2355. // Find the section.
  2356. //
  2357. pSection = SearchSectionByName(SifHandle,SectionName);
  2358. if(!pSection) {
  2359. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: delreg section %ws missing\n",SectionName));
  2360. SpMemFree(KeyPath);
  2361. SpMemFree(ValueName);
  2362. return(STATUS_UNSUCCESSFUL);
  2363. }
  2364. for(pLine=pSection->pLine; pLine; pLine=pLine->pNext) {
  2365. //
  2366. // 0th field is HKCU, HKLM, HKCR, or HKR.
  2367. // 1st field is subkey name.
  2368. // Both must be present.
  2369. //
  2370. if((pValue = pLine->pValue) && (pValue->pNext)) {
  2371. b = pSpAdjustRootAndSubkeySpec(
  2372. SifHandle,
  2373. pValue->pName,
  2374. pValue->pNext->pName,
  2375. HKLM_SYSTEM,
  2376. HKLM_SOFTWARE,
  2377. HKCU,
  2378. HKR,
  2379. &RootKey,
  2380. KeyPath
  2381. );
  2382. if(!b) {
  2383. SpMemFree(KeyPath);
  2384. SpMemFree(ValueName);
  2385. return(STATUS_UNSUCCESSFUL);
  2386. }
  2387. } else {
  2388. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: delreg: missing root key spec in section %ws\n",SectionName));
  2389. SpMemFree(KeyPath);
  2390. SpMemFree(ValueName);
  2391. return(STATUS_UNSUCCESSFUL);
  2392. }
  2393. //
  2394. // 2nd field is value name, which may be missing or empty.
  2395. // If it's missing, we want to delete the whole tree rooted at
  2396. // the given subkey. If it's present then we want to delete only
  2397. // one value (which may be the unnamed value).
  2398. //
  2399. if(pValue = pValue->pNext->pNext) {
  2400. SpProcessForStringSubs(SifHandle,pValue->pName,ValueName,1000);
  2401. if(*KeyPath) {
  2402. RtlInitUnicodeString(&UnicodeString,KeyPath);
  2403. InitializeObjectAttributes(
  2404. &ObjectAttributes,
  2405. &UnicodeString,
  2406. OBJ_CASE_INSENSITIVE,
  2407. RootKey,
  2408. NULL
  2409. );
  2410. Status = ZwOpenKey(
  2411. &hkey,
  2412. READ_CONTROL | KEY_SET_VALUE,
  2413. &ObjectAttributes
  2414. );
  2415. if(!NT_SUCCESS(Status)) {
  2416. if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
  2417. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: delreg: warning: key %ws not present for delete (%lx)\n",KeyPath,Status));
  2418. }
  2419. hkey = NULL;
  2420. }
  2421. } else {
  2422. Status = STATUS_SUCCESS;
  2423. hkey = RootKey;
  2424. }
  2425. if(NT_SUCCESS(Status)) {
  2426. RtlInitUnicodeString(&UnicodeString,ValueName);
  2427. Status = ZwDeleteValueKey(hkey,&UnicodeString);
  2428. if(!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND)) {
  2429. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: delreg: warning: delete value %ws from key %ws returned %lx\n",ValueName,KeyPath,Status));
  2430. }
  2431. }
  2432. if(hkey && (hkey != RootKey)) {
  2433. ZwClose(hkey);
  2434. }
  2435. } else {
  2436. //
  2437. // If we're trying to delete the key from the root of the hive,
  2438. // ignore it.
  2439. //
  2440. if(*KeyPath) {
  2441. SppDeleteKeyRecursive(RootKey,KeyPath,TRUE);
  2442. }
  2443. }
  2444. }
  2445. SpMemFree(ValueName);
  2446. SpMemFree(KeyPath);
  2447. return(STATUS_SUCCESS);
  2448. }
  2449. BOOLEAN
  2450. pSpAdjustRootAndSubkeySpec(
  2451. IN PVOID SifHandle,
  2452. IN LPCWSTR RootKeySpec,
  2453. IN LPCWSTR SubkeySpec,
  2454. IN HANDLE HKLM_SYSTEM,
  2455. IN HANDLE HKLM_SOFTWARE,
  2456. IN HANDLE HKCU,
  2457. IN HANDLE HKR,
  2458. OUT HANDLE *RootKey,
  2459. OUT LPWSTR Subkey
  2460. )
  2461. {
  2462. ULONG len;
  2463. if(*SubkeySpec == L'\\') {
  2464. SubkeySpec++;
  2465. }
  2466. if(!_wcsicmp(RootKeySpec,L"HKCR")) {
  2467. //
  2468. // HKEY_CLASSES_ROOT. The ultimate root is HKLM\Software\Classes.
  2469. // We take care not to produce a subkey spec that ends with \.
  2470. //
  2471. *RootKey = HKLM_SOFTWARE;
  2472. wcscpy(Subkey,L"Classes");
  2473. if(*SubkeySpec) {
  2474. if(*SubkeySpec == L'\\') {
  2475. SubkeySpec++;
  2476. }
  2477. Subkey[7] = L'\\';
  2478. SpProcessForStringSubs(SifHandle,SubkeySpec,Subkey+8,sizeof(TemporaryBuffer));
  2479. }
  2480. } else {
  2481. if(!_wcsicmp(RootKeySpec,L"HKLM")) {
  2482. //
  2483. // The first component of the subkey must be SYSTEM or SOFTWARE.
  2484. //
  2485. len = wcslen(SubkeySpec);
  2486. if((len >= 8)
  2487. && ((SubkeySpec[8] == L'\\') || !SubkeySpec[8])
  2488. && !_wcsnicmp(SubkeySpec,L"software",8)) {
  2489. *RootKey = HKLM_SOFTWARE;
  2490. SubkeySpec += 8;
  2491. } else {
  2492. if((len >= 6)
  2493. && ((SubkeySpec[6] == L'\\') || !SubkeySpec[6])
  2494. && !_wcsnicmp(SubkeySpec,L"system",6)) {
  2495. *RootKey = HKLM_SYSTEM;
  2496. SubkeySpec += 6;
  2497. } else {
  2498. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unknown root/subkey spec %ws %ws\n",RootKeySpec,SubkeySpec));
  2499. return(FALSE);
  2500. }
  2501. }
  2502. if(*SubkeySpec == L'\\') {
  2503. SubkeySpec++;
  2504. }
  2505. SpProcessForStringSubs(SifHandle,SubkeySpec,Subkey,sizeof(TemporaryBuffer));
  2506. } else {
  2507. if(!_wcsicmp(RootKeySpec,L"HKCU")) {
  2508. *RootKey = HKCU;
  2509. SpProcessForStringSubs(SifHandle,SubkeySpec,Subkey,sizeof(TemporaryBuffer));
  2510. } else {
  2511. if(!_wcsicmp(RootKeySpec,L"HKR")) {
  2512. *RootKey = HKR;
  2513. SpProcessForStringSubs(SifHandle,SubkeySpec,Subkey,sizeof(TemporaryBuffer));
  2514. } else {
  2515. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unknown root key spec %ws\n",RootKeySpec));
  2516. return(FALSE);
  2517. }
  2518. }
  2519. }
  2520. }
  2521. return(TRUE);
  2522. }
  2523. BOOLEAN
  2524. pSpIsFileInPrivateInf(
  2525. IN PCWSTR FileName
  2526. )
  2527. /*++
  2528. Routine Description:
  2529. Tell the caller if the file specified is in the inf that
  2530. lists the privates being tested (as specifed by using
  2531. the /M flag in winnt32.exe).
  2532. Arguments:
  2533. FileName - supplies name of the file we're looking for.
  2534. Return Value:
  2535. TRUE/FALSE indicating the presence of the file in the inf.
  2536. --*/
  2537. {
  2538. PWSTR SectionName = L"Privates";
  2539. PWSTR InfFileName;
  2540. UINT FileCount,i;
  2541. if (!PrivateInfHandle) {
  2542. return(FALSE);
  2543. }
  2544. FileCount = SpCountLinesInSection(PrivateInfHandle, SectionName);
  2545. for (i=0; i< FileCount; i++) {
  2546. InfFileName = SpGetSectionLineIndex( PrivateInfHandle, SectionName, i, 0);
  2547. if (InfFileName) {
  2548. if (wcscmp(InfFileName, FileName) == 0) {
  2549. return(TRUE);
  2550. }
  2551. }
  2552. }
  2553. return(FALSE);
  2554. }
  2555. BOOLEAN
  2556. SpNonZeroValuesInSection(
  2557. PVOID Handle,
  2558. PCWSTR SectionName,
  2559. ULONG ValueIndex
  2560. )
  2561. {
  2562. PTEXTFILE_SECTION pSection;
  2563. PTEXTFILE_LINE pLine;
  2564. PTEXTFILE_VALUE pVal;
  2565. ULONG i;
  2566. pSection = SearchSectionByName((PTEXTFILE) Handle, SectionName);
  2567. for(i = 0; (pLine = SearchLineInSectionByIndex(pSection, i)) != NULL; ++i) {
  2568. pVal = SearchValueInLine(pLine, ValueIndex);
  2569. if(pVal != NULL && pVal->pName != NULL && SpStringToLong(pVal->pName, NULL, 0) != 0) {
  2570. return TRUE;
  2571. }
  2572. }
  2573. return FALSE;
  2574. }