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! 52 CON_STREAM StdStreams[3] = 53 { 54 {0}, // StdIn 55 {0}, // StdOut 56 {0}, // StdErr 57 }; 58 #else 59 CON_STREAM csStdIn; 60 CON_STREAM csStdOut; 61 CON_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 */ 68 static 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) \ 88 do { \ 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) \ 112 do { \ 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 126 BOOL 127 ConStreamInitEx( 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 184 BOOL 185 ConStreamInit( 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 194 BOOL 195 ConStreamSetMode( 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 214 BOOL 215 ConStreamSetCacheCodePage( 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 239 HANDLE 240 ConStreamGetOSHandle( 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 262 BOOL 263 ConStreamSetOSHandle( 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 */ 301