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 #endif 77 78 #ifdef USE_CRT 79 80 /* 81 * See http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html 82 * and http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html 83 * for more details. 84 */ 85 86 // NOTE: May the translated mode be cached somehow? 87 // NOTE2: We may also call IsConsoleHandle to directly set the mode to 88 // _O_U16TEXT if it's ok?? 89 // NOTE3: _setmode returns the previous mode, or -1 if failure. 90 #define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \ 91 do { \ 92 fflush((Stream)->fStream); \ 93 if ((Mode) < ARRAYSIZE(ConToCRTMode)) \ 94 _setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \ 95 else \ 96 _setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \ 97 } while(0) 98 99 #else /* defined(USE_CRT) */ 100 101 /* 102 * We set Stream->CodePage to INVALID_CP (= -1) to signal that the codepage 103 * is either not assigned (if the mode is Binary, WideText, or UTF16Text), or 104 * is not cached yet (if the mode is AnsiText). In this latter case the cache 105 * is resolved inside ConWrite. Finally, if the mode is UTF8Text, the codepage 106 * cache is set to CP_UTF8. 107 * The codepage cache can be reset by an explicit call to CON_STREAM_SET_MODE 108 * (i.e. by calling ConStreamSetMode, or by reinitializing the stream with 109 * ConStreamInit(Ex)). 110 * 111 * NOTE: the magic value could not be '0' since it is reserved for CP_ACP. 112 */ 113 #define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \ 114 do { \ 115 (Stream)->Mode = (Mode); \ 116 \ 117 if ((Mode) == AnsiText) \ 118 (Stream)->CodePage = CacheCodePage; /* Possibly assigned */ \ 119 else if ((Mode) == UTF8Text) \ 120 (Stream)->CodePage = CP_UTF8; /* Fixed */ \ 121 else /* Mode == Binary, WideText, UTF16Text */ \ 122 (Stream)->CodePage = INVALID_CP; /* Not assigned (meaningless) */ \ 123 } while(0) 124 125 #endif /* defined(USE_CRT) */ 126 127 128 BOOL 129 ConStreamInitEx( 130 OUT PCON_STREAM Stream, 131 IN PVOID Handle, 132 IN CON_STREAM_MODE Mode, 133 IN UINT CacheCodePage OPTIONAL, 134 // IN CON_READ_FUNC ReadFunc OPTIONAL, 135 IN CON_WRITE_FUNC WriteFunc OPTIONAL) 136 { 137 /* Parameters validation */ 138 if (!Stream || !Handle || (Mode > UTF8Text)) 139 return FALSE; 140 141 #ifdef USE_CRT 142 143 Stream->fStream = (FILE*)Handle; 144 145 #else 146 147 if ((HANDLE)Handle == INVALID_HANDLE_VALUE) 148 return FALSE; 149 150 /* 151 * As the user calls us by giving us an existing handle to attach on, 152 * it is not our duty to close it if we are called again. The user 153 * is responsible for having opened those handles, and is responsible 154 * for closing them! 155 */ 156 #if 0 157 /* Attempt to close the handle of the old stream */ 158 if (/* Stream->IsInitialized && */ Stream->hHandle && 159 Stream->hHandle != INVALID_HANDLE_VALUE) 160 { 161 CloseHandle(Stream->hHandle); 162 } 163 #endif 164 165 /* Initialize the stream critical section if not already done */ 166 if (!Stream->IsInitialized) 167 { 168 InitializeCriticalSection/*AndSpinCount*/(&Stream->Lock /* , 4000 */); 169 Stream->IsInitialized = TRUE; 170 } 171 172 Stream->hHandle = (HANDLE)Handle; 173 Stream->IsConsole = IsConsoleHandle(Stream->hHandle); 174 175 #endif /* defined(USE_CRT) */ 176 177 /* Set the correct file translation mode */ 178 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 179 180 /* Use the default 'ConWrite' helper if nothing is specified */ 181 Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite); 182 183 return TRUE; 184 } 185 186 BOOL 187 ConStreamInit( 188 OUT PCON_STREAM Stream, 189 IN PVOID Handle, 190 IN CON_STREAM_MODE Mode, 191 IN UINT CacheCodePage OPTIONAL) 192 { 193 return ConStreamInitEx(Stream, Handle, Mode, CacheCodePage, ConWrite); 194 } 195 196 BOOL 197 ConStreamSetMode( 198 IN PCON_STREAM Stream, 199 IN CON_STREAM_MODE Mode, 200 IN UINT CacheCodePage OPTIONAL) 201 { 202 /* Parameters validation */ 203 if (!Stream || (Mode > UTF8Text)) 204 return FALSE; 205 206 #ifdef USE_CRT 207 if (!Stream->fStream) 208 return FALSE; 209 #endif 210 211 /* Set the correct file translation mode */ 212 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 213 return TRUE; 214 } 215 216 BOOL 217 ConStreamSetCacheCodePage( 218 IN PCON_STREAM Stream, 219 IN UINT CacheCodePage) 220 { 221 #ifdef USE_CRT 222 // FIXME! 223 #warning The ConStreamSetCacheCodePage function does not make much sense with the CRT! 224 #else 225 CON_STREAM_MODE Mode; 226 227 /* Parameters validation */ 228 if (!Stream) 229 return FALSE; 230 231 /* 232 * Keep the original stream mode but set the correct file codepage 233 * (will be reset only if Mode == AnsiText). 234 */ 235 Mode = Stream->Mode; 236 CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage); 237 return TRUE; 238 #endif 239 } 240 241 HANDLE 242 ConStreamGetOSHandle( 243 IN PCON_STREAM Stream) 244 { 245 /* Parameters validation */ 246 if (!Stream) 247 return INVALID_HANDLE_VALUE; 248 249 /* 250 * See https://support.microsoft.com/kb/99173 251 * for more details. 252 */ 253 254 #ifdef USE_CRT 255 if (!Stream->fStream) 256 return INVALID_HANDLE_VALUE; 257 258 return (HANDLE)_get_osfhandle(_fileno(Stream->fStream)); 259 #else 260 return Stream->hHandle; 261 #endif 262 } 263 264 BOOL 265 ConStreamSetOSHandle( 266 IN PCON_STREAM Stream, 267 IN HANDLE Handle) 268 { 269 /* Parameters validation */ 270 if (!Stream) 271 return FALSE; 272 273 /* 274 * See https://support.microsoft.com/kb/99173 275 * for more details. 276 */ 277 278 #ifdef USE_CRT 279 if (!Stream->fStream) 280 return FALSE; 281 282 int fdOut = _open_osfhandle(Handle, _O_TEXT /* FIXME! */); 283 FILE* fpOut = _fdopen(fdOut, "w"); 284 *Stream->fStream = *fpOut; 285 /// setvbuf(Stream->fStream, NULL, _IONBF, 0); 286 287 return TRUE; 288 #else 289 /* Flush the stream and reset its handle */ 290 if (Stream->hHandle != INVALID_HANDLE_VALUE) 291 FlushFileBuffers(Stream->hHandle); 292 293 Stream->hHandle = Handle; 294 Stream->IsConsole = IsConsoleHandle(Stream->hHandle); 295 296 // NOTE: Mode reset?? 297 298 return TRUE; 299 #endif 300 } 301 302 /* EOF */ 303