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
8 /* INCLUDES *****************************************************************/
17 /* PRIVATE FUNCTIONS ********************************************************/
30 if (Key
->Name
!= NULL
)
32 RtlFreeHeap(ProcessHeap
, 0, Key
->Name
);
36 if (Key
->Data
!= NULL
)
38 RtlFreeHeap(ProcessHeap
, 0, Key
->Data
);
42 RtlFreeHeap(ProcessHeap
, 0, Key
);
51 PINICACHESECTION Section
)
53 PINICACHESECTION Next
;
59 while (Section
->FirstKey
!= NULL
)
61 Section
->FirstKey
= IniCacheFreeKey(Section
->FirstKey
);
63 Section
->LastKey
= NULL
;
65 if (Section
->Name
!= NULL
)
67 RtlFreeHeap(ProcessHeap
, 0, Section
->Name
);
71 RtlFreeHeap(ProcessHeap
, 0, Section
);
80 PINICACHESECTION Section
,
86 Key
= Section
->FirstKey
;
89 if (NameLength
== wcslen(Key
->Name
))
91 if (_wcsnicmp(Key
->Name
, Name
, NameLength
) == 0)
105 PINICACHESECTION Section
,
116 if (Section
== NULL
||
122 DPRINT("Invalid parameter\n");
126 Key
= (PINICACHEKEY
)RtlAllocateHeap(ProcessHeap
,
128 sizeof(INICACHEKEY
));
131 DPRINT("RtlAllocateHeap() failed\n");
135 Key
->Name
= (WCHAR
*)RtlAllocateHeap(ProcessHeap
,
137 (NameLength
+ 1) * sizeof(WCHAR
));
138 if (Key
->Name
== NULL
)
140 DPRINT("RtlAllocateHeap() failed\n");
141 RtlFreeHeap(ProcessHeap
, 0, Key
);
145 /* Copy value name */
146 for (i
= 0; i
< NameLength
; i
++)
148 Key
->Name
[i
] = (WCHAR
)Name
[i
];
150 Key
->Name
[NameLength
] = 0;
152 Key
->Data
= (WCHAR
*)RtlAllocateHeap(ProcessHeap
,
154 (DataLength
+ 1) * sizeof(WCHAR
));
155 if (Key
->Data
== NULL
)
157 DPRINT("RtlAllocateHeap() failed\n");
158 RtlFreeHeap(ProcessHeap
, 0, Key
->Name
);
159 RtlFreeHeap(ProcessHeap
, 0, Key
);
163 /* Copy value data */
164 for (i
= 0; i
< DataLength
; i
++)
166 Key
->Data
[i
] = (WCHAR
)Data
[i
];
168 Key
->Data
[DataLength
] = 0;
171 if (Section
->FirstKey
== NULL
)
173 Section
->FirstKey
= Key
;
174 Section
->LastKey
= Key
;
178 Section
->LastKey
->Next
= Key
;
179 Key
->Prev
= Section
->LastKey
;
180 Section
->LastKey
= Key
;
194 PINICACHESECTION Section
= NULL
;
197 if (Cache
== NULL
|| Name
== NULL
|| NameLength
== 0)
199 DPRINT("Invalid parameter\n");
203 Section
= (PINICACHESECTION
)RtlAllocateHeap(ProcessHeap
,
205 sizeof(INICACHESECTION
));
208 DPRINT("RtlAllocateHeap() failed\n");
212 /* Allocate and initialize section name */
213 Section
->Name
= (WCHAR
*)RtlAllocateHeap(ProcessHeap
,
215 (NameLength
+ 1) * sizeof(WCHAR
));
216 if (Section
->Name
== NULL
)
218 DPRINT("RtlAllocateHeap() failed\n");
219 RtlFreeHeap(ProcessHeap
, 0, Section
);
223 /* Copy section name */
224 for (i
= 0; i
< NameLength
; i
++)
226 Section
->Name
[i
] = (WCHAR
)Name
[i
];
228 Section
->Name
[NameLength
] = 0;
231 if (Cache
->FirstSection
== NULL
)
233 Cache
->FirstSection
= Section
;
234 Cache
->LastSection
= Section
;
238 Cache
->LastSection
->Next
= Section
;
239 Section
->Prev
= Cache
->LastSection
;
240 Cache
->LastSection
= Section
;
249 IniCacheSkipWhitespace(
252 while (*Ptr
!= 0 && isspace(*Ptr
))
255 return (*Ptr
== 0) ? NULL
: Ptr
;
261 IniCacheSkipToNextSection(
264 while (*Ptr
!= 0 && *Ptr
!= '[')
266 while (*Ptr
!= 0 && *Ptr
!= L
'\n')
274 return (*Ptr
== 0) ? NULL
: Ptr
;
280 IniCacheGetSectionName(
291 /* Skip whitespace */
292 while (*Ptr
!= 0 && isspace(*Ptr
))
299 while (*Ptr
!= 0 && *Ptr
!= ']')
306 while (*Ptr
!= 0 && *Ptr
!= L
'\n')
314 strncpy(Name
, *NamePtr
, Size
);
317 DPRINT("SectionName: '%s'\n", Name
);
341 /* Skip whitespace and empty lines */
342 while (isspace(*Ptr
) || *Ptr
== '\n' || *Ptr
== '\r')
353 while (*Ptr
!= 0 && !isspace(*Ptr
) && *Ptr
!= '=' && *Ptr
!= ';')
360 while (*Ptr
!= 0 && *Ptr
!= '\r' && *Ptr
!= '\n')
389 /* Skip whitespace */
390 while (*Ptr
!= 0 && isspace(*Ptr
))
395 /* Check and skip '=' */
402 /* Skip whitespace */
403 while (*Ptr
!= 0 && isspace(*Ptr
))
408 if (*Ptr
== '"' && String
)
421 while (*Ptr
&& *Ptr
!= '\r' && *Ptr
!= '\n')
430 while (*Ptr
!= 0 && *Ptr
!= '\r' && *Ptr
!= ';')
437 /* Skip to next line */
449 /* PUBLIC FUNCTIONS *********************************************************/
452 IniCacheLoadFromMemory(
460 PINICACHESECTION Section
;
464 ULONG SectionNameSize
;
472 /* Allocate inicache header */
473 *Cache
= (PINICACHE
)RtlAllocateHeap(ProcessHeap
,
478 DPRINT("RtlAllocateHeap() failed\n");
479 return STATUS_INSUFFICIENT_RESOURCES
;
485 while (Ptr
!= NULL
&& *Ptr
!= 0)
487 Ptr
= IniCacheSkipWhitespace(Ptr
);
496 Ptr
= IniCacheGetSectionName(Ptr
,
500 DPRINT1("[%.*s]\n", SectionNameSize
, SectionName
);
502 Section
= IniCacheAddSection(*Cache
,
507 DPRINT("IniCacheAddSection() failed\n");
508 Ptr
= IniCacheSkipToNextSection(Ptr
);
516 Ptr
= IniCacheSkipToNextSection(Ptr
);
520 Ptr
= IniCacheGetKeyName(Ptr
,
524 Ptr
= IniCacheGetKeyValue(Ptr
,
529 DPRINT1("'%.*s' = '%.*s'\n", KeyNameSize
, KeyName
, KeyValueSize
, KeyValue
);
531 Key
= IniCacheAddKey(Section
,
538 DPRINT("IniCacheAddKey() failed\n");
543 return STATUS_SUCCESS
;
547 IniCacheLoadByHandle(
553 IO_STATUS_BLOCK IoStatusBlock
;
554 FILE_STANDARD_INFORMATION FileInfo
;
557 LARGE_INTEGER FileOffset
;
561 /* Query file size */
562 Status
= NtQueryInformationFile(FileHandle
,
565 sizeof(FILE_STANDARD_INFORMATION
),
566 FileStandardInformation
);
567 if (!NT_SUCCESS(Status
))
569 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status
);
573 FileLength
= FileInfo
.EndOfFile
.u
.LowPart
;
575 DPRINT("File size: %lu\n", FileLength
);
577 /* Allocate file buffer with NULL-terminator */
578 FileBuffer
= (CHAR
*)RtlAllocateHeap(ProcessHeap
,
581 if (FileBuffer
== NULL
)
583 DPRINT1("RtlAllocateHeap() failed\n");
584 return STATUS_INSUFFICIENT_RESOURCES
;
588 FileOffset
.QuadPart
= 0ULL;
589 Status
= NtReadFile(FileHandle
,
599 /* Append NULL-terminator */
600 FileBuffer
[FileLength
] = 0;
602 if (!NT_SUCCESS(Status
))
604 DPRINT("NtReadFile() failed (Status %lx)\n", Status
);
608 Status
= IniCacheLoadFromMemory(Cache
, FileBuffer
, FileLength
, String
);
609 if (!NT_SUCCESS(Status
))
611 DPRINT1("IniCacheLoadFromMemory() failed (Status %lx)\n", Status
);
615 /* Free the file buffer, and return */
616 RtlFreeHeap(ProcessHeap
, 0, FileBuffer
);
628 OBJECT_ATTRIBUTES ObjectAttributes
;
629 IO_STATUS_BLOCK IoStatusBlock
;
634 /* Open the INI file */
635 RtlInitUnicodeString(&Name
, FileName
);
637 InitializeObjectAttributes(&ObjectAttributes
,
639 OBJ_CASE_INSENSITIVE
,
643 Status
= NtOpenFile(&FileHandle
,
644 FILE_GENERIC_READ
| SYNCHRONIZE
,
648 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
649 if (!NT_SUCCESS(Status
))
651 DPRINT("NtOpenFile() failed (Status %lx)\n", Status
);
655 DPRINT("NtOpenFile() successful\n");
657 Status
= IniCacheLoadByHandle(Cache
, FileHandle
, String
);
659 /* Close the INI file */
672 while (Cache
->FirstSection
!= NULL
)
674 Cache
->FirstSection
= IniCacheFreeSection(Cache
->FirstSection
);
676 Cache
->LastSection
= NULL
;
678 RtlFreeHeap(ProcessHeap
, 0, Cache
);
687 PINICACHESECTION Section
= NULL
;
689 if (Cache
== NULL
|| Name
== NULL
)
691 DPRINT("Invalid parameter\n");
695 /* Iterate through list of sections */
696 Section
= Cache
->FirstSection
;
697 while (Section
!= NULL
)
699 DPRINT("Comparing '%S' and '%S'\n", Section
->Name
, Name
);
701 /* Are the section names the same? */
702 if (_wcsicmp(Section
->Name
, Name
) == 0)
705 /* Get the next section */
706 Section
= Section
->Next
;
709 DPRINT("Section not found\n");
717 PINICACHESECTION Section
,
723 if (Section
== NULL
|| KeyName
== NULL
|| KeyData
== NULL
)
725 DPRINT("Invalid parameter\n");
726 return STATUS_INVALID_PARAMETER
;
731 Key
= IniCacheFindKey(Section
, KeyName
, wcslen(KeyName
));
734 return STATUS_INVALID_PARAMETER
;
737 *KeyData
= Key
->Data
;
739 return STATUS_SUCCESS
;
744 IniCacheFindFirstValue(
745 PINICACHESECTION Section
,
749 PINICACHEITERATOR Iterator
;
752 if (Section
== NULL
|| KeyName
== NULL
|| KeyData
== NULL
)
754 DPRINT("Invalid parameter\n");
758 Key
= Section
->FirstKey
;
761 DPRINT("Invalid parameter\n");
765 *KeyName
= Key
->Name
;
766 *KeyData
= Key
->Data
;
768 Iterator
= (PINICACHEITERATOR
)RtlAllocateHeap(ProcessHeap
,
770 sizeof(INICACHEITERATOR
));
771 if (Iterator
== NULL
)
773 DPRINT("RtlAllocateHeap() failed\n");
777 Iterator
->Section
= Section
;
785 IniCacheFindNextValue(
786 PINICACHEITERATOR Iterator
,
792 if (Iterator
== NULL
|| KeyName
== NULL
|| KeyData
== NULL
)
794 DPRINT("Invalid parameter\n");
798 Key
= Iterator
->Key
->Next
;
801 DPRINT("No more entries\n");
805 *KeyName
= Key
->Name
;
806 *KeyData
= Key
->Data
;
816 PINICACHEITERATOR Iterator
)
818 if (Iterator
== NULL
)
821 RtlFreeHeap(ProcessHeap
, 0, Iterator
);
827 PINICACHESECTION Section
,
828 PINICACHEKEY AnchorKey
,
829 INSERTION_TYPE InsertionType
,
837 if (Section
== NULL
||
843 DPRINT("Invalid parameter\n");
847 /* Allocate key buffer */
848 Key
= (PINICACHEKEY
)RtlAllocateHeap(ProcessHeap
,
850 sizeof(INICACHEKEY
));
853 DPRINT("RtlAllocateHeap() failed\n");
857 /* Allocate name buffer */
858 Key
->Name
= (WCHAR
*)RtlAllocateHeap(ProcessHeap
,
860 (wcslen(Name
) + 1) * sizeof(WCHAR
));
861 if (Key
->Name
== NULL
)
863 DPRINT("RtlAllocateHeap() failed\n");
864 RtlFreeHeap(ProcessHeap
, 0, Key
);
868 /* Copy value name */
869 wcscpy(Key
->Name
, Name
);
871 /* Allocate data buffer */
872 Key
->Data
= (WCHAR
*)RtlAllocateHeap(ProcessHeap
,
874 (wcslen(Data
) + 1) * sizeof(WCHAR
));
875 if (Key
->Data
== NULL
)
877 DPRINT("RtlAllocateHeap() failed\n");
878 RtlFreeHeap(ProcessHeap
, 0, Key
->Name
);
879 RtlFreeHeap(ProcessHeap
, 0, Key
);
883 /* Copy value data */
884 wcscpy(Key
->Data
, Data
);
886 /* Insert key into section */
887 if (Section
->FirstKey
== NULL
)
889 Section
->FirstKey
= Key
;
890 Section
->LastKey
= Key
;
892 else if ((InsertionType
== INSERT_FIRST
) ||
893 ((InsertionType
== INSERT_BEFORE
) && ((AnchorKey
== NULL
) || (AnchorKey
== Section
->FirstKey
))))
895 /* Insert at the head of the list */
896 Section
->FirstKey
->Prev
= Key
;
897 Key
->Next
= Section
->FirstKey
;
898 Section
->FirstKey
= Key
;
900 else if ((InsertionType
== INSERT_BEFORE
) && (AnchorKey
!= NULL
))
902 /* Insert before the anchor key */
903 Key
->Next
= AnchorKey
;
904 Key
->Prev
= AnchorKey
->Prev
;
905 AnchorKey
->Prev
->Next
= Key
;
906 AnchorKey
->Prev
= Key
;
908 else if ((InsertionType
== INSERT_LAST
) ||
909 ((InsertionType
== INSERT_AFTER
) && ((AnchorKey
== NULL
) || (AnchorKey
== Section
->LastKey
))))
911 Section
->LastKey
->Next
= Key
;
912 Key
->Prev
= Section
->LastKey
;
913 Section
->LastKey
= Key
;
915 else if ((InsertionType
== INSERT_AFTER
) && (AnchorKey
!= NULL
))
917 /* Insert after the anchor key */
918 Key
->Next
= AnchorKey
->Next
;
919 Key
->Prev
= AnchorKey
;
920 AnchorKey
->Next
->Prev
= Key
;
921 AnchorKey
->Next
= Key
;
933 /* Allocate inicache header */
934 Cache
= (PINICACHE
)RtlAllocateHeap(ProcessHeap
,
939 DPRINT("RtlAllocateHeap() failed\n");
948 IniCacheSaveByHandle(
953 PINICACHESECTION Section
;
959 IO_STATUS_BLOCK IoStatusBlock
;
960 LARGE_INTEGER Offset
;
962 /* Calculate required buffer size */
964 Section
= Cache
->FirstSection
;
965 while (Section
!= NULL
)
967 BufferSize
+= (Section
->Name
? wcslen(Section
->Name
) : 0)
970 Key
= Section
->FirstKey
;
973 BufferSize
+= wcslen(Key
->Name
)
974 + (Key
->Data
? wcslen(Key
->Data
) : 0)
979 Section
= Section
->Next
;
981 BufferSize
+= 2; /* Extra "\r\n" at end of each section */
984 DPRINT("BufferSize: %lu\n", BufferSize
);
986 /* Allocate file buffer with NULL-terminator */
987 Buffer
= (CHAR
*)RtlAllocateHeap(ProcessHeap
,
992 DPRINT1("RtlAllocateHeap() failed\n");
993 return STATUS_INSUFFICIENT_RESOURCES
;
996 /* Fill file buffer */
998 Section
= Cache
->FirstSection
;
999 while (Section
!= NULL
)
1001 Len
= sprintf(Ptr
, "[%S]\r\n", Section
->Name
);
1004 Key
= Section
->FirstKey
;
1007 Len
= sprintf(Ptr
, "%S=%S\r\n", Key
->Name
, Key
->Data
);
1012 Section
= Section
->Next
;
1013 if (Section
!= NULL
)
1015 Len
= sprintf(Ptr
, "\r\n");
1020 /* Write to the INI file */
1021 Offset
.QuadPart
= 0LL;
1022 Status
= NtWriteFile(FileHandle
,
1031 if (!NT_SUCCESS(Status
))
1033 DPRINT("NtWriteFile() failed (Status %lx)\n", Status
);
1034 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1038 RtlFreeHeap(ProcessHeap
, 0, Buffer
);
1039 return STATUS_SUCCESS
;
1048 UNICODE_STRING Name
;
1049 OBJECT_ATTRIBUTES ObjectAttributes
;
1050 IO_STATUS_BLOCK IoStatusBlock
;
1053 /* Create the INI file */
1054 RtlInitUnicodeString(&Name
, FileName
);
1056 InitializeObjectAttributes(&ObjectAttributes
,
1058 OBJ_CASE_INSENSITIVE
,
1062 Status
= NtCreateFile(&FileHandle
,
1063 FILE_GENERIC_WRITE
| SYNCHRONIZE
,
1067 FILE_ATTRIBUTE_NORMAL
,
1070 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_SEQUENTIAL_ONLY
| FILE_NON_DIRECTORY_FILE
,
1073 if (!NT_SUCCESS(Status
))
1075 DPRINT("NtCreateFile() failed (Status %lx)\n", Status
);
1079 Status
= IniCacheSaveByHandle(Cache
, FileHandle
);
1081 /* Close the INI file */
1082 NtClose(FileHandle
);
1088 IniCacheAppendSection(
1092 PINICACHESECTION Section
= NULL
;
1094 if (Cache
== NULL
|| Name
== NULL
|| *Name
== 0)
1096 DPRINT("Invalid parameter\n");
1100 Section
= (PINICACHESECTION
)RtlAllocateHeap(ProcessHeap
,
1102 sizeof(INICACHESECTION
));
1103 if (Section
== NULL
)
1105 DPRINT("RtlAllocateHeap() failed\n");
1109 /* Allocate and initialize section name */
1110 Section
->Name
= (WCHAR
*)RtlAllocateHeap(ProcessHeap
,
1112 (wcslen(Name
) + 1) * sizeof(WCHAR
));
1113 if (Section
->Name
== NULL
)
1115 DPRINT("RtlAllocateHeap() failed\n");
1116 RtlFreeHeap(ProcessHeap
, 0, Section
);
1120 /* Copy section name */
1121 wcscpy(Section
->Name
, Name
);
1123 /* Append section */
1124 if (Cache
->FirstSection
== NULL
)
1126 Cache
->FirstSection
= Section
;
1127 Cache
->LastSection
= Section
;
1131 Cache
->LastSection
->Next
= Section
;
1132 Section
->Prev
= Cache
->LastSection
;
1133 Cache
->LastSection
= Section
;