Rev 1 | Rev 161 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 1 | Rev 136 | ||
|---|---|---|---|
| Line 5... | Line 5... | ||
| 5 | 5 | ||
| 6 | // WARNING: INI SECTION NAMES ARE CASE SENSITIVE, BUT KEY NAMES ARE NOT! | 6 | // WARNING: INI SECTION NAMES ARE CASE SENSITIVE, BUT KEY NAMES ARE NOT! | 
| 7 | 7 | ||
| 8 | 8 | ||
| 9 | // internal definitions | 9 | // internal definitions | 
| 10 | #define MAX_KEY_SIZE 124 | - | |
| 11 | #define MAX_VALUE_SIZE 3968 | - | |
| 12 | #define DICTIONARY_PAGE_SIZE 128 | 10 | #define DICTIONARY_PAGE_SIZE 128 | 
| - | 11 | #define STRING_MAXSIZE 65536 | |
| 13 | #define INI_SECTION_SEPARATOR ']' | 12 | #define INI_SECTION_SEPARATOR ']' | 
| 14 | #define INI_INVALID_KEY (wchar_t *) 0x7fffffff | 13 | #define INI_INVALID_KEY (wchar_t *) 0x7fffffff | 
| 15 | #define WSIZEOF(a) (sizeof (a) / sizeof (wchar_t)) | 14 | #define WSIZEOF(a) (sizeof (a) / sizeof (wchar_t)) | 
| 16 | 15 | ||
| 17 | 16 | ||
| 18 | // dictionary structure definitions | 17 | // dictionary structure definitions | 
| 19 | typedef struct dictionary_entry_s | 18 | typedef struct dictionary_entry_s | 
| 20 | { | 19 | { | 
| 21 | wchar_t key | 20 | wchar_t *key; // key string (mallocated) | 
| 22 | unsigned long hash; // key hash value | 21 | unsigned long hash; // key hash value | 
| 23 | wchar_t value | 22 | wchar_t *value; // value string (mallocated) | 
| 24 | } dictionary_entry_t; | 23 | } dictionary_entry_t; | 
| 25 | 24 | ||
| 26 | 25 | ||
| 27 | typedef struct dictionary_s | 26 | typedef struct dictionary_s | 
| 28 | { | 27 | { | 
| Line 64... | Line 63... | ||
| 64 | unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data); | 63 | unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data); | 
| 65 | void INIFile_FreeINIFile (void *ini_data); | 64 | void INIFile_FreeINIFile (void *ini_data); | 
| 66 | 65 | ||
| 67 | 66 | ||
| 68 | // internal variables | 67 | // internal variables | 
| 69 | static wchar_t *INIFile_default_section = L"__default"; | 68 | static wchar_t *INIFile_default_section = L"__default"; // MUST BE A READ-WRITE VARIABLE AND NOT A DEFINE | 
| 70 | static wchar_t line_buffer[MAX_KEY_SIZE + MAX_VALUE_SIZE]; | - | |
| 71 | static wchar_t temp_key[MAX_KEY_SIZE]; | - | |
| 72 | static wchar_t number_as_string[256]; | - | |
| 73 | static wchar_t temp_section[MAX_KEY_SIZE]; | - | |
| 74 | static wchar_t temp_entry[MAX_KEY_SIZE]; | - | |
| 75 | static wchar_t temp_value[MAX_VALUE_SIZE]; | - | |
| 76 | 69 | ||
| 77 | 70 | ||
| 78 | static dictionary_t *Dictionary_CreateDictionary (int size) | 71 | static dictionary_t *Dictionary_CreateDictionary (int size) | 
| 79 | { | 72 | { | 
| 80 |    // this function allocates a new dictionary object of given size and returns it. If you do | 73 |    // this function allocates a new dictionary object of given size and returns it. If you do | 
| Line 98... | Line 91... | ||
| 98 | 91 | ||
| 99 | 92 | ||
| 100 | static void Dictionary_DestroyDictionary (dictionary_t *dictionary) | 93 | static void Dictionary_DestroyDictionary (dictionary_t *dictionary) | 
| 101 | { | 94 | { | 
| 102 |    // frees a dictionary object and all memory associated to it. | 95 |    // frees a dictionary object and all memory associated to it. | 
| - | 96 | ||
| - | 97 | int entry_index; | |
| 103 | 98 | ||
| 104 | if (dictionary == NULL) | 99 | if (dictionary == NULL) | 
| 105 | return; // consistency check | 100 | return; // consistency check | 
| - | 101 | ||
| - | 102 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | |
| - | 103 |    { | |
| - | 104 | SAFE_free ((void **) &dictionary->entries[entry_index].key); // free all dictionary keys | |
| - | 105 | SAFE_free ((void **) &dictionary->entries[entry_index].value); // free all dictionary values | |
| - | 106 |    } | |
| 106 | 107 | ||
| 107 | SAFE_free ((void **) &dictionary->entries); // free the entries array | 108 | SAFE_free ((void **) &dictionary->entries); // free the entries array | 
| 108 | SAFE_free ((void **) &dictionary); // and finally, free the dictionary itself | 109 | SAFE_free ((void **) &dictionary); // and finally, free the dictionary itself | 
| 109 | 110 | ||
| 110 | return; // finished | 111 | return; // finished | 
| Line 159... | Line 160... | ||
| 159 |    // value is replaced by the provided one. If the key cannot be found in the dictionary, it | 160 |    // value is replaced by the provided one. If the key cannot be found in the dictionary, it | 
| 160 |    // is added to it. | 161 |    // is added to it. | 
| 161 | 162 | ||
| 162 | unsigned long hash; | 163 | unsigned long hash; | 
| 163 | register int entry_index; | 164 | register int entry_index; | 
| - | 165 | size_t bufsize; | |
| 164 | 166 | ||
| 165 | hash = Dictionary_ComputeHashValueForKey (key); // compute hash for this key | 167 | hash = Dictionary_ComputeHashValueForKey (key); // compute hash for this key | 
| 166 | 168 | ||
| 167 |    // for each entry in the dictionary... | 169 |    // for each entry in the dictionary... | 
| 168 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | 170 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | 
| Line 173... | Line 175... | ||
| 173 |       // does that key have the same hash value AND the same name ? | 175 |       // does that key have the same hash value AND the same name ? | 
| 174 | if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) | 176 | if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) | 
| 175 |       { | 177 |       { | 
| 176 |          // we've found the right key: modify its value and return | 178 |          // we've found the right key: modify its value and return | 
| 177 | 179 | ||
| 178 |          // have we provided a valid value ? | - | |
| 179 | if (value | 180 | if (value == NULL) | 
| 180 | 
 | 181 | value = L""; // if a null value was specified, use an empty string | 
| - | 182 | ||
| 181 | 
 | 183 | bufsize = wcslen (value) + 1; | 
| - | 184 | dictionary->entries[entry_index].value = (wchar_t *) SAFE_realloc (dictionary->entries[entry_index].value, 0, bufsize, sizeof (wchar_t), false); | |
| 182 | 
 | 185 | wcscpy_s (dictionary->entries[entry_index].value, bufsize, value); // copy the value string | 
| 183 | 186 | ||
| 184 | return; // value has been modified: return | 187 | return; // value has been modified: return | 
| 185 |       } | 188 |       } | 
| 186 |    } | 189 |    } | 
| 187 | 190 | ||
| Line 199... | Line 202... | ||
| 199 | dictionary->entries = (dictionary_entry_t *) SAFE_realloc (dictionary->entries, dictionary->size, dictionary->size + DICTIONARY_PAGE_SIZE, sizeof (dictionary_entry_t), false); // alloc 1 more page | 202 | dictionary->entries = (dictionary_entry_t *) SAFE_realloc (dictionary->entries, dictionary->size, dictionary->size + DICTIONARY_PAGE_SIZE, sizeof (dictionary_entry_t), false); // alloc 1 more page | 
| 200 | dictionary->size += DICTIONARY_PAGE_SIZE; // increase dictionary size | 203 | dictionary->size += DICTIONARY_PAGE_SIZE; // increase dictionary size | 
| 201 |    } | 204 |    } | 
| 202 | 205 | ||
| 203 |    // copy the new key | 206 |    // copy the new key | 
| 204 | - | ||
| - | 207 | bufsize = wcslen (key) + 1; | |
| - | 208 | dictionary->entries[entry_index].key = (wchar_t *) SAFE_malloc (bufsize, sizeof (wchar_t), false); | |
| 205 | wcscpy_s (dictionary->entries[entry_index].key, | 209 | wcscpy_s (dictionary->entries[entry_index].key, bufsize, key); // copy the key string... | 
| 206 | dictionary->entries[entry_index].hash = hash; // ...and store the hash value | 210 | dictionary->entries[entry_index].hash = hash; // ...and store the hash value | 
| 207 | 211 | ||
| 208 |    //  | 212 |    // copy the new value | 
| 209 | if (value | 213 | if (value == NULL) | 
| 210 | 
 | 214 | value = L""; // if a null value was specified, use an empty string | 
| 211 | 
 | 215 | bufsize = wcslen (value) + 1; | 
| - | 216 | dictionary->entries[entry_index].value = (wchar_t *) SAFE_malloc (bufsize, sizeof (wchar_t), false); | |
| 212 | 
 | 217 | wcscpy_s (dictionary->entries[entry_index].value, bufsize, value); // copy the value string | 
| 213 | 218 | ||
| 214 | dictionary->entry_count++; // there is one more entry in the dictionary now | 219 | dictionary->entry_count++; // there is one more entry in the dictionary now | 
| 215 | return; | 220 | return; | 
| 216 | } | 221 | } | 
| 217 | 222 | ||
| Line 237... | Line 242... | ||
| 237 |    } | 242 |    } | 
| 238 | 243 | ||
| 239 | if (entry_index == dictionary->entry_count) | 244 | if (entry_index == dictionary->entry_count) | 
| 240 | return; // if key was not found, just return | 245 | return; // if key was not found, just return | 
| 241 | 246 | ||
| 242 |    // clear the key, the hash and its value data | 247 |    // free its buffers, then clear the key, the hash and its value data | 
| - | 248 | SAFE_free ((void **) &dictionary->entries[entry_index].key); | |
| - | 249 | SAFE_free ((void **) &dictionary->entries[entry_index].value); | |
| 243 | memset (&dictionary->entries[entry_index], 0, sizeof (dictionary_entry_t)); | 250 | memset (&dictionary->entries[entry_index], 0, sizeof (dictionary_entry_t)); | 
| 244 | 251 | ||
| 245 | dictionary->entry_count--; // there is one entry less in the dictionary now | 252 | dictionary->entry_count--; // there is one entry less in the dictionary now | 
| 246 | return; | 253 | return; | 
| 247 | } | 254 | } | 
| Line 409... | Line 416... | ||
| 409 | if (section == NULL) | 416 | if (section == NULL) | 
| 410 | section = INIFile_default_section; // if no section was provided, use empty section name | 417 | section = INIFile_default_section; // if no section was provided, use empty section name | 
| 411 | 418 | ||
| 412 | dictionary = (dictionary_t *) ini_data; // quick access to dictionary | 419 | dictionary = (dictionary_t *) ini_data; // quick access to dictionary | 
| 413 | 420 | ||
| 414 |    // build the key entry prefix | - | |
| 415 | swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c", section, INI_SECTION_SEPARATOR); // compose the key | - | |
| 416 | length = wcslen ( | 421 | length = wcslen (section); | 
| 417 | 422 | ||
| 418 |    // cycle through all remaining entries | 423 |    // cycle through all remaining entries | 
| 419 | found_keys = 0; | 424 | found_keys = 0; | 
| 420 | for (i = 0; i < dictionary->entry_count; i++) | 425 | for (i = 0; i < dictionary->entry_count; i++) | 
| 421 |    { | 426 |    { | 
| 422 | entry = &dictionary->entries[i]; // quick access to each entry | 427 | entry = &dictionary->entries[i]; // quick access to each entry | 
| 423 | 428 | ||
| 424 | if (wcsncmp (entry->key, | 429 | if ((wcsncmp (entry->key, section, length) != 0) || (entry->key[length] != INI_SECTION_SEPARATOR)) | 
| 425 | continue; // if this key doesn't belong to the wanted section, skip it | 430 | continue; // if this key doesn't belong to the wanted section, skip it | 
| 426 | 431 | ||
| 427 |       // this key belongs to the wanted section. Is it the key we want ? | 432 |       // this key belongs to the wanted section. Is it the key we want ? | 
| 428 | if (found_keys == entry_index) | 433 | if (found_keys == entry_index) | 
| 429 | return (&entry->key[length]); // if so, return its name | 434 | return (&entry->key[length + 1]); // if so, return its name | 
| 430 | 435 | ||
| 431 | found_keys++; // it wasn't the key we want, so increase counter and proceed to the next one | 436 | found_keys++; // it wasn't the key we want, so increase counter and proceed to the next one | 
| 432 |    } | 437 |    } | 
| 433 | 438 | ||
| 434 | return (NULL); // if not found, return NULL | 439 | return (NULL); // if not found, return NULL | 
| Line 528... | Line 533... | ||
| 528 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | 533 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | 
| 529 |    // "section]key". If the key cannot be found, the pointer passed as 'def' is returned. The | 534 |    // "section]key". If the key cannot be found, the pointer passed as 'def' is returned. The | 
| 530 |    // returned char pointer points to a string allocated in the dictionary, do not free or | 535 |    // returned char pointer points to a string allocated in the dictionary, do not free or | 
| 531 |    // modify it. | 536 |    // modify it. | 
| 532 | 537 | ||
| 533 | 
 | 538 | size_t length; | 
| 534 | 
 | 539 | size_t i; | 
| - | 540 | size_t keysize; | |
| - | 541 | wchar_t *key; | |
| 535 | wchar_t *value; | 542 | wchar_t *value; | 
| 536 | 543 | ||
| 537 | if (ini_data == NULL) | 544 | if (ini_data == NULL) | 
| 538 | return (default_value); // consistency check | 545 | return (default_value); // consistency check | 
| 539 | 546 | ||
| 540 | if (section == NULL) | 547 | if (section == NULL) | 
| 541 | section = INIFile_default_section; // if no section was provided, use empty section name | 548 | section = INIFile_default_section; // if no section was provided, use empty section name | 
| - | 549 | ||
| - | 550 |    // allocate some space for the dictionary key name | |
| - | 551 | keysize = wcslen (section) + 1 + wcslen (entry) + 1; | |
| - | 552 | key = (wchar_t *) SAFE_malloc (keysize, sizeof (wchar_t), false); | |
| 542 | 553 | ||
| 543 |    // if we were given an entry, build a key as section]entry | 554 |    // if we were given an entry, build a key as section]entry | 
| 544 | if (entry != NULL) | 555 | if (entry != NULL) | 
| 545 |    { | 556 |    { | 
| 546 | swprintf_s ( | 557 | swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key | 
| 547 | 558 | ||
| 548 | i = | 559 | i = wcslen (key); // get the key string length | 
| 549 | if (i < | 560 | if (i < keysize) | 
| 550 | length = i; | 561 | length = i; | 
| 551 |       else | 562 |       else | 
| 552 | length = | 563 | length = keysize - 1; // clamp it to a max value | 
| 553 | 564 | ||
| 554 |       // for each character in the string after the section separator... | 565 |       // for each character in the string after the section separator... | 
| 555 | for (i = | 566 | for (i = wcslen (section) + 1; i < length; i++) | 
| 556 | 
 | 567 | key[i] = towlower (key[i]); // convert it to lowercase | 
| 557 | 
 | 568 | key[i] = 0; // terminate the string | 
| 558 |    } | 569 |    } | 
| 559 | 570 | ||
| 560 |    // else it must be a section name | 571 |    // else it must be a section name | 
| 561 |    else | 572 |    else | 
| 562 | wcscpy_s ( | 573 | wcscpy_s (key, keysize, section); // copy the name into the key | 
| 563 | 574 | ||
| 564 | value = Dictionary_ReadKey ((dictionary_t *) ini_data, | 575 | value = Dictionary_ReadKey ((dictionary_t *) ini_data, key, default_value); // query the dictionary... | 
| - | 576 | SAFE_free ((void **) &key); // free the key name, we no longer need it | |
| 565 | return (value); // ...and return the value | 577 | return (value); // ...and return the value | 
| 566 | } | 578 | } | 
| 567 | 579 | ||
| 568 | 580 | ||
| 569 | void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value) | 581 | void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value) | 
| Line 584... | Line 596... | ||
| 584 | void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value) | 596 | void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value) | 
| 585 | { | 597 | { | 
| 586 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 598 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 
| 587 |    // modified to contain the provided value. If it cannot be found, it is created. | 599 |    // modified to contain the provided value. If it cannot be found, it is created. | 
| 588 | 600 | ||
| 589 |    // build the long integer value equivalent string (use printf facility) | - | |
| 590 | 
 | 601 | wchar_t *number_as_string; | 
| 591 | 602 | ||
| - | 603 |    // allocate some space for the number's string representation | |
| - | 604 | number_as_string = (wchar_t *) SAFE_malloc (32, sizeof (wchar_t), false); | |
| - | 605 | ||
| - | 606 |    // build the long integer value equivalent string (use printf facility) | |
| - | 607 | swprintf_s (number_as_string, 32, L"%ld", value); | |
| 592 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | 608 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | 
| - | 609 | ||
| - | 610 | SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it | |
| 593 | return; // finished | 611 | return; // finished | 
| 594 | } | 612 | } | 
| 595 | 613 | ||
| 596 | 614 | ||
| 597 | void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value) | 615 | void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value) | 
| 598 | { | 616 | { | 
| 599 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 617 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 
| 600 |    // modified to contain the provided value. If it cannot be found, it is created. | 618 |    // modified to contain the provided value. If it cannot be found, it is created. | 
| 601 | 619 | ||
| 602 |    // build the long integer value equivalent string (use printf facility) | - | |
| 603 | 
 | 620 | wchar_t *number_as_string; | 
| 604 | 621 | ||
| - | 622 |    // allocate some space for the number's string representation | |
| - | 623 | number_as_string = (wchar_t *) SAFE_malloc (32, sizeof (wchar_t), false); | |
| - | 624 | ||
| - | 625 |    // build the long integer value equivalent string (use printf facility) | |
| - | 626 | swprintf_s (number_as_string, 32, L"%lu", value); | |
| 605 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | 627 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | 
| - | 628 | ||
| - | 629 | SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it | |
| 606 | return; // finished | 630 | return; // finished | 
| 607 | } | 631 | } | 
| 608 | 632 | ||
| 609 | 633 | ||
| 610 | void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value) | 634 | void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value) | 
| 611 | { | 635 | { | 
| 612 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 636 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 
| 613 |    // modified to contain the provided value. If it cannot be found, it is created. | 637 |    // modified to contain the provided value. If it cannot be found, it is created. | 
| 614 | 638 | ||
| 615 |    // build the long integer value equivalent string (use printf facility) | - | |
| 616 | 
 | 639 | wchar_t *number_as_string; | 
| 617 | 640 | ||
| - | 641 |    // allocate some space for the number's string representation | |
| - | 642 | number_as_string = (wchar_t *) SAFE_malloc (64, sizeof (wchar_t), false); | |
| - | 643 | ||
| - | 644 |    // build the long integer value equivalent string (use printf facility) | |
| - | 645 | swprintf_s (number_as_string, 64, L"%g", value); | |
| 618 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | 646 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | 
| - | 647 | ||
| - | 648 | SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it | |
| 619 | return; // finished | 649 | return; // finished | 
| 620 | } | 650 | } | 
| 621 | 651 | ||
| 622 | 652 | ||
| 623 | void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value) | 653 | void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value) | 
| 624 | { | 654 | { | 
| 625 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 655 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | 
| 626 |    // modified to contain the provided value. If it cannot be found, it is created. | 656 |    // modified to contain the provided value. If it cannot be found, it is created. | 
| 627 | 657 | ||
| 628 | 
 | 658 | size_t length; | 
| 629 | 
 | 659 | size_t i; | 
| - | 660 | size_t keysize; | |
| - | 661 | wchar_t *key; | |
| 630 | 662 | ||
| 631 | if (ini_data == NULL) | 663 | if (ini_data == NULL) | 
| 632 | return; // consistency check | 664 | return; // consistency check | 
| 633 | 665 | ||
| 634 | if (section == NULL) | 666 | if (section == NULL) | 
| 635 | section = INIFile_default_section; // if no section was provided, use empty section name | 667 | section = INIFile_default_section; // if no section was provided, use empty section name | 
| - | 668 | ||
| - | 669 |    // allocate some space for the dictionary key name | |
| - | 670 | keysize = wcslen (section) + 1 + (entry != NULL ? wcslen (entry) + 1 : 0); | |
| - | 671 | key = (wchar_t *) SAFE_malloc (keysize, sizeof (wchar_t), false); | |
| 636 | 672 | ||
| 637 |    // if we were given an entry, build a key as section#entry | 673 |    // if we were given an entry, build a key as section#entry | 
| 638 | if (entry != NULL) | 674 | if (entry != NULL) | 
| 639 |    { | 675 |    { | 
| 640 | Dictionary_WriteKey ((dictionary_t *) ini_data, section, NULL); // create the section if it doesn't exist | 676 | Dictionary_WriteKey ((dictionary_t *) ini_data, section, NULL); // create the section if it doesn't exist | 
| 641 | 677 | ||
| 642 | swprintf_s ( | 678 | swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key | 
| 643 | 679 | ||
| 644 | i = | 680 | i = wcslen (key); // get the key string length | 
| 645 | if (i < | 681 | if (i < keysize) | 
| 646 | length = i; | 682 | length = i; | 
| 647 |       else | 683 |       else | 
| 648 | length = | 684 | length = keysize - 1; // clamp it to a max value | 
| 649 | 685 | ||
| 650 |       // for each character in the string after the section separator... | 686 |       // for each character in the string after the section separator... | 
| 651 | for (i = | 687 | for (i = wcslen (section) + 1; i < length; i++) | 
| 652 | 
 | 688 | key[i] = towlower (key[i]); // convert it to lowercase | 
| 653 | 
 | 689 | key[i] = 0; // terminate the string | 
| 654 |    } | 690 |    } | 
| 655 | 691 | ||
| 656 |    // else it must be a section name | 692 |    // else it must be a section name | 
| 657 |    else | 693 |    else | 
| 658 | wcscpy_s ( | 694 | wcscpy_s (key, keysize, section); // copy the name into the key | 
| 659 | 695 | ||
| 660 | Dictionary_WriteKey ((dictionary_t *) ini_data, | 696 | Dictionary_WriteKey ((dictionary_t *) ini_data, key, value); // write the new key in the dictionary | 
| - | 697 | SAFE_free ((void **) &key); // free the key name, we no longer need it | |
| 661 | return; // finished | 698 | return; // finished | 
| 662 | } | 699 | } | 
| 663 | 700 | ||
| 664 | 701 | ||
| 665 | void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry) | 702 | void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry) | 
| 666 | { | 703 | { | 
| 667 |    // deletes an entry in the dictionary | 704 |    // deletes an entry in the dictionary | 
| 668 | 705 | ||
| 669 | 
 | 706 | size_t length; | 
| 670 | 
 | 707 | size_t i; | 
| - | 708 | size_t keysize; | |
| - | 709 | wchar_t *key; | |
| 671 | 710 | ||
| 672 | if (ini_data == NULL) | 711 | if (ini_data == NULL) | 
| 673 | return; // consistency check | 712 | return; // consistency check | 
| 674 | 713 | ||
| 675 | if (section == NULL) | 714 | if (section == NULL) | 
| 676 | section = INIFile_default_section; // if no section was provided, use empty section name | 715 | section = INIFile_default_section; // if no section was provided, use empty section name | 
| - | 716 | ||
| - | 717 |    // allocate some space for the dictionary key name | |
| - | 718 | keysize = wcslen (section) + 1 + wcslen (entry) + 1; | |
| - | 719 | key = (wchar_t *) SAFE_malloc (keysize, sizeof (wchar_t), false); | |
| 677 | 720 | ||
| 678 |    // if we were given an entry, build a key as section#entry | 721 |    // if we were given an entry, build a key as section#entry | 
| 679 | if (entry != NULL) | 722 | if (entry != NULL) | 
| 680 |    { | 723 |    { | 
| 681 | swprintf_s ( | 724 | swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key | 
| 682 | 725 | ||
| 683 | i = | 726 | i = wcslen (key); // get the key string length | 
| 684 | if (i < | 727 | if (i < keysize) | 
| 685 | length = i; | 728 | length = i; | 
| 686 |       else | 729 |       else | 
| 687 | length = | 730 | length = keysize - 1; // clamp it to a max value | 
| 688 | 731 | ||
| 689 |       // for each character in the string after the section separator... | 732 |       // for each character in the string after the section separator... | 
| 690 | for (i = | 733 | for (i = wcslen (section) + 1; i < length; i++) | 
| 691 | 
 | 734 | key[i] = towlower (key[i]); // convert it to lowercase | 
| 692 | 
 | 735 | key[i] = 0; // terminate the string | 
| 693 |    } | 736 |    } | 
| 694 | 737 | ||
| 695 |    // else it must be a section name | 738 |    // else it must be a section name | 
| 696 |    else | 739 |    else | 
| 697 | wcscpy_s ( | 740 | wcscpy_s (key, keysize, section); // copy the name into the key | 
| 698 | 741 | ||
| 699 | Dictionary_DeleteKey ((dictionary_t *) ini_data, | 742 | Dictionary_DeleteKey ((dictionary_t *) ini_data, key); | 
| - | 743 | SAFE_free ((void **) &key); // free the key name, we no longer need it | |
| 700 | return; | 744 | return; | 
| 701 | } | 745 | } | 
| 702 | 746 | ||
| 703 | 747 | ||
| 704 | void INIFile_DeleteSection (void *ini_data, wchar_t *section) | 748 | void INIFile_DeleteSection (void *ini_data, wchar_t *section) | 
| Line 706... | Line 750... | ||
| 706 |    // deletes a whole INI section in the dictionary | 750 |    // deletes a whole INI section in the dictionary | 
| 707 | 751 | ||
| 708 | int length; | 752 | int length; | 
| 709 | int i; | 753 | int i; | 
| 710 | dictionary_t *dictionary; | 754 | dictionary_t *dictionary; | 
| - | 755 | dictionary_entry_t *entry; | |
| 711 | 756 | ||
| 712 | if (ini_data == NULL) | 757 | if (ini_data == NULL) | 
| 713 | return; // consistency check | 758 | return; // consistency check | 
| 714 | 759 | ||
| 715 | if (section == NULL) | 760 | if (section == NULL) | 
| 716 | section = INIFile_default_section; // if no section was provided, use empty section name | 761 | section = INIFile_default_section; // if no section was provided, use empty section name | 
| 717 | 762 | ||
| 718 | dictionary = (dictionary_t *) ini_data; | 763 | dictionary = (dictionary_t *) ini_data; | 
| 719 | 764 | ||
| 720 | swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c", section, INI_SECTION_SEPARATOR); // compose the key | - | |
| 721 | length = (int) wcslen ( | 765 | length = (int) wcslen (section); // get the section string length | 
| 722 | 766 | ||
| 723 |    // for each entry in the dictionary... | 767 |    // for each entry in the dictionary... | 
| 724 | for (i = 0; i < dictionary->entry_count; i++) | 768 | for (i = 0; i < dictionary->entry_count; i++) | 
| 725 |    { | 769 |    { | 
| - | 770 | entry = &dictionary->entries[i]; // quick access to each entry | |
| - | 771 | ||
| 726 | if ( | 772 | if (entry->key[0] == 0) | 
| 727 | continue; // skip empty slots | 773 | continue; // skip empty slots | 
| 728 | 774 | ||
| 729 |       // does this entry belong to the section we want ? | 775 |       // does this entry belong to the section we want ? | 
| 730 | if (wcsncmp ( | 776 | if ((wcsncmp (entry->key, section, length) == 0) && (entry->key[length] == INI_SECTION_SEPARATOR)) | 
| 731 | Dictionary_DeleteKey (dictionary, | 777 | Dictionary_DeleteKey (dictionary, entry->key); // yes, delete it | 
| 732 |    } | 778 |    } | 
| 733 | 779 | ||
| 734 | Dictionary_DeleteKey (dictionary, section); // and finally delete the section name itself | 780 | Dictionary_DeleteKey (dictionary, section); // and finally delete the section name itself | 
| 735 | return; | 781 | return; | 
| 736 | } | 782 | } | 
| Line 740... | Line 786... | ||
| 740 | { | 786 | { | 
| 741 |    // this is the parser for ini files. This function is called, providing the name of the file | 787 |    // this is the parser for ini files. This function is called, providing the name of the file | 
| 742 |    // to be read. It returns a dictionary object that should not be accessed directly, but | 788 |    // to be read. It returns a dictionary object that should not be accessed directly, but | 
| 743 |    // through accessor functions instead. | 789 |    // through accessor functions instead. | 
| 744 | 790 | ||
| - | 791 | wchar_t *line_buffer; | |
| 745 | FILE *fp; | 792 | FILE *fp; | 
| 746 | int fieldstart; | 793 | int fieldstart; | 
| 747 | int fieldstop; | 794 | int fieldstop; | 
| 748 | int length; | 795 | int length; | 
| 749 | int i; | 796 | int i; | 
| 750 | dictionary_t *dictionary; | 797 | dictionary_t *dictionary; | 
| - | 798 | wchar_t *current_section; | |
| - | 799 | wchar_t *current_entry; | |
| - | 800 | wchar_t *current_value; | |
| 751 | 801 | ||
| 752 |    // try to open the INI file in ASCII read-only mode | 802 |    // try to open the INI file in ASCII read-only mode | 
| 753 | fp = _wfsopen (filename, L"r, ccs=UNICODE", _SH_DENYNO); | 803 | fp = _wfsopen (filename, L"r, ccs=UNICODE", _SH_DENYNO); | 
| 754 | if (fp == NULL) | 804 | if (fp == NULL) | 
| 755 | return (NULL); // cancel if file not found | 805 | return (NULL); // cancel if file not found | 
| 756 | 806 | ||
| 757 | dictionary = Dictionary_CreateDictionary (0); | 807 | dictionary = Dictionary_CreateDictionary (0); | 
| - | 808 | ||
| - | 809 |    // allocate some space for the current section, entry and values | |
| - | 810 | current_section = (wchar_t *) SAFE_malloc (STRING_MAXSIZE, sizeof (wchar_t), false); | |
| - | 811 | current_entry = (wchar_t *) SAFE_malloc (STRING_MAXSIZE, sizeof (wchar_t), false); | |
| - | 812 | current_value = (wchar_t *) SAFE_malloc (STRING_MAXSIZE, sizeof (wchar_t), false); | |
| 758 | 813 | ||
| 759 |    // set the default section for orphaned entries and add it to the dictionary | 814 |    // set the default section for orphaned entries and add it to the dictionary | 
| 760 | wcscpy_s ( | 815 | wcscpy_s (current_section, STRING_MAXSIZE, INIFile_default_section); | 
| 761 | INIFile_WriteEntryAsString (dictionary, | 816 | INIFile_WriteEntryAsString (dictionary, current_section, NULL, NULL); | 
| - | 817 | ||
| - | 818 |    // allocate some space for a line buffer | |
| - | 819 | line_buffer = (wchar_t *) SAFE_malloc (STRING_MAXSIZE + STRING_MAXSIZE, sizeof (wchar_t), false); | |
| 762 | 820 | ||
| 763 |    // read line per line... | 821 |    // read line per line... | 
| 764 | while (fgetws (line_buffer, | 822 | while (fgetws (line_buffer, STRING_MAXSIZE + STRING_MAXSIZE, fp) != NULL) | 
| 765 |    { | 823 |    { | 
| 766 | length = (int) wcslen (line_buffer); // get line length | 824 | length = (int) wcslen (line_buffer); // get line length | 
| 767 | 825 | ||
| 768 | while ((length > 0) && ((line_buffer[length - 1] == '\n') || (line_buffer[length - 1] == '\r'))) | 826 | while ((length > 0) && ((line_buffer[length - 1] == '\n') || (line_buffer[length - 1] == '\r'))) | 
| 769 | length--; // discard trailing newlines | 827 | length--; // discard trailing newlines | 
| Line 782... | Line 840... | ||
| 782 | 840 | ||
| 783 | if ((line_buffer[0] == ';') || (line_buffer[0] == '#') || (line_buffer[0] == 0)) | 841 | if ((line_buffer[0] == ';') || (line_buffer[0] == '#') || (line_buffer[0] == 0)) | 
| 784 | continue; // skip comment lines | 842 | continue; // skip comment lines | 
| 785 | 843 | ||
| 786 |       // is it a valid section name ? | 844 |       // is it a valid section name ? | 
| 787 | if (swscanf_s (line_buffer, L"[%[^]]", | 845 | if (swscanf_s (line_buffer, L"[%[^]]", current_section, STRING_MAXSIZE) == 1) | 
| 788 |       { | 846 |       { | 
| 789 | length = (int) wcslen ( | 847 | length = (int) wcslen (current_section); // get the section string length | 
| 790 | 848 | ||
| 791 | fieldstart = 0; // let's now strip leading blanks | 849 | fieldstart = 0; // let's now strip leading blanks | 
| 792 | while ((fieldstart < length) && iswspace ( | 850 | while ((fieldstart < length) && iswspace (current_section[fieldstart])) | 
| 793 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 851 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 794 | 852 | ||
| 795 | fieldstop = length - 1; // let's now strip trailing blanks | 853 | fieldstop = length - 1; // let's now strip trailing blanks | 
| 796 | while ((fieldstop >= 0) && iswspace ( | 854 | while ((fieldstop >= 0) && iswspace (current_section[fieldstop])) | 
| 797 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 855 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 798 | 856 | ||
| 799 | for (i = fieldstart; i <= fieldstop; i++) | 857 | for (i = fieldstart; i <= fieldstop; i++) | 
| 800 | 
 | 858 | current_section[i - fieldstart] = current_section[i]; // recopy section name w/out spaces | 
| 801 | 
 | 859 | current_section[i - fieldstart] = 0; // and terminate the string | 
| 802 | 860 | ||
| 803 | INIFile_WriteEntryAsString (dictionary, | 861 | INIFile_WriteEntryAsString (dictionary, current_section, NULL, NULL); // add to dictionary | 
| 804 |       } | 862 |       } | 
| 805 | 863 | ||
| 806 |       // else is it a valid entry/value pair that is enclosed between quotes? | 864 |       // else is it a valid entry/value pair that is enclosed between quotes? | 
| 807 | else if (swscanf_s (line_buffer, L"%[^=] = \"%[^\"]\"", | 865 | else if (swscanf_s (line_buffer, L"%[^=] = \"%[^\"]\"", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | 
| 808 |       { | 866 |       { | 
| 809 | length = (int) wcslen ( | 867 | length = (int) wcslen (current_entry); // get the entry string length | 
| 810 | 868 | ||
| 811 | fieldstart = 0; // let's now strip leading blanks | 869 | fieldstart = 0; // let's now strip leading blanks | 
| 812 | while ((fieldstart < length) && iswspace ( | 870 | while ((fieldstart < length) && iswspace (current_entry[fieldstart])) | 
| 813 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 871 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 814 | 872 | ||
| 815 | fieldstop = length - 1; // let's now strip trailing blanks | 873 | fieldstop = length - 1; // let's now strip trailing blanks | 
| 816 | while ((fieldstop >= 0) && iswspace ( | 874 | while ((fieldstop >= 0) && iswspace (current_entry[fieldstop])) | 
| 817 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 875 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 818 | 876 | ||
| 819 | for (i = fieldstart; i <= fieldstop; i++) | 877 | for (i = fieldstart; i <= fieldstop; i++) | 
| 820 | 
 | 878 | current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces | 
| 821 | 
 | 879 | current_entry[i - fieldstart] = 0; // and terminate the string | 
| 822 | 880 | ||
| 823 |          // when value is enclosed between quotes, DO NOT strip the blanks | 881 |          // when value is enclosed between quotes, DO NOT strip the blanks | 
| 824 | 882 | ||
| 825 |          // sscanf cannot handle "" or '' as empty value, this is done here | 883 |          // sscanf cannot handle "" or '' as empty value, this is done here | 
| 826 | if ((wcscmp ( | 884 | if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) | 
| 827 | 
 | 885 | current_value[0] = 0; // empty string | 
| 828 | 886 | ||
| 829 | INIFile_WriteEntryAsString (dictionary, | 887 | INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary | 
| 830 |       } | 888 |       } | 
| 831 | 889 | ||
| 832 |       // else is it a valid entry/value pair without quotes ? | 890 |       // else is it a valid entry/value pair without quotes ? | 
| 833 | else if ((swscanf_s (line_buffer, L"%[^=] = '%[^\']'", | 891 | else if ((swscanf_s (line_buffer, L"%[^=] = '%[^\']'", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | 
| 834 | || (swscanf_s (line_buffer, L"%[^=] = %[^;#]", | 892 | || (swscanf_s (line_buffer, L"%[^=] = %[^;#]", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2)) | 
| 835 |       { | 893 |       { | 
| 836 | length = (int) wcslen ( | 894 | length = (int) wcslen (current_entry); // get the entry string length | 
| 837 | 895 | ||
| 838 | fieldstart = 0; // let's now strip leading blanks | 896 | fieldstart = 0; // let's now strip leading blanks | 
| 839 | while ((fieldstart < length) && iswspace ( | 897 | while ((fieldstart < length) && iswspace (current_entry[fieldstart])) | 
| 840 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 898 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 841 | 899 | ||
| 842 | fieldstop = length - 1; // let's now strip trailing blanks | 900 | fieldstop = length - 1; // let's now strip trailing blanks | 
| 843 | while ((fieldstop >= 0) && iswspace ( | 901 | while ((fieldstop >= 0) && iswspace (current_entry[fieldstop])) | 
| 844 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 902 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 845 | 903 | ||
| 846 | for (i = fieldstart; i <= fieldstop; i++) | 904 | for (i = fieldstart; i <= fieldstop; i++) | 
| 847 | 
 | 905 | current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces | 
| 848 | 
 | 906 | current_entry[i - fieldstart] = 0; // and terminate the string | 
| 849 | 907 | ||
| 850 | length = (int) wcslen ( | 908 | length = (int) wcslen (current_value); // get the value string length | 
| 851 | 909 | ||
| 852 | fieldstart = 0; // let's now strip leading blanks | 910 | fieldstart = 0; // let's now strip leading blanks | 
| 853 | while ((fieldstart < length) && iswspace ( | 911 | while ((fieldstart < length) && iswspace (current_value[fieldstart])) | 
| 854 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 912 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 855 | 913 | ||
| 856 | fieldstop = length - 1; // let's now strip trailing blanks | 914 | fieldstop = length - 1; // let's now strip trailing blanks | 
| 857 | while ((fieldstop >= 0) && iswspace ( | 915 | while ((fieldstop >= 0) && iswspace (current_value[fieldstop])) | 
| 858 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 916 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 859 | 917 | ||
| 860 | for (i = fieldstart; i <= fieldstop; i++) | 918 | for (i = fieldstart; i <= fieldstop; i++) | 
| 861 | 
 | 919 | current_value[i - fieldstart] = current_value[i]; // recopy entry name w/out spaces | 
| 862 | 
 | 920 | current_value[i - fieldstart] = 0; // and terminate the string | 
| 863 | 921 | ||
| 864 |          // sscanf cannot handle "" or '' as empty value, this is done here | 922 |          // sscanf cannot handle "" or '' as empty value, this is done here | 
| 865 | if ((wcscmp ( | 923 | if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) | 
| 866 | 
 | 924 | current_value[0] = 0; // empty string | 
| 867 | 925 | ||
| 868 | INIFile_WriteEntryAsString (dictionary, | 926 | INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary | 
| 869 |       } | 927 |       } | 
| 870 |    } | 928 |    } | 
| 871 | 929 | ||
| 872 | fclose (fp); // finished, close the file | 930 | fclose (fp); // finished, close the file | 
| - | 931 | SAFE_free ((void **) &line_buffer); // free the line buffer we allocated at the beginning of the function | |
| - | 932 | SAFE_free ((void **) ¤t_value); // free the current value buffer we allocated at the beginning of the function | |
| - | 933 | SAFE_free ((void **) ¤t_entry); // free the current entry buffer we allocated at the beginning of the function | |
| - | 934 | SAFE_free ((void **) ¤t_section); // free the current section buffer we allocated at the beginning of the function | |
| 873 | 935 | ||
| 874 | return ((void *) dictionary); // and return | 936 | return ((void *) dictionary); // and return a pointer to it | 
| 875 | } | 937 | } | 
| 876 | 938 | ||
| 877 | 939 | ||
| 878 | unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data) | 940 | unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data) | 
| 879 | { | 941 | { | 
| Line 917... | Line 979... | ||
| 917 | if (wcscmp (section_name, INIFile_default_section) == 0) | 979 | if (wcscmp (section_name, INIFile_default_section) == 0) | 
| 918 | fwprintf (fp, L"\n"); // don't put the default section's name in the INI file | 980 | fwprintf (fp, L"\n"); // don't put the default section's name in the INI file | 
| 919 |       else | 981 |       else | 
| 920 | fwprintf (fp, L"\n[%s]\n", section_name); // dump all other sections into the INI file | 982 | fwprintf (fp, L"\n[%s]\n", section_name); // dump all other sections into the INI file | 
| 921 | 983 | ||
| 922 |       // build the section identifier to be used when looking up the dictionary | - | |
| 923 | swprintf_s (temp_key, WSIZEOF (temp_key), L"%s%c", section_name, INI_SECTION_SEPARATOR); | - | |
| 924 | length = (int) wcslen ( | 984 | length = (int) wcslen (section_name); | 
| 925 | 985 | ||
| 926 |       // then for each entry in the dictionary... | 986 |       // then for each entry in the dictionary... | 
| 927 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | 987 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | 
| 928 |       { | 988 |       { | 
| 929 | entry = &dictionary->entries[entry_index]; // quick access to entry | 989 | entry = &dictionary->entries[entry_index]; // quick access to entry | 
| 930 | 990 | ||
| 931 | if (entry->key[0] == 0) | 991 | if (entry->key[0] == 0) | 
| 932 | continue; // skip empty slots | 992 | continue; // skip empty slots | 
| 933 | 993 | ||
| 934 |          // does this key belong to the section we want ? if so, dump it | 994 |          // does this key belong to the section we want ? if so, dump it | 
| 935 | if (wcsncmp (entry->key, | 995 | if ((wcsncmp (entry->key, section_name, length) == 0) && (entry->key[length] == INI_SECTION_SEPARATOR)) | 
| 936 |          { | 996 |          { | 
| 937 |             // if key starts or ends with a space, enclose it between quotes, else don't | 997 |             // if key starts or ends with a space, enclose it between quotes, else don't | 
| 938 | if ((entry->value[0] != 0) | 998 | if ((entry->value[0] != 0) | 
| 939 | && (iswspace (entry->value[0]) || iswspace (entry->value[wcslen (entry->value) - 1]))) | 999 | && (iswspace (entry->value[0]) || iswspace (entry->value[wcslen (entry->value) - 1]))) | 
| 940 | fwprintf (fp, L"%s = \"%s\"\n", entry->key + | 1000 | fwprintf (fp, L"%s = \"%s\"\n", &entry->key[length + 1], entry->value); | 
| 941 |             else | 1001 |             else | 
| 942 | fwprintf (fp, L"%s = %s\n", entry->key + | 1002 | fwprintf (fp, L"%s = %s\n", &entry->key[length + 1], entry->value); | 
| 943 |          } | 1003 |          } | 
| 944 |       } | 1004 |       } | 
| 945 |    } | 1005 |    } | 
| 946 | 1006 | ||
| 947 | fclose (fp); // finished, close the file | 1007 | fclose (fp); // finished, close the file |