mirror of
https://github.com/libgit2/libgit2.git
synced 2026-01-25 02:56:17 +00:00
process: resolve paths for win32
When using `git_process_new` on win32, resolve the path to the application in the same way that we do on POSIX. Search `PATH` for command to execute (unless the given executable is fully qualified). In addition, better match Windows executable lookup behavior itself (allowing the command to be `foo`, and looking for a matching `foo.exe` or `foo.cmd`.)
This commit is contained in:
@@ -153,6 +153,7 @@ int git_win32_path_canonicalize(git_win32_path path)
|
||||
|
||||
static int git_win32_path_join(
|
||||
git_win32_path dest,
|
||||
size_t *dest_len,
|
||||
const wchar_t *one,
|
||||
size_t one_len,
|
||||
const wchar_t *two,
|
||||
@@ -176,6 +177,9 @@ static int git_win32_path_join(
|
||||
memcpy(dest + one_len + backslash, two, two_len * sizeof(wchar_t));
|
||||
dest[one_len + backslash + two_len] = L'\0';
|
||||
|
||||
if (dest_len)
|
||||
*dest_len = one_len + backslash + two_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -258,21 +262,12 @@ struct executable_suffix {
|
||||
size_t len;
|
||||
};
|
||||
|
||||
int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe)
|
||||
static struct executable_suffix suffixes[] = { { NULL, 0 }, { L".exe", 4 }, { L".cmd", 4 } };
|
||||
|
||||
static bool has_executable_suffix(wchar_t *exe, size_t exe_len)
|
||||
{
|
||||
struct win32_path_iter path_iter;
|
||||
const wchar_t *dir;
|
||||
size_t dir_len, exe_len = wcslen(exe);
|
||||
bool found = false;
|
||||
static struct executable_suffix suffixes[] = { { NULL, 0 }, { L".exe", 4 }, { L".cmd", 4 } };
|
||||
size_t skip_bare = 1, i;
|
||||
size_t i;
|
||||
|
||||
if (win32_path_iter_init(&path_iter) < 0)
|
||||
return -1;
|
||||
|
||||
/* see if the given executable has an executable suffix; if so we will
|
||||
* look for the explicit name directly, as well as with added suffixes.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
|
||||
struct executable_suffix *suffix = &suffixes[i];
|
||||
|
||||
@@ -282,40 +277,80 @@ int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe)
|
||||
if (exe_len < suffix->len)
|
||||
continue;
|
||||
|
||||
if (memcmp(&exe[exe_len - suffix->len], suffix->suffix, suffix->len) == 0) {
|
||||
skip_bare = 0;
|
||||
break;
|
||||
}
|
||||
if (memcmp(&exe[exe_len - suffix->len], suffix->suffix, suffix->len) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
while (win32_path_iter_next(&dir, &dir_len, &path_iter) != GIT_ITEROVER && !found) {
|
||||
/*
|
||||
* if the given name has an executable suffix, then try looking for it
|
||||
* directly. in all cases, append executable extensions
|
||||
* (".exe", ".cmd"...)
|
||||
*/
|
||||
for (i = skip_bare; i < ARRAY_SIZE(suffixes); i++) {
|
||||
struct executable_suffix *suffix = &suffixes[i];
|
||||
return false;
|
||||
}
|
||||
|
||||
if (git_win32_path_join(fullpath, dir, dir_len, exe, exe_len) < 0)
|
||||
static int is_executable(git_win32_path path, size_t path_len, bool suffixed)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* if the given name has an executable suffix, then try looking for it
|
||||
* directly. in all cases, append executable extensions
|
||||
* (".exe", ".cmd"...)
|
||||
*/
|
||||
for (i = suffixed ? 0 : 1; i < ARRAY_SIZE(suffixes); i++) {
|
||||
struct executable_suffix *suffix = &suffixes[i];
|
||||
|
||||
if (suffix->len) {
|
||||
if (path_len + suffix->len > MAX_PATH)
|
||||
continue;
|
||||
|
||||
if (suffix->len) {
|
||||
if (dir_len + exe_len + 1 + suffix->len > MAX_PATH)
|
||||
continue;
|
||||
wcscat(path, suffix->suffix);
|
||||
}
|
||||
|
||||
wcscat(fullpath, suffix->suffix);
|
||||
}
|
||||
if (_waccess(path, 0) == 0)
|
||||
return true;
|
||||
|
||||
if (_waccess(fullpath, 0) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
path[path_len] = L'\0';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe)
|
||||
{
|
||||
struct win32_path_iter path_iter;
|
||||
const wchar_t *dir;
|
||||
size_t dir_len, exe_len, fullpath_len;
|
||||
bool suffixed = false, found = false;
|
||||
|
||||
if ((exe_len = wcslen(exe)) > MAX_PATH)
|
||||
goto done;
|
||||
|
||||
/* see if the given executable has an executable suffix; if so we will
|
||||
* look for the explicit name directly, as well as with added suffixes.
|
||||
*/
|
||||
suffixed = has_executable_suffix(exe, exe_len);
|
||||
|
||||
/* For fully-qualified paths we do not look in PATH */
|
||||
if (wcschr(exe, L'\\') != NULL || wcschr(exe, L'/') != NULL) {
|
||||
if ((found = is_executable(exe, exe_len, suffixed)))
|
||||
wcscpy(fullpath, exe);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (win32_path_iter_init(&path_iter) < 0)
|
||||
return -1;
|
||||
|
||||
while (win32_path_iter_next(&dir, &dir_len, &path_iter) != GIT_ITEROVER && !found) {
|
||||
if (git_win32_path_join(fullpath, &fullpath_len, dir, dir_len, exe, exe_len) < 0)
|
||||
continue;
|
||||
|
||||
if (is_executable(fullpath, fullpath_len, suffixed)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
win32_path_iter_dispose(&path_iter);
|
||||
|
||||
done:
|
||||
if (found)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "git2_util.h"
|
||||
#include "process.h"
|
||||
#include "strlist.h"
|
||||
#include "fs_path.h"
|
||||
|
||||
#ifndef DWORD_MAX
|
||||
# define DWORD_MAX INT32_MAX
|
||||
@@ -19,7 +20,8 @@
|
||||
#define ENV_MAX 32767
|
||||
|
||||
struct git_process {
|
||||
wchar_t *appname;
|
||||
char *app_name;
|
||||
wchar_t *app_path;
|
||||
wchar_t *cmdline;
|
||||
wchar_t *env;
|
||||
|
||||
@@ -178,8 +180,7 @@ static int process_new(
|
||||
process = git__calloc(1, sizeof(git_process));
|
||||
GIT_ERROR_CHECK_ALLOC(process);
|
||||
|
||||
if (appname &&
|
||||
git_utf8_to_16_alloc(&process->appname, appname) < 0) {
|
||||
if (appname && (process->app_name = git__strdup(appname)) == NULL) {
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
@@ -233,7 +234,7 @@ int git_process_new(
|
||||
size_t env_len,
|
||||
git_process_options *opts)
|
||||
{
|
||||
git_str cmdline = GIT_STR_INIT;
|
||||
git_str cmd_path = GIT_STR_INIT, cmdline = GIT_STR_INIT;
|
||||
int error;
|
||||
|
||||
GIT_ASSERT_ARG(out && args && args_len > 0);
|
||||
@@ -244,6 +245,7 @@ int git_process_new(
|
||||
error = process_new(out, args[0], cmdline.ptr, env, env_len, opts);
|
||||
|
||||
done:
|
||||
git_str_dispose(&cmd_path);
|
||||
git_str_dispose(&cmdline);
|
||||
return error;
|
||||
}
|
||||
@@ -259,6 +261,19 @@ int git_process_start(git_process *process)
|
||||
out[2] = { NULL, NULL },
|
||||
err[2] = { NULL, NULL };
|
||||
|
||||
if (process->app_name) {
|
||||
git_str cmd_path = GIT_STR_INIT;
|
||||
int error;
|
||||
|
||||
if ((error = git_fs_path_find_executable(&cmd_path, process->app_name)) == 0)
|
||||
error = git_utf8_to_16_alloc(&process->app_path, cmd_path.ptr);
|
||||
|
||||
git_str_dispose(&cmd_path);
|
||||
|
||||
if (error < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
memset(&security_attrs, 0, sizeof(SECURITY_ATTRIBUTES));
|
||||
security_attrs.bInheritHandle = TRUE;
|
||||
|
||||
@@ -303,7 +318,7 @@ int git_process_start(git_process *process)
|
||||
|
||||
memset(&process->process_info, 0, sizeof(PROCESS_INFORMATION));
|
||||
|
||||
if (!CreateProcessW(process->appname, process->cmdline,
|
||||
if (!CreateProcessW(process->app_path, process->cmdline,
|
||||
NULL, NULL, TRUE, flags, process->env,
|
||||
process->cwd,
|
||||
&startup_info,
|
||||
@@ -501,6 +516,7 @@ void git_process_free(git_process *process)
|
||||
git__free(process->env);
|
||||
git__free(process->cwd);
|
||||
git__free(process->cmdline);
|
||||
git__free(process->appname);
|
||||
git__free(process->app_path);
|
||||
git__free(process->app_name);
|
||||
git__free(process);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,52 @@ void test_process_start__not_found(void)
|
||||
git_process_free(process);
|
||||
}
|
||||
|
||||
void test_process_start__finds_in_path(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
const char *args_array[] = { "cmd", "/c", "exit", "0" };
|
||||
#else
|
||||
const char *args_array[] = { "true" };
|
||||
#endif
|
||||
|
||||
git_process *process;
|
||||
git_process_options opts = GIT_PROCESS_OPTIONS_INIT;
|
||||
git_process_result result = GIT_PROCESS_RESULT_INIT;
|
||||
|
||||
cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts));
|
||||
cl_git_pass(git_process_start(process));
|
||||
cl_git_pass(git_process_wait(&result, process));
|
||||
|
||||
cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status);
|
||||
cl_assert_equal_i(0, result.exitcode);
|
||||
cl_assert_equal_i(0, result.signal);
|
||||
|
||||
git_process_free(process);
|
||||
}
|
||||
|
||||
void test_process_start__adds_suffix(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
const char *args_array[] = { "C:\\Windows\\System32\\cmd", "/c", "exit", "0" };
|
||||
|
||||
git_process *process;
|
||||
git_process_options opts = GIT_PROCESS_OPTIONS_INIT;
|
||||
git_process_result result = GIT_PROCESS_RESULT_INIT;
|
||||
|
||||
cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts));
|
||||
cl_git_pass(git_process_start(process));
|
||||
cl_git_pass(git_process_wait(&result, process));
|
||||
|
||||
cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status);
|
||||
cl_assert_equal_i(0, result.exitcode);
|
||||
cl_assert_equal_i(0, result.signal);
|
||||
|
||||
git_process_free(process);
|
||||
#else
|
||||
cl_skip();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void write_all(git_process *process, char *buf)
|
||||
{
|
||||
size_t buf_len = strlen(buf);
|
||||
|
||||
Reference in New Issue
Block a user