mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
300 lines
7.7 KiB
C
300 lines
7.7 KiB
C
/*
|
|
* PROJECT: ReactOS Console Utilities Library
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Provides basic abstraction wrappers around CRT streams or
|
|
* Win32 console API I/O functions, to deal with i18n + Unicode
|
|
* related problems.
|
|
* COPYRIGHT: Copyright 2017-2018 ReactOS Team
|
|
* Copyright 2017-2018 Hermes Belusca-Maito
|
|
*/
|
|
|
|
/**
|
|
* @file stream.c
|
|
* @ingroup ConUtils
|
|
*
|
|
* @brief Console I/O streams
|
|
**/
|
|
|
|
/*
|
|
* Enable this define if you want to only use CRT functions to output
|
|
* UNICODE stream to the console, as in the way explained by
|
|
* http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
|
|
*/
|
|
/** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
|
|
// #define USE_CRT
|
|
|
|
/* FIXME: Temporary HACK before we cleanly support UNICODE functions */
|
|
#define UNICODE
|
|
#define _UNICODE
|
|
|
|
#ifdef USE_CRT
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#endif /* USE_CRT */
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <winnls.h>
|
|
// #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
|
|
#include <wincon.h> // Console APIs (only if kernel32 support included)
|
|
#include <strsafe.h>
|
|
|
|
#include "conutils.h"
|
|
#include "stream.h"
|
|
#include "stream_private.h"
|
|
|
|
|
|
/*
|
|
* Standard console streams, initialized by
|
|
* calls to ConStreamInit/ConInitStdStreams.
|
|
*/
|
|
#if 0 // FIXME!
|
|
CON_STREAM StdStreams[3] =
|
|
{
|
|
{0}, // StdIn
|
|
{0}, // StdOut
|
|
{0}, // StdErr
|
|
};
|
|
#else
|
|
CON_STREAM csStdIn;
|
|
CON_STREAM csStdOut;
|
|
CON_STREAM csStdErr;
|
|
#endif
|
|
|
|
|
|
/* Stream translation modes */
|
|
#ifdef USE_CRT
|
|
/* Lookup table to convert CON_STREAM_MODE to CRT mode */
|
|
static int ConToCRTMode[] =
|
|
{
|
|
_O_BINARY, // Binary (untranslated)
|
|
_O_TEXT, // AnsiText (translated)
|
|
_O_WTEXT, // WideText (UTF16 with BOM; translated)
|
|
_O_U16TEXT, // UTF16Text (UTF16 without BOM; translated)
|
|
_O_U8TEXT, // UTF8Text (UTF8 without BOM; translated)
|
|
};
|
|
|
|
/*
|
|
* See http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
|
|
* and http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html
|
|
* for more details.
|
|
*/
|
|
|
|
// NOTE1: May the translated mode be cached somehow?
|
|
// NOTE2: We may also call IsConsoleHandle to directly set the mode to
|
|
// _O_U16TEXT if it's ok??
|
|
// NOTE3: _setmode returns the previous mode, or -1 if failure.
|
|
#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \
|
|
do { \
|
|
fflush((Stream)->fStream); \
|
|
if ((Mode) < ARRAYSIZE(ConToCRTMode)) \
|
|
_setmode(_fileno((Stream)->fStream), ConToCRTMode[(Mode)]); \
|
|
else \
|
|
_setmode(_fileno((Stream)->fStream), _O_TEXT); /* Default to ANSI text */ \
|
|
} while(0)
|
|
|
|
#else /* defined(USE_CRT) */
|
|
|
|
/*
|
|
* We set Stream->CodePage to INVALID_CP (== -1) to signal that the code page
|
|
* is either not assigned (if the mode is Binary, WideText, or UTF16Text), or
|
|
* is not cached (if the mode is AnsiText). In this latter case the code page
|
|
* is resolved inside ConWrite. Finally, if the mode is UTF8Text, the code page
|
|
* cache is always set to CP_UTF8.
|
|
* The code page cache can be reset by an explicit call to CON_STREAM_SET_MODE
|
|
* (i.e. by calling ConStreamSetMode, or by reinitializing the stream with
|
|
* ConStreamInit(Ex)).
|
|
*
|
|
* NOTE: the reserved values are: 0 (CP_ACP), 1 (CP_OEMCP), 2 (CP_MACCP),
|
|
* 3 (CP_THREAD_ACP), 42 (CP_SYMBOL), 65000 (CP_UTF7) and 65001 (CP_UTF8).
|
|
*/
|
|
#define CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage) \
|
|
do { \
|
|
(Stream)->Mode = (Mode); \
|
|
\
|
|
if ((Mode) == AnsiText) \
|
|
(Stream)->CodePage = CacheCodePage; /* Possibly assigned */ \
|
|
else if ((Mode) == UTF8Text) \
|
|
(Stream)->CodePage = CP_UTF8; /* Fixed */ \
|
|
else /* Mode == Binary, WideText, UTF16Text */ \
|
|
(Stream)->CodePage = INVALID_CP; /* Not assigned (meaningless) */ \
|
|
} while(0)
|
|
|
|
#endif /* defined(USE_CRT) */
|
|
|
|
|
|
BOOL
|
|
ConStreamInitEx(
|
|
OUT PCON_STREAM Stream,
|
|
IN PVOID Handle,
|
|
IN CON_STREAM_MODE Mode,
|
|
IN UINT CacheCodePage OPTIONAL,
|
|
// IN CON_READ_FUNC ReadFunc OPTIONAL,
|
|
IN CON_WRITE_FUNC WriteFunc OPTIONAL)
|
|
{
|
|
/* Parameters validation */
|
|
if (!Stream || !Handle || (Mode > UTF8Text))
|
|
return FALSE;
|
|
|
|
#ifdef USE_CRT
|
|
|
|
Stream->fStream = (FILE*)Handle;
|
|
|
|
#else
|
|
|
|
if ((HANDLE)Handle == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
/*
|
|
* As the user calls us by giving us an existing handle to attach on,
|
|
* it is not our duty to close it if we are called again. The user
|
|
* is responsible for having opened those handles, and is responsible
|
|
* for closing them!
|
|
*/
|
|
#if 0
|
|
/* Attempt to close the handle of the old stream */
|
|
if (/* Stream->IsInitialized && */ Stream->hHandle &&
|
|
Stream->hHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(Stream->hHandle);
|
|
}
|
|
#endif
|
|
|
|
/* Initialize the stream critical section if not already done */
|
|
if (!Stream->IsInitialized)
|
|
{
|
|
InitializeCriticalSection/*AndSpinCount*/(&Stream->Lock /* , 4000 */);
|
|
Stream->IsInitialized = TRUE;
|
|
}
|
|
|
|
Stream->hHandle = (HANDLE)Handle;
|
|
Stream->IsConsole = IsConsoleHandle(Stream->hHandle);
|
|
|
|
#endif /* defined(USE_CRT) */
|
|
|
|
/* Set the correct file translation mode */
|
|
CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
|
|
|
|
/* Use the default 'ConWrite' helper if nothing is specified */
|
|
Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ConStreamInit(
|
|
OUT PCON_STREAM Stream,
|
|
IN PVOID Handle,
|
|
IN CON_STREAM_MODE Mode,
|
|
IN UINT CacheCodePage OPTIONAL)
|
|
{
|
|
return ConStreamInitEx(Stream, Handle, Mode, CacheCodePage, ConWrite);
|
|
}
|
|
|
|
BOOL
|
|
ConStreamSetMode(
|
|
IN PCON_STREAM Stream,
|
|
IN CON_STREAM_MODE Mode,
|
|
IN UINT CacheCodePage OPTIONAL)
|
|
{
|
|
/* Parameters validation */
|
|
if (!Stream || (Mode > UTF8Text))
|
|
return FALSE;
|
|
|
|
#ifdef USE_CRT
|
|
if (!Stream->fStream)
|
|
return FALSE;
|
|
#endif
|
|
|
|
/* Set the correct file translation mode */
|
|
CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ConStreamSetCacheCodePage(
|
|
IN PCON_STREAM Stream,
|
|
IN UINT CacheCodePage)
|
|
{
|
|
#ifdef USE_CRT
|
|
// FIXME!
|
|
#warning The ConStreamSetCacheCodePage function does not make much sense with the CRT!
|
|
#else
|
|
CON_STREAM_MODE Mode;
|
|
|
|
/* Parameters validation */
|
|
if (!Stream)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Keep the original stream mode but set the correct file code page
|
|
* (will be reset only if Mode == AnsiText).
|
|
*/
|
|
Mode = Stream->Mode;
|
|
CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
HANDLE
|
|
ConStreamGetOSHandle(
|
|
IN PCON_STREAM Stream)
|
|
{
|
|
/* Parameters validation */
|
|
if (!Stream)
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
/*
|
|
* See https://support.microsoft.com/kb/99173
|
|
* for more details.
|
|
*/
|
|
|
|
#ifdef USE_CRT
|
|
if (!Stream->fStream)
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
return (HANDLE)_get_osfhandle(_fileno(Stream->fStream));
|
|
#else
|
|
return Stream->hHandle;
|
|
#endif
|
|
}
|
|
|
|
BOOL
|
|
ConStreamSetOSHandle(
|
|
IN PCON_STREAM Stream,
|
|
IN HANDLE Handle)
|
|
{
|
|
/* Parameters validation */
|
|
if (!Stream)
|
|
return FALSE;
|
|
|
|
/*
|
|
* See https://support.microsoft.com/kb/99173
|
|
* for more details.
|
|
*/
|
|
|
|
#ifdef USE_CRT
|
|
if (!Stream->fStream)
|
|
return FALSE;
|
|
|
|
int fdOut = _open_osfhandle((intptr_t)Handle, _O_TEXT /* FIXME! */);
|
|
FILE* fpOut = _fdopen(fdOut, "w");
|
|
*Stream->fStream = *fpOut;
|
|
/// setvbuf(Stream->fStream, NULL, _IONBF, 0);
|
|
|
|
return TRUE;
|
|
#else
|
|
/* Flush the stream and reset its handle */
|
|
if (Stream->hHandle != INVALID_HANDLE_VALUE)
|
|
FlushFileBuffers(Stream->hHandle);
|
|
|
|
Stream->hHandle = Handle;
|
|
Stream->IsConsole = IsConsoleHandle(Stream->hHandle);
|
|
|
|
// NOTE: Mode reset??
|
|
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
|
|
/* EOF */
|