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