73738faf3def8dca2d2c061eab4aaa6ad275d5a1
[reactos.git] / rosapps / applications / devutils / gdb2 / gdb2.cpp
1 /* $Id: gdb2.cpp 42968 2009-08-30 15:35:14Z sginsberg $
2 *
3 * gdb2 - gdb output splitter
4 *
5 * Copyright (C) 2000,2001 Nedko Arnaoudov <nedkohome@atia.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23 #include "ph.h"
24
25 #define GDB_INITIAL_COMMAND "gdb.exe"
26 #define PARAMS_SEPARATOR " "
27 #define PARAMS_SEPARATOR_LEN strlen(PARAMS_SEPARATOR);
28
29 void DisplayError(const char *pszAPI);
30 void ReadAndHandleOutput(HANDLE hPipeRead);
31 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
32 HANDLE hChildStdIn,
33 HANDLE hChildStdErr,
34 char *pchCommandLine);
35
36 DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);
37 HANDLE hChildProcess = NULL;
38 HANDLE hStdIn = NULL; // Handle to parents std input.
39 BOOL bRunThread = TRUE;
40
41 int main(int argc, char* argv[])
42 {
43 HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
44 HANDLE hInputWriteTmp,hInputRead,hInputWrite;
45 HANDLE hErrorWrite;
46 HANDLE hThread;
47 DWORD ThreadId;
48 SECURITY_ATTRIBUTES sa;
49
50 // Set up the security attributes struct.
51 sa.nLength= sizeof(SECURITY_ATTRIBUTES);
52 sa.lpSecurityDescriptor = NULL;
53 sa.bInheritHandle = TRUE;
54
55
56 // Create the child output pipe.
57 if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
58 DisplayError("CreatePipe");
59
60
61 // Create a duplicate of the output write handle for the std error
62 // write handle. This is necessary in case the child application
63 // closes one of its std output handles.
64 if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
65 GetCurrentProcess(),&hErrorWrite,0,
66 TRUE,DUPLICATE_SAME_ACCESS))
67 DisplayError("DuplicateHandle");
68
69
70 // Create the child input pipe.
71 if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
72 DisplayError("CreatePipe");
73
74
75 // Create new output read handle and the input write handles. Set
76 // the Properties to FALSE. Otherwise, the child inherits the
77 // properties and, as a result, non-closeable handles to the pipes
78 // are created.
79 if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
80 GetCurrentProcess(),
81 &hOutputRead, // Address of new handle.
82 0,FALSE, // Make it uninheritable.
83 DUPLICATE_SAME_ACCESS))
84 DisplayError("DupliateHandle");
85
86 if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
87 GetCurrentProcess(),
88 &hInputWrite, // Address of new handle.
89 0,FALSE, // Make it uninheritable.
90 DUPLICATE_SAME_ACCESS))
91 DisplayError("DupliateHandle");
92
93
94 // Close inheritable copies of the handles you do not want to be
95 // inherited.
96 if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle");
97 if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle");
98
99
100 // Get std input handle so you can close it and force the ReadFile to
101 // fail when you want the input thread to exit.
102 if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) ==
103 INVALID_HANDLE_VALUE )
104 DisplayError("GetStdHandle");
105
106 SetConsoleTitle("gdb control console");
107
108 size_t size = strlen(GDB_INITIAL_COMMAND)+PARAMS_SEPARATOR_LEN;
109
110 // get parameters length
111 for (int i = 1 ; i < argc ; i++)
112 size += strlen(argv[i])+PARAMS_SEPARATOR_LEN;
113
114 size++; // terminating null
115
116 char *pszCommandLine = new char [size];
117 if (!pszCommandLine)
118 {
119 printf("out of memory.\n");
120 return -1;
121 }
122
123 strcpy(pszCommandLine,GDB_INITIAL_COMMAND);
124 for (int i = 1 ; i < argc ; i++)
125 {
126 strcat(pszCommandLine,PARAMS_SEPARATOR);
127 strcat(pszCommandLine,argv[i]);
128 }
129
130 PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite,pszCommandLine);
131
132
133 // Close pipe handles (do not continue to modify the parent).
134 // You need to make sure that no handles to the write end of the
135 // output pipe are maintained in this process or else the pipe will
136 // not close when the child process exits and the ReadFile will hang.
137 if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle");
138 if (!CloseHandle(hInputRead )) DisplayError("CloseHandle");
139 if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle");
140
141
142 // Launch the thread that gets the input and sends it to the child.
143 hThread = CreateThread(NULL,0,GetAndSendInputThread,
144 (LPVOID)hInputWrite,0,&ThreadId);
145 if (hThread == NULL) DisplayError("CreateThread");
146
147
148 // Read the child's output.
149 ReadAndHandleOutput(hOutputRead);
150 // Redirection is complete
151
152
153 // Force the read on the input to return by closing the stdin handle.
154 if (!CloseHandle(hStdIn)) DisplayError("CloseHandle");
155
156
157 // Tell the thread to exit and wait for thread to die.
158 bRunThread = FALSE;
159
160 if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
161 DisplayError("WaitForSingleObject");
162
163 if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle");
164 if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle");
165
166 return 0;
167 }
168
169 ///////////////////////////////////////////////////////////////////////
170 // PrepAndLaunchRedirectedChild
171 // Sets up STARTUPINFO structure, and launches redirected child.
172 ///////////////////////////////////////////////////////////////////////
173 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
174 HANDLE hChildStdIn,
175 HANDLE hChildStdErr,
176 char *pchCommandLine)
177 {
178 PROCESS_INFORMATION pi;
179 STARTUPINFO si;
180 static CHAR Title[] = "debugged program console";
181
182 // Set up the start up info struct.
183 ZeroMemory(&si,sizeof(STARTUPINFO));
184 si.cb = sizeof(STARTUPINFO);
185 si.dwFlags = STARTF_USESTDHANDLES;
186 si.hStdOutput = hChildStdOut;
187 si.hStdInput = hChildStdIn;
188 si.hStdError = hChildStdErr;
189 si.lpTitle = Title;
190 // Use this if you want to hide the child:
191 // si.wShowWindow = SW_HIDE;
192 // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
193 // use the wShowWindow flags.
194
195
196 // Launch the process that you want to redirect (in this case,
197 // Child.exe). Make sure Child.exe is in the same directory as
198 // redirect.c launch redirect from a command line to prevent location
199 // confusion.
200 if (!CreateProcess(NULL,pchCommandLine,NULL,NULL,TRUE,
201 CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
202 DisplayError("CreateProcess");
203
204
205 // Set global child process handle to cause threads to exit.
206 hChildProcess = pi.hProcess;
207
208
209 // Close any unnecessary handles.
210 if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
211 }
212
213 ///////////////////////////////////////////////////////////////////////
214 // ReadAndHandleOutput
215 // Monitors handle for input. Exits when child exits or pipe breaks.
216 ///////////////////////////////////////////////////////////////////////
217 void ReadAndHandleOutput(HANDLE hPipeRead)
218 {
219 CHAR lpBuffer[256];
220 DWORD nBytesRead;
221 DWORD nCharsWritten;
222
223 while(TRUE)
224 {
225 if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
226 &nBytesRead,NULL) || !nBytesRead)
227 {
228 if (GetLastError() == ERROR_BROKEN_PIPE)
229 break; // pipe done - normal exit path.
230 else
231 DisplayError("ReadFile"); // Something bad happened.
232 }
233
234 // Display the character read on the screen.
235 if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
236 nBytesRead,&nCharsWritten,NULL))
237 DisplayError("WriteConsole");
238 }
239 }
240
241
242 ///////////////////////////////////////////////////////////////////////
243 // GetAndSendInputThread
244 // Thread procedure that monitors the console for input and sends input
245 // to the child process through the input pipe.
246 // This thread ends when the child application exits.
247 ///////////////////////////////////////////////////////////////////////
248 DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
249 {
250 CHAR read_buff[256];
251 DWORD nBytesRead,nBytesWrote;
252 HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
253
254 // Get input from our console and send it to child through the pipe.
255 while (bRunThread)
256 {
257 if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL))
258 DisplayError("ReadConsole");
259
260 read_buff[nBytesRead] = '\0'; // Follow input with a NULL.
261
262 if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
263 {
264 if (GetLastError() == ERROR_NO_DATA)
265 break; // Pipe was closed (normal exit path).
266 else
267 DisplayError("WriteFile");
268 }
269 }
270
271 return 1;
272 }
273
274 ///////////////////////////////////////////////////////////////////////
275 // DisplayError
276 // Displays the error number and corresponding message.
277 ///////////////////////////////////////////////////////////////////////
278 void DisplayError(const char *pszAPI)
279 {
280 LPVOID lpvMessageBuffer;
281 CHAR szPrintBuffer[512];
282 DWORD nCharsWritten;
283
284 FormatMessage(
285 FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
286 NULL, GetLastError(),
287 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
288 (LPTSTR)&lpvMessageBuffer, 0, NULL);
289
290 wsprintf(szPrintBuffer,
291 "ERROR: API = %s.\n error code = %d.\n message = %s.\n",
292 pszAPI, GetLastError(), (char *)lpvMessageBuffer);
293
294 WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer,
295 lstrlen(szPrintBuffer),&nCharsWritten,NULL);
296
297 LocalFree(lpvMessageBuffer);
298 ExitProcess(GetLastError());
299 }