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
7 * COPYRIGHT: Copyright 2017-2018 ReactOS Team
8 * Copyright 2017-2018 Hermes Belusca-Maito
15 * @brief Console I/O streams
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
23 /** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
26 /* FIXME: Temporary HACK before we cleanly support UNICODE functions */
38 // #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
39 #include <wincon.h> // Console APIs (only if kernel32 support included)
44 #include "stream_private.h"
48 * Standard console streams, initialized by
49 * calls to ConStreamInit/ConInitStdStreams.
52 CON_STREAM StdStreams
[3] =
65 /* Stream translation modes */
67 /* Lookup table to convert CON_STREAM_MODE to CRT mode */
68 static int ConToCRTMode
[] =
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)
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
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) \
92 fflush((Stream)->fStream); \
93 if ((Mode) < ARRAYSIZE(ConToCRTMode)) \
94 _setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \
96 _setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \
99 #else /* defined(USE_CRT) */
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)).
111 * NOTE: the magic value could not be '0' since it is reserved for CP_ACP.
113 #define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \
115 (Stream)->Mode = (Mode); \
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) */ \
125 #endif /* defined(USE_CRT) */
130 OUT PCON_STREAM Stream
,
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
)
137 /* Parameters validation */
138 if (!Stream
|| !Handle
|| (Mode
> UTF8Text
))
143 Stream
->fStream
= (FILE*)Handle
;
147 if ((HANDLE
)Handle
== INVALID_HANDLE_VALUE
)
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
157 /* Attempt to close the handle of the old stream */
158 if (/* Stream->IsInitialized && */ Stream
->hHandle
&&
159 Stream
->hHandle
!= INVALID_HANDLE_VALUE
)
161 CloseHandle(Stream
->hHandle
);
165 /* Initialize the stream critical section if not already done */
166 if (!Stream
->IsInitialized
)
168 InitializeCriticalSection
/*AndSpinCount*/(&Stream
->Lock
/* , 4000 */);
169 Stream
->IsInitialized
= TRUE
;
172 Stream
->hHandle
= (HANDLE
)Handle
;
173 Stream
->IsConsole
= IsConsoleHandle(Stream
->hHandle
);
175 #endif /* defined(USE_CRT) */
177 /* Set the correct file translation mode */
178 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
180 /* Use the default 'ConWrite' helper if nothing is specified */
181 Stream
->WriteFunc
= (WriteFunc
? WriteFunc
: ConWrite
);
188 OUT PCON_STREAM Stream
,
190 IN CON_STREAM_MODE Mode
,
191 IN UINT CacheCodePage OPTIONAL
)
193 return ConStreamInitEx(Stream
, Handle
, Mode
, CacheCodePage
, ConWrite
);
198 IN PCON_STREAM Stream
,
199 IN CON_STREAM_MODE Mode
,
200 IN UINT CacheCodePage OPTIONAL
)
202 /* Parameters validation */
203 if (!Stream
|| (Mode
> UTF8Text
))
207 if (!Stream
->fStream
)
211 /* Set the correct file translation mode */
212 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
217 ConStreamSetCacheCodePage(
218 IN PCON_STREAM Stream
,
219 IN UINT CacheCodePage
)
223 #warning The ConStreamSetCacheCodePage function does not make much sense with the CRT!
225 CON_STREAM_MODE Mode
;
227 /* Parameters validation */
232 * Keep the original stream mode but set the correct file codepage
233 * (will be reset only if Mode == AnsiText).
236 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
242 ConStreamGetOSHandle(
243 IN PCON_STREAM Stream
)
245 /* Parameters validation */
247 return INVALID_HANDLE_VALUE
;
250 * See https://support.microsoft.com/kb/99173
255 if (!Stream
->fStream
)
256 return INVALID_HANDLE_VALUE
;
258 return (HANDLE
)_get_osfhandle(_fileno(Stream
->fStream
));
260 return Stream
->hHandle
;
265 ConStreamSetOSHandle(
266 IN PCON_STREAM Stream
,
269 /* Parameters validation */
274 * See https://support.microsoft.com/kb/99173
279 if (!Stream
->fStream
)
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);
289 /* Flush the stream and reset its handle */
290 if (Stream
->hHandle
!= INVALID_HANDLE_VALUE
)
291 FlushFileBuffers(Stream
->hHandle
);
293 Stream
->hHandle
= Handle
;
294 Stream
->IsConsole
= IsConsoleHandle(Stream
->hHandle
);
296 // NOTE: Mode reset??