summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2013-09-05 19:24:20 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2013-09-07 20:51:26 +0200
commitd8d25acb9a2e560a5fce66d4b1c9bdf4969ea2c4 (patch)
tree0239305b76721c18df7c8d5b131437e4c42dbc59
parentd209cc47515d2feeaa30de20882ded5cb4039736 (diff)
downloadlibgit2-d8d25acb9a2e560a5fce66d4b1c9bdf4969ea2c4.tar.gz
config: add support for include directives
Relative, absolute and home-relative paths are supported. The recursion limit it set at 10, just like in git.
-rw-r--r--src/config_file.c57
-rw-r--r--tests-clar/config/include.c50
-rw-r--r--tests-clar/resources/config/config-include2
-rw-r--r--tests-clar/resources/config/config-included2
4 files changed, 106 insertions, 5 deletions
diff --git a/src/config_file.c b/src/config_file.c
index fa8aba061..891bc29d3 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -34,6 +34,8 @@ typedef struct git_config_file_iter {
cvar_t* next_var;
} git_config_file_iter;
+/* Max depth for [include] directives */
+#define MAX_INCLUDE_DEPTH 10
#define CVAR_LIST_HEAD(list) ((list)->head)
@@ -74,6 +76,8 @@ typedef struct git_config_file_iter {
(iter) = (tmp))
struct reader {
+ time_t file_mtime;
+ size_t file_size;
char *file_path;
git_buf buffer;
char *read_ptr;
@@ -89,8 +93,6 @@ typedef struct {
struct reader reader;
char *file_path;
- time_t file_mtime;
- size_t file_size;
git_config_level_t level;
} diskfile_backend;
@@ -169,7 +171,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
git_buf_init(&b->reader.buffer, 0);
res = git_futils_readbuffer_updated(
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, NULL);
+ &b->reader.buffer, b->file_path, &b->reader.file_mtime, &b->reader.file_size, NULL);
/* It's fine if the file doesn't exist */
if (res == GIT_ENOTFOUND)
@@ -191,7 +193,7 @@ static int config_refresh(git_config_backend *cfg)
git_strmap *old_values;
res = git_futils_readbuffer_updated(
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, &updated);
+ &b->reader.buffer, b->file_path, &b->reader.file_mtime, &b->reader.file_size, &updated);
if (res < 0 || !updated)
return (res == GIT_ENOTFOUND) ? 0 : res;
@@ -899,6 +901,15 @@ static int strip_comments(char *line, int in_quotes)
return quote_count;
}
+static int included_path(git_buf *out, const char *dir, const char *path)
+{
+ /* From the user's home */
+ if (path[0] == '~' && path[1] == '/')
+ return git_futils_find_global_file(out, &path[1]);
+
+ return git_path_join_unrooted(out, path, dir, NULL);
+}
+
static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
{
int c;
@@ -910,6 +921,10 @@ static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_c
int result = 0;
khiter_t pos;
+ /* FIXME: should we return an error? */
+ if (depth >= MAX_INCLUDE_DEPTH)
+ return 0;
+
/* Initialize the reading position */
reader->read_ptr = reader->buffer.ptr;
reader->eof = 0;
@@ -979,6 +994,38 @@ static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_c
existing->next = var;
}
+ if (!git__strcmp(var->entry->name, "include.path")) {
+ struct reader r;
+ git_buf path = GIT_BUF_INIT;
+ char *dir;
+
+ memset(&r, 0, sizeof(r));
+ if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
+ break;
+
+ dir = git_buf_detach(&path);
+ result = included_path(&path, dir, var->entry->value);
+ git__free(dir);
+
+ if (result < 0)
+ break;
+
+ r.file_path = git_buf_detach(&path);
+ git_buf_init(&r.buffer, 0);
+ if ((result = git_futils_readbuffer_updated(&r.buffer, r.file_path, &r.file_mtime,
+ &r.file_size, NULL)) < 0) {
+ git__free(r.file_path);
+ break;
+ }
+
+ result = config_parse(cfg_file, &r, level, depth+1);
+ git__free(r.file_path);
+ git_buf_free(&r.buffer);
+
+ if (result < 0)
+ break;
+ }
+
break;
}
}
@@ -1199,7 +1246,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
git__free(current_section);
/* refresh stats - if this errors, then commit will error too */
- (void)git_filebuf_stats(&cfg->file_mtime, &cfg->file_size, &file);
+ (void)git_filebuf_stats(&cfg->reader.file_mtime, &cfg->reader.file_size, &file);
result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
git_buf_free(&cfg->reader.buffer);
diff --git a/tests-clar/config/include.c b/tests-clar/config/include.c
new file mode 100644
index 000000000..b88a7b2d1
--- /dev/null
+++ b/tests-clar/config/include.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "fileops.h"
+
+void test_config_include__relative(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include")));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ git_config_free(cfg);
+}
+
+void test_config_include__absolute(void)
+{
+ git_config *cfg;
+ const char *str;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_printf(&buf, "[include]\npath = %s/config-included", cl_fixture("config")));
+
+ cl_git_mkfile("config-include-absolute", git_buf_cstr(&buf));
+ git_buf_free(&buf);
+ cl_git_pass(git_config_open_ondisk(&cfg, "config-include-absolute"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ git_config_free(cfg);
+}
+
+void test_config_include__homedir(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
+ cl_git_mkfile("config-include-homedir", "[include]\npath = ~/config-included");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config-include-homedir"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ git_config_free(cfg);
+}
diff --git a/tests-clar/resources/config/config-include b/tests-clar/resources/config/config-include
new file mode 100644
index 000000000..6b5e79de7
--- /dev/null
+++ b/tests-clar/resources/config/config-include
@@ -0,0 +1,2 @@
+[include]
+ path = config-included
diff --git a/tests-clar/resources/config/config-included b/tests-clar/resources/config/config-included
new file mode 100644
index 000000000..089ca08a7
--- /dev/null
+++ b/tests-clar/resources/config/config-included
@@ -0,0 +1,2 @@
+[foo "bar"]
+ baz = huzzah