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