config.c 17.8 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 9 10 11 12
#include "cache.h"

#define MAXNAME (256)

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

	c = '\n';
	if ((f = config_file) != NULL) {
		c = fgetc(f);
23 24 25 26 27 28 29 30
		if (c == '\r') {
			/* DOS like systems */
			c = fgetc(f);
			if (c != '\n') {
				ungetc(c, f);
				c = '\r';
			}
		}
L
Linus Torvalds 已提交
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 61
		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;
		}
62 63 64 65 66 67
		if (!quote) {
			if (c == ';' || c == '#') {
				comment = 1;
				continue;
			}
		}
L
Linus Torvalds 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
		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;
87 88 89 90 91 92
			/* Some characters escape as themselves */
			case '\\': case '"':
				break;
			/* Reject unknown escape sequences */
			default:
				return NULL;
L
Linus Torvalds 已提交
93 94 95 96 97 98 99 100 101 102 103 104
			}
			value[len++] = c;
			continue;
		}
		if (c == '"') {
			quote = 1-quote;
			continue;
		}
		value[len++] = c;
	}
}

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

L
Linus Torvalds 已提交
110 111 112 113 114 115 116 117 118 119
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;
120
		if (!iskeychar(c))
L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
			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 已提交
141 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
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 已提交
176 177 178 179 180 181 182 183 184 185
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 已提交
186 187
		if (isspace(c))
			return get_extended_base_var(name, baselen, c);
188
		if (!iskeychar(c) && c != '.')
L
Linus Torvalds 已提交
189 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
			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;
227
		var[baselen] = tolower(c);
L
Linus Torvalds 已提交
228 229 230
		if (get_value(fn, var, baselen+1) < 0)
			break;
	}
231
	die("bad config file line %d in %s", config_linenr, config_file_name);
L
Linus Torvalds 已提交
232 233 234 235 236 237 238 239 240 241
}

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;
	}
242
	die("bad config value for '%s' in %s", name, config_file_name);
L
Linus Torvalds 已提交
243 244 245 246 247 248 249 250
}

int git_config_bool(const char *name, const char *value)
{
	if (!value)
		return 1;
	if (!*value)
		return 0;
251
	if (!strcasecmp(value, "true") || !strcasecmp(value, "yes"))
L
Linus Torvalds 已提交
252
		return 1;
253
	if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
L
Linus Torvalds 已提交
254 255 256 257 258 259 260 261 262 263 264 265
		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 已提交
266 267 268 269 270
	if (!strcmp(var, "core.ignorestat")) {
		assume_unchanged = git_config_bool(var, value);
		return 0;
	}

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

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

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

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

291 292 293 294 295 296 297 298 299 300
	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;
	}

301 302 303 304
	if (!strcmp(var, "core.packedgitwindowsize")) {
		int pgsz = getpagesize();
		packed_git_window_size = git_config_int(var, value);
		packed_git_window_size /= pgsz;
305 306
		if (packed_git_window_size < 2)
			packed_git_window_size = 2;
307 308 309 310
		packed_git_window_size *= pgsz;
		return 0;
	}

311 312 313 314 315
	if (!strcmp(var, "core.packedgitlimit")) {
		packed_git_limit = git_config_int(var, value);
		return 0;
	}

316
	if (!strcmp(var, "user.name")) {
317
		strlcpy(git_default_name, value, sizeof(git_default_name));
318 319 320 321
		return 0;
	}

	if (!strcmp(var, "user.email")) {
322
		strlcpy(git_default_email, value, sizeof(git_default_email));
323 324 325
		return 0;
	}

J
Junio C Hamano 已提交
326
	if (!strcmp(var, "i18n.commitencoding")) {
327
		git_commit_encoding = strdup(value);
J
Junio C Hamano 已提交
328 329 330
		return 0;
	}

331 332 333 334 335 336
	if (!strcmp(var, "i18n.logoutputencoding")) {
		git_log_output_encoding = strdup(value);
		return 0;
	}


337
	if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
338 339 340 341
		pager_use_color = git_config_bool(var,value);
		return 0;
	}

P
Petr Baudis 已提交
342
	/* Add other config variables here and to Documentation/config.txt. */
L
Linus Torvalds 已提交
343 344 345
	return 0;
}

346
int git_config_from_file(config_fn_t fn, const char *filename)
L
Linus Torvalds 已提交
347 348
{
	int ret;
349
	FILE *f = fopen(filename, "r");
L
Linus Torvalds 已提交
350 351 352 353

	ret = -1;
	if (f) {
		config_file = f;
354
		config_file_name = filename;
L
Linus Torvalds 已提交
355 356 357
		config_linenr = 1;
		ret = git_parse_file(fn);
		fclose(f);
358
		config_file_name = NULL;
L
Linus Torvalds 已提交
359 360 361
	}
	return ret;
}
362

363 364
int git_config(config_fn_t fn)
{
365 366 367 368 369 370 371 372
	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. */
373
	filename = getenv(CONFIG_ENVIRONMENT);
374 375
	if (!filename) {
		home = getenv("HOME");
376
		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
377
		if (!filename)
378
			filename = repo_config = xstrdup(git_path("config"));
379
	}
380 381

	if (home) {
382
		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
383
		if (!access(user_config, R_OK))
384 385 386 387 388
			ret = git_config_from_file(fn, user_config);
		free(user_config);
	}

	ret += git_config_from_file(fn, filename);
J
Junio C Hamano 已提交
389
	free(repo_config);
390
	return ret;
391 392
}

393 394 395
/*
 * Find all the stuff for git_config_set() below.
 */
396 397 398

#define MAX_MATCHES 512

399 400 401
static struct {
	int baselen;
	char* key;
402
	int do_not_match;
403
	regex_t* value_regex;
404 405
	int multi_replace;
	off_t offset[MAX_MATCHES];
406 407 408 409
	enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
	int seen;
} store;

410 411 412 413 414 415 416 417
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)));
}

418 419 420 421
static int store_aux(const char* key, const char* value)
{
	switch (store.state) {
	case KEY_SEEN:
422
		if (matches(key, value)) {
423
			if (store.seen == 1 && store.multi_replace == 0) {
424 425 426
				fprintf(stderr,
					"Warning: %s has multiple values\n",
					key);
427 428 429
			} else if (store.seen >= MAX_MATCHES) {
				fprintf(stderr, "Too many matches\n");
				return 1;
430
			}
431 432

			store.offset[store.seen] = ftell(config_file);
433 434 435 436 437 438 439 440
			store.seen++;
		}
		break;
	case SECTION_SEEN:
		if (strncmp(key, store.key, store.baselen+1)) {
			store.state = SECTION_END_SEEN;
			break;
		} else
441 442
			/* do not increment matches: this is no match */
			store.offset[store.seen] = ftell(config_file);
443 444 445
		/* fallthru */
	case SECTION_END_SEEN:
	case START:
446
		if (matches(key, value)) {
447
			store.offset[store.seen] = ftell(config_file);
448 449
			store.state = KEY_SEEN;
			store.seen++;
L
Linus Torvalds 已提交
450 451
		} else {
			if (strrchr(key, '.') - key == store.baselen &&
S
sean 已提交
452
			      !strncmp(key, store.key, store.baselen)) {
S
sean 已提交
453
					store.state = SECTION_SEEN;
S
sean 已提交
454
					store.offset[store.seen] = ftell(config_file);
L
Linus Torvalds 已提交
455
			}
S
sean 已提交
456
		}
457 458 459 460 461 462
	}
	return 0;
}

static void store_write_section(int fd, const char* key)
{
L
Linus Torvalds 已提交
463 464 465 466 467 468 469 470 471 472 473 474
	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;
		}
	}

475
	write(fd, "[", 1);
L
Linus Torvalds 已提交
476 477 478 479 480 481 482 483 484 485 486
	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);
	}
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
	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);
}

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
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;
}

529 530
int git_config_set(const char* key, const char* value)
{
531
	return git_config_set_multivar(key, value, NULL, 0);
532 533 534 535 536
}

/*
 * If value==NULL, unset in (remove from) config,
 * if value_regex!=NULL, disregard key/value pairs where value does not match.
537 538 539
 * 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.
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
 *
 * 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,
558
	const char* value_regex, int multi_replace)
559
{
L
Linus Torvalds 已提交
560
	int i, dot;
561
	int fd = -1, in_fd;
562
	int ret;
563 564
	char* config_filename;
	char* lock_file;
565
	const char* last_dot = strrchr(key, '.');
566

567
	config_filename = getenv(CONFIG_ENVIRONMENT);
568
	if (!config_filename) {
569
		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
570 571 572
		if (!config_filename)
			config_filename  = git_path("config");
	}
573 574
	config_filename = xstrdup(config_filename);
	lock_file = xstrdup(mkpath("%s.lock", config_filename));
575

576 577 578 579
	/*
	 * Since "key" actually contains the section name and the real
	 * key name separated by a dot, we have to know where the dot is.
	 */
580

581
	if (last_dot == NULL) {
582
		fprintf(stderr, "key does not contain a section: %s\n", key);
583 584
		ret = 2;
		goto out_free;
585
	}
586 587 588
	store.baselen = last_dot - key;

	store.multi_replace = multi_replace;
589 590 591 592

	/*
	 * Validate the key and while at it, lower case it for matching.
	 */
J
Jonas Fonseca 已提交
593
	store.key = xmalloc(strlen(key) + 1);
L
Linus Torvalds 已提交
594 595 596 597 598 599 600
	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) {
601
			if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
L
Linus Torvalds 已提交
602 603 604 605 606 607 608 609 610
				fprintf(stderr, "invalid key: %s\n", key);
				free(store.key);
				ret = 1;
				goto out_free;
			}
			c = tolower(c);
		}
		store.key[i] = c;
	}
611
	store.key[i] = 0;
612 613 614 615 616 617

	/*
	 * 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);
618
	if (fd < 0 || adjust_shared_perm(lock_file)) {
619 620
		fprintf(stderr, "could not lock config file\n");
		free(store.key);
621 622
		ret = -1;
		goto out_free;
623 624 625 626 627
	}

	/*
	 * If .git/config does not exist yet, write a minimal version.
	 */
628 629
	in_fd = open(config_filename, O_RDONLY);
	if ( in_fd < 0 ) {
630 631
		free(store.key);

632 633 634
		if ( ENOENT != errno ) {
			error("opening %s: %s", config_filename,
			      strerror(errno));
635 636
			ret = 3; /* same as "invalid config file" */
			goto out_free;
637
		}
638 639
		/* if nothing to unset, error out */
		if (value == NULL) {
640 641
			ret = 5;
			goto out_free;
642 643 644 645 646 647
		}

		store.key = (char*)key;
		store_write_section(fd, key);
		store_write_pair(fd, key, value);
	} else{
648
		struct stat st;
649
		char* contents;
650
		int i, copy_begin, copy_end, new_line = 0;
651 652 653 654

		if (value_regex == NULL)
			store.value_regex = NULL;
		else {
655 656 657 658 659 660
			if (value_regex[0] == '!') {
				store.do_not_match = 1;
				value_regex++;
			} else
				store.do_not_match = 0;

J
Jonas Fonseca 已提交
661
			store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
662 663
			if (regcomp(store.value_regex, value_regex,
					REG_EXTENDED)) {
A
Alex Riesen 已提交
664
				fprintf(stderr, "Invalid pattern: %s\n",
665 666
					value_regex);
				free(store.value_regex);
667 668
				ret = 6;
				goto out_free;
669 670 671
			}
		}

672
		store.offset[0] = 0;
673 674 675 676 677 678 679 680 681
		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.
		 */
682
		if (git_config_from_file(store_aux, config_filename)) {
683 684 685 686 687 688
			fprintf(stderr, "invalid config file\n");
			free(store.key);
			if (store.value_regex != NULL) {
				regfree(store.value_regex);
				free(store.value_regex);
			}
689 690
			ret = 3;
			goto out_free;
691 692 693 694 695 696 697 698
		}

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

699 700 701
		/* if nothing to unset, or too many matches, error out */
		if ((store.seen == 0 && value == NULL) ||
				(store.seen > 1 && multi_replace == 0)) {
702 703
			ret = 5;
			goto out_free;
704 705
		}

706
		fstat(in_fd, &st);
707
		contents = xmmap(NULL, st.st_size, PROT_READ,
708 709 710
			MAP_PRIVATE, in_fd, 0);
		close(in_fd);

711 712 713 714 715 716 717 718
		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];
719
			} else
720 721 722 723 724 725 726 727 728 729 730 731
				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];
732 733 734 735 736 737 738 739 740 741
		}

		/* 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 */
742 743 744
		if (copy_begin < st.st_size)
			write(fd, contents + copy_begin,
				st.st_size - copy_begin);
745 746

		munmap(contents, st.st_size);
J
Junio C Hamano 已提交
747
		unlink(config_filename);
748 749
	}

J
Junio C Hamano 已提交
750
	if (rename(lock_file, config_filename) < 0) {
751
		fprintf(stderr, "Could not rename the lock file?\n");
752 753
		ret = 4;
		goto out_free;
754 755
	}

756 757 758
	ret = 0;

out_free:
759 760
	if (0 <= fd)
		close(fd);
J
Junio C Hamano 已提交
761
	free(config_filename);
762 763
	if (lock_file) {
		unlink(lock_file);
764
		free(lock_file);
765
	}
766
	return ret;
767 768
}

769 770 771
int git_config_rename_section(const char *old_name, const char *new_name)
{
	int ret = 0;
772
	char *config_filename;
773 774 775 776
	struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
	int out_fd;
	char buf[1024];

777
	config_filename = getenv(CONFIG_ENVIRONMENT);
778
	if (!config_filename) {
779
		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
780 781 782 783 784
		if (!config_filename)
			config_filename  = git_path("config");
	}
	config_filename = xstrdup(config_filename);
	out_fd = hold_lock_file_for_update(lock, config_filename, 0);
785 786 787 788
	if (out_fd < 0) {
		ret = error("Could not lock config file!");
		goto out;
	}
789

790 791 792 793
	if (!(config_file = fopen(config_filename, "rb"))) {
		ret = error("Could not open config file!");
		goto out;
	}
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832

	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));
	}
833
	fclose(config_file);
834
	if (close(out_fd) || commit_lock_file(lock) < 0)
835 836 837
		ret = error("Cannot commit config file!");
 out:
	free(config_filename);
838 839
	return ret;
}
840