config-file.c 16.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
J
jp9000 已提交
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

#include <stdio.h>
#include <wchar.h>
#include "config-file.h"
#include "platform.h"
#include "base.h"
#include "bmem.h"
#include "darray.h"
#include "lexer.h"
#include "dstr.h"

struct config_item {
	char *name;
	char *value;
};

static inline void config_item_free(struct config_item *item)
{
	bfree(item->name);
	bfree(item->value);
}

struct config_section {
	char *name;
	struct darray items; /* struct config_item */
};

static inline void config_section_free(struct config_section *section)
{
	struct config_item *items = section->items.array;
	size_t i;

	for (i = 0; i < section->items.num; i++)
		config_item_free(items+i);

	darray_free(&section->items);
	bfree(section->name);
}

struct config_data {
	char *file;
	struct darray sections; /* struct config_section */
	struct darray defaults; /* struct config_section */
};

61
config_t *config_create(const char *file)
J
jp9000 已提交
62 63 64 65 66 67 68 69 70
{
	struct config_data *config;
	FILE *f;

	f = os_fopen(file, "wb");
	if (!f)
		return NULL;
	fclose(f);

71
	config = bzalloc(sizeof(struct config_data));
J
jp9000 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
	return config;
}

static inline void remove_ref_whitespace(struct strref *ref)
{
	if (ref->array) {
		while (is_whitespace(*ref->array)) {
			ref->array++;
			ref->len--;
		}

		while (ref->len && is_whitespace(ref->array[ref->len-1]))
			ref->len--;
	}
}

static bool config_parse_string(struct lexer *lex, struct strref *ref,
		char end)
{
	bool success = end != 0;
	struct base_token token;
	base_token_clear(&token);

95
	while (lexer_getbasetoken(lex, &token, PARSE_WHITESPACE)) {
J
jp9000 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
		if (end) {
			if (*token.text.array == end) {
				success = true;
				break;
			} else if (is_newline(*token.text.array)) {
				success = false;
				break;
			}
		} else {
			if (is_newline(*token.text.array)) {
				success = true;
				break;
			}
		}

		strref_add(ref, &token.text);
	}

	remove_ref_whitespace(ref);
	return success;
}

static void config_add_item(struct darray *items, struct strref *name,
		struct strref *value)
{
	struct config_item item;
122 123 124 125 126 127
	struct dstr item_value;
	dstr_init_copy_strref(&item_value, value);
	dstr_replace(&item_value, "\\n", "\n");
	dstr_replace(&item_value, "\\r", "\r");
	dstr_replace(&item_value, "\\\\", "\\");

J
jp9000 已提交
128
	item.name  = bstrdup_n(name->array,  name->len);
129
	item.value = item_value.array;
J
jp9000 已提交
130 131 132 133 134 135 136 137
	darray_push_back(sizeof(struct config_item), items, &item);
}

static void config_parse_section(struct config_section *section,
		struct lexer *lex)
{
	struct base_token token;

138
	while (lexer_getbasetoken(lex, &token, PARSE_WHITESPACE)) {
J
jp9000 已提交
139 140 141
		struct strref name, value;

		while (token.type == BASETOKEN_WHITESPACE) {
142
			if (!lexer_getbasetoken(lex, &token, PARSE_WHITESPACE))
J
jp9000 已提交
143 144 145 146 147 148 149
				return;
		}

		if (token.type == BASETOKEN_OTHER) {
			if (*token.text.array == '#') {
				do {
					if (!lexer_getbasetoken(lex, &token,
150
							PARSE_WHITESPACE))
J
jp9000 已提交
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
						return;
				} while (!is_newline(*token.text.array));

				continue;
			} else if (*token.text.array == '[') {
				lex->offset--;
				return;
			}
		}

		strref_copy(&name, &token.text);
		if (!config_parse_string(lex, &name, '='))
			continue;

		strref_clear(&value);
		config_parse_string(lex, &value, 0);

168 169
		if (!strref_is_empty(&value))
			config_add_item(&section->items, &name, &value);
J
jp9000 已提交
170 171 172
	}
}

J
jp9000 已提交
173
static void parse_config_data(struct darray *sections, struct lexer *lex)
J
jp9000 已提交
174 175
{
	struct strref section_name;
J
jp9000 已提交
176
	struct base_token token;
J
jp9000 已提交
177 178 179

	base_token_clear(&token);

J
jp9000 已提交
180
	while (lexer_getbasetoken(lex, &token, PARSE_WHITESPACE)) {
J
jp9000 已提交
181 182 183
		struct config_section *section;

		while (token.type == BASETOKEN_WHITESPACE) {
J
jp9000 已提交
184 185
			if (!lexer_getbasetoken(lex, &token, PARSE_WHITESPACE))
				return;
J
jp9000 已提交
186 187 188 189
		}

		if (*token.text.array != '[') {
			while (!is_newline(*token.text.array)) {
J
jp9000 已提交
190
				if (!lexer_getbasetoken(lex, &token,
191
							PARSE_WHITESPACE))
J
jp9000 已提交
192
					return;
J
jp9000 已提交
193 194 195 196 197 198
			}

			continue;
		}

		strref_clear(&section_name);
J
jp9000 已提交
199
		config_parse_string(lex, &section_name, ']');
J
jp9000 已提交
200
		if (!section_name.len)
J
jp9000 已提交
201
			return;
J
jp9000 已提交
202 203 204 205 206

		section = darray_push_back_new(sizeof(struct config_section),
				sections);
		section->name = bstrdup_n(section_name.array,
				section_name.len);
J
jp9000 已提交
207
		config_parse_section(section, lex);
J
jp9000 已提交
208
	}
J
jp9000 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
}

static int config_parse_file(struct darray *sections, const char *file,
		bool always_open)
{
	char *file_data;
	struct lexer lex;
	FILE *f;

	f = os_fopen(file, "rb");
	if (always_open && !f)
		f = os_fopen(file, "w+");
	if (!f)
		return CONFIG_FILENOTFOUND;

	os_fread_utf8(f, &file_data);
	fclose(f);

	if (!file_data)
		return CONFIG_SUCCESS;

	lexer_init(&lex);
	lexer_start_move(&lex, file_data);

	parse_config_data(sections, &lex);
J
jp9000 已提交
234 235 236 237 238

	lexer_free(&lex);
	return CONFIG_SUCCESS;
}

239
int config_open(config_t **config, const char *file,
240
		enum config_open_type open_type)
J
jp9000 已提交
241 242
{
	int errorcode;
243
	bool always_open = open_type == CONFIG_OPEN_ALWAYS;
J
jp9000 已提交
244 245 246 247

	if (!config)
		return CONFIG_ERROR;

248
	*config = bzalloc(sizeof(struct config_data));
249 250 251
	if (!*config)
		return CONFIG_ERROR;

252 253
	(*config)->file = bstrdup(file);

J
jp9000 已提交
254
	errorcode = config_parse_file(&(*config)->sections, file, always_open);
J
jp9000 已提交
255 256 257 258 259 260 261 262 263

	if (errorcode != CONFIG_SUCCESS) {
		config_close(*config);
		*config = NULL;
	}

	return errorcode;
}

J
jp9000 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
int config_open_string(config_t **config, const char *str)
{
	struct lexer lex;

	if (!config)
		return CONFIG_ERROR;

	*config = bzalloc(sizeof(struct config_data));
	if (!*config)
		return CONFIG_ERROR;

	(*config)->file = NULL;

	lexer_init(&lex);
	lexer_start(&lex, str);
	parse_config_data(&(*config)->sections, &lex);
	lexer_free(&lex);

	return CONFIG_SUCCESS;
}

285
int config_open_defaults(config_t *config, const char *file)
J
jp9000 已提交
286 287 288 289
{
	if (!config)
		return CONFIG_ERROR;

J
jp9000 已提交
290
	return config_parse_file(&config->defaults, file, false);
J
jp9000 已提交
291 292
}

293
int config_save(config_t *config)
J
jp9000 已提交
294 295
{
	FILE *f;
296
	struct dstr str, tmp;
J
jp9000 已提交
297 298 299 300
	size_t i, j;

	if (!config)
		return CONFIG_ERROR;
J
jp9000 已提交
301 302
	if (!config->file)
		return CONFIG_ERROR;
J
jp9000 已提交
303 304

	dstr_init(&str);
305
	dstr_init(&tmp);
J
jp9000 已提交
306 307 308 309 310 311 312 313 314 315 316 317

	f = os_fopen(config->file, "wb");
	if (!f)
		return CONFIG_FILENOTFOUND;

	for (i = 0; i < config->sections.num; i++) {
		struct config_section *section = darray_item(
				sizeof(struct config_section),
				&config->sections, i);

		if (i) dstr_cat(&str, "\n");

318
		dstr_cat(&str, "[");
J
jp9000 已提交
319 320 321 322 323 324
		dstr_cat(&str, section->name);
		dstr_cat(&str, "]\n");

		for (j = 0; j < section->items.num; j++) {
			struct config_item *item = darray_item(
					sizeof(struct config_item),
J
jp9000 已提交
325
					&section->items, j);
J
jp9000 已提交
326

327 328 329 330 331
			dstr_copy(&tmp, item->value ? item->value : "");
			dstr_replace(&tmp, "\\", "\\\\");
			dstr_replace(&tmp, "\r", "\\r");
			dstr_replace(&tmp, "\n", "\\n");

J
jp9000 已提交
332 333
			dstr_cat(&str, item->name);
			dstr_cat(&str, "=");
334
			dstr_cat(&str, tmp.array);
J
jp9000 已提交
335 336 337 338 339 340 341 342 343 344
			dstr_cat(&str, "\n");
		}
	}

#ifdef _WIN32
	fwrite("\xEF\xBB\xBF", 1, 3, f);
#endif
	fwrite(str.array, 1, str.len, f);
	fclose(f);

345
	dstr_free(&tmp);
J
jp9000 已提交
346 347 348 349 350
	dstr_free(&str);

	return CONFIG_SUCCESS;
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
int config_save_safe(config_t *config, const char *temp_ext,
		const char *backup_ext)
{
	struct dstr temp_file = {0};
	struct dstr backup_file = {0};
	char *file = config->file;
	int ret;

	if (!temp_ext || !*temp_ext) {
		blog(LOG_ERROR, "config_save_safe: invalid "
		                "temporary extension specified");
		return CONFIG_ERROR;
	}

	dstr_copy(&temp_file, config->file);
	if (*temp_ext != '.')
		dstr_cat(&temp_file, ".");
	dstr_cat(&temp_file, temp_ext);

	config->file = temp_file.array;
	ret = config_save(config);
	config->file = file;

	if (ret != CONFIG_SUCCESS) {
		goto cleanup;
	}

	if (backup_ext && *backup_ext) {
		dstr_copy(&backup_file, config->file);
		if (*backup_ext != '.')
			dstr_cat(&backup_file, ".");
		dstr_cat(&backup_file, backup_ext);

		os_unlink(backup_file.array);
		os_rename(file, backup_file.array);
	} else {
		os_unlink(file);
	}

	os_rename(temp_file.array, file);

cleanup:
	dstr_free(&temp_file);
	dstr_free(&backup_file);
	return ret;
}

398
void config_close(config_t *config)
J
jp9000 已提交
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
{
	struct config_section *defaults, *sections;
	size_t i;

	if (!config) return;

	defaults = config->defaults.array;
	sections = config->sections.array;

	for (i = 0; i < config->defaults.num; i++)
		config_section_free(defaults+i);
	for (i = 0; i < config->sections.num; i++)
		config_section_free(sections+i);

	darray_free(&config->defaults);
	darray_free(&config->sections);
	bfree(config->file);
	bfree(config);
}

419
size_t config_num_sections(config_t *config)
420 421 422 423
{
	return config->sections.num;
}

424
const char *config_get_section(config_t *config, size_t idx)
425 426 427 428 429 430 431 432 433 434 435 436
{
	struct config_section *section;

	if (idx >= config->sections.num)
		return NULL;

	section = darray_item(sizeof(struct config_section), &config->sections,
			idx);

	return section->name;
}

437
static const struct config_item *config_find_item(const struct darray *sections,
J
jp9000 已提交
438 439 440 441 442
		const char *section, const char *name)
{
	size_t i, j;

	for (i = 0; i < sections->num; i++) {
443
		const struct config_section *sec = darray_item(
J
jp9000 已提交
444 445
				sizeof(struct config_section), sections, i);

446
		if (astrcmpi(sec->name, section) == 0) {
J
jp9000 已提交
447 448 449
			for (j = 0; j < sec->items.num; j++) {
				struct config_item *item = darray_item(
						sizeof(struct config_item),
450
						&sec->items, j);
J
jp9000 已提交
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474

				if (astrcmpi(item->name, name) == 0)
					return item;
			}
		}
	}

	return NULL;
}

static void config_set_item(struct darray *sections, const char *section,
		const char *name, char *value)
{
	struct config_section *sec = NULL;
	struct config_section *array = sections->array;
	struct config_item *item;
	size_t i, j;

	for (i = 0; i < sections->num; i++) {
		struct config_section *cur_sec = array+i;
		struct config_item *items = cur_sec->items.array;

		if (astrcmpi(cur_sec->name, section) == 0) {
			for (j = 0; j < cur_sec->items.num; j++) {
475
				item = items+j;
J
jp9000 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499

				if (astrcmpi(item->name, name) == 0) {
					bfree(item->value);
					item->value = value;
					return;
				}
			}

			sec = cur_sec;
			break;
		}
	}

	if (!sec) {
		sec = darray_push_back_new(sizeof(struct config_section),
				sections);
		sec->name = bstrdup(section);
	}

	item = darray_push_back_new(sizeof(struct config_item), &sec->items);
	item->name  = bstrdup(name);
	item->value = value;
}

500
void config_set_string(config_t *config, const char *section,
J
jp9000 已提交
501 502 503 504 505 506 507
		const char *name, const char *value)
{
	if (!value)
		value = "";
	config_set_item(&config->sections, section, name, bstrdup(value));
}

508
void config_set_int(config_t *config, const char *section,
J
jp9000 已提交
509 510 511 512
		const char *name, int64_t value)
{
	struct dstr str;
	dstr_init(&str);
513
	dstr_printf(&str, "%lld", value);
J
jp9000 已提交
514 515 516
	config_set_item(&config->sections, section, name, str.array);
}

517
void config_set_uint(config_t *config, const char *section,
J
jp9000 已提交
518 519 520 521
		const char *name, uint64_t value)
{
	struct dstr str;
	dstr_init(&str);
522
	dstr_printf(&str, "%llu", value);
J
jp9000 已提交
523 524 525
	config_set_item(&config->sections, section, name, str.array);
}

526
void config_set_bool(config_t *config, const char *section,
J
jp9000 已提交
527 528 529 530 531 532
		const char *name, bool value)
{
	char *str = bstrdup(value ? "true" : "false");
	config_set_item(&config->sections, section, name, str);
}

533
void config_set_double(config_t *config, const char *section,
J
jp9000 已提交
534 535
		const char *name, double value)
{
536 537 538
	char *str = bzalloc(64);
	os_dtostr(value, str, 64);
	config_set_item(&config->sections, section, name, str);
J
jp9000 已提交
539 540
}

541
void config_set_default_string(config_t *config, const char *section,
J
jp9000 已提交
542 543 544 545 546 547 548
		const char *name, const char *value)
{
	if (!value)
		value = "";
	config_set_item(&config->defaults, section, name, bstrdup(value));
}

549
void config_set_default_int(config_t *config, const char *section,
J
jp9000 已提交
550 551 552 553
		const char *name, int64_t value)
{
	struct dstr str;
	dstr_init(&str);
554
	dstr_printf(&str, "%lld", value);
J
jp9000 已提交
555 556 557
	config_set_item(&config->defaults, section, name, str.array);
}

558
void config_set_default_uint(config_t *config, const char *section,
J
jp9000 已提交
559 560 561 562
		const char *name, uint64_t value)
{
	struct dstr str;
	dstr_init(&str);
563
	dstr_printf(&str, "%llu", value);
J
jp9000 已提交
564 565 566
	config_set_item(&config->defaults, section, name, str.array);
}

567
void config_set_default_bool(config_t *config, const char *section,
J
jp9000 已提交
568 569 570 571 572 573
		const char *name, bool value)
{
	char *str = bstrdup(value ? "true" : "false");
	config_set_item(&config->defaults, section, name, str);
}

574
void config_set_default_double(config_t *config, const char *section,
J
jp9000 已提交
575 576 577 578 579 580 581 582
		const char *name, double value)
{
	struct dstr str;
	dstr_init(&str);
	dstr_printf(&str, "%g", value);
	config_set_item(&config->defaults, section, name, str.array);
}

583
const char *config_get_string(const config_t *config, const char *section,
J
jp9000 已提交
584 585
		const char *name)
{
586
	const struct config_item *item = config_find_item(&config->sections,
J
jp9000 已提交
587 588 589 590 591 592 593 594 595
			section, name);
	if (!item)
		item = config_find_item(&config->defaults, section, name);
	if (!item)
		return NULL;

	return item->value;
}

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
static inline int64_t str_to_int64(const char *str)
{
	if (!str || !*str)
		return 0;

	if (str[0] == '0' && str[1] == 'x')
		return strtoll(str + 2, NULL, 16);
	else
		return strtoll(str, NULL, 10);
}

static inline uint64_t str_to_uint64(const char *str)
{
	if (!str || !*str)
		return 0;

	if (str[0] == '0' && str[1] == 'x')
		return strtoull(str + 2, NULL, 16);
	else
		return strtoull(str, NULL, 10);
}

618
int64_t config_get_int(const config_t *config, const char *section,
J
jp9000 已提交
619 620 621 622
		const char *name)
{
	const char *value = config_get_string(config, section, name);
	if (value)
623
		return str_to_int64(value);
J
jp9000 已提交
624 625 626 627

	return 0;
}

628
uint64_t config_get_uint(const config_t *config, const char *section,
J
jp9000 已提交
629 630 631 632
		const char *name)
{
	const char *value = config_get_string(config, section, name);
	if (value)
633
		return str_to_uint64(value);
J
jp9000 已提交
634 635 636 637

	return 0;
}

638
bool config_get_bool(const config_t *config, const char *section,
J
jp9000 已提交
639 640 641 642 643
		const char *name)
{
	const char *value = config_get_string(config, section, name);
	if (value)
		return astrcmpi(value, "true") == 0 ||
644
		       !!str_to_uint64(value);
J
jp9000 已提交
645 646 647 648

	return false;
}

649
double config_get_double(const config_t *config, const char *section,
J
jp9000 已提交
650 651 652 653
		const char *name)
{
	const char *value = config_get_string(config, section, name);
	if (value)
654
		return os_strtod(value);
J
jp9000 已提交
655 656 657

	return 0.0;
}
J
jp9000 已提交
658

659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
bool config_remove_value(config_t *config, const char *section,
		const char *name)
{
	struct darray *sections = &config->sections;

	for (size_t i = 0; i < sections->num; i++) {
		struct config_section *sec = darray_item(
				sizeof(struct config_section), sections, i);

		if (astrcmpi(sec->name, section) != 0)
			continue;

		for (size_t j = 0; j < sec->items.num; j++) {
			struct config_item *item = darray_item(
					sizeof(struct config_item),
					&sec->items, j);

			if (astrcmpi(item->name, name) == 0) {
				config_item_free(item);
				darray_erase(sizeof(struct config_item),
						&sec->items, j);
				return true;
			}
		}
	}

	return false;
}

688 689
const char *config_get_default_string(const config_t *config,
		const char *section, const char *name)
J
jp9000 已提交
690
{
691
	const struct config_item *item;
J
jp9000 已提交
692 693 694 695 696 697 698 699

	item = config_find_item(&config->defaults, section, name);
	if (!item)
		return NULL;

	return item->value;
}

700
int64_t config_get_default_int(const config_t *config, const char *section,
J
jp9000 已提交
701 702 703 704
		const char *name)
{
	const char *value = config_get_default_string(config, section, name);
	if (value)
705
		return str_to_int64(value);
J
jp9000 已提交
706 707 708 709

	return 0;
}

710
uint64_t config_get_default_uint(const config_t *config, const char *section,
J
jp9000 已提交
711 712 713 714
		const char *name)
{
	const char *value = config_get_default_string(config, section, name);
	if (value)
715
		return str_to_uint64(value);
J
jp9000 已提交
716 717 718 719

	return 0;
}

720
bool config_get_default_bool(const config_t *config, const char *section,
J
jp9000 已提交
721 722 723 724 725
		const char *name)
{
	const char *value = config_get_default_string(config, section, name);
	if (value)
		return astrcmpi(value, "true") == 0 ||
726
		       !!str_to_uint64(value);
J
jp9000 已提交
727 728 729 730

	return false;
}

731
double config_get_default_double(const config_t *config, const char *section,
J
jp9000 已提交
732 733 734 735
		const char *name)
{
	const char *value = config_get_default_string(config, section, name);
	if (value)
736
		return os_strtod(value);
J
jp9000 已提交
737 738 739

	return 0.0;
}
740

741
bool config_has_user_value(const config_t *config, const char *section,
742 743 744 745 746
		const char *name)
{
	return config_find_item(&config->sections, section, name) != NULL;
}

747
bool config_has_default_value(const config_t *config, const char *section,
748 749 750 751 752
		const char *name)
{
	return config_find_item(&config->defaults, section, name) != NULL;
}