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