reactos/sdk/lib/conutils/stream.c

1052 lines
27 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Console Utilities Library
* FILE: sdk/lib/conutils/stream.c
* PURPOSE: Provides basic abstraction wrappers around CRT streams or
* Win32 console API I/O functions, to deal with i18n + Unicode
* related problems.
* PROGRAMMERS: - Hermes Belusca-Maito (for the library);
* - All programmers who wrote the different console applications
* from which I took those functions and improved them.
*/
/*
* 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 <stdlib.h> // limits.h // For MB_LEN_MAX
#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>
/* PSEH for SEH Support */
#include <pseh/pseh2.h>
#include "conutils.h"
#include "stream.h"
// #define RC_STRING_MAX_SIZE 4096
#define CON_RC_STRING_MAX_SIZE 4096
// #define MAX_BUFFER_SIZE 4096 // Some programs (wlanconf, shutdown) set it to 5024
// #define OUTPUT_BUFFER_SIZE 4096 // Name given in cmd/console.c
// MAX_STRING_SIZE // Name given in diskpart
// #define MAX_MESSAGE_SIZE 512 // See shutdown...
/*
* Console I/O streams
*/
typedef struct _CON_STREAM
{
CON_WRITE_FUNC WriteFunc;
#ifdef USE_CRT
FILE* fStream;
#else
BOOL IsInitialized;
CRITICAL_SECTION Lock;
HANDLE hHandle;
/*
* TRUE if 'hHandle' refers to a console, in which case I/O UTF-16
* is directly used. If 'hHandle' refers to a file or a pipe, the
* 'Mode' flag is used.
*/
BOOL IsConsole;
/*
* The 'Mode' flag is used to know the translation mode
* when 'hHandle' refers to a file or a pipe.
*/
CON_STREAM_MODE Mode;
UINT CodePage; // Used to convert UTF-16 text to some ANSI codepage.
#endif /* defined(USE_CRT) */
} CON_STREAM, *PCON_STREAM;
/*
* 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)
};
#endif
#ifdef USE_CRT
/*
* 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.
*/
// NOTE: 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 codepage
* is either not assigned (if the mode is Binary, WideText, or UTF16Text), or
* is not cached yet (if the mode is AnsiText). In this latter case the cache
* is resolved inside ConWrite. Finally, if the mode is UTF8Text, the codepage
* cache is set to CP_UTF8.
* The codepage 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 magic value could not be '0' since it is reserved for CP_ACP.
*/
#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 codepage
* (will be reset only if Mode == AnsiText).
*/
Mode = Stream->Mode;
CON_STREAM_SET_MODE(Stream, Mode, CacheCodePage);
return TRUE;
#endif
}
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(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
}
/*
* Console I/O utility API
* (for the moment, only Output)
*/
// NOTE: Should be called with the stream locked.
INT
__stdcall
ConWrite(
IN PCON_STREAM Stream,
IN PTCHAR szStr,
IN DWORD len)
{
#ifndef USE_CRT
DWORD TotalLen = len, dwNumBytes = 0;
PVOID p;
// CHAR strOem[CON_RC_STRING_MAX_SIZE]; // Some static buffer...
/* If we do not write anything, just return */
if (!szStr || len == 0)
return 0;
/* Check whether we are writing to a console */
// if (IsConsoleHandle(Stream->hHandle))
if (Stream->IsConsole)
{
// TODO: Check if (ConStream->Mode == WideText or UTF16Text) ??
/*
* This code is inspired from _cputws, in particular from the fact that,
* according to MSDN: https://msdn.microsoft.com/en-us/library/ms687401(v=vs.85).aspx
* the buffer size must be less than 64 KB.
*
* A similar code can be used for implementing _cputs too.
*/
DWORD cchWrite;
TotalLen = len, dwNumBytes = 0;
while (len > 0)
{
cchWrite = min(len, 65535 / sizeof(WCHAR));
// FIXME: Check return value!
WriteConsole(Stream->hHandle, szStr, cchWrite, &dwNumBytes, NULL);
szStr += cchWrite;
len -= cchWrite;
}
return (INT)TotalLen; // FIXME: Really return the number of chars written!
}
/*
* We are redirected and writing to a file or pipe instead of the console.
* Convert the string from TCHARs to the desired output format, if the two differ.
*
* Implementation NOTE:
* MultiByteToWideChar (resp. WideCharToMultiByte) are equivalent to
* OemToCharBuffW (resp. CharToOemBuffW), but the latters uselessly
* depend on user32.dll, while MultiByteToWideChar and WideCharToMultiByte
* only need kernel32.dll.
*/
if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text))
{
#ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
/* Convert from the current process/thread's codepage to UTF-16 */
WCHAR *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
if (!buffer)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
len = (DWORD)MultiByteToWideChar(CP_THREAD_ACP, // CP_ACP, CP_OEMCP
0, szStr, (INT)len, buffer, (INT)len);
szStr = (PVOID)buffer;
#else
/*
* Do not perform any conversion since we are already in UTF-16,
* that is the same encoding as the stream.
*/
#endif
/*
* Find any newline character in the buffer,
* write the part BEFORE the newline, then write
* a carriage-return + newline, and then write
* the remaining part of the buffer.
*
* This fixes output in files and serial console.
*/
while (len > 0)
{
/* Loop until we find a \r or \n character */
// FIXME: What about the pair \r\n ?
p = szStr;
while (len > 0 && *(PWCHAR)p != L'\r' && *(PWCHAR)p != L'\n')
{
/* Advance one character */
p = (PVOID)((PWCHAR)p + 1);
len--;
}
/* Write everything up to \r or \n */
dwNumBytes = ((PWCHAR)p - (PWCHAR)szStr) * sizeof(WCHAR);
WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
/* If we hit \r or \n ... */
if (len > 0 && (*(PWCHAR)p == L'\r' || *(PWCHAR)p == L'\n'))
{
/* ... send a carriage-return + newline sequence and skip \r or \n */
WriteFile(Stream->hHandle, L"\r\n", 2 * sizeof(WCHAR), &dwNumBytes, NULL);
szStr = (PVOID)((PWCHAR)p + 1);
len--;
}
}
#ifndef _UNICODE
HeapFree(GetProcessHeap(), 0, buffer);
#endif
}
else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText))
{
CHAR *buffer;
/*
* Resolve the codepage cache if it was not assigned yet
* (only if the stream is in ANSI mode; in UTF8 mode the
* codepage was already set to CP_UTF8).
*/
if (/*(Stream->Mode == AnsiText) &&*/ (Stream->CodePage == INVALID_CP))
Stream->CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP
#ifdef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
/* Convert from UTF-16 to either UTF-8 or ANSI, using stream codepage */
// NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h .
buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX);
if (!buffer)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
len = WideCharToMultiByte(Stream->CodePage, 0,
szStr, len, buffer, len * MB_LEN_MAX,
NULL, NULL);
szStr = (PVOID)buffer;
#else
/*
* Convert from the current process/thread's codepage to either
* UTF-8 or ANSI, using stream codepage.
* We need to perform a double conversion, by going through UTF-16.
*/
// TODO!
#error "Need to implement double conversion!"
#endif
/*
* Find any newline character in the buffer,
* write the part BEFORE the newline, then write
* a carriage-return + newline, and then write
* the remaining part of the buffer.
*
* This fixes output in files and serial console.
*/
while (len > 0)
{
/* Loop until we find a \r or \n character */
// FIXME: What about the pair \r\n ?
p = szStr;
while (len > 0 && *(PCHAR)p != '\r' && *(PCHAR)p != '\n')
{
/* Advance one character */
p = (PVOID)((PCHAR)p + 1);
len--;
}
/* Write everything up to \r or \n */
dwNumBytes = ((PCHAR)p - (PCHAR)szStr) * sizeof(CHAR);
WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
/* If we hit \r or \n ... */
if (len > 0 && (*(PCHAR)p == '\r' || *(PCHAR)p == '\n'))
{
/* ... send a carriage-return + newline sequence and skip \r or \n */
WriteFile(Stream->hHandle, "\r\n", 2, &dwNumBytes, NULL);
szStr = (PVOID)((PCHAR)p + 1);
len--;
}
}
#ifdef _UNICODE
HeapFree(GetProcessHeap(), 0, buffer);
#else
// TODO!
#endif
}
else // if (Stream->Mode == Binary)
{
/* Directly output the string */
WriteFile(Stream->hHandle, szStr, len, &dwNumBytes, NULL);
}
// FIXME!
return (INT)TotalLen;
#else /* defined(USE_CRT) */
DWORD total = len;
DWORD written = 0;
/* If we do not write anything, just return */
if (!szStr || len == 0)
return 0;
#if 1
/*
* There is no "counted" printf-to-stream or puts-like function, therefore
* we use this trick to output the counted string to the stream.
*/
while (1)
{
written = fwprintf(Stream->fStream, L"%.*s", total, szStr);
if (written < total)
{
/*
* Some embedded NULL or special character
* was encountered, print it apart.
*/
if (written == 0)
{
fputwc(*szStr, Stream->fStream);
written++;
}
szStr += written;
total -= written;
}
else
{
break;
}
}
return (INT)len;
#else
/* ANSI text or Binary output only */
_setmode(_fileno(Stream->fStream), _O_TEXT); // _O_BINARY
return fwrite(szStr, sizeof(*szStr), len, Stream->fStream);
#endif
#endif /* defined(USE_CRT) */
}
#define CON_STREAM_WRITE_CALL(Stream, Str, Len) \
(Stream)->WriteFunc((Stream), (Str), (Len));
/* Lock the stream only in non-USE_CRT mode (otherwise use the CRT stream lock) */
#ifndef USE_CRT
#define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
do { \
EnterCriticalSection(&(Stream)->Lock); \
(RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
LeaveCriticalSection(&(Stream)->Lock); \
} while(0)
#define CON_STREAM_WRITE(Stream, Str, Len) \
do { \
EnterCriticalSection(&(Stream)->Lock); \
CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
LeaveCriticalSection(&(Stream)->Lock); \
} while(0)
#else
#define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
do { \
(RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
} while(0)
#define CON_STREAM_WRITE(Stream, Str, Len) \
do { \
CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
} while(0)
#endif
INT
ConStreamWrite(
IN PCON_STREAM Stream,
IN PTCHAR szStr,
IN DWORD len)
{
INT Len;
CON_STREAM_WRITE2(Stream, szStr, len, Len);
return Len;
}
INT
ConPuts(
IN PCON_STREAM Stream,
IN LPWSTR szStr)
{
INT Len;
Len = wcslen(szStr);
CON_STREAM_WRITE2(Stream, szStr, Len, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
Len = 0;
return Len;
}
INT
ConPrintfV(
IN PCON_STREAM Stream,
IN LPWSTR szStr,
IN va_list args) // arg_ptr
{
INT Len;
WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
// Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI
/*
* Reuse szStr as the pointer to end-of-string, to compute
* the string length instead of calling wcslen().
*/
// StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args);
// Len = wcslen(bufSrc);
StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), &szStr, NULL, 0, szStr, args);
Len = szStr - bufSrc;
CON_STREAM_WRITE2(Stream, bufSrc, Len, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
Len = 0;
return Len;
}
INT
__cdecl
ConPrintf(
IN PCON_STREAM Stream,
IN LPWSTR szStr,
...)
{
INT Len;
va_list args;
// Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI
// StringCchPrintfW
va_start(args, szStr);
Len = ConPrintfV(Stream, szStr, args);
va_end(args);
return Len;
}
INT
ConResPutsEx(
IN PCON_STREAM Stream,
IN HINSTANCE hInstance OPTIONAL,
IN UINT uID)
{
INT Len;
PWCHAR szStr = NULL;
Len = K32LoadStringW(hInstance, uID, (PWSTR)&szStr, 0);
if (szStr && Len)
// Len = ConPuts(Stream, szStr);
CON_STREAM_WRITE2(Stream, szStr, Len, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
Len = 0;
return Len;
}
INT
ConResPuts(
IN PCON_STREAM Stream,
IN UINT uID)
{
return ConResPutsEx(Stream, NULL /*GetModuleHandleW(NULL)*/, uID);
}
INT
ConResPrintfExV(
IN PCON_STREAM Stream,
IN HINSTANCE hInstance OPTIONAL,
IN UINT uID,
IN va_list args) // arg_ptr
{
INT Len;
WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
// NOTE: We may use the special behaviour where nBufMaxSize == 0
Len = K32LoadStringW(hInstance, uID, bufSrc, ARRAYSIZE(bufSrc));
if (Len)
Len = ConPrintfV(Stream, bufSrc, args);
return Len;
}
INT
ConResPrintfV(
IN PCON_STREAM Stream,
IN UINT uID,
IN va_list args) // arg_ptr
{
return ConResPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/, uID, args);
}
INT
__cdecl
ConResPrintfEx(
IN PCON_STREAM Stream,
IN HINSTANCE hInstance OPTIONAL,
IN UINT uID,
...)
{
INT Len;
va_list args;
va_start(args, uID);
Len = ConResPrintfExV(Stream, hInstance, uID, args);
va_end(args);
return Len;
}
INT
__cdecl
ConResPrintf(
IN PCON_STREAM Stream,
IN UINT uID,
...)
{
INT Len;
va_list args;
va_start(args, uID);
Len = ConResPrintfV(Stream, uID, args);
va_end(args);
return Len;
}
INT
ConMsgPuts(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId)
{
INT Len;
DWORD dwLength = 0;
LPWSTR lpMsgBuf = NULL;
/*
* Sanitize dwFlags. This version always ignore explicitely the inserts
* as we emulate the behaviour of the *puts function.
*/
dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage.
dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
/*
* Retrieve the message string without appending extra newlines.
* Wrap in SEH to protect from invalid string parameters.
*/
_SEH2_TRY
{
dwLength = FormatMessageW(dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
(LPWSTR)&lpMsgBuf,
0, NULL);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
Len = (INT)dwLength;
if (!lpMsgBuf)
{
// ASSERT(dwLength == 0);
}
else
{
// ASSERT(dwLength != 0);
/* lpMsgBuf is NULL-terminated by FormatMessage */
// Len = ConPuts(Stream, lpMsgBuf);
CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
Len = 0;
/* Free the buffer allocated by FormatMessage */
LocalFree(lpMsgBuf);
}
return Len;
}
INT
ConMsgPrintf2V(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
IN va_list args) // arg_ptr
{
INT Len;
DWORD dwLength = 0;
LPWSTR lpMsgBuf = NULL;
/*
* Sanitize dwFlags. This version always ignore explicitely the inserts.
* The string that we will return to the user will not be pre-formatted.
*/
dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage.
dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
/*
* Retrieve the message string without appending extra newlines.
* Wrap in SEH to protect from invalid string parameters.
*/
_SEH2_TRY
{
dwLength = FormatMessageW(dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
(LPWSTR)&lpMsgBuf,
0, NULL);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
Len = (INT)dwLength;
if (!lpMsgBuf)
{
// ASSERT(dwLength == 0);
}
else
{
// ASSERT(dwLength != 0);
/* lpMsgBuf is NULL-terminated by FormatMessage */
Len = ConPrintfV(Stream, lpMsgBuf, args);
// CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
Len = 0;
/* Free the buffer allocated by FormatMessage */
LocalFree(lpMsgBuf);
}
return Len;
}
INT
ConMsgPrintfV(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
IN va_list args) // arg_ptr
{
INT Len;
DWORD dwLength = 0;
LPWSTR lpMsgBuf = NULL;
/* Sanitize dwFlags */
dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
// dwFlags &= ~FORMAT_MESSAGE_IGNORE_INSERTS; // We always use arguments.
dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY; // We always use arguments of type 'va_list'.
//
// NOTE: Technique taken from eventvwr.c!GetMessageStringFromDll()
//
dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
/*
* Retrieve the message string without appending extra newlines.
* Use the "safe" FormatMessage version (SEH-protected) to protect
* from invalid string parameters.
*/
dwLength = FormatMessageSafeW(dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
(LPWSTR)&lpMsgBuf,
0, &args);
Len = (INT)dwLength;
if (!lpMsgBuf)
{
// ASSERT(dwLength == 0);
}
else
{
// ASSERT(dwLength != 0);
// Len = ConPrintfV(Stream, lpMsgBuf, args);
CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */
if (Len < 0)
Len = 0;
/* Free the buffer allocated by FormatMessage */
LocalFree(lpMsgBuf);
}
return Len;
}
INT
__cdecl
ConMsgPrintf(
IN PCON_STREAM Stream,
IN DWORD dwFlags,
IN LPCVOID lpSource OPTIONAL,
IN DWORD dwMessageId,
IN DWORD dwLanguageId,
...)
{
INT Len;
va_list args;
va_start(args, dwLanguageId);
// ConMsgPrintf2V
Len = ConMsgPrintfV(Stream,
dwFlags,
lpSource,
dwMessageId,
dwLanguageId,
args);
va_end(args);
return Len;
}
VOID
ConClearLine(IN PCON_STREAM Stream)
{
HANDLE hOutput = ConStreamGetOSHandle(Stream);
/*
* Erase the full line where the cursor is, and move
* the cursor back to the beginning of the line.
*/
if (IsConsoleHandle(hOutput))
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwWritten;
GetConsoleScreenBufferInfo(hOutput, &csbi);
csbi.dwCursorPosition.X = 0;
// csbi.dwCursorPosition.Y;
FillConsoleOutputCharacterW(hOutput, L' ',
csbi.dwSize.X,
csbi.dwCursorPosition,
&dwWritten);
SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition);
}
else if (IsTTYHandle(hOutput))
{
ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
}
// else, do nothing for files
}