diff options
Diffstat (limited to 'config.c')
-rw-r--r-- | config.c | 269 |
1 files changed, 220 insertions, 49 deletions
@@ -8,6 +8,7 @@ #include "cache.h" #include "branch.h" #include "config.h" +#include "environment.h" #include "repository.h" #include "lockfile.h" #include "exec-cmd.h" @@ -332,7 +333,7 @@ int git_config_include(const char *var, const char *value, void *data) return ret; } -void git_config_push_parameter(const char *text) +static void git_config_push_split_parameter(const char *key, const char *value) { struct strbuf env = STRBUF_INIT; const char *old = getenv(CONFIG_DATA_ENVIRONMENT); @@ -340,11 +341,74 @@ void git_config_push_parameter(const char *text) strbuf_addstr(&env, old); strbuf_addch(&env, ' '); } - sq_quote_buf(&env, text); + sq_quote_buf(&env, key); + strbuf_addch(&env, '='); + if (value) + sq_quote_buf(&env, value); setenv(CONFIG_DATA_ENVIRONMENT, env.buf, 1); strbuf_release(&env); } +void git_config_push_parameter(const char *text) +{ + const char *value; + + /* + * When we see: + * + * section.subsection=with=equals.key=value + * + * we cannot tell if it means: + * + * [section "subsection=with=equals"] + * key = value + * + * or: + * + * [section] + * subsection = with=equals.key=value + * + * We parse left-to-right for the first "=", meaning we'll prefer to + * keep the value intact over the subsection. This is historical, but + * also sensible since values are more likely to contain odd or + * untrusted input than a section name. + * + * A missing equals is explicitly allowed (as a bool-only entry). + */ + value = strchr(text, '='); + if (value) { + char *key = xmemdupz(text, value - text); + git_config_push_split_parameter(key, value + 1); + free(key); + } else { + git_config_push_split_parameter(text, NULL); + } +} + +void git_config_push_env(const char *spec) +{ + char *key; + const char *env_name; + const char *env_value; + + env_name = strrchr(spec, '='); + if (!env_name) + die(_("invalid config format: %s"), spec); + key = xmemdupz(spec, env_name - spec); + env_name++; + if (!*env_name) + die(_("missing environment variable name for configuration '%.*s'"), + (int)(env_name - spec - 1), spec); + + env_value = getenv(env_name); + if (!env_value) + die(_("missing environment variable '%s' for configuration '%.*s'"), + env_name, (int)(env_name - spec - 1), spec); + + git_config_push_split_parameter(key, env_value); + free(key); +} + static inline int iskeychar(int c) { return isalnum(c) || c == '-'; @@ -437,11 +501,26 @@ int git_config_key_is_valid(const char *key) return !git_config_parse_key_1(key, NULL, NULL, 1); } +static int config_parse_pair(const char *key, const char *value, + config_fn_t fn, void *data) +{ + char *canonical_name; + int ret; + + if (!strlen(key)) + return error(_("empty config key")); + if (git_config_parse_key(key, &canonical_name, NULL)) + return -1; + + ret = (fn(canonical_name, value, data) < 0) ? -1 : 0; + free(canonical_name); + return ret; +} + int git_config_parse_parameter(const char *text, config_fn_t fn, void *data) { const char *value; - char *canonical_name; struct strbuf **pair; int ret; @@ -462,51 +541,131 @@ int git_config_parse_parameter(const char *text, return error(_("bogus config parameter: %s"), text); } - if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) { - ret = -1; - } else { - ret = (fn(canonical_name, value, data) < 0) ? -1 : 0; - free(canonical_name); - } + ret = config_parse_pair(pair[0]->buf, value, fn, data); strbuf_list_free(pair); return ret; } +static int parse_config_env_list(char *env, config_fn_t fn, void *data) +{ + char *cur = env; + while (cur && *cur) { + const char *key = sq_dequote_step(cur, &cur); + if (!key) + return error(_("bogus format in %s"), + CONFIG_DATA_ENVIRONMENT); + + if (!cur || isspace(*cur)) { + /* old-style 'key=value' */ + if (git_config_parse_parameter(key, fn, data) < 0) + return -1; + } + else if (*cur == '=') { + /* new-style 'key'='value' */ + const char *value; + + cur++; + if (*cur == '\'') { + /* quoted value */ + value = sq_dequote_step(cur, &cur); + if (!value || (cur && !isspace(*cur))) { + return error(_("bogus format in %s"), + CONFIG_DATA_ENVIRONMENT); + } + } else if (!*cur || isspace(*cur)) { + /* implicit bool: 'key'= */ + value = NULL; + } else { + return error(_("bogus format in %s"), + CONFIG_DATA_ENVIRONMENT); + } + + if (config_parse_pair(key, value, fn, data) < 0) + return -1; + } + else { + /* unknown format */ + return error(_("bogus format in %s"), + CONFIG_DATA_ENVIRONMENT); + } + + if (cur) { + while (isspace(*cur)) + cur++; + } + } + return 0; +} + int git_config_from_parameters(config_fn_t fn, void *data) { - const char *env = getenv(CONFIG_DATA_ENVIRONMENT); + const char *env; + struct strbuf envvar = STRBUF_INIT; + struct strvec to_free = STRVEC_INIT; int ret = 0; - char *envw; - const char **argv = NULL; - int nr = 0, alloc = 0; - int i; + char *envw = NULL; struct config_source source; - if (!env) - return 0; - memset(&source, 0, sizeof(source)); source.prev = cf; source.origin_type = CONFIG_ORIGIN_CMDLINE; cf = &source; - /* sq_dequote will write over it */ - envw = xstrdup(env); + env = getenv(CONFIG_COUNT_ENVIRONMENT); + if (env) { + unsigned long count; + char *endp; + int i; - if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) { - ret = error(_("bogus format in %s"), CONFIG_DATA_ENVIRONMENT); - goto out; + count = strtoul(env, &endp, 10); + if (*endp) { + ret = error(_("bogus count in %s"), CONFIG_COUNT_ENVIRONMENT); + goto out; + } + if (count > INT_MAX) { + ret = error(_("too many entries in %s"), CONFIG_COUNT_ENVIRONMENT); + goto out; + } + + for (i = 0; i < count; i++) { + const char *key, *value; + + strbuf_addf(&envvar, "GIT_CONFIG_KEY_%d", i); + key = getenv_safe(&to_free, envvar.buf); + if (!key) { + ret = error(_("missing config key %s"), envvar.buf); + goto out; + } + strbuf_reset(&envvar); + + strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%d", i); + value = getenv_safe(&to_free, envvar.buf); + if (!value) { + ret = error(_("missing config value %s"), envvar.buf); + goto out; + } + strbuf_reset(&envvar); + + if (config_parse_pair(key, value, fn, data) < 0) { + ret = -1; + goto out; + } + } } - for (i = 0; i < nr; i++) { - if (git_config_parse_parameter(argv[i], fn, data) < 0) { + env = getenv(CONFIG_DATA_ENVIRONMENT); + if (env) { + /* sq_dequote will write over it */ + envw = xstrdup(env); + if (parse_config_env_list(envw, fn, data) < 0) { ret = -1; goto out; } } out: - free(argv); + strbuf_release(&envvar); + strvec_clear(&to_free); free(envw); cf = source.prev; return ret; @@ -996,15 +1155,6 @@ static void die_bad_number(const char *name, const char *value) if (!value) value = ""; - if (!strcmp(name, "GIT_TEST_GETTEXT_POISON")) - /* - * We explicitly *don't* use _() here since it would - * cause an infinite loop with _() needing to call - * use_gettext_poison(). This is why marked up - * translations with N_() above. - */ - die(bad_numeric, value, name, error_type); - if (!(cf && cf->name)) die(_(bad_numeric), value, name, _(error_type)); @@ -1102,8 +1252,10 @@ int git_config_bool_or_int(const char *name, const char *value, int *is_bool) int git_config_bool(const char *name, const char *value) { - int discard; - return !!git_config_bool_or_int(name, value, &discard); + int v = git_parse_maybe_bool(value); + if (v < 0) + die(_("bad boolean config value '%s' for '%s'"), value, name); + return v; } int git_config_string(const char **dest, const char *var, const char *value) @@ -1217,6 +1369,8 @@ static int git_default_core_config(const char *var, const char *value, void *cb) return config_error_nonbool(var); if (!strcasecmp(value, "auto")) default_abbrev = -1; + else if (!git_parse_maybe_bool_text(value)) + default_abbrev = the_hash_algo->hexsz; else { int abbrev = git_config_int(var, value); if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz) @@ -1676,12 +1830,26 @@ static int git_config_from_blob_ref(config_fn_t fn, return git_config_from_blob_oid(fn, name, &oid, data); } -const char *git_etc_gitconfig(void) +char *git_system_config(void) { - static const char *system_wide; - if (!system_wide) - system_wide = system_path(ETC_GITCONFIG); - return system_wide; + char *system_config = xstrdup_or_null(getenv("GIT_CONFIG_SYSTEM")); + if (system_config) + return system_config; + return system_path(ETC_GITCONFIG); +} + +void git_global_config(char **user_out, char **xdg_out) +{ + char *user_config = xstrdup_or_null(getenv("GIT_CONFIG_GLOBAL")); + char *xdg_config = NULL; + + if (!user_config) { + user_config = expand_user_path("~/.gitconfig", 0); + xdg_config = xdg_config_home("config"); + } + + *user_out = user_config; + *xdg_out = xdg_config; } /* @@ -1715,8 +1883,9 @@ static int do_git_config_sequence(const struct config_options *opts, config_fn_t fn, void *data) { int ret = 0; - char *xdg_config = xdg_config_home("config"); - char *user_config = expand_user_path("~/.gitconfig", 0); + char *system_config = git_system_config(); + char *xdg_config = NULL; + char *user_config = NULL; char *repo_config; enum config_scope prev_parsing_scope = current_parsing_scope; @@ -1728,13 +1897,14 @@ static int do_git_config_sequence(const struct config_options *opts, repo_config = NULL; current_parsing_scope = CONFIG_SCOPE_SYSTEM; - if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, - opts->system_gently ? - ACCESS_EACCES_OK : 0)) - ret += git_config_from_file(fn, git_etc_gitconfig(), - data); + if (git_config_system() && system_config && + !access_or_die(system_config, R_OK, + opts->system_gently ? ACCESS_EACCES_OK : 0)) + ret += git_config_from_file(fn, system_config, data); current_parsing_scope = CONFIG_SCOPE_GLOBAL; + git_global_config(&user_config, &xdg_config); + if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) ret += git_config_from_file(fn, xdg_config, data); @@ -1759,6 +1929,7 @@ static int do_git_config_sequence(const struct config_options *opts, die(_("unable to parse command-line config")); current_parsing_scope = prev_parsing_scope; + free(system_config); free(xdg_config); free(user_config); free(repo_config); @@ -2101,7 +2272,7 @@ static void repo_read_config(struct repository *repo) opts.git_dir = repo->gitdir; if (!repo->config) - repo->config = xcalloc(1, sizeof(struct config_set)); + CALLOC_ARRAY(repo->config, 1); else git_configset_clear(repo->config); |