packaging: Add contrib installation
[platform/upstream/git.git] / config.c
index d75f88c..1137bd7 100644 (file)
--- a/config.c
+++ b/config.c
@@ -37,6 +37,7 @@ struct config_source {
        enum config_error_action default_error_action;
        int linenr;
        int eof;
+       size_t total_len;
        struct strbuf value;
        struct strbuf var;
        unsigned subsection_case_sensitive : 1;
@@ -204,7 +205,7 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
                strbuf_splice(pat, 0, 1, path.buf, slash - path.buf);
                prefix = slash - path.buf + 1 /* slash */;
        } else if (!is_absolute_path(pat->buf))
-               strbuf_insert(pat, 0, "**/", 3);
+               strbuf_insertstr(pat, 0, "**/");
 
        add_trailing_starstar_for_dir(pat);
 
@@ -309,7 +310,7 @@ int git_config_include(const char *var, const char *value, void *data)
 {
        struct config_include_data *inc = data;
        const char *cond, *key;
-       int cond_len;
+       size_t cond_len;
        int ret;
 
        /*
@@ -358,12 +359,13 @@ static inline int iskeychar(int c)
  *
  * 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
+ * baselen - pointer to size_t which will hold the length of the
  *           section + subsection part, can be NULL
  */
-static int git_config_parse_key_1(const char *key, char **store_key, int *baselen_, int quiet)
+static int git_config_parse_key_1(const char *key, char **store_key, size_t *baselen_, int quiet)
 {
-       int i, dot, baselen;
+       size_t i, baselen;
+       int dot;
        const char *last_dot = strrchr(key, '.');
 
        /*
@@ -425,7 +427,7 @@ out_free_ret_1:
        return -CONFIG_INVALID_KEY;
 }
 
-int git_config_parse_key(const char *key, char **store_key, int *baselen)
+int git_config_parse_key(const char *key, char **store_key, size_t *baselen)
 {
        return git_config_parse_key_1(key, store_key, baselen, 0);
 }
@@ -523,6 +525,19 @@ static int get_next_char(void)
                        c = '\r';
                }
        }
+
+       if (c != EOF && ++cf->total_len > INT_MAX) {
+               /*
+                * This is an absurdly long config file; refuse to parse
+                * further in order to protect downstream code from integer
+                * overflows. Note that we can't return an error specifically,
+                * but we can mark EOF and put trash in the return value,
+                * which will trigger a parse error.
+                */
+               cf->eof = 1;
+               return 0;
+       }
+
        if (c == '\n')
                cf->linenr++;
        if (c == EOF) {
@@ -728,7 +743,7 @@ static int git_parse_source(config_fn_t fn, void *data,
                            const struct config_options *opts)
 {
        int comment = 0;
-       int baselen = 0;
+       size_t baselen = 0;
        struct strbuf *var = &cf->var;
        int error_return = 0;
        char *error_msg = NULL;
@@ -1539,6 +1554,7 @@ static int do_config_from(struct config_source *top, config_fn_t fn, void *data,
        top->prev = cf;
        top->linenr = 1;
        top->eof = 0;
+       top->total_len = 0;
        strbuf_init(&top->value, 1024);
        strbuf_init(&top->var, 1024);
        cf = top;
@@ -1702,6 +1718,7 @@ static int do_git_config_sequence(const struct config_options *opts,
        char *xdg_config = xdg_config_home("config");
        char *user_config = expand_user_path("~/.gitconfig", 0);
        char *repo_config;
+       enum config_scope prev_parsing_scope = current_parsing_scope;
 
        if (opts->commondir)
                repo_config = mkpathdup("%s/config", opts->commondir);
@@ -1724,15 +1741,12 @@ static int do_git_config_sequence(const struct config_options *opts,
        if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file(fn, user_config, data);
 
-       current_parsing_scope = CONFIG_SCOPE_REPO;
+       current_parsing_scope = CONFIG_SCOPE_LOCAL;
        if (!opts->ignore_repo && repo_config &&
            !access_or_die(repo_config, R_OK, 0))
                ret += git_config_from_file(fn, repo_config, data);
 
-       /*
-        * Note: this should have a new scope, CONFIG_SCOPE_WORKTREE.
-        * But let's not complicate things before it's actually needed.
-        */
+       current_parsing_scope = CONFIG_SCOPE_WORKTREE;
        if (!opts->ignore_worktree && repository_format_worktree_config) {
                char *path = git_pathdup("config.worktree");
                if (!access_or_die(path, R_OK, 0))
@@ -1740,11 +1754,11 @@ static int do_git_config_sequence(const struct config_options *opts,
                free(path);
        }
 
-       current_parsing_scope = CONFIG_SCOPE_CMDLINE;
+       current_parsing_scope = CONFIG_SCOPE_COMMAND;
        if (!opts->ignore_cmdline && git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
 
-       current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
+       current_parsing_scope = prev_parsing_scope;
        free(xdg_config);
        free(user_config);
        free(repo_config);
@@ -1765,6 +1779,9 @@ int config_with_options(config_fn_t fn, void *data,
                data = &inc;
        }
 
+       if (config_source)
+               current_parsing_scope = config_source->scope;
+
        /*
         * If we have a specific filename, use it. Otherwise, follow the
         * regular lookup sequence.
@@ -1946,7 +1963,7 @@ void git_configset_clear(struct config_set *cs)
                free(entry->key);
                string_list_clear(&entry->value_list, 1);
        }
-       hashmap_free_entries(&cs->config_hash, struct config_set_element, ent);
+       hashmap_clear_and_free(&cs->config_hash, struct config_set_element, ent);
        cs->hash_initialized = 0;
        free(cs->list.items);
        cs->list.nr = 0;
@@ -1989,18 +2006,27 @@ const struct string_list *git_configset_get_value_multi(struct config_set *cs, c
        return e ? &e->value_list : NULL;
 }
 
-int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest)
+int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
 {
        const char *value;
        if (!git_configset_get_value(cs, key, &value))
-               return git_config_string(dest, key, value);
+               return git_config_string((const char **)dest, key, value);
        else
                return 1;
 }
 
-int git_configset_get_string(struct config_set *cs, const char *key, char **dest)
+int git_configset_get_string_tmp(struct config_set *cs, const char *key,
+                                const char **dest)
 {
-       return git_configset_get_string_const(cs, key, (const char **)dest);
+       const char *value;
+       if (!git_configset_get_value(cs, key, &value)) {
+               if (!value)
+                       return config_error_nonbool(key);
+               *dest = value;
+               return 0;
+       } else {
+               return 1;
+       }
 }
 
 int git_configset_get_int(struct config_set *cs, const char *key, int *dest)
@@ -2130,22 +2156,26 @@ const struct string_list *repo_config_get_value_multi(struct repository *repo,
        return git_configset_get_value_multi(repo->config, key);
 }
 
-int repo_config_get_string_const(struct repository *repo,
-                                const char *key, const char **dest)
+int repo_config_get_string(struct repository *repo,
+                          const char *key, char **dest)
 {
        int ret;
        git_config_check_init(repo);
-       ret = git_configset_get_string_const(repo->config, key, dest);
+       ret = git_configset_get_string(repo->config, key, dest);
        if (ret < 0)
                git_die_config(key, NULL);
        return ret;
 }
 
-int repo_config_get_string(struct repository *repo,
-                          const char *key, char **dest)
+int repo_config_get_string_tmp(struct repository *repo,
+                              const char *key, const char **dest)
 {
+       int ret;
        git_config_check_init(repo);
-       return repo_config_get_string_const(repo, key, (const char **)dest);
+       ret = git_configset_get_string_tmp(repo->config, key, dest);
+       if (ret < 0)
+               git_die_config(key, NULL);
+       return ret;
 }
 
 int repo_config_get_int(struct repository *repo,
@@ -2215,14 +2245,14 @@ const struct string_list *git_config_get_value_multi(const char *key)
        return repo_config_get_value_multi(the_repository, key);
 }
 
-int git_config_get_string_const(const char *key, const char **dest)
+int git_config_get_string(const char *key, char **dest)
 {
-       return repo_config_get_string_const(the_repository, key, dest);
+       return repo_config_get_string(the_repository, key, dest);
 }
 
-int git_config_get_string(const char *key, char **dest)
+int git_config_get_string_tmp(const char *key, const char **dest)
 {
-       return repo_config_get_string(the_repository, key, dest);
+       return repo_config_get_string_tmp(the_repository, key, dest);
 }
 
 int git_config_get_int(const char *key, int *dest)
@@ -2257,7 +2287,7 @@ int git_config_get_pathname(const char *key, const char **dest)
 
 int git_config_get_expiry(const char *key, const char **output)
 {
-       int ret = git_config_get_string_const(key, output);
+       int ret = git_config_get_string(key, (char **)output);
        if (ret)
                return ret;
        if (strcmp(*output, "now")) {
@@ -2270,11 +2300,11 @@ int git_config_get_expiry(const char *key, const char **output)
 
 int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamp_t now)
 {
-       char *expiry_string;
+       const char *expiry_string;
        intmax_t days;
        timestamp_t when;
 
-       if (git_config_get_string(key, &expiry_string))
+       if (git_config_get_string_tmp(key, &expiry_string))
                return 1; /* no such thing */
 
        if (git_parse_signed(expiry_string, &days, maximum_signed_value_of_type(int))) {
@@ -2382,10 +2412,11 @@ void git_die_config(const char *key, const char *err, ...)
  */
 
 struct config_store_data {
-       int baselen;
+       size_t baselen;
        char *key;
        int do_not_match;
-       regex_t *value_regex;
+       const char *fixed_value;
+       regex_t *value_pattern;
        int multi_replace;
        struct {
                size_t begin, end;
@@ -2399,10 +2430,10 @@ struct config_store_data {
 static void config_store_data_clear(struct config_store_data *store)
 {
        free(store->key);
-       if (store->value_regex != NULL &&
-           store->value_regex != CONFIG_REGEX_NONE) {
-               regfree(store->value_regex);
-               free(store->value_regex);
+       if (store->value_pattern != NULL &&
+           store->value_pattern != CONFIG_REGEX_NONE) {
+               regfree(store->value_pattern);
+               free(store->value_pattern);
        }
        free(store->parsed);
        free(store->seen);
@@ -2414,13 +2445,15 @@ static int matches(const char *key, const char *value,
 {
        if (strcmp(key, store->key))
                return 0; /* not ours */
-       if (!store->value_regex)
+       if (store->fixed_value)
+               return !strcmp(store->fixed_value, value);
+       if (!store->value_pattern)
                return 1; /* always matches */
-       if (store->value_regex == CONFIG_REGEX_NONE)
+       if (store->value_pattern == CONFIG_REGEX_NONE)
                return 0; /* never matches */
 
        return store->do_not_match ^
-               (value && !regexec(store->value_regex, value, 0, NULL, 0));
+               (value && !regexec(store->value_pattern, value, 0, NULL, 0));
 }
 
 static int store_aux_event(enum config_event_t type,
@@ -2508,7 +2541,7 @@ static struct strbuf store_create_section(const char *key,
                                          const struct config_store_data *store)
 {
        const char *dot;
-       int i;
+       size_t i;
        struct strbuf sb = STRBUF_INIT;
 
        dot = memchr(key, '.', store->baselen);
@@ -2521,7 +2554,9 @@ static struct strbuf store_create_section(const char *key,
                }
                strbuf_addstr(&sb, "\"]\n");
        } else {
-               strbuf_addf(&sb, "[%.*s]\n", store->baselen, key);
+               strbuf_addch(&sb, '[');
+               strbuf_add(&sb, key, store->baselen);
+               strbuf_addstr(&sb, "]\n");
        }
 
        return sb;
@@ -2544,7 +2579,6 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
 {
        int i;
        ssize_t ret;
-       int length = strlen(key + store->baselen + 1);
        const char *quote = "";
        struct strbuf sb = STRBUF_INIT;
 
@@ -2563,8 +2597,7 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
        if (i && value[i - 1] == ' ')
                quote = "\"";
 
-       strbuf_addf(&sb, "\t%.*s = %s",
-                   length, key + store->baselen + 1, quote);
+       strbuf_addf(&sb, "\t%s = %s", key + store->baselen + 1, quote);
 
        for (i = 0; value[i]; i++)
                switch (value[i]) {
@@ -2696,12 +2729,12 @@ void git_config_set(const char *key, const char *value)
 
 /*
  * If value==NULL, unset in (remove from) config,
- * if value_regex!=NULL, disregard key/value pairs where value does not match.
- * if value_regex==CONFIG_REGEX_NONE, do not match any existing values
+ * if value_pattern!=NULL, disregard key/value pairs where value does not match.
+ * if value_pattern==CONFIG_REGEX_NONE, do not match any existing values
  *     (only add a new one)
- * if multi_replace==0, nothing, or only one matching key/value is replaced,
- *     else all matching key/values (regardless how many) are removed,
- *     before the new pair is written.
+ * if flags contains the CONFIG_FLAGS_MULTI_REPLACE flag, all matching
+ *     key/values are removed before a single new pair is written. If the
+ *     flag is not present, then replace only the first match.
  *
  * Returns 0 on success.
  *
@@ -2721,8 +2754,8 @@ void git_config_set(const char *key, const char *value)
  */
 int git_config_set_multivar_in_file_gently(const char *config_filename,
                                           const char *key, const char *value,
-                                          const char *value_regex,
-                                          int multi_replace)
+                                          const char *value_pattern,
+                                          unsigned flags)
 {
        int fd = -1, in_fd = -1;
        int ret;
@@ -2739,7 +2772,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        if (ret)
                goto out_free;
 
-       store.multi_replace = multi_replace;
+       store.multi_replace = (flags & CONFIG_FLAGS_MULTI_REPLACE) != 0;
 
        if (!config_filename)
                config_filename = filename_buf = git_pathdup("config");
@@ -2782,22 +2815,24 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                int i, new_line = 0;
                struct config_options opts;
 
-               if (value_regex == NULL)
-                       store.value_regex = NULL;
-               else if (value_regex == CONFIG_REGEX_NONE)
-                       store.value_regex = CONFIG_REGEX_NONE;
+               if (value_pattern == NULL)
+                       store.value_pattern = NULL;
+               else if (value_pattern == CONFIG_REGEX_NONE)
+                       store.value_pattern = CONFIG_REGEX_NONE;
+               else if (flags & CONFIG_FLAGS_FIXED_VALUE)
+                       store.fixed_value = value_pattern;
                else {
-                       if (value_regex[0] == '!') {
+                       if (value_pattern[0] == '!') {
                                store.do_not_match = 1;
-                               value_regex++;
+                               value_pattern++;
                        } else
                                store.do_not_match = 0;
 
-                       store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
-                       if (regcomp(store.value_regex, value_regex,
+                       store.value_pattern = (regex_t*)xmalloc(sizeof(regex_t));
+                       if (regcomp(store.value_pattern, value_pattern,
                                        REG_EXTENDED)) {
-                               error(_("invalid pattern: %s"), value_regex);
-                               FREE_AND_NULL(store.value_regex);
+                               error(_("invalid pattern: %s"), value_pattern);
+                               FREE_AND_NULL(store.value_pattern);
                                ret = CONFIG_INVALID_PATTERN;
                                goto out_free;
                        }
@@ -2828,7 +2863,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 
                /* if nothing to unset, or too many matches, error out */
                if ((store.seen_nr == 0 && value == NULL) ||
-                   (store.seen_nr > 1 && multi_replace == 0)) {
+                   (store.seen_nr > 1 && !store.multi_replace)) {
                        ret = CONFIG_NOTHING_SET;
                        goto out_free;
                }
@@ -2967,10 +3002,10 @@ write_err_out:
 
 void git_config_set_multivar_in_file(const char *config_filename,
                                     const char *key, const char *value,
-                                    const char *value_regex, int multi_replace)
+                                    const char *value_pattern, unsigned flags)
 {
        if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
-                                                   value_regex, multi_replace))
+                                                   value_pattern, flags))
                return;
        if (value)
                die(_("could not set '%s' to '%s'"), key, value);
@@ -2979,17 +3014,17 @@ void git_config_set_multivar_in_file(const char *config_filename,
 }
 
 int git_config_set_multivar_gently(const char *key, const char *value,
-                                  const char *value_regex, int multi_replace)
+                                  const char *value_pattern, unsigned flags)
 {
-       return git_config_set_multivar_in_file_gently(NULL, key, value, value_regex,
-                                                     multi_replace);
+       return git_config_set_multivar_in_file_gently(NULL, key, value, value_pattern,
+                                                     flags);
 }
 
 void git_config_set_multivar(const char *key, const char *value,
-                            const char *value_regex, int multi_replace)
+                            const char *value_pattern, unsigned flags)
 {
-       git_config_set_multivar_in_file(NULL, key, value, value_regex,
-                                       multi_replace);
+       git_config_set_multivar_in_file(NULL, key, value, value_pattern,
+                                       flags);
 }
 
 static int section_name_match (const char *buf, const char *name)
@@ -3098,7 +3133,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
        }
 
        while (fgets(buf, sizeof(buf), config_file)) {
-               int i;
+               unsigned i;
                int length;
                int is_section = 0;
                char *output = buf;
@@ -3237,7 +3272,7 @@ int config_error_nonbool(const char *var)
 
 int parse_config_key(const char *var,
                     const char *section,
-                    const char **subsection, int *subsection_len,
+                    const char **subsection, size_t *subsection_len,
                     const char **key)
 {
        const char *dot;
@@ -3297,6 +3332,26 @@ const char *current_config_origin_type(void)
        }
 }
 
+const char *config_scope_name(enum config_scope scope)
+{
+       switch (scope) {
+       case CONFIG_SCOPE_SYSTEM:
+               return "system";
+       case CONFIG_SCOPE_GLOBAL:
+               return "global";
+       case CONFIG_SCOPE_LOCAL:
+               return "local";
+       case CONFIG_SCOPE_WORKTREE:
+               return "worktree";
+       case CONFIG_SCOPE_COMMAND:
+               return "command";
+       case CONFIG_SCOPE_SUBMODULE:
+               return "submodule";
+       default:
+               return "unknown";
+       }
+}
+
 const char *current_config_name(void)
 {
        const char *name;
@@ -3317,6 +3372,14 @@ enum config_scope current_config_scope(void)
                return current_parsing_scope;
 }
 
+int current_config_line(void)
+{
+       if (current_config_kvi)
+               return current_config_kvi->linenr;
+       else
+               return cf->linenr;
+}
+
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
        int i;