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