Style-cleanup
[git-cheetah.git] / common / winexec.c
blobfbec54f1365f5220e980eda6fcda4443ca01be81
1 #pragma once
3 #ifdef _WIN32
5 /* Windows-specific implementation of exec_program_v() */
7 #include "cache.h"
8 #include "debug.h"
9 #include "exec.h"
10 #include "git-compat-util.h"
11 #include "strbuf.h"
12 #include "systeminfo.h"
14 struct output_rec {
15 HANDLE out;
16 HANDLE err;
19 struct handles_rec {
20 HANDLE in;
21 HANDLE out;
22 HANDLE err;
25 struct path_rec {
26 char *envpath;
27 char *envpath_old;
30 struct cmd_rec {
31 char *cmd;
32 char *args;
35 #define MAX_PROCESSING_TIME (60 * 1000)
37 static char **path_split(const char *envpath)
39 char *orig, *p, **path;
40 int i, n = 0;
42 orig = xstrdup(envpath);
43 p = orig;
45 while (p) {
46 char *dir = p;
47 p = strchr(p, ';');
49 if (p)
50 *p++ = '\0';
52 if (*dir) /* not earlier, catches series of ; */
53 ++n;
56 if (!n)
57 return NULL;
59 path = (char **)xmalloc((n + 1) * sizeof(char *));
60 p = orig;
61 i = 0;
63 do {
65 if (*p)
66 path[i++] = xstrdup(p);
68 p = p + strlen(p) + 1;
70 } while (i < n);
72 path[i] = NULL;
73 free(orig);
75 return path;
78 static char *path_lookup_prog(const char *dir, const char *cmd)
80 char path[MAX_PATH];
81 int len = strlen(cmd);
82 int isexe = len >= 4 && 0 == strcasecmp(cmd + len - 4, ".exe");
84 if (isexe) {
85 snprintf(path, sizeof(path), "%s/%s", dir, cmd);
86 } else {
87 snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd);
90 if (0 == access(path, F_OK))
91 return xstrdup(path);
93 return NULL;
97 * Determines the absolute path of cmd using the the split path in path.
98 * If cmd contains a slash or backslash, no lookup is performed.
100 static char *path_lookup(const char *cmd, char **path)
102 char *prog = NULL;
104 if (strchr(cmd, '/') || strchr(cmd, '\\'))
105 return xstrdup(cmd);
107 while (!prog && *path)
108 prog = path_lookup_prog(*path++, cmd);
110 return prog;
113 static const char *escape_arg(const char *arg)
115 /* count chars to quote */
116 int len = 0, n = 0;
117 int force_quotes = 0;
118 char *q, *d;
119 const char *p = arg;
120 if (!*p) force_quotes = 1;
121 while (*p) {
122 if (isspace(*p) || *p == '*' || *p == '?' || *p == '{')
123 force_quotes = 1;
124 else if (*p == '"')
125 n++;
126 else if (*p == '\\') {
127 int count = 0;
128 while (*p == '\\') {
129 count++;
130 p++;
131 len++;
133 if (*p == '"')
134 n += count*2 + 1;
135 continue;
137 len++;
138 p++;
140 if (!force_quotes && n == 0)
141 return arg;
143 /* insert \ where necessary */
144 d = q = (char *)xmalloc(len+n+3);
145 *d++ = '"';
146 while (*arg) {
147 if (*arg == '"')
148 *d++ = '\\';
149 else if (*arg == '\\') {
150 int count = 0;
151 while (*arg == '\\') {
152 count++;
153 *d++ = *arg++;
155 if (*arg == '"') {
156 while (count-- > 0)
157 *d++ = '\\';
158 *d++ = '\\';
161 *d++ = *arg++;
163 *d++ = '"';
164 *d++ = 0;
165 return q;
168 static void cmd_rec_init(const char **argv, const char *envpath,
169 struct cmd_rec *rec)
171 struct strbuf args;
172 char *escaped;
173 char **path = path_split(envpath);
174 strbuf_init(&args, 0);
176 rec->cmd = path_lookup(*argv, path);
178 if (!rec->cmd)
179 return;
181 for (; *argv; argv++) {
182 escaped = (char *)escape_arg(*argv);
183 if (args.len)
184 strbuf_addch(&args, ' ');
185 strbuf_addstr(&args, escaped);
186 if (escaped != *argv)
187 free(escaped);
190 rec->args = strbuf_detach(&args, NULL);
193 static void cmd_rec_final(struct cmd_rec *rec)
195 if (rec->cmd)
196 free(rec->cmd);
198 if (rec->args)
199 free(rec->args);
202 static BOOL create_output_handles(PHANDLE hRead, PHANDLE hWrite, HANDLE hProc)
204 HANDLE hWriteTmp;
206 /* create pipe with no inheritance */
207 if (!CreatePipe(hRead, &hWriteTmp, NULL, 0))
208 return FALSE;
210 /* dup write end with inheritance to hWrite and close hWriteTmp */
211 if (!DuplicateHandle(hProc, hWriteTmp, hProc, hWrite, 0, TRUE,
212 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
213 CloseHandle(hWriteTmp);
214 return FALSE;
217 return TRUE;
220 static BOOL create_handles(struct handles_rec *hStd, struct output_rec *hOutput)
222 HANDLE hProc = GetCurrentProcess();
223 hStd->in = GetStdHandle(STD_INPUT_HANDLE);
225 /* create stdOut */
226 if (!create_output_handles(&hOutput->out, &hStd->out, hProc))
227 return FALSE;
229 /* create stdErr */
230 if (!create_output_handles(&hOutput->err, &hStd->err, hProc))
231 return FALSE;
233 return TRUE;
236 static BOOL has_console(void)
238 HANDLE con = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
239 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
241 if (con == INVALID_HANDLE_VALUE) {
242 return FALSE;
243 } else {
244 CloseHandle(con);
245 return TRUE;
249 static void path_rec_init(const char *gitpath, struct path_rec *rec)
251 struct strbuf path = STRBUF_INIT;
253 if (gitpath)
254 strbuf_addstr(&path, gitpath);
256 if ((rec->envpath_old = getenv("PATH"))) {
257 rec->envpath_old = xstrdup(rec->envpath_old);
258 strbuf_addch(&path, ';');
259 strbuf_addstr(&path, rec->envpath_old);
262 if (path.len) {
263 setenv("PATH", path.buf, 1);
264 rec->envpath = strbuf_detach(&path, NULL);
268 static void path_rec_final(struct path_rec *rec)
270 if (rec->envpath_old) {
271 setenv("PATH", rec->envpath_old, 1);
272 free(rec->envpath_old);
275 if (rec->envpath)
276 free(rec->envpath);
279 static void path_split_free(char **path)
281 char **p;
283 if (!path)
284 return;
286 p = path;
287 while (*p)
288 free(*p++);
290 free(path);
293 static HANDLE process_init(struct cmd_rec cmdInfo, const char *dir,
294 struct handles_rec hStd)
296 STARTUPINFO si;
297 PROCESS_INFORMATION pi;
298 unsigned flags;
299 BOOL success;
301 flags = has_console() ? 0 : CREATE_NO_WINDOW;
303 memset(&pi, 0, sizeof(pi));
304 memset(&si, 0, sizeof(si));
305 si.cb = sizeof(si);
306 si.dwFlags = STARTF_USESTDHANDLES;
307 si.hStdInput = hStd.in;
308 si.hStdOutput = hStd.out;
309 si.hStdError = hStd.err;
311 success = CreateProcess(cmdInfo.cmd, cmdInfo.args, NULL, NULL,
312 TRUE, flags, NULL, dir, &si, &pi);
314 if (!success)
315 errno = ENOENT;
317 /* close our end of the output handles */
318 CloseHandle(hStd.out);
319 CloseHandle(hStd.err);
321 CloseHandle(pi.hThread);
323 return success ? pi.hProcess : INVALID_HANDLE_VALUE;
326 static int process_final(HANDLE hProc, int wait, struct strbuf *output,
327 struct strbuf *error, struct output_rec hOutput)
329 int res, status = 0;
331 if (wait) {
333 res = wait_for_process((pid_t)hProc, MAX_PROCESSING_TIME, &status);
335 if (res) {
337 if (res < 0)
338 status = -1;
340 if (output) {
341 int fd = _open_osfhandle((intptr_t)hOutput.out, _O_RDONLY);
342 strbuf_read(output, fd, 0);
345 if (error) {
346 int fd = _open_osfhandle((intptr_t)hOutput.err, _O_RDONLY);
347 strbuf_read(error, fd, 0);
350 } else {
351 status = -ERR_RUN_COMMAND_WAITPID_NOEXIT;
355 CloseHandle(hProc);
356 CloseHandle(hOutput.out);
357 CloseHandle(hOutput.err);
359 return status;
362 int exec_program_v(const char *working_directory, struct strbuf *output,
363 struct strbuf *error, int flags, const char **argv)
365 struct handles_rec hStd;
366 struct output_rec hOutput;
367 struct path_rec pathInfo = {NULL, NULL};
368 struct cmd_rec cmdInfo = {NULL, NULL};
369 const char *gitpath;
370 HANDLE hProc;
371 int wait, status = 0;
373 if (!(gitpath = git_path()))
374 return -1;
376 if (!create_handles(&hStd, &hOutput))
377 return -1;
379 path_rec_init(gitpath, &pathInfo);
380 cmd_rec_init(argv, pathInfo.envpath, &cmdInfo);
382 if (cmdInfo.cmd) {
383 hProc = process_init(cmdInfo, working_directory, hStd);
385 if (INVALID_HANDLE_VALUE != hProc) {
386 wait = WAITMODE & flags;
387 status = process_final(hProc, wait, output, error, hOutput);
391 cmd_rec_final(&cmdInfo);
392 path_rec_final(&pathInfo);
394 return status;
396 #endif