Git fork
at reftables-rust 118 lines 3.4 kB view raw
1/* 2 * headless Git - run Git without opening a console window on Windows 3 */ 4 5#define STRICT 6#define WIN32_LEAN_AND_MEAN 7#define UNICODE 8#define _UNICODE 9#include <windows.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <wchar.h> 13 14#pragma GCC diagnostic ignored "-Wunused-parameter" 15 16/* 17 * If `dir` contains the path to a Git exec directory, extend `PATH` to 18 * include the corresponding `bin/` directory (which is where all those 19 * `.dll` files needed by `git.exe` are, on Windows). 20 */ 21static int extend_path(wchar_t *dir, size_t dir_len) 22{ 23 const wchar_t *suffix = L"\\libexec\\git-core"; 24 size_t suffix_len = wcslen(suffix); 25 wchar_t *env; 26 DWORD len; 27 28 if (dir_len < suffix_len) 29 return 0; 30 31 dir_len -= suffix_len; 32 if (memcmp(dir + dir_len, suffix, suffix_len * sizeof(wchar_t))) 33 return 0; 34 35 len = GetEnvironmentVariableW(L"PATH", NULL, 0); 36 if (!len) 37 return 0; 38 39 env = _alloca((dir_len + 5 + len) * sizeof(wchar_t)); 40 wcsncpy(env, dir, dir_len); 41 wcscpy(env + dir_len, L"\\bin;"); 42 if (!GetEnvironmentVariableW(L"PATH", env + dir_len + 5, len)) 43 return 0; 44 45 SetEnvironmentVariableW(L"PATH", env); 46 return 1; 47} 48 49int WINAPI wWinMain(_In_ HINSTANCE instance, 50 _In_opt_ HINSTANCE previous_instance, 51 _In_ LPWSTR command_line, _In_ int show) 52{ 53 wchar_t git_command_line[32768]; 54 size_t size = sizeof(git_command_line) / sizeof(wchar_t); 55 const wchar_t *needs_quotes = L""; 56 size_t slash = 0; 57 int len; 58 59 STARTUPINFO startup_info = { 60 .cb = sizeof(STARTUPINFO), 61 .dwFlags = STARTF_USESHOWWINDOW, 62 .wShowWindow = SW_HIDE, 63 }; 64 PROCESS_INFORMATION process_info = { 0 }; 65 DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | 66 CREATE_NEW_CONSOLE | CREATE_NO_WINDOW; 67 DWORD exit_code; 68 69 /* First, determine the full path of argv[0] */ 70 for (size_t i = 0; _wpgmptr[i]; i++) 71 if (_wpgmptr[i] == L' ') 72 needs_quotes = L"\""; 73 else if (_wpgmptr[i] == L'\\') 74 slash = i; 75 76 if (slash >= size - 11) 77 return 127; /* Too long path */ 78 79 /* If it is in Git's exec path, add the bin/ directory to the PATH */ 80 extend_path(_wpgmptr, slash); 81 82 /* Then, add the full path of `git.exe` as argv[0] */ 83 len = swprintf_s(git_command_line, size, L"%ls%.*ls\\git.exe%ls", 84 needs_quotes, (int) slash, _wpgmptr, needs_quotes); 85 if (len < 0) 86 return 127; /* Too long path */ 87 88 if (*command_line) { 89 /* Now, append the command-line arguments */ 90 len = swprintf_s(git_command_line + len, size - len, 91 L" %ls", command_line); 92 if (len < 0) 93 return 127; 94 } 95 96 startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 97 startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 98 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); 99 100 if (!CreateProcess(NULL, /* infer argv[0] from the command line */ 101 git_command_line, /* modified command line */ 102 NULL, /* inherit process handles? */ 103 NULL, /* inherit thread handles? */ 104 FALSE, /* handles inheritable? */ 105 creation_flags, 106 NULL, /* use this process' environment */ 107 NULL, /* use this process' working directory */ 108 &startup_info, &process_info)) 109 return 129; /* could not start */ 110 WaitForSingleObject(process_info.hProcess, INFINITE); 111 if (!GetExitCodeProcess(process_info.hProcess, &exit_code)) 112 exit_code = 130; /* Could not determine exit code? */ 113 114 CloseHandle(process_info.hProcess); 115 CloseHandle(process_info.hThread); 116 117 return (int)exit_code; 118}