]> git.ipfire.org Git - thirdparty/git.git/blobdiff - config.c
The sixteenth batch
[thirdparty/git.git] / config.c
index 3cfeb3d8bd99f4ca15d0f3a06cd4b1fe932f7f47..716d3d0ebf13abc2728834eac4ceacb2fc9bd52c 100644 (file)
--- a/config.c
+++ b/config.c
@@ -125,7 +125,7 @@ struct config_include_data {
        config_fn_t fn;
        void *data;
        const struct config_options *opts;
-       struct git_config_source *config_source;
+       const struct git_config_source *config_source;
        struct repository *repo;
 
        /*
@@ -303,7 +303,8 @@ static int include_by_branch(const char *cond, size_t cond_len)
        int ret;
        struct strbuf pattern = STRBUF_INIT;
        const char *refname = !the_repository->gitdir ?
-               NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+               NULL : refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+                                              "HEAD", 0, NULL, &flags);
        const char *shortname;
 
        if (!refname || !(flags & REF_ISSYMREF) ||
@@ -817,7 +818,8 @@ static int get_next_char(struct config_source *cs)
 
 static char *parse_value(struct config_source *cs)
 {
-       int quote = 0, comment = 0, space = 0;
+       int quote = 0, comment = 0;
+       size_t trim_len = 0;
 
        strbuf_reset(&cs->value);
        for (;;) {
@@ -827,13 +829,17 @@ static char *parse_value(struct config_source *cs)
                                cs->linenr--;
                                return NULL;
                        }
+                       if (trim_len)
+                               strbuf_setlen(&cs->value, trim_len);
                        return cs->value.buf;
                }
                if (comment)
                        continue;
                if (isspace(c) && !quote) {
+                       if (!trim_len)
+                               trim_len = cs->value.len;
                        if (cs->value.len)
-                               space++;
+                               strbuf_addch(&cs->value, c);
                        continue;
                }
                if (!quote) {
@@ -842,8 +848,8 @@ static char *parse_value(struct config_source *cs)
                                continue;
                        }
                }
-               for (; space; space--)
-                       strbuf_addch(&cs->value, ' ');
+               if (trim_len)
+                       trim_len = 0;
                if (c == '\\') {
                        c = get_next_char(cs);
                        switch (c) {
@@ -869,7 +875,7 @@ static char *parse_value(struct config_source *cs)
                        continue;
                }
                if (c == '"') {
-                       quote = 1-quote;
+                       quote = 1 - quote;
                        continue;
                }
                strbuf_addch(&cs->value, c);
@@ -1238,6 +1244,15 @@ ssize_t git_config_ssize_t(const char *name, const char *value,
        return ret;
 }
 
+double git_config_double(const char *name, const char *value,
+                        const struct key_value_info *kvi)
+{
+       double ret;
+       if (!git_parse_double(value, &ret))
+               die_bad_number(name, value, kvi);
+       return ret;
+}
+
 static const struct fsync_component_name {
        const char *name;
        enum fsync_component component_bits;
@@ -1332,7 +1347,7 @@ int git_config_bool(const char *name, const char *value)
        return v;
 }
 
-int git_config_string(const char **dest, const char *var, const char *value)
+int git_config_string(char **dest, const char *var, const char *value)
 {
        if (!value)
                return config_error_nonbool(var);
@@ -1340,7 +1355,7 @@ int git_config_string(const char **dest, const char *var, const char *value)
        return 0;
 }
 
-int git_config_pathname(const char **dest, const char *var, const char *value)
+int git_config_pathname(char **dest, const char *var, const char *value)
 {
        if (!value)
                return config_error_nonbool(var);
@@ -1408,11 +1423,15 @@ static int git_default_core_config(const char *var, const char *value,
                return 0;
        }
 
-       if (!strcmp(var, "core.attributesfile"))
+       if (!strcmp(var, "core.attributesfile")) {
+               FREE_AND_NULL(git_attributes_file);
                return git_config_pathname(&git_attributes_file, var, value);
+       }
 
-       if (!strcmp(var, "core.hookspath"))
+       if (!strcmp(var, "core.hookspath")) {
+               FREE_AND_NULL(git_hooks_path);
                return git_config_pathname(&git_hooks_path, var, value);
+       }
 
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
@@ -1450,10 +1469,10 @@ static int git_default_core_config(const char *var, const char *value,
                if (!strcasecmp(value, "auto"))
                        default_abbrev = -1;
                else if (!git_parse_maybe_bool_text(value))
-                       default_abbrev = the_hash_algo->hexsz;
+                       default_abbrev = GIT_MAX_HEXSZ;
                else {
                        int abbrev = git_config_int(var, value, ctx->kvi);
-                       if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
+                       if (abbrev < minimum_abbrev)
                                return error(_("abbrev length out of range: %d"), abbrev);
                        default_abbrev = abbrev;
                }
@@ -1547,8 +1566,10 @@ static int git_default_core_config(const char *var, const char *value,
                return 0;
        }
 
-       if (!strcmp(var, "core.checkroundtripencoding"))
+       if (!strcmp(var, "core.checkroundtripencoding")) {
+               FREE_AND_NULL(check_roundtrip_encoding);
                return git_config_string(&check_roundtrip_encoding, var, value);
+       }
 
        if (!strcmp(var, "core.notesref")) {
                if (!value)
@@ -1557,27 +1578,36 @@ static int git_default_core_config(const char *var, const char *value,
                return 0;
        }
 
-       if (!strcmp(var, "core.editor"))
+       if (!strcmp(var, "core.editor")) {
+               FREE_AND_NULL(editor_program);
                return git_config_string(&editor_program, var, value);
+       }
 
-       if (!strcmp(var, "core.commentchar")) {
+       if (!strcmp(var, "core.commentchar") ||
+           !strcmp(var, "core.commentstring")) {
                if (!value)
                        return config_error_nonbool(var);
                else if (!strcasecmp(value, "auto"))
                        auto_comment_line_char = 1;
-               else if (value[0] && !value[1]) {
-                       comment_line_char = value[0];
+               else if (value[0]) {
+                       if (strchr(value, '\n'))
+                               return error(_("%s cannot contain newline"), var);
+                       comment_line_str = xstrdup(value);
                        auto_comment_line_char = 0;
                } else
-                       return error(_("core.commentChar should only be one ASCII character"));
+                       return error(_("%s must have at least one character"), var);
                return 0;
        }
 
-       if (!strcmp(var, "core.askpass"))
+       if (!strcmp(var, "core.askpass")) {
+               FREE_AND_NULL(askpass_program);
                return git_config_string(&askpass_program, var, value);
+       }
 
-       if (!strcmp(var, "core.excludesfile"))
+       if (!strcmp(var, "core.excludesfile")) {
+               FREE_AND_NULL(excludes_file);
                return git_config_pathname(&excludes_file, var, value);
+       }
 
        if (!strcmp(var, "core.whitespace")) {
                if (!value)
@@ -1678,11 +1708,15 @@ static int git_default_sparse_config(const char *var, const char *value)
 
 static int git_default_i18n_config(const char *var, const char *value)
 {
-       if (!strcmp(var, "i18n.commitencoding"))
+       if (!strcmp(var, "i18n.commitencoding")) {
+               FREE_AND_NULL(git_commit_encoding);
                return git_config_string(&git_commit_encoding, var, value);
+       }
 
-       if (!strcmp(var, "i18n.logoutputencoding"))
+       if (!strcmp(var, "i18n.logoutputencoding")) {
+               FREE_AND_NULL(git_log_output_encoding);
                return git_config_string(&git_log_output_encoding, var, value);
+       }
 
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
@@ -1755,10 +1789,15 @@ static int git_default_push_config(const char *var, const char *value)
 
 static int git_default_mailmap_config(const char *var, const char *value)
 {
-       if (!strcmp(var, "mailmap.file"))
+       if (!strcmp(var, "mailmap.file")) {
+               FREE_AND_NULL(git_mailmap_file);
                return git_config_pathname(&git_mailmap_file, var, value);
-       if (!strcmp(var, "mailmap.blob"))
+       }
+
+       if (!strcmp(var, "mailmap.blob")) {
+               FREE_AND_NULL(git_mailmap_blob);
                return git_config_string(&git_mailmap_blob, var, value);
+       }
 
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
@@ -1766,8 +1805,10 @@ static int git_default_mailmap_config(const char *var, const char *value)
 
 static int git_default_attr_config(const char *var, const char *value)
 {
-       if (!strcmp(var, "attr.tree"))
+       if (!strcmp(var, "attr.tree")) {
+               FREE_AND_NULL(git_attr_tree);
                return git_config_string(&git_attr_tree, var, value);
+       }
 
        /*
         * Add other attribute related config variables here and to
@@ -2095,7 +2136,7 @@ static int do_git_config_sequence(const struct config_options *opts,
 }
 
 int config_with_options(config_fn_t fn, void *data,
-                       struct git_config_source *config_source,
+                       const struct git_config_source *config_source,
                        struct repository *repo,
                        const struct config_options *opts)
 {
@@ -2394,7 +2435,7 @@ int git_configset_get_string(struct config_set *set, const char *key, char **des
 {
        const char *value;
        if (!git_configset_get_value(set, key, &value, NULL))
-               return git_config_string((const char **)dest, key, value);
+               return git_config_string(dest, key, value);
        else
                return 1;
 }
@@ -2472,7 +2513,7 @@ int git_configset_get_maybe_bool(struct config_set *set, const char *key, int *d
                return 1;
 }
 
-int git_configset_get_pathname(struct config_set *set, const char *key, const char **dest)
+int git_configset_get_pathname(struct config_set *set, const char *key, char **dest)
 {
        const char *value;
        if (!git_configset_get_value(set, key, &value, NULL))
@@ -2617,7 +2658,7 @@ int repo_config_get_maybe_bool(struct repository *repo,
 }
 
 int repo_config_get_pathname(struct repository *repo,
-                            const char *key, const char **dest)
+                            const char *key, char **dest)
 {
        int ret;
        git_config_check_init(repo);
@@ -2716,7 +2757,7 @@ int git_config_get_maybe_bool(const char *key, int *dest)
        return repo_config_get_maybe_bool(the_repository, key, dest);
 }
 
-int git_config_get_pathname(const char *key, const char **dest)
+int git_config_get_pathname(const char *key, char **dest)
 {
        return repo_config_get_pathname(the_repository, key, dest);
 }
@@ -2812,7 +2853,6 @@ void git_die_config_linenr(const char *key, const char *filename, int linenr)
                    key, filename, linenr);
 }
 
-NORETURN __attribute__((format(printf, 2, 3)))
 void git_die_config(const char *key, const char *err, ...)
 {
        const struct string_list *values;
@@ -3001,6 +3041,7 @@ static ssize_t write_section(int fd, const char *key,
 }
 
 static ssize_t write_pair(int fd, const char *key, const char *value,
+                         const char *comment,
                          const struct config_store_data *store)
 {
        int i;
@@ -3041,7 +3082,11 @@ static ssize_t write_pair(int fd, const char *key, const char *value,
                        strbuf_addch(&sb, value[i]);
                        break;
                }
-       strbuf_addf(&sb, "%s\n", quote);
+
+       if (comment)
+               strbuf_addf(&sb, "%s%s\n", quote, comment);
+       else
+               strbuf_addf(&sb, "%s\n", quote);
 
        ret = write_in_full(fd, sb.buf, sb.len);
        strbuf_release(&sb);
@@ -3130,9 +3175,9 @@ static void maybe_remove_section(struct config_store_data *store,
 }
 
 int git_config_set_in_file_gently(const char *config_filename,
-                                 const char *key, const char *value)
+                                 const char *key, const char *comment, const char *value)
 {
-       return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, 0);
+       return git_config_set_multivar_in_file_gently(config_filename, key, value, NULL, comment, 0);
 }
 
 void git_config_set_in_file(const char *config_filename,
@@ -3153,7 +3198,7 @@ int repo_config_set_worktree_gently(struct repository *r,
        if (r->repository_format_worktree_config) {
                char *file = repo_git_path(r, "config.worktree");
                int ret = git_config_set_multivar_in_file_gently(
-                                       file, key, value, NULL, 0);
+                                       file, key, value, NULL, NULL, 0);
                free(file);
                return ret;
        }
@@ -3167,6 +3212,58 @@ void git_config_set(const char *key, const char *value)
        trace2_cmd_set_config(key, value);
 }
 
+char *git_config_prepare_comment_string(const char *comment)
+{
+       size_t leading_blanks;
+       char *prepared;
+
+       if (!comment)
+               return NULL;
+
+       if (strchr(comment, '\n'))
+               die(_("no multi-line comment allowed: '%s'"), comment);
+
+       /*
+        * If it begins with one or more leading whitespace characters
+        * followed by '#", the comment string is used as-is.
+        *
+        * If it begins with '#', a SP is inserted between the comment
+        * and the value the comment is about.
+        *
+        * Otherwise, the value is followed by a SP followed by '#'
+        * followed by SP and then the comment string comes.
+        */
+
+       leading_blanks = strspn(comment, " \t");
+       if (leading_blanks && comment[leading_blanks] == '#')
+               prepared = xstrdup(comment); /* use it as-is */
+       else if (comment[0] == '#')
+               prepared = xstrfmt(" %s", comment);
+       else
+               prepared = xstrfmt(" # %s", comment);
+
+       return prepared;
+}
+
+static void validate_comment_string(const char *comment)
+{
+       size_t leading_blanks;
+
+       if (!comment)
+               return;
+       /*
+        * The front-end must have massaged the comment string
+        * properly before calling us.
+        */
+       if (strchr(comment, '\n'))
+               BUG("multi-line comments are not permitted: '%s'", comment);
+
+       leading_blanks = strspn(comment, " \t");
+       if (!leading_blanks || comment[leading_blanks] != '#')
+               BUG("comment must begin with one or more SP followed by '#': '%s'",
+                   comment);
+}
+
 /*
  * If value==NULL, unset in (remove from) config,
  * if value_pattern!=NULL, disregard key/value pairs where value does not match.
@@ -3195,6 +3292,7 @@ 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_pattern,
+                                          const char *comment,
                                           unsigned flags)
 {
        int fd = -1, in_fd = -1;
@@ -3205,6 +3303,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        size_t contents_sz;
        struct config_store_data store = CONFIG_STORE_INIT;
 
+       validate_comment_string(comment);
+
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
        if (ret)
@@ -3245,7 +3345,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                free(store.key);
                store.key = xstrdup(key);
                if (write_section(fd, key, &store) < 0 ||
-                   write_pair(fd, key, value, &store) < 0)
+                   write_pair(fd, key, value, comment, &store) < 0)
                        goto write_err_out;
        } else {
                struct stat st;
@@ -3399,7 +3499,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                                if (write_section(fd, key, &store) < 0)
                                        goto write_err_out;
                        }
-                       if (write_pair(fd, key, value, &store) < 0)
+                       if (write_pair(fd, key, value, comment, &store) < 0)
                                goto write_err_out;
                }
 
@@ -3444,7 +3544,7 @@ void git_config_set_multivar_in_file(const char *config_filename,
                                     const char *value_pattern, unsigned flags)
 {
        if (!git_config_set_multivar_in_file_gently(config_filename, key, value,
-                                                   value_pattern, flags))
+                                                   value_pattern, NULL, flags))
                return;
        if (value)
                die(_("could not set '%s' to '%s'"), key, value);
@@ -3467,7 +3567,7 @@ int repo_config_set_multivar_gently(struct repository *r, const char *key,
        int res = git_config_set_multivar_in_file_gently(file,
                                                         key, value,
                                                         value_pattern,
-                                                        flags);
+                                                        NULL, flags);
        free(file);
        return res;
 }