Subversion Repositories Games.Chess Giants

Rev

Rev 1 | Rev 161 | Go to most recent revision | 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
 
864
      // else is it a valid entry/value pair that is enclosed between quotes?
136 pmbaty 865
      else if (swscanf_s (line_buffer, L"%[^=] = \"%[^\"]\"", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2)
1 pmbaty 866
      {
136 pmbaty 867
         length = (int) wcslen (current_entry); // get the entry string length
1 pmbaty 868
 
869
         fieldstart = 0; // let's now strip leading blanks
136 pmbaty 870
         while ((fieldstart < length) && iswspace (current_entry[fieldstart]))
1 pmbaty 871
            fieldstart++; // ignore any tabs or spaces, going forward from the start
872
 
873
         fieldstop = length - 1; // let's now strip trailing blanks
136 pmbaty 874
         while ((fieldstop >= 0) && iswspace (current_entry[fieldstop]))
1 pmbaty 875
            fieldstop--; // ignore any tabs or spaces, going backwards from the end
876
 
877
         for (i = fieldstart; i <= fieldstop; i++)
136 pmbaty 878
            current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces
879
         current_entry[i - fieldstart] = 0; // and terminate the string
1 pmbaty 880
 
881
         // when value is enclosed between quotes, DO NOT strip the blanks
882
 
883
         // sscanf cannot handle "" or '' as empty value, this is done here
136 pmbaty 884
         if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0))
885
            current_value[0] = 0; // empty string
1 pmbaty 886
 
136 pmbaty 887
         INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary
1 pmbaty 888
      }
889
 
890
      // else is it a valid entry/value pair without quotes ?
136 pmbaty 891
      else if ((swscanf_s (line_buffer, L"%[^=] = '%[^\']'", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2)
892
               || (swscanf_s (line_buffer, L"%[^=] = %[^;#]", current_entry, STRING_MAXSIZE, current_value, STRING_MAXSIZE) == 2))
1 pmbaty 893
      {
136 pmbaty 894
         length = (int) wcslen (current_entry); // get the entry string length
1 pmbaty 895
 
896
         fieldstart = 0; // let's now strip leading blanks
136 pmbaty 897
         while ((fieldstart < length) && iswspace (current_entry[fieldstart]))
1 pmbaty 898
            fieldstart++; // ignore any tabs or spaces, going forward from the start
899
 
900
         fieldstop = length - 1; // let's now strip trailing blanks
136 pmbaty 901
         while ((fieldstop >= 0) && iswspace (current_entry[fieldstop]))
1 pmbaty 902
            fieldstop--; // ignore any tabs or spaces, going backwards from the end
903
 
904
         for (i = fieldstart; i <= fieldstop; i++)
136 pmbaty 905
            current_entry[i - fieldstart] = towlower (current_entry[i]); // recopy entry name w/out spaces
906
         current_entry[i - fieldstart] = 0; // and terminate the string
1 pmbaty 907
 
136 pmbaty 908
         length = (int) wcslen (current_value); // get the value string length
1 pmbaty 909
 
910
         fieldstart = 0; // let's now strip leading blanks
136 pmbaty 911
         while ((fieldstart < length) && iswspace (current_value[fieldstart]))
1 pmbaty 912
            fieldstart++; // ignore any tabs or spaces, going forward from the start
913
 
914
         fieldstop = length - 1; // let's now strip trailing blanks
136 pmbaty 915
         while ((fieldstop >= 0) && iswspace (current_value[fieldstop]))
1 pmbaty 916
            fieldstop--; // ignore any tabs or spaces, going backwards from the end
917
 
918
         for (i = fieldstart; i <= fieldstop; i++)
136 pmbaty 919
            current_value[i - fieldstart] = current_value[i]; // recopy entry name w/out spaces
920
         current_value[i - fieldstart] = 0; // and terminate the string
1 pmbaty 921
 
922
         // sscanf cannot handle "" or '' as empty value, this is done here
136 pmbaty 923
         if ((wcscmp (current_value, L"\"\"") == 0) || (wcscmp (current_value, L"''") == 0))
924
            current_value[0] = 0; // empty string
1 pmbaty 925
 
136 pmbaty 926
         INIFile_WriteEntryAsString (dictionary, current_section, current_entry, current_value); // add to dictionary
1 pmbaty 927
      }
928
   }
929
 
930
   fclose (fp); // finished, close the file
136 pmbaty 931
   SAFE_free ((void **) &line_buffer); // free the line buffer we allocated at the beginning of the function
932
   SAFE_free ((void **) &current_value); // free the current value buffer we allocated at the beginning of the function
933
   SAFE_free ((void **) &current_entry); // free the current entry buffer we allocated at the beginning of the function
934
   SAFE_free ((void **) &current_section); // free the current section buffer we allocated at the beginning of the function
1 pmbaty 935
 
136 pmbaty 936
   return ((void *) dictionary); // and return a pointer to it
1 pmbaty 937
}
938
 
939
 
940
unsigned char INIFile_SaveINIFile (const wchar_t *filename, void *ini_data)
941
{
942
   // this function dumps a given dictionary into a loadable ini file.
943
 
944
   FILE *fp;
945
   int section_index;
946
   wchar_t *section_name;
947
   int section_count;
948
   int entry_index;
949
   int length;
950
   dictionary_t *dictionary;
951
   dictionary_entry_t *entry;
952
 
953
   // try to open the INI file in ASCII write mode
954
   fp = _wfsopen (filename, L"w, ccs=UNICODE", _SH_DENYNO);
955
   if (fp == NULL)
956
      return (0); // cancel if unable to open file
957
 
958
   // get a hand on the INI data dictionary
959
   dictionary = (dictionary_t *) ini_data;
960
 
961
   // keep only the file name for the comment
962
   if (wcsrchr (filename, '/') != NULL)
963
      filename = wcsrchr (filename, '/') + 1;
964
   else if (wcsrchr (filename, '\\') != NULL)
965
      filename = wcsrchr (filename, '\\') + 1;
966
 
967
   // print the INI file name as a comment
968
   fwprintf (fp, L"# %s\n", filename);
969
 
970
   // get the number of sections there are in this INI dictionary
971
   section_count = INIFile_GetNumberOfSections (dictionary);
972
 
973
   // for each section...
974
   for (section_index = 0; section_index < section_count; section_index++)
975
   {
976
      section_name = INIFile_GetSectionName (dictionary, section_index); // read section name
977
 
978
      // is it the default section ?
979
      if (wcscmp (section_name, INIFile_default_section) == 0)
980
         fwprintf (fp, L"\n"); // don't put the default section's name in the INI file
981
      else
982
         fwprintf (fp, L"\n[%s]\n", section_name); // dump all other sections into the INI file
983
 
136 pmbaty 984
      length = (int) wcslen (section_name);
1 pmbaty 985
 
986
      // then for each entry in the dictionary...
987
      for (entry_index = 0; entry_index < dictionary->entry_count; entry_index++)
988
      {
989
         entry = &dictionary->entries[entry_index]; // quick access to entry
990
 
991
         if (entry->key[0] == 0)
992
            continue; // skip empty slots
993
 
994
         // does this key belong to the section we want ? if so, dump it
136 pmbaty 995
         if ((wcsncmp (entry->key, section_name, length) == 0) && (entry->key[length] == INI_SECTION_SEPARATOR))
1 pmbaty 996
         {
997
            // if key starts or ends with a space, enclose it between quotes, else don't
998
            if ((entry->value[0] != 0)
999
                && (iswspace (entry->value[0]) || iswspace (entry->value[wcslen (entry->value) - 1])))
136 pmbaty 1000
               fwprintf (fp, L"%s = \"%s\"\n", &entry->key[length + 1], entry->value);
1 pmbaty 1001
            else
136 pmbaty 1002
               fwprintf (fp, L"%s = %s\n", &entry->key[length + 1], entry->value);
1 pmbaty 1003
         }
1004
      }
1005
   }
1006
 
1007
   fclose (fp); // finished, close the file
1008
 
1009
   return (1); // and return TRUE as the save occured successfully
1010
}
1011
 
1012
 
1013
void INIFile_FreeINIFile (void *ini_data)
1014
{
1015
   // this function frees the dictionary object used to store an INI file data
1016
 
1017
   if (ini_data == NULL)
1018
      return; // consistency check
1019
 
1020
   Dictionary_DestroyDictionary ((dictionary_t *) ini_data); // just destroy the dictionary
1021
   return; // finished
1022
}