builtin-config.c 12.8 KB
Newer Older
1
#include "builtin.h"
2
#include "cache.h"
3
#include "color.h"
4
#include "parse-options.h"
5

6 7 8 9
static const char *const builtin_config_usage[] = {
	"git config [options]",
	NULL
};
10

11 12 13 14 15 16 17 18
static char *key;
static regex_t *key_regexp;
static regex_t *regexp;
static int show_keys;
static int use_key_regexp;
static int do_all;
static int do_not_match;
static int seen;
19 20 21
static char delim = '=';
static char key_delim = ' ';
static char term = '\n';
22

23 24
static int use_global_config, use_system_config;
static const char *given_config_file;
25
static int actions, types;
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
static const char *get_color_slot, *get_colorbool_slot;
static int end_null;

#define ACTION_GET (1<<0)
#define ACTION_GET_ALL (1<<1)
#define ACTION_GET_REGEXP (1<<2)
#define ACTION_REPLACE_ALL (1<<3)
#define ACTION_ADD (1<<4)
#define ACTION_UNSET (1<<5)
#define ACTION_UNSET_ALL (1<<6)
#define ACTION_RENAME_SECTION (1<<7)
#define ACTION_REMOVE_SECTION (1<<8)
#define ACTION_LIST (1<<9)
#define ACTION_EDIT (1<<10)
#define ACTION_SET (1<<11)
#define ACTION_SET_ALL (1<<12)
#define ACTION_GET_COLOR (1<<13)
#define ACTION_GET_COLORBOOL (1<<14)

45 46 47 48
#define TYPE_BOOL (1<<0)
#define TYPE_INT (1<<1)
#define TYPE_BOOL_OR_INT (1<<2)

49 50 51 52 53 54 55 56 57
static struct option builtin_config_options[] = {
	OPT_GROUP("Config file location"),
	OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
	OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
	OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
	OPT_GROUP("Action"),
	OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
	OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
	OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP),
58
	OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL),
59 60 61 62 63 64 65 66 67 68
	OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD),
	OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET),
	OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL),
	OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION),
	OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION),
	OPT_BIT('l', "list", &actions, "list all", ACTION_LIST),
	OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT),
	OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"),
	OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"),
	OPT_GROUP("Type"),
69 70
	OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL),
	OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT),
71
	OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT),
72 73 74 75 76 77 78 79 80 81 82 83
	OPT_GROUP("Other"),
	OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
	OPT_END(),
};

static void check_argc(int argc, int min, int max) {
	if (argc >= min && argc <= max)
		return;
	error("wrong number of arguments");
	usage_with_options(builtin_config_usage, builtin_config_options);
}

84
static int show_all_config(const char *key_, const char *value_, void *cb)
P
Petr Baudis 已提交
85 86
{
	if (value_)
87
		printf("%s%c%s%c", key_, delim, value_, term);
P
Petr Baudis 已提交
88
	else
89
		printf("%s%c", key_, term);
P
Petr Baudis 已提交
90 91 92
	return 0;
}

93
static int show_config(const char *key_, const char *value_, void *cb)
94
{
95 96
	char value[256];
	const char *vptr = value;
97
	int dup_error = 0;
98

99 100 101 102 103
	if (!use_key_regexp && strcmp(key_, key))
		return 0;
	if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
		return 0;
	if (regexp != NULL &&
104
	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
105 106
		return 0;

107 108
	if (show_keys) {
		if (value_)
109
			printf("%s%c", key_, key_delim);
110 111 112
		else
			printf("%s", key_);
	}
113 114
	if (seen && !do_all)
		dup_error = 1;
115
	if (types == TYPE_INT)
116
		sprintf(value, "%d", git_config_int(key_, value_?value_:""));
117
	else if (types == TYPE_BOOL)
118
		vptr = git_config_bool(key_, value_) ? "true" : "false";
119
	else if (types == TYPE_BOOL_OR_INT) {
J
Junio C Hamano 已提交
120 121 122 123 124 125 126
		int is_bool, v;
		v = git_config_bool_or_int(key_, value_, &is_bool);
		if (is_bool)
			vptr = v ? "true" : "false";
		else
			sprintf(value, "%d", v);
	}
127
	else
128
		vptr = value_?value_:"";
129 130 131 132
	seen++;
	if (dup_error) {
		error("More than one value for the key %s: %s",
				key_, vptr);
133
	}
134
	else
135
		printf("%s%c", vptr, term);
136

137 138 139
	return 0;
}

140
static int get_value(const char *key_, const char *regex_)
141
{
142
	int ret = -1;
L
Linus Torvalds 已提交
143
	char *tl;
144
	char *global = NULL, *repo_config = NULL;
145
	const char *system_wide = NULL, *local;
146

147
	local = config_exclusive_filename;
148 149
	if (!local) {
		const char *home = getenv("HOME");
150
		local = repo_config = git_pathdup("config");
151
		if (git_config_global() && home)
152
			global = xstrdup(mkpath("%s/.gitconfig", home));
153 154
		if (git_config_system())
			system_wide = git_etc_gitconfig();
155
	}
156

157
	key = xstrdup(key_);
L
Linus Torvalds 已提交
158 159 160 161
	for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
		*tl = tolower(*tl);
	for (tl=key; *tl && *tl != '.'; ++tl)
		*tl = tolower(*tl);
162

163
	if (use_key_regexp) {
J
Jonas Fonseca 已提交
164
		key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
165
		if (regcomp(key_regexp, key, REG_EXTENDED)) {
166
			fprintf(stderr, "Invalid key pattern: %s\n", key_);
167
			goto free_strings;
168 169 170
		}
	}

171
	if (regex_) {
172 173 174 175 176
		if (regex_[0] == '!') {
			do_not_match = 1;
			regex_++;
		}

J
Jonas Fonseca 已提交
177
		regexp = (regex_t*)xmalloc(sizeof(regex_t));
178
		if (regcomp(regexp, regex_, REG_EXTENDED)) {
179
			fprintf(stderr, "Invalid pattern: %s\n", regex_);
180
			goto free_strings;
181 182 183
		}
	}

184
	if (do_all && system_wide)
185
		git_config_from_file(show_config, system_wide, NULL);
186
	if (do_all && global)
187 188
		git_config_from_file(show_config, global, NULL);
	git_config_from_file(show_config, local, NULL);
189
	if (!do_all && !seen && global)
190
		git_config_from_file(show_config, global, NULL);
191
	if (!do_all && !seen && system_wide)
192
		git_config_from_file(show_config, system_wide, NULL);
193

194
	free(key);
195 196 197
	if (regexp) {
		regfree(regexp);
		free(regexp);
198 199 200
	}

	if (do_all)
201 202
		ret = !seen;
	else
P
Petr Baudis 已提交
203
		ret = (seen == 1) ? 0 : seen > 1 ? 2 : 1;
204 205

free_strings:
J
Junio C Hamano 已提交
206 207
	free(repo_config);
	free(global);
208
	return ret;
209
}
210

211
static char *normalize_value(const char *key, const char *value)
212 213 214 215 216 217
{
	char *normalized;

	if (!value)
		return NULL;

218
	if (types == 0)
219 220 221
		normalized = xstrdup(value);
	else {
		normalized = xmalloc(64);
222
		if (types == TYPE_INT) {
223 224 225
			int v = git_config_int(key, value);
			sprintf(normalized, "%d", v);
		}
226
		else if (types == TYPE_BOOL)
227 228
			sprintf(normalized, "%s",
				git_config_bool(key, value) ? "true" : "false");
229
		else if (types == TYPE_BOOL_OR_INT) {
J
Junio C Hamano 已提交
230 231 232 233 234 235 236
			int is_bool, v;
			v = git_config_bool_or_int(key, value, &is_bool);
			if (!is_bool)
				sprintf(normalized, "%d", v);
			else
				sprintf(normalized, "%s", v ? "true" : "false");
		}
237 238 239 240 241
	}

	return normalized;
}

242 243
static int get_color_found;
static const char *get_color_slot;
244
static const char *get_colorbool_slot;
245 246
static char parsed_color[COLOR_MAXLEN];

247
static int git_get_color_config(const char *var, const char *value, void *cb)
248 249
{
	if (!strcmp(var, get_color_slot)) {
250 251
		if (!value)
			config_error_nonbool(var);
252 253 254 255 256 257
		color_parse(value, var, parsed_color);
		get_color_found = 1;
	}
	return 0;
}

258
static void get_color(const char *def_color)
259 260 261
{
	get_color_found = 0;
	parsed_color[0] = '\0';
262
	git_config(git_get_color_config, NULL);
263 264 265 266 267 268 269

	if (!get_color_found && def_color)
		color_parse(def_color, "command line", parsed_color);

	fputs(parsed_color, stdout);
}

J
Junio C Hamano 已提交
270 271
static int stdout_is_tty;
static int get_colorbool_found;
272
static int get_diff_color_found;
273 274
static int git_get_colorbool_config(const char *var, const char *value,
		void *cb)
J
Junio C Hamano 已提交
275
{
276
	if (!strcmp(var, get_colorbool_slot)) {
J
Junio C Hamano 已提交
277 278
		get_colorbool_found =
			git_config_colorbool(var, value, stdout_is_tty);
279 280 281 282 283
	}
	if (!strcmp(var, "diff.color")) {
		get_diff_color_found =
			git_config_colorbool(var, value, stdout_is_tty);
	}
284 285 286 287
	if (!strcmp(var, "color.ui")) {
		git_use_color_default = git_config_colorbool(var, value, stdout_is_tty);
		return 0;
	}
J
Junio C Hamano 已提交
288 289 290
	return 0;
}

291
static int get_colorbool(int print)
J
Junio C Hamano 已提交
292
{
293 294
	get_colorbool_found = -1;
	get_diff_color_found = -1;
295
	git_config(git_get_colorbool_config, NULL);
J
Junio C Hamano 已提交
296

297
	if (get_colorbool_found < 0) {
298
		if (!strcmp(get_colorbool_slot, "color.diff"))
299 300
			get_colorbool_found = get_diff_color_found;
		if (get_colorbool_found < 0)
301
			get_colorbool_found = git_use_color_default;
302 303
	}

304
	if (print) {
J
Junio C Hamano 已提交
305 306
		printf("%s\n", get_colorbool_found ? "true" : "false");
		return 0;
307 308
	} else
		return get_colorbool_found ? 0 : 1;
J
Junio C Hamano 已提交
309 310
}

311
int cmd_config(int argc, const char **argv, const char *unused_prefix)
312
{
313
	int nongit;
314
	char *value;
315
	const char *prefix = setup_git_directory_gently(&nongit);
316

317 318
	config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);

319 320 321
	argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage,
			     PARSE_OPT_STOP_AT_NON_OPTION);

322 323 324 325 326
	if (use_global_config + use_system_config + !!given_config_file > 1) {
		error("only one config file at a time.");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}

327 328 329 330 331
	if (use_global_config) {
		char *home = getenv("HOME");
		if (home) {
			char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
			config_exclusive_filename = user_config;
332
		} else {
333
			die("$HOME not set");
334
		}
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	}
	else if (use_system_config)
		config_exclusive_filename = git_etc_gitconfig();
	else if (given_config_file) {
		if (!is_absolute_path(given_config_file) && prefix)
			config_exclusive_filename = prefix_filename(prefix,
								    strlen(prefix),
								    argv[2]);
		else
			config_exclusive_filename = given_config_file;
	}

	if (end_null) {
		term = '\0';
		delim = '\n';
		key_delim = '\n';
	}

353 354 355 356 357
	if (HAS_MULTI_BITS(types)) {
		error("only one type at a time.");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}

358 359 360 361 362
	if (get_color_slot)
	    actions |= ACTION_GET_COLOR;
	if (get_colorbool_slot)
	    actions |= ACTION_GET_COLORBOOL;

363 364 365 366 367
	if ((get_color_slot || get_colorbool_slot) && types) {
		error("--get-color and variable type are incoherent");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}

368 369 370 371 372 373 374 375 376 377 378
	if (HAS_MULTI_BITS(actions)) {
		error("only one action at a time.");
		usage_with_options(builtin_config_usage, builtin_config_options);
	}
	if (actions == 0)
		switch (argc) {
		case 1: actions = ACTION_GET; break;
		case 2: actions = ACTION_SET; break;
		case 3: actions = ACTION_SET_ALL; break;
		default:
			usage_with_options(builtin_config_usage, builtin_config_options);
379
		}
380 381

	if (actions == ACTION_LIST) {
382
		check_argc(argc, 0, 0);
383 384 385 386 387 388
		if (git_config(show_all_config, NULL) < 0) {
			if (config_exclusive_filename)
				die("unable to read config file %s: %s",
				    config_exclusive_filename, strerror(errno));
			else
				die("error processing config file(s)");
389
		}
390
	}
391
	else if (actions == ACTION_EDIT) {
392
		check_argc(argc, 0, 0);
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
		git_config(git_default_config, NULL);
		launch_editor(config_exclusive_filename ?
			      config_exclusive_filename : git_path("config"),
			      NULL, NULL);
	}
	else if (actions == ACTION_SET) {
		check_argc(argc, 2, 2);
		value = normalize_value(argv[0], argv[1]);
		return git_config_set(argv[0], value);
	}
	else if (actions == ACTION_SET_ALL) {
		check_argc(argc, 2, 3);
		value = normalize_value(argv[0], argv[1]);
		return git_config_set_multivar(argv[0], value, argv[2], 0);
	}
	else if (actions == ACTION_ADD) {
		check_argc(argc, 2, 2);
		value = normalize_value(argv[0], argv[1]);
		return git_config_set_multivar(argv[0], value, "^$", 0);
	}
	else if (actions == ACTION_REPLACE_ALL) {
		check_argc(argc, 2, 3);
		value = normalize_value(argv[0], argv[1]);
		return git_config_set_multivar(argv[0], value, argv[2], 1);
	}
	else if (actions == ACTION_GET) {
		check_argc(argc, 1, 2);
		return get_value(argv[0], argv[1]);
	}
	else if (actions == ACTION_GET_ALL) {
		do_all = 1;
		check_argc(argc, 1, 2);
		return get_value(argv[0], argv[1]);
	}
	else if (actions == ACTION_GET_REGEXP) {
		show_keys = 1;
		use_key_regexp = 1;
		do_all = 1;
		check_argc(argc, 1, 2);
		return get_value(argv[0], argv[1]);
	}
	else if (actions == ACTION_UNSET) {
		check_argc(argc, 1, 2);
		if (argc == 2)
			return git_config_set_multivar(argv[0], NULL, argv[1], 0);
		else
			return git_config_set(argv[0], NULL);
	}
	else if (actions == ACTION_UNSET_ALL) {
		check_argc(argc, 1, 2);
		return git_config_set_multivar(argv[0], NULL, argv[1], 1);
	}
	else if (actions == ACTION_RENAME_SECTION) {
		int ret;
		check_argc(argc, 2, 2);
		ret = git_config_rename_section(argv[0], argv[1]);
		if (ret < 0)
			return ret;
		if (ret == 0)
			die("No such section!");
	}
	else if (actions == ACTION_REMOVE_SECTION) {
		int ret;
		check_argc(argc, 1, 1);
		ret = git_config_rename_section(argv[0], NULL);
		if (ret < 0)
			return ret;
		if (ret == 0)
			die("No such section!");
	}
	else if (actions == ACTION_GET_COLOR) {
		get_color(argv[0]);
	}
	else if (actions == ACTION_GET_COLORBOOL) {
		if (argc == 1)
			stdout_is_tty = git_config_bool("command line", argv[0]);
		else if (argc == 0)
			stdout_is_tty = isatty(1);
		return get_colorbool(argc != 0);
	}

474 475
	return 0;
}