Merge 15268:15329 from trunk
[reactos.git] / rosapps / devutils / gdb2 / gdb2.cpp
1 /* $Id: gdb2.cpp,v 1.1 2001/04/15 23:42:07 narnaoud Exp $
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(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
181 // Set up the start up info struct.
182 ZeroMemory(&si,sizeof(STARTUPINFO));
183 si.cb = sizeof(STARTUPINFO);
184 si.dwFlags = STARTF_USESTDHANDLES;
185 si.hStdOutput = hChildStdOut;
186 si.hStdInput = hChildStdIn;
187 si.hStdError = hChildStdErr;
188 si.lpTitle = "debugged program console";
189 // Use this if you want to hide the child:
190 // si.wShowWindow = SW_HIDE;
191 // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
192 // use the wShowWindow flags.
193
194
195 // Launch the process that you want to redirect (in this case,
196 // Child.exe). Make sure Child.exe is in the same directory as
197 // redirect.c launch redirect from a command line to prevent location
198 // confusion.
199 if (!CreateProcess(NULL,pchCommandLine,NULL,NULL,TRUE,
200 CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
201 DisplayError("CreateProcess");
202
203
204 // Set global child process handle to cause threads to exit.
205 hChildProcess = pi.hProcess;
206
207
208 // Close any unnecessary handles.
209 if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
210 }
211
212 ///////////////////////////////////////////////////////////////////////
213 // ReadAndHandleOutput
214 // Monitors handle for input. Exits when child exits or pipe breaks.
215 ///////////////////////////////////////////////////////////////////////
216 void ReadAndHandleOutput(HANDLE hPipeRead)
217 {
218 CHAR lpBuffer[256];
219 DWORD nBytesRead;
220 DWORD nCharsWritten;
221
222 while(TRUE)
223 {
224 if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
225 &nBytesRead,NULL) || !nBytesRead)
226 {
227 if (GetLastError() == ERROR_BROKEN_PIPE)
228 break; // pipe done - normal exit path.
229 else
230 DisplayError("ReadFile"); // Something bad happened.
231 }
232
233 // Display the character read on the screen.
234 if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
235 nBytesRead,&nCharsWritten,NULL))
236 DisplayError("WriteConsole");
237 }
238 }
239
240
241 ///////////////////////////////////////////////////////////////////////
242 // GetAndSendInputThread
243 // Thread procedure that monitors the console for input and sends input
244 // to the child process through the input pipe.
245 // This thread ends when the child application exits.
246 ///////////////////////////////////////////////////////////////////////
247 DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
248 {
249 CHAR read_buff[256];
250 DWORD nBytesRead,nBytesWrote;
251 HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
252
253 // Get input from our console and send it to child through the pipe.
254 while (bRunThread)
255 {
256 if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL))
257 DisplayError("ReadConsole");
258
259 read_buff[nBytesRead] = '\0'; // Follow input with a NULL.
260
261 if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
262 {
263 if (GetLastError() == ERROR_NO_DATA)
264 break; // Pipe was closed (normal exit path).
265 else
266 DisplayError("WriteFile");
267 }
268 }
269
270 return 1;
271 }
272
273 ///////////////////////////////////////////////////////////////////////
274 // DisplayError
275 // Displays the error number and corresponding message.
276 ///////////////////////////////////////////////////////////////////////
277 void DisplayError(char *pszAPI)
278 {
279 LPVOID lpvMessageBuffer;
280 CHAR szPrintBuffer[512];
281 DWORD nCharsWritten;
282
283 FormatMessage(
284 FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
285 NULL, GetLastError(),
286 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
287 (LPTSTR)&lpvMessageBuffer, 0, NULL);
288
289 wsprintf(szPrintBuffer,
290 "ERROR: API = %s.\n error code = %d.\n message = %s.\n",
291 pszAPI, GetLastError(), (char *)lpvMessageBuffer);
292
293 WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer,
294 lstrlen(szPrintBuffer),&nCharsWritten,NULL);
295
296 LocalFree(lpvMessageBuffer);
297 ExitProcess(GetLastError());
298 }