Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301/* * PROJECT: ReactOS Console Utilities Library * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Provides basic abstraction wrappers around CRT streams or * Win32 console API I/O functions, to deal with i18n + Unicode * related problems. * COPYRIGHT: Copyright 2017-2018 ReactOS Team * Copyright 2017-2018 Hermes Belusca-Maito */
/** * @file stream.c * @ingroup ConUtils * * @brief Console I/O streams **/
/* * Enable this define if you want to only use CRT functions to output * UNICODE stream to the console, as in the way explained by * http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html *//** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/// #define USE_CRT
/* FIXME: Temporary HACK before we cleanly support UNICODE functions */#define UNICODE#define _UNICODE
#ifdef USE_CRT#include <fcntl.h>#include <io.h>#endif /* USE_CRT */
#include <windef.h>#include <winbase.h>#include <winnls.h>// #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING#include <wincon.h> // Console APIs (only if kernel32 support included)#include <strsafe.h>
#include "conutils.h"#include "stream.h"#include "stream_private.h"
/* * Standard console streams, initialized by * calls to ConStreamInit/ConInitStdStreams. */#if 0 // FIXME!CON_STREAM StdStreams[3] ={ {0}, // StdIn {0}, // StdOut {0}, // StdErr};#elseCON_STREAM csStdIn;CON_STREAM csStdOut;CON_STREAM csStdErr;#endif
/* Stream translation modes */#ifdef USE_CRT/* Lookup table to convert CON_STREAM_MODE to CRT mode */static int ConToCRTMode[] ={ _O_BINARY, // Binary (untranslated) _O_TEXT, // AnsiText (translated) _O_WTEXT, // WideText (UTF16 with BOM; translated) _O_U16TEXT, // UTF16Text (UTF16 without BOM; translated) _O_U8TEXT, // UTF8Text (UTF8 without BOM; translated)};
/* * See http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html * and http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html * for more details. */
// NOTE1: May the translated mode be cached somehow?// NOTE2: We may also call IsConsoleHandle to directly set the mode to// _O_U16TEXT if it's ok??// NOTE3: _setmode returns the previous mode, or -1 if failure.#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \do { \ fflush((Stream)->fStream); \ if ((Mode) < ARRAYSIZE(ConToCRTMode)) \ _setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \ else \ _setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \} while(0)
#else /* defined(USE_CRT) */
/* * We set Stream->CodePage to INVALID_CP (== -1) to signal that the code page * is either not assigned (if the mode is Binary, WideText, or UTF16Text), or * is not cached (if the mode is AnsiText). In this latter case the code page * is resolved inside ConWrite. Finally, if the mode is UTF8Text, the code page * cache is always set to CP_UTF8. * The code page cache can be reset by an explicit call to CON_STREAM_SET_MODE * (i.e. by calling ConStreamSetMode, or by reinitializing the stream with * ConStreamInit(Ex)). * * NOTE: the reserved values are: 0 (CP_ACP), 1 (CP_OEMCP), 2 (CP_MACCP), * 3 (CP_THREAD_ACP), 42 (CP_SYMBOL), 65000 (CP_UTF7) and 65001 (CP_UTF8). */#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \do { \ (Stream)->Mode = (Mode); \\ if ((Mode) == AnsiText) \ (Stream)->CodePage = CacheCodePage; /* Possibly assigned */ \ else if ((Mode) == UTF8Text) \ (Stream)->CodePage = CP_UTF8; /* Fixed */ \ else /* Mode == Binary, WideText, UTF16Text */ \ (Stream)->CodePage = INVALID_CP; /* Not assigned (meaningless) */ \} while(0)
#endif /* defined(USE_CRT) */
BOOLConStreamInitEx( OUT PCON_STREAM Stream, IN PVOID Handle, IN CON_STREAM_MODE Mode, IN UINT CacheCodePage OPTIONAL, // IN CON_READ_FUNC ReadFunc OPTIONAL, IN CON_WRITE_FUNC WriteFunc OPTIONAL){ /* Parameters validation */ if (!Stream || !Handle || (Mode > UTF8Text)) return FALSE;
#ifdef USE_CRT
Stream->fStream = (FILE*)Handle;
#else
if ((HANDLE)Handle == INVALID_HANDLE_VALUE) return FALSE;
/* * As the user calls us by giving us an existing handle to attach on, * it is not our duty to close it if we are called again. The user * is responsible for having opened those handles, and is responsible * for closing them! */#if 0 /* Attempt to close the handle of the old stream */ if (/* Stream->IsInitialized && */ Stream->hHandle && Stream->hHandle != INVALID_HANDLE_VALUE) { CloseHandle(Stream->hHandle); }#endif
/* Initialize the stream critical section if not already done */ if (!Stream->IsInitialized) { InitializeCriticalSection/*AndSpinCount*/(&Stream->Lock /* , 4000 */); Stream->IsInitialized = TRUE; }
Stream->hHandle = (HANDLE)Handle; Stream->IsConsole = IsConsoleHandle(Stream->hHandle);
#endif /* defined(USE_CRT) */
/* Set the correct file translation mode */ CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
/* Use the default 'ConWrite' helper if nothing is specified */ Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite);
return TRUE;}
BOOLConStreamInit( OUT PCON_STREAM Stream, IN PVOID Handle, IN CON_STREAM_MODE Mode, IN UINT CacheCodePage OPTIONAL){ return ConStreamInitEx(Stream, Handle, Mode, CacheCodePage, ConWrite);}
BOOLConStreamSetMode( IN PCON_STREAM Stream, IN CON_STREAM_MODE Mode, IN UINT CacheCodePage OPTIONAL){ /* Parameters validation */ if (!Stream || (Mode > UTF8Text)) return FALSE;
#ifdef USE_CRT if (!Stream->fStream) return FALSE;#endif
/* Set the correct file translation mode */ CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); return TRUE;}
BOOLConStreamSetCacheCodePage( IN PCON_STREAM Stream, IN UINT CacheCodePage){#ifdef USE_CRT// FIXME!#warning The ConStreamSetCacheCodePage function does not make much sense with the CRT!#else CON_STREAM_MODE Mode;
/* Parameters validation */ if (!Stream) return FALSE;
/* * Keep the original stream mode but set the correct file code page * (will be reset only if Mode == AnsiText). */ Mode = Stream->Mode; CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);#endif return TRUE;}
HANDLEConStreamGetOSHandle( IN PCON_STREAM Stream){ /* Parameters validation */ if (!Stream) return INVALID_HANDLE_VALUE;
/* * See https://support.microsoft.com/kb/99173 * for more details. */
#ifdef USE_CRT if (!Stream->fStream) return INVALID_HANDLE_VALUE;
return (HANDLE)_get_osfhandle(_fileno(Stream->fStream));#else return Stream->hHandle;#endif}
BOOLConStreamSetOSHandle( IN PCON_STREAM Stream, IN HANDLE Handle){ /* Parameters validation */ if (!Stream) return FALSE;
/* * See https://support.microsoft.com/kb/99173 * for more details. */
#ifdef USE_CRT if (!Stream->fStream) return FALSE;
int fdOut = _open_osfhandle((intptr_t)Handle, _O_TEXT /* FIXME! */); FILE* fpOut = _fdopen(fdOut, "w"); *Stream->fStream = *fpOut; /// setvbuf(Stream->fStream, NULL, _IONBF, 0);
return TRUE;#else /* Flush the stream and reset its handle */ if (Stream->hHandle != INVALID_HANDLE_VALUE) FlushFileBuffers(Stream->hHandle);
Stream->hHandle = Handle; Stream->IsConsole = IsConsoleHandle(Stream->hHandle);
// NOTE: Mode reset??
return TRUE;#endif}
/* EOF */