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