Frees the orig variable in the path_split function
[git-cheetah.git] / common / winexec.c
bloba9373beb8dae1918811dc45bcae70c3e1b9095c5
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';
53 if (*dir) { /* not earlier, catches series of ; */
54 ++n;
58 if (!n) {
59 return NULL;
62 path = (char **)xmalloc((n + 1) * sizeof(char *));
63 p = orig;
64 i = 0;
66 do {
68 if (*p) {
69 path[i++] = xstrdup(p);
71 p = p + strlen(p) + 1;
73 } while (i < n);
75 path[i] = NULL;
76 free(orig);
78 return path;
81 static char *path_lookup_prog(const char *dir, const char *cmd)
83 char path[MAX_PATH];
84 int len = strlen(cmd);
85 int isexe = len >= 4 && 0 == strcasecmp(cmd + len - 4, ".exe");
87 if (isexe) {
88 snprintf(path, sizeof(path), "%s/%s", dir, cmd);
89 } else {
90 snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd);
93 if (0 == access(path, F_OK)) {
94 return xstrdup(path);
97 return NULL;
101 * Determines the absolute path of cmd using the the split path in path.
102 * If cmd contains a slash or backslash, no lookup is performed.
104 static char *path_lookup(const char *cmd, char **path)
106 char *prog = NULL;
108 if (strchr(cmd, '/') || strchr(cmd, '\\')) {
109 return xstrdup(cmd);
112 while (!prog && *path) {
113 prog = path_lookup_prog(*path++, cmd);
116 return prog;
119 static const char *escape_arg(const char *arg)
121 /* count chars to quote */
122 int len = 0, n = 0;
123 int force_quotes = 0;
124 char *q, *d;
125 const char *p = arg;
126 if (!*p) force_quotes = 1;
127 while (*p) {
128 if (isspace(*p) || *p == '*' || *p == '?' || *p == '{')
129 force_quotes = 1;
130 else if (*p == '"')
131 n++;
132 else if (*p == '\\') {
133 int count = 0;
134 while (*p == '\\') {
135 count++;
136 p++;
137 len++;
139 if (*p == '"')
140 n += count*2 + 1;
141 continue;
143 len++;
144 p++;
146 if (!force_quotes && n == 0) {
147 return arg;
150 /* insert \ where necessary */
151 d = q = (char *)xmalloc(len+n+3);
152 *d++ = '"';
153 while (*arg) {
154 if (*arg == '"')
155 *d++ = '\\';
156 else if (*arg == '\\') {
157 int count = 0;
158 while (*arg == '\\') {
159 count++;
160 *d++ = *arg++;
162 if (*arg == '"') {
163 while (count-- > 0)
164 *d++ = '\\';
165 *d++ = '\\';
168 *d++ = *arg++;
170 *d++ = '"';
171 *d++ = 0;
172 return q;
175 static void cmd_rec_init(const char **argv, const char *envpath,
176 struct cmd_rec *rec)
178 struct strbuf args;
179 char *escaped;
180 char **path = path_split(envpath);
181 strbuf_init(&args, 0);
183 rec->cmd = path_lookup(*argv, path);
185 if (!rec->cmd) {
186 return;
189 for (; *argv; argv++) {
190 escaped = (char *)escape_arg(*argv);
191 if (args.len) {
192 strbuf_addch(&args, ' ');
194 strbuf_addstr(&args, escaped);
195 if (escaped != *argv) {
196 free(escaped);
200 rec->args = strbuf_detach(&args, NULL);
203 static void cmd_rec_final(struct cmd_rec *rec)
205 if (rec->cmd) {
206 free(rec->cmd);
209 if (rec->args) {
210 free(rec->args);
214 static BOOL create_output_handles(PHANDLE hRead, PHANDLE hWrite, HANDLE hProc)
216 HANDLE hWriteTmp;
218 /* create pipe with no inheritance */
219 if (!CreatePipe(hRead, &hWriteTmp, NULL, 0)) {
220 return FALSE;
223 /* dup write end with inheritance to hWrite and close hWriteTmp */
224 if (!DuplicateHandle(hProc, hWriteTmp, hProc, hWrite, 0, TRUE,
225 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
226 CloseHandle(hWriteTmp);
227 return FALSE;
230 return TRUE;
233 static BOOL create_handles(struct handles_rec *hStd, struct output_rec *hOutput)
235 HANDLE hProc = GetCurrentProcess();
236 hStd->in = GetStdHandle(STD_INPUT_HANDLE);
238 /* create stdOut */
239 if (!create_output_handles(&hOutput->out, &hStd->out, hProc)) {
240 return FALSE;
243 /* create stdErr */
244 if (!create_output_handles(&hOutput->err, &hStd->err, hProc)) {
245 return FALSE;
248 return TRUE;
251 static BOOL has_console(void)
253 HANDLE con = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
254 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
256 if (con == INVALID_HANDLE_VALUE) {
257 return FALSE;
258 } else {
259 CloseHandle(con);
260 return TRUE;
264 static void path_rec_init(const char *gitpath, struct path_rec *rec)
266 struct strbuf path = STRBUF_INIT;
268 if (gitpath) {
269 strbuf_addstr(&path, gitpath);
272 if ((rec->envpath_old = getenv("PATH"))) {
273 rec->envpath_old = xstrdup(rec->envpath_old);
274 strbuf_addch(&path, ';');
275 strbuf_addstr(&path, rec->envpath_old);
278 if (path.len) {
279 setenv("PATH", path.buf, 1);
280 rec->envpath = strbuf_detach(&path, NULL);
284 static void path_rec_final(struct path_rec *rec)
286 if (rec->envpath_old) {
287 setenv("PATH", rec->envpath_old, 1);
288 free(rec->envpath_old);
291 if (rec->envpath) {
292 free(rec->envpath);
296 static void path_split_free(char **path)
298 char **p;
300 if (!path) {
301 return;
304 p = path;
305 while (*p) {
306 free(*p++);
309 free(path);
312 static HANDLE process_init(struct cmd_rec cmdInfo, const char *dir,
313 struct handles_rec hStd)
315 STARTUPINFO si;
316 PROCESS_INFORMATION pi;
317 unsigned flags;
318 BOOL success;
320 flags = has_console() ? 0 : CREATE_NO_WINDOW;
322 memset(&pi, 0, sizeof(pi));
323 memset(&si, 0, sizeof(si));
324 si.cb = sizeof(si);
325 si.dwFlags = STARTF_USESTDHANDLES;
326 si.hStdInput = hStd.in;
327 si.hStdOutput = hStd.out;
328 si.hStdError = hStd.err;
330 success = CreateProcess(cmdInfo.cmd, cmdInfo.args, NULL, NULL,
331 TRUE, flags, NULL, dir, &si, &pi);
333 if (!success) {
334 errno = ENOENT;
337 /* close our end of the output handles */
338 CloseHandle(hStd.out);
339 CloseHandle(hStd.err);
341 CloseHandle(pi.hThread);
343 return success ? pi.hProcess : INVALID_HANDLE_VALUE;
346 static int process_final(HANDLE hProc, int wait, struct strbuf *output,
347 struct strbuf *error, struct output_rec hOutput)
349 int res, status = 0;
351 if (wait) {
353 res = wait_for_process((pid_t)hProc, MAX_PROCESSING_TIME, &status);
355 if (res) {
357 if (res < 0) {
358 status = -1;
361 if (output) {
362 int fd = _open_osfhandle((intptr_t)hOutput.out, _O_RDONLY);
363 strbuf_read(output, fd, 0);
366 if (error) {
367 int fd = _open_osfhandle((intptr_t)hOutput.err, _O_RDONLY);
368 strbuf_read(error, fd, 0);
371 } else {
372 status = -ERR_RUN_COMMAND_WAITPID_NOEXIT;
376 CloseHandle(hProc);
377 CloseHandle(hOutput.out);
378 CloseHandle(hOutput.err);
380 return status;
383 int exec_program_v(const char *working_directory, struct strbuf *output,
384 struct strbuf *error, int flags, const char **argv)
386 struct handles_rec hStd;
387 struct output_rec hOutput;
388 struct path_rec pathInfo = {NULL, NULL};
389 struct cmd_rec cmdInfo = {NULL, NULL};
390 const char *gitpath;
391 HANDLE hProc;
392 int wait, status = 0;
394 if (!(gitpath = git_path())) {
395 return -1;
398 if (!create_handles(&hStd, &hOutput)) {
399 return -1;
402 path_rec_init(gitpath, &pathInfo);
403 cmd_rec_init(argv, pathInfo.envpath, &cmdInfo);
405 if (cmdInfo.cmd) {
406 hProc = process_init(cmdInfo, working_directory, hStd);
408 if (INVALID_HANDLE_VALUE != hProc) {
409 wait = WAITMODE & flags;
410 status = process_final(hProc, wait, output, error, hOutput);
414 cmd_rec_final(&cmdInfo);
415 path_rec_final(&pathInfo);
417 return status;
419 #endif