diff options
Diffstat (limited to 'src/win32/path_w32.c')
-rw-r--r-- | src/win32/path_w32.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c index 5d7ad11f6..b79f2f328 100644 --- a/src/win32/path_w32.c +++ b/src/win32/path_w32.c @@ -151,6 +151,142 @@ int git_win32_path_canonicalize(git_win32_path path) return (int)(to - path); } +static int git_win32_path_join_n( + git_win32_path dest, + wchar_t *one, + size_t one_len, + wchar_t *two, + size_t two_len) +{ + size_t backslash = 0; + + if (one_len && two_len && one[one_len - 1] != L'\\') + backslash = 1; + + if (one_len + two_len + backslash > MAX_PATH) { + git_error_set(GIT_ERROR_INVALID, "path too long"); + return -1; + } + + memmove(dest, one, one_len * sizeof(wchar_t)); + + if (backslash) + dest[one_len] = L'\\'; + + memcpy(dest + one_len + backslash, two, two_len * sizeof(wchar_t)); + dest[one_len + backslash + two_len] = L'\0'; + + return 0; +} + +static int git_win32_path_join(git_win32_path dest, wchar_t *one, wchar_t *two) +{ + return git_win32_path_join_n(dest, one, wcslen(one), two, wcslen(two)); +} + +struct win32_path_iter { + wchar_t *env; + const wchar_t *current_dir; +}; + +static int win32_path_iter_init(struct win32_path_iter *iter) +{ + DWORD len = GetEnvironmentVariableW(L"PATH", NULL, 0); + + if (len < 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + iter->env = NULL; + iter->current_dir = NULL; + return 0; + } else if (len <= 0) { + git_error_set(GIT_ERROR_OS, "could not load PATH"); + return -1; + } + + iter->env = git__malloc(len * sizeof(wchar_t)); + GIT_ERROR_CHECK_ALLOC(iter->env); + + len = GetEnvironmentVariableW(L"PATH", iter->env, len); + + if (len < 0) { + git_error_set(GIT_ERROR_OS, "could not load PATH"); + return -1; + } + + iter->current_dir = iter->env; + return 0; +} + +static int win32_path_iter_next( + const wchar_t **out, + size_t *out_len, + struct win32_path_iter *iter) +{ + const wchar_t *start; + wchar_t term; + size_t len = 0; + + if (!iter->current_dir || !*iter->current_dir) + return GIT_ITEROVER; + + term = (*iter->current_dir == L'"') ? *iter->current_dir++ : L';'; + start = iter->current_dir; + + while (*iter->current_dir && *iter->current_dir != term) { + *iter->current_dir++; + len++; + } + + *out = start; + *out_len = len; + + if (term == L'"' && *iter->current_dir) + *iter->current_dir++; + + while (*iter->current_dir == L';') + *iter->current_dir++; + + return 0; +} + +static void win32_path_iter_dispose(struct win32_path_iter *iter) +{ + if (!iter) + return; + + git__free(iter->env); + iter->env = NULL; + iter->current_dir = NULL; +} + +int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe) +{ + struct win32_path_iter path_iter; + wchar_t *dir; + size_t dir_len, exe_len = wcslen(exe); + bool found = false; + + if (win32_path_iter_init(&path_iter) < 0) + return -1; + + while (win32_path_iter_next(&dir, &dir_len, &path_iter) != GIT_ITEROVER) { + if (git_win32_path_join_n(fullpath, dir, dir_len, exe, exe_len) < 0) + continue; + + if (_waccess(fullpath, 0) == 0) { + found = true; + break; + } + } + + win32_path_iter_dispose(&path_iter); + + if (found) + return 0; + + fullpath[0] = L'\0'; + return GIT_ENOTFOUND; +} + static int win32_path_cwd(wchar_t *out, size_t len) { int cwd_len; |