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)
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
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) \
89 fflush((Stream)->fStream); \
90 if ((Mode) < ARRAYSIZE(ConToCRTMode)) \
91 _setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \
93 _setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \
96 #else /* defined(USE_CRT) */
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)).
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).
111 #define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \
113 (Stream)->Mode = (Mode); \
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) */ \
123 #endif /* defined(USE_CRT) */
128 OUT PCON_STREAM Stream
,
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
)
135 /* Parameters validation */
136 if (!Stream
|| !Handle
|| (Mode
> UTF8Text
))
141 Stream
->fStream
= (FILE*)Handle
;
145 if ((HANDLE
)Handle
== INVALID_HANDLE_VALUE
)
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
155 /* Attempt to close the handle of the old stream */
156 if (/* Stream->IsInitialized && */ Stream
->hHandle
&&
157 Stream
->hHandle
!= INVALID_HANDLE_VALUE
)
159 CloseHandle(Stream
->hHandle
);
163 /* Initialize the stream critical section if not already done */
164 if (!Stream
->IsInitialized
)
166 InitializeCriticalSection
/*AndSpinCount*/(&Stream
->Lock
/* , 4000 */);
167 Stream
->IsInitialized
= TRUE
;
170 Stream
->hHandle
= (HANDLE
)Handle
;
171 Stream
->IsConsole
= IsConsoleHandle(Stream
->hHandle
);
173 #endif /* defined(USE_CRT) */
175 /* Set the correct file translation mode */
176 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
178 /* Use the default 'ConWrite' helper if nothing is specified */
179 Stream
->WriteFunc
= (WriteFunc
? WriteFunc
: ConWrite
);
186 OUT PCON_STREAM Stream
,
188 IN CON_STREAM_MODE Mode
,
189 IN UINT CacheCodePage OPTIONAL
)
191 return ConStreamInitEx(Stream
, Handle
, Mode
, CacheCodePage
, ConWrite
);
196 IN PCON_STREAM Stream
,
197 IN CON_STREAM_MODE Mode
,
198 IN UINT CacheCodePage OPTIONAL
)
200 /* Parameters validation */
201 if (!Stream
|| (Mode
> UTF8Text
))
205 if (!Stream
->fStream
)
209 /* Set the correct file translation mode */
210 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
215 ConStreamSetCacheCodePage(
216 IN PCON_STREAM Stream
,
217 IN UINT CacheCodePage
)
221 #warning The ConStreamSetCacheCodePage function does not make much sense with the CRT!
223 CON_STREAM_MODE Mode
;
225 /* Parameters validation */
230 * Keep the original stream mode but set the correct file code page
231 * (will be reset only if Mode == AnsiText).
234 CON_STREAM_SET_MODE(Stream
, Mode
, CacheCodePage
);
240 ConStreamGetOSHandle(
241 IN PCON_STREAM Stream
)
243 /* Parameters validation */
245 return INVALID_HANDLE_VALUE
;
248 * See https://support.microsoft.com/kb/99173
253 if (!Stream
->fStream
)
254 return INVALID_HANDLE_VALUE
;
256 return (HANDLE
)_get_osfhandle(_fileno(Stream
->fStream
));
258 return Stream
->hHandle
;
263 ConStreamSetOSHandle(
264 IN PCON_STREAM Stream
,
267 /* Parameters validation */
272 * See https://support.microsoft.com/kb/99173
277 if (!Stream
->fStream
)
280 int fdOut
= _open_osfhandle(Handle
, _O_TEXT
/* FIXME! */);
281 FILE* fpOut
= _fdopen(fdOut
, "w");
282 *Stream
->fStream
= *fpOut
;
283 /// setvbuf(Stream->fStream, NULL, _IONBF, 0);
287 /* Flush the stream and reset its handle */
288 if (Stream
->hHandle
!= INVALID_HANDLE_VALUE
)
289 FlushFileBuffers(Stream
->hHandle
);
291 Stream
->hHandle
= Handle
;
292 Stream
->IsConsole
= IsConsoleHandle(Stream
->hHandle
);
294 // NOTE: Mode reset??