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