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