/* * Copyright (C) 2009-2011 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. */ #include "common.h" #include "fileops.h" #include "hashtable.h" #include "config.h" #include "git2/config.h" #include "vector.h" #if GIT_WIN32 # include #endif #include typedef struct { git_config_file *file; int priority; } file_internal; static void config_free(git_config *cfg) { unsigned int i; git_config_file *file; file_internal *internal; for(i = 0; i < cfg->files.length; ++i){ internal = git_vector_get(&cfg->files, i); file = internal->file; file->free(file); git__free(internal); } git_vector_free(&cfg->files); 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) { const file_internal *bk_a = (const file_internal *)(a); const file_internal *bk_b = (const file_internal *)(b); return bk_b->priority - bk_a->priority; } int git_config_new(git_config **out) { git_config *cfg; cfg = git__malloc(sizeof(git_config)); if (cfg == NULL) return GIT_ENOMEM; memset(cfg, 0x0, sizeof(git_config)); if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) { git__free(cfg); return GIT_ENOMEM; } *out = cfg; GIT_REFCOUNT_INC(cfg); return GIT_SUCCESS; } 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; error = git_config_add_file(cfg, file, priority); if (error < GIT_SUCCESS) { /* * 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 GIT_SUCCESS; } int git_config_open_ondisk(git_config **cfg, const char *path) { int error; error = git_config_new(cfg); if (error < GIT_SUCCESS) return error; error = git_config_add_file_ondisk(*cfg, path, 1); if (error < GIT_SUCCESS) git_config_free(*cfg); return error; } int git_config_add_file(git_config *cfg, git_config_file *file, int priority) { file_internal *internal; int error; assert(cfg && file); if ((error = file->open(file)) < GIT_SUCCESS) return git__rethrow(error, "Failed to open config file"); internal = git__malloc(sizeof(file_internal)); if (internal == NULL) return GIT_ENOMEM; internal->file = file; internal->priority = priority; if (git_vector_insert(&cfg->files, internal) < 0) { git__free(internal); return GIT_ENOMEM; } git_vector_sort(&cfg->files); internal->file->cfg = cfg; return GIT_SUCCESS; } /* * Loop over all the variables */ int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) { int ret = GIT_SUCCESS; unsigned int i; file_internal *internal; git_config_file *file; for(i = 0; i < cfg->files.length && ret == 0; ++i) { internal = git_vector_get(&cfg->files, i); file = internal->file; ret = file->foreach(file, fn, data); } return ret; } int git_config_delete(git_config *cfg, const char *name) { return git_config_set_string(cfg, name, NULL); } /************** * Setters **************/ int git_config_set_int64(git_config *cfg, const char *name, int64_t value) { char str_value[32]; /* All numbers should fit in here */ p_snprintf(str_value, sizeof(str_value), "%" PRId64, value); return git_config_set_string(cfg, name, str_value); } int git_config_set_int32(git_config *cfg, const char *name, int32_t value) { return git_config_set_int64(cfg, name, (int64_t)value); } int git_config_set_bool(git_config *cfg, const char *name, int value) { return git_config_set_string(cfg, name, value ? "true" : "false"); } int git_config_set_string(git_config *cfg, const char *name, const char *value) { 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"); internal = git_vector_get(&cfg->files, 0); file = internal->file; return file->set(file, name, value); } /*********** * Getters ***********/ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out) { const char *value, *num_end; int ret; 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); switch (*num_end) { case 'g': case 'G': num *= 1024; /* fallthrough */ case 'm': case 'M': num *= 1024; /* fallthrough */ case 'k': case 'K': num *= 1024; /* 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); /* fallthrough */ case '\0': *out = num; return GIT_SUCCESS; default: return git__throw(GIT_EINVALIDTYPE, "Failed to get value for '%s'. Value is of invalid type", name); } } int git_config_get_int32(git_config *cfg, const char *name, int32_t *out) { int64_t tmp_long; int32_t tmp_int; int ret; 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); *out = tmp_int; return ret; } int git_config_get_bool(git_config *cfg, const char *name, int *out) { const char *value; int error = GIT_SUCCESS; error = git_config_get_string(cfg, name, &value); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to get value for %s", name); /* A missing value means true */ if (value == NULL) { *out = 1; return GIT_SUCCESS; } 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; } /* Try to parse it as an integer */ error = git_config_get_int32(cfg, name, out); if (error == GIT_SUCCESS) *out = !!(*out); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to get value for %s", name); return error; } 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; 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"); 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; } return git__throw(error, "Config value '%s' not found", name); } int git_config_find_global(char *global_config_path) { const char *home; home = getenv("HOME"); #ifdef GIT_WIN32 if (home == NULL) home = getenv("USERPROFILE"); #endif if (home == NULL) return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory"); git_path_join(global_config_path, home, GIT_CONFIG_FILENAME); 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"); return GIT_SUCCESS; } #if GIT_WIN32 static int win32_find_system(char *system_config_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; ret = ExpandEnvironmentStringsW(query, apphome_utf16, size); if (ret != size) return git__throw(GIT_ERROR, "Failed to expand environment strings"); if (_waccess(apphome_utf16, F_OK) < 0) { git__free(apphome_utf16); return GIT_ENOTFOUND; } apphome_utf8 = gitwin_from_utf16(apphome_utf16); git__free(apphome_utf16); if (strlen(apphome_utf8) >= GIT_PATH_MAX) { git__free(apphome_utf8); return git__throw(GIT_ESHORTBUFFER, "Path is too long"); } strcpy(system_config_path, apphome_utf8); git__free(apphome_utf8); return GIT_SUCCESS; } #endif int git_config_find_system(char *system_config_path) { const char *etc = "/etc/gitconfig"; if (git_futils_exists(etc) == GIT_SUCCESS) { memcpy(system_config_path, etc, strlen(etc) + 1); return GIT_SUCCESS; } #if GIT_WIN32 return win32_find_system(system_config_path); #else return GIT_ENOTFOUND; #endif } 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) return error; return git_config_open_ondisk(out, global_path); }