Merge branch 'rs/apply-avoid-over-reading'
[gitweb.git] / config.c
index a30056ec7e9a0cca87dea494a92a4671a19b7643..4638b0696ab8885afe1fc696dd6e0a25dcc1c89f 100644 (file)
--- a/config.c
+++ b/config.c
@@ -6,6 +6,8 @@
  *
  */
 #include "cache.h"
+#include "config.h"
+#include "repository.h"
 #include "lockfile.h"
 #include "exec_cmd.h"
 #include "strbuf.h"
@@ -71,13 +73,6 @@ static int core_compression_seen;
 static int pack_compression_seen;
 static int zlib_compression_seen;
 
-/*
- * Default config_set that contains key-value pairs from the usual set of config
- * config files (i.e repo specific .git/config, user wide ~/.gitconfig, XDG
- * config file and the global /etc/gitconfig)
- */
-static struct config_set the_config_set;
-
 static int config_file_fgetc(struct config_source *conf)
 {
        return getc_unlocked(conf->u.file);
@@ -214,11 +209,10 @@ static int include_by_gitdir(const struct config_options *opts,
        struct strbuf pattern = STRBUF_INIT;
        int ret = 0, prefix;
        const char *git_dir;
+       int already_tried_absolute = 0;
 
        if (opts->git_dir)
                git_dir = opts->git_dir;
-       else if (have_git_dir())
-               git_dir = get_git_dir();
        else
                goto done;
 
@@ -226,6 +220,7 @@ static int include_by_gitdir(const struct config_options *opts,
        strbuf_add(&pattern, cond, cond_len);
        prefix = prepare_include_condition_pattern(&pattern);
 
+again:
        if (prefix < 0)
                goto done;
 
@@ -245,6 +240,20 @@ static int include_by_gitdir(const struct config_options *opts,
        ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
                         icase ? WM_CASEFOLD : 0, NULL);
 
+       if (!ret && !already_tried_absolute) {
+               /*
+                * We've tried e.g. matching gitdir:~/work, but if
+                * ~/work is a symlink to /mnt/storage/work
+                * strbuf_realpath() will expand it, so the rule won't
+                * match. Let's match against a
+                * strbuf_add_absolute_path() version of the path,
+                * which'll do the right thing
+                */
+               strbuf_reset(&text);
+               strbuf_add_absolute_path(&text, git_dir);
+               already_tried_absolute = 1;
+               goto again;
+       }
 done:
        strbuf_release(&pattern);
        strbuf_release(&text);
@@ -379,8 +388,7 @@ static int git_config_parse_key_1(const char *key, char **store_key, int *basele
 
 out_free_ret_1:
        if (store_key) {
-               free(*store_key);
-               *store_key = NULL;
+               FREE_AND_NULL(*store_key);
        }
        return -CONFIG_INVALID_KEY;
 }
@@ -588,7 +596,8 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name)
         */
        cf->linenr--;
        ret = fn(name->buf, value, data);
-       cf->linenr++;
+       if (ret >= 0)
+               cf->linenr++;
        return ret;
 }
 
@@ -1422,7 +1431,7 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        int ret = -1;
        FILE *f;
 
-       f = fopen(filename, "r");
+       f = fopen_or_warn(filename, "r");
        if (f) {
                flockfile(f);
                ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
@@ -1529,10 +1538,8 @@ static int do_git_config_sequence(const struct config_options *opts,
        char *user_config = expand_user_path("~/.gitconfig", 0);
        char *repo_config;
 
-       if (opts->git_dir)
-               repo_config = mkpathdup("%s/config", opts->git_dir);
-       else if (have_git_dir())
-               repo_config = git_pathdup("config");
+       if (opts->commondir)
+               repo_config = mkpathdup("%s/config", opts->commondir);
        else
                repo_config = NULL;
 
@@ -1563,9 +1570,9 @@ static int do_git_config_sequence(const struct config_options *opts,
        return ret;
 }
 
-int git_config_with_options(config_fn_t fn, void *data,
-                           struct git_config_source *config_source,
-                           const struct config_options *opts)
+int config_with_options(config_fn_t fn, void *data,
+                       struct git_config_source *config_source,
+                       const struct config_options *opts)
 {
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
 
@@ -1591,26 +1598,6 @@ int git_config_with_options(config_fn_t fn, void *data,
        return do_git_config_sequence(opts, fn, data);
 }
 
-static void git_config_raw(config_fn_t fn, void *data)
-{
-       struct config_options opts = {0};
-
-       opts.respect_includes = 1;
-       if (git_config_with_options(fn, data, NULL, &opts) < 0)
-               /*
-                * git_config_with_options() normally returns only
-                * zero, as most errors are fatal, and
-                * non-fatal potential errors are guarded by "if"
-                * statements that are entered only when no error is
-                * possible.
-                *
-                * If we ever encounter a non-fatal error, it means
-                * something went really wrong and we should stop
-                * immediately.
-                */
-               die(_("unknown error occurred while reading the configuration files"));
-}
-
 static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
 {
        int i, value_index;
@@ -1637,11 +1624,13 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
 void read_early_config(config_fn_t cb, void *data)
 {
        struct config_options opts = {0};
-       struct strbuf buf = STRBUF_INIT;
+       struct strbuf commondir = STRBUF_INIT;
+       struct strbuf gitdir = STRBUF_INIT;
 
        opts.respect_includes = 1;
 
-       if (have_git_dir())
+       if (have_git_dir()) {
+               opts.commondir = get_git_common_dir();
                opts.git_dir = get_git_dir();
        /*
         * When setup_git_directory() was not yet asked to discover the
@@ -1651,20 +1640,15 @@ void read_early_config(config_fn_t cb, void *data)
         * notably, the current working directory is still the same after the
         * call).
         */
-       else if (discover_git_directory(&buf))
-               opts.git_dir = buf.buf;
-
-       git_config_with_options(cb, data, NULL, &opts);
-
-       strbuf_release(&buf);
-}
+       } else if (!discover_git_directory(&commondir, &gitdir)) {
+               opts.commondir = commondir.buf;
+               opts.git_dir = gitdir.buf;
+       }
 
-static void git_config_check_init(void);
+       config_with_options(cb, data, NULL, &opts);
 
-void git_config(config_fn_t fn, void *data)
-{
-       git_config_check_init();
-       configset_iter(&the_config_set, fn, data);
+       strbuf_release(&commondir);
+       strbuf_release(&gitdir);
 }
 
 static struct config_set_element *configset_find_element(struct config_set *cs, const char *key)
@@ -1876,87 +1860,194 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha
                return 1;
 }
 
-static void git_config_check_init(void)
+/* Functions use to read configuration from a repository */
+static void repo_read_config(struct repository *repo)
 {
-       if (the_config_set.hash_initialized)
+       struct config_options opts;
+
+       opts.respect_includes = 1;
+       opts.commondir = repo->commondir;
+       opts.git_dir = repo->gitdir;
+
+       if (!repo->config)
+               repo->config = xcalloc(1, sizeof(struct config_set));
+       else
+               git_configset_clear(repo->config);
+
+       git_configset_init(repo->config);
+
+       if (config_with_options(config_set_callback, repo->config, NULL, &opts) < 0)
+               /*
+                * config_with_options() normally returns only
+                * zero, as most errors are fatal, and
+                * non-fatal potential errors are guarded by "if"
+                * statements that are entered only when no error is
+                * possible.
+                *
+                * If we ever encounter a non-fatal error, it means
+                * something went really wrong and we should stop
+                * immediately.
+                */
+               die(_("unknown error occurred while reading the configuration files"));
+}
+
+static void git_config_check_init(struct repository *repo)
+{
+       if (repo->config && repo->config->hash_initialized)
                return;
-       git_configset_init(&the_config_set);
-       git_config_raw(config_set_callback, &the_config_set);
+       repo_read_config(repo);
 }
 
-void git_config_clear(void)
+static void repo_config_clear(struct repository *repo)
 {
-       if (!the_config_set.hash_initialized)
+       if (!repo->config || !repo->config->hash_initialized)
                return;
-       git_configset_clear(&the_config_set);
+       git_configset_clear(repo->config);
 }
 
-int git_config_get_value(const char *key, const char **value)
+void repo_config(struct repository *repo, config_fn_t fn, void *data)
 {
-       git_config_check_init();
-       return git_configset_get_value(&the_config_set, key, value);
+       git_config_check_init(repo);
+       configset_iter(repo->config, fn, data);
 }
 
-const struct string_list *git_config_get_value_multi(const char *key)
+int repo_config_get_value(struct repository *repo,
+                         const char *key, const char **value)
 {
-       git_config_check_init();
-       return git_configset_get_value_multi(&the_config_set, key);
+       git_config_check_init(repo);
+       return git_configset_get_value(repo->config, key, value);
 }
 
-int git_config_get_string_const(const char *key, const char **dest)
+const struct string_list *repo_config_get_value_multi(struct repository *repo,
+                                                     const char *key)
+{
+       git_config_check_init(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 ret;
-       git_config_check_init();
-       ret = git_configset_get_string_const(&the_config_set, key, dest);
+       git_config_check_init(repo);
+       ret = git_configset_get_string_const(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)
+{
+       git_config_check_init(repo);
+       return repo_config_get_string_const(repo, key, (const char **)dest);
+}
+
+int repo_config_get_int(struct repository *repo,
+                       const char *key, int *dest)
+{
+       git_config_check_init(repo);
+       return git_configset_get_int(repo->config, key, dest);
+}
+
+int repo_config_get_ulong(struct repository *repo,
+                         const char *key, unsigned long *dest)
+{
+       git_config_check_init(repo);
+       return git_configset_get_ulong(repo->config, key, dest);
+}
+
+int repo_config_get_bool(struct repository *repo,
+                        const char *key, int *dest)
+{
+       git_config_check_init(repo);
+       return git_configset_get_bool(repo->config, key, dest);
+}
+
+int repo_config_get_bool_or_int(struct repository *repo,
+                               const char *key, int *is_bool, int *dest)
+{
+       git_config_check_init(repo);
+       return git_configset_get_bool_or_int(repo->config, key, is_bool, dest);
+}
+
+int repo_config_get_maybe_bool(struct repository *repo,
+                              const char *key, int *dest)
+{
+       git_config_check_init(repo);
+       return git_configset_get_maybe_bool(repo->config, key, dest);
+}
+
+int repo_config_get_pathname(struct repository *repo,
+                            const char *key, const char **dest)
+{
+       int ret;
+       git_config_check_init(repo);
+       ret = git_configset_get_pathname(repo->config, key, dest);
+       if (ret < 0)
+               git_die_config(key, NULL);
+       return ret;
+}
+
+/* Functions used historically to read configuration from 'the_repository' */
+void git_config(config_fn_t fn, void *data)
+{
+       repo_config(the_repository, fn, data);
+}
+
+void git_config_clear(void)
+{
+       repo_config_clear(the_repository);
+}
+
+int git_config_get_value(const char *key, const char **value)
+{
+       return repo_config_get_value(the_repository, key, value);
+}
+
+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)
+{
+       return repo_config_get_string_const(the_repository, key, dest);
+}
+
 int git_config_get_string(const char *key, char **dest)
 {
-       git_config_check_init();
-       return git_config_get_string_const(key, (const char **)dest);
+       return repo_config_get_string(the_repository, key, dest);
 }
 
 int git_config_get_int(const char *key, int *dest)
 {
-       git_config_check_init();
-       return git_configset_get_int(&the_config_set, key, dest);
+       return repo_config_get_int(the_repository, key, dest);
 }
 
 int git_config_get_ulong(const char *key, unsigned long *dest)
 {
-       git_config_check_init();
-       return git_configset_get_ulong(&the_config_set, key, dest);
+       return repo_config_get_ulong(the_repository, key, dest);
 }
 
 int git_config_get_bool(const char *key, int *dest)
 {
-       git_config_check_init();
-       return git_configset_get_bool(&the_config_set, key, dest);
+       return repo_config_get_bool(the_repository, key, dest);
 }
 
 int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest)
 {
-       git_config_check_init();
-       return git_configset_get_bool_or_int(&the_config_set, key, is_bool, dest);
+       return repo_config_get_bool_or_int(the_repository, key, is_bool, dest);
 }
 
 int git_config_get_maybe_bool(const char *key, int *dest)
 {
-       git_config_check_init();
-       return git_configset_get_maybe_bool(&the_config_set, key, dest);
+       return repo_config_get_maybe_bool(the_repository, key, dest);
 }
 
 int git_config_get_pathname(const char *key, const char **dest)
 {
-       int ret;
-       git_config_check_init();
-       ret = git_configset_get_pathname(&the_config_set, key, dest);
-       if (ret < 0)
-               git_die_config(key, NULL);
-       return ret;
+       return repo_config_get_pathname(the_repository, key, dest);
 }
 
 int git_config_get_expiry(const char *key, const char **output)
@@ -1965,7 +2056,7 @@ int git_config_get_expiry(const char *key, const char **output)
        if (ret)
                return ret;
        if (strcmp(*output, "now")) {
-               unsigned long now = approxidate("now");
+               timestamp_t now = approxidate("now");
                if (approxidate(*output) >= now)
                        git_die_config(key, _("Invalid %s: '%s'"), key, *output);
        }
@@ -2640,6 +2731,9 @@ int git_config_rename_section_in_file(const char *config_filename,
        }
 
        if (!(config_file = fopen(config_filename, "rb"))) {
+               ret = warn_on_fopen_errors(config_filename);
+               if (ret)
+                       goto out;
                /* no config file means nothing to rename, no error */
                goto commit_and_out;
        }