Code

Merge branch 'jk/fetch-no-tail-match-refs' into maint
[git.git] / config.c
index 749e5afc45ee17e840f0983f7c814a2c2efd6340..5ea101fb251d27eadac20c665a7f01fb210c20d1 100644 (file)
--- a/config.c
+++ b/config.c
 
 #define MAXNAME (256)
 
-static FILE *config_file;
-static const char *config_file_name;
-static int config_linenr;
-static int config_file_eof;
+typedef struct config_file {
+       struct config_file *prev;
+       FILE *f;
+       const char *name;
+       int linenr;
+       int eof;
+       struct strbuf value;
+       char var[MAXNAME];
+} config_file;
+
+static config_file *cf;
+
 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++)
@@ -47,34 +47,31 @@ void git_config_push_parameter(const char *text)
        strbuf_release(&env);
 }
 
-int git_config_parse_parameter(const char *text)
+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);
-       pair = strbuf_split(&tmp, '=');
+       pair = strbuf_split_str(text, '=', 2);
+       if (!pair[0])
+               return error("bogus config parameter: %s", text);
        if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
                strbuf_setlen(pair[0], pair[0]->len - 1);
        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;
 }
 
-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;
@@ -92,8 +89,7 @@ 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;
@@ -102,7 +98,7 @@ int git_config_parse_environment(void) {
 
        free(argv);
        free(envw);
-       return 0;
+       return nr > 0;
 }
 
 static int get_next_char(void)
@@ -111,7 +107,7 @@ static int get_next_char(void)
        FILE *f;
 
        c = '\n';
-       if ((f = config_file) != NULL) {
+       if (cf && ((f = cf->f) != NULL)) {
                c = fgetc(f);
                if (c == '\r') {
                        /* DOS like systems */
@@ -122,9 +118,9 @@ static int get_next_char(void)
                        }
                }
                if (c == '\n')
-                       config_linenr++;
+                       cf->linenr++;
                if (c == EOF) {
-                       config_file_eof = 1;
+                       cf->eof = 1;
                        c = '\n';
                }
        }
@@ -133,23 +129,20 @@ static int get_next_char(void)
 
 static char *parse_value(void)
 {
-       static char value[1024];
-       int quote = 0, comment = 0, len = 0, space = 0;
+       int quote = 0, comment = 0, space = 0;
 
+       strbuf_reset(&cf->value);
        for (;;) {
                int c = get_next_char();
-               if (len >= sizeof(value) - 1)
-                       return NULL;
                if (c == '\n') {
                        if (quote)
                                return NULL;
-                       value[len] = 0;
-                       return value;
+                       return cf->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
-                       if (len)
+                       if (cf->value.len)
                                space++;
                        continue;
                }
@@ -160,7 +153,7 @@ static char *parse_value(void)
                        }
                }
                for (; space; space--)
-                       value[len++] = ' ';
+                       strbuf_addch(&cf->value, ' ');
                if (c == '\\') {
                        c = get_next_char();
                        switch (c) {
@@ -182,14 +175,14 @@ static char *parse_value(void)
                        default:
                                return NULL;
                        }
-                       value[len++] = c;
+                       strbuf_addch(&cf->value, c);
                        continue;
                }
                if (c == '"') {
                        quote = 1-quote;
                        continue;
                }
-               value[len++] = c;
+               strbuf_addch(&cf->value, c);
        }
 }
 
@@ -206,7 +199,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
        /* Get the full name */
        for (;;) {
                c = get_next_char();
-               if (config_file_eof)
+               if (cf->eof)
                        break;
                if (!iskeychar(c))
                        break;
@@ -270,7 +263,7 @@ static int get_base_var(char *name)
 
        for (;;) {
                int c = get_next_char();
-               if (config_file_eof)
+               if (cf->eof)
                        return -1;
                if (c == ']')
                        return baselen;
@@ -288,7 +281,7 @@ static int git_parse_file(config_fn_t fn, void *data)
 {
        int comment = 0;
        int baselen = 0;
-       static char var[MAXNAME];
+       char *var = cf->var;
 
        /* U+FEFF Byte Order Mark in UTF8 */
        static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
@@ -312,7 +305,7 @@ static int git_parse_file(config_fn_t fn, void *data)
                        }
                }
                if (c == '\n') {
-                       if (config_file_eof)
+                       if (cf->eof)
                                return 0;
                        comment = 0;
                        continue;
@@ -337,10 +330,10 @@ static int git_parse_file(config_fn_t fn, void *data)
                if (get_value(fn, data, var, baselen+1) < 0)
                        break;
        }
-       die("bad config file line %d in %s", config_linenr, config_file_name);
+       die("bad config file line %d in %s", cf->linenr, cf->name);
 }
 
-static int parse_unit_factor(const char *end, unsigned long *val)
+static int parse_unit_factor(const char *end, uintmax_t *val)
 {
        if (!*end)
                return 1;
@@ -363,11 +356,23 @@ static int git_parse_long(const char *value, long *ret)
 {
        if (value && *value) {
                char *end;
-               long val = strtol(value, &end, 0);
-               unsigned long factor = 1;
+               intmax_t val;
+               uintmax_t uval;
+               uintmax_t factor = 1;
+
+               errno = 0;
+               val = strtoimax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
                if (!parse_unit_factor(end, &factor))
                        return 0;
-               *ret = val * factor;
+               uval = abs(val);
+               uval *= factor;
+               if ((uval > maximum_signed_value_of_type(long)) ||
+                   (abs(val) > uval))
+                       return 0;
+               val *= factor;
+               *ret = val;
                return 1;
        }
        return 0;
@@ -377,9 +382,19 @@ int git_parse_ulong(const char *value, unsigned long *ret)
 {
        if (value && *value) {
                char *end;
-               unsigned long val = strtoul(value, &end, 0);
+               uintmax_t val;
+               uintmax_t oldval;
+
+               errno = 0;
+               val = strtoumax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
+               oldval = val;
                if (!parse_unit_factor(end, &val))
                        return 0;
+               if ((val > maximum_unsigned_value_of_type(long)) ||
+                   (oldval > val))
+                       return 0;
                *ret = val;
                return 1;
        }
@@ -388,8 +403,8 @@ int git_parse_ulong(const char *value, unsigned long *ret)
 
 static void die_bad_config(const char *name)
 {
-       if (config_file_name)
-               die("bad config value for '%s' in %s", name, config_file_name);
+       if (cf && cf->name)
+               die("bad config value for '%s' in %s", name, cf->name);
        die("bad config value for '%s'", name);
 }
 
@@ -498,6 +513,9 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.attributesfile"))
+               return git_config_pathname(&git_attributes_file, var, value);
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@ -557,7 +575,7 @@ static int git_default_core_config(const char *var, const char *value)
 
        if (!strcmp(var, "core.packedgitwindowsize")) {
                int pgsz_x2 = getpagesize() * 2;
-               packed_git_window_size = git_config_int(var, value);
+               packed_git_window_size = git_config_ulong(var, value);
 
                /* This value must be multiple of (pagesize * 2) */
                packed_git_window_size /= pgsz_x2;
@@ -567,19 +585,27 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.bigfilethreshold")) {
+               big_file_threshold = git_config_ulong(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.packedgitlimit")) {
-               packed_git_limit = git_config_int(var, value);
+               packed_git_limit = git_config_ulong(var, value);
                return 0;
        }
 
        if (!strcmp(var, "core.deltabasecachelimit")) {
-               delta_base_cache_limit = git_config_int(var, value);
+               delta_base_cache_limit = git_config_ulong(var, value);
                return 0;
        }
 
+       if (!strcmp(var, "core.logpackaccess"))
+               return git_config_string(&log_pack_access, var, value);
+
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
-                       if (eol == EOL_CRLF)
+                       if (core_eol == EOL_CRLF)
                                return error("core.autocrlf=input conflicts with core.eol=crlf");
                        auto_crlf = AUTO_CRLF_INPUT;
                        return 0;
@@ -599,14 +625,14 @@ static int git_default_core_config(const char *var, const char *value)
 
        if (!strcmp(var, "core.eol")) {
                if (value && !strcasecmp(value, "lf"))
-                       eol = EOL_LF;
+                       core_eol = EOL_LF;
                else if (value && !strcasecmp(value, "crlf"))
-                       eol = EOL_CRLF;
+                       core_eol = EOL_CRLF;
                else if (value && !strcasecmp(value, "native"))
-                       eol = EOL_NATIVE;
+                       core_eol = EOL_NATIVE;
                else
-                       eol = EOL_UNSET;
-               if (eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
+                       core_eol = EOL_UNSET;
+               if (core_eol == EOL_CRLF && auto_crlf == AUTO_CRLF_INPUT)
                        return error("core.autocrlf=input conflicts with core.eol=crlf");
                return 0;
        }
@@ -803,13 +829,24 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 
        ret = -1;
        if (f) {
-               config_file = f;
-               config_file_name = filename;
-               config_linenr = 1;
-               config_file_eof = 0;
+               config_file top;
+
+               /* push config-file parsing state stack */
+               top.prev = cf;
+               top.f = f;
+               top.name = filename;
+               top.linenr = 1;
+               top.eof = 0;
+               strbuf_init(&top.value, 1024);
+               cf = &top;
+
                ret = git_parse_file(fn, data);
+
+               /* pop config-file parsing state stack */
+               strbuf_release(&top.value);
+               cf = top.prev;
+
                fclose(f);
-               config_file_name = NULL;
        }
        return ret;
 }
@@ -833,27 +870,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;
@@ -869,13 +885,13 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
        }
 
        home = getenv("HOME");
-       if (git_config_global() && home) {
-               char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
+       if (home) {
+               char buf[PATH_MAX];
+               char *user_config = mksnpath(buf, sizeof(buf), "%s/.gitconfig", home);
                if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
                        found += 1;
                }
-               free(user_config);
        }
 
        if (repo_config && !access(repo_config, R_OK)) {
@@ -883,9 +899,16 @@ 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 */
+               die("unable to parse command-line config");
+               break;
+       case 0: /* found nothing */
+               break;
+       default: /* found at least one item */
+               found++;
+               break;
+       }
 
        return ret == 0 ? found : ret;
 }
@@ -931,6 +954,7 @@ static int store_aux(const char *key, const char *value, void *cb)
 {
        const char *ep;
        size_t section_len;
+       FILE *f = cf->f;
 
        switch (store.state) {
        case KEY_SEEN:
@@ -942,7 +966,7 @@ static int store_aux(const char *key, const char *value, void *cb)
                                return 1;
                        }
 
-                       store.offset[store.seen] = ftell(config_file);
+                       store.offset[store.seen] = ftell(f);
                        store.seen++;
                }
                break;
@@ -969,19 +993,19 @@ static int store_aux(const char *key, const char *value, void *cb)
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
-               store.offset[store.seen] = ftell(config_file);
+               store.offset[store.seen] = ftell(f);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
-                       store.offset[store.seen] = ftell(config_file);
+                       store.offset[store.seen] = ftell(f);
                        store.state = KEY_SEEN;
                        store.seen++;
                } else {
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
-                                       store.offset[store.seen] = ftell(config_file);
+                                       store.offset[store.seen] = ftell(f);
                        }
                }
        }
@@ -1095,6 +1119,12 @@ contline:
        return offset;
 }
 
+int git_config_set_in_file(const char *config_filename,
+                       const char *key, const char *value)
+{
+       return git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
+}
+
 int git_config_set(const char *key, const char *value)
 {
        return git_config_set_multivar(key, value, NULL, 0);
@@ -1124,12 +1154,12 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 
        if (last_dot == NULL || last_dot == key) {
                error("key does not contain a section: %s", key);
-               return -2;
+               return -CONFIG_NO_SECTION_OR_NAME;
        }
 
        if (!last_dot[1]) {
                error("key does not contain variable name: %s", key);
-               return -2;
+               return -CONFIG_NO_SECTION_OR_NAME;
        }
 
        baselen = last_dot - key;
@@ -1166,7 +1196,7 @@ int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 
 out_free_ret_1:
        free(*store_key);
-       return -1;
+       return -CONFIG_INVALID_KEY;
 }
 
 /*
@@ -1192,19 +1222,14 @@ out_free_ret_1:
  * - the config file is removed and the lock file rename()d to it.
  *
  */
-int git_config_set_multivar(const char *key, const char *value,
-       const char *value_regex, int multi_replace)
+int git_config_set_multivar_in_file(const char *config_filename,
+                               const char *key, const char *value,
+                               const char *value_regex, int multi_replace)
 {
        int fd = -1, in_fd;
        int ret;
-       char *config_filename;
        struct lock_file *lock = NULL;
 
-       if (config_exclusive_filename)
-               config_filename = xstrdup(config_exclusive_filename);
-       else
-               config_filename = git_pathdup("config");
-
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
        if (ret)
@@ -1222,7 +1247,7 @@ int git_config_set_multivar(const char *key, const char *value,
        if (fd < 0) {
                error("could not lock config file %s: %s", config_filename, strerror(errno));
                free(store.key);
-               ret = -1;
+               ret = CONFIG_NO_LOCK;
                goto out_free;
        }
 
@@ -1236,12 +1261,12 @@ int git_config_set_multivar(const char *key, const char *value,
                if ( ENOENT != errno ) {
                        error("opening %s: %s", config_filename,
                              strerror(errno));
-                       ret = 3; /* same as "invalid config file" */
+                       ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
                /* if nothing to unset, error out */
                if (value == NULL) {
-                       ret = 5;
+                       ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
 
@@ -1269,7 +1294,7 @@ int git_config_set_multivar(const char *key, const char *value,
                                        REG_EXTENDED)) {
                                error("invalid pattern: %s", value_regex);
                                free(store.value_regex);
-                               ret = 6;
+                               ret = CONFIG_INVALID_PATTERN;
                                goto out_free;
                        }
                }
@@ -1291,7 +1316,7 @@ int git_config_set_multivar(const char *key, const char *value,
                                regfree(store.value_regex);
                                free(store.value_regex);
                        }
-                       ret = 3;
+                       ret = CONFIG_INVALID_FILE;
                        goto out_free;
                }
 
@@ -1304,7 +1329,7 @@ int git_config_set_multivar(const char *key, const char *value,
                /* if nothing to unset, or too many matches, error out */
                if ((store.seen == 0 && value == NULL) ||
                                (store.seen > 1 && multi_replace == 0)) {
-                       ret = 5;
+                       ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
 
@@ -1365,7 +1390,7 @@ int git_config_set_multivar(const char *key, const char *value,
 
        if (commit_lock_file(lock) < 0) {
                error("could not commit config file %s", config_filename);
-               ret = 4;
+               ret = CONFIG_NO_WRITE;
                goto out_free;
        }
 
@@ -1381,7 +1406,6 @@ int git_config_set_multivar(const char *key, const char *value,
 out_free:
        if (lock)
                rollback_lock_file(lock);
-       free(config_filename);
        return ret;
 
 write_err_out:
@@ -1390,6 +1414,24 @@ write_err_out:
 
 }
 
+int git_config_set_multivar(const char *key, const char *value,
+                       const char *value_regex, int multi_replace)
+{
+       const char *config_filename;
+       char *buf = NULL;
+       int ret;
+
+       if (config_exclusive_filename)
+               config_filename = config_exclusive_filename;
+       else
+               config_filename = buf = git_pathdup("config");
+
+       ret = git_config_set_multivar_in_file(config_filename, key, value,
+                                       value_regex, multi_replace);
+       free(buf);
+       return ret;
+}
+
 static int section_name_match (const char *buf, const char *name)
 {
        int i = 0, j = 0, dot = 0;
@@ -1437,6 +1479,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
        struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
        int out_fd;
        char buf[1024];
+       FILE *config_file;
 
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
@@ -1501,10 +1544,10 @@ int git_config_rename_section(const char *old_name, const char *new_name)
                }
        }
        fclose(config_file);
- unlock_and_out:
+unlock_and_out:
        if (commit_lock_file(lock) < 0)
                ret = error("could not commit config file %s", config_filename);
- out:
+out:
        free(config_filename);
        return ret;
 }