config.c 19.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 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
}

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;
241 242 243 244 245 246
		if (!strcasecmp(end, "k"))
			return val * 1024;
		if (!strcasecmp(end, "m"))
			return val * 1024 * 1024;
		if (!strcasecmp(end, "g"))
			return val * 1024 * 1024 * 1024;
L
Linus Torvalds 已提交
247
	}
248
	die("bad config value for '%s' in %s", name, config_file_name);
L
Linus Torvalds 已提交
249 250 251 252 253 254 255 256
}

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

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

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

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

292 293 294 295 296
	if (!strcmp(var, "core.legacyheaders")) {
		use_legacy_headers = git_config_bool(var, value);
		return 0;
	}

297 298 299 300 301 302 303 304 305 306
	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;
	}

307 308 309 310
	if (!strcmp(var, "core.packedgitwindowsize")) {
		int pgsz = getpagesize();
		packed_git_window_size = git_config_int(var, value);
		packed_git_window_size /= pgsz;
311 312
		if (packed_git_window_size < 2)
			packed_git_window_size = 2;
313 314 315 316
		packed_git_window_size *= pgsz;
		return 0;
	}

317 318 319 320 321
	if (!strcmp(var, "core.packedgitlimit")) {
		packed_git_limit = git_config_int(var, value);
		return 0;
	}

322
	if (!strcmp(var, "user.name")) {
323
		strlcpy(git_default_name, value, sizeof(git_default_name));
324 325 326 327
		return 0;
	}

	if (!strcmp(var, "user.email")) {
328
		strlcpy(git_default_email, value, sizeof(git_default_email));
329 330 331
		return 0;
	}

J
Junio C Hamano 已提交
332
	if (!strcmp(var, "i18n.commitencoding")) {
333
		git_commit_encoding = strdup(value);
J
Junio C Hamano 已提交
334 335 336
		return 0;
	}

337 338 339 340 341 342
	if (!strcmp(var, "i18n.logoutputencoding")) {
		git_log_output_encoding = strdup(value);
		return 0;
	}


343
	if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
344 345 346 347
		pager_use_color = git_config_bool(var,value);
		return 0;
	}

P
Petr Baudis 已提交
348
	/* Add other config variables here and to Documentation/config.txt. */
L
Linus Torvalds 已提交
349 350 351
	return 0;
}

352
int git_config_from_file(config_fn_t fn, const char *filename)
L
Linus Torvalds 已提交
353 354
{
	int ret;
355
	FILE *f = fopen(filename, "r");
L
Linus Torvalds 已提交
356 357 358 359

	ret = -1;
	if (f) {
		config_file = f;
360
		config_file_name = filename;
L
Linus Torvalds 已提交
361 362 363
		config_linenr = 1;
		ret = git_parse_file(fn);
		fclose(f);
364
		config_file_name = NULL;
L
Linus Torvalds 已提交
365 366 367
	}
	return ret;
}
368

369 370
int git_config(config_fn_t fn)
{
371 372 373 374 375 376 377 378
	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. */
379
	filename = getenv(CONFIG_ENVIRONMENT);
380 381
	if (!filename) {
		home = getenv("HOME");
382
		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
383
		if (!filename)
384
			filename = repo_config = xstrdup(git_path("config"));
385
	}
386 387

	if (home) {
388
		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
389
		if (!access(user_config, R_OK))
390 391 392 393 394
			ret = git_config_from_file(fn, user_config);
		free(user_config);
	}

	ret += git_config_from_file(fn, filename);
J
Junio C Hamano 已提交
395
	free(repo_config);
396
	return ret;
397 398
}

399 400 401
/*
 * Find all the stuff for git_config_set() below.
 */
402 403 404

#define MAX_MATCHES 512

405 406 407
static struct {
	int baselen;
	char* key;
408
	int do_not_match;
409
	regex_t* value_regex;
410 411
	int multi_replace;
	off_t offset[MAX_MATCHES];
412 413 414 415
	enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
	int seen;
} store;

416 417 418 419 420 421 422 423
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)));
}

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

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

467 468 469 470 471 472 473 474 475
static int write_error()
{
	fprintf(stderr, "Failed to write new configuration file\n");

	/* Same error code as "failed to rename". */
	return 4;
}

static int store_write_section(int fd, const char* key)
476
{
L
Linus Torvalds 已提交
477 478 479 480 481 482 483 484 485 486 487 488
	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;
		}
	}

489 490 491
	if (write_in_full(fd, "[", 1) != 1 ||
	    write_in_full(fd, key, len1) != len1)
		return 0;
L
Linus Torvalds 已提交
492
	if (len2 >= 0) {
493 494
		if (write_in_full(fd, " \"", 2) != 2)
			return 0;
L
Linus Torvalds 已提交
495 496 497
		while (--len2 >= 0) {
			unsigned char c = *++dot;
			if (c == '"')
498 499 500 501
				if (write_in_full(fd, "\\", 1) != 1)
					return 0;
			if (write_in_full(fd, &c, 1) != 1)
				return 0;
L
Linus Torvalds 已提交
502
		}
503 504
		if (write_in_full(fd, "\"", 1) != 1)
			return 0;
L
Linus Torvalds 已提交
505
	}
506 507 508 509
	if (write_in_full(fd, "]\n", 2) != 2)
		return 0;

	return 1;
510 511
}

512
static int store_write_pair(int fd, const char* key, const char* value)
513 514
{
	int i;
515
	int length = strlen(key+store.baselen+1);
516 517 518 519 520 521 522 523 524 525
	int quote = 0;

	/* Check to see if the value needs to be quoted. */
	if (value[0] == ' ')
		quote = 1;
	for (i = 0; value[i]; i++)
		if (value[i] == ';' || value[i] == '#')
			quote = 1;
	if (value[i-1] == ' ')
		quote = 1;
526

527 528 529 530
	if (write_in_full(fd, "\t", 1) != 1 ||
	    write_in_full(fd, key+store.baselen+1, length) != length ||
	    write_in_full(fd, " = ", 3) != 3)
		return 0;
531 532
	if (quote && write_in_full(fd, "\"", 1) != 1)
		return 0;
533 534
	for (i = 0; value[i]; i++)
		switch (value[i]) {
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
		case '\n':
			if (write_in_full(fd, "\\n", 2) != 2)
				return 0;
			break;
		case '\t':
			if (write_in_full(fd, "\\t", 2) != 2)
				return 0;
			break;
		case '"':
		case '\\':
			if (write_in_full(fd, "\\", 1) != 1)
				return 0;
		default:
			if (write_in_full(fd, value+i, 1) != 1)
				return 0;
			break;
		}
552 553
	if (quote && write_in_full(fd, "\"", 1) != 1)
		return 0;
554 555 556
	if (write_in_full(fd, "\n", 1) != 1)
		return 0;
	return 1;
557 558
}

559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
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;
}

580 581
int git_config_set(const char* key, const char* value)
{
582
	return git_config_set_multivar(key, value, NULL, 0);
583 584 585 586 587
}

/*
 * If value==NULL, unset in (remove from) config,
 * if value_regex!=NULL, disregard key/value pairs where value does not match.
588 589 590
 * 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.
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
 *
 * 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,
609
	const char* value_regex, int multi_replace)
610
{
L
Linus Torvalds 已提交
611
	int i, dot;
612
	int fd = -1, in_fd;
613
	int ret;
614 615
	char* config_filename;
	char* lock_file;
616
	const char* last_dot = strrchr(key, '.');
617

618
	config_filename = getenv(CONFIG_ENVIRONMENT);
619
	if (!config_filename) {
620
		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
621 622 623
		if (!config_filename)
			config_filename  = git_path("config");
	}
624 625
	config_filename = xstrdup(config_filename);
	lock_file = xstrdup(mkpath("%s.lock", config_filename));
626

627 628 629 630
	/*
	 * Since "key" actually contains the section name and the real
	 * key name separated by a dot, we have to know where the dot is.
	 */
631

632
	if (last_dot == NULL) {
633
		fprintf(stderr, "key does not contain a section: %s\n", key);
634 635
		ret = 2;
		goto out_free;
636
	}
637 638 639
	store.baselen = last_dot - key;

	store.multi_replace = multi_replace;
640 641 642 643

	/*
	 * Validate the key and while at it, lower case it for matching.
	 */
J
Jonas Fonseca 已提交
644
	store.key = xmalloc(strlen(key) + 1);
L
Linus Torvalds 已提交
645 646 647 648 649 650 651
	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) {
652
			if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
L
Linus Torvalds 已提交
653 654 655 656 657 658 659 660 661
				fprintf(stderr, "invalid key: %s\n", key);
				free(store.key);
				ret = 1;
				goto out_free;
			}
			c = tolower(c);
		}
		store.key[i] = c;
	}
662
	store.key[i] = 0;
663 664 665 666 667 668

	/*
	 * 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);
669
	if (fd < 0 || adjust_shared_perm(lock_file)) {
670 671
		fprintf(stderr, "could not lock config file\n");
		free(store.key);
672 673
		ret = -1;
		goto out_free;
674 675 676 677 678
	}

	/*
	 * If .git/config does not exist yet, write a minimal version.
	 */
679 680
	in_fd = open(config_filename, O_RDONLY);
	if ( in_fd < 0 ) {
681 682
		free(store.key);

683 684 685
		if ( ENOENT != errno ) {
			error("opening %s: %s", config_filename,
			      strerror(errno));
686 687
			ret = 3; /* same as "invalid config file" */
			goto out_free;
688
		}
689 690
		/* if nothing to unset, error out */
		if (value == NULL) {
691 692
			ret = 5;
			goto out_free;
693 694 695
		}

		store.key = (char*)key;
696 697 698 699 700
		if (!store_write_section(fd, key) ||
		    !store_write_pair(fd, key, value)) {
			ret = write_error();
			goto out_free;
		}
701
	} else{
702
		struct stat st;
703
		char* contents;
704
		int i, copy_begin, copy_end, new_line = 0;
705 706 707 708

		if (value_regex == NULL)
			store.value_regex = NULL;
		else {
709 710 711 712 713 714
			if (value_regex[0] == '!') {
				store.do_not_match = 1;
				value_regex++;
			} else
				store.do_not_match = 0;

J
Jonas Fonseca 已提交
715
			store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
716 717
			if (regcomp(store.value_regex, value_regex,
					REG_EXTENDED)) {
A
Alex Riesen 已提交
718
				fprintf(stderr, "Invalid pattern: %s\n",
719 720
					value_regex);
				free(store.value_regex);
721 722
				ret = 6;
				goto out_free;
723 724 725
			}
		}

726
		store.offset[0] = 0;
727 728 729 730 731 732 733 734 735
		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.
		 */
736
		if (git_config_from_file(store_aux, config_filename)) {
737 738 739 740 741 742
			fprintf(stderr, "invalid config file\n");
			free(store.key);
			if (store.value_regex != NULL) {
				regfree(store.value_regex);
				free(store.value_regex);
			}
743 744
			ret = 3;
			goto out_free;
745 746 747 748 749 750 751 752
		}

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

753 754 755
		/* if nothing to unset, or too many matches, error out */
		if ((store.seen == 0 && value == NULL) ||
				(store.seen > 1 && multi_replace == 0)) {
756 757
			ret = 5;
			goto out_free;
758 759
		}

760
		fstat(in_fd, &st);
761
		contents = xmmap(NULL, st.st_size, PROT_READ,
762 763 764
			MAP_PRIVATE, in_fd, 0);
		close(in_fd);

765 766 767 768 769 770 771 772
		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];
773
			} else
774 775 776 777 778 779
				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) {
780
				write_in_full(fd, contents + copy_begin,
781 782
				copy_end - copy_begin);
				if (new_line)
783
					write_in_full(fd, "\n", 1);
784 785
			}
			copy_begin = store.offset[i];
786 787 788 789 790
		}

		/* write the pair (value == NULL means unset) */
		if (value != NULL) {
			if (store.state == START)
791 792 793 794 795 796 797 798
				if (!store_write_section(fd, key)) {
					ret = write_error();
					goto out_free;
				}
			if (!store_write_pair(fd, key, value)) {
				ret = write_error();
				goto out_free;
			}
799 800 801
		}

		/* write the rest of the config */
802
		if (copy_begin < st.st_size)
803
			write_in_full(fd, contents + copy_begin,
804
				st.st_size - copy_begin);
805 806

		munmap(contents, st.st_size);
J
Junio C Hamano 已提交
807
		unlink(config_filename);
808 809
	}

J
Junio C Hamano 已提交
810
	if (rename(lock_file, config_filename) < 0) {
811
		fprintf(stderr, "Could not rename the lock file?\n");
812 813
		ret = 4;
		goto out_free;
814 815
	}

816 817 818
	ret = 0;

out_free:
819 820
	if (0 <= fd)
		close(fd);
J
Junio C Hamano 已提交
821
	free(config_filename);
822 823
	if (lock_file) {
		unlink(lock_file);
824
		free(lock_file);
825
	}
826
	return ret;
827 828
}

829 830 831
int git_config_rename_section(const char *old_name, const char *new_name)
{
	int ret = 0;
832
	char *config_filename;
833 834 835 836
	struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
	int out_fd;
	char buf[1024];

837
	config_filename = getenv(CONFIG_ENVIRONMENT);
838
	if (!config_filename) {
839
		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
840 841 842 843 844
		if (!config_filename)
			config_filename  = git_path("config");
	}
	config_filename = xstrdup(config_filename);
	out_fd = hold_lock_file_for_update(lock, config_filename, 0);
845 846 847 848
	if (out_fd < 0) {
		ret = error("Could not lock config file!");
		goto out;
	}
849

850 851 852 853
	if (!(config_file = fopen(config_filename, "rb"))) {
		ret = error("Could not open config file!");
		goto out;
	}
854 855 856

	while (fgets(buf, sizeof(buf), config_file)) {
		int i;
857
		int length;
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
		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);
888 889 890 891
				if (!store_write_section(out_fd, new_name)) {
					ret = write_error();
					goto out;
				}
892 893 894
				continue;
			}
		}
895 896 897 898 899
		length = strlen(buf);
		if (write_in_full(out_fd, buf, length) != length) {
			ret = write_error();
			goto out;
		}
900
	}
901
	fclose(config_file);
902
	if (close(out_fd) || commit_lock_file(lock) < 0)
903
			ret = error("Cannot commit config file!");
904 905
 out:
	free(config_filename);
906 907
	return ret;
}
908