config.c 17.3 KB
Newer Older
1 2 3 4 5 6 7
/*
 * GIT - The information manager from hell
 *
 * Copyright (C) Linus Torvalds, 2005
 * Copyright (C) Johannes Schindelin, 2005
 *
 */
L
Linus Torvalds 已提交
8
#include "cache.h"
9
#include <regex.h>
L
Linus Torvalds 已提交
10 11 12 13

#define MAXNAME (256)

static FILE *config_file;
14
static const char *config_file_name;
L
Linus Torvalds 已提交
15 16 17 18 19 20 21 22 23
static int config_linenr;
static int get_next_char(void)
{
	int c;
	FILE *f;

	c = '\n';
	if ((f = config_file) != NULL) {
		c = fgetc(f);
24 25 26 27 28 29 30 31
		if (c == '\r') {
			/* DOS like systems */
			c = fgetc(f);
			if (c != '\n') {
				ungetc(c, f);
				c = '\r';
			}
		}
L
Linus Torvalds 已提交
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 61 62
		if (c == '\n')
			config_linenr++;
		if (c == EOF) {
			config_file = NULL;
			c = '\n';
		}
	}
	return c;
}

static char *parse_value(void)
{
	static char value[1024];
	int quote = 0, comment = 0, len = 0, space = 0;

	for (;;) {
		int c = get_next_char();
		if (len >= sizeof(value))
			return NULL;
		if (c == '\n') {
			if (quote)
				return NULL;
			value[len] = 0;
			return value;
		}
		if (comment)
			continue;
		if (isspace(c) && !quote) {
			space = 1;
			continue;
		}
63 64 65 66 67 68
		if (!quote) {
			if (c == ';' || c == '#') {
				comment = 1;
				continue;
			}
		}
L
Linus Torvalds 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
		if (space) {
			if (len)
				value[len++] = ' ';
			space = 0;
		}
		if (c == '\\') {
			c = get_next_char();
			switch (c) {
			case '\n':
				continue;
			case 't':
				c = '\t';
				break;
			case 'b':
				c = '\b';
				break;
			case 'n':
				c = '\n';
				break;
88 89 90 91 92 93
			/* Some characters escape as themselves */
			case '\\': case '"':
				break;
			/* Reject unknown escape sequences */
			default:
				return NULL;
L
Linus Torvalds 已提交
94 95 96 97 98 99 100 101 102 103 104 105
			}
			value[len++] = c;
			continue;
		}
		if (c == '"') {
			quote = 1-quote;
			continue;
		}
		value[len++] = c;
	}
}

106 107 108 109 110
static inline int iskeychar(int c)
{
	return isalnum(c) || c == '-';
}

L
Linus Torvalds 已提交
111 112 113 114 115 116 117 118 119 120
static int get_value(config_fn_t fn, char *name, unsigned int len)
{
	int c;
	char *value;

	/* Get the full name */
	for (;;) {
		c = get_next_char();
		if (c == EOF)
			break;
121
		if (!iskeychar(c))
L
Linus Torvalds 已提交
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
			break;
		name[len++] = tolower(c);
		if (len >= MAXNAME)
			return -1;
	}
	name[len] = 0;
	while (c == ' ' || c == '\t')
		c = get_next_char();

	value = NULL;
	if (c != '\n') {
		if (c != '=')
			return -1;
		value = parse_value();
		if (!value)
			return -1;
	}
	return fn(name, value);
}

L
Linus Torvalds 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
static int get_extended_base_var(char *name, int baselen, int c)
{
	do {
		if (c == '\n')
			return -1;
		c = get_next_char();
	} while (isspace(c));

	/* We require the format to be '[base "extension"]' */
	if (c != '"')
		return -1;
	name[baselen++] = '.';

	for (;;) {
		int c = get_next_char();
		if (c == '\n')
			return -1;
		if (c == '"')
			break;
		if (c == '\\') {
			c = get_next_char();
			if (c == '\n')
				return -1;
		}
		name[baselen++] = c;
		if (baselen > MAXNAME / 2)
			return -1;
	}

	/* Final ']' */
	if (get_next_char() != ']')
		return -1;
	return baselen;
}

L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185 186
static int get_base_var(char *name)
{
	int baselen = 0;

	for (;;) {
		int c = get_next_char();
		if (c == EOF)
			return -1;
		if (c == ']')
			return baselen;
L
Linus Torvalds 已提交
187 188
		if (isspace(c))
			return get_extended_base_var(name, baselen, c);
189
		if (!iskeychar(c) && c != '.')
L
Linus Torvalds 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
			return -1;
		if (baselen > MAXNAME / 2)
			return -1;
		name[baselen++] = tolower(c);
	}
}

static int git_parse_file(config_fn_t fn)
{
	int comment = 0;
	int baselen = 0;
	static char var[MAXNAME];

	for (;;) {
		int c = get_next_char();
		if (c == '\n') {
			/* EOF? */
			if (!config_file)
				return 0;
			comment = 0;
			continue;
		}
		if (comment || isspace(c))
			continue;
		if (c == '#' || c == ';') {
			comment = 1;
			continue;
		}
		if (c == '[') {
			baselen = get_base_var(var);
			if (baselen <= 0)
				break;
			var[baselen++] = '.';
			var[baselen] = 0;
			continue;
		}
		if (!isalpha(c))
			break;
228
		var[baselen] = tolower(c);
L
Linus Torvalds 已提交
229 230 231
		if (get_value(fn, var, baselen+1) < 0)
			break;
	}
232
	die("bad config file line %d in %s", config_linenr, config_file_name);
L
Linus Torvalds 已提交
233 234 235 236 237 238 239 240 241 242
}

int git_config_int(const char *name, const char *value)
{
	if (value && *value) {
		char *end;
		int val = strtol(value, &end, 0);
		if (!*end)
			return val;
	}
243
	die("bad config value for '%s' in %s", name, config_file_name);
L
Linus Torvalds 已提交
244 245 246 247 248 249 250 251
}

int git_config_bool(const char *name, const char *value)
{
	if (!value)
		return 1;
	if (!*value)
		return 0;
252
	if (!strcasecmp(value, "true") || !strcasecmp(value, "yes"))
L
Linus Torvalds 已提交
253
		return 1;
254
	if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
L
Linus Torvalds 已提交
255 256 257 258 259 260 261 262 263 264 265 266
		return 0;
	return git_config_int(name, value) != 0;
}

int git_default_config(const char *var, const char *value)
{
	/* This needs a better name */
	if (!strcmp(var, "core.filemode")) {
		trust_executable_bit = git_config_bool(var, value);
		return 0;
	}

J
Junio C Hamano 已提交
267 268 269 270 271
	if (!strcmp(var, "core.ignorestat")) {
		assume_unchanged = git_config_bool(var, value);
		return 0;
	}

272 273
	if (!strcmp(var, "core.prefersymlinkrefs")) {
		prefer_symlink_refs = git_config_bool(var, value);
274 275 276
		return 0;
	}

277 278 279 280 281
	if (!strcmp(var, "core.logallrefupdates")) {
		log_all_ref_updates = git_config_bool(var, value);
		return 0;
	}

282 283 284 285 286
	if (!strcmp(var, "core.warnambiguousrefs")) {
		warn_ambiguous_refs = git_config_bool(var, value);
		return 0;
	}

287 288 289 290 291
	if (!strcmp(var, "core.legacyheaders")) {
		use_legacy_headers = git_config_bool(var, value);
		return 0;
	}

292 293 294 295 296 297 298 299 300 301
	if (!strcmp(var, "core.compression")) {
		int level = git_config_int(var, value);
		if (level == -1)
			level = Z_DEFAULT_COMPRESSION;
		else if (level < 0 || level > Z_BEST_COMPRESSION)
			die("bad zlib compression level %d", level);
		zlib_compression_level = level;
		return 0;
	}

302
	if (!strcmp(var, "user.name")) {
303
		strlcpy(git_default_name, value, sizeof(git_default_name));
304 305 306 307
		return 0;
	}

	if (!strcmp(var, "user.email")) {
308
		strlcpy(git_default_email, value, sizeof(git_default_email));
309 310 311
		return 0;
	}

J
Junio C Hamano 已提交
312
	if (!strcmp(var, "i18n.commitencoding")) {
313
		strlcpy(git_commit_encoding, value, sizeof(git_commit_encoding));
J
Junio C Hamano 已提交
314 315 316
		return 0;
	}

317
	if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
318 319 320 321
		pager_use_color = git_config_bool(var,value);
		return 0;
	}

P
Petr Baudis 已提交
322
	/* Add other config variables here and to Documentation/config.txt. */
L
Linus Torvalds 已提交
323 324 325
	return 0;
}

326
int git_config_from_file(config_fn_t fn, const char *filename)
L
Linus Torvalds 已提交
327 328
{
	int ret;
329
	FILE *f = fopen(filename, "r");
L
Linus Torvalds 已提交
330 331 332 333

	ret = -1;
	if (f) {
		config_file = f;
334
		config_file_name = filename;
L
Linus Torvalds 已提交
335 336 337
		config_linenr = 1;
		ret = git_parse_file(fn);
		fclose(f);
338
		config_file_name = NULL;
L
Linus Torvalds 已提交
339 340 341
	}
	return ret;
}
342

343 344
int git_config(config_fn_t fn)
{
345 346 347 348 349 350 351 352 353 354 355
	int ret = 0;
	char *repo_config = NULL;
	const char *home = NULL, *filename;

	/* $GIT_CONFIG makes git read _only_ the given config file,
	 * $GIT_CONFIG_LOCAL will make it process it in addition to the
	 * global config file, the same way it would the per-repository
	 * config file otherwise. */
	filename = getenv("GIT_CONFIG");
	if (!filename) {
		home = getenv("HOME");
356
		filename = getenv("GIT_CONFIG_LOCAL");
357
		if (!filename)
358
			filename = repo_config = xstrdup(git_path("config"));
359
	}
360 361

	if (home) {
362
		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
363
		if (!access(user_config, R_OK))
364 365 366 367 368
			ret = git_config_from_file(fn, user_config);
		free(user_config);
	}

	ret += git_config_from_file(fn, filename);
J
Junio C Hamano 已提交
369
	free(repo_config);
370
	return ret;
371 372
}

373 374 375
/*
 * Find all the stuff for git_config_set() below.
 */
376 377 378

#define MAX_MATCHES 512

379 380 381
static struct {
	int baselen;
	char* key;
382
	int do_not_match;
383
	regex_t* value_regex;
384 385
	int multi_replace;
	off_t offset[MAX_MATCHES];
386 387 388 389
	enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
	int seen;
} store;

390 391 392 393 394 395 396 397
static int matches(const char* key, const char* value)
{
	return !strcmp(key, store.key) &&
		(store.value_regex == NULL ||
		 (store.do_not_match ^
		  !regexec(store.value_regex, value, 0, NULL, 0)));
}

398 399 400 401
static int store_aux(const char* key, const char* value)
{
	switch (store.state) {
	case KEY_SEEN:
402
		if (matches(key, value)) {
403
			if (store.seen == 1 && store.multi_replace == 0) {
404 405 406
				fprintf(stderr,
					"Warning: %s has multiple values\n",
					key);
407 408 409
			} else if (store.seen >= MAX_MATCHES) {
				fprintf(stderr, "Too many matches\n");
				return 1;
410
			}
411 412

			store.offset[store.seen] = ftell(config_file);
413 414 415 416 417 418 419 420
			store.seen++;
		}
		break;
	case SECTION_SEEN:
		if (strncmp(key, store.key, store.baselen+1)) {
			store.state = SECTION_END_SEEN;
			break;
		} else
421 422
			/* do not increment matches: this is no match */
			store.offset[store.seen] = ftell(config_file);
423 424 425
		/* fallthru */
	case SECTION_END_SEEN:
	case START:
426
		if (matches(key, value)) {
427
			store.offset[store.seen] = ftell(config_file);
428 429
			store.state = KEY_SEEN;
			store.seen++;
L
Linus Torvalds 已提交
430 431
		} else {
			if (strrchr(key, '.') - key == store.baselen &&
S
sean 已提交
432
			      !strncmp(key, store.key, store.baselen)) {
S
sean 已提交
433
					store.state = SECTION_SEEN;
S
sean 已提交
434
					store.offset[store.seen] = ftell(config_file);
L
Linus Torvalds 已提交
435
			}
S
sean 已提交
436
		}
437 438 439 440 441 442
	}
	return 0;
}

static void store_write_section(int fd, const char* key)
{
L
Linus Torvalds 已提交
443 444 445 446 447 448 449 450 451 452 453 454
	const char *dot = strchr(key, '.');
	int len1 = store.baselen, len2 = -1;

	dot = strchr(key, '.');
	if (dot) {
		int dotlen = dot - key;
		if (dotlen < len1) {
			len2 = len1 - dotlen - 1;
			len1 = dotlen;
		}
	}

455
	write(fd, "[", 1);
L
Linus Torvalds 已提交
456 457 458 459 460 461 462 463 464 465 466
	write(fd, key, len1);
	if (len2 >= 0) {
		write(fd, " \"", 2);
		while (--len2 >= 0) {
			unsigned char c = *++dot;
			if (c == '"')
				write(fd, "\\", 1);
			write(fd, &c, 1);
		}
		write(fd, "\"", 1);
	}
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
	write(fd, "]\n", 2);
}

static void store_write_pair(int fd, const char* key, const char* value)
{
	int i;

	write(fd, "\t", 1);
	write(fd, key+store.baselen+1,
		strlen(key+store.baselen+1));
	write(fd, " = ", 3);
	for (i = 0; value[i]; i++)
		switch (value[i]) {
		case '\n': write(fd, "\\n", 2); break;
		case '\t': write(fd, "\\t", 2); break;
		case '"': case '\\': write(fd, "\\", 1);
		default: write(fd, value+i, 1);
	}
	write(fd, "\n", 1);
}

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
static int find_beginning_of_line(const char* contents, int size,
	int offset_, int* found_bracket)
{
	int equal_offset = size, bracket_offset = size;
	int offset;

	for (offset = offset_-2; offset > 0 
			&& contents[offset] != '\n'; offset--)
		switch (contents[offset]) {
			case '=': equal_offset = offset; break;
			case ']': bracket_offset = offset; break;
		}
	if (bracket_offset < equal_offset) {
		*found_bracket = 1;
		offset = bracket_offset+1;
	} else
		offset++;

	return offset;
}

509 510
int git_config_set(const char* key, const char* value)
{
511
	return git_config_set_multivar(key, value, NULL, 0);
512 513 514 515 516
}

/*
 * If value==NULL, unset in (remove from) config,
 * if value_regex!=NULL, disregard key/value pairs where value does not match.
517 518 519
 * if multi_replace==0, nothing, or only one matching key/value is replaced,
 *     else all matching key/values (regardless how many) are removed,
 *     before the new pair is written.
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
 *
 * Returns 0 on success.
 *
 * This function does this:
 *
 * - it locks the config file by creating ".git/config.lock"
 *
 * - it then parses the config using store_aux() as validator to find
 *   the position on the key/value pair to replace. If it is to be unset,
 *   it must be found exactly once.
 *
 * - the config file is mmap()ed and the part before the match (if any) is
 *   written to the lock file, then the changed part and the rest.
 *
 * - the config file is removed and the lock file rename()d to it.
 *
 */
int git_config_set_multivar(const char* key, const char* value,
538
	const char* value_regex, int multi_replace)
539
{
L
Linus Torvalds 已提交
540
	int i, dot;
541
	int fd = -1, in_fd;
542
	int ret;
543 544
	char* config_filename;
	char* lock_file;
545
	const char* last_dot = strrchr(key, '.');
546

547 548 549 550 551 552
	config_filename = getenv("GIT_CONFIG");
	if (!config_filename) {
		config_filename = getenv("GIT_CONFIG_LOCAL");
		if (!config_filename)
			config_filename  = git_path("config");
	}
553 554
	config_filename = xstrdup(config_filename);
	lock_file = xstrdup(mkpath("%s.lock", config_filename));
555

556 557 558 559
	/*
	 * Since "key" actually contains the section name and the real
	 * key name separated by a dot, we have to know where the dot is.
	 */
560

561
	if (last_dot == NULL) {
562
		fprintf(stderr, "key does not contain a section: %s\n", key);
563 564
		ret = 2;
		goto out_free;
565
	}
566 567 568
	store.baselen = last_dot - key;

	store.multi_replace = multi_replace;
569 570 571 572

	/*
	 * Validate the key and while at it, lower case it for matching.
	 */
J
Jonas Fonseca 已提交
573
	store.key = xmalloc(strlen(key) + 1);
L
Linus Torvalds 已提交
574 575 576 577 578 579 580
	dot = 0;
	for (i = 0; key[i]; i++) {
		unsigned char c = key[i];
		if (c == '.')
			dot = 1;
		/* Leave the extended basename untouched.. */
		if (!dot || i > store.baselen) {
581
			if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
L
Linus Torvalds 已提交
582 583 584 585 586 587 588 589 590
				fprintf(stderr, "invalid key: %s\n", key);
				free(store.key);
				ret = 1;
				goto out_free;
			}
			c = tolower(c);
		}
		store.key[i] = c;
	}
591
	store.key[i] = 0;
592 593 594 595 596 597

	/*
	 * The lock_file serves a purpose in addition to locking: the new
	 * contents of .git/config will be written into it.
	 */
	fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
598
	if (fd < 0 || adjust_shared_perm(lock_file)) {
599 600
		fprintf(stderr, "could not lock config file\n");
		free(store.key);
601 602
		ret = -1;
		goto out_free;
603 604 605 606 607
	}

	/*
	 * If .git/config does not exist yet, write a minimal version.
	 */
608 609
	in_fd = open(config_filename, O_RDONLY);
	if ( in_fd < 0 ) {
610 611
		free(store.key);

612 613 614
		if ( ENOENT != errno ) {
			error("opening %s: %s", config_filename,
			      strerror(errno));
615 616
			ret = 3; /* same as "invalid config file" */
			goto out_free;
617
		}
618 619
		/* if nothing to unset, error out */
		if (value == NULL) {
620 621
			ret = 5;
			goto out_free;
622 623 624 625 626 627
		}

		store.key = (char*)key;
		store_write_section(fd, key);
		store_write_pair(fd, key, value);
	} else{
628
		struct stat st;
629
		char* contents;
630
		int i, copy_begin, copy_end, new_line = 0;
631 632 633 634

		if (value_regex == NULL)
			store.value_regex = NULL;
		else {
635 636 637 638 639 640
			if (value_regex[0] == '!') {
				store.do_not_match = 1;
				value_regex++;
			} else
				store.do_not_match = 0;

J
Jonas Fonseca 已提交
641
			store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
642 643
			if (regcomp(store.value_regex, value_regex,
					REG_EXTENDED)) {
A
Alex Riesen 已提交
644
				fprintf(stderr, "Invalid pattern: %s\n",
645 646
					value_regex);
				free(store.value_regex);
647 648
				ret = 6;
				goto out_free;
649 650 651
			}
		}

652
		store.offset[0] = 0;
653 654 655 656 657 658 659 660 661
		store.state = START;
		store.seen = 0;

		/*
		 * After this, store.offset will contain the *end* offset
		 * of the last match, or remain at 0 if no match was found.
		 * As a side effect, we make sure to transform only a valid
		 * existing config file.
		 */
662
		if (git_config_from_file(store_aux, config_filename)) {
663 664 665 666 667 668
			fprintf(stderr, "invalid config file\n");
			free(store.key);
			if (store.value_regex != NULL) {
				regfree(store.value_regex);
				free(store.value_regex);
			}
669 670
			ret = 3;
			goto out_free;
671 672 673 674 675 676 677 678
		}

		free(store.key);
		if (store.value_regex != NULL) {
			regfree(store.value_regex);
			free(store.value_regex);
		}

679 680 681
		/* if nothing to unset, or too many matches, error out */
		if ((store.seen == 0 && value == NULL) ||
				(store.seen > 1 && multi_replace == 0)) {
682 683
			ret = 5;
			goto out_free;
684 685
		}

686
		fstat(in_fd, &st);
687 688 689 690
		contents = mmap(NULL, st.st_size, PROT_READ,
			MAP_PRIVATE, in_fd, 0);
		close(in_fd);

691 692 693 694 695 696 697 698
		if (store.seen == 0)
			store.seen = 1;

		for (i = 0, copy_begin = 0; i < store.seen; i++) {
			if (store.offset[i] == 0) {
				store.offset[i] = copy_end = st.st_size;
			} else if (store.state != KEY_SEEN) {
				copy_end = store.offset[i];
699
			} else
700 701 702 703 704 705 706 707 708 709 710 711
				copy_end = find_beginning_of_line(
					contents, st.st_size,
					store.offset[i]-2, &new_line);

			/* write the first part of the config */
			if (copy_end > copy_begin) {
				write(fd, contents + copy_begin,
				copy_end - copy_begin);
				if (new_line)
					write(fd, "\n", 1);
			}
			copy_begin = store.offset[i];
712 713 714 715 716 717 718 719 720 721
		}

		/* write the pair (value == NULL means unset) */
		if (value != NULL) {
			if (store.state == START)
				store_write_section(fd, key);
			store_write_pair(fd, key, value);
		}

		/* write the rest of the config */
722 723 724
		if (copy_begin < st.st_size)
			write(fd, contents + copy_begin,
				st.st_size - copy_begin);
725 726

		munmap(contents, st.st_size);
J
Junio C Hamano 已提交
727
		unlink(config_filename);
728 729
	}

J
Junio C Hamano 已提交
730
	if (rename(lock_file, config_filename) < 0) {
731
		fprintf(stderr, "Could not rename the lock file?\n");
732 733
		ret = 4;
		goto out_free;
734 735
	}

736 737 738
	ret = 0;

out_free:
739 740
	if (0 <= fd)
		close(fd);
J
Junio C Hamano 已提交
741
	free(config_filename);
742 743
	if (lock_file) {
		unlink(lock_file);
744
		free(lock_file);
745
	}
746
	return ret;
747 748
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
int git_config_rename_section(const char *old_name, const char *new_name)
{
	int ret = 0;
	const char *config_filename;
	struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
	int out_fd;
	char buf[1024];

	config_filename = getenv("GIT_CONFIG");
	if (!config_filename) {
		config_filename = getenv("GIT_CONFIG_LOCAL");
		if (!config_filename)
			config_filename  = git_path("config");
	}
	config_filename = xstrdup(config_filename);
	out_fd = hold_lock_file_for_update(lock, config_filename, 0);
	if (out_fd < 0)
		return error("Could not lock config file!");

	if (!(config_file = fopen(config_filename, "rb")))
		return error("Could not open config file!");

	while (fgets(buf, sizeof(buf), config_file)) {
		int i;
		for (i = 0; buf[i] && isspace(buf[i]); i++)
			; /* do nothing */
		if (buf[i] == '[') {
			/* it's a section */
			int j = 0, dot = 0;
			for (i++; buf[i] && buf[i] != ']'; i++) {
				if (!dot && isspace(buf[i])) {
					dot = 1;
					if (old_name[j++] != '.')
						break;
					for (i++; isspace(buf[i]); i++)
						; /* do nothing */
					if (buf[i] != '"')
						break;
					continue;
				}
				if (buf[i] == '\\' && dot)
					i++;
				else if (buf[i] == '"' && dot) {
					for (i++; isspace(buf[i]); i++)
						; /* do_nothing */
					break;
				}
				if (buf[i] != old_name[j++])
					break;
			}
			if (buf[i] == ']') {
				/* old_name matches */
				ret++;
				store.baselen = strlen(new_name);
				store_write_section(out_fd, new_name);
				continue;
			}
		}
		write(out_fd, buf, strlen(buf));
	}
	if (close(out_fd) || commit_lock_file(lock) < 0)
		return error("Cannot commit config file!");
	return ret;
}
813