fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / desktop / win32 / source / guistdio / guistdio.inc
blobf918807ae9d5dcc3bd20a178d6434f33d5f4469c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
20 #define UNICODE
21 #define WIN32_LEAN_AND_MEAN
22 #ifdef _MSC_VER
23 #pragma warning(push,1) // disable warnings within system headers
24 #endif
25 #include <windows.h>
26 #ifdef _MSC_VER
27 #pragma warning(pop)
28 #endif
30 #define _UNICODE
31 #include <tchar.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <systools/win32/uwinapi.h>
37 #include <stdio.h>
38 #include <sal/macros.h>
40 #ifdef UNOPKG
42 DWORD passOutputToConsole(HANDLE readPipe, HANDLE console)
44     BYTE aBuffer[1024];
45     DWORD dwRead = 0;
46     HANDLE hReadPipe = readPipe;
47     DWORD dwWritten;
49     //Indicates that we read an odd number of bytes. That is, we only read half of the last
50     //wchar_t
51     bool bIncompleteWchar = false;
52     //fprintf, fwprintf will both send char data without the terminating zero.
53     //fwprintf converts the unicode string first.
54     //We expect here to receive unicode without the terminating zero.
55     //unopkg and the extension manager code MUST
56     //use dp_misc::writeConsole instead of using fprintf, etc.
58     DWORD dwToRead = sizeof(aBuffer);
59     BYTE * pBuffer = aBuffer;
60     while ( ReadFile( hReadPipe, pBuffer, dwToRead, &dwRead, NULL ) )
61     {
62         //If the previous ReadFile call read an odd number of bytes, then the last one was
63         //put at the front of the buffer. We increase the number of read bytes by one to reflect
64         //that one byte.
65         if (bIncompleteWchar)
66             dwRead++;
67         //We must make sure that only complete wchar_t|s are written. WriteConsolse takes
68         //the number of wchar_t|s as argument. ReadFile, however, reads bytes.
69         bIncompleteWchar = (dwRead % 2) ? true : false;
70         if (bIncompleteWchar)
71         {
72             //To test this case, give aBuffer a small odd size, e.g. aBuffer[3]
73             //The last byte, which is the incomplete wchar_t (half of it), will not be written.
74             (void) WriteConsoleW( console, aBuffer,
75                 (dwRead - 1) / 2, &dwWritten, NULL );
77             //Move the last byte to the front of the buffer, so that it is the start of the
78             //next string
79             aBuffer[0] = aBuffer[dwRead - 1];
81             //Make sure that ReadFile does not overwrite the first byte the next time
82             dwToRead = sizeof(aBuffer) - 1;
83             pBuffer = aBuffer + 1;
85         }
86         else
87         {   //We have read an even number of bytes. Therefore, we do not put the last incomplete
88             //wchar_t at the front of the buffer. We will use the complete buffer the next time
89             //when ReadFile is called.
90             dwToRead = sizeof(aBuffer);
91             pBuffer = aBuffer;
92             (void) WriteConsoleW( console,
93                 aBuffer, dwRead / 2, &dwWritten, NULL );
94         }
95     }
97     return 0;
100 #endif
102 #ifdef UNOPKG
103 DWORD WINAPI OutputThread( LPVOID pParam )
105     return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_OUTPUT_HANDLE ));
108 #else
109 DWORD WINAPI OutputThread( LPVOID pParam )
111     BYTE    aBuffer[256];
112     DWORD   dwRead = 0;
113     HANDLE  hReadPipe = (HANDLE)pParam;
114     while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
115     {
116         DWORD   dwWritten;
118         (void) WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
119     }
121     return 0;
123 #endif
125 // Thread that reads from child process standard error pipe
128 #ifdef UNOPKG
129 DWORD WINAPI ErrorThread( LPVOID pParam )
131     return passOutputToConsole((HANDLE)pParam, GetStdHandle( STD_ERROR_HANDLE ));
134 #else
135 DWORD WINAPI ErrorThread( LPVOID pParam )
137     BYTE    aBuffer[256];
138     DWORD   dwRead = 0;
139     HANDLE  hReadPipe = (HANDLE)pParam;
141     while ( ReadFile( hReadPipe, &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
142     {
143         DWORD   dwWritten;
145         (void) WriteFile( GetStdHandle( STD_ERROR_HANDLE ), aBuffer, dwRead, &dwWritten, NULL );
146     }
148     return 0;
150 #endif
152 // Thread that writes to child process standard input pipe
154 #ifdef UNOPKG
156 DWORD WINAPI InputThread( LPVOID pParam )
158     DWORD   dwRead = 0;
159     HANDLE  hWritePipe = (HANDLE)pParam;
161     //We need to read in the complete input until we encounter a new line before
162     //converting to Unicode. This is necessary because the input string can use
163     //characters of one, two, and more bytes. If the last character is not
164     //complete, then it will not be converted properly.
166     //Find out how a new line (0xd 0xa) looks like with the used code page.
167     //Characters may have one or multiple bytes and different byte ordering
168     //can be used (little and big endian);
169     int cNewLine = WideCharToMultiByte(
170         GetConsoleCP(), 0, L"\r\n", 2, NULL, 0, NULL, NULL);
171     char * mbBuff = new char[cNewLine];
172     WideCharToMultiByte(
173         GetConsoleCP(), 0, L"\r\n", 2, mbBuff, cNewLine, NULL, NULL);
175     const DWORD dwBufferSize = 256;
176     char* readBuf = (char*) malloc(dwBufferSize);
177     int readAll = 0;
178     DWORD curBufSize = dwBufferSize;
180     while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ),
181                       readBuf + readAll,
182                       curBufSize - readAll, &dwRead, NULL ) )
183     {
184         readAll += dwRead;
185         int lastBufSize = curBufSize;
186         //Grow the buffer if necessary
187         if (readAll > curBufSize * 0.7)
188         {
189             curBufSize *= 2;
190             readBuf = (char *) realloc(readBuf, curBufSize);
191         }
193         //If the buffer was filled completely then
194         //there could be more input coming. But if we read from the console
195         //and the console input fits exactly in the buffer, then the next
196         //ReadFile would block until the users presses return, etc.
197         //Therefore we check if last character is a new line.
198         //To test this, set dwBufferSize to 4 and enter "no". This should produce
199         //4 bytes with most code pages.
200         if ( readAll == lastBufSize
201              && memcmp(readBuf + lastBufSize - cNewLine, mbBuff, cNewLine) != 0)
202         {
203             //The buffer was completely filled and the last byte(s) are no
204             //new line, so there is more to come.
205             continue;
206         }
207         //Obtain the size of the buffer for the converted string.
208         int sizeWBuf = MultiByteToWideChar(
209             GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, NULL, 0);
211         wchar_t * wideBuf = new wchar_t[sizeWBuf];
213         //Do the conversion.
214         MultiByteToWideChar(
215             GetConsoleCP(), MB_PRECOMPOSED, readBuf, readAll, wideBuf, sizeWBuf);
217         DWORD   dwWritten;
218         (void)WriteFile( hWritePipe, wideBuf, sizeWBuf * 2, &dwWritten, NULL );
220         delete[] wideBuf;
221         readAll = 0;
222     }
223     delete[] mbBuff;
224     free(readBuf);
225     return 0;
227 #else
228 DWORD WINAPI InputThread( LPVOID pParam )
230     BYTE    aBuffer[256];
231     DWORD   dwRead = 0;
232     HANDLE  hWritePipe = (HANDLE)pParam;
234     while ( ReadFile( GetStdHandle( STD_INPUT_HANDLE ), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
235     {
236         DWORD dwWritten;
237         (void) WriteFile( hWritePipe, aBuffer, dwRead, &dwWritten, NULL );
238     }
240     return 0;
242 #endif
245 // Thread that waits until child process reached input idle
248 DWORD WINAPI WaitForUIThread( LPVOID pParam )
250 #ifndef UNOPKG
251     HANDLE  hProcess = (HANDLE)pParam;
253     if ( !_tgetenv( TEXT("UNOPKG") ) )
254         WaitForInputIdle( hProcess, INFINITE );
255 #else
256     (void) pParam;
257 #endif
259     return 0;
264 // Ctrl-Break handler that terminates the child process if Ctrl-C was pressed
267 HANDLE  hTargetProcess = INVALID_HANDLE_VALUE;
269 BOOL WINAPI CtrlBreakHandler(
270   DWORD   //  control signal type
273     TerminateProcess( hTargetProcess, 255 );
274     return TRUE;
282 #ifdef __MINGW32__
283 int main( int, char ** )
284 #else
285 int _tmain( int, _TCHAR ** )
286 #endif
288     TCHAR               szTargetFileName[MAX_PATH] = TEXT("");
289     STARTUPINFO         aStartupInfo;
290     PROCESS_INFORMATION aProcessInfo;
292     ZeroMemory( &aStartupInfo, sizeof(aStartupInfo) );
293     aStartupInfo.cb = sizeof(aStartupInfo);
294     aStartupInfo.dwFlags = STARTF_USESTDHANDLES;
296     // Create an output pipe where the write end is inheritable
298     HANDLE  hOutputRead, hOutputWrite;
300     if ( CreatePipe( &hOutputRead, &hOutputWrite, NULL, 0 ) )
301     {
302         HANDLE  hTemp;
304         DuplicateHandle( GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
305         CloseHandle( hOutputWrite );
306         hOutputWrite = hTemp;
308         aStartupInfo.hStdOutput = hOutputWrite;
309     }
311     // Create an error pipe where the write end is inheritable
313     HANDLE  hErrorRead, hErrorWrite;
315     if ( CreatePipe( &hErrorRead, &hErrorWrite, NULL, 0 ) )
316     {
317         HANDLE  hTemp;
319         DuplicateHandle( GetCurrentProcess(), hErrorWrite, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
320         CloseHandle( hErrorWrite );
321         hErrorWrite = hTemp;
323         aStartupInfo.hStdError = hErrorWrite;
324     }
326     // Create an input pipe where the read end is inheritable
328     HANDLE  hInputRead, hInputWrite;
330     if ( CreatePipe( &hInputRead, &hInputWrite, NULL, 0 ) )
331     {
332         HANDLE  hTemp;
334         DuplicateHandle( GetCurrentProcess(), hInputRead, GetCurrentProcess(), &hTemp, 0, TRUE, DUPLICATE_SAME_ACCESS );
335         CloseHandle( hInputRead );
336         hInputRead = hTemp;
338         aStartupInfo.hStdInput = hInputRead;
339     }
341     // Get image path with same name but with .exe extension
343     TCHAR               szModuleFileName[MAX_PATH];
345     GetModuleFileName( NULL, szModuleFileName, MAX_PATH );
346     _TCHAR  *lpLastDot = _tcsrchr( szModuleFileName, '.' );
347     if ( lpLastDot && 0 == _tcsicmp( lpLastDot, _T(".COM") ) )
348     {
349         size_t len = lpLastDot - szModuleFileName;
350         _tcsncpy( szTargetFileName, szModuleFileName, len );
351         _tcsncpy( szTargetFileName + len, _T(".EXE"), SAL_N_ELEMENTS(szTargetFileName) - len );
352     }
354     // Create process with same command line, environment and stdio handles which
355     // are directed to the created pipes
357     BOOL    fSuccess = CreateProcess(
358         szTargetFileName,
359         GetCommandLine(),
360         NULL,
361         NULL,
362         TRUE,
363         0,
364         NULL,
365         NULL,
366         &aStartupInfo,
367         &aProcessInfo );
369     if ( fSuccess )
370     {
371         // These pipe ends are inherited by the child process and no longer used
372         CloseHandle( hOutputWrite );
373         CloseHandle( hErrorWrite );
374         CloseHandle( hInputRead );
376         // Set the Ctrl-Break handler
377         hTargetProcess = aProcessInfo.hProcess;
378         SetConsoleCtrlHandler( CtrlBreakHandler, TRUE );
380         // Create threads that redirect remote pipe io to current process's console stdio
382         DWORD   dwOutputThreadId, dwErrorThreadId, dwInputThreadId;
384         HANDLE  hOutputThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hOutputRead, 0, &dwOutputThreadId );
385         HANDLE  hErrorThread = CreateThread( NULL, 0, OutputThread, (LPVOID)hErrorRead, 0, &dwErrorThreadId );
386         HANDLE  hInputThread = CreateThread( NULL, 0, InputThread, (LPVOID)hInputWrite, 0, &dwInputThreadId );
388         // Create thread that wait until child process entered input idle
390         DWORD   dwWaitForUIThreadId;
391         HANDLE  hWaitForUIThread = CreateThread( NULL, 0, WaitForUIThread, (LPVOID)aProcessInfo.hProcess, 0, &dwWaitForUIThreadId );
393         HANDLE  hObjects[] =
394             {
395                 hTargetProcess,
396                 hWaitForUIThread,
397                 hOutputThread,
398                 hErrorThread
399             };
401  #ifdef UNOPKG
402         WaitForMultipleObjects( SAL_N_ELEMENTS(hObjects), hObjects, TRUE, INFINITE );
403  #else
404         bool    bDetach = false;
405         int     nOpenPipes = 2;
406         do
407         {
408             DWORD dwWaitResult = WaitForMultipleObjects( SAL_N_ELEMENTS(hObjects), hObjects, FALSE, INFINITE );
410             switch ( dwWaitResult )
411             {
412             case WAIT_OBJECT_0: // The child process has terminated
413             case WAIT_OBJECT_0 + 1: // The child process entered input idle
414                 bDetach = true;
415                 break;
416             case WAIT_OBJECT_0 + 2: // The remote end of stdout pipe was closed
417             case WAIT_OBJECT_0 + 3: // The remote end of stderr pipe was closed
418                 bDetach = --nOpenPipes <= 0;
419                 break;
420             default: // Something went wrong
421                 bDetach = true;
422                 break;
423             }
424         } while( !bDetach );
426 #endif
428         CloseHandle( hOutputThread );
429         CloseHandle( hErrorThread );
430         CloseHandle( hInputThread );
431         CloseHandle( hWaitForUIThread );
433         DWORD   dwExitCode = 0;
434         GetExitCodeProcess( aProcessInfo.hProcess, &dwExitCode );
435         CloseHandle( aProcessInfo.hProcess );
436         CloseHandle( aProcessInfo.hThread );
438         return dwExitCode;
439     }
441     return -1;
444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */