Reactos
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 */