diff --git a/sdk/lib/conutils/CMakeLists.txt b/sdk/lib/conutils/CMakeLists.txt index 8b978d8e3b8..90d8dfb98dc 100644 --- a/sdk/lib/conutils/CMakeLists.txt +++ b/sdk/lib/conutils/CMakeLists.txt @@ -1,5 +1,7 @@ list(APPEND SOURCE + # instream.c + outstream.c pager.c screen.c stream.c diff --git a/sdk/lib/conutils/README.txt b/sdk/lib/conutils/README.txt index 680512c8204..4a4a7a73a8a 100644 --- a/sdk/lib/conutils/README.txt +++ b/sdk/lib/conutils/README.txt @@ -1,7 +1,9 @@ -The ReactOS Console Utilities Library v0.1 +The ReactOS Console Utilities Library v0.2 ========================================== -COPYRIGHT: Under GPLv2, see COPYING in the top level directory. +LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) +COPYRIGHT: Copyright 2017-2018 ReactOS Team + Copyright 2017-2018 Hermes Belusca-Maito CREDITS: Thanks to the many people who originally wrote the code that finally ended up inside this library, with more or less refactoring, or whose code served as a basis for some functions of the library. @@ -21,23 +23,22 @@ together with printf-like functionality. CONTENTS ~-~-~-~- -0. 'conutils_base' (utils.c and utils.h): Base set of functions for loading +-- Main ConUtils Library -- + +0. "BASE" (utils.c and utils.h): Base set of functions for loading string resources and message strings, and handle type identification. -1. 'conutils_stream' (stream.c and stream.h): Console Stream API (CON_STREAM): +1. "STREAM" (stream.c and stream.h, instream.c and instream.h, outstream.c + and outstream.h): Console Stream API (CON_STREAM): Stream initialization, basic ConStreamRead/Write. Stream utility functions: - ConPuts/Printf, ConResPuts/Printf, ConMsgPuts/Printf. - Depends on 'conutils_base'. + ConPuts/Printf, ConResPuts/Printf, ConMsgPuts/Printf. Depends on "BASE". -2. 'conutils_screen' (screen.c and screen.h): Console Screen API (CON_SCREEN): +2. "SCREEN" (screen.c and screen.h): Console Screen API (CON_SCREEN): Introduces the notion of console/terminal screen around the streams. Manages console/terminal screen metrics for Win32 consoles and TTYs (serial...). Additional Screen utility functions. - Depends on 'conutils_stream', and indirectly on 'conutils_base'. + Depends on "STREAM", and indirectly on "BASE". -3. 'conutils_pager' (pager.c and pager.h): Console Pager API (CON_PAGER): +3. "PAGER" (pager.c and pager.h): Console Pager API (CON_PAGER): Implements core console/terminal paging functionality around console screens. - Depends on 'conutils_screen' and indirectly on 'conutils_stream' and - 'conutils_base'. - -4. More to come! + Depends on "SCREEN", and indirectly on "STREAM" and "BASE". diff --git a/sdk/lib/conutils/conutils.h b/sdk/lib/conutils/conutils.h index 2857bd450b0..93d79e8848a 100644 --- a/sdk/lib/conutils/conutils.h +++ b/sdk/lib/conutils/conutils.h @@ -1,25 +1,29 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/conutils.h - * PURPOSE: Provides simple 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. + * PROJECT: ReactOS Console Utilities Library + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Provides simple 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 */ #ifndef __CONUTILS_H__ #define __CONUTILS_H__ +#pragma once + #ifndef _UNICODE #error The ConUtils library only supports compilation with _UNICODE defined, at the moment! #endif #include "utils.h" #include "stream.h" +// #include "instream.h" +#include "outstream.h" #include "screen.h" #include "pager.h" #endif /* __CONUTILS_H__ */ + +/* EOF */ diff --git a/sdk/lib/conutils/instream.c b/sdk/lib/conutils/instream.c new file mode 100644 index 00000000000..a9fa105766c --- /dev/null +++ b/sdk/lib/conutils/instream.c @@ -0,0 +1,49 @@ +/* + * 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 + */ + +/* + * 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 +#include +#endif /* USE_CRT */ + +#include // limits.h // For MB_LEN_MAX + +#include +#include +#include +#include // MAKEINTRESOURCEW, RT_STRING +#include // Console APIs (only if kernel32 support included) +#include + +/* PSEH for SEH Support */ +#include + +#include "conutils.h" +#include "stream.h" +#include "stream_private.h" + + +/* + * Console I/O utility API -- Input + */ + +/* EOF */ diff --git a/sdk/lib/conutils/instream.h b/sdk/lib/conutils/instream.h new file mode 100644 index 00000000000..d16a9c30de1 --- /dev/null +++ b/sdk/lib/conutils/instream.h @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#ifndef __INSTREAM_H__ +#define __INSTREAM_H__ + +#pragma once + +/* + * 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 + +#ifndef _UNICODE +#error The ConUtils library at the moment only supports compilation with _UNICODE defined! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Shadow type, implementation-specific +typedef struct _CON_STREAM CON_STREAM, *PCON_STREAM; + +/* + * Console I/O utility API -- Input + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __INSTREAM_H__ */ + +/* EOF */ diff --git a/sdk/lib/conutils/outstream.c b/sdk/lib/conutils/outstream.c new file mode 100644 index 00000000000..63adaf31841 --- /dev/null +++ b/sdk/lib/conutils/outstream.c @@ -0,0 +1,762 @@ +/* + * 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 + */ + +/* + * 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 +#include +#endif /* USE_CRT */ + +#include // limits.h // For MB_LEN_MAX + +#include +#include +#include +#include // MAKEINTRESOURCEW, RT_STRING +#include // Console APIs (only if kernel32 support included) +#include + +/* PSEH for SEH Support */ +#include + +#include "conutils.h" +#include "stream.h" +#include "stream_private.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 utility API -- 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 +} + +/* EOF */ diff --git a/sdk/lib/conutils/outstream.h b/sdk/lib/conutils/outstream.h new file mode 100644 index 00000000000..a4099e2e10b --- /dev/null +++ b/sdk/lib/conutils/outstream.h @@ -0,0 +1,162 @@ +/* + * 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 + */ + +#ifndef __OUTSTREAM_H__ +#define __OUTSTREAM_H__ + +#pragma once + +/* + * 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 + +#ifndef _UNICODE +#error The ConUtils library at the moment only supports compilation with _UNICODE defined! +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Shadow type, implementation-specific +typedef struct _CON_STREAM CON_STREAM, *PCON_STREAM; + +// typedef INT (__stdcall *CON_READ_FUNC)(IN PCON_STREAM, IN PTCHAR, IN DWORD); + // Stream, szStr, len +typedef INT (__stdcall *CON_WRITE_FUNC)(IN PCON_STREAM, IN PTCHAR, IN DWORD); + + +/* + * Console I/O utility API -- Output + */ + +INT +__stdcall +ConWrite( + IN PCON_STREAM Stream, + IN PTCHAR szStr, + IN DWORD len); + +INT +ConStreamWrite( + IN PCON_STREAM Stream, + IN PTCHAR szStr, + IN DWORD len); + +INT +ConPuts( + IN PCON_STREAM Stream, + IN LPWSTR szStr); + +INT +ConPrintfV( + IN PCON_STREAM Stream, + IN LPWSTR szStr, + IN va_list args); // arg_ptr + +INT +__cdecl +ConPrintf( + IN PCON_STREAM Stream, + IN LPWSTR szStr, + ...); + +INT +ConResPutsEx( + IN PCON_STREAM Stream, + IN HINSTANCE hInstance OPTIONAL, + IN UINT uID); + +INT +ConResPuts( + IN PCON_STREAM Stream, + IN UINT uID); + +INT +ConResPrintfExV( + IN PCON_STREAM Stream, + IN HINSTANCE hInstance OPTIONAL, + IN UINT uID, + IN va_list args); // arg_ptr + +INT +ConResPrintfV( + IN PCON_STREAM Stream, + IN UINT uID, + IN va_list args); // arg_ptr + +INT +__cdecl +ConResPrintfEx( + IN PCON_STREAM Stream, + IN HINSTANCE hInstance OPTIONAL, + IN UINT uID, + ...); + +INT +__cdecl +ConResPrintf( + IN PCON_STREAM Stream, + IN UINT uID, + ...); + +INT +ConMsgPuts( + IN PCON_STREAM Stream, + IN DWORD dwFlags, + IN LPCVOID lpSource OPTIONAL, + IN DWORD dwMessageId, + IN DWORD dwLanguageId); + +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 +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 +__cdecl +ConMsgPrintf( + IN PCON_STREAM Stream, + IN DWORD dwFlags, + IN LPCVOID lpSource OPTIONAL, + IN DWORD dwMessageId, + IN DWORD dwLanguageId, + ...); + + + +VOID +ConClearLine(IN PCON_STREAM Stream); + + +#ifdef __cplusplus +} +#endif + +#endif /* __OUTSTREAM_H__ */ + +/* EOF */ diff --git a/sdk/lib/conutils/pager.c b/sdk/lib/conutils/pager.c index cc2c2c4181b..ac6bd3f0b51 100644 --- a/sdk/lib/conutils/pager.c +++ b/sdk/lib/conutils/pager.c @@ -1,19 +1,15 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/pager.c - * PURPOSE: Console/terminal paging functionality. - * PROGRAMMERS: - Hermes Belusca-Maito (for the library); - * - All programmers who wrote the different console applications - * from which I took those functions and improved them. + * PROJECT: ReactOS Console Utilities Library + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Console/terminal paging functionality. + * COPYRIGHT: Copyright 2017-2018 ReactOS Team + * Copyright 2017-2018 Hermes Belusca-Maito */ /* FIXME: Temporary HACK before we cleanly support UNICODE functions */ #define UNICODE #define _UNICODE -#include // limits.h // For MB_LEN_MAX - #include #include // #include @@ -158,3 +154,5 @@ ConResPaging( return ConResPagingEx(Pager, PagePrompt, StartPaging, NULL /*GetModuleHandleW(NULL)*/, uID); } + +/* EOF */ diff --git a/sdk/lib/conutils/pager.h b/sdk/lib/conutils/pager.h index 6c62ab955d5..486b520d124 100644 --- a/sdk/lib/conutils/pager.h +++ b/sdk/lib/conutils/pager.h @@ -1,16 +1,16 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/pager.h - * PURPOSE: Console/terminal paging functionality. - * PROGRAMMERS: - Hermes Belusca-Maito (for the library); - * - All programmers who wrote the different console applications - * from which I took those functions and improved them. + * PROJECT: ReactOS Console Utilities Library + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Console/terminal paging functionality. + * COPYRIGHT: Copyright 2017-2018 ReactOS Team + * Copyright 2017-2018 Hermes Belusca-Maito */ #ifndef __PAGER_H__ #define __PAGER_H__ +#pragma once + #ifndef _UNICODE #error The ConUtils library at the moment only supports compilation with _UNICODE defined! #endif @@ -42,6 +42,7 @@ do { \ (pPager)->LineCount = 0; \ } while (0) + // Pager, Done, Total typedef BOOL (__stdcall *PAGE_PROMPT)(IN PCON_PAGER, IN DWORD, IN DWORD); @@ -78,4 +79,7 @@ ConResPaging( #ifdef __cplusplus } #endif + #endif /* __PAGER_H__ */ + +/* EOF */ diff --git a/sdk/lib/conutils/screen.c b/sdk/lib/conutils/screen.c index b6bc8681593..04f0b6e91d4 100644 --- a/sdk/lib/conutils/screen.c +++ b/sdk/lib/conutils/screen.c @@ -1,19 +1,15 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/screen.c - * PURPOSE: Console/terminal screen management. - * PROGRAMMERS: - Hermes Belusca-Maito (for the library); - * - All programmers who wrote the different console applications - * from which I took those functions and improved them. + * PROJECT: ReactOS Console Utilities Library + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Console/terminal screen management. + * COPYRIGHT: Copyright 2017-2018 ReactOS Team + * Copyright 2017-2018 Hermes Belusca-Maito */ /* FIXME: Temporary HACK before we cleanly support UNICODE functions */ #define UNICODE #define _UNICODE -#include // limits.h // For MB_LEN_MAX - #include #include // #include @@ -173,3 +169,5 @@ ConClearScreen(IN PCON_SCREEN Screen) CON_STREAM_WRITE(Screen->Stream, &ch, 1); } } + +/* EOF */ diff --git a/sdk/lib/conutils/screen.h b/sdk/lib/conutils/screen.h index bc2cce9d14a..2ab11a7a659 100644 --- a/sdk/lib/conutils/screen.h +++ b/sdk/lib/conutils/screen.h @@ -1,16 +1,16 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/screen.h - * PURPOSE: Console/terminal screen management. - * PROGRAMMERS: - Hermes Belusca-Maito (for the library); - * - All programmers who wrote the different console applications - * from which I took those functions and improved them. + * PROJECT: ReactOS Console Utilities Library + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Console/terminal screen management. + * COPYRIGHT: Copyright 2017-2018 ReactOS Team + * Copyright 2017-2018 Hermes Belusca-Maito */ #ifndef __SCREEN_H__ #define __SCREEN_H__ +#pragma once + #ifndef _UNICODE #error The ConUtils library at the moment only supports compilation with _UNICODE defined! #endif @@ -60,4 +60,7 @@ ConClearScreen(IN PCON_SCREEN Screen); #ifdef __cplusplus } #endif + #endif /* __SCREEN_H__ */ + +/* EOF */ diff --git a/sdk/lib/conutils/stream.c b/sdk/lib/conutils/stream.c index f31d397c27c..5b843c183e8 100644 --- a/sdk/lib/conutils/stream.c +++ b/sdk/lib/conutils/stream.c @@ -1,13 +1,11 @@ /* - * 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. + * 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 */ /* @@ -27,63 +25,22 @@ #include #endif /* USE_CRT */ -#include // limits.h // For MB_LEN_MAX - #include #include #include -#include // MAKEINTRESOURCEW, RT_STRING +// #include // MAKEINTRESOURCEW, RT_STRING #include // Console APIs (only if kernel32 support included) #include -/* PSEH for SEH Support */ -#include - #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... +#include "stream_private.h" /* * 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. @@ -339,713 +296,4 @@ ConStreamSetOSHandle( #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 -} - +/* EOF */ diff --git a/sdk/lib/conutils/stream.h b/sdk/lib/conutils/stream.h index 7b6038705b2..f3804b61034 100644 --- a/sdk/lib/conutils/stream.h +++ b/sdk/lib/conutils/stream.h @@ -1,18 +1,18 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/stream.h - * 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. + * 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 */ #ifndef __STREAM_H__ #define __STREAM_H__ +#pragma once + /* * Enable this define if you want to only use CRT functions to output * UNICODE stream to the console, as in the way explained by @@ -152,124 +152,10 @@ ConStreamSetOSHandle( IN HANDLE Handle); -/* - * Console I/O utility API - * (for the moment, only Output) - */ - -INT -__stdcall -ConWrite( - IN PCON_STREAM Stream, - IN PTCHAR szStr, - IN DWORD len); - -INT -ConStreamWrite( - IN PCON_STREAM Stream, - IN PTCHAR szStr, - IN DWORD len); - -INT -ConPuts( - IN PCON_STREAM Stream, - IN LPWSTR szStr); - -INT -ConPrintfV( - IN PCON_STREAM Stream, - IN LPWSTR szStr, - IN va_list args); // arg_ptr - -INT -__cdecl -ConPrintf( - IN PCON_STREAM Stream, - IN LPWSTR szStr, - ...); - -INT -ConResPutsEx( - IN PCON_STREAM Stream, - IN HINSTANCE hInstance OPTIONAL, - IN UINT uID); - -INT -ConResPuts( - IN PCON_STREAM Stream, - IN UINT uID); - -INT -ConResPrintfExV( - IN PCON_STREAM Stream, - IN HINSTANCE hInstance OPTIONAL, - IN UINT uID, - IN va_list args); // arg_ptr - -INT -ConResPrintfV( - IN PCON_STREAM Stream, - IN UINT uID, - IN va_list args); // arg_ptr - -INT -__cdecl -ConResPrintfEx( - IN PCON_STREAM Stream, - IN HINSTANCE hInstance OPTIONAL, - IN UINT uID, - ...); - -INT -__cdecl -ConResPrintf( - IN PCON_STREAM Stream, - IN UINT uID, - ...); - -INT -ConMsgPuts( - IN PCON_STREAM Stream, - IN DWORD dwFlags, - IN LPCVOID lpSource OPTIONAL, - IN DWORD dwMessageId, - IN DWORD dwLanguageId); - -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 -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 -__cdecl -ConMsgPrintf( - IN PCON_STREAM Stream, - IN DWORD dwFlags, - IN LPCVOID lpSource OPTIONAL, - IN DWORD dwMessageId, - IN DWORD dwLanguageId, - ...); - - - -VOID -ConClearLine(IN PCON_STREAM Stream); - - #ifdef __cplusplus } #endif + #endif /* __STREAM_H__ */ + +/* EOF */ diff --git a/sdk/lib/conutils/stream_private.h b/sdk/lib/conutils/stream_private.h new file mode 100644 index 00000000000..6b801dfb234 --- /dev/null +++ b/sdk/lib/conutils/stream_private.h @@ -0,0 +1,55 @@ +/* + * 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 + */ + +#ifndef __STREAM_PRIVATE_H__ +#define __STREAM_PRIVATE_H__ + +#pragma once + +/* + * Console I/O streams + */ + +#if 0 +// Shadow type, implementation-specific +typedef struct _CON_STREAM CON_STREAM, *PCON_STREAM; +#endif + +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; + +#endif /* __STREAM_PRIVATE_H__ */ + +/* EOF */ diff --git a/sdk/lib/conutils/utils.c b/sdk/lib/conutils/utils.c index b46ee911688..b5dfdeefd14 100644 --- a/sdk/lib/conutils/utils.c +++ b/sdk/lib/conutils/utils.c @@ -1,12 +1,10 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/utils.c - * PURPOSE: Base set of functions for loading string resources - * and message strings, and handle type identification. - * PROGRAMMERS: - Hermes Belusca-Maito (for the library); - * - All programmers who wrote the different console applications - * from which I took those functions and improved them. + * PROJECT: ReactOS Console Utilities Library + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Base set of functions for loading string resources + * and message strings, and handle type identification. + * COPYRIGHT: Copyright 2017-2018 ReactOS Team + * Copyright 2017-2018 Hermes Belusca-Maito */ /* FIXME: Temporary HACK before we cleanly support UNICODE functions */ @@ -232,3 +230,5 @@ IsConsoleHandle(IN HANDLE hHandle) */ return GetConsoleMode(hHandle, &dwMode); } + +/* EOF */ diff --git a/sdk/lib/conutils/utils.h b/sdk/lib/conutils/utils.h index 52063c25bf4..9f414ab0a84 100644 --- a/sdk/lib/conutils/utils.h +++ b/sdk/lib/conutils/utils.h @@ -1,17 +1,17 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/utils.h - * PURPOSE: Base set of functions for loading string resources - * and message strings, and handle type identification. - * PROGRAMMERS: - Hermes Belusca-Maito (for the library); - * - All programmers who wrote the different console applications - * from which I took those functions and improved them. + * PROJECT: ReactOS Console Utilities Library + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Base set of functions for loading string resources + * and message strings, and handle type identification. + * COPYRIGHT: Copyright 2017-2018 ReactOS Team + * Copyright 2017-2018 Hermes Belusca-Maito */ #ifndef __UTILS_H__ #define __UTILS_H__ +#pragma once + #ifndef _UNICODE #error The ConUtils library at the moment only supports compilation with _UNICODE defined! #endif @@ -57,4 +57,7 @@ IsConsoleHandle(IN HANDLE hHandle); #ifdef __cplusplus } #endif + #endif /* __UTILS_H__ */ + +/* EOF */