"branch -d" can remove more than one branches
[gitweb.git] / config.c
index 230fe2c0a2f779fd20ba1730349c0fc7bb0d1bef..61de4d6a178ec8c2886331daefead68e762362db 100644 (file)
--- a/config.c
+++ b/config.c
@@ -20,15 +20,6 @@ static int zlib_compression_seen;
 
 const char *config_exclusive_filename = NULL;
 
-struct config_item
-{
-       struct config_item *next;
-       char *name;
-       char *value;
-};
-static struct config_item *config_parameters;
-static struct config_item **config_parameters_tail = &config_parameters;
-
 static void lowercase(char *p)
 {
        for (; *p; p++)
@@ -48,9 +39,9 @@ void git_config_push_parameter(const char *text)
        strbuf_release(&env);
 }
 
-static int git_config_parse_parameter(const char *text)
+static int git_config_parse_parameter(const char *text,
+                                     config_fn_t fn, void *data)
 {
-       struct config_item *ct;
        struct strbuf tmp = STRBUF_INIT;
        struct strbuf **pair;
        strbuf_addstr(&tmp, text);
@@ -60,22 +51,19 @@ static int git_config_parse_parameter(const char *text)
        strbuf_trim(pair[0]);
        if (!pair[0]->len) {
                strbuf_list_free(pair);
-               return -1;
+               return error("bogus config parameter: %s", text);
        }
-       ct = xcalloc(1, sizeof(struct config_item));
-       ct->name = strbuf_detach(pair[0], NULL);
-       if (pair[1]) {
-               strbuf_trim(pair[1]);
-               ct->value = strbuf_detach(pair[1], NULL);
+       lowercase(pair[0]->buf);
+       if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
+               strbuf_list_free(pair);
+               return -1;
        }
        strbuf_list_free(pair);
-       lowercase(ct->name);
-       *config_parameters_tail = ct;
-       config_parameters_tail = &ct->next;
        return 0;
 }
 
-static int git_config_parse_environment(void) {
+int git_config_from_parameters(config_fn_t fn, void *data)
+{
        const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
        char *envw;
        const char **argv = NULL;
@@ -93,8 +81,7 @@ static int git_config_parse_environment(void) {
        }
 
        for (i = 0; i < nr; i++) {
-               if (git_config_parse_parameter(argv[i]) < 0) {
-                       error("bogus config parameter: %s", argv[i]);
+               if (git_config_parse_parameter(argv[i], fn, data) < 0) {
                        free(argv);
                        free(envw);
                        return -1;
@@ -103,7 +90,7 @@ static int git_config_parse_environment(void) {
 
        free(argv);
        free(envw);
-       return 0;
+       return nr > 0;
 }
 
 static int get_next_char(void)
@@ -410,7 +397,7 @@ unsigned long git_config_ulong(const char *name, const char *value)
        return ret;
 }
 
-int git_config_maybe_bool(const char *name, const char *value)
+static int git_config_maybe_bool_text(const char *name, const char *value)
 {
        if (!value)
                return 1;
@@ -427,9 +414,19 @@ int git_config_maybe_bool(const char *name, const char *value)
        return -1;
 }
 
+int git_config_maybe_bool(const char *name, const char *value)
+{
+       long v = git_config_maybe_bool_text(name, value);
+       if (0 <= v)
+               return v;
+       if (git_parse_long(value, &v))
+               return !!v;
+       return -1;
+}
+
 int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 {
-       int v = git_config_maybe_bool(name, value);
+       int v = git_config_maybe_bool_text(name, value);
        if (0 <= v) {
                *is_bool = 1;
                return v;
@@ -514,6 +511,14 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.abbrev")) {
+               int abbrev = git_config_int(var, value);
+               if (abbrev < minimum_abbrev || abbrev > 40)
+                       return -1;
+               default_abbrev = abbrev;
+               return 0;
+       }
+
        if (!strcmp(var, "core.loosecompression")) {
                int level = git_config_int(var, value);
                if (level == -1)
@@ -550,6 +555,12 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.bigfilethreshold")) {
+               long n = git_config_int(var, value);
+               big_file_threshold = 0 < n ? n : 0;
+               return 0;
+       }
+
        if (!strcmp(var, "core.packedgitlimit")) {
                packed_git_limit = git_config_int(var, value);
                return 0;
@@ -720,8 +731,10 @@ static int git_default_push_config(const char *var, const char *value)
                        push_default = PUSH_DEFAULT_NOTHING;
                else if (!strcmp(value, "matching"))
                        push_default = PUSH_DEFAULT_MATCHING;
-               else if (!strcmp(value, "tracking"))
-                       push_default = PUSH_DEFAULT_TRACKING;
+               else if (!strcmp(value, "upstream"))
+                       push_default = PUSH_DEFAULT_UPSTREAM;
+               else if (!strcmp(value, "tracking")) /* deprecated */
+                       push_default = PUSH_DEFAULT_UPSTREAM;
                else if (!strcmp(value, "current"))
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
@@ -814,27 +827,6 @@ int git_config_system(void)
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-int git_config_global(void)
-{
-       return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
-}
-
-int git_config_from_parameters(config_fn_t fn, void *data)
-{
-       static int loaded_environment;
-       const struct config_item *ct;
-
-       if (!loaded_environment) {
-               if (git_config_parse_environment() < 0)
-                       return -1;
-               loaded_environment = 1;
-       }
-       for (ct = config_parameters; ct; ct = ct->next)
-               if (fn(ct->name, ct->value, data) < 0)
-                       return -1;
-       return 0;
-}
-
 int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 {
        int ret = 0, found = 0;
@@ -850,7 +842,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
        }
 
        home = getenv("HOME");
-       if (git_config_global() && home) {
+       if (home) {
                char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
                if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
@@ -864,13 +856,18 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
                found += 1;
        }
 
-       ret += git_config_from_parameters(fn, data);
-       if (config_parameters)
-               found += 1;
+       switch (git_config_from_parameters(fn, data)) {
+       case -1: /* error */
+               ret--;
+               break;
+       case 0: /* found nothing */
+               break;
+       default: /* found at least one item */
+               found++;
+               break;
+       }
 
-       if (found == 0)
-               return -1;
-       return ret;
+       return ret == 0 ? found : ret;
 }
 
 int git_config(config_fn_t fn, void *data)
@@ -1083,6 +1080,75 @@ int git_config_set(const char *key, const char *value)
        return git_config_set_multivar(key, value, NULL, 0);
 }
 
+/*
+ * Auxiliary function to sanity-check and split the key into the section
+ * identifier and variable name.
+ *
+ * Returns 0 on success, -1 when there is an invalid character in the key and
+ * -2 if there is no section name in the key.
+ *
+ * store_key - pointer to char* which will hold a copy of the key with
+ *             lowercase section and variable name
+ * baselen - pointer to int which will hold the length of the
+ *           section + subsection part, can be NULL
+ */
+int git_config_parse_key(const char *key, char **store_key, int *baselen_)
+{
+       int i, dot, baselen;
+       const char *last_dot = strrchr(key, '.');
+
+       /*
+        * Since "key" actually contains the section name and the real
+        * key name separated by a dot, we have to know where the dot is.
+        */
+
+       if (last_dot == NULL || last_dot == key) {
+               error("key does not contain a section: %s", key);
+               return -2;
+       }
+
+       if (!last_dot[1]) {
+               error("key does not contain variable name: %s", key);
+               return -2;
+       }
+
+       baselen = last_dot - key;
+       if (baselen_)
+               *baselen_ = baselen;
+
+       /*
+        * Validate the key and while at it, lower case it for matching.
+        */
+       *store_key = xmalloc(strlen(key) + 1);
+
+       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 > baselen) {
+                       if (!iskeychar(c) ||
+                           (i == baselen + 1 && !isalpha(c))) {
+                               error("invalid key: %s", key);
+                               goto out_free_ret_1;
+                       }
+                       c = tolower(c);
+               } else if (c == '\n') {
+                       error("invalid key (newline): %s", key);
+                       goto out_free_ret_1;
+               }
+               (*store_key)[i] = c;
+       }
+       (*store_key)[i] = 0;
+
+       return 0;
+
+out_free_ret_1:
+       free(*store_key);
+       return -1;
+}
+
 /*
  * If value==NULL, unset in (remove from) config,
  * if value_regex!=NULL, disregard key/value pairs where value does not match.
@@ -1109,59 +1175,23 @@ int git_config_set(const char *key, const char *value)
 int git_config_set_multivar(const char *key, const char *value,
        const char *value_regex, int multi_replace)
 {
-       int i, dot;
        int fd = -1, in_fd;
        int ret;
        char *config_filename;
        struct lock_file *lock = NULL;
-       const char *last_dot = strrchr(key, '.');
 
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
        else
                config_filename = git_pathdup("config");
 
-       /*
-        * Since "key" actually contains the section name and the real
-        * key name separated by a dot, we have to know where the dot is.
-        */
-
-       if (last_dot == NULL) {
-               error("key does not contain a section: %s", key);
-               ret = 2;
+       /* parse-key returns negative; flip the sign to feed exit(3) */
+       ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
+       if (ret)
                goto out_free;
-       }
-       store.baselen = last_dot - key;
 
        store.multi_replace = multi_replace;
 
-       /*
-        * Validate the key and while at it, lower case it for matching.
-        */
-       store.key = xmalloc(strlen(key) + 1);
-       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 (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
-                               error("invalid key: %s", key);
-                               free(store.key);
-                               ret = 1;
-                               goto out_free;
-                       }
-                       c = tolower(c);
-               } else if (c == '\n') {
-                       error("invalid key (newline): %s", key);
-                       free(store.key);
-                       ret = 1;
-                       goto out_free;
-               }
-               store.key[i] = c;
-       }
-       store.key[i] = 0;
 
        /*
         * The lock serves a purpose in addition to locking: the new