Rev 161 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 1 | pmbaty | 1 | // inifile.c | 
| 2 | |||
| 3 | #include "common.h" | ||
| 4 | |||
| 5 | |||
| 6 | // WARNING: INI SECTION NAMES ARE CASE SENSITIVE, BUT KEY NAMES ARE NOT! | ||
| 7 | |||
| 8 | |||
| 9 | // internal definitions | ||
| 10 | #define DICTIONARY_PAGE_SIZE 128 | ||
| 136 | pmbaty | 11 | #define STRING_MAXSIZE 65536 | 
| 1 | pmbaty | 12 | #define INI_SECTION_SEPARATOR ']' | 
| 13 | #define INI_INVALID_KEY (wchar_t *) 0x7fffffff | ||
| 14 | #define WSIZEOF(a) (sizeof (a) / sizeof (wchar_t)) | ||
| 15 | |||
| 16 | |||
| 17 | // dictionary structure definitions | ||
| 18 | typedef struct dictionary_entry_s | ||
| 19 | { | ||
| 136 | pmbaty | 20 | wchar_t *key; // key string (mallocated) | 
| 1 | pmbaty | 21 | unsigned long hash; // key hash value | 
| 136 | pmbaty | 22 | wchar_t *value; // value string (mallocated) | 
| 1 | pmbaty | 23 | } dictionary_entry_t; | 
| 24 | |||
| 25 | |||
| 26 | typedef struct dictionary_s | ||
| 27 | { | ||
| 28 | int size; // storage size | ||
| 29 | dictionary_entry_t *entries; // array of entries (mallocated) | ||
| 30 | int entry_count; // number of entries in dictionary | ||
| 31 | } dictionary_t; | ||
| 32 | |||
| 33 | |||
| 34 | // dictionary function prototypes | ||
| 35 | static dictionary_t *Dictionary_CreateDictionary (int size); | ||
| 36 | static void Dictionary_DestroyDictionary (dictionary_t *dictionary); | ||
| 37 | static dictionary_entry_t *Dictionary_GetKey (dictionary_t *dictionary, wchar_t *key); | ||
| 38 | static wchar_t *Dictionary_ReadKey (dictionary_t *dictionary, wchar_t *key, wchar_t *default_value); | ||
| 39 | static void Dictionary_WriteKey (dictionary_t *dictionary, wchar_t *key, wchar_t *value); | ||
| 40 | static void Dictionary_DeleteKey (dictionary_t *dictionary, wchar_t *key); | ||
| 41 | static unsigned long Dictionary_ComputeHashValueForKey (wchar_t *key); | ||
| 42 | |||
| 43 | |||
| 44 | // ini file function prototypes | ||
| 45 | void *INIFile_NewINIFile (void); | ||
| 46 | int INIFile_GetNumberOfSections (void *ini_data); | ||
| 47 | wchar_t *INIFile_GetSectionName (void *ini_data, int section_index); | ||
| 48 | int INIFile_GetNumberOfEntries (void *ini_data, wchar_t *section); | ||
| 49 | wchar_t *INIFile_GetEntryName (void *ini_data, wchar_t *section, int entry_index); | ||
| 50 | unsigned char INIFile_ReadEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char default_value); | ||
| 51 | long INIFile_ReadEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long default_value); | ||
| 52 | unsigned long INIFile_ReadEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long default_value); | ||
| 53 | double INIFile_ReadEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double default_value); | ||
| 54 | wchar_t *INIFile_ReadEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *default_value); | ||
| 55 | void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value); | ||
| 56 | void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value); | ||
| 57 | void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value); | ||
| 58 | void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value); | ||
| 59 | void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value); | ||
| 60 | void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry); | ||
| 61 | void INIFile_DeleteSection (void *ini_data, wchar_t *section); | ||
| 62 | void *INIFile_LoadINIFile (const wchar_t *filename); | ||
| 63 | unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data); | ||
| 64 | void INIFile_FreeINIFile (void *ini_data); | ||
| 65 | |||
| 66 | |||
| 67 | // internal variables | ||
| 136 | pmbaty | 68 | static wchar_t *INIFile_default_section = L"__default"; // MUST BE A READ-WRITE VARIABLE AND NOT A DEFINE | 
| 1 | pmbaty | 69 | |
| 70 | |||
| 71 | static dictionary_t *Dictionary_CreateDictionary (int size) | ||
| 72 | { | ||
| 73 |    // this function allocates a new dictionary object of given size and returns it. If you do | ||
| 74 |    // not know in advance (roughly) the number of entries in the dictionary, give size = 0. | ||
| 75 | |||
| 76 | dictionary_t *dictionary; | ||
| 77 | |||
| 78 |    // if no size was specified, allocate space for one page | ||
| 79 | if (size < DICTIONARY_PAGE_SIZE) | ||
| 80 | size = DICTIONARY_PAGE_SIZE; | ||
| 81 | |||
| 82 |    // allocate one instance of the dictionary and blank it out | ||
| 83 | dictionary = (dictionary_t *) SAFE_malloc (1, sizeof (dictionary_t), true); | ||
| 84 | dictionary->size = size; // dictionary can currently hold size entries | ||
| 85 | |||
| 86 |    // allocate space for the dictionary entries and zero all the crap out | ||
| 87 | dictionary->entries = (dictionary_entry_t *) SAFE_malloc (size, sizeof (dictionary_entry_t), true); | ||
| 88 | |||
| 89 | return (dictionary); // finished, return a pointer to the dictionary object | ||
| 90 | } | ||
| 91 | |||
| 92 | |||
| 93 | static void Dictionary_DestroyDictionary (dictionary_t *dictionary) | ||
| 94 | { | ||
| 95 |    // frees a dictionary object and all memory associated to it. | ||
| 96 | |||
| 136 | pmbaty | 97 | int entry_index; | 
| 98 | |||
| 1 | pmbaty | 99 | if (dictionary == NULL) | 
| 100 | return; // consistency check | ||
| 101 | |||
| 136 | pmbaty | 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 |    } | ||
| 107 | |||
| 1 | pmbaty | 108 | SAFE_free ((void **) &dictionary->entries); // free the entries array | 
| 109 | SAFE_free ((void **) &dictionary); // and finally, free the dictionary itself | ||
| 110 | |||
| 111 | return; // finished | ||
| 112 | } | ||
| 113 | |||
| 114 | |||
| 115 | static dictionary_entry_t *Dictionary_GetKey (dictionary_t *dictionary, wchar_t *key) | ||
| 116 | { | ||
| 117 |    // this function locates a key in a dictionary and returns a pointer to it, or a NULL | ||
| 118 |    // pointer if no such key can be found in dictionary. The returned pointer points to data | ||
| 119 |    // internal to the dictionary object, do not try to free or modify it. | ||
| 120 | |||
| 121 | unsigned long hash; | ||
| 122 | register int entry_index; | ||
| 123 | |||
| 124 | hash = Dictionary_ComputeHashValueForKey (key); // get the hash value for key | ||
| 125 | |||
| 126 |    // cycle through all entries in the dictionary | ||
| 127 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 128 |    { | ||
| 129 | if (dictionary->entries[entry_index].key[0] == 0) | ||
| 130 | continue; // skip empty slots | ||
| 131 | |||
| 132 |       // compare hash AND string, to avoid hash collisions | ||
| 133 | if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) | ||
| 134 | return (&dictionary->entries[entry_index]); // return a pointer to the key if we find it | ||
| 135 |    } | ||
| 136 | |||
| 137 | return (NULL); // else return a NULL pointer | ||
| 138 | } | ||
| 139 | |||
| 140 | |||
| 141 | static wchar_t *Dictionary_ReadKey (dictionary_t *dictionary, wchar_t *key, wchar_t *default_value) | ||
| 142 | { | ||
| 143 |    // this function locates a key in a dictionary and returns a pointer to its value, or the | ||
| 144 |    // passed 'def' pointer if no such key can be found in dictionary. The returned char pointer | ||
| 145 |    // points to data internal to the dictionary object, do not try to free or modify it. | ||
| 146 | |||
| 147 | dictionary_entry_t *element; | ||
| 148 | |||
| 149 | element = Dictionary_GetKey (dictionary, key); // query the dictionary for the key | ||
| 150 | if (element != NULL) | ||
| 151 | return (element->value); // if we found a valid key, return the value associed to it | ||
| 152 | |||
| 153 | return (default_value); // else return the default value | ||
| 154 | } | ||
| 155 | |||
| 156 | |||
| 157 | static void Dictionary_WriteKey (dictionary_t *dictionary, wchar_t *key, wchar_t *value) | ||
| 158 | { | ||
| 159 |    // sets a value in a dictionary. If the given key is found in the dictionary, the associated | ||
| 160 |    // value is replaced by the provided one. If the key cannot be found in the dictionary, it | ||
| 161 |    // is added to it. | ||
| 162 | |||
| 163 | unsigned long hash; | ||
| 164 | register int entry_index; | ||
| 136 | pmbaty | 165 | size_t bufsize; | 
| 1 | pmbaty | 166 | |
| 167 | hash = Dictionary_ComputeHashValueForKey (key); // compute hash for this key | ||
| 168 | |||
| 169 |    // for each entry in the dictionary... | ||
| 170 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 171 |    { | ||
| 172 | if (dictionary->entries[entry_index].key[0] == 0) | ||
| 173 | continue; // skip empty slots | ||
| 174 | |||
| 175 |       // does that key have the same hash value AND the same name ? | ||
| 176 | if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) | ||
| 177 |       { | ||
| 178 |          // we've found the right key: modify its value and return | ||
| 179 | |||
| 136 | pmbaty | 180 | if (value == NULL) | 
| 181 | value = L""; // if a null value was specified, use an empty string | ||
| 1 | pmbaty | 182 | |
| 136 | pmbaty | 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); | ||
| 185 | wcscpy_s (dictionary->entries[entry_index].value, bufsize, value); // copy the value string | ||
| 186 | |||
| 1 | pmbaty | 187 | return; // value has been modified: return | 
| 188 |       } | ||
| 189 |    } | ||
| 190 | |||
| 191 |    // here either the dictionary was empty, or we couldn't find a similar value: add a new one | ||
| 192 | |||
| 193 |    // cycle through all entries in the dictionary... | ||
| 194 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 195 | if (dictionary->entries[entry_index].key[0] == 0) | ||
| 196 | break; // and stop at the first empty one we find | ||
| 197 | |||
| 198 |    // no empty entry found, see if dictionary needs to grow or if there's still room left | ||
| 199 | if (entry_index == dictionary->size) | ||
| 200 |    { | ||
| 201 |       // mallocate some more space for the dictionary and zero all that crap out | ||
| 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 | ||
| 203 | dictionary->size += DICTIONARY_PAGE_SIZE; // increase dictionary size | ||
| 204 |    } | ||
| 205 | |||
| 206 |    // copy the new key | ||
| 136 | pmbaty | 207 | bufsize = wcslen (key) + 1; | 
| 208 | dictionary->entries[entry_index].key = (wchar_t *) SAFE_malloc (bufsize, sizeof (wchar_t), false); | ||
| 209 | wcscpy_s (dictionary->entries[entry_index].key, bufsize, key); // copy the key string... | ||
| 1 | pmbaty | 210 | dictionary->entries[entry_index].hash = hash; // ...and store the hash value | 
| 211 | |||
| 136 | pmbaty | 212 |    // copy the new value | 
| 213 | if (value == NULL) | ||
| 214 | value = L""; // if a null value was specified, use an empty string | ||
| 215 | bufsize = wcslen (value) + 1; | ||
| 216 | dictionary->entries[entry_index].value = (wchar_t *) SAFE_malloc (bufsize, sizeof (wchar_t), false); | ||
| 217 | wcscpy_s (dictionary->entries[entry_index].value, bufsize, value); // copy the value string | ||
| 1 | pmbaty | 218 | |
| 219 | dictionary->entry_count++; // there is one more entry in the dictionary now | ||
| 220 | return; | ||
| 221 | } | ||
| 222 | |||
| 223 | |||
| 224 | static void Dictionary_DeleteKey (dictionary_t *dictionary, wchar_t *key) | ||
| 225 | { | ||
| 226 |    // this function deletes a key in a dictionary. Nothing is done if the key cannot be found. | ||
| 227 | |||
| 228 | unsigned long hash; | ||
| 229 | register int entry_index; | ||
| 230 | |||
| 231 | hash = Dictionary_ComputeHashValueForKey (key); // get the hash value for key | ||
| 232 | |||
| 233 |    // cycle through all entries in the dictionary... | ||
| 234 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 235 |    { | ||
| 236 | if (dictionary->entries[entry_index].key[0] == 0) | ||
| 237 | continue; // skip empty slots | ||
| 238 | |||
| 239 |       // compare hash AND string, to avoid hash collisions | ||
| 240 | if ((hash == dictionary->entries[entry_index].hash) && (wcscmp (key, dictionary->entries[entry_index].key) == 0)) | ||
| 241 | break; // break as soon as we've found the key we want | ||
| 242 |    } | ||
| 243 | |||
| 244 | if (entry_index == dictionary->entry_count) | ||
| 245 | return; // if key was not found, just return | ||
| 246 | |||
| 136 | pmbaty | 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); | ||
| 1 | pmbaty | 250 | memset (&dictionary->entries[entry_index], 0, sizeof (dictionary_entry_t)); | 
| 251 | |||
| 252 | dictionary->entry_count--; // there is one entry less in the dictionary now | ||
| 253 | return; | ||
| 254 | } | ||
| 255 | |||
| 256 | |||
| 257 | static unsigned long Dictionary_ComputeHashValueForKey (wchar_t *key) | ||
| 258 | { | ||
| 259 |    // Compute the hash key for a string. This hash function has been taken from an article in | ||
| 260 |    // Dr Dobbs Journal. This is normally a collision-free function, distributing keys evenly. | ||
| 261 |    // The key is stored anyway in the struct so that collision can be avoided by comparing the | ||
| 262 |    // key itself in last resort. THIS FUNCTION IS TO BE USED INTERNALLY ONLY! | ||
| 263 | |||
| 264 | register unsigned long hash; | ||
| 265 | int length; | ||
| 266 | int char_index; | ||
| 267 | |||
| 268 | hash = 0; | ||
| 269 | length = (int) wcslen (key); | ||
| 270 | |||
| 271 |    // for each character of the string... | ||
| 272 | for (char_index = 0; char_index < length; char_index++) | ||
| 273 |    { | ||
| 274 | hash += (unsigned long) key[char_index]; // take it in account and compute the hash value | ||
| 275 | hash += (hash << 10); | ||
| 276 | hash ^= (hash >> 6); | ||
| 277 |    } | ||
| 278 | |||
| 279 | hash += (hash << 3); // finalize hashing (what the hell does it do, I have no clue) | ||
| 280 | hash ^= (hash >> 11); | ||
| 281 | hash += (hash << 15); | ||
| 282 | |||
| 283 | return (hash); // and return the hash value | ||
| 284 | } | ||
| 285 | |||
| 286 | |||
| 287 | void *INIFile_NewINIFile (void) | ||
| 288 | { | ||
| 289 |    // allocates a dictionary for a new INI file. Mostly a helper function. | ||
| 290 | |||
| 291 | return ((void *) Dictionary_CreateDictionary (0)); | ||
| 292 | } | ||
| 293 | |||
| 294 | |||
| 295 | int INIFile_GetNumberOfSections (void *ini_data) | ||
| 296 | { | ||
| 297 |    // this function returns the number of sections found in a dictionary. The test to recognize | ||
| 298 |    // sections is done on the string stored in the dictionary: a section name is given as | ||
| 299 |    // "section" whereas a key is stored as "section]key", thus the test looks for entries that | ||
| 300 |    // do NOT contain a ']'. This clearly fails in the case a section name contains a ']', | ||
| 301 |    // but this should simply be avoided. | ||
| 302 | |||
| 303 | int section_count; | ||
| 304 | int entry_index; | ||
| 305 | dictionary_t *dictionary; | ||
| 306 | |||
| 307 | if (ini_data == NULL) | ||
| 308 | return (0); // consistency check | ||
| 309 | |||
| 310 | dictionary = (dictionary_t *) ini_data; | ||
| 311 | |||
| 312 | section_count = 0; // no sections found yet | ||
| 313 | |||
| 314 |    // for each entry in the dictionary... | ||
| 315 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 316 |    { | ||
| 317 | if (dictionary->entries[entry_index].key[0] == 0) | ||
| 318 | continue; // skip empty slots | ||
| 319 | |||
| 320 | if (wcschr (dictionary->entries[entry_index].key, INI_SECTION_SEPARATOR) == NULL) | ||
| 321 | section_count++; // if this entry has NO section separator, then it's a section name | ||
| 322 |    } | ||
| 323 | |||
| 324 | return (section_count); // return the section count we found | ||
| 325 | } | ||
| 326 | |||
| 327 | |||
| 328 | wchar_t *INIFile_GetSectionName (void *ini_data, int section_index) | ||
| 329 | { | ||
| 330 |    // this function locates the n-th section in a dictionary and returns its name as a pointer | ||
| 331 |    // to a string allocated inside the dictionary. Do not free or modify the returned string!! | ||
| 332 |    // This function returns NULL in case of error. | ||
| 333 | |||
| 334 | int sections_found; | ||
| 335 | int entry_index; | ||
| 336 | dictionary_t *dictionary; | ||
| 337 | |||
| 338 | if ((ini_data == NULL) || (section_index < 0)) | ||
| 339 | return (NULL); // consistency check | ||
| 340 | |||
| 341 | dictionary = (dictionary_t *) ini_data; | ||
| 342 | |||
| 343 | sections_found = 0; // no sections found yet | ||
| 344 | |||
| 345 |    // for each entry in the dictionary... | ||
| 346 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 347 |    { | ||
| 348 | if (dictionary->entries[entry_index].key[0] == 0) | ||
| 349 | continue; // skip empty slots | ||
| 350 | |||
| 351 |       // is this entry a section name ? | ||
| 352 | if (wcschr (dictionary->entries[entry_index].key, INI_SECTION_SEPARATOR) == NULL) | ||
| 353 |       { | ||
| 354 |          // is it the section we want ? | ||
| 355 | if (sections_found == section_index) | ||
| 356 | return (dictionary->entries[entry_index].key); // then return its name | ||
| 357 | |||
| 358 | sections_found++; // we found one section more | ||
| 359 |       } | ||
| 360 |    } | ||
| 361 | |||
| 362 | return (INIFile_default_section); // section was not found, return the default section name | ||
| 363 | } | ||
| 364 | |||
| 365 | |||
| 366 | int INIFile_GetNumberOfEntries (void *ini_data, wchar_t *section) | ||
| 367 | { | ||
| 368 |    // this function returns the number of entries found in a dictionary within a given section. | ||
| 369 |    // The test to recognize section entries is done on the string stored in the dictionary: a | ||
| 370 |    // section name is given as "section" whereas a key is stored as "section]key", thus the test | ||
| 371 |    // looks for all entries that begin with "section]". | ||
| 372 | |||
| 373 | int entry_count; | ||
| 374 | int entry_index; | ||
| 375 | int sectionname_length; | ||
| 376 | dictionary_t *dictionary; | ||
| 377 | |||
| 378 | if (ini_data == NULL) | ||
| 379 | return (0); // consistency check | ||
| 380 | |||
| 381 | dictionary = (dictionary_t *) ini_data; | ||
| 382 | sectionname_length = wcslen (section); | ||
| 383 | |||
| 384 | entry_count = 0; // no entries found yet | ||
| 385 | |||
| 386 |    // for each entry in the dictionary... | ||
| 387 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 388 |    { | ||
| 389 | if (dictionary->entries[entry_index].key[0] == 0) | ||
| 390 | continue; // skip empty slots | ||
| 391 | |||
| 392 | if ((wcsncmp (dictionary->entries[entry_index].key, section, sectionname_length) == 0) | ||
| 393 | && (dictionary->entries[entry_index].key[sectionname_length] == INI_SECTION_SEPARATOR)) | ||
| 394 | entry_count++; // if this entry has section name + separator, then it's one of those we want | ||
| 395 |    } | ||
| 396 | |||
| 397 | return (entry_count); // return the number of entries we found in the given section | ||
| 398 | } | ||
| 399 | |||
| 400 | |||
| 401 | wchar_t *INIFile_GetEntryName (void *ini_data, wchar_t *section, int entry_index) | ||
| 402 | { | ||
| 403 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | ||
| 404 |    // "section]key". If the key cannot be found, NULL is returned. The returned char pointer | ||
| 405 |    // points to a string allocated in the dictionary, do not free or modify it. | ||
| 406 | |||
| 407 | int i; | ||
| 408 | int length; | ||
| 409 | int found_keys; | ||
| 410 | dictionary_t *dictionary; | ||
| 411 | dictionary_entry_t *entry; | ||
| 412 | |||
| 413 | if (ini_data == NULL) | ||
| 414 | return (NULL); // consistency check | ||
| 415 | |||
| 416 | if (section == NULL) | ||
| 417 | section = INIFile_default_section; // if no section was provided, use empty section name | ||
| 418 | |||
| 419 | dictionary = (dictionary_t *) ini_data; // quick access to dictionary | ||
| 420 | |||
| 136 | pmbaty | 421 | length = wcslen (section); | 
| 1 | pmbaty | 422 | |
| 423 |    // cycle through all remaining entries | ||
| 424 | found_keys = 0; | ||
| 425 | for (i = 0; i < dictionary->entry_count; i++) | ||
| 426 |    { | ||
| 427 | entry = &dictionary->entries[i]; // quick access to each entry | ||
| 428 | |||
| 136 | pmbaty | 429 | if ((wcsncmp (entry->key, section, length) != 0) || (entry->key[length] != INI_SECTION_SEPARATOR)) | 
| 1 | pmbaty | 430 | continue; // if this key doesn't belong to the wanted section, skip it | 
| 431 | |||
| 432 |       // this key belongs to the wanted section. Is it the key we want ? | ||
| 433 | if (found_keys == entry_index) | ||
| 136 | pmbaty | 434 | return (&entry->key[length + 1]); // if so, return its name | 
| 1 | pmbaty | 435 | |
| 436 | found_keys++; // it wasn't the key we want, so increase counter and proceed to the next one | ||
| 437 |    } | ||
| 438 | |||
| 439 | return (NULL); // if not found, return NULL | ||
| 440 | } | ||
| 441 | |||
| 442 | |||
| 443 | unsigned char INIFile_ReadEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char default_value) | ||
| 444 | { | ||
| 445 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | ||
| 446 |    // "section]key". If the key cannot be found, the notfound value is returned. A true boolean | ||
| 447 |    // is found if a string starting with either 'y', 'Y', 't', 'T' or '1' is matched. A false | ||
| 448 |    // boolean is found if a string starting with 'n', 'N', 'f', 'F' or '0' is matched. | ||
| 449 | |||
| 450 | wchar_t *value; | ||
| 451 | |||
| 452 | if (ini_data == NULL) | ||
| 453 | return (default_value); // consistency check | ||
| 454 | |||
| 455 |    // get the value as a string first | ||
| 456 | value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); | ||
| 457 | |||
| 458 | if (value == INI_INVALID_KEY) | ||
| 459 | return (default_value); // if the entry could not be read, return the default value | ||
| 460 | |||
| 461 |    // decide of the boolean's value by looking at the first character | ||
| 462 | if ((toupper (value[0]) == 'T') || (toupper (value[0]) == 'Y') || (value[0] == '1')) | ||
| 463 | return (1); // boolean value is TRUE | ||
| 464 | else if ((toupper (value[0]) == 'F') || (toupper (value[0]) == 'N') || (value[0] == '0')) | ||
| 465 | return (0); // boolean value is FALSE | ||
| 466 | |||
| 467 | return (default_value); // boolean value is undefined, return default value | ||
| 468 | } | ||
| 469 | |||
| 470 | |||
| 471 | long INIFile_ReadEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long default_value) | ||
| 472 | { | ||
| 473 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | ||
| 474 |    // "section]key". If the key cannot be found, the notfound value is returned. | ||
| 475 | |||
| 476 | wchar_t *value; | ||
| 477 | |||
| 478 | if (ini_data == NULL) | ||
| 479 | return (default_value); // consistency check | ||
| 480 | |||
| 481 |    // get the value as a string first | ||
| 482 | value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); | ||
| 483 | |||
| 484 | if (value == INI_INVALID_KEY) | ||
| 485 | return (default_value); // if the entry could not be read, return the default value | ||
| 486 | |||
| 487 | return (_wtol (value)); // else convert the value to a long integer and return it | ||
| 488 | } | ||
| 489 | |||
| 490 | |||
| 491 | unsigned long INIFile_ReadEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long default_value) | ||
| 492 | { | ||
| 493 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | ||
| 494 |    // "section]key". If the key cannot be found, the notfound value is returned. | ||
| 495 | |||
| 496 | wchar_t *value; | ||
| 497 | |||
| 498 | if (ini_data == NULL) | ||
| 499 | return (default_value); // consistency check | ||
| 500 | |||
| 501 |    // get the value as a string first | ||
| 502 | value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); | ||
| 503 | |||
| 504 | if (value == INI_INVALID_KEY) | ||
| 505 | return (default_value); // if the entry could not be read, return the default value | ||
| 506 | |||
| 507 | return (wcstoul (value, NULL, 10)); // else convert the value to a long integer and return it | ||
| 508 | } | ||
| 509 | |||
| 510 | |||
| 511 | double INIFile_ReadEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double default_value) | ||
| 512 | { | ||
| 513 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | ||
| 514 |    // "section]key". If the key cannot be found, the notfound value is returned. | ||
| 515 | |||
| 516 | wchar_t *value; | ||
| 517 | |||
| 518 | if (ini_data == NULL) | ||
| 519 | return (default_value); // consistency check | ||
| 520 | |||
| 521 |    // get the value as a string first | ||
| 522 | value = INIFile_ReadEntryAsString ((dictionary_t *) ini_data, section, entry, INI_INVALID_KEY); | ||
| 523 | |||
| 524 | if (value == INI_INVALID_KEY) | ||
| 525 | return (default_value); // if the entry could not be read, return the default value | ||
| 526 | |||
| 527 | return (_wtof (value)); // else convert the value to a double precision number and return it | ||
| 528 | } | ||
| 529 | |||
| 530 | |||
| 531 | wchar_t *INIFile_ReadEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *default_value) | ||
| 532 | { | ||
| 533 |    // this function queries a dictionary for a key. A key as read from an ini file is given as | ||
| 534 |    // "section]key". If the key cannot be found, the pointer passed as 'def' is returned. The | ||
| 535 |    // returned char pointer points to a string allocated in the dictionary, do not free or | ||
| 536 |    // modify it. | ||
| 537 | |||
| 136 | pmbaty | 538 | size_t length; | 
| 539 | size_t i; | ||
| 540 | size_t keysize; | ||
| 541 | wchar_t *key; | ||
| 1 | pmbaty | 542 | wchar_t *value; | 
| 543 | |||
| 544 | if (ini_data == NULL) | ||
| 545 | return (default_value); // consistency check | ||
| 546 | |||
| 547 | if (section == NULL) | ||
| 548 | section = INIFile_default_section; // if no section was provided, use empty section name | ||
| 549 | |||
| 136 | pmbaty | 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); | ||
| 553 | |||
| 1 | pmbaty | 554 |    // if we were given an entry, build a key as section]entry | 
| 555 | if (entry != NULL) | ||
| 556 |    { | ||
| 136 | pmbaty | 557 | swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key | 
| 1 | pmbaty | 558 | |
| 136 | pmbaty | 559 | i = wcslen (key); // get the key string length | 
| 560 | if (i < keysize) | ||
| 1 | pmbaty | 561 | length = i; | 
| 562 |       else | ||
| 136 | pmbaty | 563 | length = keysize - 1; // clamp it to a max value | 
| 1 | pmbaty | 564 | |
| 565 |       // for each character in the string after the section separator... | ||
| 136 | pmbaty | 566 | for (i = wcslen (section) + 1; i < length; i++) | 
| 567 | key[i] = towlower (key[i]); // convert it to lowercase | ||
| 568 | key[i] = 0; // terminate the string | ||
| 1 | pmbaty | 569 |    } | 
| 570 | |||
| 571 |    // else it must be a section name | ||
| 572 |    else | ||
| 136 | pmbaty | 573 | wcscpy_s (key, keysize, section); // copy the name into the key | 
| 1 | pmbaty | 574 | |
| 136 | pmbaty | 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 | ||
| 1 | pmbaty | 577 | return (value); // ...and return the value | 
| 578 | } | ||
| 579 | |||
| 580 | |||
| 581 | void INIFile_WriteEntryAsBool (void *ini_data, wchar_t *section, wchar_t *entry, unsigned char value) | ||
| 582 | { | ||
| 583 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | ||
| 584 |    // modified to contain the provided value. If it cannot be found, it is created. | ||
| 585 | |||
| 586 |    // according the boolean's value, write the equivalent string | ||
| 587 | if (value) | ||
| 588 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, L"true"); // write the new entry | ||
| 589 |    else | ||
| 590 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, L"false"); // write the new entry | ||
| 591 | |||
| 592 | return; // finished | ||
| 593 | } | ||
| 594 | |||
| 595 | |||
| 596 | void INIFile_WriteEntryAsLong (void *ini_data, wchar_t *section, wchar_t *entry, long value) | ||
| 597 | { | ||
| 598 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | ||
| 599 |    // modified to contain the provided value. If it cannot be found, it is created. | ||
| 600 | |||
| 136 | pmbaty | 601 | wchar_t *number_as_string; | 
| 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 | |||
| 1 | pmbaty | 606 |    // build the long integer value equivalent string (use printf facility) | 
| 136 | pmbaty | 607 | swprintf_s (number_as_string, 32, L"%ld", value); | 
| 608 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | ||
| 1 | pmbaty | 609 | |
| 136 | pmbaty | 610 | SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it | 
| 1 | pmbaty | 611 | return; // finished | 
| 612 | } | ||
| 613 | |||
| 614 | |||
| 615 | void INIFile_WriteEntryAsUnsignedLong (void *ini_data, wchar_t *section, wchar_t *entry, unsigned long value) | ||
| 616 | { | ||
| 617 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | ||
| 618 |    // modified to contain the provided value. If it cannot be found, it is created. | ||
| 619 | |||
| 136 | pmbaty | 620 | wchar_t *number_as_string; | 
| 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 | |||
| 1 | pmbaty | 625 |    // build the long integer value equivalent string (use printf facility) | 
| 136 | pmbaty | 626 | swprintf_s (number_as_string, 32, L"%lu", value); | 
| 627 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | ||
| 1 | pmbaty | 628 | |
| 136 | pmbaty | 629 | SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it | 
| 1 | pmbaty | 630 | return; // finished | 
| 631 | } | ||
| 632 | |||
| 633 | |||
| 634 | void INIFile_WriteEntryAsDouble (void *ini_data, wchar_t *section, wchar_t *entry, double value) | ||
| 635 | { | ||
| 636 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | ||
| 637 |    // modified to contain the provided value. If it cannot be found, it is created. | ||
| 638 | |||
| 136 | pmbaty | 639 | wchar_t *number_as_string; | 
| 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 | |||
| 1 | pmbaty | 644 |    // build the long integer value equivalent string (use printf facility) | 
| 136 | pmbaty | 645 | swprintf_s (number_as_string, 64, L"%g", value); | 
| 646 | INIFile_WriteEntryAsString ((dictionary_t *) ini_data, section, entry, number_as_string); // write the new entry | ||
| 1 | pmbaty | 647 | |
| 136 | pmbaty | 648 | SAFE_free ((void **) &number_as_string); // free the number's string representation, we no longer need it | 
| 1 | pmbaty | 649 | return; // finished | 
| 650 | } | ||
| 651 | |||
| 652 | |||
| 653 | void INIFile_WriteEntryAsString (void *ini_data, wchar_t *section, wchar_t *entry, wchar_t *value) | ||
| 654 | { | ||
| 655 |    // sets an entry in a dictionary. If the given entry can be found in the dictionary, it is | ||
| 656 |    // modified to contain the provided value. If it cannot be found, it is created. | ||
| 657 | |||
| 136 | pmbaty | 658 | size_t length; | 
| 659 | size_t i; | ||
| 660 | size_t keysize; | ||
| 661 | wchar_t *key; | ||
| 1 | pmbaty | 662 | |
| 663 | if (ini_data == NULL) | ||
| 664 | return; // consistency check | ||
| 665 | |||
| 666 | if (section == NULL) | ||
| 667 | section = INIFile_default_section; // if no section was provided, use empty section name | ||
| 668 | |||
| 136 | pmbaty | 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); | ||
| 672 | |||
| 1 | pmbaty | 673 |    // if we were given an entry, build a key as section#entry | 
| 674 | if (entry != NULL) | ||
| 675 |    { | ||
| 676 | Dictionary_WriteKey ((dictionary_t *) ini_data, section, NULL); // create the section if it doesn't exist | ||
| 677 | |||
| 136 | pmbaty | 678 | swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key | 
| 1 | pmbaty | 679 | |
| 136 | pmbaty | 680 | i = wcslen (key); // get the key string length | 
| 681 | if (i < keysize) | ||
| 1 | pmbaty | 682 | length = i; | 
| 683 |       else | ||
| 136 | pmbaty | 684 | length = keysize - 1; // clamp it to a max value | 
| 1 | pmbaty | 685 | |
| 686 |       // for each character in the string after the section separator... | ||
| 136 | pmbaty | 687 | for (i = wcslen (section) + 1; i < length; i++) | 
| 688 | key[i] = towlower (key[i]); // convert it to lowercase | ||
| 689 | key[i] = 0; // terminate the string | ||
| 1 | pmbaty | 690 |    } | 
| 691 | |||
| 692 |    // else it must be a section name | ||
| 693 |    else | ||
| 136 | pmbaty | 694 | wcscpy_s (key, keysize, section); // copy the name into the key | 
| 1 | pmbaty | 695 | |
| 136 | pmbaty | 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 | ||
| 1 | pmbaty | 698 | return; // finished | 
| 699 | } | ||
| 700 | |||
| 701 | |||
| 702 | void INIFile_DeleteEntry (void *ini_data, wchar_t *section, wchar_t *entry) | ||
| 703 | { | ||
| 704 |    // deletes an entry in the dictionary | ||
| 705 | |||
| 136 | pmbaty | 706 | size_t length; | 
| 707 | size_t i; | ||
| 708 | size_t keysize; | ||
| 709 | wchar_t *key; | ||
| 1 | pmbaty | 710 | |
| 711 | if (ini_data == NULL) | ||
| 712 | return; // consistency check | ||
| 713 | |||
| 714 | if (section == NULL) | ||
| 715 | section = INIFile_default_section; // if no section was provided, use empty section name | ||
| 716 | |||
| 136 | pmbaty | 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); | ||
| 720 | |||
| 1 | pmbaty | 721 |    // if we were given an entry, build a key as section#entry | 
| 722 | if (entry != NULL) | ||
| 723 |    { | ||
| 136 | pmbaty | 724 | swprintf_s (key, keysize, L"%s%c%s", section, INI_SECTION_SEPARATOR, entry); // compose the key | 
| 1 | pmbaty | 725 | |
| 136 | pmbaty | 726 | i = wcslen (key); // get the key string length | 
| 727 | if (i < keysize) | ||
| 1 | pmbaty | 728 | length = i; | 
| 729 |       else | ||
| 136 | pmbaty | 730 | length = keysize - 1; // clamp it to a max value | 
| 1 | pmbaty | 731 | |
| 732 |       // for each character in the string after the section separator... | ||
| 136 | pmbaty | 733 | for (i = wcslen (section) + 1; i < length; i++) | 
| 734 | key[i] = towlower (key[i]); // convert it to lowercase | ||
| 735 | key[i] = 0; // terminate the string | ||
| 1 | pmbaty | 736 |    } | 
| 737 | |||
| 738 |    // else it must be a section name | ||
| 739 |    else | ||
| 136 | pmbaty | 740 | wcscpy_s (key, keysize, section); // copy the name into the key | 
| 1 | pmbaty | 741 | |
| 136 | pmbaty | 742 | Dictionary_DeleteKey ((dictionary_t *) ini_data, key); | 
| 743 | SAFE_free ((void **) &key); // free the key name, we no longer need it | ||
| 1 | pmbaty | 744 | return; | 
| 745 | } | ||
| 746 | |||
| 747 | |||
| 748 | void INIFile_DeleteSection (void *ini_data, wchar_t *section) | ||
| 749 | { | ||
| 750 |    // deletes a whole INI section in the dictionary | ||
| 751 | |||
| 752 | int length; | ||
| 753 | int i; | ||
| 754 | dictionary_t *dictionary; | ||
| 136 | pmbaty | 755 | dictionary_entry_t *entry; | 
| 1 | pmbaty | 756 | |
| 757 | if (ini_data == NULL) | ||
| 758 | return; // consistency check | ||
| 759 | |||
| 760 | if (section == NULL) | ||
| 761 | section = INIFile_default_section; // if no section was provided, use empty section name | ||
| 762 | |||
| 763 | dictionary = (dictionary_t *) ini_data; | ||
| 764 | |||
| 136 | pmbaty | 765 | length = (int) wcslen (section); // get the section string length | 
| 1 | pmbaty | 766 | |
| 767 |    // for each entry in the dictionary... | ||
| 768 | for (i = 0; i < dictionary->entry_count; i++) | ||
| 769 |    { | ||
| 136 | pmbaty | 770 | entry = &dictionary->entries[i]; // quick access to each entry | 
| 771 | |||
| 772 | if (entry->key[0] == 0) | ||
| 1 | pmbaty | 773 | continue; // skip empty slots | 
| 774 | |||
| 775 |       // does this entry belong to the section we want ? | ||
| 136 | pmbaty | 776 | if ((wcsncmp (entry->key, section, length) == 0) && (entry->key[length] == INI_SECTION_SEPARATOR)) | 
| 777 | Dictionary_DeleteKey (dictionary, entry->key); // yes, delete it | ||
| 1 | pmbaty | 778 |    } | 
| 779 | |||
| 780 | Dictionary_DeleteKey (dictionary, section); // and finally delete the section name itself | ||
| 781 | return; | ||
| 782 | } | ||
| 783 | |||
| 784 | |||
| 785 | void *INIFile_LoadINIFile (const wchar_t *filename) | ||
| 786 | { | ||
| 787 |    // this is the parser for ini files. This function is called, providing the name of the file | ||
| 788 |    // to be read. It returns a dictionary object that should not be accessed directly, but | ||
| 789 |    // through accessor functions instead. | ||
| 790 | |||
| 136 | pmbaty | 791 | wchar_t *line_buffer; | 
| 1 | pmbaty | 792 | FILE *fp; | 
| 793 | int fieldstart; | ||
| 794 | int fieldstop; | ||
| 795 | int length; | ||
| 796 | int i; | ||
| 797 | dictionary_t *dictionary; | ||
| 136 | pmbaty | 798 | wchar_t *current_section; | 
| 799 | wchar_t *current_entry; | ||
| 800 | wchar_t *current_value; | ||
| 1 | pmbaty | 801 | |
| 802 |    // try to open the INI file in ASCII read-only mode | ||
| 803 | fp = _wfsopen (filename, L"r, ccs=UNICODE", _SH_DENYNO); | ||
| 804 | if (fp == NULL) | ||
| 805 | return (NULL); // cancel if file not found | ||
| 806 | |||
| 807 | dictionary = Dictionary_CreateDictionary (0); | ||
| 808 | |||
| 136 | pmbaty | 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); | ||
| 813 | |||
| 1 | pmbaty | 814 |    // set the default section for orphaned entries and add it to the dictionary | 
| 136 | pmbaty | 815 | wcscpy_s (current_section, STRING_MAXSIZE, INIFile_default_section); | 
| 816 | INIFile_WriteEntryAsString (dictionary, current_section, NULL, NULL); | ||
| 1 | pmbaty | 817 | |
| 136 | pmbaty | 818 |    // allocate some space for a line buffer | 
| 819 | line_buffer = (wchar_t *) SAFE_malloc (STRING_MAXSIZE + STRING_MAXSIZE, sizeof (wchar_t), false); | ||
| 820 | |||
| 1 | pmbaty | 821 |    // read line per line... | 
| 136 | pmbaty | 822 | while (fgetws (line_buffer, STRING_MAXSIZE + STRING_MAXSIZE, fp) != NULL) | 
| 1 | pmbaty | 823 |    { | 
| 824 | length = (int) wcslen (line_buffer); // get line length | ||
| 825 | |||
| 826 | while ((length > 0) && ((line_buffer[length - 1] == '\n') || (line_buffer[length - 1] == '\r'))) | ||
| 827 | length--; // discard trailing newlines | ||
| 828 | |||
| 829 | fieldstart = 0; // let's now strip leading blanks | ||
| 830 | while ((fieldstart < length) && iswspace (line_buffer[fieldstart])) | ||
| 831 | fieldstart++; // ignore any tabs or spaces, going forward from the start | ||
| 832 | |||
| 833 | fieldstop = length - 1; // let's now strip trailing blanks | ||
| 834 | while ((fieldstop >= 0) && iswspace (line_buffer[fieldstop])) | ||
| 835 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | ||
| 836 | |||
| 837 | for (i = fieldstart; i <= fieldstop; i++) | ||
| 838 | line_buffer[i - fieldstart] = line_buffer[i]; // recopy line buffer without the spaces | ||
| 839 | line_buffer[i - fieldstart] = 0; // and terminate the string | ||
| 840 | |||
| 841 | if ((line_buffer[0] == ';') || (line_buffer[0] == '#') || (line_buffer[0] == 0)) | ||
| 842 | continue; // skip comment lines | ||
| 843 | |||
| 844 |       // is it a valid section name ? | ||
| 136 | pmbaty | 845 | if (swscanf_s (line_buffer, L"[%[^]]", current_section, STRING_MAXSIZE) == 1) | 
| 1 | pmbaty | 846 |       { | 
| 136 | pmbaty | 847 | length = (int) wcslen (current_section); // get the section string length | 
| 1 | pmbaty | 848 | |
| 849 | fieldstart = 0; // let's now strip leading blanks | ||
| 136 | pmbaty | 850 | while ((fieldstart < length) && iswspace (current_section[fieldstart])) | 
| 1 | pmbaty | 851 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 852 | |||
| 853 | fieldstop = length - 1; // let's now strip trailing blanks | ||
| 136 | pmbaty | 854 | while ((fieldstop >= 0) && iswspace (current_section[fieldstop])) | 
| 1 | pmbaty | 855 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 856 | |||
| 857 | for (i = fieldstart; i <= fieldstop; i++) | ||
| 136 | pmbaty | 858 | current_section[i - fieldstart] = current_section[i]; // recopy section name w/out spaces | 
| 859 | current_section[i - fieldstart] = 0; // and terminate the string | ||
| 1 | pmbaty | 860 | |
| 136 | pmbaty | 861 | INIFile_WriteEntryAsString (dictionary, current_section, NULL, NULL); // add to dictionary | 
| 1 | pmbaty | 862 |       } | 
| 863 | |||
| 161 | pmbaty | 864 |       // else is it a valid entry/value pair whose entry is enclosed between quotes? | 
| 865 | else if ((swscanf_s (line_buffer, L"\"%[^\"]\" = %[^;#]", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | ||
| 866 | || (swscanf_s (line_buffer, L"'%[^']' = %[^;#]", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2)) | ||
| 1 | pmbaty | 867 |       { | 
| 161 | pmbaty | 868 |          // when entry is enclosed between quotes, DO NOT strip the blanks | 
| 869 | |||
| 870 | length = (int) wcslen (current_value); // get the value string length | ||
| 871 | |||
| 872 | fieldstart = 0; // let's now strip leading blanks | ||
| 873 | while ((fieldstart < length) && iswspace (current_value[fieldstart])) | ||
| 874 | fieldstart++; // ignore any tabs or spaces, going forward from the start | ||
| 875 | |||
| 876 | fieldstop = length - 1; // let's now strip trailing blanks | ||
| 877 | while ((fieldstop >= 0) && iswspace (current_value[fieldstop])) | ||
| 878 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | ||
| 879 | |||
| 880 | for (i = fieldstart; i <= fieldstop; i++) | ||
| 881 | current_value[i - fieldstart] = current_value[i]; // recopy entry name w/out spaces | ||
| 882 | current_value[i - fieldstart] = 0; // and terminate the string | ||
| 883 | |||
| 884 |          // sscanf cannot handle "" or '' as empty value, this is done here | ||
| 885 | if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) | ||
| 886 | current_value[0] = 0; // empty string | ||
| 887 | |||
| 888 | INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary | ||
| 889 |       } | ||
| 890 | |||
| 891 |       // else is it a valid entry/value pair whose entry AND value are enclosed between quotes? | ||
| 892 | else if ((swscanf_s (line_buffer, L"\"%[^\"]\" = \"%[^\"]\"", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | ||
| 893 | || (swscanf_s (line_buffer, L"'%[^']' = \"%[^\"]\"", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | ||
| 894 | || (swscanf_s (line_buffer, L"\"%[^\"]\" = '%[^']'", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | ||
| 895 | || (swscanf_s (line_buffer, L"'%[^']' = '%[^']'", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2)) | ||
| 896 |       { | ||
| 897 |          // when entry is enclosed between quotes, DO NOT strip the blanks | ||
| 898 | |||
| 899 |          // when value is enclosed between quotes, DO NOT strip the blanks | ||
| 900 | |||
| 901 |          // sscanf cannot handle "" or '' as empty value, this is done here | ||
| 902 | if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) | ||
| 903 | current_value[0] = 0; // empty string | ||
| 904 | |||
| 905 | INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary | ||
| 906 |       } | ||
| 907 | |||
| 908 |       // else is it a valid entry/value pair whose value is enclosed between quotes? | ||
| 909 | else if ((swscanf_s (line_buffer, L"%[^=] = \"%[^\"]\"", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | ||
| 910 | || (swscanf_s (line_buffer, L"%[^=] = '%[^']'", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2)) | ||
| 911 |       { | ||
| 136 | pmbaty | 912 | length = (int) wcslen (current_entry); // get the entry string length | 
| 1 | pmbaty | 913 | |
| 914 | fieldstart = 0; // let's now strip leading blanks | ||
| 136 | pmbaty | 915 | while ((fieldstart < length) && iswspace (current_entry[fieldstart])) | 
| 1 | pmbaty | 916 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 917 | |||
| 918 | fieldstop = length - 1; // let's now strip trailing blanks | ||
| 136 | pmbaty | 919 | while ((fieldstop >= 0) && iswspace (current_entry[fieldstop])) | 
| 1 | pmbaty | 920 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 921 | |||
| 922 | for (i = fieldstart; i <= fieldstop; i++) | ||
| 136 | pmbaty | 923 | current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces | 
| 924 | current_entry[i - fieldstart] = 0; // and terminate the string | ||
| 1 | pmbaty | 925 | |
| 926 |          // when value is enclosed between quotes, DO NOT strip the blanks | ||
| 927 | |||
| 928 |          // sscanf cannot handle "" or '' as empty value, this is done here | ||
| 136 | pmbaty | 929 | if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) | 
| 930 | current_value[0] = 0; // empty string | ||
| 1 | pmbaty | 931 | |
| 136 | pmbaty | 932 | INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary | 
| 1 | pmbaty | 933 |       } | 
| 934 | |||
| 935 |       // else is it a valid entry/value pair without quotes ? | ||
| 161 | pmbaty | 936 | else if (swscanf_s (line_buffer, L"%[^=] = %[^;#]", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2) | 
| 1 | pmbaty | 937 |       { | 
| 136 | pmbaty | 938 | length = (int) wcslen (current_entry); // get the entry string length | 
| 1 | pmbaty | 939 | |
| 940 | fieldstart = 0; // let's now strip leading blanks | ||
| 136 | pmbaty | 941 | while ((fieldstart < length) && iswspace (current_entry[fieldstart])) | 
| 1 | pmbaty | 942 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 943 | |||
| 944 | fieldstop = length - 1; // let's now strip trailing blanks | ||
| 136 | pmbaty | 945 | while ((fieldstop >= 0) && iswspace (current_entry[fieldstop])) | 
| 1 | pmbaty | 946 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 947 | |||
| 948 | for (i = fieldstart; i <= fieldstop; i++) | ||
| 136 | pmbaty | 949 | current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces | 
| 950 | current_entry[i - fieldstart] = 0; // and terminate the string | ||
| 1 | pmbaty | 951 | |
| 136 | pmbaty | 952 | length = (int) wcslen (current_value); // get the value string length | 
| 1 | pmbaty | 953 | |
| 954 | fieldstart = 0; // let's now strip leading blanks | ||
| 136 | pmbaty | 955 | while ((fieldstart < length) && iswspace (current_value[fieldstart])) | 
| 1 | pmbaty | 956 | fieldstart++; // ignore any tabs or spaces, going forward from the start | 
| 957 | |||
| 958 | fieldstop = length - 1; // let's now strip trailing blanks | ||
| 136 | pmbaty | 959 | while ((fieldstop >= 0) && iswspace (current_value[fieldstop])) | 
| 1 | pmbaty | 960 | fieldstop--; // ignore any tabs or spaces, going backwards from the end | 
| 961 | |||
| 962 | for (i = fieldstart; i <= fieldstop; i++) | ||
| 136 | pmbaty | 963 | current_value[i - fieldstart] = current_value[i]; // recopy entry name w/out spaces | 
| 964 | current_value[i - fieldstart] = 0; // and terminate the string | ||
| 1 | pmbaty | 965 | |
| 966 |          // sscanf cannot handle "" or '' as empty value, this is done here | ||
| 136 | pmbaty | 967 | if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0)) | 
| 968 | current_value[0] = 0; // empty string | ||
| 1 | pmbaty | 969 | |
| 136 | pmbaty | 970 | INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary | 
| 1 | pmbaty | 971 |       } | 
| 972 |    } | ||
| 973 | |||
| 974 | fclose (fp); // finished, close the file | ||
| 136 | pmbaty | 975 | SAFE_free ((void **) &line_buffer); // free the line buffer we allocated at the beginning of the function | 
| 976 | SAFE_free ((void **) ¤t_value); // free the current value buffer we allocated at the beginning of the function | ||
| 977 | SAFE_free ((void **) ¤t_entry); // free the current entry buffer we allocated at the beginning of the function | ||
| 978 | SAFE_free ((void **) ¤t_section); // free the current section buffer we allocated at the beginning of the function | ||
| 1 | pmbaty | 979 | |
| 136 | pmbaty | 980 | return ((void *) dictionary); // and return a pointer to it | 
| 1 | pmbaty | 981 | } | 
| 982 | |||
| 983 | |||
| 984 | unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data) | ||
| 985 | { | ||
| 986 |    // this function dumps a given dictionary into a loadable ini file. | ||
| 987 | |||
| 988 | FILE *fp; | ||
| 989 | int section_index; | ||
| 990 | wchar_t *section_name; | ||
| 991 | int section_count; | ||
| 992 | int entry_index; | ||
| 993 | int length; | ||
| 994 | dictionary_t *dictionary; | ||
| 995 | dictionary_entry_t *entry; | ||
| 996 | |||
| 997 |    // try to open the INI file in ASCII write mode | ||
| 998 | fp = _wfsopen (filename, L"w, ccs=UNICODE", _SH_DENYNO); | ||
| 999 | if (fp == NULL) | ||
| 1000 | return (0); // cancel if unable to open file | ||
| 1001 | |||
| 1002 |    // get a hand on the INI data dictionary | ||
| 1003 | dictionary = (dictionary_t *) ini_data; | ||
| 1004 | |||
| 1005 |    // keep only the file name for the comment | ||
| 1006 | if (wcsrchr (filename, '/') != NULL) | ||
| 1007 | filename = wcsrchr (filename, '/') + 1; | ||
| 1008 | else if (wcsrchr (filename, '\\') != NULL) | ||
| 1009 | filename = wcsrchr (filename, '\\') + 1; | ||
| 1010 | |||
| 1011 |    // print the INI file name as a comment | ||
| 1012 | fwprintf (fp, L"# %s\n", filename); | ||
| 1013 | |||
| 1014 |    // get the number of sections there are in this INI dictionary | ||
| 1015 | section_count = INIFile_GetNumberOfSections (dictionary); | ||
| 1016 | |||
| 1017 |    // for each section... | ||
| 1018 | for (section_index = 0; section_index < section_count; section_index++) | ||
| 1019 |    { | ||
| 1020 | section_name = INIFile_GetSectionName (dictionary, section_index); // read section name | ||
| 1021 | |||
| 1022 |       // is it the default section ? | ||
| 1023 | if (wcscmp (section_name, INIFile_default_section) == 0) | ||
| 1024 | fwprintf (fp, L"\n"); // don't put the default section's name in the INI file | ||
| 1025 |       else | ||
| 1026 | fwprintf (fp, L"\n[%s]\n", section_name); // dump all other sections into the INI file | ||
| 1027 | |||
| 136 | pmbaty | 1028 | length = (int) wcslen (section_name); | 
| 1 | pmbaty | 1029 | |
| 1030 |       // then for each entry in the dictionary... | ||
| 1031 | for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++) | ||
| 1032 |       { | ||
| 1033 | entry = &dictionary->entries[entry_index]; // quick access to entry | ||
| 1034 | |||
| 1035 | if (entry->key[0] == 0) | ||
| 1036 | continue; // skip empty slots | ||
| 1037 | |||
| 1038 |          // does this key belong to the section we want ? if so, dump it | ||
| 136 | pmbaty | 1039 | if ((wcsncmp (entry->key, section_name, length) == 0) && (entry->key[length] == INI_SECTION_SEPARATOR)) | 
| 1 | pmbaty | 1040 |          { | 
| 192 | pmbaty | 1041 |             // if value starts or ends with a space, enclose it between quotes, else don't | 
| 1 | pmbaty | 1042 | if ((entry->value[0] != 0) | 
| 1043 | && (iswspace (entry->value[0]) || iswspace (entry->value[wcslen (entry->value) - 1]))) | ||
| 136 | pmbaty | 1044 | fwprintf (fp, L"%s = \"%s\"\n", &entry->key[length + 1], entry->value); | 
| 1 | pmbaty | 1045 |             else | 
| 136 | pmbaty | 1046 | fwprintf (fp, L"%s = %s\n", &entry->key[length + 1], entry->value); | 
| 1 | pmbaty | 1047 |          } | 
| 1048 |       } | ||
| 1049 |    } | ||
| 1050 | |||
| 1051 | fclose (fp); // finished, close the file | ||
| 1052 | |||
| 1053 | return (1); // and return TRUE as the save occured successfully | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | |||
| 1057 | void INIFile_FreeINIFile (void *ini_data) | ||
| 1058 | { | ||
| 1059 |    // this function frees the dictionary object used to store an INI file data | ||
| 1060 | |||
| 1061 | if (ini_data == NULL) | ||
| 1062 | return; // consistency check | ||
| 1063 | |||
| 1064 | Dictionary_DestroyDictionary ((dictionary_t *) ini_data); // just destroy the dictionary | ||
| 1065 | return; // finished | ||
| 1066 | } |