1 /*
2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: INI file parser that caches contents of INI file in memory.
5 * COPYRIGHT: Copyright 2002-2018 Royce Mitchell III
6 */
7
8 /* INCLUDES *****************************************************************/
9
10 #include "precomp.h"
11
12 #include "inicache.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* PRIVATE FUNCTIONS ********************************************************/
18
19 static VOID
IniCacheFreeKey(_In_ PINI_KEYWORD Key)20 IniCacheFreeKey(
21 _In_ PINI_KEYWORD Key)
22 {
23 /* Unlink the key */
24 RemoveEntryList(&Key->ListEntry);
25
26 /* Free its data */
27 if (Key->Name)
28 RtlFreeHeap(ProcessHeap, 0, Key->Name);
29 if (Key->Data)
30 RtlFreeHeap(ProcessHeap, 0, Key->Data);
31 RtlFreeHeap(ProcessHeap, 0, Key);
32 }
33
34 static VOID
IniCacheFreeSection(_In_ PINI_SECTION Section)35 IniCacheFreeSection(
36 _In_ PINI_SECTION Section)
37 {
38 /* Unlink the section */
39 RemoveEntryList(&Section->ListEntry);
40
41 /* Free its data */
42 while (!IsListEmpty(&Section->KeyList))
43 {
44 PLIST_ENTRY Entry = RemoveHeadList(&Section->KeyList);
45 PINI_KEYWORD Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
46 IniCacheFreeKey(Key);
47 }
48 if (Section->Name)
49 RtlFreeHeap(ProcessHeap, 0, Section->Name);
50 RtlFreeHeap(ProcessHeap, 0, Section);
51 }
52
53 static
54 PINI_SECTION
IniCacheFindSection(_In_ PINICACHE Cache,_In_ PCWSTR Name)55 IniCacheFindSection(
56 _In_ PINICACHE Cache,
57 _In_ PCWSTR Name)
58 {
59 PLIST_ENTRY Entry;
60
61 for (Entry = Cache->SectionList.Flink;
62 Entry != &Cache->SectionList;
63 Entry = Entry->Flink)
64 {
65 PINI_SECTION Section = CONTAINING_RECORD(Entry, INI_SECTION, ListEntry);
66 if (_wcsicmp(Section->Name, Name) == 0)
67 return Section;
68 }
69 return NULL;
70 }
71
72 static
73 PINI_KEYWORD
IniCacheFindKey(_In_ PINI_SECTION Section,_In_ PCWSTR Name)74 IniCacheFindKey(
75 _In_ PINI_SECTION Section,
76 _In_ PCWSTR Name)
77 {
78 PLIST_ENTRY Entry;
79
80 for (Entry = Section->KeyList.Flink;
81 Entry != &Section->KeyList;
82 Entry = Entry->Flink)
83 {
84 PINI_KEYWORD Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
85 if (_wcsicmp(Key->Name, Name) == 0)
86 return Key;
87 }
88 return NULL;
89 }
90
91 static
92 PINI_KEYWORD
IniCacheAddKeyAorW(_In_ PINI_SECTION Section,_In_ PINI_KEYWORD AnchorKey,_In_ INSERTION_TYPE InsertionType,_In_ const VOID * Name,_In_ ULONG NameLength,_In_ const VOID * Data,_In_ ULONG DataLength,_In_ BOOLEAN IsUnicode)93 IniCacheAddKeyAorW(
94 _In_ PINI_SECTION Section,
95 _In_ PINI_KEYWORD AnchorKey,
96 _In_ INSERTION_TYPE InsertionType,
97 _In_ const VOID* Name,
98 _In_ ULONG NameLength,
99 _In_ const VOID* Data,
100 _In_ ULONG DataLength,
101 _In_ BOOLEAN IsUnicode)
102 {
103 PINI_KEYWORD Key;
104 PWSTR NameU, DataU;
105
106 if (!Section || !Name || NameLength == 0 || !Data || DataLength == 0)
107 {
108 DPRINT("Invalid parameter\n");
109 return NULL;
110 }
111
112 /* Allocate the UNICODE key name */
113 NameU = (PWSTR)RtlAllocateHeap(ProcessHeap,
114 0,
115 (NameLength + 1) * sizeof(WCHAR));
116 if (!NameU)
117 {
118 DPRINT("RtlAllocateHeap() failed\n");
119 return NULL;
120 }
121 /* Copy the value name (ANSI or UNICODE) */
122 if (IsUnicode)
123 wcsncpy(NameU, (PCWCH)Name, NameLength);
124 else
125 _snwprintf(NameU, NameLength, L"%.*S", NameLength, (PCCH)Name);
126 NameU[NameLength] = UNICODE_NULL;
127
128 /*
129 * Find whether a key with the given name already exists in the section.
130 * If so, modify the data and return it; otherwise create a new one.
131 */
132 Key = IniCacheFindKey(Section, NameU);
133 if (Key)
134 {
135 RtlFreeHeap(ProcessHeap, 0, NameU);
136
137 /* Modify the existing data */
138
139 /* Allocate the UNICODE data buffer */
140 DataU = (PWSTR)RtlAllocateHeap(ProcessHeap,
141 0,
142 (DataLength + 1) * sizeof(WCHAR));
143 if (!DataU)
144 {
145 DPRINT("RtlAllocateHeap() failed\n");
146 return NULL; // We failed, don't modify the original key.
147 }
148 /* Copy the data (ANSI or UNICODE) */
149 if (IsUnicode)
150 wcsncpy(DataU, (PCWCH)Data, DataLength);
151 else
152 _snwprintf(DataU, DataLength, L"%.*S", DataLength, (PCCH)Data);
153 DataU[DataLength] = UNICODE_NULL;
154
155 /* Swap the old key data with the new one */
156 RtlFreeHeap(ProcessHeap, 0, Key->Data);
157 Key->Data = DataU;
158
159 /* Return the modified key */
160 return Key;
161 }
162
163 /* Allocate the key buffer and name */
164 Key = (PINI_KEYWORD)RtlAllocateHeap(ProcessHeap,
165 HEAP_ZERO_MEMORY,
166 sizeof(INI_KEYWORD));
167 if (!Key)
168 {
169 DPRINT("RtlAllocateHeap() failed\n");
170 RtlFreeHeap(ProcessHeap, 0, NameU);
171 return NULL;
172 }
173 Key->Name = NameU;
174
175 /* Allocate the UNICODE data buffer */
176 DataU = (PWSTR)RtlAllocateHeap(ProcessHeap,
177 0,
178 (DataLength + 1) * sizeof(WCHAR));
179 if (!DataU)
180 {
181 DPRINT("RtlAllocateHeap() failed\n");
182 RtlFreeHeap(ProcessHeap, 0, NameU);
183 RtlFreeHeap(ProcessHeap, 0, Key);
184 return NULL;
185 }
186 /* Copy the data (ANSI or UNICODE) */
187 if (IsUnicode)
188 wcsncpy(DataU, (PCWCH)Data, DataLength);
189 else
190 _snwprintf(DataU, DataLength, L"%.*S", DataLength, (PCCH)Data);
191 DataU[DataLength] = UNICODE_NULL;
192 Key->Data = DataU;
193
194 /* Insert the key into section */
195 if (IsListEmpty(&Section->KeyList))
196 {
197 InsertHeadList(&Section->KeyList, &Key->ListEntry);
198 }
199 else if ((InsertionType == INSERT_FIRST) ||
200 ((InsertionType == INSERT_BEFORE) &&
201 (!AnchorKey || (&AnchorKey->ListEntry == Section->KeyList.Flink))))
202 {
203 /* Insert at the head of the list */
204 InsertHeadList(&Section->KeyList, &Key->ListEntry);
205 }
206 else if ((InsertionType == INSERT_BEFORE) && AnchorKey)
207 {
208 /* Insert before the anchor key */
209 InsertTailList(&AnchorKey->ListEntry, &Key->ListEntry);
210 }
211 else if ((InsertionType == INSERT_LAST) ||
212 ((InsertionType == INSERT_AFTER) &&
213 (!AnchorKey || (&AnchorKey->ListEntry == Section->KeyList.Blink))))
214 {
215 /* Insert at the tail of the list */
216 InsertTailList(&Section->KeyList, &Key->ListEntry);
217 }
218 else if ((InsertionType == INSERT_AFTER) && AnchorKey)
219 {
220 /* Insert after the anchor key */
221 InsertHeadList(&AnchorKey->ListEntry, &Key->ListEntry);
222 }
223
224 return Key;
225 }
226
227 static
228 PINI_SECTION
IniCacheAddSectionAorW(_In_ PINICACHE Cache,_In_ const VOID * Name,_In_ ULONG NameLength,_In_ BOOLEAN IsUnicode)229 IniCacheAddSectionAorW(
230 _In_ PINICACHE Cache,
231 _In_ const VOID* Name,
232 _In_ ULONG NameLength,
233 _In_ BOOLEAN IsUnicode)
234 {
235 PINI_SECTION Section;
236 PWSTR NameU;
237
238 if (!Cache || !Name || NameLength == 0)
239 {
240 DPRINT("Invalid parameter\n");
241 return NULL;
242 }
243
244 /* Allocate the UNICODE section name */
245 NameU = (PWSTR)RtlAllocateHeap(ProcessHeap,
246 0,
247 (NameLength + 1) * sizeof(WCHAR));
248 if (!NameU)
249 {
250 DPRINT("RtlAllocateHeap() failed\n");
251 return NULL;
252 }
253 /* Copy the section name (ANSI or UNICODE) */
254 if (IsUnicode)
255 wcsncpy(NameU, (PCWCH)Name, NameLength);
256 else
257 _snwprintf(NameU, NameLength, L"%.*S", NameLength, (PCCH)Name);
258 NameU[NameLength] = UNICODE_NULL;
259
260 /*
261 * Find whether a section with the given name already exists.
262 * If so, just return it; otherwise create a new one.
263 */
264 Section = IniCacheFindSection(Cache, NameU);
265 if (Section)
266 {
267 RtlFreeHeap(ProcessHeap, 0, NameU);
268 return Section;
269 }
270
271 /* Allocate the section buffer and name */
272 Section = (PINI_SECTION)RtlAllocateHeap(ProcessHeap,
273 HEAP_ZERO_MEMORY,
274 sizeof(INI_SECTION));
275 if (!Section)
276 {
277 DPRINT("RtlAllocateHeap() failed\n");
278 RtlFreeHeap(ProcessHeap, 0, NameU);
279 return NULL;
280 }
281 Section->Name = NameU;
282 InitializeListHead(&Section->KeyList);
283
284 /* Append the section */
285 InsertTailList(&Cache->SectionList, &Section->ListEntry);
286
287 return Section;
288 }
289
290 static
291 PCHAR
IniCacheSkipWhitespace(PCHAR Ptr)292 IniCacheSkipWhitespace(
293 PCHAR Ptr)
294 {
295 while (*Ptr != 0 && isspace(*Ptr))
296 Ptr++;
297
298 return (*Ptr == 0) ? NULL : Ptr;
299 }
300
301 static
302 PCHAR
IniCacheSkipToNextSection(PCHAR Ptr)303 IniCacheSkipToNextSection(
304 PCHAR Ptr)
305 {
306 while (*Ptr != 0 && *Ptr != '[')
307 {
308 while (*Ptr != 0 && *Ptr != L'\n')
309 {
310 Ptr++;
311 }
312
313 Ptr++;
314 }
315
316 return (*Ptr == 0) ? NULL : Ptr;
317 }
318
319 static
320 PCHAR
IniCacheGetSectionName(PCHAR Ptr,PCHAR * NamePtr,PULONG NameSize)321 IniCacheGetSectionName(
322 PCHAR Ptr,
323 PCHAR *NamePtr,
324 PULONG NameSize)
325 {
326 ULONG Size = 0;
327
328 *NamePtr = NULL;
329 *NameSize = 0;
330
331 /* Skip whitespace */
332 while (*Ptr != 0 && isspace(*Ptr))
333 {
334 Ptr++;
335 }
336
337 *NamePtr = Ptr;
338
339 while (*Ptr != 0 && *Ptr != ']')
340 {
341 Size++;
342 Ptr++;
343 }
344 Ptr++;
345
346 while (*Ptr != 0 && *Ptr != L'\n')
347 {
348 Ptr++;
349 }
350 Ptr++;
351
352 *NameSize = Size;
353
354 DPRINT("SectionName: '%.*s'\n", Size, *NamePtr);
355
356 return Ptr;
357 }
358
359 static
360 PCHAR
IniCacheGetKeyName(PCHAR Ptr,PCHAR * NamePtr,PULONG NameSize)361 IniCacheGetKeyName(
362 PCHAR Ptr,
363 PCHAR *NamePtr,
364 PULONG NameSize)
365 {
366 ULONG Size = 0;
367
368 *NamePtr = NULL;
369 *NameSize = 0;
370
371 while (Ptr && *Ptr)
372 {
373 *NamePtr = NULL;
374 *NameSize = 0;
375 Size = 0;
376
377 /* Skip whitespace and empty lines */
378 while (isspace(*Ptr) || *Ptr == '\n' || *Ptr == '\r')
379 {
380 Ptr++;
381 }
382 if (*Ptr == 0)
383 {
384 continue;
385 }
386
387 *NamePtr = Ptr;
388
389 while (*Ptr != 0 && !isspace(*Ptr) && *Ptr != '=' && *Ptr != ';')
390 {
391 Size++;
392 Ptr++;
393 }
394 if (*Ptr == ';')
395 {
396 while (*Ptr != 0 && *Ptr != '\r' && *Ptr != '\n')
397 {
398 Ptr++;
399 }
400 }
401 else
402 {
403 *NameSize = Size;
404 break;
405 }
406 }
407
408 return Ptr;
409 }
410
411 static
412 PCHAR
IniCacheGetKeyValue(PCHAR Ptr,PCHAR * DataPtr,PULONG DataSize,BOOLEAN String)413 IniCacheGetKeyValue(
414 PCHAR Ptr,
415 PCHAR *DataPtr,
416 PULONG DataSize,
417 BOOLEAN String)
418 {
419 ULONG Size = 0;
420
421 *DataPtr = NULL;
422 *DataSize = 0;
423
424 /* Skip whitespace */
425 while (*Ptr != 0 && isspace(*Ptr))
426 {
427 Ptr++;
428 }
429
430 /* Check and skip '=' */
431 if (*Ptr != '=')
432 {
433 return NULL;
434 }
435 Ptr++;
436
437 /* Skip whitespace */
438 while (*Ptr != 0 && isspace(*Ptr))
439 {
440 Ptr++;
441 }
442
443 if (*Ptr == '"' && String)
444 {
445 Ptr++;
446
447 /* Get data */
448 *DataPtr = Ptr;
449 while (*Ptr != '"')
450 {
451 Ptr++;
452 Size++;
453 }
454 Ptr++;
455
456 while (*Ptr && *Ptr != '\r' && *Ptr != '\n')
457 {
458 Ptr++;
459 }
460 }
461 else
462 {
463 /* Get data */
464 *DataPtr = Ptr;
465 while (*Ptr != 0 && *Ptr != '\r' && *Ptr != ';')
466 {
467 Ptr++;
468 Size++;
469 }
470 }
471
472 /* Skip to next line */
473 if (*Ptr == '\r')
474 Ptr++;
475 if (*Ptr == '\n')
476 Ptr++;
477
478 *DataSize = Size;
479
480 return Ptr;
481 }
482
483
484 /* PUBLIC FUNCTIONS *********************************************************/
485
486 NTSTATUS
IniCacheLoadFromMemory(PINICACHE * Cache,PCHAR FileBuffer,ULONG FileLength,BOOLEAN String)487 IniCacheLoadFromMemory(
488 PINICACHE *Cache,
489 PCHAR FileBuffer,
490 ULONG FileLength,
491 BOOLEAN String)
492 {
493 PCHAR Ptr;
494
495 PINI_SECTION Section;
496 PINI_KEYWORD Key;
497
498 PCHAR SectionName;
499 ULONG SectionNameSize;
500
501 PCHAR KeyName;
502 ULONG KeyNameSize;
503
504 PCHAR KeyValue;
505 ULONG KeyValueSize;
506
507 /* Allocate inicache header */
508 *Cache = IniCacheCreate();
509 if (!*Cache)
510 return STATUS_INSUFFICIENT_RESOURCES;
511
512 /* Parse ini file */
513 Section = NULL;
514 Ptr = FileBuffer;
515 while (Ptr != NULL && *Ptr != 0)
516 {
517 Ptr = IniCacheSkipWhitespace(Ptr);
518 if (Ptr == NULL)
519 continue;
520
521 if (*Ptr == '[')
522 {
523 Section = NULL;
524 Ptr++;
525
526 Ptr = IniCacheGetSectionName(Ptr,
527 &SectionName,
528 &SectionNameSize);
529
530 DPRINT("[%.*s]\n", SectionNameSize, SectionName);
531
532 Section = IniCacheAddSectionAorW(*Cache,
533 SectionName,
534 SectionNameSize,
535 FALSE);
536 if (Section == NULL)
537 {
538 DPRINT("IniCacheAddSectionAorW() failed\n");
539 Ptr = IniCacheSkipToNextSection(Ptr);
540 continue;
541 }
542 }
543 else
544 {
545 if (Section == NULL)
546 {
547 Ptr = IniCacheSkipToNextSection(Ptr);
548 continue;
549 }
550
551 Ptr = IniCacheGetKeyName(Ptr,
552 &KeyName,
553 &KeyNameSize);
554
555 Ptr = IniCacheGetKeyValue(Ptr,
556 &KeyValue,
557 &KeyValueSize,
558 String);
559
560 DPRINT("'%.*s' = '%.*s'\n", KeyNameSize, KeyName, KeyValueSize, KeyValue);
561
562 Key = IniCacheAddKeyAorW(Section,
563 NULL,
564 INSERT_LAST,
565 KeyName,
566 KeyNameSize,
567 KeyValue,
568 KeyValueSize,
569 FALSE);
570 if (Key == NULL)
571 {
572 DPRINT("IniCacheAddKeyAorW() failed\n");
573 }
574 }
575 }
576
577 return STATUS_SUCCESS;
578 }
579
580 NTSTATUS
IniCacheLoadByHandle(PINICACHE * Cache,HANDLE FileHandle,BOOLEAN String)581 IniCacheLoadByHandle(
582 PINICACHE *Cache,
583 HANDLE FileHandle,
584 BOOLEAN String)
585 {
586 NTSTATUS Status;
587 IO_STATUS_BLOCK IoStatusBlock;
588 FILE_STANDARD_INFORMATION FileInfo;
589 PCHAR FileBuffer;
590 ULONG FileLength;
591 LARGE_INTEGER FileOffset;
592
593 *Cache = NULL;
594
595 /* Query file size */
596 Status = NtQueryInformationFile(FileHandle,
597 &IoStatusBlock,
598 &FileInfo,
599 sizeof(FILE_STANDARD_INFORMATION),
600 FileStandardInformation);
601 if (!NT_SUCCESS(Status))
602 {
603 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
604 return Status;
605 }
606
607 FileLength = FileInfo.EndOfFile.u.LowPart;
608
609 DPRINT("File size: %lu\n", FileLength);
610
611 /* Allocate file buffer with NULL-terminator */
612 FileBuffer = (PCHAR)RtlAllocateHeap(ProcessHeap,
613 0,
614 FileLength + 1);
615 if (FileBuffer == NULL)
616 {
617 DPRINT1("RtlAllocateHeap() failed\n");
618 return STATUS_INSUFFICIENT_RESOURCES;
619 }
620
621 /* Read file */
622 FileOffset.QuadPart = 0ULL;
623 Status = NtReadFile(FileHandle,
624 NULL,
625 NULL,
626 NULL,
627 &IoStatusBlock,
628 FileBuffer,
629 FileLength,
630 &FileOffset,
631 NULL);
632
633 /* Append NULL-terminator */
634 FileBuffer[FileLength] = 0;
635
636 if (!NT_SUCCESS(Status))
637 {
638 DPRINT("NtReadFile() failed (Status %lx)\n", Status);
639 goto Quit;
640 }
641
642 Status = IniCacheLoadFromMemory(Cache, FileBuffer, FileLength, String);
643 if (!NT_SUCCESS(Status))
644 {
645 DPRINT1("IniCacheLoadFromMemory() failed (Status %lx)\n", Status);
646 }
647
648 Quit:
649 /* Free the file buffer, and return */
650 RtlFreeHeap(ProcessHeap, 0, FileBuffer);
651 return Status;
652 }
653
654 NTSTATUS
IniCacheLoad(PINICACHE * Cache,PWCHAR FileName,BOOLEAN String)655 IniCacheLoad(
656 PINICACHE *Cache,
657 PWCHAR FileName,
658 BOOLEAN String)
659 {
660 NTSTATUS Status;
661 UNICODE_STRING Name;
662 OBJECT_ATTRIBUTES ObjectAttributes;
663 IO_STATUS_BLOCK IoStatusBlock;
664 HANDLE FileHandle;
665
666 *Cache = NULL;
667
668 /* Open the INI file */
669 RtlInitUnicodeString(&Name, FileName);
670
671 InitializeObjectAttributes(&ObjectAttributes,
672 &Name,
673 OBJ_CASE_INSENSITIVE,
674 NULL,
675 NULL);
676
677 Status = NtOpenFile(&FileHandle,
678 FILE_GENERIC_READ | SYNCHRONIZE,
679 &ObjectAttributes,
680 &IoStatusBlock,
681 FILE_SHARE_READ,
682 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
683 if (!NT_SUCCESS(Status))
684 {
685 DPRINT("NtOpenFile() failed (Status %lx)\n", Status);
686 return Status;
687 }
688
689 DPRINT("NtOpenFile() successful\n");
690
691 Status = IniCacheLoadByHandle(Cache, FileHandle, String);
692
693 /* Close the INI file */
694 NtClose(FileHandle);
695 return Status;
696 }
697
698 VOID
IniCacheDestroy(_In_ PINICACHE Cache)699 IniCacheDestroy(
700 _In_ PINICACHE Cache)
701 {
702 if (!Cache)
703 return;
704
705 while (!IsListEmpty(&Cache->SectionList))
706 {
707 PLIST_ENTRY Entry = RemoveHeadList(&Cache->SectionList);
708 PINI_SECTION Section = CONTAINING_RECORD(Entry, INI_SECTION, ListEntry);
709 IniCacheFreeSection(Section);
710 }
711
712 RtlFreeHeap(ProcessHeap, 0, Cache);
713 }
714
715
716 PINI_SECTION
IniGetSection(_In_ PINICACHE Cache,_In_ PCWSTR Name)717 IniGetSection(
718 _In_ PINICACHE Cache,
719 _In_ PCWSTR Name)
720 {
721 if (!Cache || !Name)
722 {
723 DPRINT("Invalid parameter\n");
724 return NULL;
725 }
726 return IniCacheFindSection(Cache, Name);
727 }
728
729 PINI_KEYWORD
IniGetKey(_In_ PINI_SECTION Section,_In_ PCWSTR KeyName,_Out_ PCWSTR * KeyData)730 IniGetKey(
731 _In_ PINI_SECTION Section,
732 _In_ PCWSTR KeyName,
733 _Out_ PCWSTR* KeyData)
734 {
735 PINI_KEYWORD Key;
736
737 if (!Section || !KeyName || !KeyData)
738 {
739 DPRINT("Invalid parameter\n");
740 return NULL;
741 }
742
743 *KeyData = NULL;
744
745 Key = IniCacheFindKey(Section, KeyName);
746 if (!Key)
747 return NULL;
748
749 *KeyData = Key->Data;
750
751 return Key;
752 }
753
754
755 PINICACHEITERATOR
IniFindFirstValue(_In_ PINI_SECTION Section,_Out_ PCWSTR * KeyName,_Out_ PCWSTR * KeyData)756 IniFindFirstValue(
757 _In_ PINI_SECTION Section,
758 _Out_ PCWSTR* KeyName,
759 _Out_ PCWSTR* KeyData)
760 {
761 PINICACHEITERATOR Iterator;
762 PLIST_ENTRY Entry;
763 PINI_KEYWORD Key;
764
765 if (!Section || !KeyName || !KeyData)
766 {
767 DPRINT("Invalid parameter\n");
768 return NULL;
769 }
770
771 Entry = Section->KeyList.Flink;
772 if (Entry == &Section->KeyList)
773 {
774 DPRINT("Invalid parameter\n");
775 return NULL;
776 }
777 Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
778
779 Iterator = (PINICACHEITERATOR)RtlAllocateHeap(ProcessHeap,
780 0,
781 sizeof(INICACHEITERATOR));
782 if (!Iterator)
783 {
784 DPRINT("RtlAllocateHeap() failed\n");
785 return NULL;
786 }
787 Iterator->Section = Section;
788 Iterator->Key = Key;
789
790 *KeyName = Key->Name;
791 *KeyData = Key->Data;
792
793 return Iterator;
794 }
795
796 BOOLEAN
IniFindNextValue(_In_ PINICACHEITERATOR Iterator,_Out_ PCWSTR * KeyName,_Out_ PCWSTR * KeyData)797 IniFindNextValue(
798 _In_ PINICACHEITERATOR Iterator,
799 _Out_ PCWSTR* KeyName,
800 _Out_ PCWSTR* KeyData)
801 {
802 PLIST_ENTRY Entry;
803 PINI_KEYWORD Key;
804
805 if (!Iterator || !KeyName || !KeyData)
806 {
807 DPRINT("Invalid parameter\n");
808 return FALSE;
809 }
810
811 Entry = Iterator->Key->ListEntry.Flink;
812 if (Entry == &Iterator->Section->KeyList)
813 {
814 DPRINT("No more entries\n");
815 return FALSE;
816 }
817 Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
818
819 Iterator->Key = Key;
820
821 *KeyName = Key->Name;
822 *KeyData = Key->Data;
823
824 return TRUE;
825 }
826
827 VOID
IniFindClose(_In_ PINICACHEITERATOR Iterator)828 IniFindClose(
829 _In_ PINICACHEITERATOR Iterator)
830 {
831 if (!Iterator)
832 return;
833 RtlFreeHeap(ProcessHeap, 0, Iterator);
834 }
835
836
837 PINI_SECTION
IniAddSection(_In_ PINICACHE Cache,_In_ PCWSTR Name)838 IniAddSection(
839 _In_ PINICACHE Cache,
840 _In_ PCWSTR Name)
841 {
842 if (!Cache || !Name || !*Name)
843 {
844 DPRINT("Invalid parameter\n");
845 return NULL;
846 }
847 return IniCacheAddSectionAorW(Cache, Name, wcslen(Name), TRUE);
848 }
849
850 VOID
IniRemoveSection(_In_ PINI_SECTION Section)851 IniRemoveSection(
852 _In_ PINI_SECTION Section)
853 {
854 if (!Section)
855 {
856 DPRINT("Invalid parameter\n");
857 return;
858 }
859 IniCacheFreeSection(Section);
860 }
861
862 PINI_KEYWORD
IniInsertKey(_In_ PINI_SECTION Section,_In_ PINI_KEYWORD AnchorKey,_In_ INSERTION_TYPE InsertionType,_In_ PCWSTR Name,_In_ PCWSTR Data)863 IniInsertKey(
864 _In_ PINI_SECTION Section,
865 _In_ PINI_KEYWORD AnchorKey,
866 _In_ INSERTION_TYPE InsertionType,
867 _In_ PCWSTR Name,
868 _In_ PCWSTR Data)
869 {
870 if (!Section || !Name || !*Name || !Data || !*Data)
871 {
872 DPRINT("Invalid parameter\n");
873 return NULL;
874 }
875 return IniCacheAddKeyAorW(Section,
876 AnchorKey, InsertionType,
877 Name, wcslen(Name),
878 Data, wcslen(Data),
879 TRUE);
880 }
881
882 PINI_KEYWORD
IniAddKey(_In_ PINI_SECTION Section,_In_ PCWSTR Name,_In_ PCWSTR Data)883 IniAddKey(
884 _In_ PINI_SECTION Section,
885 _In_ PCWSTR Name,
886 _In_ PCWSTR Data)
887 {
888 return IniInsertKey(Section, NULL, INSERT_LAST, Name, Data);
889 }
890
891 VOID
IniRemoveKeyByName(_In_ PINI_SECTION Section,_In_ PCWSTR KeyName)892 IniRemoveKeyByName(
893 _In_ PINI_SECTION Section,
894 _In_ PCWSTR KeyName)
895 {
896 PINI_KEYWORD Key;
897 UNREFERENCED_PARAMETER(Section);
898
899 Key = IniCacheFindKey(Section, KeyName);
900 if (Key)
901 IniCacheFreeKey(Key);
902 }
903
904 VOID
IniRemoveKey(_In_ PINI_SECTION Section,_In_ PINI_KEYWORD Key)905 IniRemoveKey(
906 _In_ PINI_SECTION Section,
907 _In_ PINI_KEYWORD Key)
908 {
909 UNREFERENCED_PARAMETER(Section);
910 if (!Key)
911 {
912 DPRINT("Invalid parameter\n");
913 return;
914 }
915 IniCacheFreeKey(Key);
916 }
917
918 PINICACHE
IniCacheCreate(VOID)919 IniCacheCreate(VOID)
920 {
921 PINICACHE Cache;
922
923 /* Allocate inicache header */
924 Cache = (PINICACHE)RtlAllocateHeap(ProcessHeap,
925 HEAP_ZERO_MEMORY,
926 sizeof(INICACHE));
927 if (!Cache)
928 {
929 DPRINT("RtlAllocateHeap() failed\n");
930 return NULL;
931 }
932 InitializeListHead(&Cache->SectionList);
933
934 return Cache;
935 }
936
937 NTSTATUS
IniCacheSaveByHandle(PINICACHE Cache,HANDLE FileHandle)938 IniCacheSaveByHandle(
939 PINICACHE Cache,
940 HANDLE FileHandle)
941 {
942 NTSTATUS Status;
943 PLIST_ENTRY Entry1, Entry2;
944 PINI_SECTION Section;
945 PINI_KEYWORD Key;
946 ULONG BufferSize;
947 PCHAR Buffer;
948 PCHAR Ptr;
949 ULONG Len;
950 IO_STATUS_BLOCK IoStatusBlock;
951 LARGE_INTEGER Offset;
952
953 /* Calculate required buffer size */
954 BufferSize = 0;
955 Entry1 = Cache->SectionList.Flink;
956 while (Entry1 != &Cache->SectionList)
957 {
958 Section = CONTAINING_RECORD(Entry1, INI_SECTION, ListEntry);
959 BufferSize += (Section->Name ? wcslen(Section->Name) : 0)
960 + 4; /* "[]\r\n" */
961
962 Entry2 = Section->KeyList.Flink;
963 while (Entry2 != &Section->KeyList)
964 {
965 Key = CONTAINING_RECORD(Entry2, INI_KEYWORD, ListEntry);
966 BufferSize += wcslen(Key->Name)
967 + (Key->Data ? wcslen(Key->Data) : 0)
968 + 3; /* "=\r\n" */
969 Entry2 = Entry2->Flink;
970 }
971
972 Entry1 = Entry1->Flink;
973 if (Entry1 != &Cache->SectionList)
974 BufferSize += 2; /* Extra "\r\n" at end of each section */
975 }
976
977 DPRINT("BufferSize: %lu\n", BufferSize);
978
979 /* Allocate file buffer with NULL-terminator */
980 Buffer = (PCHAR)RtlAllocateHeap(ProcessHeap,
981 HEAP_ZERO_MEMORY,
982 BufferSize + 1);
983 if (Buffer == NULL)
984 {
985 DPRINT1("RtlAllocateHeap() failed\n");
986 return STATUS_INSUFFICIENT_RESOURCES;
987 }
988
989 /* Fill file buffer */
990 Ptr = Buffer;
991 Entry1 = Cache->SectionList.Flink;
992 while (Entry1 != &Cache->SectionList)
993 {
994 Section = CONTAINING_RECORD(Entry1, INI_SECTION, ListEntry);
995 Len = sprintf(Ptr, "[%S]\r\n", Section->Name);
996 Ptr += Len;
997
998 Entry2 = Section->KeyList.Flink;
999 while (Entry2 != &Section->KeyList)
1000 {
1001 Key = CONTAINING_RECORD(Entry2, INI_KEYWORD, ListEntry);
1002 Len = sprintf(Ptr, "%S=%S\r\n", Key->Name, Key->Data);
1003 Ptr += Len;
1004 Entry2 = Entry2->Flink;
1005 }
1006
1007 Entry1 = Entry1->Flink;
1008 if (Entry1 != &Cache->SectionList)
1009 {
1010 Len = sprintf(Ptr, "\r\n");
1011 Ptr += Len;
1012 }
1013 }
1014
1015 /* Write to the INI file */
1016 Offset.QuadPart = 0LL;
1017 Status = NtWriteFile(FileHandle,
1018 NULL,
1019 NULL,
1020 NULL,
1021 &IoStatusBlock,
1022 Buffer,
1023 BufferSize,
1024 &Offset,
1025 NULL);
1026 if (!NT_SUCCESS(Status))
1027 {
1028 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1029 RtlFreeHeap(ProcessHeap, 0, Buffer);
1030 return Status;
1031 }
1032
1033 RtlFreeHeap(ProcessHeap, 0, Buffer);
1034 return STATUS_SUCCESS;
1035 }
1036
1037 NTSTATUS
IniCacheSave(PINICACHE Cache,PWCHAR FileName)1038 IniCacheSave(
1039 PINICACHE Cache,
1040 PWCHAR FileName)
1041 {
1042 NTSTATUS Status;
1043 UNICODE_STRING Name;
1044 OBJECT_ATTRIBUTES ObjectAttributes;
1045 IO_STATUS_BLOCK IoStatusBlock;
1046 HANDLE FileHandle;
1047
1048 /* Create the INI file */
1049 RtlInitUnicodeString(&Name, FileName);
1050
1051 InitializeObjectAttributes(&ObjectAttributes,
1052 &Name,
1053 OBJ_CASE_INSENSITIVE,
1054 NULL,
1055 NULL);
1056
1057 Status = NtCreateFile(&FileHandle,
1058 FILE_GENERIC_WRITE | SYNCHRONIZE,
1059 &ObjectAttributes,
1060 &IoStatusBlock,
1061 NULL,
1062 FILE_ATTRIBUTE_NORMAL,
1063 0,
1064 FILE_SUPERSEDE,
1065 FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY | FILE_NON_DIRECTORY_FILE,
1066 NULL,
1067 0);
1068 if (!NT_SUCCESS(Status))
1069 {
1070 DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1071 return Status;
1072 }
1073
1074 Status = IniCacheSaveByHandle(Cache, FileHandle);
1075
1076 /* Close the INI file */
1077 NtClose(FileHandle);
1078 return Status;
1079 }
1080
1081 /* EOF */
1082