3 * Copyright (C) 2002 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS text-mode setup
22 * FILE: subsys/system/usetup/inicache.c
23 * PURPOSE: INI file parser that caches contents of INI file in memory
24 * PROGRAMMER: Royce Mitchell III
28 /* INCLUDES *****************************************************************/
30 #include <ddk/ntddk.h>
35 /* PRIVATE FUNCTIONS ********************************************************/
38 IniCacheFreeKey(PINICACHEKEY Key)
48 if (Key->Name != NULL)
50 RtlFreeHeap(ProcessHeap,
56 if (Key->Data != NULL)
58 RtlFreeHeap(ProcessHeap,
64 RtlFreeHeap(ProcessHeap,
72 static PINICACHESECTION
73 IniCacheFreeSection(PINICACHESECTION Section)
75 PINICACHESECTION Next;
83 while (Section->FirstKey != NULL)
85 Section->FirstKey = IniCacheFreeKey(Section->FirstKey);
87 Section->LastKey = NULL;
89 if (Section->Name != NULL)
91 RtlFreeHeap(ProcessHeap,
97 RtlFreeHeap(ProcessHeap,
106 IniCacheFindKey(PINICACHESECTION Section,
112 Key = Section->FirstKey;
115 if (NameLength == wcslen(Key->Name))
117 if (_wcsnicmp(Key->Name, Name, NameLength) == 0)
129 IniCacheAddKey(PINICACHESECTION Section,
140 if (Section == NULL ||
146 DPRINT("Invalid parameter\n");
150 Key = (PINICACHEKEY)RtlAllocateHeap(ProcessHeap,
152 sizeof(INICACHEKEY));
155 DPRINT("RtlAllocateHeap() failed\n");
160 sizeof(INICACHEKEY));
163 Key->Name = RtlAllocateHeap(ProcessHeap,
165 (NameLength + 1) * sizeof(WCHAR));
166 if (Key->Name == NULL)
168 DPRINT("RtlAllocateHeap() failed\n");
169 RtlFreeHeap(ProcessHeap,
175 /* Copy value name */
176 for (i = 0; i < NameLength; i++)
178 Key->Name[i] = (WCHAR)Name[i];
180 Key->Name[NameLength] = 0;
183 Key->Data = RtlAllocateHeap(ProcessHeap,
185 (DataLength + 1) * sizeof(WCHAR));
186 if (Key->Data == NULL)
188 DPRINT("RtlAllocateHeap() failed\n");
189 RtlFreeHeap(ProcessHeap,
192 RtlFreeHeap(ProcessHeap,
198 /* Copy value data */
199 for (i = 0; i < DataLength; i++)
201 Key->Data[i] = (WCHAR)Data[i];
203 Key->Data[DataLength] = 0;
206 if (Section->FirstKey == NULL)
208 Section->FirstKey = Key;
209 Section->LastKey = Key;
213 Section->LastKey->Next = Key;
214 Key->Prev = Section->LastKey;
215 Section->LastKey = Key;
222 static PINICACHESECTION
223 IniCacheFindSection(PINICACHE Cache,
227 PINICACHESECTION Section = NULL;
229 if (Cache == NULL || Name == NULL || NameLength == 0)
234 Section = Cache->FirstSection;
236 /* iterate through list of sections */
237 while (Section != NULL)
239 if (NameLength == wcslen(Section->Name))
241 /* are the contents the same too? */
242 if (_wcsnicmp(Section->Name, Name, NameLength) == 0)
246 /* get the next section*/
247 Section = Section->Next;
254 static PINICACHESECTION
255 IniCacheAddSection(PINICACHE Cache,
259 PINICACHESECTION Section = NULL;
262 if (Cache == NULL || Name == NULL || NameLength == 0)
264 DPRINT("Invalid parameter\n");
268 Section = (PINICACHESECTION)RtlAllocateHeap(ProcessHeap,
270 sizeof(INICACHESECTION));
273 DPRINT("RtlAllocateHeap() failed\n");
276 RtlZeroMemory(Section,
277 sizeof(INICACHESECTION));
279 /* Allocate and initialize section name */
280 Section->Name = RtlAllocateHeap(ProcessHeap,
282 (NameLength + 1) * sizeof(WCHAR));
283 if (Section->Name == NULL)
285 DPRINT("RtlAllocateHeap() failed\n");
286 RtlFreeHeap(ProcessHeap,
292 /* Copy section name */
293 for (i = 0; i < NameLength; i++)
295 Section->Name[i] = (WCHAR)Name[i];
297 Section->Name[NameLength] = 0;
300 if (Cache->FirstSection == NULL)
302 Cache->FirstSection = Section;
303 Cache->LastSection = Section;
307 Cache->LastSection->Next = Section;
308 Section->Prev = Cache->LastSection;
309 Cache->LastSection = Section;
317 IniCacheSkipWhitespace(PCHAR Ptr)
319 while (*Ptr != 0 && isspace(*Ptr))
322 return((*Ptr == 0) ? NULL : Ptr);
327 IniCacheSkipToNextSection(PCHAR Ptr)
329 while (*Ptr != 0 && *Ptr != '[')
331 while (*Ptr != 0 && *Ptr != L'\n')
338 return((*Ptr == 0) ? NULL : Ptr);
343 IniCacheGetSectionName(PCHAR Ptr,
353 /* skip whitespace */
354 while (*Ptr != 0 && isspace(*Ptr))
361 while (*Ptr != 0 && *Ptr != ']')
369 while (*Ptr != 0 && *Ptr != L'\n')
377 strncpy(Name, *NamePtr, Size);
380 DPRINT("SectionName: '%s'\n", Name);
387 IniCacheGetKeyName(PCHAR Ptr,
402 /* skip whitespace and empty lines */
403 while (isspace(*Ptr) || *Ptr == '\n' || *Ptr == '\r')
414 while (*Ptr != 0 && !isspace(*Ptr) && *Ptr != '=' && *Ptr != ';')
421 while (*Ptr != 0 && *Ptr != '\r' && *Ptr != '\n')
438 IniCacheGetKeyValue(PCHAR Ptr,
448 /* Skip whitespace */
449 while (*Ptr != 0 && isspace(*Ptr))
454 /* Check and skip '=' */
461 /* Skip whitespace */
462 while (*Ptr != 0 && isspace(*Ptr))
467 if (*Ptr == '"' && String)
479 while (*Ptr && *Ptr != '\r' && *Ptr != '\n')
488 while (*Ptr != 0 && *Ptr != '\r' && *Ptr != ';')
495 /* Skip to next line */
509 /* PUBLIC FUNCTIONS *********************************************************/
512 IniCacheLoad(PINICACHE *Cache,
513 PUNICODE_STRING FileName,
516 OBJECT_ATTRIBUTES ObjectAttributes;
517 FILE_STANDARD_INFORMATION FileInfo;
518 IO_STATUS_BLOCK IoStatusBlock;
524 LARGE_INTEGER FileOffset;
527 PINICACHESECTION Section;
531 ULONG SectionNameSize;
542 InitializeObjectAttributes(&ObjectAttributes,
548 Status = NtOpenFile(&FileHandle,
549 GENERIC_READ | SYNCHRONIZE,
553 FILE_NON_DIRECTORY_FILE);
554 if (!NT_SUCCESS(Status))
556 DPRINT("NtOpenFile() failed (Status %lx)\n", Status);
560 DPRINT("NtOpenFile() successful\n");
562 /* Query file size */
563 Status = NtQueryInformationFile(FileHandle,
566 sizeof(FILE_STANDARD_INFORMATION),
567 FileStandardInformation);
568 if (Status == STATUS_PENDING)
570 DPRINT("NtQueryInformationFile() returns STATUS_PENDING\n");
573 else if (!NT_SUCCESS(Status))
575 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
580 FileLength = FileInfo.EndOfFile.u.LowPart;
582 DPRINT("File size: %lu\n", FileLength);
584 /* Allocate file buffer */
585 FileBuffer = RtlAllocateHeap(ProcessHeap,
588 if (FileBuffer == NULL)
590 DPRINT1("RtlAllocateHeap() failed\n");
592 return(STATUS_INSUFFICIENT_RESOURCES);
596 FileOffset.QuadPart = 0ULL;
597 Status = NtReadFile(FileHandle,
607 if (Status == STATUS_PENDING)
609 DPRINT("NtReadFile() returns STATUS_PENDING\n");
611 Status = IoStatusBlock.Status;
614 /* Append string terminator */
615 FileBuffer[FileLength] = 0;
619 if (!NT_SUCCESS(Status))
621 DPRINT("NtReadFile() failed (Status %lx)\n", Status);
622 RtlFreeHeap(ProcessHeap,
629 /* Allocate inicache header */
630 *Cache = (PINICACHE)RtlAllocateHeap(ProcessHeap,
635 DPRINT("RtlAllocateHeap() failed\n");
636 return(STATUS_INSUFFICIENT_RESOURCES);
639 /* Initialize inicache header */
640 RtlZeroMemory(*Cache,
646 while (Ptr != NULL && *Ptr != 0)
648 Ptr = IniCacheSkipWhitespace(Ptr);
657 Ptr = IniCacheGetSectionName(Ptr,
661 DPRINT1("[%.*s]\n", SectionNameSize, SectionName);
663 Section = IniCacheAddSection(*Cache,
668 DPRINT("IniCacheAddSection() failed\n");
669 Ptr = IniCacheSkipToNextSection(Ptr);
677 Ptr = IniCacheSkipToNextSection(Ptr);
681 Ptr = IniCacheGetKeyName(Ptr,
685 Ptr = IniCacheGetKeyValue(Ptr,
690 DPRINT1("'%.*s' = '%.*s'\n", KeyNameSize, KeyName, KeyValueSize, KeyValue);
692 Key = IniCacheAddKey(Section,
699 DPRINT("IniCacheAddKey() failed\n");
704 /* Free file buffer */
705 RtlFreeHeap(ProcessHeap,
714 IniCacheDestroy(PINICACHE Cache)
721 while (Cache->FirstSection != NULL)
723 Cache->FirstSection = IniCacheFreeSection(Cache->FirstSection);
725 Cache->LastSection = NULL;
727 RtlFreeHeap(ProcessHeap,
734 IniCacheGetSection(PINICACHE Cache,
737 PINICACHESECTION Section = NULL;
739 if (Cache == NULL || Name == NULL)
741 DPRINT("Invalid parameter\n");
745 /* Iterate through list of sections */
746 Section = Cache->FirstSection;
747 while (Section != NULL)
749 DPRINT("Comparing '%S' and '%S'\n", Section->Name, Name);
751 /* Are the section names the same? */
752 if (_wcsicmp(Section->Name, Name) == 0)
755 /* Get the next section */
756 Section = Section->Next;
759 DPRINT("Section not found\n");
766 IniCacheGetKey(PINICACHESECTION Section,
772 if (Section == NULL || KeyName == NULL || KeyData == NULL)
774 DPRINT("Invalid parameter\n");
775 return(STATUS_INVALID_PARAMETER);
780 Key = IniCacheFindKey(Section, KeyName, wcslen(KeyName));
783 return(STATUS_INVALID_PARAMETER);
786 *KeyData = Key->Data;
788 return(STATUS_SUCCESS);
793 IniCacheFindFirstValue(PINICACHESECTION Section,
797 PINICACHEITERATOR Iterator;
800 if (Section == NULL || KeyName == NULL || KeyData == NULL)
802 DPRINT("Invalid parameter\n");
806 Key = Section->FirstKey;
809 DPRINT("Invalid parameter\n");
813 *KeyName = Key->Name;
814 *KeyData = Key->Data;
816 Iterator = (PINICACHEITERATOR)RtlAllocateHeap(ProcessHeap,
818 sizeof(INICACHEITERATOR));
819 if (Iterator == NULL)
821 DPRINT("RtlAllocateHeap() failed\n");
825 Iterator->Section = Section;
833 IniCacheFindNextValue(PINICACHEITERATOR Iterator,
839 if (Iterator == NULL || KeyName == NULL || KeyData == NULL)
841 DPRINT("Invalid parameter\n");
845 Key = Iterator->Key->Next;
848 DPRINT("No more entries\n");
852 *KeyName = Key->Name;
853 *KeyData = Key->Data;
862 IniCacheFindClose(PINICACHEITERATOR Iterator)
864 if (Iterator == NULL)
867 RtlFreeHeap(ProcessHeap,
874 IniCacheInsertKey(PINICACHESECTION Section,
875 PINICACHEKEY AnchorKey,
876 INSERTATION_TYPE InsertationType,
885 if (Section == NULL ||
891 DPRINT("Invalid parameter\n");
895 /* Allocate key buffer */
896 Key = (PINICACHEKEY)RtlAllocateHeap(ProcessHeap,
898 sizeof(INICACHEKEY));
901 DPRINT("RtlAllocateHeap() failed\n");
905 sizeof(INICACHEKEY));
907 /* Allocate name buffer */
908 Key->Name = RtlAllocateHeap(ProcessHeap,
910 (wcslen(Name) + 1) * sizeof(WCHAR));
911 if (Key->Name == NULL)
913 DPRINT("RtlAllocateHeap() failed\n");
914 RtlFreeHeap(ProcessHeap,
920 /* Copy value name */
921 wcscpy(Key->Name, Name);
923 /* Allocate data buffer */
924 Key->Data = RtlAllocateHeap(ProcessHeap,
926 (wcslen(Data) + 1) * sizeof(WCHAR));
927 if (Key->Data == NULL)
929 DPRINT("RtlAllocateHeap() failed\n");
930 RtlFreeHeap(ProcessHeap,
933 RtlFreeHeap(ProcessHeap,
939 /* Copy value data */
940 wcscpy(Key->Data, Data);
942 /* Insert key into section */
943 if (Section->FirstKey == NULL)
945 Section->FirstKey = Key;
946 Section->LastKey = Key;
948 else if ((InsertationType == INSERT_FIRST) ||
949 ((InsertationType == INSERT_BEFORE) && ((AnchorKey == NULL) || (AnchorKey == Section->FirstKey))))
951 /* Insert at the head of the list */
952 Section->FirstKey->Prev = Key;
953 Key->Next = Section->FirstKey;
954 Section->FirstKey = Key;
956 else if ((InsertationType == INSERT_BEFORE) && (AnchorKey != NULL))
958 /* Insert before the anchor key */
959 Key->Next = AnchorKey;
960 Key->Prev = AnchorKey->Prev;
961 AnchorKey->Prev->Next = Key;
962 AnchorKey->Prev = Key;
964 else if ((InsertationType == INSERT_LAST) ||
965 ((InsertationType == INSERT_AFTER) && ((AnchorKey == NULL) || (AnchorKey == Section->LastKey))))
967 Section->LastKey->Next = Key;
968 Key->Prev = Section->LastKey;
969 Section->LastKey = Key;
971 else if ((InsertationType == INSERT_AFTER) && (AnchorKey != NULL))
973 /* Insert before the anchor key */
974 Key->Next = AnchorKey->Next;
975 Key->Prev = AnchorKey;
976 AnchorKey->Next->Prev = Key;
977 AnchorKey->Next = Key;
989 /* Allocate inicache header */
990 Cache = (PINICACHE)RtlAllocateHeap(ProcessHeap,
995 DPRINT("RtlAllocateHeap() failed\n");
999 /* Initialize inicache header */
1000 RtlZeroMemory(Cache,
1008 IniCacheSave(PINICACHE Cache,
1011 UNICODE_STRING Name;
1012 PINICACHESECTION Section;
1020 OBJECT_ATTRIBUTES ObjectAttributes;
1021 IO_STATUS_BLOCK IoStatusBlock;
1022 LARGE_INTEGER Offset;
1026 /* Calculate required buffer size */
1028 Section = Cache->FirstSection;
1029 while (Section != NULL)
1031 BufferSize += (Section->Name ? wcslen(Section->Name) : 0)
1034 Key = Section->FirstKey;
1037 BufferSize += wcslen(Key->Name)
1038 + (Key->Data ? wcslen(Key->Data) : 0)
1043 Section = Section->Next;
1044 if (Section != NULL)
1045 BufferSize += 2; /* extra "\r\n" at end of each section */
1047 BufferSize++; /* Null-terminator */
1049 DPRINT1("BufferSize: %lu\n", BufferSize);
1051 /* Allocate file buffer */
1052 Buffer = RtlAllocateHeap(ProcessHeap,
1057 DPRINT1("RtlAllocateHeap() failed\n");
1058 return(STATUS_INSUFFICIENT_RESOURCES);
1060 RtlZeroMemory(Buffer, BufferSize);
1062 /* Fill file buffer */
1064 Section = Cache->FirstSection;
1065 while (Section != NULL)
1067 Len = sprintf(Ptr, "[%S]\r\n", Section->Name);
1070 Key = Section->FirstKey;
1073 Len = sprintf(Ptr, "%S=%S\r\n", Key->Name, Key->Data);
1078 Section = Section->Next;
1079 if (Section != NULL)
1081 Len = sprintf(Ptr, "\r\n");
1086 /* Create ini file */
1087 RtlInitUnicodeString(&Name,
1090 InitializeObjectAttributes(&ObjectAttributes,
1096 Status = NtCreateFile(&FileHandle,
1101 FILE_ATTRIBUTE_NORMAL,
1104 FILE_SYNCHRONOUS_IO_ALERT | FILE_SEQUENTIAL_ONLY,
1107 if (!NT_SUCCESS(Status))
1109 DPRINT("NtCreateFile() failed (Status %lx)\n", Status);
1110 RtlFreeHeap(ProcessHeap,
1116 Offset.QuadPart = 0LL;
1117 Status = NtWriteFile(FileHandle,
1126 if (!NT_SUCCESS(Status))
1128 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
1129 NtClose(FileHandle);
1130 RtlFreeHeap(ProcessHeap,
1136 NtClose(FileHandle);
1138 RtlFreeHeap(ProcessHeap,
1142 return(STATUS_SUCCESS);
1147 IniCacheAppendSection(PINICACHE Cache,
1150 PINICACHESECTION Section = NULL;
1153 if (Cache == NULL || Name == NULL || *Name == 0)
1155 DPRINT("Invalid parameter\n");
1159 Section = (PINICACHESECTION)RtlAllocateHeap(ProcessHeap,
1161 sizeof(INICACHESECTION));
1162 if (Section == NULL)
1164 DPRINT("RtlAllocateHeap() failed\n");
1167 RtlZeroMemory(Section,
1168 sizeof(INICACHESECTION));
1170 /* Allocate and initialize section name */
1171 Section->Name = RtlAllocateHeap(ProcessHeap,
1173 (wcslen(Name) + 1) * sizeof(WCHAR));
1174 if (Section->Name == NULL)
1176 DPRINT("RtlAllocateHeap() failed\n");
1177 RtlFreeHeap(ProcessHeap,
1183 /* Copy section name */
1184 wcscpy(Section->Name, Name);
1186 /* Append section */
1187 if (Cache->FirstSection == NULL)
1189 Cache->FirstSection = Section;
1190 Cache->LastSection = Section;
1194 Cache->LastSection->Next = Section;
1195 Section->Prev = Cache->LastSection;
1196 Cache->LastSection = Section;