builtin-config.c 12.2 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';
J
Junio C Hamano 已提交
22
static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW;
23

24 25 26 27 28 29 30 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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
static int use_global_config, use_system_config;
static const char *given_config_file;
static int actions;
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)

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),
	OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name [value [value_regex]", ACTION_REPLACE_ALL),
	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"),
	OPT_SET_INT(0, "bool", &type, "value is \"true\" or \"false\"", T_BOOL),
	OPT_SET_INT(0, "int", &type, "value is decimal number", T_INT),
	OPT_SET_INT(0, "bool-or-int", &type, NULL, T_BOOL_OR_INT),
	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);
}

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

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

96 97 98 99 100
	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 &&
101
	    (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
102 103
		return 0;

104 105
	if (show_keys) {
		if (value_)
106
			printf("%s%c", key_, key_delim);
107 108 109
		else
			printf("%s", key_);
	}
110 111 112
	if (seen && !do_all)
		dup_error = 1;
	if (type == T_INT)
113
		sprintf(value, "%d", git_config_int(key_, value_?value_:""));
114 115
	else if (type == T_BOOL)
		vptr = git_config_bool(key_, value_) ? "true" : "false";
J
Junio C Hamano 已提交
116 117 118 119 120 121 122 123
	else if (type == T_BOOL_OR_INT) {
		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);
	}
124
	else
125
		vptr = value_?value_:"";
126 127 128 129
	seen++;
	if (dup_error) {
		error("More than one value for the key %s: %s",
				key_, vptr);
130
	}
131
	else
132
		printf("%s%c", vptr, term);
133

134 135 136
	return 0;
}

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

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

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

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

168
	if (regex_) {
169 170 171 172 173
		if (regex_[0] == '!') {
			do_not_match = 1;
			regex_++;
		}

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

181
	if (do_all && system_wide)
182
		git_config_from_file(show_config, system_wide, NULL);
183
	if (do_all && global)
184 185
		git_config_from_file(show_config, global, NULL);
	git_config_from_file(show_config, local, NULL);
186
	if (!do_all && !seen && global)
187
		git_config_from_file(show_config, global, NULL);
188
	if (!do_all && !seen && system_wide)
189
		git_config_from_file(show_config, system_wide, NULL);
190

191
	free(key);
192 193 194
	if (regexp) {
		regfree(regexp);
		free(regexp);
195 196 197
	}

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

free_strings:
J
Junio C Hamano 已提交
203 204
	free(repo_config);
	free(global);
205
	return ret;
206
}
207

208
static char *normalize_value(const char *key, const char *value)
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
{
	char *normalized;

	if (!value)
		return NULL;

	if (type == T_RAW)
		normalized = xstrdup(value);
	else {
		normalized = xmalloc(64);
		if (type == T_INT) {
			int v = git_config_int(key, value);
			sprintf(normalized, "%d", v);
		}
		else if (type == T_BOOL)
			sprintf(normalized, "%s",
				git_config_bool(key, value) ? "true" : "false");
J
Junio C Hamano 已提交
226 227 228 229 230 231 232 233
		else if (type == T_BOOL_OR_INT) {
			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");
		}
234 235 236 237 238
	}

	return normalized;
}

239 240
static int get_color_found;
static const char *get_color_slot;
241
static const char *get_colorbool_slot;
242 243
static char parsed_color[COLOR_MAXLEN];

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

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

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

	fputs(parsed_color, stdout);
}

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

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

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

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

308
int cmd_config(int argc, const char **argv, const char *unused_prefix)
309
{
310
	int nongit;
311
	char *value;
312
	const char *prefix = setup_git_directory_gently(&nongit);
313

314 315
	config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);

316 317 318 319 320 321 322 323
	argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage,
			     PARSE_OPT_STOP_AT_NON_OPTION);

	if (use_global_config) {
		char *home = getenv("HOME");
		if (home) {
			char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
			config_exclusive_filename = user_config;
324
		} else {
325
			die("$HOME not set");
326
		}
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
	}
	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';
	}

	if (get_color_slot)
	    actions |= ACTION_GET_COLOR;
	if (get_colorbool_slot)
	    actions |= ACTION_GET_COLORBOOL;

	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);
361
		}
362 363 364 365 366 367 368 369

	if (actions == ACTION_LIST) {
		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)");
370
		}
371
	}
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 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
	else if (actions == ACTION_EDIT) {
		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);
	}

454 455
	return 0;
}