/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
int priority;
} file_internal;
-void git_config_free(git_config *cfg)
+static void config_free(git_config *cfg)
{
unsigned int i;
git_config_file *file;
file_internal *internal;
- if (cfg == NULL)
- return;
-
for(i = 0; i < cfg->files.length; ++i){
internal = git_vector_get(&cfg->files, i);
file = internal->file;
file->free(file);
- free(internal);
+ git__free(internal);
}
git_vector_free(&cfg->files);
- free(cfg);
+ git__free(cfg);
+}
+
+void git_config_free(git_config *cfg)
+{
+ if (cfg == NULL)
+ return;
+
+ GIT_REFCOUNT_DEC(cfg, config_free);
}
static int config_backend_cmp(const void *a, const void *b)
memset(cfg, 0x0, sizeof(git_config));
if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
- free(cfg);
- return GIT_ENOMEM;
+ git__free(cfg);
+ return -1;
}
*out = cfg;
-
- return GIT_SUCCESS;
+ GIT_REFCOUNT_INC(cfg);
+ return 0;
}
int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
{
git_config_file *file = NULL;
- int error;
- error = git_config_file__ondisk(&file, path);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_config_file__ondisk(&file, path) < 0)
+ return -1;
- error = git_config_add_file(cfg, file, priority);
- if (error < GIT_SUCCESS) {
+ if (git_config_add_file(cfg, file, priority) < 0) {
/*
* free manually; the file is not owned by the config
* instance yet and will not be freed on cleanup
*/
file->free(file);
- return error;
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
int git_config_open_ondisk(git_config **cfg, const char *path)
{
- int error;
-
- error = git_config_new(cfg);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_config_new(cfg) < 0)
+ return -1;
- error = git_config_add_file_ondisk(*cfg, path, 1);
- if (error < GIT_SUCCESS)
+ if (git_config_add_file_ondisk(*cfg, path, 1) < 0) {
git_config_free(*cfg);
+ return -1;
+ }
- return error;
+ return 0;
}
int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
{
file_internal *internal;
- int error;
+ int result;
assert(cfg && file);
- if ((error = file->open(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to open config file");
+ if ((result = file->open(file)) < 0)
+ return result;
internal = git__malloc(sizeof(file_internal));
- if (internal == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(internal);
internal->file = file;
internal->priority = priority;
if (git_vector_insert(&cfg->files, internal) < 0) {
- free(internal);
- return GIT_ENOMEM;
+ git__free(internal);
+ return -1;
}
git_vector_sort(&cfg->files);
internal->file->cfg = cfg;
- return GIT_SUCCESS;
+ return 0;
}
/*
int git_config_delete(git_config *cfg, const char *name)
{
- return git_config_set_string(cfg, name, NULL);
+ file_internal *internal;
+ git_config_file *file;
+
+ assert(cfg->files.length);
+
+ internal = git_vector_get(&cfg->files, 0);
+ file = internal->file;
+
+ return file->del(file, name);
}
/**************
file_internal *internal;
git_config_file *file;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance");
+ assert(cfg->files.length);
internal = git_vector_get(&cfg->files, 0);
file = internal->file;
return file->set(file, name, value);
}
-/***********
- * Getters
- ***********/
+static int parse_bool(int *out, const char *value)
+{
+ /* A missing value means true */
+ if (value == NULL) {
+ *out = 1;
+ return GIT_SUCCESS;
+ }
-int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
+ if (!strcasecmp(value, "true") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "on")) {
+ *out = 1;
+ return GIT_SUCCESS;
+ }
+ if (!strcasecmp(value, "false") ||
+ !strcasecmp(value, "no") ||
+ !strcasecmp(value, "off")) {
+ *out = 0;
+ return GIT_SUCCESS;
+ }
+
+ return GIT_EINVALIDTYPE;
+}
+
+static int parse_int64(int64_t *out, const char *value)
{
- const char *value, *num_end;
- int ret;
+ const char *num_end;
int64_t num;
- ret = git_config_get_string(cfg, name, &value);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
-
- ret = git__strtol64(&num, value, &num_end, 0);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
+ if (git__strtol64(&num, value, &num_end, 0) < 0)
+ return -1;
switch (*num_end) {
case 'g':
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Invalid type suffix", name);
+ return -1;
/* fallthrough */
case '\0':
*out = num;
- return GIT_SUCCESS;
+ return 0;
default:
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Value is of invalid type", name);
+ return -1;
}
}
-int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
+static int parse_int32(int32_t *out, const char *value)
{
- int64_t tmp_long;
- int32_t tmp_int;
- int ret;
+ int64_t tmp;
+ int32_t truncate;
- ret = git_config_get_int64(cfg, name, &tmp_long);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
-
- tmp_int = tmp_long & 0xFFFFFFFF;
- if (tmp_int != tmp_long)
- return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
+ if (parse_int64(&tmp, value) < 0)
+ return -1;
- *out = tmp_int;
+ truncate = tmp & 0xFFFFFFFF;
+ if (truncate != tmp)
+ return -1;
- return ret;
+ *out = truncate;
+ return 0;
}
-int git_config_get_bool(git_config *cfg, const char *name, int *out)
+/***********
+ * Getters
+ ***********/
+int git_config_get_mapped(git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n, int *out)
{
+ size_t i;
const char *value;
- int error = GIT_SUCCESS;
+ int error;
error = git_config_get_string(cfg, name, &value);
if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
+ return error;
- /* A missing value means true */
- if (value == NULL) {
- *out = 1;
- return GIT_SUCCESS;
+ for (i = 0; i < map_n; ++i) {
+ git_cvar_map *m = maps + i;
+
+ switch (m->cvar_type) {
+ case GIT_CVAR_FALSE:
+ case GIT_CVAR_TRUE: {
+ int bool_val;
+
+ if (parse_bool(&bool_val, value) == 0 &&
+ bool_val == (int)m->cvar_type) {
+ *out = m->map_value;
+ return 0;
+ }
+
+ break;
+ }
+
+ case GIT_CVAR_INT32:
+ if (parse_int32(out, value) == 0)
+ return 0;
+
+ break;
+
+ case GIT_CVAR_STRING:
+ if (strcasecmp(value, m->str_match) == 0) {
+ *out = m->map_value;
+ return 0;
+ }
+ }
}
- if (!strcasecmp(value, "true") ||
- !strcasecmp(value, "yes") ||
- !strcasecmp(value, "on")) {
- *out = 1;
- return GIT_SUCCESS;
+ giterr_set(GITERR_CONFIG,
+ "Failed to map the '%s' config variable with a valid value", name);
+ return -1;
+}
+
+int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
+{
+ const char *value;
+ int ret;
+
+ ret = git_config_get_string(cfg, name, &value);
+ if (ret < 0)
+ return ret;
+
+ if (parse_int64(out, value) < 0) {
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
+ return -1;
}
- if (!strcasecmp(value, "false") ||
- !strcasecmp(value, "no") ||
- !strcasecmp(value, "off")) {
- *out = 0;
- return GIT_SUCCESS;
+
+ return 0;
+}
+
+int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
+{
+ const char *value;
+ int ret;
+
+ ret = git_config_get_string(cfg, name, &value);
+ if (ret < 0)
+ return ret;
+
+ if (parse_int32(out, value) < 0) {
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
+ return -1;
}
+
+ return 0;
+}
+
+int git_config_get_bool(git_config *cfg, const char *name, int *out)
+{
+ const char *value;
+ int ret;
+
+ ret = git_config_get_string(cfg, name, &value);
+ if (ret < 0)
+ return ret;
- /* Try to parse it as an integer */
- error = git_config_get_int32(cfg, name, out);
- if (error == GIT_SUCCESS)
+ if (parse_bool(out, value) == 0)
+ return 0;
+
+ if (parse_int32(out, value) == 0) {
*out = !!(*out);
+ return 0;
+ }
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
- return error;
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
+ return -1;
}
int git_config_get_string(git_config *cfg, const char *name, const char **out)
{
file_internal *internal;
git_config_file *file;
- int error = GIT_ENOTFOUND;
+ int ret = GIT_ENOTFOUND;
unsigned int i;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
+ assert(cfg->files.length);
for (i = 0; i < cfg->files.length; ++i) {
internal = git_vector_get(&cfg->files, i);
file = internal->file;
- if ((error = file->get(file, name, out)) == GIT_SUCCESS)
- return GIT_SUCCESS;
+
+ ret = file->get(file, name, out);
+ if (ret == 0)
+ return 0;
+
+ /* File backend doesn't set error message on variable
+ * not found */
+ if (ret == GIT_ENOTFOUND)
+ continue;
+
+ return ret;
}
- return git__throw(error, "Config value '%s' not found", name);
+ giterr_set(GITERR_CONFIG, "Config variable '%s' not found", name);
+ return GIT_ENOTFOUND;
}
-int git_config_find_global(char *global_config_path)
+int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
+ int (*fn)(const char *value, void *data), void *data)
{
- const char *home;
+ file_internal *internal;
+ git_config_file *file;
+ int ret = GIT_ENOTFOUND;
+ unsigned int i;
- home = getenv("HOME");
+ assert(cfg->files.length);
-#ifdef GIT_WIN32
- if (home == NULL)
- home = getenv("USERPROFILE");
-#endif
+ /*
+ * This loop runs the "wrong" way 'round because we need to
+ * look at every value from the most general to most specific
+ */
+ for (i = cfg->files.length; i > 0; --i) {
+ internal = git_vector_get(&cfg->files, i - 1);
+ file = internal->file;
+ ret = file->get_multivar(file, name, regexp, fn, data);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
+ }
- if (home == NULL)
- return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory");
+ return 0;
+}
- git_path_join(global_config_path, home, GIT_CONFIG_FILENAME);
+int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
+{
+ file_internal *internal;
+ git_config_file *file;
+ int ret = GIT_ENOTFOUND;
+ unsigned int i;
- if (git_futils_exists(global_config_path) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist");
+ for (i = cfg->files.length; i > 0; --i) {
+ internal = git_vector_get(&cfg->files, i - 1);
+ file = internal->file;
+ ret = file->set_multivar(file, name, regexp, value);
+ if (ret < GIT_SUCCESS && ret != GIT_ENOTFOUND)
+ return ret;
+ }
- return GIT_SUCCESS;
+ return 0;
}
-
-
-#if GIT_WIN32
-static int win32_find_system(char *system_config_path)
+int git_config_find_global_r(git_buf *path)
{
- const wchar_t *query = L"%PROGRAMFILES%\\Git\\etc\\gitconfig";
- wchar_t *apphome_utf16;
- char *apphome_utf8;
- DWORD size, ret;
-
- size = ExpandEnvironmentStringsW(query, NULL, 0);
- /* The function gave us the full size of the buffer in chars, including NUL */
- apphome_utf16 = git__malloc(size * sizeof(wchar_t));
- if (apphome_utf16 == NULL)
- return GIT_ENOMEM;
+ return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
+}
- ret = ExpandEnvironmentStringsW(query, apphome_utf16, size);
- if (ret != size)
- return git__throw(GIT_ERROR, "Failed to expand environment strings");
+int git_config_find_global(char *global_config_path)
+{
+ git_buf path = GIT_BUF_INIT;
+ int ret = git_config_find_global_r(&path);
- if (_waccess(apphome_utf16, F_OK) < 0) {
- free(apphome_utf16);
- return GIT_ENOTFOUND;
+ if (ret < 0) {
+ git_buf_free(&path);
+ return ret;
}
- apphome_utf8 = conv_utf16_to_utf8(apphome_utf16);
- free(apphome_utf16);
-
- if (strlen(apphome_utf8) >= GIT_PATH_MAX) {
- free(apphome_utf8);
- return git__throw(GIT_ESHORTBUFFER, "Path is too long");
+ if (path.size > GIT_PATH_MAX) {
+ git_buf_free(&path);
+ giterr_set(GITERR_NOMEMORY,
+ "Path is to long to fit on the given buffer");
+ return -1;
}
- strcpy(system_config_path, apphome_utf8);
- free(apphome_utf8);
- return GIT_SUCCESS;
+ git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
+ git_buf_free(&path);
+ return 0;
+}
+
+int git_config_find_system_r(git_buf *path)
+{
+ return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
}
-#endif
int git_config_find_system(char *system_config_path)
{
- const char *etc = "/etc/gitconfig";
+ git_buf path = GIT_BUF_INIT;
+ int ret = git_config_find_system_r(&path);
- if (git_futils_exists(etc) == GIT_SUCCESS) {
- memcpy(system_config_path, etc, strlen(etc) + 1);
- return GIT_SUCCESS;
+ if (ret < 0) {
+ git_buf_free(&path);
+ return ret;
}
-#if GIT_WIN32
- return win32_find_system(system_config_path);
-#else
- return GIT_ENOTFOUND;
-#endif
+ if (path.size > GIT_PATH_MAX) {
+ git_buf_free(&path);
+ giterr_set(GITERR_NOMEMORY,
+ "Path is to long to fit on the given buffer");
+ return -1;
+ }
+
+ git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
+ git_buf_free(&path);
+ return 0;
}
int git_config_open_global(git_config **out)
int error;
char global_path[GIT_PATH_MAX];
- if ((error = git_config_find_global(global_path)) < GIT_SUCCESS)
+ if ((error = git_config_find_global(global_path)) < 0)
return error;
return git_config_open_ondisk(out, global_path);