Reactos
at master 300 lines 7.9 kB view raw
1/* 2 * PROJECT: ReactOS Console Utilities Library 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Provides basic abstraction wrappers around CRT streams or 5 * Win32 console API I/O functions, to deal with i18n + Unicode 6 * related problems. 7 * COPYRIGHT: Copyright 2017-2018 ReactOS Team 8 * Copyright 2017-2018 Hermes Belusca-Maito 9 */ 10 11/** 12 * @file stream.c 13 * @ingroup ConUtils 14 * 15 * @brief Console I/O streams 16 **/ 17 18/* 19 * Enable this define if you want to only use CRT functions to output 20 * UNICODE stream to the console, as in the way explained by 21 * http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html 22 */ 23/** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/ 24// #define USE_CRT 25 26/* FIXME: Temporary HACK before we cleanly support UNICODE functions */ 27#define UNICODE 28#define _UNICODE 29 30#ifdef USE_CRT 31#include <fcntl.h> 32#include <io.h> 33#endif /* USE_CRT */ 34 35#include <windef.h> 36#include <winbase.h> 37#include <winnls.h> 38// #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING 39#include <wincon.h> // Console APIs (only if kernel32 support included) 40#include <strsafe.h> 41 42#include "conutils.h" 43#include "stream.h" 44#include "stream_private.h" 45 46 47/* 48 * Standard console streams, initialized by 49 * calls to ConStreamInit/ConInitStdStreams. 50 */ 51#if 0 // FIXME! 52CON_STREAM StdStreams[3] = 53{ 54 {0}, // StdIn 55 {0}, // StdOut 56 {0}, // StdErr 57}; 58#else 59CON_STREAM csStdIn; 60CON_STREAM csStdOut; 61CON_STREAM csStdErr; 62#endif 63 64 65/* Stream translation modes */ 66#ifdef USE_CRT 67/* Lookup table to convert CON_STREAM_MODE to CRT mode */ 68static int ConToCRTMode[] = 69{ 70 _O_BINARY, // Binary (untranslated) 71 _O_TEXT, // AnsiText (translated) 72 _O_WTEXT, // WideText (UTF16 with BOM; translated) 73 _O_U16TEXT, // UTF16Text (UTF16 without BOM; translated) 74 _O_U8TEXT, // UTF8Text (UTF8 without BOM; translated) 75}; 76 77/* 78 * See http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html 79 * and http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html 80 * for more details. 81 */ 82 83// NOTE1: May the translated mode be cached somehow? 84// NOTE2: We may also call IsConsoleHandle to directly set the mode to 85// _O_U16TEXT if it's ok?? 86// NOTE3: _setmode returns the previous mode, or -1 if failure. 87#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \ 88do { \ 89 fflush((Stream)->fStream); \ 90 if ((Mode) < ARRAYSIZE(ConToCRTMode)) \ 91 _setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \ 92 else \ 93 _setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \ 94} while(0) 95 96#else /* defined(USE_CRT) */ 97 98/* 99 * We set Stream->CodePage to INVALID_CP (== -1) to signal that the code page 100 * is either not assigned (if the mode is Binary, WideText, or UTF16Text), or 101 * is not cached (if the mode is AnsiText). In this latter case the code page 102 * is resolved inside ConWrite. Finally, if the mode is UTF8Text, the code page 103 * cache is always set to CP_UTF8. 104 * The code page cache can be reset by an explicit call to CON_STREAM_SET_MODE 105 * (i.e. by calling ConStreamSetMode, or by reinitializing the stream with 106 * ConStreamInit(Ex)). 107 * 108 * NOTE: the reserved values are: 0 (CP_ACP), 1 (CP_OEMCP), 2 (CP_MACCP), 109 * 3 (CP_THREAD_ACP), 42 (CP_SYMBOL), 65000 (CP_UTF7) and 65001 (CP_UTF8). 110 */ 111#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \ 112do { \ 113 (Stream)->Mode = (Mode); \ 114\ 115 if ((Mode) == AnsiText) \ 116 (Stream)->CodePage = CacheCodePage; /* Possibly assigned */ \ 117 else if ((Mode) == UTF8Text) \ 118 (Stream)->CodePage = CP_UTF8; /* Fixed */ \ 119 else /* Mode == Binary, WideText, UTF16Text */ \ 120 (Stream)->CodePage = INVALID_CP; /* Not assigned (meaningless) */ \ 121} while(0) 122 123#endif /* defined(USE_CRT) */ 124 125 126BOOL 127ConStreamInitEx( 128 OUT PCON_STREAM Stream, 129 IN PVOID Handle, 130 IN CON_STREAM_MODE Mode, 131 IN UINT CacheCodePage OPTIONAL, 132 // IN CON_READ_FUNC ReadFunc OPTIONAL, 133 IN CON_WRITE_FUNC WriteFunc OPTIONAL) 134{ 135 /* Parameters validation */ 136 if (!Stream || !Handle || (Mode > UTF8Text)) 137 return FALSE; 138 139#ifdef USE_CRT 140 141 Stream->fStream = (FILE*)Handle; 142 143#else 144 145 if ((HANDLE)Handle == INVALID_HANDLE_VALUE) 146 return FALSE; 147 148 /* 149 * As the user calls us by giving us an existing handle to attach on, 150 * it is not our duty to close it if we are called again. The user 151 * is responsible for having opened those handles, and is responsible 152 * for closing them! 153 */ 154#if 0 155 /* Attempt to close the handle of the old stream */ 156 if (/* Stream->IsInitialized && */ Stream->hHandle && 157 Stream->hHandle != INVALID_HANDLE_VALUE) 158 { 159 CloseHandle(Stream->hHandle); 160 } 161#endif 162 163 /* Initialize the stream critical section if not already done */ 164 if (!Stream->IsInitialized) 165 { 166 InitializeCriticalSection/*AndSpinCount*/(&Stream->Lock /* , 4000 */); 167 Stream->IsInitialized = TRUE; 168 } 169 170 Stream->hHandle = (HANDLE)Handle; 171 Stream->IsConsole = IsConsoleHandle(Stream->hHandle); 172 173#endif /* defined(USE_CRT) */ 174 175 /* Set the correct file translation mode */ 176 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 177 178 /* Use the default 'ConWrite' helper if nothing is specified */ 179 Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite); 180 181 return TRUE; 182} 183 184BOOL 185ConStreamInit( 186 OUT PCON_STREAM Stream, 187 IN PVOID Handle, 188 IN CON_STREAM_MODE Mode, 189 IN UINT CacheCodePage OPTIONAL) 190{ 191 return ConStreamInitEx(Stream, Handle, Mode, CacheCodePage, ConWrite); 192} 193 194BOOL 195ConStreamSetMode( 196 IN PCON_STREAM Stream, 197 IN CON_STREAM_MODE Mode, 198 IN UINT CacheCodePage OPTIONAL) 199{ 200 /* Parameters validation */ 201 if (!Stream || (Mode > UTF8Text)) 202 return FALSE; 203 204#ifdef USE_CRT 205 if (!Stream->fStream) 206 return FALSE; 207#endif 208 209 /* Set the correct file translation mode */ 210 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 211 return TRUE; 212} 213 214BOOL 215ConStreamSetCacheCodePage( 216 IN PCON_STREAM Stream, 217 IN UINT CacheCodePage) 218{ 219#ifdef USE_CRT 220// FIXME! 221#warning The ConStreamSetCacheCodePage function does not make much sense with the CRT! 222#else 223 CON_STREAM_MODE Mode; 224 225 /* Parameters validation */ 226 if (!Stream) 227 return FALSE; 228 229 /* 230 * Keep the original stream mode but set the correct file code page 231 * (will be reset only if Mode == AnsiText). 232 */ 233 Mode = Stream->Mode; 234 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 235#endif 236 return TRUE; 237} 238 239HANDLE 240ConStreamGetOSHandle( 241 IN PCON_STREAM Stream) 242{ 243 /* Parameters validation */ 244 if (!Stream) 245 return INVALID_HANDLE_VALUE; 246 247 /* 248 * See https://support.microsoft.com/kb/99173 249 * for more details. 250 */ 251 252#ifdef USE_CRT 253 if (!Stream->fStream) 254 return INVALID_HANDLE_VALUE; 255 256 return (HANDLE)_get_osfhandle(_fileno(Stream->fStream)); 257#else 258 return Stream->hHandle; 259#endif 260} 261 262BOOL 263ConStreamSetOSHandle( 264 IN PCON_STREAM Stream, 265 IN HANDLE Handle) 266{ 267 /* Parameters validation */ 268 if (!Stream) 269 return FALSE; 270 271 /* 272 * See https://support.microsoft.com/kb/99173 273 * for more details. 274 */ 275 276#ifdef USE_CRT 277 if (!Stream->fStream) 278 return FALSE; 279 280 int fdOut = _open_osfhandle((intptr_t)Handle, _O_TEXT /* FIXME! */); 281 FILE* fpOut = _fdopen(fdOut, "w"); 282 *Stream->fStream = *fpOut; 283 /// setvbuf(Stream->fStream, NULL, _IONBF, 0); 284 285 return TRUE; 286#else 287 /* Flush the stream and reset its handle */ 288 if (Stream->hHandle != INVALID_HANDLE_VALUE) 289 FlushFileBuffers(Stream->hHandle); 290 291 Stream->hHandle = Handle; 292 Stream->IsConsole = IsConsoleHandle(Stream->hHandle); 293 294 // NOTE: Mode reset?? 295 296 return TRUE; 297#endif 298} 299 300/* EOF */