ReactOS  0.4.13-dev-79-gcd489d8
stream.c
Go to the documentation of this file.
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 
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  */
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!
53 {
54  {0}, // StdIn
55  {0}, // StdOut
56  {0}, // StdErr
57 };
58 #else
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
129  IN PVOID Handle,
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 
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
187  IN PVOID Handle,
189  IN UINT CacheCodePage OPTIONAL)
190 {
191  return ConStreamInitEx(Stream, Handle, Mode, CacheCodePage, ConWrite);
192 }
193 
194 BOOL
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
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
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  return TRUE;
236 #endif
237 }
238 
239 HANDLE
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
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(Handle, _O_TEXT /* FIXME! */);
281  FILE* fpOut = _fdopen(fdOut, "w");
282  *Stream->fStream = *fpOut;
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 */
BOOL ConStreamInitEx(OUT PCON_STREAM Stream, IN PVOID Handle, IN CON_STREAM_MODE Mode, IN UINT CacheCodePage OPTIONAL, IN CON_WRITE_FUNC WriteFunc OPTIONAL)
Definition: stream.c:127
#define IN
Definition: typedefs.h:38
#define TRUE
Definition: types.h:120
#define CloseHandle
Definition: compat.h:398
_In_ ULONG Mode
Definition: hubbusif.h:303
_Check_return_ _CRTIMP FILE *__cdecl _fdopen(_In_ int _FileHandle, _In_z_ const char *_Mode)
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
#define _O_WTEXT
Definition: fcntl.h:20
_CRTIMP intptr_t __cdecl _get_osfhandle(_In_ int _FileHandle)
unsigned int BOOL
Definition: ntddk_ex.h:94
BOOL ConStreamSetCacheCodePage(IN PCON_STREAM Stream, IN UINT CacheCodePage)
Definition: stream.c:215
VOID WINAPI InitializeCriticalSection(OUT LPCRITICAL_SECTION lpCriticalSection)
Definition: synch.c:697
#define _O_U8TEXT
Definition: fcntl.h:22
HANDLE ConStreamGetOSHandle(IN PCON_STREAM Stream)
Definition: stream.c:240
_In_ HANDLE Handle
Definition: extypes.h:390
BOOL ConStreamInit(OUT PCON_STREAM Stream, IN PVOID Handle, IN CON_STREAM_MODE Mode, IN UINT CacheCodePage OPTIONAL)
Definition: stream.c:185
CON_STREAM csStdIn
Definition: stream.c:59
BOOL WINAPI FlushFileBuffers(IN HANDLE hFile)
Definition: fileinfo.c:175
CON_STREAM csStdErr
Definition: stream.c:61
CON_STREAM csStdOut
Definition: stream.c:60
PVOID HANDLE
Definition: typedefs.h:71
static const PCON_STREAM StdStreams[]
Definition: redir.c:36
#define IsConsoleHandle(h)
Definition: console.h:14
#define _O_U16TEXT
Definition: fcntl.h:21
INT __stdcall ConWrite(IN PCON_STREAM Stream, IN PTCHAR szStr, IN DWORD len)
Definition: outstream.c:85
#define _O_BINARY
Definition: cabinet.h:51
BOOL ConStreamSetOSHandle(IN PCON_STREAM Stream, IN HANDLE Handle)
Definition: stream.c:263
BOOL ConStreamSetMode(IN PCON_STREAM Stream, IN CON_STREAM_MODE Mode, IN UINT CacheCodePage OPTIONAL)
Definition: stream.c:195
INT(__stdcall * CON_WRITE_FUNC)(IN PCON_STREAM, IN PTCHAR, IN DWORD)
Definition: outstream.h:44
enum _CON_STREAM_MODE CON_STREAM_MODE
#define _O_TEXT
Definition: cabinet.h:50
unsigned int UINT
Definition: ndis.h:50
#define OUT
Definition: typedefs.h:39
_CRTIMP int __cdecl _open_osfhandle(_In_ intptr_t _OSFileHandle, _In_ int _Flags)
_Check_return_ _CRTIMP int __cdecl _fileno(_In_ FILE *_File)
_Inout_opt_ PUNICODE_STRING _Inout_opt_ PUNICODE_STRING Stream
Definition: fltkernel.h:1092
#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage)
Definition: stream.c:111
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68