1/**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements.  See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership.  The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied.  See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24#define UNICODE
25#define WIN32_LEAN_AND_MEAN
26#ifdef _MSC_VER
27#pragma warning(push,1) // disable warnings within system headers
28#endif
29#include <windows.h>
30#ifdef _MSC_VER
31#pragma warning(pop)
32#endif
33
34#define _UNICODE
35#include <tchar.h>
36
37#include <string.h>
38#include <stdlib.h>
39#include <systools/win32/uwinapi.h>
40
41#include <stdio.h>
42
43#ifdef UNOPKG
44
45DWORD passOutputToConsole(HANDLE readPipe, HANDLE console)
46{
47	BYTE aBuffer[1024];
48	DWORD dwRead = 0;
49	HANDLE hReadPipe = readPipe;
50	BOOL fSuccess;
51	DWORD dwWritten;
52
53	//Indicates that we read an odd number of bytes. That is, we only read half of the last
54	//wchar_t
55	bool bIncompleteWchar = false;
56	//fprintf, fwprintf will both send char data without the terminating zero.
57	//fwprintf converts the unicode string first.
58	//We expect here to receive unicode without the terminating zero.
59	//unopkg and the extension manager code MUST
60	//use dp_misc::writeConsole instead of using fprintf, etc.
61
62	DWORD dwToRead = sizeof(aBuffer);
63	BYTE * pBuffer = aBuffer;
64	while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) )
65	{
66		//If the previous ReadFile call read an odd number of bytes, then the last one was
67		//put at the front of the buffer. We increase the number of read bytes by one to reflect
68		//that one byte.
69		if (bIncompleteWchar)
70			dwRead++;
71		//We must make sure that only complete wchar_t|s are written. WriteConsole takes
72		//the number of wchar_t|s as argument. ReadFile, however, reads bytes.
73		bIncompleteWchar = dwRead % 2 ? true : false;
74		if (bIncompleteWchar)
75		{
76			//To test this case, give aBuffer a small odd size, e.g. aBuffer[3]
77			//The last byte, which is the incomplete wchar_t (half of it), will not be written.
78			fSuccess = WriteConsoleW( console, aBuffer,
79				(dwRead - 1) / 2, &dwWritten, NULL );
80
81			//Move the last byte to the front of the buffer, so that it is the start of the
82			//next string
83			aBuffer[0] = aBuffer[dwRead - 1];
84
85			//Make sure that ReadFile does not overwrite the first byte the next time
86			dwToRead = sizeof(aBuffer) - 1;
87			pBuffer = aBuffer + 1;
88
89		}
90		else
91		{	//We have read an even number of bytes. Therefore, we do not put the last incomplete
92			//wchar_t at the front of the buffer. We will use the complete buffer the next time
93			//when ReadFile is called.
94			dwToRead = sizeof(aBuffer);
95			pBuffer = aBuffer;
96			fSuccess = WriteConsoleW( console,
97				aBuffer, dwRead / 2, &dwWritten, NULL );
98		}
99	}
100
101	return 0;
102}
103
104#endif
105
106#ifdef UNOPKG
107DWORD WINAPI OutputThread( LPVOID pParam )
108{
109	return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE ));
110}
111
112#else
113DWORD WINAPI OutputThread( LPVOID pParam )
114{
115	BYTE	aBuffer[256];
116	DWORD	dwRead = 0;
117	HANDLE	hReadPipe = (HANDLE)pParam;
118	while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
119	{
120		BOOL	fSuccess;
121		DWORD	dwWritten;
122
123		fSuccess = WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
124	}
125
126	return 0;
127}
128#endif
129//---------------------------------------------------------------------------
130// Thread that reads from child process standard error pipe
131//---------------------------------------------------------------------------
132
133#ifdef UNOPKG
134DWORD WINAPI ErrorThread( LPVOID pParam )
135{
136	return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE ));
137}
138
139#else
140DWORD WINAPI ErrorThread( LPVOID pParam )
141{
142	BYTE	aBuffer[256];
143	DWORD	dwRead = 0;
144	HANDLE	hReadPipe = (HANDLE)pParam;
145
146	while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
147	{
148		BOOL	fSuccess;
149		DWORD	dwWritten;
150
151		fSuccess = WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
152	}
153
154	return 0;
155}
156#endif
157//---------------------------------------------------------------------------
158// Thread that writes to child process standard input pipe
159//---------------------------------------------------------------------------
160#ifdef UNOPKG
161
162DWORD WINAPI InputThread( LPVOID pParam )
163{
164	DWORD	dwRead = 0;
165	HANDLE	hWritePipe = (HANDLE)pParam;
166
167    //We need to read in the complete input until we encounter a new line before
168    //converting to Unicode. This is necessary because the input string can use
169    //characters of one, two, and more bytes. If the last character is not
170    //complete, then it will not be converted properly.
171
172    //Find out how a new line (0xd 0xa) looks like with the used code page.
173    //Characters may have one or multiple bytes and different byte ordering
174    //can be used (little and big endian);
175    int cNewLine = WideCharToMultiByte(
176        GetConsoleCP(), 0, L"\r\n", 2, NULL, 0, NULL, NULL);
177    char * mbBuff = new char[cNewLine];
178    WideCharToMultiByte(
179        GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL);
180
181    const size_t dwBufferSize = 256;
182    char* readBuf = (char*) malloc(dwBufferSize);
183    int readAll = 0;
184    size_t curBufSize = dwBufferSize;
185
186    while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ),
187                      readBuf + readAll,
188                      curBufSize - readAll, &dwRead, NULL ) )
189	{
190        readAll += dwRead;
191        int lastBufSize = curBufSize;
192        //Grow the buffer if necessary
193        if (readAll > curBufSize * 0.7)
194        {
195            curBufSize *= 2;
196            readBuf = (char *) realloc(readBuf, curBufSize);
197        }
198
199        //If the buffer was filled completely then
200        //there could be more input coming. But if we read from the console
201        //and the console input fits exactly in the buffer, then the next
202        //ReadFile would block until the user presses return, etc.
203        //Therefor we check if last character is a new line.
204        //To test this, set dwBufferSize to 4 and enter "no". This should produce
205        //4 bytes with most code pages.
206        if ( readAll == lastBufSize
207             && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0)
208        {
209            //The buffer was completely filled and the last byte(s) are no
210            //new line, so there is more to come.
211            continue;
212        }
213        //Obtain the size of the buffer for the converted string.
214        int sizeWBuf = MultiByteToWideChar(
215            GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, NULL, 0);
216
217        wchar_t * wideBuf = new wchar_t[sizeWBuf];
218
219        //Do the conversion.
220        MultiByteToWideChar(
221            GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf);
222
223        BOOL	fSuccess;
224		DWORD	dwWritten;
225        fSuccess = WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL );
226        delete[] wideBuf;
227        readAll = 0;
228	}
229    delete[] mbBuff;
230    free(readBuf);
231	return 0;
232}
233#else
234DWORD WINAPI InputThread( LPVOID pParam )
235{
236	BYTE	aBuffer[256];
237	DWORD	dwRead = 0;
238	HANDLE	hWritePipe = (HANDLE)pParam;
239
240	while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
241	{
242		BOOL	fSuccess;
243		DWORD	dwWritten;
244
245		fSuccess = WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL );
246	}
247
248	return 0;
249}
250#endif
251
252//---------------------------------------------------------------------------
253// Thread that waits until child process reached input idle
254//---------------------------------------------------------------------------
255
256DWORD WINAPI WaitForUIThread( LPVOID pParam )
257{
258	HANDLE	hProcess = (HANDLE)pParam;
259
260#ifndef UNOPKG
261	if ( !_tgetenv( TEXT("UNOPKG") ) )
262		WaitForInputIdle( hProcess, INFINITE );
263#endif
264
265	return 0;
266}
267
268
269//---------------------------------------------------------------------------
270// Ctrl-Break handler that terminates the child process if Ctrl-C was pressed
271//---------------------------------------------------------------------------
272
273HANDLE	hTargetProcess = INVALID_HANDLE_VALUE;
274
275BOOL WINAPI CtrlBreakHandler(
276  DWORD   // control signal type
277)
278{
279	TerminateProcess( hTargetProcess, 255 );
280	return TRUE;
281}
282
283
284//---------------------------------------------------------------------------
285
286//---------------------------------------------------------------------------
287
288#ifdef __MINGW32__
289int main( int, char ** )
290#else
291int _tmain( int, _TCHAR ** )
292#endif
293{
294	TCHAR				szTargetFileName[MAX_PATH] = TEXT("");
295	STARTUPINFO			aStartupInfo;
296	PROCESS_INFORMATION	aProcessInfo;
297
298	ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
299	aStartupInfo.cb = sizeof(aStartupInfo);
300	aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
301
302	// Create an output pipe where the write end is inheritable
303
304	HANDLE	hOutputRead, hOutputWrite;
305
306	if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) )
307	{
308		HANDLE	hTemp;
309
310		DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
311		CloseHandle( hOutputWrite );
312		hOutputWrite = hTemp;
313
314		aStartupInfo.hStdOutput = hOutputWrite;
315	}
316
317	// Create an error pipe where the write end is inheritable
318
319	HANDLE	hErrorRead, hErrorWrite;
320
321	if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) )
322	{
323		HANDLE	hTemp;
324
325		DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
326		CloseHandle( hErrorWrite );
327		hErrorWrite = hTemp;
328
329		aStartupInfo.hStdError = hErrorWrite;
330	}
331
332	// Create an input pipe where the read end is inheritable
333
334	HANDLE	hInputRead, hInputWrite;
335
336	if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) )
337	{
338		HANDLE	hTemp;
339
340		DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
341		CloseHandle( hInputRead );
342		hInputRead = hTemp;
343
344		aStartupInfo.hStdInput = hInputRead;
345	}
346
347	// Get image path with same name but with .exe extension
348
349	TCHAR				szModuleFileName[MAX_PATH];
350
351	GetModuleFileName( NULL, szModuleFileName, MAX_PATH );
352	_TCHAR	*lpLastDot = _tcsrchr( szModuleFileName, '.' );
353	if ( lpLastDot && 0 == _tcsicmp( lpLastDot, _T(".COM") ) )
354	{
355		size_t len = lpLastDot - szModuleFileName;
356		_tcsncpy( szTargetFileName, szModuleFileName, len );
357		_tcsncpy( szTargetFileName + len, _T(".EXE"), sizeof(szTargetFileName)/sizeof(szTargetFileName[0]) - len );
358	}
359
360	// Create process with same command line, environment and stdio handles which
361	// are directed to the created pipes
362
363	BOOL	fSuccess = CreateProcess(
364		szTargetFileName,
365		GetCommandLine(),
366		NULL,
367		NULL,
368		TRUE,
369		0,
370		NULL,
371		NULL,
372		&aStartupInfo,
373		&aProcessInfo );
374
375	if ( fSuccess )
376	{
377		// These pipe ends are inherited by the child process and no longer used
378		CloseHandle( hOutputWrite );
379		CloseHandle( hErrorWrite );
380		CloseHandle( hInputRead );
381
382		// Set the Ctrl-Break handler
383		hTargetProcess = aProcessInfo.hProcess;
384		SetConsoleCtrlHandler( CtrlBreakHandler, TRUE );
385
386		// Create threads that redirect remote pipe io to current process's console stdio
387
388		DWORD	dwOutputThreadId, dwErrorThreadId, dwInputThreadId;
389
390		HANDLE	hOutputThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hOutputRead, 0, &dwOutputThreadId );
391		HANDLE	hErrorThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hErrorRead, 0, &dwErrorThreadId );
392		HANDLE	hInputThread = CreateThread( NULL, 0, InputThread, (LPVOID)hInputWrite, 0, &dwInputThreadId );
393
394		// Create thread that wait until child process entered input idle
395
396		DWORD	dwWaitForUIThreadId;
397		HANDLE	hWaitForUIThread = CreateThread( NULL, 0, WaitForUIThread, (LPVOID)aProcessInfo.hProcess, 0, &dwWaitForUIThreadId );
398
399		DWORD	dwWaitResult;
400		HANDLE	hObjects[] =
401			{
402				hTargetProcess,
403				hWaitForUIThread,
404				hOutputThread,
405				hErrorThread
406			};
407
408 #ifdef UNOPKG
409		dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, TRUE, INFINITE );
410 #else
411		bool	bDetach = false;
412		int		nOpenPipes = 2;
413		do
414		{
415			dwWaitResult = WaitForMultipleObjects( elementsof(hObjects), hObjects, FALSE, INFINITE );
416
417			switch ( dwWaitResult )
418			{
419			case WAIT_OBJECT_0:	// The child process has terminated
420			case WAIT_OBJECT_0 + 1: // The child process entered input idle
421				bDetach = true;
422				break;
423			case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed
424			case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed
425				bDetach = --nOpenPipes <= 0;
426				break;
427			default: // Something went wrong
428				bDetach = true;
429				break;
430			}
431		} while( !bDetach );
432
433#endif
434
435		CloseHandle( hOutputThread );
436		CloseHandle( hErrorThread );
437		CloseHandle( hInputThread );
438		CloseHandle( hWaitForUIThread );
439
440		DWORD	dwExitCode = 0;
441		GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode );
442		CloseHandle( aProcessInfo.hProcess );
443		CloseHandle( aProcessInfo.hThread );
444
445		return dwExitCode;
446	}
447
448	return -1;
449}
450