Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2be39ce

Browse files
committed
config: introduce new read-only in-memory backend
Now that we have abstracted away how to store and retrieve config entries, it became trivial to implement a new in-memory backend by making use of this. And thus we do so. This commit implements a new read-only in-memory backend that can parse a chunk of memory into a `git_config_backend` structure.
1 parent b78f4ab commit 2be39ce

File tree

4 files changed

+372
-1
lines changed

4 files changed

+372
-1
lines changed

src/config_backend.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@
2525
*/
2626
extern int git_config_backend_from_file(git_config_backend **out, const char *path);
2727

28+
/**
29+
* Create an in-memory configuration file backend
30+
*
31+
* @param out the new backend
32+
* @param cfg the configuration that is to be parsed
33+
*/
34+
extern int git_config_backend_from_string(git_config_backend **out, const char *cfg);
35+
2836
GIT_INLINE(int) git_config_backend_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
2937
{
3038
return cfg->open(cfg, level, repo);

src/config_mem.c

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#include "config.h"
9+
10+
#include "config_backend.h"
11+
#include "config_parse.h"
12+
#include "config_entries.h"
13+
14+
typedef struct {
15+
git_config_backend parent;
16+
git_config_entries *entries;
17+
git_buf cfg;
18+
} config_memory_backend;
19+
20+
typedef struct {
21+
git_config_entries *entries;
22+
git_config_level_t level;
23+
} config_memory_parse_data;
24+
25+
static int config_error_readonly(void)
26+
{
27+
giterr_set(GITERR_CONFIG, "this backend is read-only");
28+
return -1;
29+
}
30+
31+
static int read_variable_cb(
32+
git_config_parser *reader,
33+
const char *current_section,
34+
const char *var_name,
35+
const char *var_value,
36+
const char *line,
37+
size_t line_len,
38+
void *payload)
39+
{
40+
config_memory_parse_data *parse_data = (config_memory_parse_data *) payload;
41+
git_buf buf = GIT_BUF_INIT;
42+
git_config_entry *entry;
43+
const char *c;
44+
int result;
45+
46+
GIT_UNUSED(reader);
47+
GIT_UNUSED(line);
48+
GIT_UNUSED(line_len);
49+
50+
if (current_section) {
51+
/* TODO: Once warnings land, we should likely warn
52+
* here. Git appears to warn in most cases if it sees
53+
* un-namespaced config options.
54+
*/
55+
git_buf_puts(&buf, current_section);
56+
git_buf_putc(&buf, '.');
57+
}
58+
59+
for (c = var_name; *c; c++)
60+
git_buf_putc(&buf, git__tolower(*c));
61+
62+
if (git_buf_oom(&buf))
63+
return -1;
64+
65+
entry = git__calloc(1, sizeof(git_config_entry));
66+
GITERR_CHECK_ALLOC(entry);
67+
entry->name = git_buf_detach(&buf);
68+
entry->value = var_value ? git__strdup(var_value) : NULL;
69+
entry->level = parse_data->level;
70+
entry->include_depth = 0;
71+
72+
if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
73+
return result;
74+
75+
return result;
76+
}
77+
78+
static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo)
79+
{
80+
config_memory_backend *memory_backend = (config_memory_backend *) backend;
81+
config_memory_parse_data parse_data;
82+
git_config_parser reader;
83+
84+
GIT_UNUSED(repo);
85+
86+
if (memory_backend->cfg.size == 0)
87+
return 0;
88+
89+
git_parse_ctx_init(&reader.ctx, memory_backend->cfg.ptr, memory_backend->cfg.size);
90+
reader.file = NULL;
91+
parse_data.entries = memory_backend->entries;
92+
parse_data.level = level;
93+
94+
return git_config_parse(&reader, NULL, read_variable_cb, NULL, NULL, &parse_data);
95+
}
96+
97+
static int config_memory_get(git_config_backend *backend, const char *key, git_config_entry **out)
98+
{
99+
config_memory_backend *memory_backend = (config_memory_backend *) backend;
100+
return git_config_entries_get(out, memory_backend->entries, key);
101+
}
102+
103+
static int config_memory_iterator(
104+
git_config_iterator **iter,
105+
git_config_backend *backend)
106+
{
107+
config_memory_backend *memory_backend = (config_memory_backend *) backend;
108+
git_config_entries *entries;
109+
int error;
110+
111+
if ((error = git_config_entries_dup(&entries, memory_backend->entries)) < 0)
112+
goto out;
113+
114+
if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
115+
goto out;
116+
117+
out:
118+
/* Let iterator delete duplicated entries when it's done */
119+
git_config_entries_free(entries);
120+
return error;
121+
}
122+
123+
static int config_memory_set(git_config_backend *backend, const char *name, const char *value)
124+
{
125+
GIT_UNUSED(backend);
126+
GIT_UNUSED(name);
127+
GIT_UNUSED(value);
128+
return config_error_readonly();
129+
}
130+
131+
static int config_memory_set_multivar(
132+
git_config_backend *backend, const char *name, const char *regexp, const char *value)
133+
{
134+
GIT_UNUSED(backend);
135+
GIT_UNUSED(name);
136+
GIT_UNUSED(regexp);
137+
GIT_UNUSED(value);
138+
return config_error_readonly();
139+
}
140+
141+
static int config_memory_delete(git_config_backend *backend, const char *name)
142+
{
143+
GIT_UNUSED(backend);
144+
GIT_UNUSED(name);
145+
return config_error_readonly();
146+
}
147+
148+
static int config_memory_delete_multivar(git_config_backend *backend, const char *name, const char *regexp)
149+
{
150+
GIT_UNUSED(backend);
151+
GIT_UNUSED(name);
152+
GIT_UNUSED(regexp);
153+
return config_error_readonly();
154+
}
155+
156+
static int config_memory_lock(git_config_backend *backend)
157+
{
158+
GIT_UNUSED(backend);
159+
return config_error_readonly();
160+
}
161+
162+
static int config_memory_unlock(git_config_backend *backend, int success)
163+
{
164+
GIT_UNUSED(backend);
165+
GIT_UNUSED(success);
166+
return config_error_readonly();
167+
}
168+
169+
static int config_memory_snapshot(git_config_backend **out, git_config_backend *backend)
170+
{
171+
GIT_UNUSED(out);
172+
GIT_UNUSED(backend);
173+
giterr_set(GITERR_CONFIG, "this backend does not support snapshots");
174+
return -1;
175+
}
176+
177+
static void config_memory_free(git_config_backend *_backend)
178+
{
179+
config_memory_backend *backend = (config_memory_backend *)_backend;
180+
181+
if (backend == NULL)
182+
return;
183+
184+
git_config_entries_free(backend->entries);
185+
git_buf_dispose(&backend->cfg);
186+
git__free(backend);
187+
}
188+
189+
int git_config_backend_from_string(git_config_backend **out, const char *cfg)
190+
{
191+
config_memory_backend *backend;
192+
193+
backend = git__calloc(1, sizeof(config_memory_backend));
194+
GITERR_CHECK_ALLOC(backend);
195+
196+
if (git_config_entries_new(&backend->entries) < 0) {
197+
git__free(backend);
198+
return -1;
199+
}
200+
201+
if (git_buf_sets(&backend->cfg, cfg) < 0) {
202+
git_config_entries_free(backend->entries);
203+
git__free(backend);
204+
return -1;
205+
}
206+
207+
backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
208+
backend->parent.readonly = 1;
209+
backend->parent.open = config_memory_open;
210+
backend->parent.get = config_memory_get;
211+
backend->parent.set = config_memory_set;
212+
backend->parent.set_multivar = config_memory_set_multivar;
213+
backend->parent.del = config_memory_delete;
214+
backend->parent.del_multivar = config_memory_delete_multivar;
215+
backend->parent.iterator = config_memory_iterator;
216+
backend->parent.lock = config_memory_lock;
217+
backend->parent.unlock = config_memory_unlock;
218+
backend->parent.snapshot = config_memory_snapshot;
219+
backend->parent.free = config_memory_free;
220+
221+
*out = (git_config_backend *)backend;
222+
223+
return 0;
224+
}

src/config_parse.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ const char *git_config_escaped = "\n\t\b\"\\";
1616

1717
static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
1818
{
19+
const char *file = reader->file ? reader->file->path : "in-memory";
1920
giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
20-
error_str, reader->file->path, reader->ctx.line_num, col);
21+
error_str, file, reader->ctx.line_num, col);
2122
}
2223

2324

tests/config/memory.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#include "clar_libgit2.h"
2+
3+
#include "config_backend.h"
4+
5+
static git_config_backend *backend;
6+
7+
void test_config_memory__initialize(void)
8+
{
9+
backend = NULL;
10+
}
11+
12+
void test_config_memory__cleanup(void)
13+
{
14+
git_config_backend_free(backend);
15+
}
16+
17+
static void assert_config_contains(git_config_backend *backend,
18+
const char *name, const char *value)
19+
{
20+
git_config_entry *entry;
21+
cl_git_pass(git_config_backend_get_string(&entry, backend, name));
22+
cl_assert_equal_s(entry->value, value);
23+
}
24+
25+
struct expected_entry {
26+
const char *name;
27+
const char *value;
28+
int seen;
29+
};
30+
31+
static int contains_all_cb(const git_config_entry *entry, void *payload)
32+
{
33+
struct expected_entry *entries = (struct expected_entry *) payload;
34+
int i;
35+
36+
for (i = 0; entries[i].name; i++) {
37+
if (strcmp(entries[i].name, entry->name) ||
38+
strcmp(entries[i].value , entry->value))
39+
continue;
40+
41+
if (entries[i].seen)
42+
cl_fail("Entry seen more than once");
43+
entries[i].seen = 1;
44+
return 0;
45+
}
46+
47+
cl_fail("Unexpected entry");
48+
return -1;
49+
}
50+
51+
static void assert_config_contains_all(git_config_backend *backend,
52+
struct expected_entry *entries)
53+
{
54+
int i;
55+
56+
cl_git_pass(git_config_backend_foreach(backend, contains_all_cb, entries));
57+
58+
for (i = 0; entries[i].name; i++)
59+
cl_assert(entries[i].seen);
60+
}
61+
62+
static void setup_backend(const char *cfg)
63+
{
64+
cl_git_pass(git_config_backend_from_string(&backend, cfg));
65+
cl_git_pass(git_config_backend_open(backend, 0, NULL));
66+
}
67+
68+
void test_config_memory__write_operations_fail(void)
69+
{
70+
setup_backend("");
71+
cl_git_fail(git_config_backend_set_string(backend, "general.foo", "var"));
72+
cl_git_fail(git_config_backend_delete(backend, "general.foo"));
73+
cl_git_fail(git_config_backend_lock(backend));
74+
cl_git_fail(git_config_backend_unlock(backend, 0));
75+
}
76+
77+
void test_config_memory__simple(void)
78+
{
79+
setup_backend(
80+
"[general]\n"
81+
"foo=bar\n");
82+
83+
assert_config_contains(backend, "general.foo", "bar");
84+
}
85+
86+
void test_config_memory__malformed_fails_to_open(void)
87+
{
88+
cl_git_pass(git_config_backend_from_string(&backend,
89+
"[general\n"
90+
"foo=bar\n"));
91+
cl_git_fail(git_config_backend_open(backend, 0, NULL));
92+
}
93+
94+
void test_config_memory__multiple_vars(void)
95+
{
96+
setup_backend(
97+
"[general]\n"
98+
"foo=bar\n"
99+
"key=value\n");
100+
assert_config_contains(backend, "general.foo", "bar");
101+
assert_config_contains(backend, "general.key", "value");
102+
}
103+
104+
void test_config_memory__multiple_sections(void)
105+
{
106+
setup_backend(
107+
"[general]\n"
108+
"foo=bar\n"
109+
"\n"
110+
"[other]\n"
111+
"key=value\n");
112+
assert_config_contains(backend, "general.foo", "bar");
113+
assert_config_contains(backend, "other.key", "value");
114+
}
115+
116+
void test_config_memory__multivar_gets_correct_string(void)
117+
{
118+
setup_backend(
119+
"[general]\n"
120+
"foo=bar1\n"
121+
"foo=bar2\n");
122+
assert_config_contains(backend, "general.foo", "bar2");
123+
}
124+
125+
void test_config_memory__foreach_sees_multivar(void)
126+
{
127+
struct expected_entry entries[] = {
128+
{ "general.foo", "bar1", 0 },
129+
{ "general.foo", "bar2", 0 },
130+
{ NULL, NULL, 0 },
131+
};
132+
133+
setup_backend(
134+
"[general]\n"
135+
"foo=bar1\n"
136+
"foo=bar2\n");
137+
assert_config_contains_all(backend, entries);
138+
}

0 commit comments

Comments
 (0)