5 /* Windows-specific implementation of exec_program_v() */
10 #include "git-compat-util.h"
12 #include "systeminfo.h"
35 #define MAX_PROCESSING_TIME (60 * 1000)
37 static char **path_split(const char *envpath
)
39 char *orig
, *p
, **path
;
42 orig
= xstrdup(envpath
);
53 if (*dir
) { /* not earlier, catches series of ; */
62 path
= (char **)xmalloc((n
+ 1) * sizeof(char *));
69 path
[i
++] = xstrdup(p
);
71 p
= p
+ strlen(p
) + 1;
81 static char *path_lookup_prog(const char *dir
, const char *cmd
)
84 int len
= strlen(cmd
);
85 int isexe
= len
>= 4 && 0 == strcasecmp(cmd
+ len
- 4, ".exe");
88 snprintf(path
, sizeof(path
), "%s/%s", dir
, cmd
);
90 snprintf(path
, sizeof(path
), "%s/%s.exe", dir
, cmd
);
93 if (0 == access(path
, F_OK
)) {
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
)
108 if (strchr(cmd
, '/') || strchr(cmd
, '\\')) {
112 while (!prog
&& *path
) {
113 prog
= path_lookup_prog(*path
++, cmd
);
119 static const char *escape_arg(const char *arg
)
121 /* count chars to quote */
123 int force_quotes
= 0;
126 if (!*p
) force_quotes
= 1;
128 if (isspace(*p
) || *p
== '*' || *p
== '?' || *p
== '{')
132 else if (*p
== '\\') {
146 if (!force_quotes
&& n
== 0) {
150 /* insert \ where necessary */
151 d
= q
= (char *)xmalloc(len
+n
+3);
156 else if (*arg
== '\\') {
158 while (*arg
== '\\') {
175 static void cmd_rec_init(const char **argv
, const char *envpath
,
180 char **path
= path_split(envpath
);
181 strbuf_init(&args
, 0);
183 rec
->cmd
= path_lookup(*argv
, path
);
189 for (; *argv
; argv
++) {
190 escaped
= (char *)escape_arg(*argv
);
192 strbuf_addch(&args
, ' ');
194 strbuf_addstr(&args
, escaped
);
195 if (escaped
!= *argv
) {
200 rec
->args
= strbuf_detach(&args
, NULL
);
203 static void cmd_rec_final(struct cmd_rec
*rec
)
214 static BOOL
create_output_handles(PHANDLE hRead
, PHANDLE hWrite
, HANDLE hProc
)
218 /* create pipe with no inheritance */
219 if (!CreatePipe(hRead
, &hWriteTmp
, NULL
, 0)) {
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
);
233 static BOOL
create_handles(struct handles_rec
*hStd
, struct output_rec
*hOutput
)
235 HANDLE hProc
= GetCurrentProcess();
236 hStd
->in
= GetStdHandle(STD_INPUT_HANDLE
);
239 if (!create_output_handles(&hOutput
->out
, &hStd
->out
, hProc
)) {
244 if (!create_output_handles(&hOutput
->err
, &hStd
->err
, hProc
)) {
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
) {
264 static void path_rec_init(const char *gitpath
, struct path_rec
*rec
)
266 struct strbuf path
= STRBUF_INIT
;
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
);
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
);
296 static void path_split_free(char **path
)
312 static HANDLE
process_init(struct cmd_rec cmdInfo
, const char *dir
,
313 struct handles_rec hStd
)
316 PROCESS_INFORMATION pi
;
320 flags
= has_console() ? 0 : CREATE_NO_WINDOW
;
322 memset(&pi
, 0, sizeof(pi
));
323 memset(&si
, 0, 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
);
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
)
353 res
= wait_for_process((pid_t
)hProc
, MAX_PROCESSING_TIME
, &status
);
362 int fd
= _open_osfhandle((intptr_t)hOutput
.out
, _O_RDONLY
);
363 strbuf_read(output
, fd
, 0);
367 int fd
= _open_osfhandle((intptr_t)hOutput
.err
, _O_RDONLY
);
368 strbuf_read(error
, fd
, 0);
372 status
= -ERR_RUN_COMMAND_WAITPID_NOEXIT
;
377 CloseHandle(hOutput
.out
);
378 CloseHandle(hOutput
.err
);
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
};
392 int wait
, status
= 0;
394 if (!(gitpath
= git_path())) {
398 if (!create_handles(&hStd
, &hOutput
)) {
402 path_rec_init(gitpath
, &pathInfo
);
403 cmd_rec_init(argv
, pathInfo
.envpath
, &cmdInfo
);
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
);