ini_file.c 52.6 KB
Newer Older
1
/* Functions to create and parse .ini files */
2
3

/* $Id$ */
rswindell's avatar
rswindell committed
4
// vi: tabstop=4
5
6
7
8
9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include <stdlib.h>		/* strtol */
rob's avatar
rob committed
38
39
#include <string.h>		/* strlen */
#include <ctype.h>		/* isdigit */
40
#include <math.h>		/* fmod */
41
42
#include "xpdatetime.h"	/* isoDateTime_t */
#include "datewrap.h"	/* ctime_r */
43
#include "dirwrap.h"	/* fexist */
rswindell's avatar
rswindell committed
44
#include "filewrap.h"	/* chsize */
45
#include "ini_file.h"
deuce's avatar
deuce committed
46
47
48
#if !defined(NO_SOCKET_SUPPORT)
#include "sockwrap.h"
#endif
49

50
/* Maximum length of entire line, includes '\0' */
51
52
53
54
#define INI_MAX_LINE_LEN		(INI_MAX_VALUE_LEN*2)
#define INI_COMMENT_CHAR		';'
#define INI_OPEN_SECTION_CHAR	'['
#define INI_CLOSE_SECTION_CHAR	']'
55
#define INI_SECTION_NAME_SEP	"|"
56
#define INI_BIT_SEP				'|'
57
#define INI_NEW_SECTION			((char*)~0)
58
#define INI_EOF_DIRECTIVE		"!eof"
59
60
#define INI_INCLUDE_DIRECTIVE	"!include"
#define INI_INCLUDE_MAX			10000
61

62
63
static ini_style_t default_style;

deuce's avatar
deuce committed
64
void DLLCALL iniSetDefaultStyle(ini_style_t style)
65
66
67
68
{
	default_style = style;
}

69
/* These correlate with the LOG_* definitions in syslog.h/gen_defs.h */
70
static char* logLevelStringList[]
71
	= {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debugging", NULL};
72

deuce's avatar
deuce committed
73
str_list_t DLLCALL iniLogLevelStringList(void)
74
{
75
	return(logLevelStringList);
76
77
}

78
79
80
81
82
static BOOL is_eof(char* str)
{
	return(*str=='!' && stricmp(truncsp(str),INI_EOF_DIRECTIVE)==0);
}

83
84
85
86
87
static char* section_name(char* p)
{
	char*	tp;

	SKIP_WHITESPACE(p);
88
	if(*p!=INI_OPEN_SECTION_CHAR)
89
90
91
		return(NULL);
	p++;
	SKIP_WHITESPACE(p);
92
	tp=strrchr(p,INI_CLOSE_SECTION_CHAR);
93
94
95
96
97
98
99
100
	if(tp==NULL)
		return(NULL);
	*tp=0;
	truncsp(p);

	return(p);
}

101
static BOOL section_match(const char* name, const char* compare, BOOL case_sensitive)
102
103
104
105
106
107
108
109
{
	BOOL found=FALSE;
	str_list_t names=strListSplitCopy(NULL,name,INI_SECTION_NAME_SEP);
	str_list_t comps=strListSplitCopy(NULL,compare,INI_SECTION_NAME_SEP);
	size_t	i,j;
	char*	n;
	char*	c;

110
	/* Ignore trailing whitespace */
111
112
113
114
115
116
117
118
119
120
121
122
	for(i=0; names[i]!=NULL; i++)
		truncsp(names[i]);
	for(i=0; comps[i]!=NULL; i++)
		truncsp(comps[i]);

	/* Search for matches */
	for(i=0; names[i]!=NULL && !found; i++)
		for(j=0; comps[j]!=NULL && !found; j++) {
			n=names[i];
			SKIP_WHITESPACE(n);
			c=comps[j];
			SKIP_WHITESPACE(c);
123
124
125
126
			if (case_sensitive)
				found = strcmp(n, c) == 0;
			else
				found = stricmp(n, c) == 0;
127
128
129
130
131
132
133
134
		}

	strListFree(&names);
	strListFree(&comps);

	return(found);
}

135
static BOOL seek_section(FILE* fp, const char* section)
136
137
{
	char*	p;
138
	char	str[INI_MAX_LINE_LEN];
139
140
141

	rewind(fp);

rswindell's avatar
rswindell committed
142
	if(section==ROOT_SECTION)
143
144
		return(TRUE);

145
	/* Perform case-sensitive search first */
146
	while(!feof(fp)) {
147
		if(fgets(str,sizeof(str),fp)==NULL)
148
			break;
149
150
		if(is_eof(str))
			break;
151
		if((p=section_name(str))==NULL)
152
			continue;
153
		if(section_match(p, section, /* case-sensitive */TRUE))
rswindell's avatar
rswindell committed
154
			return(TRUE);
155
	}
156
157
158
159
160
161
162
163
164
165
166
167
168
169

	/* Then perform case-insensitive search */
	rewind(fp);
	while (!feof(fp)) {
		if (fgets(str, sizeof(str), fp) == NULL)
			break;
		if (is_eof(str))
			break;
		if ((p = section_name(str)) == NULL)
			continue;
		if (section_match(p, section, /* case-sensitive */FALSE))
			return(TRUE);
	}

170
171
172
	return(FALSE);
}

173
174
175
176
177
178
static size_t find_section_index(str_list_t list, const char* section)
{
	char*	p;
	char	str[INI_MAX_VALUE_LEN];
	size_t	i;

179
180
181
	/* Perform case-sensitive search first */
	for (i = 0; list[i] != NULL; i++) {
		SAFECOPY(str, list[i]);
182
183
		if(is_eof(str))
			return(strListCount(list));
184
185
186
187
188
189
190
191
192
193
		if((p=section_name(str))!=NULL && section_match(p, section, /* case-sensitive */TRUE))
			return(i);
	}

	/* Then perform case-insensitive search */
	for (i = 0; list[i] != NULL; i++) {
		SAFECOPY(str, list[i]);
		if (is_eof(str))
			return(strListCount(list));
		if ((p = section_name(str)) != NULL && section_match(p, section, /* case-sensitive */FALSE))
194
			return(i);
195
196
197
198
199
	}

	return(i);
}

200
static size_t find_section(str_list_t list, const char* section)
201
202
203
204
205
206
207
208
209
210
211
212
{
	size_t	i;

	if(section==ROOT_SECTION)
		return(0);

	i=find_section_index(list,section);
	if(list[i]!=NULL)
		i++;
	return(i);
}

213
static char* key_name(char* p, char** vp, BOOL literals_supported)
214
{
215
216
	char* equal;
	char* colon;
217
	char* tp;
218

219
    *vp=NULL;
220

221
222
223
	if(p==NULL)
		return(NULL);

224
225
	/* Parse value name */
	SKIP_WHITESPACE(p);
226
	if(*p==INI_COMMENT_CHAR)
227
		return(NULL);
228
229
230
231
232
233
234
235
236
237
	if(*p==INI_OPEN_SECTION_CHAR)
		return(INI_NEW_SECTION);
	equal=strchr(p,'=');
	colon=strchr(p,':');
	if(colon==NULL || (equal!=NULL && equal<colon)) {
		*vp=equal;
		colon=NULL;
	} else
		*vp=colon;

238
239
	if(*vp==NULL)
		return(NULL);
240

241
242
243
244
245
246
	*(*vp)=0;
	truncsp(p);

	/* Parse value */
	(*vp)++;
	SKIP_WHITESPACE(*vp);
247
	if(literals_supported && colon!=NULL) {		/* string literal value */
248
		truncnl(*vp);		/* "key : value" - truncate new-line chars only */
rswindell's avatar
rswindell committed
249
250
		if(*(*vp) == '"') {	/* handled quoted-strings here */
			(*vp)++;
251
			tp = strrchr(*vp, '"');
rswindell's avatar
rswindell committed
252
253
254
255
256
257
			if(tp != NULL) {
				*tp = 0;
			}
		}
		c_unescape_str(*vp);
	} else
258
		truncsp(*vp);		/* "key = value" - truncate all white-space chars */
259
260
261
262

	return(p);
}

263
static char* read_value(FILE* fp, const char* section, const char* key, char* value, BOOL literals_supported)
264
265
{
	char*	p;
266
	char*	vp=NULL;
267
	char	str[INI_MAX_LINE_LEN];
268

269
270
271
	if(fp==NULL)
		return(NULL);

272
	if(!seek_section(fp,section))
273
274
275
		return(NULL);

	while(!feof(fp)) {
276
		if(fgets(str,sizeof(str),fp)==NULL)
277
			break;
278
279
		if(is_eof(str))
			break;
280
		if((p=key_name(str, &vp, literals_supported))==NULL)
281
			continue;
282
		if(p==INI_NEW_SECTION)
283
284
285
			break;
		if(stricmp(p,key)!=0)
			continue;
286
287
		if(vp==NULL)
			break;
288
		/* key found */
289
		sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,vp);
290
291
292
293
294
295
		return(value);
	}

	return(NULL);
}

296
static size_t get_value(str_list_t list, const char* section, const char* key, char* value, char** vpp, BOOL literals_supported)
297
{
298
	char    str[INI_MAX_LINE_LEN];
299
300
301
302
	char*	p;
	char*	vp;
	size_t	i;

303
304
305
306
	if(value!=NULL)
		value[0]=0;
	if(vpp!=NULL)
		*vpp=NULL;
307
308
309
	if(list==NULL)
		return 0;

310
	for(i=find_section(list, section); list[i]!=NULL; i++) {
311
		SAFECOPY(str,list[i]);
312
313
		if(is_eof(str))
			break;
314
		if((p=key_name(str, &vp, literals_supported))==NULL)
315
			continue;
316
		if(p==INI_NEW_SECTION)
317
			break;
318
319
		if(stricmp(p,key)!=0)
			continue;
320
321
322
		if(value!=NULL)
			sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,vp);
		if(vpp!=NULL)
rswindell's avatar
rswindell committed
323
			*vpp=list[i] + (vp - str);
324
		return(i);
325
326
327
328
329
	}

	return(i);
}

deuce's avatar
deuce committed
330
BOOL DLLCALL iniSectionExists(str_list_t list, const char* section)
rswindell's avatar
rswindell committed
331
332
333
334
335
336
{
	size_t	i;

	if(section==ROOT_SECTION)
		return(TRUE);

337
338
	i=find_section_index(list,section);
	return(list[i]!=NULL);
rswindell's avatar
rswindell committed
339
340
}

deuce's avatar
deuce committed
341
str_list_t DLLCALL	iniGetSection(str_list_t list, const char *section)
342
343
{
	size_t		i;
344
	str_list_t	retval;
345
346
	char		*p;

347
	if(list==NULL)
348
349
350
351
352
		return(NULL);

	if((retval=strListInit())==NULL)
		return(NULL);

353
	i=find_section(list,section);
354
355
356
357
358
	if(list[i]!=NULL) {
		strListPush(&retval, list[i]);
		for(i++;list[i]!=NULL;i++) {
			p=list[i];
			SKIP_WHITESPACE(p);
deuce's avatar
deuce committed
359
			if(*p==INI_OPEN_SECTION_CHAR)
360
361
362
363
364
365
366
				break;
			strListPush(&retval, list[i]);
		}
	}
	return(retval);
}

deuce's avatar
deuce committed
367
BOOL DLLCALL iniKeyExists(str_list_t list, const char* section, const char* key)
368
369
370
{
	size_t	i;

371
372
373
	if(list==NULL)
		return(FALSE);

374
	i=get_value(list, section, key, NULL, NULL, /* literals_supported: */FALSE);
375

376
	if(list[i]==NULL || *(list[i])==INI_OPEN_SECTION_CHAR)
377
378
379
380
381
		return(FALSE);

	return(TRUE);
}

deuce's avatar
deuce committed
382
BOOL DLLCALL iniValueExists(str_list_t list, const char* section, const char* key)
383
{
384
	char*	vp=NULL;
385

386
	get_value(list, section, key, NULL, &vp, /* literals_supported: */FALSE);
387

388
	return(vp!=NULL && *vp!=0);
389
390
}

deuce's avatar
deuce committed
391
BOOL DLLCALL iniRemoveKey(str_list_t* list, const char* section, const char* key)
392
393
{
	size_t	i;
394
	char*	vp=NULL;
395

396
	i=get_value(*list, section, key, NULL, &vp, /* literals_supported: */FALSE);
397

398
	if(vp==NULL)
399
400
401
402
403
		return(FALSE);

	return(strListDelete(list,i));
}

deuce's avatar
deuce committed
404
BOOL DLLCALL iniRemoveValue(str_list_t* list, const char* section, const char* key)
405
{
406
	char*	vp=NULL;
407

408
	get_value(*list, section, key, NULL, &vp, /* literals_supported: */FALSE);
409

410
	if(vp==NULL)
411
412
		return(FALSE);

413
	*vp=0;
414
415
416
	return(TRUE);
}

deuce's avatar
deuce committed
417
BOOL DLLCALL iniRemoveSection(str_list_t* list, const char* section)
rswindell's avatar
rswindell committed
418
419
420
421
422
423
424
425
426
427
428
429
430
{
	size_t	i;

	i=find_section_index(*list,section);
	if((*list)[i]==NULL)	/* not found */
		return(FALSE);
	do {
		strListDelete(list,i);
	} while((*list)[i]!=NULL && *(*list)[i]!=INI_OPEN_SECTION_CHAR);

	return(TRUE);
}

431
432
BOOL DLLCALL iniRemoveSections(str_list_t* list, const char* prefix)
{
433
	str_list_t sections;
434
435
	const char* section;

deuce's avatar
deuce committed
436
	if (list == NULL)
deuce's avatar
deuce committed
437
438
		return FALSE;
	sections = iniGetSectionList(*list, prefix);
439
440
441
442
443
444
445
446
447
	while((section = strListPop(&sections)) != NULL)
		if(!iniRemoveSection(list, section))
			return(FALSE);

	strListFree(&sections);

	return(TRUE);
}

deuce's avatar
deuce committed
448
BOOL DLLCALL iniRenameSection(str_list_t* list, const char* section, const char* newname)
rswindell's avatar
rswindell committed
449
450
451
452
453
454
455
{
	char	str[INI_MAX_LINE_LEN];
	size_t	i;

	if(section==ROOT_SECTION)
		return(FALSE);

456
	if(stricmp(section, newname)!=0) {
457
458
459
460
		i=find_section_index(*list,newname);
		if((*list)[i]!=NULL)	/* duplicate */
			return(FALSE);
	}
461

rswindell's avatar
rswindell committed
462
463
464
465
	i=find_section_index(*list,section);
	if((*list)[i]==NULL)	/* not found */
		return(FALSE);

466
	SAFEPRINTF(str,"[%s]",newname);
rswindell's avatar
rswindell committed
467
468
469
	return(strListReplace(*list, i, str)!=NULL);
}

470
471
static size_t ini_add_section(str_list_t* list, const char* section
					,ini_style_t* style, size_t index)
472
473
474
{
	char	str[INI_MAX_LINE_LEN];

rswindell's avatar
rswindell committed
475
	if(section==ROOT_SECTION)
476
477
		return(0);

478
479
	if((*list)[index]!=NULL)
		return(index);
480

481
482
483
484
485
486
487
488
489
490
	if(style==NULL)
		style=&default_style;
	if(index > 0 && style->section_separator!=NULL)
		strListAppend(list, style->section_separator, index++);
	SAFEPRINTF(str,"[%s]",section);
	strListAppend(list, str, index);

	return(index);
}

deuce's avatar
deuce committed
491
size_t DLLCALL iniAddSection(str_list_t* list, const char* section, ini_style_t* style)
492
493
494
495
496
497
498
{
	if(section==ROOT_SECTION)
		return(0);

	return ini_add_section(list,section,style,find_section_index(*list, section));
}

deuce's avatar
deuce committed
499
size_t DLLCALL iniAppendSection(str_list_t* list, const char* section, ini_style_t* style)
500
501
502
503
504
{
	if(section==ROOT_SECTION)
		return(0);

	return ini_add_section(list,section,style,strListCount(*list));
505
506
}

rswindell's avatar
rswindell committed
507
508
509
510
511
512
513
514
515
516
517
static BOOL str_contains_ctrl_char(const char* str)
{
	while(*str) {
		if(*(unsigned char*)str < ' ')
			return TRUE;
		str++;
	}
	return FALSE;
}

static char* ini_set_string(str_list_t* list, const char* section, const char* key, const char* value, BOOL literal
518
519
520
				 ,ini_style_t* style)
{
	char	str[INI_MAX_LINE_LEN];
rswindell's avatar
rswindell committed
521
	char	litstr[INI_MAX_VALUE_LEN];
522
	char	curval[INI_MAX_VALUE_LEN];
rswindell's avatar
rswindell committed
523
	const char*	value_separator;
524
525
	size_t	i;

526
527
528
529
	if(style==NULL)
		style=&default_style;

	iniAddSection(list, section, style);
530
531
532
533
534
535
536

	if(key==NULL)
		return(NULL);
	if(style->key_prefix==NULL)
		style->key_prefix="";
	if(style->value_separator==NULL)
		style->value_separator="=";
rswindell's avatar
rswindell committed
537
538
	if(style->literal_separator==NULL)
		style->literal_separator=":";
539
540
	if(value==NULL)
		value="";
rswindell's avatar
rswindell committed
541
542
543
544
545
546
547
	if(literal) {
		char cstr[INI_MAX_VALUE_LEN];
		SAFEPRINTF(litstr, "\"%s\"", c_escape_str(value, cstr, sizeof(cstr)-1, /* ctrl_only: */FALSE));
		value = litstr;
		value_separator = style->literal_separator;
	} else
		value_separator = style->value_separator;
548
	safe_snprintf(str, sizeof(str), "%s%-*s%s%s"
rswindell's avatar
rswindell committed
549
		,style->key_prefix, style->key_len, key, value_separator, value);
550
	i=get_value(*list, section, key, curval, NULL, /* literals_supported: */literal);
551
552
	if((*list)[i]==NULL || *(*list)[i]==INI_OPEN_SECTION_CHAR) {
        while(i && *(*list)[i-1]==0) i--;   /* Insert before blank lines, not after */
553
		return strListInsert(list, str, i);
554
    }
555

556
557
558
	if(strcmp(curval,value)==0)
		return((*list)[i]);	/* no change */

559
560
	return strListReplace(*list, i, str);
}
rswindell's avatar
rswindell committed
561
562
563
564
565
566
567
568
569
570
571
572
573
char* DLLCALL iniSetString(str_list_t* list, const char* section, const char* key, const char* value
				 ,ini_style_t* style)
{
	BOOL literal = value != NULL && (str_contains_ctrl_char(value) || *value==' ' || *lastchar(value)==' ');
		
	return ini_set_string(list, section, key, value, literal, style);
}

char* DLLCALL iniSetStringLiteral(str_list_t* list, const char* section, const char* key, const char* value
				 ,ini_style_t* style)
{
	return ini_set_string(list, section, key, value, /* literal: */TRUE, style);
}
574

575
576
577
578
579
580
char* DLLCALL iniSetValue(str_list_t* list, const char* section, const char* key, const char* value
				 ,ini_style_t* style)
{
	return ini_set_string(list, section, key, value, /* literal: */FALSE, style);
}

deuce's avatar
deuce committed
581
char* DLLCALL iniSetInteger(str_list_t* list, const char* section, const char* key, long value
582
583
584
585
					,ini_style_t* style)
{
	char	str[INI_MAX_VALUE_LEN];

586
	SAFEPRINTF(str,"%ld",value);
587
588
589
	return iniSetString(list, section, key, str, style);
}

deuce's avatar
deuce committed
590
char* DLLCALL iniSetShortInt(str_list_t* list, const char* section, const char* key, ushort value
591
592
593
594
					,ini_style_t* style)
{
	char	str[INI_MAX_VALUE_LEN];

595
	SAFEPRINTF(str,"%hu",value);
596
597
598
	return iniSetString(list, section, key, str, style);
}

deuce's avatar
deuce committed
599
char* DLLCALL iniSetLongInt(str_list_t* list, const char* section, const char* key, ulong value
600
601
602
603
604
605
606
607
					,ini_style_t* style)
{
	char	str[INI_MAX_VALUE_LEN];

	SAFEPRINTF(str,"%lu",value);
	return iniSetString(list, section, key, str, style);
}

deuce's avatar
deuce committed
608
char* DLLCALL iniSetHexInt(str_list_t* list, const char* section, const char* key, ulong value
609
610
611
612
					,ini_style_t* style)
{
	char	str[INI_MAX_VALUE_LEN];

613
	SAFEPRINTF(str,"0x%lx",value);
614
615
616
	return iniSetString(list, section, key, str, style);
}

deuce's avatar
deuce committed
617
char* DLLCALL iniSetFloat(str_list_t* list, const char* section, const char* key, double value
618
619
620
621
					,ini_style_t* style)
{
	char	str[INI_MAX_VALUE_LEN];

622
	SAFEPRINTF(str,"%g",value);
623
624
625
	return iniSetString(list, section, key, str, style);
}

deuce's avatar
deuce committed
626
char* DLLCALL iniSetBytes(str_list_t* list, const char* section, const char* key, ulong unit
627
					,int64_t value, ini_style_t* style)
628
629
630
{
	char	str[INI_MAX_VALUE_LEN];

631
	if(value==0)
rswindell's avatar
rswindell committed
632
		SAFECOPY(str,"0");
633
634
635
636
637
638
639
640
641
642
643
644
645
646
	else
		switch(unit) {
			case 1024*1024*1024:
				SAFEPRINTF(str,"%"PRIi64"G",value);
				break;
			case 1024*1024:
				SAFEPRINTF(str,"%"PRIi64"M",value);
				break;
			case 1024:
				SAFEPRINTF(str,"%"PRIi64"K",value);
				break;
			default:
				if(unit<1)
					unit=1;
647
				byte_count_to_str(value*unit, str, sizeof(str));
648
		}
649
650
651
652

	return iniSetString(list, section, key, str, style);
}

653
654
655
656
657
char* DLLCALL iniSetDuration(str_list_t* list, const char* section, const char* key
					,double value, ini_style_t* style)
{
	char	str[INI_MAX_VALUE_LEN];

658
	return iniSetString(list, section, key, duration_to_str(value, str, sizeof(str)), style);
659
660
661
}


662
#if !defined(NO_SOCKET_SUPPORT)
deuce's avatar
deuce committed
663
char* DLLCALL iniSetIpAddress(str_list_t* list, const char* section, const char* key, ulong value
664
665
666
					,ini_style_t* style)
{
	struct in_addr in_addr;
667
	in_addr.s_addr=htonl(value);
668
669
	return iniSetString(list, section, key, inet_ntoa(in_addr), style);
}
deuce's avatar
deuce committed
670

deuce's avatar
deuce committed
671
char* DLLCALL iniSetIp6Address(str_list_t* list, const char* section, const char* key, struct in6_addr value
deuce's avatar
deuce committed
672
673
					,ini_style_t* style)
{
674
	char				addrstr[INET6_ADDRSTRLEN];
675
	union xp_sockaddr	addr = {{0}};
deuce's avatar
deuce committed
676

677
678
679
680
	addr.in6.sin6_addr = value;
	addr.in6.sin6_family = AF_INET6;
	inet_addrtop(&addr, addrstr, sizeof(addrstr));
	return iniSetString(list, section, key, addrstr, style);
deuce's avatar
deuce committed
681
}
682
#endif
683

deuce's avatar
deuce committed
684
char* DLLCALL iniSetBool(str_list_t* list, const char* section, const char* key, BOOL value
685
686
687
688
689
					,ini_style_t* style)
{
	return iniSetString(list, section, key, value ? "true":"false", style);
}

deuce's avatar
deuce committed
690
char* DLLCALL iniSetDateTime(str_list_t* list, const char* section, const char* key
691
692
693
					 ,BOOL include_time, time_t value, ini_style_t* style)
{
	char	str[INI_MAX_VALUE_LEN];
694
	char	tstr[32];
695
696
	char*	p;

697
698
	if(value==0)
		SAFECOPY(str,"Never");
699
	else if((p=ctime_r(&value,tstr))==NULL)
700
701
702
703
704
705
706
707
708
		SAFEPRINTF(str,"0x%lx",value);
	else if(!include_time)	/* reformat into "Mon DD YYYY" */
		safe_snprintf(str,sizeof(str),"%.3s %.2s %.4s"		,p+4,p+8,p+20);
	else					/* reformat into "Mon DD YYYY HH:MM:SS" */
		safe_snprintf(str,sizeof(str),"%.3s %.2s %.4s %.8s"	,p+4,p+8,p+20,p+11);

	return iniSetString(list, section, key, str, style);
}

deuce's avatar
deuce committed
709
char* DLLCALL iniSetEnum(str_list_t* list, const char* section, const char* key, str_list_t names, unsigned value
710
711
712
713
714
					,ini_style_t* style)
{
	if(value < strListCount(names))
		return iniSetString(list, section, key, names[value], style);

715
716
717
	return iniSetLongInt(list, section, key, value, style);
}

deuce's avatar
deuce committed
718
char* DLLCALL iniSetEnumList(str_list_t* list, const char* section, const char* key
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
					,const char* sep, str_list_t names, unsigned* val_list, unsigned count, ini_style_t* style)
{
	char	value[INI_MAX_VALUE_LEN];
	size_t	i;
	size_t	name_count;

	value[0]=0;

	if(sep==NULL)
		sep=",";

	if(val_list!=NULL) {
		name_count = strListCount(names);
		for(i=0; i < count; i++) {
			if(value[0])
				strcat(value,sep);
			if(val_list[i] < name_count)
				strcat(value, names[val_list[i]]);
			else
				sprintf(value + strlen(value), "%u", val_list[i]);
		}
	}

	return iniSetString(list, section, key, value, style);
}

deuce's avatar
deuce committed
745
char* DLLCALL iniSetNamedInt(str_list_t* list, const char* section, const char* key, named_long_t* names
746
747
748
749
750
751
752
753
754
755
756
					 ,long value, ini_style_t* style)
{
	size_t	i;

	for(i=0;names[i].name!=NULL;i++)
		if(names[i].value==value)
			return iniSetString(list, section, key, names[i].name, style);

	return iniSetInteger(list, section, key, value, style);
}

757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
char* DLLCALL iniSetNamedHexInt(str_list_t* list, const char* section, const char* key, named_ulong_t* names
					 ,ulong value, ini_style_t* style)
{
	size_t	i;

	for(i=0;names[i].name!=NULL;i++)
		if(names[i].value==value)
			return iniSetString(list, section, key, names[i].name, style);

	return iniSetHexInt(list, section, key, value, style);
}

char* DLLCALL iniSetNamedLongInt(str_list_t* list, const char* section, const char* key, named_ulong_t* names
					 ,ulong value, ini_style_t* style)
{
	size_t	i;

	for(i=0;names[i].name!=NULL;i++)
		if(names[i].value==value)
			return iniSetString(list, section, key, names[i].name, style);

	return iniSetLongInt(list, section, key, value, style);
}

deuce's avatar
deuce committed
781
char* DLLCALL iniSetNamedFloat(str_list_t* list, const char* section, const char* key, named_double_t* names
782
783
784
785
786
787
788
789
790
					 ,double value, ini_style_t* style)
{
	size_t	i;

	for(i=0;names[i].name!=NULL;i++)
		if(names[i].value==value)
			return iniSetString(list, section, key, names[i].name, style);

	return iniSetFloat(list, section, key, value, style);
791
792
}

deuce's avatar
deuce committed
793
char* DLLCALL iniSetBitField(str_list_t* list, const char* section, const char* key
794
					 ,ini_bitdesc_t* bitdesc, ulong value, ini_style_t* style)
795
796
797
798
{
	char	str[INI_MAX_VALUE_LEN];
	int		i;

799
800
	if(style==NULL)
		style=&default_style;
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
	if(style->bit_separator==NULL)
		style->bit_separator="|";
	str[0]=0;
	for(i=0;bitdesc[i].name;i++) {
		if((value&bitdesc[i].bit)==0)
			continue;
		if(str[0])
			strcat(str,style->bit_separator);
		strcat(str,bitdesc[i].name);
		value&=~bitdesc[i].bit;
	}
	if(value) {	/* left over bits? */
		if(str[0])
			strcat(str,style->bit_separator);
		sprintf(str+strlen(str), "0x%lX", value);
	}
	return iniSetString(list, section, key, str, style);
}

deuce's avatar
deuce committed
820
char* DLLCALL iniSetStringList(str_list_t* list, const char* section, const char* key
821
822
823
824
825
826
827
					,const char* sep, str_list_t val_list, ini_style_t* style)
{
	char	value[INI_MAX_VALUE_LEN];

	if(sep==NULL)
		sep=",";

828
	return iniSetString(list, section, key, strListCombine(val_list, value, sizeof(value), sep), style);
829
830
}

831
832
static char* default_value(const char* deflt, char* value)
{
833
	if(deflt!=NULL && deflt!=value && value!=NULL)
834
835
		sprintf(value,"%.*s",INI_MAX_VALUE_LEN-1,deflt);

rswindell's avatar
rswindell committed
836
	return((char*)deflt);
837
838
}

839
/* Supports string literals: */
deuce's avatar
deuce committed
840
char* DLLCALL iniReadString(FILE* fp, const char* section, const char* key, const char* deflt, char* value)
841
{
842
	if(read_value(fp, section, key, value, /* literals_supported: */TRUE)==NULL || *value==0 /* blank */)
843
		return default_value(deflt,value);
844

845
846
847
	return(value);
}

848
849
850
851
852
853
854
855
856
857
/* Does NOT support string literals: */
char* DLLCALL iniReadValue(FILE* fp, const char* section, const char* key, const char* deflt, char* value)
{
	if(read_value(fp, section, key, value, /* literals_supported: */FALSE)==NULL || *value==0 /* blank */)
		return default_value(deflt,value);

	return(value);
}

/* Supports string literals: */
deuce's avatar
deuce committed
858
char* DLLCALL iniGetString(str_list_t list, const char* section, const char* key, const char* deflt, char* value)
859
{
860
861
	char*	vp=NULL;

862
	get_value(list, section, key, value, &vp, /* literals_supported: */TRUE);
863

864
	if(vp==NULL || *vp==0 /* blank value or missing key */)
865
866
		return default_value(deflt,value);

867
868
869
	if(value != NULL)	/* return the modified (trimmed) value */
		return value;

870
	return(vp);
871
872
}

873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
/* Does NOT support string literals: */
char* DLLCALL iniGetValue(str_list_t list, const char* section, const char* key, const char* deflt, char* value)
{
	char*	vp=NULL;

	get_value(list, section, key, value, &vp, /* literals_supported: */FALSE);

	if(vp==NULL || *vp==0 /* blank value or missing key */)
		return default_value(deflt,value);

	if(value != NULL)	/* return the modified (trimmed) value */
		return value;

	return(vp);
}

/* Does NOT support string literals: */
deuce's avatar
deuce committed
890
char* DLLCALL iniPopKey(str_list_t* list, const char* section, const char* key, char* value)
891
{
892
	size_t i;
893

894
	if(list==NULL || *list==NULL)
895
896
		return NULL;

897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
	i=get_value(*list, section, key, value, NULL, /* literals_supported: */FALSE);

	if((*list)[i]==NULL)
		return NULL;

	strListDelete(list,i);

	return(value);
}

/* Supports string literals: */
char* DLLCALL iniPopString(str_list_t* list, const char* section, const char* key, char* value)
{
	size_t i;

	if(list==NULL || *list==NULL)
		return NULL;

	i=get_value(*list, section, key, value, NULL, /* literals_supported: */TRUE);
916

917
	if((*list)[i]==NULL)
918
919
920
921
922
923
924
		return NULL;

	strListDelete(list,i);

	return(value);
}

deuce's avatar
deuce committed
925
char* DLLCALL iniReadExistingString(FILE* fp, const char* section, const char* key, const char* deflt, char* value)
926
{
927
	if(read_value(fp,section,key,value, /* literals_supported: */TRUE)==NULL)
928
929
930
931
932
933
934
935
		return(NULL);

	if(*value==0 /* blank */)
		return default_value(deflt,value);

	return(value);
}

deuce's avatar
deuce committed
936
char* DLLCALL iniGetExistingString(str_list_t list, const char* section, const char* key, const char* deflt, char* value)
937
{
938
	if(!iniKeyExists(list, section, key))
939
940
		return(NULL);

941
	return iniGetString(list, section, key, deflt, value);
942
943
}

944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
char* DLLCALL iniReadExistingValue(FILE* fp, const char* section, const char* key, const char* deflt, char* value)
{
	if(read_value(fp,section,key,value, /* literals_supported: */FALSE)==NULL)
		return(NULL);

	if(*value==0 /* blank */)
		return default_value(deflt,value);

	return(value);
}

char* DLLCALL iniGetExistingValue(str_list_t list, const char* section, const char* key, const char* deflt, char* value)
{
	if(!iniKeyExists(list, section, key))
		return(NULL);

	return iniGetValue(list, section, key, deflt, value);
}


964
965
966
static str_list_t splitList(char* list, const char* sep)
{
	char*		token;
967
	char*		tmp;
968
969
	ulong		items=0;
	str_list_t	lp;
970

971
	if((lp=strListInit())==NULL)
972
973
		return(NULL);

974
975
976
	if(sep==NULL)
		sep=",";

977
	token=strtok_r(list,sep,&tmp);
978
	while(token!=NULL) {
rswindell's avatar
rswindell committed
979
		SKIP_WHITESPACE(token);
980
		truncsp(token);
981
		if(strListAppend(&lp,token,items++)==NULL)
982
			break;
983
		token=strtok_r(NULL,sep,&tmp);
984
	}
985

986
987
988
	return(lp);
}

deuce's avatar
deuce committed
989
str_list_t DLLCALL iniReadStringList(FILE* fp, const char* section, const char* key
990
991
992
993
994
995
						 ,const char* sep, const char* deflt)
{
	char*	value;
	char	buf[INI_MAX_VALUE_LEN];
	char	list[INI_MAX_VALUE_LEN];

996
	if((value=read_value(fp,section,key,buf, /* literals_supported: */TRUE))==NULL || *value==0 /* blank */)
997
998
		value=(char*)deflt;

rswindell's avatar
rswindell committed
999
1000
1001
	if(value==NULL)
		return(NULL);

1002
1003
1004
1005
1006
	SAFECOPY(list,value);

	return(splitList(list,sep));
}

deuce's avatar
deuce committed
1007
str_list_t DLLCALL iniGetStringList(str_list_t list, const char* section, const char* key
1008
1009
						 ,const char* sep, const char* deflt)
{
1010
	char	value[INI_MAX_VALUE_LEN];
1011

1012
	get_value(list, section, key, value, NULL, /* literals_supported: */TRUE);
1013

1014
1015
1016
	if(*value==0 /* blank value or missing key */) {
		if(deflt==NULL)
			return(NULL);
1017
		SAFECOPY(value,deflt);
1018
	}
1019

1020
	return(splitList(value,sep));
1021
1022
}

deuce's avatar
deuce committed
1023
void* DLLCALL iniFreeStringList(str_list_t list)
1024
{
1025
1026
	strListFree(&list);
	return(list);
1027
}
1028

deuce's avatar
deuce committed
1029
void* DLLCALL iniFreeNamedStringList(named_string_t** list)
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
{
	ulong	i;

	if(list==NULL)
		return(NULL);

	for(i=0;list[i]!=NULL;i++) {
		if(list[i]->name!=NULL)
			free(list[i]->name);
		if(list[i]->value!=NULL)
			free(list[i]->value);
		free(list[i]);
	}

	free(list);
	return(NULL);
}

deuce's avatar
deuce committed
1048
str_list_t DLLCALL iniReadSectionList(FILE* fp, const char* prefix)
1049
1050
{
	char*	p;
1051
	char	str[INI_MAX_LINE_LEN];
1052
	ulong	items=0;
1053
	str_list_t	lp;
1054

1055
	if((lp=strListInit())==NULL)
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
		return(NULL);

	if(fp==NULL)
		return(lp);

	rewind(fp);

	while(!feof(fp)) {
		if(fgets(str,sizeof(str),fp)==NULL)
			break;
1066
1067
		if(is_eof(str))
			break;
1068
		if((p=section_name(str))==NULL)
1069
			continue;
1070
		if(prefix!=NULL)
1071
1072
			if(strnicmp(p,prefix,strlen(prefix))!=0)
				continue;
1073
		if(strListAppend(&lp,p,items++)==NULL)
1074
1075
1076
1077
1078
1079
			break;
	}

	return(lp);
}

deuce's avatar
deuce committed
1080
str_list_t DLLCALL iniGetSectionList(str_list_t list, const char* prefix)
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
{
	char*	p;
	char	str[INI_MAX_LINE_LEN];
	ulong	i,items=0;
	str_list_t	lp;

	if((lp=strListInit())==NULL)