config.c 23.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 "exec_cmd.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
static int config_linenr;
16
static int config_file_eof;
17 18
static int zlib_compression_seen;

L
Linus Torvalds 已提交
19 20 21 22 23 24 25 26
static int get_next_char(void)
{
	int c;
	FILE *f;

	c = '\n';
	if ((f = config_file) != NULL) {
		c = fgetc(f);
27 28 29 30 31 32 33 34
		if (c == '\r') {
			/* DOS like systems */
			c = fgetc(f);
			if (c != '\n') {
				ungetc(c, f);
				c = '\r';
			}
		}
L
Linus Torvalds 已提交
35 36 37
		if (c == '\n')
			config_linenr++;
		if (c == EOF) {
38
			config_file_eof = 1;
L
Linus Torvalds 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
			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;
		}
66 67 68 69 70 71
		if (!quote) {
			if (c == ';' || c == '#') {
				comment = 1;
				continue;
			}
		}
L
Linus Torvalds 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
		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;
91 92 93 94 95 96
			/* Some characters escape as themselves */
			case '\\': case '"':
				break;
			/* Reject unknown escape sequences */
			default:
				return NULL;
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105 106 107 108
			}
			value[len++] = c;
			continue;
		}
		if (c == '"') {
			quote = 1-quote;
			continue;
		}
		value[len++] = c;
	}
}

109 110 111 112 113
static inline int iskeychar(int c)
{
	return isalnum(c) || c == '-';
}

L
Linus Torvalds 已提交
114 115 116 117 118 119 120 121
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();
122
		if (config_file_eof)
L
Linus Torvalds 已提交
123
			break;
124
		if (!iskeychar(c))
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
			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 已提交
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 177 178 179
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 已提交
180 181 182 183 184 185
static int get_base_var(char *name)
{
	int baselen = 0;

	for (;;) {
		int c = get_next_char();
186
		if (config_file_eof)
L
Linus Torvalds 已提交
187 188 189
			return -1;
		if (c == ']')
			return baselen;
L
Linus Torvalds 已提交
190 191
		if (isspace(c))
			return get_extended_base_var(name, baselen, c);
192
		if (!iskeychar(c) && c != '.')
L
Linus Torvalds 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
			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') {
209
			if (config_file_eof)
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
				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;
230
		var[baselen] = tolower(c);
L
Linus Torvalds 已提交
231 232 233
		if (get_value(fn, var, baselen+1) < 0)
			break;
	}
234
	die("bad config file line %d in %s", config_linenr, config_file_name);
L
Linus Torvalds 已提交
235 236
}

237
static int parse_unit_factor(const char *end, unsigned long *val)
238 239 240
{
	if (!*end)
		return 1;
241 242 243 244 245 246 247 248 249 250 251 252 253
	else if (!strcasecmp(end, "k")) {
		*val *= 1024;
		return 1;
	}
	else if (!strcasecmp(end, "m")) {
		*val *= 1024 * 1024;
		return 1;
	}
	else if (!strcasecmp(end, "g")) {
		*val *= 1024 * 1024 * 1024;
		return 1;
	}
	return 0;
254 255 256 257 258 259 260
}

int git_parse_long(const char *value, long *ret)
{
	if (value && *value) {
		char *end;
		long val = strtol(value, &end, 0);
261 262 263 264
		unsigned long factor = 1;
		if (!parse_unit_factor(end, &factor))
			return 0;
		*ret = val * factor;
265 266 267 268 269 270
		return 1;
	}
	return 0;
}

int git_parse_ulong(const char *value, unsigned long *ret)
L
Linus Torvalds 已提交
271 272 273
{
	if (value && *value) {
		char *end;
274
		unsigned long val = strtoul(value, &end, 0);
275 276 277
		if (!parse_unit_factor(end, &val))
			return 0;
		*ret = val;
278
		return 1;
L
Linus Torvalds 已提交
279
	}
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	return 0;
}

int git_config_int(const char *name, const char *value)
{
	long ret;
	if (!git_parse_long(value, &ret))
		die("bad config value for '%s' in %s", name, config_file_name);
	return ret;
}

unsigned long git_config_ulong(const char *name, const char *value)
{
	unsigned long ret;
	if (!git_parse_ulong(value, &ret))
		die("bad config value for '%s' in %s", name, config_file_name);
	return ret;
L
Linus Torvalds 已提交
297 298 299 300 301 302 303 304
}

int git_config_bool(const char *name, const char *value)
{
	if (!value)
		return 1;
	if (!*value)
		return 0;
305
	if (!strcasecmp(value, "true") || !strcasecmp(value, "yes"))
L
Linus Torvalds 已提交
306
		return 1;
307
	if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
L
Linus Torvalds 已提交
308 309 310 311 312 313 314 315 316 317 318 319
		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;
	}

320 321 322 323 324
	if (!strcmp(var, "core.quotepath")) {
		quote_path_fully = git_config_bool(var, value);
		return 0;
	}

325 326 327 328 329
	if (!strcmp(var, "core.symlinks")) {
		has_symlinks = git_config_bool(var, value);
		return 0;
	}

330 331 332 333 334
	if (!strcmp(var, "core.bare")) {
		is_bare_repository_cfg = git_config_bool(var, value);
		return 0;
	}

J
Junio C Hamano 已提交
335 336 337 338 339
	if (!strcmp(var, "core.ignorestat")) {
		assume_unchanged = git_config_bool(var, value);
		return 0;
	}

340 341
	if (!strcmp(var, "core.prefersymlinkrefs")) {
		prefer_symlink_refs = git_config_bool(var, value);
342 343 344
		return 0;
	}

345 346 347 348 349
	if (!strcmp(var, "core.logallrefupdates")) {
		log_all_ref_updates = git_config_bool(var, value);
		return 0;
	}

350 351 352 353 354
	if (!strcmp(var, "core.warnambiguousrefs")) {
		warn_ambiguous_refs = git_config_bool(var, value);
		return 0;
	}

355
	if (!strcmp(var, "core.loosecompression")) {
356 357 358 359 360 361
		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;
362 363 364 365 366 367 368 369 370 371 372 373 374 375
		zlib_compression_seen = 1;
		return 0;
	}

	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);
		core_compression_level = level;
		core_compression_seen = 1;
		if (!zlib_compression_seen)
			zlib_compression_level = level;
376 377 378
		return 0;
	}

379
	if (!strcmp(var, "core.packedgitwindowsize")) {
380
		int pgsz_x2 = getpagesize() * 2;
381
		packed_git_window_size = git_config_int(var, value);
382 383 384 385 386 387

		/* This value must be multiple of (pagesize * 2) */
		packed_git_window_size /= pgsz_x2;
		if (packed_git_window_size < 1)
			packed_git_window_size = 1;
		packed_git_window_size *= pgsz_x2;
388 389 390
		return 0;
	}

391 392 393 394 395
	if (!strcmp(var, "core.packedgitlimit")) {
		packed_git_limit = git_config_int(var, value);
		return 0;
	}

396 397 398 399 400
	if (!strcmp(var, "core.deltabasecachelimit")) {
		delta_base_cache_limit = git_config_int(var, value);
		return 0;
	}

L
Linus Torvalds 已提交
401
	if (!strcmp(var, "core.autocrlf")) {
L
Linus Torvalds 已提交
402 403 404 405
		if (value && !strcasecmp(value, "input")) {
			auto_crlf = -1;
			return 0;
		}
L
Linus Torvalds 已提交
406 407 408 409
		auto_crlf = git_config_bool(var, value);
		return 0;
	}

410
	if (!strcmp(var, "user.name")) {
411
		strlcpy(git_default_name, value, sizeof(git_default_name));
412 413 414 415
		return 0;
	}

	if (!strcmp(var, "user.email")) {
416
		strlcpy(git_default_email, value, sizeof(git_default_email));
417 418 419
		return 0;
	}

J
Junio C Hamano 已提交
420
	if (!strcmp(var, "i18n.commitencoding")) {
S
Shawn O. Pearce 已提交
421
		git_commit_encoding = xstrdup(value);
J
Junio C Hamano 已提交
422 423 424
		return 0;
	}

425
	if (!strcmp(var, "i18n.logoutputencoding")) {
S
Shawn O. Pearce 已提交
426
		git_log_output_encoding = xstrdup(value);
427 428 429 430
		return 0;
	}


431
	if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
432 433 434 435
		pager_use_color = git_config_bool(var,value);
		return 0;
	}

436 437 438 439 440
	if (!strcmp(var, "core.pager")) {
		pager_program = xstrdup(value);
		return 0;
	}

441 442 443 444 445
	if (!strcmp(var, "core.editor")) {
		editor_program = xstrdup(value);
		return 0;
	}

J
Junio C Hamano 已提交
446 447 448 449 450 451 452
	if (!strcmp(var, "core.excludesfile")) {
		if (!value)
			die("core.excludesfile without value");
		excludes_file = xstrdup(value);
		return 0;
	}

453
	if (!strcmp(var, "core.whitespace")) {
454
		whitespace_rule_cfg = parse_whitespace_rule(value);
455 456 457
		return 0;
	}

P
Petr Baudis 已提交
458
	/* Add other config variables here and to Documentation/config.txt. */
L
Linus Torvalds 已提交
459 460 461
	return 0;
}

462
int git_config_from_file(config_fn_t fn, const char *filename)
L
Linus Torvalds 已提交
463 464
{
	int ret;
465
	FILE *f = fopen(filename, "r");
L
Linus Torvalds 已提交
466 467 468 469

	ret = -1;
	if (f) {
		config_file = f;
470
		config_file_name = filename;
L
Linus Torvalds 已提交
471
		config_linenr = 1;
472
		config_file_eof = 0;
L
Linus Torvalds 已提交
473 474
		ret = git_parse_file(fn);
		fclose(f);
475
		config_file_name = NULL;
L
Linus Torvalds 已提交
476 477 478
	}
	return ret;
}
479

480 481
const char *git_etc_gitconfig(void)
{
482 483 484 485 486 487 488 489 490 491 492
	static const char *system_wide;
	if (!system_wide) {
		system_wide = ETC_GITCONFIG;
		if (!is_absolute_path(system_wide)) {
			/* interpret path relative to exec-dir */
			const char *exec_path = git_exec_path();
			system_wide = prefix_path(exec_path, strlen(exec_path),
						system_wide);
		}
	}
	return system_wide;
493 494
}

495 496
int git_config(config_fn_t fn)
{
497 498 499 500 501 502 503 504
	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. */
505
	filename = getenv(CONFIG_ENVIRONMENT);
506
	if (!filename) {
507 508
		if (!access(git_etc_gitconfig(), R_OK))
			ret += git_config_from_file(fn, git_etc_gitconfig());
509
		home = getenv("HOME");
510
		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
511
		if (!filename)
512
			filename = repo_config = xstrdup(git_path("config"));
513
	}
514 515

	if (home) {
516
		char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
517
		if (!access(user_config, R_OK))
518 519 520 521 522
			ret = git_config_from_file(fn, user_config);
		free(user_config);
	}

	ret += git_config_from_file(fn, filename);
J
Junio C Hamano 已提交
523
	free(repo_config);
524
	return ret;
525 526
}

527 528 529
/*
 * Find all the stuff for git_config_set() below.
 */
530 531 532

#define MAX_MATCHES 512

533 534 535
static struct {
	int baselen;
	char* key;
536
	int do_not_match;
537
	regex_t* value_regex;
538
	int multi_replace;
539
	size_t offset[MAX_MATCHES];
540 541 542 543
	enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
	int seen;
} store;

544 545 546 547 548 549 550 551
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)));
}

552 553
static int store_aux(const char* key, const char* value)
{
554 555 556
	const char *ep;
	size_t section_len;

557 558
	switch (store.state) {
	case KEY_SEEN:
559
		if (matches(key, value)) {
560
			if (store.seen == 1 && store.multi_replace == 0) {
561 562 563
				fprintf(stderr,
					"Warning: %s has multiple values\n",
					key);
564 565 566
			} else if (store.seen >= MAX_MATCHES) {
				fprintf(stderr, "Too many matches\n");
				return 1;
567
			}
568 569

			store.offset[store.seen] = ftell(config_file);
570 571 572 573
			store.seen++;
		}
		break;
	case SECTION_SEEN:
574 575 576 577 578 579 580 581 582 583 584 585 586 587
		/*
		 * What we are looking for is in store.key (both
		 * section and var), and its section part is baselen
		 * long.  We found key (again, both section and var).
		 * We would want to know if this key is in the same
		 * section as what we are looking for.  We already
		 * know we are in the same section as what should
		 * hold store.key.
		 */
		ep = strrchr(key, '.');
		section_len = ep - key;

		if ((section_len != store.baselen) ||
		    memcmp(key, store.key, section_len+1)) {
588 589
			store.state = SECTION_END_SEEN;
			break;
590 591 592 593 594 595 596
		}

		/*
		 * Do not increment matches: this is no match, but we
		 * just made sure we are in the desired section.
		 */
		store.offset[store.seen] = ftell(config_file);
597 598 599
		/* fallthru */
	case SECTION_END_SEEN:
	case START:
600
		if (matches(key, value)) {
601
			store.offset[store.seen] = ftell(config_file);
602 603
			store.state = KEY_SEEN;
			store.seen++;
L
Linus Torvalds 已提交
604 605
		} else {
			if (strrchr(key, '.') - key == store.baselen &&
S
sean 已提交
606
			      !strncmp(key, store.key, store.baselen)) {
S
sean 已提交
607
					store.state = SECTION_SEEN;
S
sean 已提交
608
					store.offset[store.seen] = ftell(config_file);
L
Linus Torvalds 已提交
609
			}
S
sean 已提交
610
		}
611 612 613 614
	}
	return 0;
}

J
Junio C Hamano 已提交
615
static int write_error(void)
616 617 618 619 620 621 622 623
{
	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)
624
{
625 626 627
	const char *dot;
	int i, success;
	struct strbuf sb;
L
Linus Torvalds 已提交
628

629 630
	strbuf_init(&sb, 0);
	dot = memchr(key, '.', store.baselen);
L
Linus Torvalds 已提交
631
	if (dot) {
632 633 634 635 636
		strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
		for (i = dot - key + 1; i < store.baselen; i++) {
			if (key[i] == '"')
				strbuf_addch(&sb, '\\');
			strbuf_addch(&sb, key[i]);
L
Linus Torvalds 已提交
637
		}
638 639 640
		strbuf_addstr(&sb, "\"]\n");
	} else {
		strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
L
Linus Torvalds 已提交
641 642
	}

643 644
	success = write_in_full(fd, sb.buf, sb.len) == sb.len;
	strbuf_release(&sb);
645

646
	return success;
647 648
}

649
static int store_write_pair(int fd, const char* key, const char* value)
650
{
651 652 653 654
	int i, success;
	int length = strlen(key + store.baselen + 1);
	const char *quote = "";
	struct strbuf sb;
655

656 657 658 659 660 661 662
	/*
	 * Check to see if the value needs to be surrounded with a dq pair.
	 * Note that problematic characters are always backslash-quoted; this
	 * check is about not losing leading or trailing SP and strings that
	 * follow beginning-of-comment characters (i.e. ';' and '#') by the
	 * configuration parser.
	 */
663
	if (value[0] == ' ')
664
		quote = "\"";
665 666
	for (i = 0; value[i]; i++)
		if (value[i] == ';' || value[i] == '#')
667 668 669 670 671 672 673
			quote = "\"";
	if (i && value[i - 1] == ' ')
		quote = "\"";

	strbuf_init(&sb, 0);
	strbuf_addf(&sb, "\t%.*s = %s",
		    length, key + store.baselen + 1, quote);
674 675 676

	for (i = 0; value[i]; i++)
		switch (value[i]) {
677
		case '\n':
678
			strbuf_addstr(&sb, "\\n");
679 680
			break;
		case '\t':
681
			strbuf_addstr(&sb, "\\t");
682 683 684
			break;
		case '"':
		case '\\':
685
			strbuf_addch(&sb, '\\');
686
		default:
687
			strbuf_addch(&sb, value[i]);
688 689
			break;
		}
690 691 692 693 694 695
	strbuf_addf(&sb, "%s\n", quote);

	success = write_in_full(fd, sb.buf, sb.len) == sb.len;
	strbuf_release(&sb);

	return success;
696 697
}

698 699
static ssize_t find_beginning_of_line(const char* contents, size_t size,
	size_t offset_, int* found_bracket)
700
{
701 702
	size_t equal_offset = size, bracket_offset = size;
	ssize_t offset;
703

J
Junio C Hamano 已提交
704
	for (offset = offset_-2; offset > 0
705 706 707 708 709 710 711 712 713 714 715 716 717 718
			&& 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;
}

719 720
int git_config_set(const char* key, const char* value)
{
721
	return git_config_set_multivar(key, value, NULL, 0);
722 723 724 725 726
}

/*
 * If value==NULL, unset in (remove from) config,
 * if value_regex!=NULL, disregard key/value pairs where value does not match.
727 728 729
 * 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.
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
 *
 * 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,
748
	const char* value_regex, int multi_replace)
749
{
L
Linus Torvalds 已提交
750
	int i, dot;
751
	int fd = -1, in_fd;
752
	int ret;
753
	char* config_filename;
754
	struct lock_file *lock = NULL;
755
	const char* last_dot = strrchr(key, '.');
756

757
	config_filename = getenv(CONFIG_ENVIRONMENT);
758
	if (!config_filename) {
759
		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
760 761 762
		if (!config_filename)
			config_filename  = git_path("config");
	}
763
	config_filename = xstrdup(config_filename);
764

765 766 767 768
	/*
	 * Since "key" actually contains the section name and the real
	 * key name separated by a dot, we have to know where the dot is.
	 */
769

770
	if (last_dot == NULL) {
771
		fprintf(stderr, "key does not contain a section: %s\n", key);
772 773
		ret = 2;
		goto out_free;
774
	}
775 776 777
	store.baselen = last_dot - key;

	store.multi_replace = multi_replace;
778 779 780 781

	/*
	 * Validate the key and while at it, lower case it for matching.
	 */
J
Jonas Fonseca 已提交
782
	store.key = xmalloc(strlen(key) + 1);
L
Linus Torvalds 已提交
783 784 785 786 787 788 789
	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) {
790
			if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
L
Linus Torvalds 已提交
791 792 793 794 795 796
				fprintf(stderr, "invalid key: %s\n", key);
				free(store.key);
				ret = 1;
				goto out_free;
			}
			c = tolower(c);
797 798 799 800 801
		} else if (c == '\n') {
			fprintf(stderr, "invalid key (newline): %s\n", key);
			free(store.key);
			ret = 1;
			goto out_free;
L
Linus Torvalds 已提交
802 803 804
		}
		store.key[i] = c;
	}
805
	store.key[i] = 0;
806 807

	/*
808
	 * The lock serves a purpose in addition to locking: the new
809 810
	 * contents of .git/config will be written into it.
	 */
811 812 813
	lock = xcalloc(sizeof(struct lock_file), 1);
	fd = hold_lock_file_for_update(lock, config_filename, 0);
	if (fd < 0) {
814 815
		fprintf(stderr, "could not lock config file\n");
		free(store.key);
816 817
		ret = -1;
		goto out_free;
818 819 820 821 822
	}

	/*
	 * If .git/config does not exist yet, write a minimal version.
	 */
823 824
	in_fd = open(config_filename, O_RDONLY);
	if ( in_fd < 0 ) {
825 826
		free(store.key);

827 828 829
		if ( ENOENT != errno ) {
			error("opening %s: %s", config_filename,
			      strerror(errno));
830 831
			ret = 3; /* same as "invalid config file" */
			goto out_free;
832
		}
833 834
		/* if nothing to unset, error out */
		if (value == NULL) {
835 836
			ret = 5;
			goto out_free;
837 838 839
		}

		store.key = (char*)key;
840
		if (!store_write_section(fd, key) ||
841 842 843
		    !store_write_pair(fd, key, value))
			goto write_err_out;
	} else {
844
		struct stat st;
845
		char* contents;
846 847
		size_t contents_sz, copy_begin, copy_end;
		int i, new_line = 0;
848 849 850 851

		if (value_regex == NULL)
			store.value_regex = NULL;
		else {
852 853 854 855 856 857
			if (value_regex[0] == '!') {
				store.do_not_match = 1;
				value_regex++;
			} else
				store.do_not_match = 0;

J
Jonas Fonseca 已提交
858
			store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
859 860
			if (regcomp(store.value_regex, value_regex,
					REG_EXTENDED)) {
A
Alex Riesen 已提交
861
				fprintf(stderr, "Invalid pattern: %s\n",
862 863
					value_regex);
				free(store.value_regex);
864 865
				ret = 6;
				goto out_free;
866 867 868
			}
		}

869
		store.offset[0] = 0;
870 871 872 873 874 875 876 877 878
		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.
		 */
879
		if (git_config_from_file(store_aux, config_filename)) {
880 881 882 883 884 885
			fprintf(stderr, "invalid config file\n");
			free(store.key);
			if (store.value_regex != NULL) {
				regfree(store.value_regex);
				free(store.value_regex);
			}
886 887
			ret = 3;
			goto out_free;
888 889 890 891 892 893 894 895
		}

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

896 897 898
		/* if nothing to unset, or too many matches, error out */
		if ((store.seen == 0 && value == NULL) ||
				(store.seen > 1 && multi_replace == 0)) {
899 900
			ret = 5;
			goto out_free;
901 902
		}

903
		fstat(in_fd, &st);
904 905
		contents_sz = xsize_t(st.st_size);
		contents = xmmap(NULL, contents_sz, PROT_READ,
906 907 908
			MAP_PRIVATE, in_fd, 0);
		close(in_fd);

909 910 911 912 913
		if (store.seen == 0)
			store.seen = 1;

		for (i = 0, copy_begin = 0; i < store.seen; i++) {
			if (store.offset[i] == 0) {
914
				store.offset[i] = copy_end = contents_sz;
915 916
			} else if (store.state != KEY_SEEN) {
				copy_end = store.offset[i];
917
			} else
918
				copy_end = find_beginning_of_line(
919
					contents, contents_sz,
920 921
					store.offset[i]-2, &new_line);

922 923 924
			if (copy_end > 0 && contents[copy_end-1] != '\n')
				new_line = 1;

925 926
			/* write the first part of the config */
			if (copy_end > copy_begin) {
927 928 929 930 931 932 933
				if (write_in_full(fd, contents + copy_begin,
						  copy_end - copy_begin) <
				    copy_end - copy_begin)
					goto write_err_out;
				if (new_line &&
				    write_in_full(fd, "\n", 1) != 1)
					goto write_err_out;
934 935
			}
			copy_begin = store.offset[i];
936 937 938 939
		}

		/* write the pair (value == NULL means unset) */
		if (value != NULL) {
940 941 942
			if (store.state == START) {
				if (!store_write_section(fd, key))
					goto write_err_out;
943
			}
944 945
			if (!store_write_pair(fd, key, value))
				goto write_err_out;
946 947 948
		}

		/* write the rest of the config */
949
		if (copy_begin < contents_sz)
950
			if (write_in_full(fd, contents + copy_begin,
951 952
					  contents_sz - copy_begin) <
			    contents_sz - copy_begin)
953
				goto write_err_out;
954

955
		munmap(contents, contents_sz);
956 957
	}

B
Brandon Casey 已提交
958
	if (commit_lock_file(lock) < 0) {
959
		fprintf(stderr, "Cannot commit config file!\n");
960 961
		ret = 4;
		goto out_free;
962 963
	}

964 965 966 967 968 969 970
	/*
	 * lock is committed, so don't try to roll it back below.
	 * NOTE: Since lockfile.c keeps a linked list of all created
	 * lock_file structures, it isn't safe to free(lock).  It's
	 * better to just leave it hanging around.
	 */
	lock = NULL;
971 972 973
	ret = 0;

out_free:
974 975
	if (lock)
		rollback_lock_file(lock);
J
Junio C Hamano 已提交
976
	free(config_filename);
977
	return ret;
978 979 980 981 982

write_err_out:
	ret = write_error();
	goto out_free;

983 984
}

985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
static int section_name_match (const char *buf, const char *name)
{
	int i = 0, j = 0, dot = 0;
	for (; buf[i] && buf[i] != ']'; i++) {
		if (!dot && isspace(buf[i])) {
			dot = 1;
			if (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] != name[j++])
			break;
	}
	return (buf[i] == ']' && name[j] == 0);
}

/* if new_name == NULL, the section is removed instead */
1013 1014
int git_config_rename_section(const char *old_name, const char *new_name)
{
1015
	int ret = 0, remove = 0;
1016
	char *config_filename;
1017 1018 1019 1020
	struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
	int out_fd;
	char buf[1024];

1021
	config_filename = getenv(CONFIG_ENVIRONMENT);
1022
	if (!config_filename) {
1023
		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
1024 1025 1026 1027 1028
		if (!config_filename)
			config_filename  = git_path("config");
	}
	config_filename = xstrdup(config_filename);
	out_fd = hold_lock_file_for_update(lock, config_filename, 0);
1029 1030 1031 1032
	if (out_fd < 0) {
		ret = error("Could not lock config file!");
		goto out;
	}
1033

1034
	if (!(config_file = fopen(config_filename, "rb"))) {
1035 1036
		/* no config file means nothing to rename, no error */
		goto unlock_and_out;
1037
	}
1038 1039 1040

	while (fgets(buf, sizeof(buf), config_file)) {
		int i;
1041
		int length;
1042 1043 1044 1045
		for (i = 0; buf[i] && isspace(buf[i]); i++)
			; /* do nothing */
		if (buf[i] == '[') {
			/* it's a section */
1046 1047 1048 1049
			if (section_name_match (&buf[i+1], old_name)) {
				ret++;
				if (new_name == NULL) {
					remove = 1;
1050 1051 1052
					continue;
				}
				store.baselen = strlen(new_name);
1053 1054 1055 1056
				if (!store_write_section(out_fd, new_name)) {
					ret = write_error();
					goto out;
				}
1057 1058
				continue;
			}
1059
			remove = 0;
1060
		}
1061 1062
		if (remove)
			continue;
1063 1064 1065 1066 1067
		length = strlen(buf);
		if (write_in_full(out_fd, buf, length) != length) {
			ret = write_error();
			goto out;
		}
1068
	}
1069
	fclose(config_file);
1070
 unlock_and_out:
B
Brandon Casey 已提交
1071
	if (commit_lock_file(lock) < 0)
1072
			ret = error("Cannot commit config file!");
1073 1074
 out:
	free(config_filename);
1075 1076
	return ret;
}