blob: a6eb116ddc7b586fea80f46ac36d50a4b3ddb656 [file] [log] [blame]
Johannes Schindelin4b8a2712023-08-09 16:54:46 +00001/*
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
Jeff King14149182024-08-27 23:59:52 -040014#pragma GCC diagnostic ignored "-Wunused-parameter"
15
Johannes Schindelin4b8a2712023-08-09 16:54:46 +000016/*
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"";
Patrick Steinhardt709fdce2024-12-06 11:27:18 +010056 size_t slash = 0;
57 int len;
Johannes Schindelin4b8a2712023-08-09 16:54:46 +000058
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] */
Patrick Steinhardt709fdce2024-12-06 11:27:18 +010070 for (size_t i = 0; _wpgmptr[i]; i++)
Johannes Schindelin4b8a2712023-08-09 16:54:46 +000071 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] */
Patrick Steinhardt709fdce2024-12-06 11:27:18 +010083 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)
Johannes Schindelin4b8a2712023-08-09 16:54:46 +000086 return 127; /* Too long path */
87
88 if (*command_line) {
89 /* Now, append the command-line arguments */
Patrick Steinhardt709fdce2024-12-06 11:27:18 +010090 len = swprintf_s(git_command_line + len, size - len,
91 L" %ls", command_line);
92 if (len < 0)
Johannes Schindelin4b8a2712023-08-09 16:54:46 +000093 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}