runnerw handles spaces in command line
[idea/community.git] / native / runner / runnerw / runnerw.cpp
1 #include <windows.h>
2 #include <stdio.h>
3 #include <tlhelp32.h>
4 #include <iostream>
5 #include <string>
6
7 void PrintUsage() {
8         printf("Usage: runnerw.exe <app> <args>\n");
9         printf("where <app> is console application and <args> it's arguments.\n");
10         printf("\n");
11         printf(
12                         "Runner invokes console application as a process with inherited input and output streams.\n");
13         printf(
14                         "Input stream is scanned for presence of 2 char 255(IAC) and 243(BRK) sequence and generates Ctrl-Break event in that case.\n");
15         printf(
16                         "Also in case of all type of event(Ctrl-C, Close, Shutdown etc) Ctrl-Break event is generated.\n");
17
18         exit(0);
19 }
20
21 void ErrorMessage(char *str) {
22
23         LPVOID msg;
24
25         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
26                         NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
27                         (LPTSTR) &msg, 0, NULL);
28
29         printf("%s: %s\n", str, msg);
30         LocalFree(msg);
31 }
32
33 void CtrlBreak() {
34         if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0)) {
35                 ErrorMessage("GenerateConsoleCtrlEvent");
36         }
37 }
38
39 BOOL is_iac = FALSE;
40
41 char IAC = 5;
42 char BRK = 3;
43
44 BOOL Scan(char buf[], int count) {
45         for (int i = 0; i < count; i++) {
46                 if (is_iac) {
47                         if (buf[i] == BRK) {
48                                 CtrlBreak();
49                                 return TRUE;
50                         } else {
51                                 is_iac = FALSE;
52                         }
53                 }
54                 if (buf[i] == IAC) {
55                         is_iac = TRUE;
56                 }
57         }
58
59         return FALSE;
60 }
61
62 BOOL CtrlHandler(DWORD fdwCtrlType) {
63         switch (fdwCtrlType) {
64         case CTRL_C_EVENT:
65         case CTRL_CLOSE_EVENT:
66         case CTRL_LOGOFF_EVENT:
67         case CTRL_SHUTDOWN_EVENT:
68                 CtrlBreak();
69                 return (TRUE);
70         case CTRL_BREAK_EVENT:
71                 return FALSE;
72         default:
73                 return FALSE;
74         }
75 }
76
77 struct StdInThreadParams
78 {
79         HANDLE hEvent;
80         HANDLE write_stdin;
81 };
82
83 DWORD WINAPI StdInThread(void *param)
84 {
85         StdInThreadParams *threadParams = (StdInThreadParams *) param;
86         char buf[1];
87         memset(buf, 0, sizeof(buf));
88
89         HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
90         while(true)
91         {
92                 DWORD cbRead = 0;
93                 DWORD cbWrite = 0;
94
95                 char c;
96                 ReadFile(hStdin, &c, 1, &cbRead, NULL);
97                 if (cbRead > 0)
98                 {
99                         buf[0] = c;
100                         bool ctrlBroken = Scan(buf, 1);
101                         WriteFile(threadParams->write_stdin, buf, 1, &cbWrite, NULL);
102                         if (ctrlBroken)
103                         {
104                                 SetEvent(threadParams->hEvent);
105                                 break;
106                         }
107                 }
108         }
109         return 0;
110 }
111
112 int main(int argc, char * argv[]) {
113         if (argc < 2) {
114                 PrintUsage();
115         }
116
117         std::string app(argv[1]);
118         std::string args("");
119
120         for (int i = 2; i < argc; i++) {
121                 args += " ";
122                 if (strchr(argv [i], ' '))
123                 {
124                         args += "\"";
125                         args += argv[i];
126                         args += "\"";
127                 }
128                 else 
129                 {
130                         args += argv[i];
131                 }
132         }
133
134         if (app.length() == 0) {
135                 PrintUsage();
136         }
137
138         STARTUPINFO si;
139         SECURITY_ATTRIBUTES sa;
140         PROCESS_INFORMATION pi;
141
142         HANDLE newstdin, write_stdin;
143
144         sa.lpSecurityDescriptor = NULL;
145
146         sa.nLength = sizeof(SECURITY_ATTRIBUTES);
147         sa.bInheritHandle = true;
148
149         if (!CreatePipe(&newstdin, &write_stdin, &sa, 0)) {
150                 ErrorMessage("CreatePipe");
151                 exit(0);
152         }
153
154         GetStartupInfo(&si);
155
156         si.dwFlags = STARTF_USESTDHANDLES;
157         si.wShowWindow = SW_HIDE;
158         si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
159         si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
160         si.hStdInput = newstdin;
161
162         char* c_app = new char[app.size() + 1];
163         strcpy(c_app, app.c_str());
164
165         char* c_args = new char[args.size() + 1];
166         strcpy(c_args, args.c_str());
167
168         SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE);
169
170         if (!CreateProcess(c_app, // Application name
171                         c_args, // Application arguments
172                         NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &si, &pi)) {
173                 ErrorMessage("CreateProcess");
174                 CloseHandle(newstdin);
175                 CloseHandle(write_stdin);
176                 exit(0);
177         }
178
179         unsigned long exit = 0;
180         unsigned long b_read;
181         unsigned long avail;
182
183         HANDLE threadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
184
185         StdInThreadParams params;
186         params.hEvent = threadEvent;
187         params.write_stdin = write_stdin;
188
189         CreateThread(NULL, 0, &StdInThread, &params, 0, NULL);
190
191         HANDLE objects_to_wait[2];
192         objects_to_wait[0] = threadEvent;
193         objects_to_wait[1] = pi.hProcess;
194
195         while(true)
196         {
197                 int rc = WaitForMultipleObjects(2, objects_to_wait, FALSE, INFINITE);
198                 if (rc == WAIT_OBJECT_0 + 1)
199                 {
200                         break;
201                 }
202         }
203
204         GetExitCodeProcess(pi.hProcess, &exit);
205
206         CloseHandle(pi.hThread);
207         CloseHandle(pi.hProcess);
208         CloseHandle(newstdin);
209         CloseHandle(write_stdin);
210         return exit;
211 }