CommitDlg: Update empty file list message
[TortoiseGit.git] / src / libgit2 / system-call.c
blob86a4beab0a5e1c3f69ca5a661867beafe1f7a6ad
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.
20 #include "buffer.h"
21 #include "netops.h"
22 #include "system-call.h"
24 static void safeCloseHandle(HANDLE *handle)
26 if (*handle != INVALID_HANDLE_VALUE) {
27 CloseHandle(*handle);
28 *handle = INVALID_HANDLE_VALUE;
32 static int command_read(HANDLE handle, char *buffer, size_t buf_size, size_t *bytes_read)
34 *bytes_read = 0;
36 if (!ReadFile(handle, buffer, (DWORD)buf_size, (DWORD*)bytes_read, NULL)) {
37 giterr_set(GITERR_OS, "could not read data from external process");
38 return -1;
41 return 0;
44 static int command_readall(HANDLE handle, git_buf *buf)
46 size_t bytes_read = 0;
47 do {
48 char buffer[65536];
49 command_read(handle, buffer, sizeof(buffer), &bytes_read);
50 git_buf_put(buf, buffer, bytes_read);
51 } while (bytes_read);
53 return 0;
56 struct ASYNCREADINGTHREADARGS {
57 HANDLE *handle;
58 git_buf *dest;
61 static DWORD WINAPI AsyncReadingThread(LPVOID lpParam)
63 struct ASYNCREADINGTHREADARGS* pDataArray = (struct ASYNCREADINGTHREADARGS*)lpParam;
65 int ret = command_readall(*pDataArray->handle, pDataArray->dest);
67 safeCloseHandle(pDataArray->handle);
69 git__free(pDataArray);
71 return ret;
74 static HANDLE commmand_start_reading_thread(HANDLE *handle, git_buf *dest)
76 struct ASYNCREADINGTHREADARGS *threadArguments = git__calloc(1, sizeof(struct ASYNCREADINGTHREADARGS));
77 HANDLE thread;
78 if (!threadArguments) {
79 giterr_set_oom();
80 return NULL;
83 threadArguments->handle = handle;
84 threadArguments->dest = dest;
86 thread = CreateThread(NULL, 0, AsyncReadingThread, threadArguments, 0, NULL);
87 if (!thread) {
88 git__free(threadArguments);
89 giterr_set(GITERR_OS, "Could not create thread");
91 return thread;
94 int commmand_start_stdout_reading_thread(COMMAND_HANDLE *commandHandle, git_buf *dest)
96 HANDLE thread = commmand_start_reading_thread(&commandHandle->out, dest);
97 if (!thread)
98 return -1;
99 commandHandle->asyncReadOutThread = thread;
100 return 0;
103 static int command_wait_reading_thread(HANDLE *handle)
105 DWORD exitCode = MAXDWORD;
106 if (*handle == INVALID_HANDLE_VALUE)
107 return -1;
109 WaitForSingleObject(*handle, INFINITE);
110 if (!GetExitCodeThread(*handle, &exitCode) || exitCode) {
111 safeCloseHandle(handle);
112 return -1;
114 safeCloseHandle(handle);
115 return 0;
118 int command_wait_stdout_reading_thread(COMMAND_HANDLE *commandHandle)
120 return command_wait_reading_thread(&commandHandle->asyncReadOutThread);
123 void command_init(COMMAND_HANDLE *commandHandle)
125 memset(commandHandle, 0, sizeof(COMMAND_HANDLE));
126 commandHandle->in = INVALID_HANDLE_VALUE;
127 commandHandle->out = INVALID_HANDLE_VALUE;
128 commandHandle->err = INVALID_HANDLE_VALUE;
129 commandHandle->asyncReadErrorThread = INVALID_HANDLE_VALUE;
130 commandHandle->asyncReadOutThread = INVALID_HANDLE_VALUE;
133 int command_start(wchar_t *cmd, COMMAND_HANDLE *commandHandle, LPWSTR pEnv, DWORD flags)
135 SECURITY_ATTRIBUTES sa;
136 HANDLE hReadOut = INVALID_HANDLE_VALUE, hWriteOut = INVALID_HANDLE_VALUE, hReadIn = INVALID_HANDLE_VALUE, hWriteIn = INVALID_HANDLE_VALUE, hReadError = INVALID_HANDLE_VALUE, hWriteError = INVALID_HANDLE_VALUE;
137 STARTUPINFOW si = { 0 };
138 PROCESS_INFORMATION pi = { 0 };
140 si.cb = sizeof(STARTUPINFOW);
142 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
143 sa.lpSecurityDescriptor = NULL;
144 sa.bInheritHandle = TRUE;
145 if (!CreatePipe(&hReadOut, &hWriteOut, &sa, 0)) {
146 giterr_set(GITERR_OS, "Could not create pipe");
147 return -1;
149 if (!CreatePipe(&hReadIn, &hWriteIn, &sa, 0)) {
150 giterr_set(GITERR_OS, "Could not create pipe");
151 CloseHandle(hReadOut);
152 CloseHandle(hWriteOut);
153 return -1;
155 if (commandHandle->errBuf && !CreatePipe(&hReadError, &hWriteError, &sa, 0)) {
156 giterr_set(GITERR_OS, "Could not create pipe");
157 CloseHandle(hReadOut);
158 CloseHandle(hWriteOut);
159 CloseHandle(hReadIn);
160 CloseHandle(hWriteIn);
161 return -1;
164 si.hStdOutput = hWriteOut;
165 si.hStdInput = hReadIn;
166 si.hStdError = hWriteError;
168 // Ensure the read/write handle to the pipe for STDOUT resp. STDIN are not inherited.
169 if (!SetHandleInformation(hReadOut, HANDLE_FLAG_INHERIT, 0) || !SetHandleInformation(hWriteIn, HANDLE_FLAG_INHERIT, 0) || (commandHandle->errBuf && !SetHandleInformation(hReadError, HANDLE_FLAG_INHERIT, 0))) {
170 giterr_set(GITERR_OS, "SetHandleInformation failed");
171 CloseHandle(hReadOut);
172 CloseHandle(hWriteOut);
173 CloseHandle(hReadIn);
174 CloseHandle(hWriteIn);
175 safeCloseHandle(&hReadError);
176 safeCloseHandle(&hWriteError);
177 return -1;
180 si.wShowWindow = SW_HIDE;
181 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
183 if (!CreateProcessW(NULL, cmd, NULL, NULL, TRUE, (pEnv ? CREATE_UNICODE_ENVIRONMENT : 0) | flags, pEnv, NULL, &si, &pi)) {
184 giterr_set(GITERR_OS, "Could not start external tool");
185 CloseHandle(hReadOut);
186 CloseHandle(hWriteOut);
187 CloseHandle(hReadIn);
188 CloseHandle(hWriteIn);
189 safeCloseHandle(&hReadError);
190 safeCloseHandle(&hWriteError);
191 return -1;
194 AllowSetForegroundWindow(pi.dwProcessId);
195 WaitForInputIdle(pi.hProcess, 10000);
197 CloseHandle(hReadIn);
198 CloseHandle(hWriteOut);
199 if (commandHandle->errBuf) {
200 HANDLE asyncReadErrorThread;
201 CloseHandle(hWriteError);
202 commandHandle->err = hReadError;
203 asyncReadErrorThread = commmand_start_reading_thread(&commandHandle->err, commandHandle->errBuf);
204 if (!asyncReadErrorThread) {
205 CloseHandle(hReadOut);
206 CloseHandle(hWriteIn);
207 CloseHandle(hReadError);
208 return -1;
210 commandHandle->asyncReadErrorThread = asyncReadErrorThread;
213 commandHandle->pi = pi;
214 commandHandle->out = hReadOut;
215 commandHandle->in = hWriteIn;
216 commandHandle->running = TRUE;
217 return 0;
220 int command_read_stdout(COMMAND_HANDLE *commandHandle, char *buffer, size_t buf_size, size_t *bytes_read)
222 return command_read(commandHandle->out, buffer, buf_size, bytes_read);
225 int command_write(COMMAND_HANDLE *commandHandle, const char *buffer, size_t len)
227 size_t off = 0;
228 DWORD written = 0;
230 do {
231 if (!WriteFile(commandHandle->in, buffer + off, (DWORD)(len - off), &written, NULL)) {
232 giterr_set(GITERR_OS, "could not write data to external process");
233 return -1;
236 off += written;
237 } while (off < len);
239 return 0;
242 int command_write_gitbuf(COMMAND_HANDLE *commandHandle, const git_buf *buf)
244 return command_write(commandHandle, buf->ptr, buf->size);
247 void command_close_stdin(COMMAND_HANDLE *commandHandle)
249 safeCloseHandle(&commandHandle->in);
252 void command_close_stdout(COMMAND_HANDLE *commandHandle)
254 safeCloseHandle(&commandHandle->out);
257 DWORD command_close(COMMAND_HANDLE *commandHandle)
259 DWORD exitcode = MAXDWORD;
260 if (!commandHandle->running)
261 return exitcode;
263 commandHandle->running = FALSE;
265 command_close_stdin(commandHandle);
266 command_wait_stdout_reading_thread(commandHandle);
267 command_close_stdout(commandHandle);
269 CloseHandle(commandHandle->pi.hThread);
271 WaitForSingleObject(commandHandle->pi.hProcess, INFINITE);
273 if (commandHandle->errBuf)
274 command_wait_reading_thread(&commandHandle->asyncReadErrorThread);
276 GetExitCodeProcess(commandHandle->pi.hProcess, &exitcode);
277 CloseHandle(commandHandle->pi.hProcess);
279 return exitcode;