Merge pull request #2819 from libgit2/cmn/config-get-path config: add parsing and getter for paths
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e1c02f9..bffcb25 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,10 @@ v0.22 + 1
### API additions
+* Parsing and retrieving a configuration value as a path is exposed
+ via `git_config_parse_path()` and `git_config_get_path()`
+ respectively.
+
### API removals
### Breaking API changes
diff --git a/include/git2/config.h b/include/git2/config.h
index e32c614..1ed8d24 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -320,6 +320,24 @@ GIT_EXTERN(int) git_config_get_int64(int64_t *out, const git_config *cfg, const
GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char *name);
/**
+ * Get the value of a path config variable.
+ *
+ * A leading '~' will be expanded to the global search path (which
+ * defaults to the user's home directory but can be overridden via
+ * `git_libgit2_opts()`.
+ *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
+ * @param out the buffer in which to store the result
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_path(git_buf *out, const git_config *cfg, const char *name);
+
+/**
* Get the value of a string config variable.
*
* The string is owned by the variable and should not be freed by the
@@ -615,6 +633,20 @@ GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value);
*/
GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
+/**
+ * Parse a string value as a path.
+ *
+ * A leading '~' will be expanded to the global search path (which
+ * defaults to the user's home directory but can be overridden via
+ * `git_libgit2_opts()`.
+ *
+ * If the value does not begin with a tilde, the input will be
+ * returned.
+ *
+ * @param out placae to store the result of parsing
+ * @param value the path to evaluate
+ */
+GIT_EXTERN(int) git_config_parse_path(git_buf *out, const char *value);
/**
* Perform an operation on each config variable in given config backend
diff --git a/src/config.c b/src/config.c
index 0f8c244..f807701 100644
--- a/src/config.c
+++ b/src/config.c
@@ -785,6 +785,17 @@ int git_config_get_bool(int *out, const git_config *cfg, const char *name)
return git_config_parse_bool(out, entry->value);
}
+int git_config_get_path(git_buf *out, const git_config *cfg, const char *name)
+{
+ const git_config_entry *entry;
+ int error;
+
+ if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ return error;
+
+ return git_config_parse_path(out, entry->value);
+}
+
int git_config_get_string(
const char **out, const git_config *cfg, const char *name)
{
@@ -1184,6 +1195,36 @@ fail_parse:
return -1;
}
+int git_config_parse_path(git_buf *out, const char *value)
+{
+ int error = 0;
+ const git_buf *home;
+
+ assert(out && value);
+
+ git_buf_sanitize(out);
+
+ if (value[0] == '~') {
+ if (value[1] != '\0' && value[1] != '/') {
+ giterr_set(GITERR_CONFIG, "retrieving a homedir by name is not supported");
+ return -1;
+ }
+
+ if ((error = git_sysdir_get(&home, GIT_SYSDIR_GLOBAL)) < 0)
+ return error;
+
+ git_buf_sets(out, home->ptr);
+ git_buf_puts(out, value + 1);
+
+ if (git_buf_oom(out))
+ return -1;
+
+ return 0;
+ }
+
+ return git_buf_sets(out, value);
+}
+
/* Take something the user gave us and make it nice for our hash function */
int git_config__normalize_name(const char *in, char **out)
{
diff --git a/tests/config/read.c b/tests/config/read.c
index 2567272..1799970 100644
--- a/tests/config/read.c
+++ b/tests/config/read.c
@@ -1,4 +1,6 @@
#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
void test_config_read__simple_read(void)
{
@@ -567,3 +569,47 @@ void test_config_read__override_variable(void)
git_config_free(cfg);
}
+
+void test_config_read__path(void)
+{
+ git_config *cfg;
+ git_buf path = GIT_BUF_INIT;
+ git_buf old_path = GIT_BUF_INIT;
+ git_buf home_path = GIT_BUF_INIT;
+ git_buf expected_path = GIT_BUF_INIT;
+
+ cl_git_pass(p_mkdir("fakehome", 0777));
+ cl_git_pass(git_path_prettify(&home_path, "fakehome", NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &old_path));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, home_path.ptr));
+ cl_git_mkfile("./testconfig", "[some]\n path = ~/somefile");
+ cl_git_pass(git_path_join_unrooted(&expected_path, "somefile", home_path.ptr, NULL));
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
+ cl_assert_equal_s(expected_path.ptr, path.ptr);
+ git_buf_free(&path);
+
+ cl_git_mkfile("./testconfig", "[some]\n path = ~/");
+ cl_git_pass(git_path_join_unrooted(&expected_path, "", home_path.ptr, NULL));
+
+ cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
+ cl_assert_equal_s(expected_path.ptr, path.ptr);
+ git_buf_free(&path);
+
+ cl_git_mkfile("./testconfig", "[some]\n path = ~");
+ cl_git_pass(git_buf_sets(&expected_path, home_path.ptr));
+
+ cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
+ cl_assert_equal_s(expected_path.ptr, path.ptr);
+ git_buf_free(&path);
+
+ cl_git_mkfile("./testconfig", "[some]\n path = ~user/foo");
+ cl_git_fail(git_config_get_path(&path, cfg, "some.path"));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, old_path.ptr));
+ git_buf_free(&old_path);
+ git_buf_free(&home_path);
+ git_buf_free(&expected_path);
+ git_config_free(cfg);
+}