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