xref: /reactos/base/setup/lib/utils/inicache.c (revision f4d29a74)
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 */
1140