1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2014 TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "system-call.h"
24 int command_start(wchar_t *cmd
, COMMAND_HANDLE
*commandHandle
, LPWSTR pEnv
)
26 SECURITY_ATTRIBUTES sa
;
27 HANDLE hReadOut
= INVALID_HANDLE_VALUE
, hWriteOut
= INVALID_HANDLE_VALUE
, hReadIn
= INVALID_HANDLE_VALUE
, hWriteIn
= INVALID_HANDLE_VALUE
;
28 STARTUPINFOW si
= { 0 };
29 PROCESS_INFORMATION pi
= { 0 };
31 si
.cb
= sizeof(STARTUPINFOW
);
33 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
34 sa
.lpSecurityDescriptor
= NULL
;
35 sa
.bInheritHandle
= TRUE
;
36 if (!CreatePipe(&hReadOut
, &hWriteOut
, &sa
, 0)) {
37 giterr_set(GITERR_OS
, "Could not create pipe");
40 if (!CreatePipe(&hReadIn
, &hWriteIn
, &sa
, 0)) {
41 giterr_set(GITERR_OS
, "Could not create pipe");
42 CloseHandle(hReadOut
);
43 CloseHandle(hWriteOut
);
47 si
.hStdOutput
= hWriteOut
;
48 si
.hStdInput
= hReadIn
;
49 si
.hStdError
= INVALID_HANDLE_VALUE
;
51 // Ensure the read/write handle to the pipe for STDOUT resp. STDIN are not inherited.
52 if (!SetHandleInformation(hReadOut
, HANDLE_FLAG_INHERIT
, 0) || !SetHandleInformation(hWriteIn
, HANDLE_FLAG_INHERIT
, 0)) {
53 CloseHandle(hReadOut
);
54 CloseHandle(hWriteOut
);
56 CloseHandle(hWriteIn
);
60 si
.wShowWindow
= SW_HIDE
;
61 si
.dwFlags
= STARTF_USESTDHANDLES
| STARTF_USESHOWWINDOW
;
63 if (!CreateProcessW(NULL
, cmd
, NULL
, NULL
, TRUE
, pEnv
? CREATE_UNICODE_ENVIRONMENT
: 0, pEnv
, NULL
, &si
, &pi
)) {
64 giterr_set(GITERR_OS
, "Could not start external tool");
65 CloseHandle(hReadOut
);
66 CloseHandle(hWriteOut
);
68 CloseHandle(hWriteIn
);
72 AllowSetForegroundWindow(pi
.dwProcessId
);
73 WaitForInputIdle(pi
.hProcess
, 10000);
76 CloseHandle(hWriteOut
);
78 commandHandle
->pi
= pi
;
79 commandHandle
->out
= hReadOut
;
80 commandHandle
->in
= hWriteIn
;
81 commandHandle
->running
= TRUE
;
85 int command_read(COMMAND_HANDLE
*commandHandle
, char *buffer
, size_t buf_size
, size_t *bytes_read
)
89 if (!ReadFile(commandHandle
->out
, buffer
, (DWORD
)buf_size
, (DWORD
*)bytes_read
, NULL
)) {
90 giterr_set(GITERR_OS
, "could not read data from external process");
97 int command_readall(COMMAND_HANDLE
*commandHandle
, git_buf
*buf
)
99 size_t bytes_read
= 0;
102 command_read(commandHandle
->out
, buffer
, sizeof(buffer
), &bytes_read
);
103 git_buf_put(buf
, buffer
, bytes_read
);
104 } while (bytes_read
);
109 int command_write(COMMAND_HANDLE
*commandHandle
, const char *buffer
, size_t len
)
115 if (!WriteFile(commandHandle
->in
, buffer
+ off
, (DWORD
)(len
- off
), &written
, NULL
)) {
116 giterr_set(GITERR_OS
, "could not write data to external process");
126 int command_write_gitbuf(COMMAND_HANDLE
*commandHandle
, const git_buf
*buf
)
128 return command_write(commandHandle
, buf
->ptr
, buf
->size
);
131 static void safeCloseHandle(HANDLE
*handle
)
133 if (*handle
!= INVALID_HANDLE_VALUE
) {
134 CloseHandle(*handle
);
135 *handle
= INVALID_HANDLE_VALUE
;
139 void command_close_stdin(COMMAND_HANDLE
*commandHandle
)
141 safeCloseHandle(&commandHandle
->in
);
144 void command_close_stdout(COMMAND_HANDLE
*commandHandle
)
146 safeCloseHandle(&commandHandle
->out
);
149 DWORD
command_close(COMMAND_HANDLE
*commandHandle
)
151 if (!commandHandle
->running
)
154 commandHandle
->running
= FALSE
;
156 command_close_stdin(commandHandle
);
157 command_close_stdout(commandHandle
);
159 CloseHandle(commandHandle
->pi
.hThread
);
161 WaitForSingleObject(commandHandle
->pi
.hProcess
, INFINITE
);
163 DWORD exitcode
= MAXDWORD
;
164 GetExitCodeProcess(commandHandle
->pi
.hProcess
, &exitcode
);
165 CloseHandle(commandHandle
->pi
.hProcess
);