Commit d8d25acb by Carlos Martín Nieto

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.
parent d209cc47
......@@ -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);
......
#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);
}
[include]
path = config-included
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment