Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
9169 turbocat 1
/*
2
 * OpenTyrian: A modern cross-platform port of Tyrian
3
 * Copyright (C) 2015  The OpenTyrian Development Team
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
 */
19
/*!
20
 * \file config_file.c
21
 * \author Carl Reinke
22
 * \date 2015
23
 * \copyright GNU General Public License v2+ or Mozilla Public License 2.0
24
 */
25
#include "config_file.h"
26
 
27
#include 
28
#include 
29
#include 
30
#include 
31
#include 
32
 
33
/* potential size of decimal representation of type */
34
#define udecsizeof(t) ((CHAR_BIT * sizeof(t) / 3) + 1)
35
#define sdecsizeof(t) (udecsizeof(t) + 1)
36
 
37
extern void config_oom( void );
38
 
39
void config_oom( void )
40
{
41
	fprintf(stderr, "out of memory\n");
42
	exit(EXIT_FAILURE);
43
}
44
 
45
/* string manipulators */
46
 
47
static ConfigString string_init_len( const char *s, size_t n )
48
{
49
	ConfigString string;
50
 
51
	if (s == NULL)
52
	{
53
		CONFIG_STRING_LONG_TAG(string) = true;
54
 
55
		string.long_buf = NULL;
56
	}
57
	else
58
	{
59
		char is_long = n >= COUNTOF(string.short_buf);
60
 
61
		CONFIG_STRING_LONG_TAG(string) = is_long;
62
 
63
		char *buffer = is_long ?
64
			string.long_buf = malloc((n + 1) * sizeof(char)) :
65
			string.short_buf;
66
		if (buffer == NULL)
67
			config_oom();
68
 
69
		memcpy(buffer, s, n * sizeof(char));
70
		buffer[n] = '\0';
71
	}
72
 
73
	return string;
74
}
75
 
76
static void string_deinit( ConfigString *string )
77
{
78
	char is_long = CONFIG_STRING_LONG_TAG(*string);
79
 
80
	if (is_long)
81
	{
82
		free(string->long_buf);
83
		string->long_buf = NULL;
84
	}
85
}
86
 
87
static bool string_equal_len( ConfigString *string, const char *s, size_t n )
88
{
89
	const char *cstr = config_string_to_cstr(string);
90
	return strncmp(cstr, s, n) == 0 && cstr[n] == '\0';
91
}
92
 
93
/* config manipulators */
94
 
95
static void deinit_section( ConfigSection *section );
96
static void deinit_option( ConfigOption *option );
97
 
98
void config_init( Config *config )
99
{
100
	assert(config != NULL);
101
 
102
	config->sections_count = 0;
103
	config->sections = NULL;
104
}
105
 
106
void config_deinit( Config *config )
107
{
108
	assert(config != NULL);
109
 
110
	for (unsigned int s = 0; s < config->sections_count; ++s)
111
	{
112
		ConfigSection *section = &config->sections[s];
113
 
114
		deinit_section(section);
115
	}
116
 
117
	free(config->sections);
118
	config->sections = NULL;
119
}
120
 
121
/* config section manipulators -- internal */
122
 
123
static void init_section( ConfigSection *section, const char *type, size_t type_len, const char *name, size_t name_len )
124
{
125
	section->type = string_init_len(type, type_len);
126
	section->name = string_init_len(name, name_len);
127
	section->options_count = 0;
128
	section->options = NULL;
129
}
130
 
131
static void deinit_section( ConfigSection *section )
132
{
133
	for (unsigned int o = 0; o < section->options_count; ++o)
134
	{
135
		ConfigOption *option = §ion->options[o];
136
 
137
		deinit_option(option);
138
	}
139
 
140
	string_deinit(§ion->type);
141
	string_deinit(§ion->name);
142
 
143
	free(section->options);
144
	section->options = NULL;
145
}
146
 
147
/* config section accessors/manipulators -- by type, name */
148
 
149
ConfigSection *config_add_section_len( Config *config, const char *type, size_t type_len, const char *name, size_t name_len )
150
{
151
	assert(config != NULL);
152
	assert(type != NULL);
153
 
154
	ConfigSection *sections = realloc(config->sections, (config->sections_count + 1) * sizeof(ConfigSection));
155
	if (sections == NULL)
156
		return NULL;
157
 
158
	ConfigSection *section = §ions[config->sections_count];
159
 
160
	config->sections_count += 1;
161
	config->sections = sections;
162
 
163
	init_section(section, type, type_len, name, name_len);
164
 
165
	return section;
166
}
167
 
168
ConfigSection *config_find_sections( Config *config, const char *type, ConfigSection **save )
169
{
170
	assert(config != NULL);
171
	assert(type != NULL);
172
 
173
	ConfigSection *sections_end = &config->sections[config->sections_count];
174
 
175
	ConfigSection *section = save != NULL && *save != NULL ?
176
		*save :
177
		&config->sections[0];
178
 
179
	for (; section < sections_end; ++section)
180
		if (strcmp(config_string_to_cstr(§ion->type), type) == 0)
181
			break;
182
 
183
	if (save != NULL)
184
		*save = section;
185
 
186
	return section < sections_end ? section : NULL;
187
}
188
 
189
ConfigSection *config_find_section( Config *config, const char *type, const char *name )
190
{
191
	assert(config != NULL);
192
	assert(type != NULL);
193
 
194
	ConfigSection *sections_end = &config->sections[config->sections_count];
195
 
196
	for (ConfigSection *section = &config->sections[0]; section < sections_end; ++section)
197
	{
198
		if (strcmp(config_string_to_cstr(§ion->type), type) == 0)
199
		{
200
			const char *section_name = config_string_to_cstr(§ion->name);
201
			if ((section_name == NULL || name == NULL) ? section_name == name : strcmp(config_string_to_cstr(§ion->name), name) == 0)
202
				return section;
203
		}
204
	}
205
 
206
	return NULL;
207
}
208
 
209
ConfigSection *config_find_or_add_section( Config *config, const char *type, const char *name )
210
{
211
	assert(config != NULL);
212
	assert(type != NULL);
213
 
214
	ConfigSection *section = config_find_section(config, type, name);
215
 
216
	if (section != NULL)
217
		return section;
218
 
219
	return config_add_section(config, type, name);
220
}
221
 
222
/* config option manipulators -- internal */
223
 
224
static void init_option_value( ConfigOption *option, const char *value, size_t value_len )
225
{
226
	option->values_count = 0;
227
	option->v.value = string_init_len(value, value_len);
228
}
229
 
230
static void deinit_option_value( ConfigOption *option )
231
{
232
	if (option->values_count != 0)
233
	{
234
		ConfigString *values_end = &option->v.values[option->values_count];
235
		for (ConfigString *value = &option->v.values[0]; value < values_end; ++value)
236
			string_deinit(value);
237
 
238
		free(option->v.values);
239
		option->v.values = NULL;
240
	}
241
	else
242
	{
243
		string_deinit(&option->v.value);
244
	}
245
}
246
 
247
static void init_option( ConfigOption *option, const char *key, size_t key_len, const char *value, size_t value_len )
248
{
249
	option->key = string_init_len(key, key_len);
250
	init_option_value(option, value, value_len);
251
}
252
 
253
static void deinit_option( ConfigOption *option )
254
{
255
	string_deinit(&option->key);
256
	deinit_option_value(option);
257
}
258
 
259
static ConfigOption *append_option( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
260
{
261
	ConfigOption *options = realloc(section->options, (section->options_count + 1) * sizeof(ConfigSection));
262
	if (options == NULL)
263
		return NULL;
264
 
265
	ConfigOption *option = &options[section->options_count];
266
 
267
	section->options_count += 1;
268
	section->options = options;
269
 
270
	init_option(option, key, key_len, value, value_len);
271
 
272
	return option;
273
}
274
 
275
static ConfigOption *get_option_len( ConfigSection *section, const char *key, size_t key_len )
276
{
277
	assert(section != NULL);
278
	assert(key != NULL);
279
 
280
	ConfigOption *options_end = §ion->options[section->options_count];
281
	for (ConfigOption *option = §ion->options[0]; option < options_end; ++option)
282
		if (string_equal_len(&option->key, key, key_len))
283
			return option;
284
 
285
	return NULL;
286
}
287
 
288
/* config option accessors/manipulators -- by key */
289
 
290
ConfigOption *config_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
291
{
292
	assert(section != NULL);
293
	assert(key != NULL);
294
 
295
	ConfigOption *option = get_option_len(section, key, key_len);
296
 
297
	if (option != NULL)
298
		return config_set_value_len(option, value, value_len);
299
 
300
	return append_option(section, key, key_len, value, value_len);
301
}
302
 
303
ConfigOption *config_get_option( const ConfigSection *section, const char *key )
304
{
305
	assert(section != NULL);
306
	assert(key != NULL);
307
 
308
	ConfigOption *options_end = §ion->options[section->options_count];
309
	for (ConfigOption *option = §ion->options[0]; option < options_end; ++option)
310
		if (strcmp(config_string_to_cstr(&option->key), key) == 0)
311
			return option;
312
 
313
	return NULL;
314
}
315
 
316
ConfigOption *config_get_or_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
317
{
318
	assert(section != NULL);
319
	assert(key != NULL);
320
 
321
	ConfigOption *option = get_option_len(section, key, key_len);
322
 
323
	if (option != NULL)
324
		return option;
325
 
326
	return append_option(section, key, key_len, value, value_len);
327
}
328
 
329
void config_set_string_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len )
330
{
331
	if (config_set_option_len(section, key, key_len, value, value_len) == NULL)
332
		config_oom();
333
}
334
 
335
bool config_get_string_option( const ConfigSection *section, const char *key, const char **out_value )
336
{
337
	assert(section != NULL);
338
	assert(key != NULL);
339
 
340
	ConfigOption *option = config_get_option(section, key);
341
	if (option != NULL)
342
	{
343
		const char *value = config_get_value(option);
344
		if (value != NULL)
345
		{
346
			*out_value = value;
347
			return true;
348
		}
349
	}
350
 
351
	return false;
352
}
353
 
354
const char *config_get_or_set_string_option( ConfigSection *section, const char *key, const char *value )
355
{
356
	if (!config_get_string_option(section, key, &value))
357
		config_set_string_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value));
358
	return value;
359
}
360
 
361
static const char *bool_values[][2] =
362
{
363
	{ "0", "1" },
364
	{ "no", "yes" },
365
	{ "off", "on" },
366
	{ "false", "true" },
367
};
368
 
369
void config_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style )
370
{
371
	if (config_set_option(section, key, bool_values[style][value ? 1 : 0]) == NULL)
372
		config_oom();
373
}
374
 
375
bool config_get_bool_option( const ConfigSection *section, const char *key, bool *out_value )
376
{
377
	assert(section != NULL);
378
	assert(key != NULL);
379
	assert(out_value != NULL);
380
 
381
	const char *value;
382
	if (config_get_string_option(section, key, &value))
383
	{
384
		for (size_t i = 0; i < COUNTOF(bool_values); ++i)
385
		{
386
			for (size_t j = 0; j < COUNTOF(bool_values[i]); ++j)
387
			{
388
				if (strcmp(value, bool_values[i][j]) == 0)
389
				{
390
					*out_value = j == 0 ? false : true;
391
					return true;
392
				}
393
			}
394
		}
395
	}
396
 
397
	return false;
398
}
399
 
400
bool config_get_or_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style )
401
{
402
	if (!config_get_bool_option(section, key, &value))
403
		config_set_bool_option(section, key, value, style);
404
	return value;
405
}
406
 
407
void config_set_int_option( ConfigSection *section, const char *key, int value )
408
{
409
	assert(key != NULL);
410
 
411
	char buffer[sdecsizeof(int) + 1];
412
	int buffer_len = snprintf(buffer, sizeof(buffer), "%i", value);
413
 
414
	if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL)
415
		config_oom();
416
}
417
 
418
bool config_get_int_option( const ConfigSection *section, const char *key, int *out_value )
419
{
420
	assert(section != NULL);
421
	assert(key != NULL);
422
	assert(out_value != NULL);
423
 
424
	const char *value;
425
	if (config_get_string_option(section, key, &value))
426
	{
427
		int i;
428
		int n;
429
		if (sscanf(value, "%i%n", &i, &n) > 0 && value[n] == '\0')  /* must be entire string */
430
		{
431
			*out_value = i;
432
			return true;
433
		}
434
	}
435
 
436
	return false;
437
}
438
 
439
int config_get_or_set_int_option( ConfigSection *section, const char *key, int value )
440
{
441
	if (!config_get_int_option(section, key, &value))
442
		config_set_int_option(section, key, value);
443
	return value;
444
}
445
 
446
void config_set_uint_option( ConfigSection *section, const char *key, unsigned int value )
447
{
448
	assert(key != NULL);
449
 
450
	char buffer[udecsizeof(unsigned int) + 1];
451
	int buffer_len = snprintf(buffer, sizeof(buffer), "%u", value);
452
 
453
	if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL)
454
		config_oom();
455
}
456
 
457
bool config_get_uint_option( const ConfigSection *section, const char *key, unsigned int *out_value )
458
{
459
	assert(section != NULL);
460
	assert(key != NULL);
461
	assert(out_value != NULL);
462
 
463
	const char *value;
464
	if (config_get_string_option(section, key, &value))
465
	{
466
		unsigned int u;
467
		int n;
468
		if (sscanf(value, "%u%n", &u, &n) > 0 && value[n] == '\0')  /* must be entire string */
469
		{
470
			*out_value = u;
471
			return true;
472
		}
473
	}
474
 
475
	return false;
476
}
477
 
478
unsigned int config_get_or_set_uint_option( ConfigSection *section, const char *key, unsigned int value )
479
{
480
	if (!config_get_uint_option(section, key, &value))
481
		config_set_uint_option(section, key, value);
482
	return value;
483
}
484
 
485
/* config option accessors/manipulators -- by reference */
486
 
487
ConfigOption *config_set_value_len( ConfigOption *option, const char *value, size_t value_len )
488
{
489
	assert(option != NULL);
490
 
491
	deinit_option_value(option);
492
 
493
	init_option_value(option, value, value_len);
494
 
495
	return option;
496
}
497
 
498
ConfigOption *config_add_value_len( ConfigOption *option, const char *value, size_t value_len )
499
{
500
	assert(option != NULL);
501
	assert(value != NULL);
502
 
503
	/* convert 'item' to 'list' */
504
	if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL)
505
	{
506
		ConfigString option_value = option->v.value;
507
 
508
		ConfigString *values = malloc(2 * sizeof(ConfigString));
509
		if (values == NULL)
510
			return NULL;
511
 
512
		option->v.values = values;
513
		option->v.values[0] = option_value;
514
		option->v.values[1] = string_init_len(value, value_len);
515
		option->values_count = 2;
516
	}
517
	else
518
	{
519
		ConfigString *values = realloc(option->v.values, (option->values_count + 1) * sizeof(ConfigString));
520
		if (values == NULL)
521
			return NULL;
522
 
523
		option->v.values = values;
524
		option->v.values[option->values_count] = string_init_len(value, value_len);
525
		option->values_count += 1;
526
	}
527
 
528
	return option;
529
}
530
 
531
ConfigOption *config_remove_value( ConfigOption *option, unsigned int i )
532
{
533
	assert(option != NULL);
534
 
535
	if (!config_is_value_list(option))
536
	{
537
		if (i > 0)
538
			return NULL;
539
 
540
		config_set_value_len(option, NULL, 0);
541
	}
542
	else
543
	{
544
		if (i >= option->values_count)
545
			return NULL;
546
 
547
		string_deinit(&option->v.values[i]);
548
		memmove(&option->v.values[i], &option->v.values[i + 1], (option->values_count - i - 1) * sizeof(ConfigString));
549
 
550
		if (option->values_count - 1 == 0)
551
		{
552
			option->v.value = string_init_len(NULL, 0);
553
			option->values_count = 0;
554
		}
555
		else
556
		{
557
			ConfigString *values = realloc(option->v.values, (option->values_count - 1) * sizeof(ConfigString));
558
			if (values == NULL)
559
				return NULL;
560
 
561
			option->v.values = values;
562
			option->values_count -= 1;
563
		}
564
	}
565
 
566
	return option;
567
}
568
 
569
const char *config_get_value( const ConfigOption *option )
570
{
571
	if (option == NULL || option->values_count != 0)
572
		return NULL;
573
 
574
	return config_string_to_cstr(&option->v.value);
575
}
576
 
577
/* config parser */
578
 
579
static bool is_whitespace( char c )
580
{
581
	return c == '\t' || c == ' ';
582
}
583
 
584
static bool is_end( char c )
585
{
586
	return c == '\0' || c == '\n' || c == '\r';
587
}
588
 
589
static bool is_whitespace_or_end( char c )
590
{
591
	return is_whitespace(c) || is_end(c);
592
}
593
 
594
typedef enum
595
{
596
	INVALID_DIRECTIVE = 0,
597
	SECTION_DIRECTIVE,
598
	ITEM_DIRECTIVE,
599
	LIST_DIRECTIVE,
600
} Directive;
601
 
602
static Directive match_directive( const char *buffer, size_t *index )
603
{
604
	size_t i = *index;
605
 
606
	while (is_whitespace(buffer[i]))
607
		++i;
608
 
609
	Directive directive;
610
 
611
	if (strncmp("section", &buffer[i], 7) == 0)
612
	{
613
		directive = SECTION_DIRECTIVE;
614
		i += 7;
615
	}
616
	else if (strncmp("item", &buffer[i], 4) == 0)
617
	{
618
		directive = ITEM_DIRECTIVE;
619
		i += 4;
620
	}
621
	else if (strncmp("list", &buffer[i], 4) == 0)
622
	{
623
		directive = LIST_DIRECTIVE;
624
		i += 4;
625
	}
626
	else
627
	{
628
		return INVALID_DIRECTIVE;
629
	}
630
 
631
	if (!is_whitespace_or_end(buffer[i]))
632
		return INVALID_DIRECTIVE;
633
 
634
	*index = i;
635
 
636
	return directive;
637
}
638
 
639
static bool match_nonquote_field( const char *buffer, size_t *index, size_t *length )
640
{
641
	size_t i = *index;
642
 
643
	for (; ; ++i)
644
	{
645
		char c = buffer[i];
646
 
647
		if (is_whitespace_or_end(c))
648
		{
649
			break;
650
		}
651
		else if (c <= ' ' || c > '~' || c == '#' || c == '\'' || c == '"')
652
		{
653
			return false;
654
		}
655
	}
656
 
657
	*length = i - *index;
658
	*index = i;
659
 
660
	return *length > 0;
661
}
662
 
663
static bool parse_quote_field( char *buffer, size_t *index, size_t *length )
664
{
665
	size_t i = *index;
666
	size_t o = *index;
667
 
668
	char quote = buffer[i];
669
 
670
	for (; ; )
671
	{
672
		char c = buffer[++i];
673
 
674
		if (c == quote)
675
		{
676
			++i;
677
			break;
678
		}
679
		else if (c == '\\')
680
		{
681
			c = buffer[++i];
682
			if (c == quote)
683
			{
684
				buffer[o++] = quote;
685
			}
686
			else
687
			{
688
				switch (c)
689
				{
690
				case 't':
691
					buffer[o++] = '\t';
692
					break;
693
				case 'n':
694
					buffer[o++] = '\n';
695
					break;
696
				case 'r':
697
					buffer[o++] = '\r';
698
					break;
699
				case '\\':
700
					buffer[o++] = '\\';
701
					break;
702
				case 'x':
703
					/* parse two hex digits */
704
					c = buffer[++i];
705
					char m = (c >= '0' && c <= '9') ? '0' :
706
					         (c >= 'a' && c <= 'f') ? 'a' - 10 :
707
					         (c >= 'A' && c <= 'F') ? 'A' - 10 : 0;
708
					if (m == 0)
709
						return false;
710
					char h = c - m;
711
					c = buffer[++i];
712
					m = (c >= '0' && c <= '9') ? '0' :
713
					    (c >= 'a' && c <= 'f') ? 'a' - 10 :
714
					    (c >= 'A' && c <= 'F') ? 'A' - 10 : 0;
715
					if (m == 0)
716
						return false;
717
					buffer[o++] = (h << 4) | (c - m);
718
					break;
719
				default:
720
					return false;
721
				}
722
			}
723
		}
724
		else if (c >= ' ' && c <= '~')
725
		{
726
			buffer[o++] = c;
727
		}
728
		else
729
		{
730
			return false;
731
		}
732
	}
733
 
734
	*length = o - *index;
735
	*index = i;
736
 
737
	return true;
738
}
739
 
740
static bool parse_field( char *buffer, size_t *index, size_t *start, size_t *length )
741
{
742
	size_t i = *index;
743
 
744
	while (is_whitespace(buffer[i]))
745
		++i;
746
 
747
	*start = i;
748
 
749
	if (buffer[i] == '"' || buffer[i] == '\'')
750
	{
751
		if (!parse_quote_field(buffer, &i, length))
752
			return false;
753
	}
754
	else
755
	{
756
		if (!match_nonquote_field(buffer, &i, length))
757
			return false;
758
	}
759
 
760
	if (!is_whitespace_or_end(buffer[i]))
761
		return INVALID_DIRECTIVE;
762
 
763
	*index = i;
764
 
765
	return true;
766
}
767
 
768
bool config_parse( Config *config, FILE *file )
769
{
770
	assert(config != NULL);
771
	assert(file != NULL);
772
 
773
	config_init(config);
774
 
775
	ConfigSection *section = NULL;
776
	ConfigOption *option = NULL;
777
 
778
	size_t buffer_cap = 128;
779
	char *buffer = malloc(buffer_cap * sizeof(char));
780
	if (buffer == NULL)
781
		config_oom();
782
	size_t buffer_end = 1;
783
	buffer[buffer_end - 1] = '\0';
784
 
785
	for (size_t line = 0, next_line = 0; ; line = next_line)
786
	{
787
		/* find begining of next line */
788
		while (next_line < buffer_end)
789
		{
790
			char c = buffer[next_line];
791
 
792
			if (c == '\0' && next_line == buffer_end - 1)
793
			{
794
				if (line > 0)
795
				{
796
					/* shift to front */
797
					memmove(&buffer[0], &buffer[line], buffer_end - line);
798
					buffer_end -= line;
799
					next_line -= line;
800
					line = 0;
801
				}
802
				else if (buffer_end > 1)
803
				{
804
					/* need larger capacity */
805
					buffer_cap *= 2;
806
					char *new_buffer = realloc(buffer, buffer_cap * sizeof(char));
807
					if (new_buffer == NULL)
808
						config_oom();
809
					buffer = new_buffer;
810
				}
811
 
812
				size_t read = fread(&buffer[buffer_end - 1], sizeof(char), buffer_cap - buffer_end, file);
813
				if (read == 0)
814
					break;
815
 
816
				buffer_end += read;
817
				buffer[buffer_end - 1] = '\0';
818
			}
819
			else
820
			{
821
				++next_line;
822
 
823
				if (c == '\n' || c == '\r')
824
					break;
825
			}
826
		}
827
 
828
		/* if at end of file */
829
		if (next_line == line)
830
			break;
831
 
832
		size_t i = line;
833
 
834
		Directive directive = match_directive(buffer, &i);
835
 
836
		switch (directive)
837
		{
838
		case INVALID_DIRECTIVE:
839
			continue;
840
		case SECTION_DIRECTIVE:
841
			{
842
				size_t type_start;
843
				size_t type_length;
844
 
845
				if (!parse_field(buffer, &i, &type_start, &type_length))
846
					continue;
847
 
848
				size_t name_start;
849
				size_t name_length;
850
 
851
				bool has_name = parse_field(buffer, &i, &name_start, &name_length);
852
 
853
				section = config_add_section_len(config,
854
						&buffer[type_start], type_length,
855
						has_name ? &buffer[name_start] : NULL, has_name ? name_length : 0);
856
				if (section == NULL)
857
					config_oom();
858
				option = NULL;
859
			}
860
			break;
861
		case ITEM_DIRECTIVE:
862
		case LIST_DIRECTIVE:
863
			{
864
				if (section == NULL)
865
					continue;
866
 
867
				size_t key_start;
868
				size_t key_length;
869
 
870
				if (!parse_field(buffer, &i, &key_start, &key_length))
871
					continue;
872
 
873
				size_t value_start;
874
				size_t value_length;
875
 
876
				if (!parse_field(buffer, &i, &value_start, &value_length))
877
					continue;
878
 
879
				if (directive == ITEM_DIRECTIVE)
880
				{
881
					option = config_set_option_len(section,
882
							&buffer[key_start], key_length,
883
							&buffer[value_start], value_length);
884
				}
885
				else
886
				{
887
					if (option == NULL || !string_equal_len(&option->key, &buffer[key_start], key_length))
888
						option = config_get_or_set_option_len(section,
889
								&buffer[key_start], key_length,
890
								NULL, 0);
891
					if (option != NULL)
892
						option = config_add_value_len(option,
893
								&buffer[value_start], value_length);
894
				}
895
				if (option == NULL)
896
					config_oom();
897
			}
898
			break;
899
		}
900
 
901
		assert(i <= next_line);
902
	}
903
 
904
	free(buffer);
905
 
906
	return config;
907
}
908
 
909
/* config writer */
910
 
911
static void write_field( const ConfigString *field, FILE *file )
912
{
913
	fputc('\'', file);
914
 
915
	char buffer[128];
916
	size_t o = 0;
917
 
918
	for (const char *ci = config_string_to_cstr(field); *ci != '\0'; ++ci)
919
	{
920
		char c = *ci;
921
 
922
		size_t l;
923
		switch (c)
924
		{
925
			case '\t':
926
			case '\n':
927
			case '\r':
928
			case '\'':
929
			case '\\':
930
				l = 2;
931
				break;
932
			default:
933
				l = (c >= ' ' && c <= '~') ? 1 : 4;
934
				break;
935
		}
936
 
937
		if (o + l > COUNTOF(buffer))
938
		{
939
			fwrite(buffer, sizeof(*buffer), o, file);
940
			o = 0;
941
		}
942
 
943
		switch (l)
944
		{
945
			case 1:
946
				buffer[o++] = c;
947
				break;
948
			case 2:
949
				switch (c)
950
				{
951
					case '\t':
952
						buffer[o++] = '\\';
953
						buffer[o++] = 't';
954
						break;
955
					case '\n':
956
						buffer[o++] = '\\';
957
						buffer[o++] = 'n';
958
						break;
959
					case '\r':
960
						buffer[o++] = '\\';
961
						buffer[o++] = 'r';
962
						break;
963
					case '\'':
964
					case '\\':
965
						buffer[o++] = '\\';
966
						buffer[o++] = c;
967
						break;
968
				}
969
				break;
970
			case 4:
971
				buffer[o++] = '\\';
972
				buffer[o++] = 'x';
973
				char n = (c >> 4) & 0x0f;
974
				buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n;
975
				n = c & 0x0f;
976
				buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n;
977
				break;
978
		}
979
	}
980
 
981
	if (o > 0)
982
		fwrite(buffer, sizeof(*buffer), o, file);
983
 
984
	fputc('\'', file);
985
}
986
 
987
void config_write( const Config *config, FILE *file )
988
{
989
	assert(config != NULL);
990
	assert(file != NULL);
991
 
992
	for (unsigned int s = 0; s < config->sections_count; ++s)
993
	{
994
		ConfigSection *section = &config->sections[s];
995
 
996
		fputs("section ", file);
997
		write_field(§ion->type, file);
998
		if (config_string_to_cstr(§ion->name) != NULL)
999
		{
1000
			fputc(' ', file);
1001
			write_field(§ion->name, file);
1002
		}
1003
		fputc('\n', file);
1004
 
1005
		for (unsigned int o = 0; o < section->options_count; ++o)
1006
		{
1007
			ConfigOption *option = §ion->options[o];
1008
 
1009
			if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL)
1010
			{
1011
				fputs("\titem ", file);
1012
				write_field(&option->key, file);
1013
				fputc(' ', file);
1014
				write_field(&option->v.value, file);
1015
				fputc('\n', file);
1016
			}
1017
			else
1018
			{
1019
				ConfigString *values_end = &option->v.values[option->values_count];
1020
				for (ConfigString *value = &option->v.values[0]; value < values_end; ++value)
1021
				{
1022
					fputs("\tlist ", file);
1023
					write_field(&option->key, file);
1024
					fputc(' ', file);
1025
					write_field(value, file);
1026
					fputc('\n', file);
1027
				}
1028
			}
1029
		}
1030
 
1031
		fputc('\n', file);
1032
	}
1033
}