diff options
author | Vicent Martà <vicent@github.com> | 2013-03-19 03:17:14 +0400 |
---|---|---|
committer | Vicent Martà <vicent@github.com> | 2013-03-19 03:17:14 +0400 |
commit | 5b27bf7e5bfd5c2f92a15c0058c801d49faf8403 (patch) | |
tree | eb4e79c950ec3260ad8a532b4d84dae2ad60ae5d /src/fileops.c | |
parent | 50eb8520d06ac0c484a0ce3a3fba6c5da25ec976 (diff) | |
parent | 324602514fec5ba04fa236c67d633f9b18ad9845 (diff) |
Merge pull request #1417 from arrbee/opts-for-paths
Implement opts interface for global/system file search paths
Diffstat (limited to 'src/fileops.c')
-rw-r--r-- | src/fileops.c | 209 |
1 files changed, 160 insertions, 49 deletions
diff --git a/src/fileops.c b/src/fileops.c index e33377eac..d6244711f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -558,75 +558,186 @@ clean_up: return error; } -int git_futils_find_system_file(git_buf *path, const char *filename) + +static int git_futils_guess_system_dirs(git_buf *out) { #ifdef GIT_WIN32 - // try to find git.exe/git.cmd on path - if (!win32_find_system_file_using_path(path, filename)) - return 0; + return git_win32__find_system_dirs(out); +#else + return git_buf_sets(out, "/etc"); +#endif +} - // try to find msysgit installation path using registry - if (!win32_find_system_file_using_registry(path, filename)) - return 0; +static int git_futils_guess_global_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_global_dirs(out); #else - if (git_buf_joinpath(path, "/etc", filename) < 0) - return -1; + return git_buf_sets(out, getenv("HOME")); +#endif +} - if (git_path_exists(path->ptr) == true) - return 0; +static int git_futils_guess_xdg_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_xdg_dirs(out); +#else + const char *env = NULL; + + if ((env = getenv("XDG_CONFIG_HOME")) != NULL) + return git_buf_joinpath(out, env, "git"); + else if ((env = getenv("HOME")) != NULL) + return git_buf_joinpath(out, env, ".config/git"); + + git_buf_clear(out); + return 0; #endif +} - git_buf_clear(path); - giterr_set(GITERR_OS, "The system file '%s' doesn't exist", filename); - return GIT_ENOTFOUND; +typedef int (*git_futils_dirs_guess_cb)(git_buf *out); + +static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] = + { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; + +static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = { + git_futils_guess_system_dirs, + git_futils_guess_global_dirs, + git_futils_guess_xdg_dirs, +}; + +static int git_futils_check_selector(git_futils_dir_t which) +{ + if (which < GIT_FUTILS_DIR__MAX) + return 0; + giterr_set(GITERR_INVALID, "config directory selector out of range"); + return -1; } -int git_futils_find_global_file(git_buf *path, const char *filename) +int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which) { -#ifdef GIT_WIN32 - struct win32_path root; - static const wchar_t *tmpls[4] = { - L"%HOME%\\", - L"%HOMEDRIVE%%HOMEPATH%\\", - L"%USERPROFILE%\\", - NULL, - }; - const wchar_t **tmpl; - - for (tmpl = tmpls; *tmpl != NULL; tmpl++) { - /* try to expand environment variable, skipping if not set */ - if (win32_expand_path(&root, *tmpl) != 0 || root.path[0] == L'%') + assert(out); + + *out = NULL; + + GITERR_CHECK_ERROR(git_futils_check_selector(which)); + + if (!git_buf_len(&git_futils__dirs[which])) + GITERR_CHECK_ERROR( + git_futils__dir_guess[which](&git_futils__dirs[which])); + + *out = &git_futils__dirs[which]; + return 0; +} + +int git_futils_dirs_get_str(char *out, size_t outlen, git_futils_dir_t which) +{ + const git_buf *path = NULL; + + GITERR_CHECK_ERROR(git_futils_check_selector(which)); + GITERR_CHECK_ERROR(git_futils_dirs_get(&path, which)); + + if (!out || path->size >= outlen) { + giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path"); + return GIT_EBUFS; + } + + git_buf_copy_cstr(out, outlen, path); + return 0; +} + +#define PATH_MAGIC "$PATH" + +int git_futils_dirs_set(git_futils_dir_t which, const char *search_path) +{ + const char *expand_path = NULL; + git_buf merge = GIT_BUF_INIT; + + GITERR_CHECK_ERROR(git_futils_check_selector(which)); + + if (search_path != NULL) + expand_path = strstr(search_path, PATH_MAGIC); + + /* init with default if not yet done and needed (ignoring error) */ + if ((!search_path || expand_path) && + !git_buf_len(&git_futils__dirs[which])) + git_futils__dir_guess[which](&git_futils__dirs[which]); + + /* if $PATH is not referenced, then just set the path */ + if (!expand_path) + return git_buf_sets(&git_futils__dirs[which], search_path); + + /* otherwise set to join(before $PATH, old value, after $PATH) */ + if (expand_path > search_path) + git_buf_set(&merge, search_path, expand_path - search_path); + + if (git_buf_len(&git_futils__dirs[which])) + git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, + merge.ptr, git_futils__dirs[which].ptr); + + expand_path += strlen(PATH_MAGIC); + if (*expand_path) + git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path); + + git_buf_swap(&git_futils__dirs[which], &merge); + git_buf_free(&merge); + + return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0; +} + +void git_futils_dirs_free(void) +{ + int i; + for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i) + git_buf_free(&git_futils__dirs[i]); +} + +static int git_futils_find_in_dirlist( + git_buf *path, const char *name, git_futils_dir_t which, const char *label) +{ + size_t len; + const char *scan, *next = NULL; + const git_buf *syspath; + + GITERR_CHECK_ERROR(git_futils_dirs_get(&syspath, which)); + + for (scan = git_buf_cstr(syspath); scan; scan = next) { + for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR); + next && next > scan && next[-1] == '\\'; + next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR)) + /* find unescaped separator or end of string */; + + len = next ? (size_t)(next++ - scan) : strlen(scan); + if (!len) continue; - /* try to look up file under path */ - if (!win32_find_file(path, &root, filename)) + GITERR_CHECK_ERROR(git_buf_set(path, scan, len)); + GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name)); + + if (git_path_exists(path->ptr)) return 0; } - giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename); git_buf_clear(path); - + giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name); return GIT_ENOTFOUND; -#else - const char *home = getenv("HOME"); - - if (home == NULL) { - giterr_set(GITERR_OS, "Global file lookup failed. " - "Cannot locate the user's home directory"); - return GIT_ENOTFOUND; - } +} - if (git_buf_joinpath(path, home, filename) < 0) - return -1; +int git_futils_find_system_file(git_buf *path, const char *filename) +{ + return git_futils_find_in_dirlist( + path, filename, GIT_FUTILS_DIR_SYSTEM, "system"); +} - if (git_path_exists(path->ptr) == false) { - giterr_set(GITERR_OS, "The global file '%s' doesn't exist", filename); - git_buf_clear(path); - return GIT_ENOTFOUND; - } +int git_futils_find_global_file(git_buf *path, const char *filename) +{ + return git_futils_find_in_dirlist( + path, filename, GIT_FUTILS_DIR_GLOBAL, "global"); +} - return 0; -#endif +int git_futils_find_xdg_file(git_buf *path, const char *filename) +{ + return git_futils_find_in_dirlist( + path, filename, GIT_FUTILS_DIR_XDG, "global/xdg"); } int git_futils_fake_symlink(const char *old, const char *new) |