config.c 14.6 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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
			}
			value[len++] = c;
			continue;
		}
		if (c == '"') {
			quote = 1-quote;
			continue;
		}
		value[len++] = c;
	}
}

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;
		if (!isalnum(c))
			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 已提交
137 138 139 140 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
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 已提交
172 173 174 175 176 177 178 179 180 181
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 已提交
182 183
		if (isspace(c))
			return get_extended_base_var(name, baselen, c);
184
		if (!isalnum(c) && c != '.')
L
Linus Torvalds 已提交
185 186 187 188 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
			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;
223
		var[baselen] = tolower(c);
L
Linus Torvalds 已提交
224 225 226
		if (get_value(fn, var, baselen+1) < 0)
			break;
	}
227
	die("bad config file line %d in %s", config_linenr, config_file_name);
L
Linus Torvalds 已提交
228 229 230 231 232 233 234 235 236 237
}

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;
	}
238
	die("bad config value for '%s' in %s", name, config_file_name);
L
Linus Torvalds 已提交
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
}

int git_config_bool(const char *name, const char *value)
{
	if (!value)
		return 1;
	if (!*value)
		return 0;
	if (!strcasecmp(value, "true"))
		return 1;
	if (!strcasecmp(value, "false"))
		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 已提交
262 263 264 265 266
	if (!strcmp(var, "core.ignorestat")) {
		assume_unchanged = git_config_bool(var, value);
		return 0;
	}

267 268
	if (!strcmp(var, "core.prefersymlinkrefs")) {
		prefer_symlink_refs = git_config_bool(var, value);
269 270 271
		return 0;
	}

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

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

282
	if (!strcmp(var, "user.name")) {
283
		safe_strncpy(git_default_name, value, sizeof(git_default_name));
284 285 286 287
		return 0;
	}

	if (!strcmp(var, "user.email")) {
288
		safe_strncpy(git_default_email, value, sizeof(git_default_email));
289 290 291
		return 0;
	}

J
Junio C Hamano 已提交
292
	if (!strcmp(var, "i18n.commitencoding")) {
293
		safe_strncpy(git_commit_encoding, value, sizeof(git_commit_encoding));
J
Junio C Hamano 已提交
294 295 296
		return 0;
	}

P
Petr Baudis 已提交
297
	/* Add other config variables here and to Documentation/config.txt. */
L
Linus Torvalds 已提交
298 299 300
	return 0;
}

301
int git_config_from_file(config_fn_t fn, const char *filename)
L
Linus Torvalds 已提交
302 303
{
	int ret;
304
	FILE *f = fopen(filename, "r");
L
Linus Torvalds 已提交
305 306 307 308

	ret = -1;
	if (f) {
		config_file = f;
309
		config_file_name = filename;
L
Linus Torvalds 已提交
310 311 312
		config_linenr = 1;
		ret = git_parse_file(fn);
		fclose(f);
313
		config_file_name = NULL;
L
Linus Torvalds 已提交
314 315 316
	}
	return ret;
}
317

318 319
int git_config(config_fn_t fn)
{
320 321 322 323 324 325 326 327 328 329 330
	const char *filename = git_path("config");
	/* Forward-compatibility cue: $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. */
	if (getenv("GIT_CONFIG")) {
		filename = getenv("GIT_CONFIG");
	} else if (getenv("GIT_CONFIG_LOCAL")) {
		filename = getenv("GIT_CONFIG_LOCAL");
	}
	return git_config_from_file(fn, filename);
331 332
}

333 334 335
/*
 * Find all the stuff for git_config_set() below.
 */
336 337 338

#define MAX_MATCHES 512

339 340 341
static struct {
	int baselen;
	char* key;
342
	int do_not_match;
343
	regex_t* value_regex;
344 345
	int multi_replace;
	off_t offset[MAX_MATCHES];
346 347 348 349
	enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
	int seen;
} store;

350 351 352 353 354 355 356 357
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)));
}

358 359 360 361
static int store_aux(const char* key, const char* value)
{
	switch (store.state) {
	case KEY_SEEN:
362
		if (matches(key, value)) {
363
			if (store.seen == 1 && store.multi_replace == 0) {
364 365 366
				fprintf(stderr,
					"Warning: %s has multiple values\n",
					key);
367 368 369
			} else if (store.seen >= MAX_MATCHES) {
				fprintf(stderr, "Too many matches\n");
				return 1;
370
			}
371 372

			store.offset[store.seen] = ftell(config_file);
373 374 375 376 377 378 379 380
			store.seen++;
		}
		break;
	case SECTION_SEEN:
		if (strncmp(key, store.key, store.baselen+1)) {
			store.state = SECTION_END_SEEN;
			break;
		} else
381 382
			/* do not increment matches: this is no match */
			store.offset[store.seen] = ftell(config_file);
383 384 385
		/* fallthru */
	case SECTION_END_SEEN:
	case START:
386
		if (matches(key, value)) {
387
			store.offset[store.seen] = ftell(config_file);
388 389
			store.state = KEY_SEEN;
			store.seen++;
L
Linus Torvalds 已提交
390 391
		} else {
			if (strrchr(key, '.') - key == store.baselen &&
S
sean 已提交
392
			      !strncmp(key, store.key, store.baselen)) {
S
sean 已提交
393
					store.state = SECTION_SEEN;
S
sean 已提交
394
					store.offset[store.seen] = ftell(config_file);
L
Linus Torvalds 已提交
395
			}
S
sean 已提交
396
		}
397 398 399 400 401 402
	}
	return 0;
}

static void store_write_section(int fd, const char* key)
{
L
Linus Torvalds 已提交
403 404 405 406 407 408 409 410 411 412 413 414
	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;
		}
	}

415
	write(fd, "[", 1);
L
Linus Torvalds 已提交
416 417 418 419 420 421 422 423 424 425 426
	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);
	}
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
	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);
}

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
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;
}

469 470
int git_config_set(const char* key, const char* value)
{
471
	return git_config_set_multivar(key, value, NULL, 0);
472 473 474 475 476
}

/*
 * If value==NULL, unset in (remove from) config,
 * if value_regex!=NULL, disregard key/value pairs where value does not match.
477 478 479
 * 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.
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
 *
 * 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,
498
	const char* value_regex, int multi_replace)
499
{
L
Linus Torvalds 已提交
500
	int i, dot;
501
	int fd = -1, in_fd;
502
	int ret;
J
Junio C Hamano 已提交
503
	char* config_filename = strdup(git_path("config"));
504
	char* lock_file = strdup(git_path("config.lock"));
505
	const char* last_dot = strrchr(key, '.');
506

507 508 509 510
	/*
	 * Since "key" actually contains the section name and the real
	 * key name separated by a dot, we have to know where the dot is.
	 */
511

512
	if (last_dot == NULL) {
513
		fprintf(stderr, "key does not contain a section: %s\n", key);
514 515
		ret = 2;
		goto out_free;
516
	}
517 518 519
	store.baselen = last_dot - key;

	store.multi_replace = multi_replace;
520 521 522 523 524

	/*
	 * Validate the key and while at it, lower case it for matching.
	 */
	store.key = (char*)malloc(strlen(key)+1);
L
Linus Torvalds 已提交
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
	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) {
			if (!isalnum(c) || (i == store.baselen+1 && !isalpha(c))) {
				fprintf(stderr, "invalid key: %s\n", key);
				free(store.key);
				ret = 1;
				goto out_free;
			}
			c = tolower(c);
		}
		store.key[i] = c;
	}
542
	store.key[i] = 0;
543 544 545 546 547 548

	/*
	 * 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);
549
	if (fd < 0 || adjust_shared_perm(lock_file)) {
550 551
		fprintf(stderr, "could not lock config file\n");
		free(store.key);
552 553
		ret = -1;
		goto out_free;
554 555 556 557 558
	}

	/*
	 * If .git/config does not exist yet, write a minimal version.
	 */
559 560
	in_fd = open(config_filename, O_RDONLY);
	if ( in_fd < 0 ) {
561 562
		free(store.key);

563 564 565
		if ( ENOENT != errno ) {
			error("opening %s: %s", config_filename,
			      strerror(errno));
566 567
			ret = 3; /* same as "invalid config file" */
			goto out_free;
568
		}
569 570
		/* if nothing to unset, error out */
		if (value == NULL) {
571 572
			ret = 5;
			goto out_free;
573 574 575 576 577 578
		}

		store.key = (char*)key;
		store_write_section(fd, key);
		store_write_pair(fd, key, value);
	} else{
579
		struct stat st;
580
		char* contents;
581
		int i, copy_begin, copy_end, new_line = 0;
582 583 584 585

		if (value_regex == NULL)
			store.value_regex = NULL;
		else {
586 587 588 589 590 591
			if (value_regex[0] == '!') {
				store.do_not_match = 1;
				value_regex++;
			} else
				store.do_not_match = 0;

592 593 594
			store.value_regex = (regex_t*)malloc(sizeof(regex_t));
			if (regcomp(store.value_regex, value_regex,
					REG_EXTENDED)) {
A
Alex Riesen 已提交
595
				fprintf(stderr, "Invalid pattern: %s\n",
596 597
					value_regex);
				free(store.value_regex);
598 599
				ret = 6;
				goto out_free;
600 601 602
			}
		}

603
		store.offset[0] = 0;
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
		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.
		 */
		if (git_config(store_aux)) {
			fprintf(stderr, "invalid config file\n");
			free(store.key);
			if (store.value_regex != NULL) {
				regfree(store.value_regex);
				free(store.value_regex);
			}
620 621
			ret = 3;
			goto out_free;
622 623 624 625 626 627 628 629
		}

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

630 631 632
		/* if nothing to unset, or too many matches, error out */
		if ((store.seen == 0 && value == NULL) ||
				(store.seen > 1 && multi_replace == 0)) {
633 634
			ret = 5;
			goto out_free;
635 636
		}

637
		fstat(in_fd, &st);
638 639 640 641
		contents = mmap(NULL, st.st_size, PROT_READ,
			MAP_PRIVATE, in_fd, 0);
		close(in_fd);

642 643 644 645 646 647 648 649
		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];
650
			} else
651 652 653 654 655 656 657 658 659 660 661 662
				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];
663 664 665 666 667 668 669 670 671 672
		}

		/* 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 */
673 674 675
		if (copy_begin < st.st_size)
			write(fd, contents + copy_begin,
				st.st_size - copy_begin);
676 677

		munmap(contents, st.st_size);
J
Junio C Hamano 已提交
678
		unlink(config_filename);
679 680
	}

J
Junio C Hamano 已提交
681
	if (rename(lock_file, config_filename) < 0) {
682
		fprintf(stderr, "Could not rename the lock file?\n");
683 684
		ret = 4;
		goto out_free;
685 686
	}

687 688 689
	ret = 0;

out_free:
690 691
	if (0 <= fd)
		close(fd);
692 693
	if (config_filename)
		free(config_filename);
694 695
	if (lock_file) {
		unlink(lock_file);
696
		free(lock_file);
697
	}
698
	return ret;
699 700
}

701