]> git.ipfire.org Git - thirdparty/git.git/blobdiff - config.c
Merge branch 'ab/pager-exit-log'
[thirdparty/git.git] / config.c
index 1137bd73aff07c77e9b73a0b4c1d193cd38d88de..f90b633dba21f3cf63698e62170dab15e316e3e5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -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));
 
@@ -1030,6 +1180,20 @@ static void die_bad_number(const char *name, const char *value)
        }
 }
 
+NORETURN
+static void die_bad_bool(const char *name, const char *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().
+                */
+               die("bad boolean config value '%s' for '%s'", value, name);
+       else
+               die(_("bad boolean config value '%s' for '%s'"), value, name);
+}
+
 int git_config_int(const char *name, const char *value)
 {
        int ret;
@@ -1102,8 +1266,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_bool(name, value);
+       return v;
 }
 
 int git_config_string(const char **dest, const char *var, const char *value)
@@ -1217,6 +1383,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)