1 /* inifile.c
2 Copyright (C) 2007-2019 Dmitry Groshev
3
4 This file is part of mtPaint.
5
6 mtPaint is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 mtPaint is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with mtPaint in the file COPYING.
18 */
19
20 /* *** PREFACE ***
21 * This implementation has no comment preservation, and has nested sections -
22 * because this is how we like our inifiles. :-)
23 * Allocations are done in slabs, because no slot is ever deallocated or
24 * reordered; only modified string values are allocated singly, since it is
25 * probable they will keep being modified.
26 * Implementation uses one 32-bit hash function as two 16-bit ones while
27 * possible, but with 40000 keys or more, has to evaluate two 32-bit functions
28 * instead. But such loads are expected to be rare. - WJ */
29
30 #include <math.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <gtk/gtk.h>
35
36 #include "global.h"
37 #include "memory.h"
38 #include "inifile.h"
39
40 /* Make code not compile where it cannot run */
41 typedef char Integers_Do_Not_Fit_Into_Pointers[2 * (sizeof(int) <= sizeof(char *)) - 1];
42
43 #define SLAB_INCREMENT 16384
44 #define SLAB_RESERVED 64 /* Reserved for allocator overhead */
45
46 #define INI_LIMIT 0x40000000 /* Limit imposed by hashing scheme */
47 #define HASHFILL(X) ((X) * 3) /* Hash occupancy no more than 1/3 */
48 #define HASHSEED 0x811C9DC5
49 #define HASH_RND(X) ((X) * 0x10450405 + 1)
50 #define HASH_MIN 256 /* Minimum hash capacity, so that it won't be too tiny */
51
52 /* Slot types */
53 enum {
54 INI_NONE = 0,
55 INI_UNDEF,
56 INI_STR,
57 INI_INT,
58 INI_BOOL,
59 INI_REF
60 };
61 // Negative types are section nesting levels
62
63 /* Slot flags */
64 #define INI_SYSTEM 0x0001 /* Systemwide inifile */
65 #define INI_MALLOC 0x0002 /* Value is allocated singly (not in block) */
66 #define INI_DEFAULT 0x0004 /* Default value is defined */
67 #define INI_TEMP 0x0008 /* Transient - not written out */
68
69 /* Escaping is needed for leading tabs and spaces, for inline CR and LF, and
70 * possibly for inline backslash */
escape_string(char * src,int bs)71 static char *escape_string(char *src, int bs)
72 {
73 static const char *escaped = "\t \\\r\n";
74 const char *e = escaped;
75 char c, *cp, *tmp, *t2;
76
77 if ((src[0] != '\t') && (src[0] != ' ') &&
78 !src[strcspn(src, "\\\r\n" + !bs)]) return (NULL);
79 t2 = tmp = calloc(1, strlen(src) * 2 + 1);
80 if (tmp)
81 {
82 while ((c = *src++))
83 {
84 if ((cp = strchr(e, c)))
85 {
86 *t2++ = '\\';
87 c = "t \\rn"[cp - escaped];
88 }
89 *t2++ = c;
90 e = escaped + 2;
91 }
92 *t2 = '\0';
93 }
94 return (tmp);
95 }
96
97 #if GTK_MAJOR_VERSION == 1
98
99 /* GLib 1.2 doesn't provide g_strcompress() */
unescape_string(char * buf)100 static void unescape_string(char *buf)
101 {
102 #define NUM_ESCAPES 5
103 static const char escapes[] = "bfnrt01234567";
104 static const char escaped[] = { 7, 12, 10, 13, 8, 0, 1, 2, 3, 4, 5, 6, 7 };
105 char c, cc, *tmp, *src = buf;
106 int v;
107
108 while ((c = *src++))
109 {
110 if (c == '\\')
111 {
112 c = *src++;
113 if (!c) break;
114 while ((tmp = strchr(escapes, c)))
115 {
116 v = tmp - escapes;
117 c = escaped[v];
118 if (v >= NUM_ESCAPES)
119 {
120 // Octal escape
121 cc = *src - '0';
122 if (cc & ~7) break;
123 c = c * 8 + cc;
124 cc = *(++src) - '0';
125 if (cc & ~7) break;
126 c = c * 8 + cc;
127 ++src;
128 }
129 break;
130 }
131 }
132 *buf++ = c;
133 }
134 *buf = '\0';
135 #undef NUM_ESCAPES
136 }
137
138 #else
139
unescape_string(char * buf)140 static void unescape_string(char *buf)
141 {
142 char *tmp = g_strcompress(buf);
143 strcpy(buf, tmp);
144 g_free(tmp);
145 }
146
147 #endif
148
149 /* Thomas Wang's and "One at a time" hash functions */
hashf(guint32 seed,int section,char * key)150 static guint32 hashf(guint32 seed, int section, char *key)
151 {
152 seed += section;
153 seed = (seed << 15) + ~seed;
154 seed ^= (seed >> 12);
155 seed += seed << 2;
156 seed ^= seed >> 4;
157 seed *= 2057;
158 seed ^= seed >> 16;
159 for (; *key; key++)
160 {
161 seed += *key;
162 seed += seed << 10;
163 seed ^= seed >> 6;
164 }
165 seed += seed << 3;
166 seed ^= seed >> 11;
167 seed += seed << 15;
168 return (seed);
169 }
170
cuckoo_insert(inifile * inip,inislot * slotp)171 static int cuckoo_insert(inifile *inip, inislot *slotp)
172 {
173 gint32 idx, tmp;
174 guint32 key;
175 int i, j, d;
176
177 /* Update section's chain */
178 i = slotp->sec;
179 idx = slotp - inip->slots;
180 if ((j = (int)inip->slots[i].value) < idx)
181 {
182 inip->slots[i].value = (char *)idx;
183 if (j) inip->slots[j].chain = idx;
184 else inip->slots[i].defv = (char *)idx;
185 }
186
187 /* Decide if using one-key mode */
188 d = inip->seed[0] == inip->seed[1];
189
190 /* Normal cuckoo process */
191 for (i = 0; i < inip->maxloop; i++)
192 {
193 key = hashf(inip->seed[i & 1], slotp->sec, slotp->key);
194 key >>= (i & d) << 4; // Shift by 16 or don't
195 j = (key & inip->hmask) * 2 + (i & 1);
196 tmp = inip->hash[j];
197 inip->hash[j] = idx;
198 idx = tmp;
199 if (!idx) return (TRUE);
200 slotp = inip->slots + idx;
201 }
202 return (FALSE);
203 }
204
resize_hash(inifile * inip,int cnt)205 static int resize_hash(inifile *inip, int cnt)
206 {
207 int len;
208
209 if (!cnt) return (0); /* Degenerate case */
210 len = nextpow2(HASHFILL(cnt) - 1);
211 if (len <= inip->hmask * 2 + 2) return (0); /* Large enough */
212 free(inip->hash);
213 inip->hash = calloc(1, len * sizeof(gint32));
214 if (!inip->hash) return (-1); /* Failure */
215 inip->hmask = (len >> 1) - 1;
216 inip->maxloop = ceil(3.0 * log(len / 2) /
217 log((double)(len / 2) / cnt));
218 return (1); /* Resized */
219 }
220
rehash(inifile * inip)221 static int rehash(inifile *inip)
222 {
223 int i, flag;
224
225 flag = resize_hash(inip, inip->count);
226 if (flag < 0) return (FALSE); /* Failed */
227
228 while (TRUE) /* Until done */
229 {
230 if (!flag) /* No size change */
231 {
232 inip->seed[0] = inip->seed[1] = HASH_RND(inip->seed[0]);
233 memset(inip->hash, 0, (inip->hmask + 1) * 2 * sizeof(gint32));
234 }
235 /* Enter two-key mode */
236 if (inip->hmask > 0xFFFF) inip->seed[1] = HASH_RND(inip->seed[0]);
237 /* Re-insert items */
238 for (i = 1; (i <= inip->count) &&
239 (flag = cuckoo_insert(inip, inip->slots + i)); i++);
240 if (flag) return (TRUE);
241 }
242 }
243
cuckoo_find(inifile * inip,int section,char * name)244 static inislot *cuckoo_find(inifile *inip, int section, char *name)
245 {
246 inislot *slotp;
247 guint32 key;
248 gint32 i;
249 int j = 0;
250
251 key = hashf(inip->seed[0], section, name);
252 while (TRUE)
253 {
254 i = inip->hash[(key & inip->hmask) * 2 + j];
255 if (i)
256 {
257 slotp = inip->slots + i;
258 if ((slotp->sec == section) && !strcmp(name, slotp->key))
259 return (slotp);
260 }
261 if (j++) return (NULL);
262 if (inip->seed[0] == inip->seed[1]) key >>= 16;
263 else key = hashf(inip->seed[1], section, name);
264 }
265 }
266
add_slot(inifile * inip)267 static inislot *add_slot(inifile *inip)
268 {
269 inislot *ra;
270 int i, j;
271
272 if (inip->count >= INI_LIMIT) return (NULL); /* Too many entries */
273 /* Extend the slab if needed */
274 i = inip->count * sizeof(inislot) + sizeof(inislot) +
275 SLAB_RESERVED + SLAB_INCREMENT - 1;
276 j = (i + sizeof(inislot)) / SLAB_INCREMENT;
277 if (i / SLAB_INCREMENT < j)
278 {
279 ra = realloc(inip->slots, j * SLAB_INCREMENT - SLAB_RESERVED);
280 if (!ra) return (NULL);
281 inip->slots = ra;
282 }
283 ra = inip->slots + ++inip->count;
284 memset(ra, 0, sizeof(inislot));
285 return (ra);
286 }
287
store_string(inifile * inip,char * str)288 static char *store_string(inifile *inip, char *str)
289 {
290 int i, l;
291 char *ra;
292
293 /* First byte of first block for zero length */
294 if (!str || !*str) return (*(char **)inip->sblocks + sizeof(char *));
295
296 /* Add new block if needed */
297 l = strlen(str) + 1;
298 i = inip->slen + SLAB_RESERVED + SLAB_INCREMENT - 1;
299 if (i / SLAB_INCREMENT < (i + l) / SLAB_INCREMENT)
300 {
301 i = l + sizeof(char *) + SLAB_RESERVED + SLAB_INCREMENT - 1;
302 i -= i % SLAB_INCREMENT + SLAB_RESERVED;
303 ra = calloc(1, i);
304 if (!ra) return (NULL);
305 /* Insert at tail of ring */
306 *(char **)ra = *(char **)inip->sblocks;
307 *(char **)inip->sblocks = ra;
308 inip->sblocks = ra;
309 inip->slen = sizeof(char *);
310 }
311 ra = inip->sblocks + inip->slen;
312 memcpy(ra, str, l);
313 inip->slen += l;
314 return (ra);
315 }
316
key_slot(inifile * inip,int section,char * key,int type)317 static inislot *key_slot(inifile *inip, int section, char *key, int type)
318 {
319 inislot *slot;
320
321 if (type < 0) type = inip->slots[section].type - 1;
322 slot = cuckoo_find(inip, section, key);
323 if (slot)
324 {
325 if (type == INI_NONE) return (slot);
326 if (slot->flags & INI_MALLOC)
327 {
328 free(slot->value);
329 slot->flags ^= INI_MALLOC;
330 }
331 #if VALIDATE_TYPE
332 if ((slot->type != type) && (slot->type > INI_UNDEF))
333 g_warning("INI key '%s' changed type\n", key);
334 #endif
335 }
336 else
337 {
338 key = store_string(inip, key);
339 if (!key) return (NULL);
340 slot = add_slot(inip);
341 if (!slot) return (NULL);
342 slot->sec = section;
343 slot->key = key;
344 if (!cuckoo_insert(inip, slot) && !rehash(inip))
345 return (NULL);
346 }
347 slot->type = type;
348 return (slot);
349 }
350
new_ini(inifile * inip)351 int new_ini(inifile *inip)
352 {
353 memset(inip, 0, sizeof(inifile));
354 inip->sblocks = calloc(1, SLAB_INCREMENT - SLAB_RESERVED);
355 if (!inip->sblocks) return (FALSE);
356 *(char **)inip->sblocks = inip->sblocks;
357 inip->slen = sizeof(char *) + 1;
358 inip->slots = calloc(1, SLAB_INCREMENT - SLAB_RESERVED);
359 inip->seed[0] = inip->seed[1] = HASHSEED;
360 return (inip->slots && (resize_hash(inip, HASH_MIN) > 0));
361 }
362
forget_ini(inifile * inip)363 void forget_ini(inifile *inip)
364 {
365 char *this, *next, *mem = inip->sblocks;
366 int i;
367
368 if (mem) for (this = *(char **)mem; this != mem; this = next)
369 {
370 next = *(char **)this;
371 free(this);
372 }
373 free(mem);
374 for (i = 1; i <= inip->count; i++)
375 {
376 if (inip->slots[i].flags & INI_MALLOC)
377 free(inip->slots[i].value);
378 }
379 free(inip->slots);
380 free(inip->hash);
381 memset(inip, 0, sizeof(inifile));
382 }
383
384 /* Load file whole into memory, with N zero bytes before and two after */
slurp_file_l(char * fname,int before,int * len)385 char *slurp_file_l(char *fname, int before, int *len)
386 {
387 FILE *fp;
388 char *buf = NULL;
389 int i, l;
390
391 if (!fname || !(fp = fopen(fname, "rb"))) return (NULL);
392 fseek(fp, 0, SEEK_END);
393 l = ftell(fp);
394 if ((l >= 0) && (l < INT_MAX - before - 2))
395 buf = calloc(1, l + before + 2);
396 if (buf)
397 {
398 fseek(fp, 0, SEEK_SET);
399 i = fread(buf + before, 1, l, fp);
400 if (i != l)
401 {
402 free(buf);
403 buf = NULL;
404 }
405 }
406 fclose(fp);
407 if (len) *len = l;
408 return (buf);
409 }
410
read_ini(inifile * inip,char * fname,int itype)411 int read_ini(inifile *inip, char *fname, int itype)
412 {
413 inifile ini;
414 inislot *slot;
415 char *tmp, *wrk, *w2, *str;
416 int i, j, l, q, sec = 0;
417
418
419 /* Read the file */
420 tmp = slurp_file(fname, sizeof(char *) + 1);
421 if (!tmp) return (0);
422 ini = *inip;
423 /* Insert at head of ring */
424 *(char **)tmp = *(char **)inip->sblocks;
425 *(char **)inip->sblocks = tmp;
426
427 /* Parse the contents */
428 for (tmp += sizeof(char *) + 1; ; tmp = str)
429 {
430 tmp += strspn(tmp, "\r\n\t ");
431 if (!*tmp) break;
432 str = tmp + strcspn(tmp, "\r\n");
433 if (*str) *str++ = '\0';
434 if ((*tmp == ';') || (*tmp == '#')) continue; /* Comment */
435 if (*tmp == '[') /* Section */
436 {
437 if (!(w2 = strchr(tmp + 1, ']'))) goto error;
438 for (i = 0; tmp[i + 1] == '>'; i++); // Nesting level
439 j = i + ini.slots[sec].type;
440 if (j > 0) goto error;
441 l = sec;
442 while (j++) l = ini.slots[l].sec;
443 *w2 = '\0';
444 /* Hash this */
445 tmp += ++i;
446 slot = l || *tmp ? cuckoo_find(&ini, l, tmp) : ini.slots;
447 if (!slot) /* New section */
448 {
449 slot = add_slot(&ini);
450 if (!slot) goto fail;
451 slot->type = -i;
452 slot->sec = l;
453 slot->key = tmp;
454 slot->flags = itype;
455 if (!cuckoo_insert(&ini, slot) && !rehash(&ini))
456 goto fail;
457 }
458 sec = slot - ini.slots; /* Activate */
459 continue;
460 }
461 /* Variable (spaces in name allowed) */
462 w2 = strchr(tmp, '=');
463 if (!w2)
464 {
465 error: g_printerr("Wrong INI line: '%s'\n", tmp);
466 continue;
467 }
468 for (wrk = w2 - 1; wrk - tmp >= 0; wrk--)
469 if ((*wrk != ' ') && (*wrk != '\t')) break;
470 wrk[1] = '\0';
471 q = *(++w2) == '='; /* "==" means quoted value */
472 w2 += q + strspn(w2 + q, "\t ");
473 // for (wrk = str - 1; *wrk && ((*wrk == '\t') || (*wrk == ' '); *wrk-- = '\0');
474 /* Hash this pair */
475 slot = cuckoo_find(&ini, sec, tmp);
476 if (!slot) /* New key */
477 {
478 slot = add_slot(&ini);
479 if (!slot) goto fail;
480 slot->sec = sec;
481 slot->key = tmp;
482 slot->flags = itype;
483 if (!cuckoo_insert(&ini, slot) && !rehash(&ini))
484 goto fail;
485 }
486 if (q) unescape_string(w2);
487 slot->type = INI_UNDEF;
488 slot->value = w2;
489 }
490
491 /* Return the result */
492 *inip = ini;
493 return (1);
494
495 /* Catastrophic failure - unable to add key */
496 fail: forget_ini(&ini);
497 *inip = ini;
498 return (-1);
499 }
500
write_ini(inifile * inip,char * fname,char * header)501 int write_ini(inifile *inip, char *fname, char *header)
502 {
503 FILE *fp;
504 inislot *slotp, *secp = NULL;
505 char *name, *sv, *xv;
506 int i, j, sec, var, up, down, written = 0;
507
508 if (!(fp = fopen(fname, "w"))) return (FALSE);
509 if (header) fprintf(fp, "%s\n", header);
510
511 sec = 0; var = -1;
512 while (TRUE)
513 {
514 /* (Re)start scanning a new section */
515 if (var <= 0) i = (int)inip->slots[sec].defv , var = !var;
516 else if (!sec) break; /* All done */
517 /* Return to scanning for parent's subsections */
518 else
519 {
520 i = inip->slots[sec].chain;
521 sec = inip->slots[sec].sec;
522 if (written > sec) written = sec;
523 }
524 for (; i; i = inip->slots[i].chain)
525 {
526 slotp = inip->slots + i;
527
528 /* Transients are skipped with whole subtrees */
529 if (slotp->flags & INI_TEMP) continue;
530
531 /* Variables first, subsections second */
532 if ((slotp->type < 0) ^ var) continue;
533
534 if (var) /* Section */
535 {
536 sec = i; var = -1;
537 break;
538 }
539
540 sv = slotp->value ? slotp->value : "";
541
542 /* Keys from system inifile ignore defaults, because
543 * they exist to override the defaults - WJ */
544 if (!(slotp->flags & INI_SYSTEM) &&
545 (slotp->flags & INI_DEFAULT) &&
546 (slotp->type == INI_STR ?
547 !strcmp(slotp->defv, sv) :
548 (slotp->type == INI_INT) || (slotp->type == INI_REF) ?
549 (int)slotp->value == (int)slotp->defv :
550 slotp->type == INI_BOOL ?
551 !!slotp->value == !!slotp->defv :
552 FALSE)) continue;
553
554 /* Write out sections */
555 up = 0;
556 while (written < sec) // Reverse chain to written level
557 {
558 down = inip->slots[sec].sec;
559 inip->slots[sec].sec = up;
560 up = sec;
561 sec = down;
562 }
563 while (up) // Write out reversing back
564 {
565 down = sec;
566 written = sec = up;
567 secp = inip->slots + sec;
568 up = secp->sec;
569 secp->sec = down;
570 fputc('[', fp);
571 for (j = -1; j > secp->type; j--) fputc('>', fp);
572 fprintf(fp, "%s]\n", secp->key);
573 }
574
575 /* Write out variable */
576 name = slotp->key;
577 switch (slotp->type)
578 {
579 case INI_REF:
580 fprintf(fp, "%s == ", name);
581 j = (int)slotp->value;
582 up = 0;
583 while (j > 0) // Reverse chain to level 0
584 {
585 down = inip->slots[j].sec;
586 inip->slots[j].sec = up;
587 up = j;
588 j = down;
589 }
590 while (up) // Write out reversing back
591 {
592 down = j;
593 j = up;
594 secp = inip->slots + j;
595 up = secp->sec;
596 secp->sec = down;
597 xv = escape_string(secp->key, TRUE);
598 fprintf(fp, "%s%s", xv ? xv : secp->key,
599 up ? "\\n" : "");
600 free(xv);
601 }
602 fputc('\n', fp);
603 break;
604 case INI_INT:
605 fprintf(fp, "%s = %d\n", name, (int)slotp->value);
606 break;
607 case INI_BOOL:
608 fprintf(fp, "%s = %s\n", name, slotp->value ?
609 "true" : "false");
610 break;
611 default:
612 /* Escape value if needed */
613 if ((xv = escape_string(sv, FALSE)))
614 {
615 fprintf(fp, "%s == %s\n", name, xv);
616 free(xv);
617 }
618 else fprintf(fp, "%s = %s\n", name, sv);
619 break;
620 }
621 }
622 }
623 /* Return to main section if have sections */
624 if (secp) fputs("[]\n", fp);
625
626 fclose(fp);
627 return (TRUE);
628 }
629
ini_setstr(inifile * inip,int section,char * key,char * value)630 int ini_setstr(inifile *inip, int section, char *key, char *value)
631 {
632 inislot *slot;
633
634 /* NULLs are stored as empty strings, for less hazardous handling.
635 * Value duplicated at once, for key_slot() might free it
636 * (if it is the current one, being stored over itself) */
637 value = value && *value ? strdup(value) : NULL;
638 if (!(slot = key_slot(inip, section, key, INI_STR)))
639 {
640 free(value);
641 return (0);
642 }
643 slot->value = "";
644 if (value)
645 {
646 slot->value = value;
647 slot->flags |= INI_MALLOC;
648 }
649 return (slot - inip->slots);
650 }
651
ini_setint(inifile * inip,int section,char * key,int value)652 int ini_setint(inifile *inip, int section, char *key, int value)
653 {
654 inislot *slot;
655
656 if (!(slot = key_slot(inip, section, key, INI_INT))) return (0);
657 slot->value = (char *)value;
658 return (slot - inip->slots);
659 }
660
ini_setbool(inifile * inip,int section,char * key,int value)661 int ini_setbool(inifile *inip, int section, char *key, int value)
662 {
663 inislot *slot;
664
665 if (!(slot = key_slot(inip, section, key, INI_BOOL))) return (0);
666 slot->value = (char *)!!value;
667 return (slot - inip->slots);
668 }
669
ini_setref(inifile * inip,int section,char * key,int value)670 int ini_setref(inifile *inip, int section, char *key, int value)
671 {
672 inislot *slot;
673
674 if (!(slot = key_slot(inip, section, key, INI_REF))) return (0);
675 slot->value = (char *)value;
676 return (slot - inip->slots);
677 }
678
ini_getstr(inifile * inip,int section,char * key,char * defv)679 char *ini_getstr(inifile *inip, int section, char *key, char *defv)
680 {
681 inislot *slot;
682
683 /* NULLs are stored as empty strings, for less hazardous handling */
684 if (!defv) defv = "";
685
686 /* Read existing */
687 slot = key_slot(inip, section, key, INI_NONE);
688 if (!slot) return (defv);
689 if (slot->type == INI_STR)
690 {
691 #if VALIDATE_DEF
692 if ((slot->flags & INI_DEFAULT) &&
693 strcmp(defv ? defv : "", slot->defv))
694 g_warning("INI key '%s' new default\n", key);
695 #endif
696 if (slot->flags & INI_DEFAULT) return (slot->value);
697 slot->type = INI_UNDEF; /* Fall through to storing default */
698 }
699
700 /* Store default */
701 slot->flags &= ~INI_DEFAULT;
702 if ((slot->defv = store_string(inip, defv)))
703 slot->flags |= INI_DEFAULT;
704
705 if (slot->type != INI_UNDEF)
706 {
707 #if VALIDATE_TYPE
708 if (slot->type != INI_NONE)
709 g_printerr("INI key '%s' wrong type\n", key);
710 #endif
711 if (!defv) slot->value = NULL;
712 else if (!*defv) slot->value = "";
713 else
714 {
715 slot->value = strdup(defv);
716 if (slot->value) slot->flags |= INI_MALLOC;
717 }
718 }
719 slot->type = INI_STR;
720 return (slot->value);
721 }
722
ini_getint(inifile * inip,int section,char * key,int defv)723 int ini_getint(inifile *inip, int section, char *key, int defv)
724 {
725 inislot *slot;
726 char *tail;
727 long l;
728
729 /* Read existing */
730 slot = key_slot(inip, section, key, INI_NONE);
731 if (!slot) return (defv);
732 if (slot->type == INI_INT)
733 {
734 #if VALIDATE_DEF
735 if ((slot->flags & INI_DEFAULT) && ((char *)defv != slot->defv))
736 g_warning("INI key '%s' new default\n", key);
737 #endif
738 if (slot->flags & INI_DEFAULT) return ((int)(slot->value));
739 /* Fall through to storing default */
740 }
741
742 /* Store default */
743 slot->defv = (char *)defv;
744 slot->flags |= INI_DEFAULT;
745
746 while (slot->type != INI_INT)
747 {
748 if (slot->type == INI_UNDEF)
749 {
750 l = strtol(slot->value, &tail, 10);
751 slot->value = (char *)l;
752 if (!*tail) break;
753 }
754 else if (slot->flags & INI_MALLOC)
755 {
756 free(slot->value);
757 slot->flags ^= INI_MALLOC;
758 }
759 #if VALIDATE_TYPE
760 if (slot->type != INI_NONE)
761 g_printerr("INI key '%s' wrong type\n", key);
762 #endif
763 slot->value = (char *)defv;
764 break;
765 }
766 slot->type = INI_INT;
767 return ((int)(slot->value));
768 }
769
str2bool(const char * s)770 int str2bool(const char *s)
771 {
772 static const char *YN[] = { "n", "y", "0", "1", "no", "yes",
773 "off", "on", "false", "true", "disabled", "enabled", NULL };
774 int i;
775
776 for (i = 0; YN[i]; i++) if (!strcasecmp(YN[i], s)) return (i & 1);
777 return (-1);
778 }
779
ini_getbool(inifile * inip,int section,char * key,int defv)780 int ini_getbool(inifile *inip, int section, char *key, int defv)
781 {
782 inislot *slot;
783 int i;
784
785 defv = !!defv;
786
787 /* Read existing */
788 slot = key_slot(inip, section, key, INI_NONE);
789 if (!slot) return (defv);
790 if (slot->type == INI_BOOL)
791 {
792 #if VALIDATE_DEF
793 if ((slot->flags & INI_DEFAULT) && ((char *)defv != slot->defv))
794 g_warning("INI key '%s' new default\n", key);
795 #endif
796 if (slot->flags & INI_DEFAULT) return ((int)(slot->value));
797 /* Fall through to storing default */
798 }
799
800 /* Store default */
801 slot->defv = (char *)defv;
802 slot->flags |= INI_DEFAULT;
803
804 while (slot->type != INI_BOOL)
805 {
806 if (slot->type == INI_UNDEF)
807 {
808 slot->value = (char *)(i = str2bool(slot->value));
809 if (i >= 0) break;
810 }
811 else if (slot->flags & INI_MALLOC)
812 {
813 free(slot->value);
814 slot->flags ^= INI_MALLOC;
815 }
816 #if VALIDATE_TYPE
817 if (slot->type != INI_NONE)
818 g_printerr("INI key '%s' wrong type\n", key);
819 #endif
820 slot->value = (char *)defv;
821 break;
822 }
823 slot->type = INI_BOOL;
824 return ((int)(slot->value));
825 }
826
ini_getref(inifile * inip,int section,char * key,int defv)827 int ini_getref(inifile *inip, int section, char *key, int defv)
828 {
829 inislot *slot;
830 char *w, *tmp;
831
832 /* Read existing */
833 slot = key_slot(inip, section, key, INI_NONE);
834 if (!slot) return (defv);
835 if (slot->type == INI_REF)
836 {
837 #if VALIDATE_DEF
838 if ((slot->flags & INI_DEFAULT) && ((char *)defv != slot->defv))
839 g_warning("INI key '%s' new default\n", key);
840 #endif
841 if (slot->flags & INI_DEFAULT) return ((int)(slot->value));
842 /* Fall through to storing default */
843 }
844
845 /* Store default */
846 slot->defv = (char *)defv;
847 slot->flags |= INI_DEFAULT;
848
849 while (slot->type != INI_REF)
850 {
851 if (slot->type == INI_UNDEF)
852 {
853 /* Parse a LF-separated path through sections */
854 section = 0;
855 tmp = slot->value;
856 while (tmp)
857 {
858 tmp = strchr(w = tmp, '\n');
859 if (tmp)
860 {
861 *tmp++ = '\0';
862 section = ini_getsection(inip, section, w);
863 if (section <= 0) break;
864 }
865 else if (*w)
866 {
867 inislot *s = cuckoo_find(inip, section, w);
868 section = s ? s - inip->slots : 0;
869 }
870 }
871 /* Empty stays empty, broken goes to default */
872 if ((section <= 0) && slot->value[0]) section = defv;
873 slot->value = (char *)section;
874 break;
875 }
876 else if (slot->flags & INI_MALLOC)
877 {
878 free(slot->value);
879 slot->flags ^= INI_MALLOC;
880 }
881 #if VALIDATE_TYPE
882 if (slot->type != INI_NONE)
883 g_printerr("INI key '%s' wrong type\n", key);
884 #endif
885 slot->value = (char *)defv;
886 break;
887 }
888 slot->type = INI_REF;
889 return ((int)(slot->value));
890 }
891
ini_setsection(inifile * inip,int section,char * key)892 int ini_setsection(inifile *inip, int section, char *key)
893 {
894 inislot *slot = key_slot(inip, section, key, -1);
895 if (!slot) return (-1);
896 return (slot - inip->slots);
897 }
898
ini_getsection(inifile * inip,int section,char * key)899 int ini_getsection(inifile *inip, int section, char *key)
900 {
901 inislot *slot = cuckoo_find(inip, section, key);
902 if (!slot || (slot->type != inip->slots[section].type - 1)) return (-1);
903 return (slot - inip->slots);
904 }
905
ini_transient(inifile * inip,int section,char * key)906 int ini_transient(inifile *inip, int section, char *key)
907 {
908 inislot *slot = key ? cuckoo_find(inip, section, key) :
909 section ? inip->slots + section : NULL;
910 if (!slot) return (FALSE);
911 slot->flags |= INI_TEMP;
912 return (TRUE);
913 }
914
915 #ifdef WIN32
916
get_home_directory(void)917 char *get_home_directory(void)
918 {
919 static char *homedir = NULL;
920
921 if (homedir) return homedir;
922 homedir = getenv("USERPROFILE"); // Gets the current users home directory in WinXP
923 if (!homedir) homedir = ""; // And this, in Win9x :-)
924 return homedir;
925 }
926
extend_path(const char * path)927 char *extend_path(const char *path)
928 {
929 char *dir, *name;
930
931 if (path[0] == '~')
932 return (g_strdup_printf("%s%s", get_home_directory(), path + 1));
933 if (path[0] && (path[1] == ':')) // Path w/drive letter is absolute
934 return (g_strdup(path));
935 name = g_win32_get_package_installation_directory(NULL, NULL);
936 dir = g_locale_from_utf8(name, -1, NULL, NULL, NULL);
937 g_free(name);
938 name = g_strdup_printf("%s%s", dir, path);
939 g_free(dir);
940 return (name);
941 }
942
943 #else
944
945 #include <unistd.h>
946 #include <pwd.h>
947
948 /*
949 * This function came from mhWaveEdit
950 * by Magnus Hjorth, 2003.
951 */
get_home_directory(void)952 gchar *get_home_directory(void)
953 {
954 static char *homedir = NULL;
955 struct passwd *p;
956
957 if (homedir) return homedir;
958 homedir = getenv("HOME");
959 if (!homedir)
960 {
961 p = getpwuid(getuid());
962 if (p) homedir = p->pw_dir;
963 }
964 if (!homedir)
965 {
966 g_warning(_("Could not find home directory. Using current directory as "
967 "home directory."));
968 homedir = ".";
969 }
970 return homedir;
971 }
972
extend_path(const char * path)973 char *extend_path(const char *path)
974 {
975 if (path[0] == '~')
976 return (g_strdup_printf("%s%s", get_home_directory(), path + 1));
977 return (g_strdup(path));
978 }
979
980 #endif
981
982 /* Compatibility functions */
983
984 inifile main_ini;
985 static char *main_ininame;
986
inifile_init(char * system_ini,char * user_ini)987 void inifile_init(char *system_ini, char *user_ini)
988 {
989 char *tmp, *ini = user_ini;
990 int res, mask = 3;
991
992 while (new_ini(&main_ini))
993 {
994 if ((mask & 1) && system_ini)
995 {
996 tmp = extend_path(system_ini);
997 res = read_ini(&main_ini, tmp, INI_SYSTEM);
998 g_free(tmp);
999 if (res <= 0) mask ^= 1; // Don't try again if failed
1000 if (res < 0) continue; // Restart if struct got deleted
1001 /* !!! Allow system inifile to relocate user inifile */
1002 ini = inifile_get("userINI", user_ini);
1003 if (!ini[0]) ini = system_ini;
1004 }
1005 if ((mask & 2) && user_ini)
1006 {
1007 main_ininame = extend_path(ini);
1008 res = read_ini(&main_ini, main_ininame, 0);
1009 if (res <= 0) // Failed
1010 {
1011 mask ^= 2;
1012 if (res < 0) continue;
1013 }
1014 }
1015 break;
1016 }
1017 }
1018
inifile_quit()1019 void inifile_quit()
1020 {
1021 write_ini(&main_ini, main_ininame,
1022 "# Remove this file to restore default settings.\n");
1023 forget_ini(&main_ini);
1024 g_free(main_ininame);
1025 }
1026
inifile_get(char * setting,char * defaultValue)1027 char *inifile_get(char *setting, char *defaultValue)
1028 {
1029 return (ini_getstr(&main_ini, 0, setting, defaultValue));
1030 }
1031
inifile_get_gint32(char * setting,int defaultValue)1032 int inifile_get_gint32(char *setting, int defaultValue)
1033 {
1034 return (ini_getint(&main_ini, 0, setting, defaultValue));
1035 }
1036
inifile_get_gboolean(char * setting,int defaultValue)1037 int inifile_get_gboolean(char *setting, int defaultValue)
1038 {
1039 return (ini_getbool(&main_ini, 0, setting, defaultValue));
1040 }
1041
inifile_set(char * setting,char * value)1042 int inifile_set(char *setting, char *value)
1043 {
1044 return (ini_setstr(&main_ini, 0, setting, value));
1045 }
1046
inifile_set_gint32(char * setting,int value)1047 int inifile_set_gint32(char *setting, int value)
1048 {
1049 return (ini_setint(&main_ini, 0, setting, value));
1050 }
1051
inifile_set_gboolean(char * setting,int value)1052 int inifile_set_gboolean(char *setting, int value)
1053 {
1054 return (ini_setbool(&main_ini, 0, setting, value));
1055 }
1056