From eb198b854d56f6246cb2b09ff072f0729fe163d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Sat, 22 Oct 2016 21:54:29 +0000 Subject: [PATCH] [CONUTILS] - Introduce new functionalities related to console streams, a console screen management api (almost stubbed), a console pager api (adapted from code from CMD). This new functionality will be used in the future. See CORE-10504 for more details. - As this library is therefore growing up, split it in small parts (sublibraries) that can be used, with the following dependency scheme: base utils (standalone); streams depending on base; screen depending on streams; pager depending on screen. [APPS]: As a result, modify the CMakeLists of the different apps that use conutils to make them depend on the correct sublibrary. svn path=/trunk/; revision=73024 --- .../base/applications/cacls/CMakeLists.txt | 2 +- .../applications/cmdutils/clip/CMakeLists.txt | 2 +- .../applications/cmdutils/comp/CMakeLists.txt | 2 +- .../cmdutils/eventcreate/CMakeLists.txt | 4 +- .../applications/cmdutils/help/CMakeLists.txt | 2 +- .../applications/cmdutils/mode/CMakeLists.txt | 2 +- .../base/applications/logoff/CMakeLists.txt | 2 +- .../applications/network/net/CMakeLists.txt | 2 +- .../base/applications/shutdown/CMakeLists.txt | 2 +- reactos/base/system/chkdsk/CMakeLists.txt | 2 +- reactos/base/system/diskpart/CMakeLists.txt | 2 +- reactos/base/system/format/CMakeLists.txt | 2 +- reactos/base/system/subst/CMakeLists.txt | 2 +- reactos/sdk/lib/conutils/CMakeLists.txt | 37 +- reactos/sdk/lib/conutils/README.txt | 43 ++ reactos/sdk/lib/conutils/conutils.h | 241 +------ reactos/sdk/lib/conutils/pager.c | 148 ++++ reactos/sdk/lib/conutils/pager.h | 58 ++ reactos/sdk/lib/conutils/screen.c | 175 +++++ reactos/sdk/lib/conutils/screen.h | 47 ++ .../sdk/lib/conutils/{conutils.c => stream.c} | 671 +++++++++++------- reactos/sdk/lib/conutils/stream.h | 246 +++++++ reactos/sdk/lib/conutils/utils.c | 234 ++++++ reactos/sdk/lib/conutils/utils.h | 53 ++ 24 files changed, 1462 insertions(+), 519 deletions(-) create mode 100644 reactos/sdk/lib/conutils/README.txt create mode 100644 reactos/sdk/lib/conutils/pager.c create mode 100644 reactos/sdk/lib/conutils/pager.h create mode 100644 reactos/sdk/lib/conutils/screen.c create mode 100644 reactos/sdk/lib/conutils/screen.h rename reactos/sdk/lib/conutils/{conutils.c => stream.c} (56%) create mode 100644 reactos/sdk/lib/conutils/stream.h create mode 100644 reactos/sdk/lib/conutils/utils.c create mode 100644 reactos/sdk/lib/conutils/utils.h diff --git a/reactos/base/applications/cacls/CMakeLists.txt b/reactos/base/applications/cacls/CMakeLists.txt index 3abda6c12fe..d35f5af8122 100644 --- a/reactos/base/applications/cacls/CMakeLists.txt +++ b/reactos/base/applications/cacls/CMakeLists.txt @@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(cacls cacls.c cacls.rc) set_module_type(cacls win32cui UNICODE) -target_link_libraries(cacls conutils ${PSEH_LIB}) +target_link_libraries(cacls conutils_stream ${PSEH_LIB}) add_importlibs(cacls advapi32 user32 shell32 msvcrt kernel32) add_cd_file(TARGET cacls DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/cmdutils/clip/CMakeLists.txt b/reactos/base/applications/cmdutils/clip/CMakeLists.txt index f15c2e93101..38bba2ec6bc 100644 --- a/reactos/base/applications/cmdutils/clip/CMakeLists.txt +++ b/reactos/base/applications/cmdutils/clip/CMakeLists.txt @@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(clip clip.c clip.rc) set_module_type(clip win32cui UNICODE) -target_link_libraries(clip conutils ${PSEH_LIB}) +target_link_libraries(clip conutils_stream ${PSEH_LIB}) add_importlibs(clip advapi32 user32 msvcrt kernel32) add_cd_file(TARGET clip DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/cmdutils/comp/CMakeLists.txt b/reactos/base/applications/cmdutils/comp/CMakeLists.txt index 0e2ce7a573b..4bbe2163104 100644 --- a/reactos/base/applications/cmdutils/comp/CMakeLists.txt +++ b/reactos/base/applications/cmdutils/comp/CMakeLists.txt @@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(comp comp.c comp.rc) set_module_type(comp win32cui UNICODE) -target_link_libraries(comp conutils ${PSEH_LIB}) +target_link_libraries(comp conutils_stream ${PSEH_LIB}) add_importlibs(comp msvcrt kernel32) add_cd_file(TARGET comp DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt b/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt index 12dcf645a0a..49de7d5b033 100644 --- a/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt +++ b/reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt @@ -1,6 +1,4 @@ -PROJECT(eventcreate) - include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) ## The message string templates are in ANSI to reduce binary size @@ -9,6 +7,6 @@ add_message_headers(ANSI evtmsgstr.mc) add_executable(eventcreate eventcreate.c eventcreate.rc) set_module_type(eventcreate win32cui UNICODE) add_dependencies(eventcreate evtmsgstr) -target_link_libraries(eventcreate conutils ${PSEH_LIB}) +target_link_libraries(eventcreate conutils_stream ${PSEH_LIB}) add_importlibs(eventcreate advapi32 msvcrt kernel32) add_cd_file(TARGET eventcreate DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/cmdutils/help/CMakeLists.txt b/reactos/base/applications/cmdutils/help/CMakeLists.txt index 0ffad38fc79..f5ce2080bc8 100644 --- a/reactos/base/applications/cmdutils/help/CMakeLists.txt +++ b/reactos/base/applications/cmdutils/help/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(cmd_help help.c help.rc) set_module_type(cmd_help win32cui UNICODE) -target_link_libraries(cmd_help conutils ${PSEH_LIB}) +target_link_libraries(cmd_help conutils_stream ${PSEH_LIB}) add_importlibs(cmd_help msvcrt kernel32) set_target_properties(cmd_help PROPERTIES OUTPUT_NAME "help") add_cd_file(TARGET cmd_help DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/cmdutils/mode/CMakeLists.txt b/reactos/base/applications/cmdutils/mode/CMakeLists.txt index e5bcbc07f7e..2d1eee5d6ad 100644 --- a/reactos/base/applications/cmdutils/mode/CMakeLists.txt +++ b/reactos/base/applications/cmdutils/mode/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(mode mode.c mode.rc) set_module_type(mode win32cui UNICODE) -target_link_libraries(mode conutils ${PSEH_LIB}) +target_link_libraries(mode conutils_stream ${PSEH_LIB}) add_importlibs(mode user32 msvcrt kernel32) set_target_properties(mode PROPERTIES SUFFIX ".com") add_cd_file(TARGET mode DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/logoff/CMakeLists.txt b/reactos/base/applications/logoff/CMakeLists.txt index 798d0e1d60a..ccaf710b4be 100644 --- a/reactos/base/applications/logoff/CMakeLists.txt +++ b/reactos/base/applications/logoff/CMakeLists.txt @@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(logoff logoff.c logoff.rc) set_module_type(logoff win32cui UNICODE) -target_link_libraries(logoff conutils ${PSEH_LIB}) +target_link_libraries(logoff conutils_stream ${PSEH_LIB}) add_importlibs(logoff advapi32 user32 msvcrt kernel32) add_cd_file(TARGET logoff DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/network/net/CMakeLists.txt b/reactos/base/applications/network/net/CMakeLists.txt index 94c0c17f4e5..45b85d98d3e 100644 --- a/reactos/base/applications/network/net/CMakeLists.txt +++ b/reactos/base/applications/network/net/CMakeLists.txt @@ -19,7 +19,7 @@ list(APPEND SOURCE add_executable(net ${SOURCE} net.rc) set_module_type(net win32cui UNICODE) -target_link_libraries(net conutils ${PSEH_LIB}) +target_link_libraries(net conutils_stream ${PSEH_LIB}) add_importlibs(net advapi32 netapi32 mpr msvcrt kernel32 ntdll) add_pch(net net.h SOURCE) add_cd_file(TARGET net DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/applications/shutdown/CMakeLists.txt b/reactos/base/applications/shutdown/CMakeLists.txt index 7f91d250ba5..e4a38ddd048 100644 --- a/reactos/base/applications/shutdown/CMakeLists.txt +++ b/reactos/base/applications/shutdown/CMakeLists.txt @@ -9,7 +9,7 @@ list(APPEND SOURCE add_executable(shutdown ${SOURCE} shutdown.rc) set_module_type(shutdown win32cui UNICODE) -target_link_libraries(shutdown conutils ${PSEH_LIB}) +target_link_libraries(shutdown conutils_stream ${PSEH_LIB}) add_importlibs(shutdown advapi32 user32 powrprof msvcrt kernel32) add_pch(shutdown precomp.h SOURCE) add_cd_file(TARGET shutdown DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/system/chkdsk/CMakeLists.txt b/reactos/base/system/chkdsk/CMakeLists.txt index c89a29cdcf4..5e759105cca 100644 --- a/reactos/base/system/chkdsk/CMakeLists.txt +++ b/reactos/base/system/chkdsk/CMakeLists.txt @@ -4,6 +4,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(chkdsk chkdsk.c chkdsk.rc) set_module_type(chkdsk win32cui UNICODE) -target_link_libraries(chkdsk conutils ${PSEH_LIB}) +target_link_libraries(chkdsk conutils_stream ${PSEH_LIB}) add_importlibs(chkdsk fmifs msvcrt kernel32 ntdll) add_cd_file(TARGET chkdsk DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/system/diskpart/CMakeLists.txt b/reactos/base/system/diskpart/CMakeLists.txt index 7675dad1d83..93a0ee3e0f1 100644 --- a/reactos/base/system/diskpart/CMakeLists.txt +++ b/reactos/base/system/diskpart/CMakeLists.txt @@ -45,7 +45,7 @@ list(APPEND SOURCE add_executable(diskpart ${SOURCE} diskpart.rc) set_module_type(diskpart win32cui UNICODE) -target_link_libraries(diskpart conutils ${PSEH_LIB}) +target_link_libraries(diskpart conutils_stream ${PSEH_LIB}) add_importlibs(diskpart advapi32 msvcrt kernel32 ntdll) if(MSVC) diff --git a/reactos/base/system/format/CMakeLists.txt b/reactos/base/system/format/CMakeLists.txt index eb0c275b4a2..edf5603f850 100644 --- a/reactos/base/system/format/CMakeLists.txt +++ b/reactos/base/system/format/CMakeLists.txt @@ -4,7 +4,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(format format.c format.rc) set_module_type(format win32cui UNICODE) -target_link_libraries(format conutils ${PSEH_LIB}) +target_link_libraries(format conutils_stream ${PSEH_LIB}) add_importlibs(format fmifs msvcrt kernel32 ntdll) set_target_properties(format PROPERTIES SUFFIX ".com") add_cd_file(TARGET format DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/system/subst/CMakeLists.txt b/reactos/base/system/subst/CMakeLists.txt index 7531f5daa03..e625cbfc2ab 100644 --- a/reactos/base/system/subst/CMakeLists.txt +++ b/reactos/base/system/subst/CMakeLists.txt @@ -3,6 +3,6 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils) add_executable(subst subst.c subst.rc) set_module_type(subst win32cui UNICODE) -target_link_libraries(subst conutils ${PSEH_LIB}) +target_link_libraries(subst conutils_stream ${PSEH_LIB}) add_importlibs(subst msvcrt kernel32 ntdll) add_cd_file(TARGET subst DESTINATION reactos/system32 FOR all) diff --git a/reactos/sdk/lib/conutils/CMakeLists.txt b/reactos/sdk/lib/conutils/CMakeLists.txt index ba7e49ad0a9..9d8d7dabdd0 100644 --- a/reactos/sdk/lib/conutils/CMakeLists.txt +++ b/reactos/sdk/lib/conutils/CMakeLists.txt @@ -1,3 +1,36 @@ -add_library(conutils conutils.c) -add_dependencies(conutils xdk) +## +## 'conutils_base': the base utility library. +## +add_library(conutils_base utils.c) +add_dependencies(conutils_base xdk) +target_link_libraries(conutils_base ${PSEH_LIB}) +add_importlibs(conutils_base msvcrt kernel32) + +## +## 'conutils_stream': Console Stream API library. +## +add_library(conutils_stream stream.c) +target_link_libraries(conutils_stream PUBLIC conutils_base) +# add_dependencies(conutils_stream conutils_base xdk) + +## +## 'conutils_screen': Console Screen API library. +## +add_library(conutils_screen screen.c) +target_link_libraries(conutils_screen PUBLIC conutils_stream conutils_base) +# add_dependencies(conutils_screen conutils_stream conutils_base xdk) + +## +## 'conutils_pager': Console Pager API library. +## +add_library(conutils_pager pager.c) +target_link_libraries(conutils_pager PUBLIC conutils_screen conutils_stream conutils_base) +# add_dependencies(conutils_pager conutils_screen conutils_stream conutils_base xdk) + +## +## 'conutils': Library target that groups everything together. +## +add_library(conutils INTERFACE) +target_link_libraries(conutils INTERFACE conutils_pager conutils_screen conutils_stream conutils_base) +# add_dependencies(conutils conutils_pager conutils_screen conutils_stream conutils_base xdk) diff --git a/reactos/sdk/lib/conutils/README.txt b/reactos/sdk/lib/conutils/README.txt new file mode 100644 index 00000000000..245fe4c27dd --- /dev/null +++ b/reactos/sdk/lib/conutils/README.txt @@ -0,0 +1,43 @@ +The ReactOS Console Utilities Library v0.1 +========================================== + +COPYRIGHT: Under GPLv2, see COPYING in the top level directory. +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. + + +INTRODUCTION +~-~-~-~-~-~- + +This library contains common functions used in many places inside the ReactOS +console utilities and the ReactOS Command-Line Interpreter. Most of these +functions are related with internationalisation and the problem of correctly +displaying Unicode text on the console. Besides those, helpful functions for +retrieving strings and messages from application resources are provided, +together with printf-like functionality. + + +CONTENTS +~-~-~-~- + +0. 'conutils_base' library (utils.c and utils.h): Base set of functions for + loading string resources and message strings, and handle type identification. + +1. 'conutils_stream' library (stream.c and stream.h): Console Stream API (CON_STREAM): + Stream initialization, basic ConStreamRead/Write. Stream utility functions: + ConPuts/Printf, ConResPuts/Printf, ConMsgPuts/Printf. + Depends on the 'conutils_base' library. + +2. 'conutils_screen' library (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 the 'conutils_stream' library, and indirectly on 'conutils_base'. + +3. 'conutils_pager' library (pager.c and pager.h): Console Pager API (CON_PAGER): + Implements core console/terminal paging functionality around console screens. + Depends on the 'conutils_screen' library, and indirectly on 'conutils_stream' + and 'conutils_base'. + +4. More to come! diff --git a/reactos/sdk/lib/conutils/conutils.h b/reactos/sdk/lib/conutils/conutils.h index ba597cad688..2857bd450b0 100644 --- a/reactos/sdk/lib/conutils/conutils.h +++ b/reactos/sdk/lib/conutils/conutils.h @@ -2,10 +2,10 @@ * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Console Utilities Library * FILE: sdk/lib/conutils/conutils.h - * PURPOSE: Provides simple ready-to-use abstraction wrappers around - * CRT streams or Win32 console API I/O functions, to deal with - * i18n + Unicode related problems. - * PROGRAMMERS: - Hermes Belusca-Maito (for making this library); + * 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. */ @@ -13,238 +13,13 @@ #ifndef __CONUTILS_H__ #define __CONUTILS_H__ -/* - * 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 only supports compilation with _UNICODE defined, at the moment! #endif -/* - * General-purpose utility functions (wrappers around, - * or reimplementations of, Win32 APIs). - */ - -INT -WINAPI -K32LoadStringW( - IN HINSTANCE hInstance OPTIONAL, - IN UINT uID, - OUT LPWSTR lpBuffer, - IN INT nBufferMax); - -DWORD -WINAPI -FormatMessageSafeW( - IN DWORD dwFlags, - IN LPCVOID lpSource OPTIONAL, - IN DWORD dwMessageId, - IN DWORD dwLanguageId, - OUT LPWSTR lpBuffer, - IN DWORD nSize, - IN va_list *Arguments OPTIONAL); - - -/* - * Console I/O streams - */ - -/* - * See http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html - * for more information. - */ -typedef enum _CON_STREAM_MODE -{ - Binary = 0, // #define _O_BINARY 0x8000 // file mode is binary (untranslated) - // #define _O_RAW _O_BINARY - AnsiText, // #define _O_TEXT 0x4000 // file mode is text (translated) -- "ANSI" - WideText, // #define _O_WTEXT 0x10000 // file mode is UTF16 with BOM (translated) -- "Unicode" of Windows - UTF16Text, // #define _O_U16TEXT 0x20000 // file mode is UTF16 no BOM (translated) -- "" "" - UTF8Text, // #define _O_U8TEXT 0x40000 // file mode is UTF8 no BOM (translated) -} CON_STREAM_MODE, *PCON_STREAM_MODE; - -// Shadow type, implementation-specific -typedef struct _CON_STREAM CON_STREAM, *PCON_STREAM; - - // Stream, szStr, len -typedef INT (__stdcall *CON_WRITE_FUNC)(IN PCON_STREAM, IN PTCHAR, IN DWORD); - -/* - * Standard console streams, initialized by - * calls to ConStreamInit/ConInitStdStreams. - */ -#if 0 // FIXME! -extern CON_STREAM StdStreams[3]; -#define StdIn (&StdStreams[0]) // TODO! -#define StdOut (&StdStreams[1]) -#define StdErr (&StdStreams[2]) -#else -extern CON_STREAM csStdIn; -extern CON_STREAM csStdOut; -extern CON_STREAM csStdErr; -#define StdIn (&csStdIn) // TODO! -#define StdOut (&csStdOut) -#define StdErr (&csStdErr) -#endif - -// static -BOOL -IsConsoleHandle(IN HANDLE hHandle); - -BOOL -ConStreamInitEx( - OUT PCON_STREAM Stream, - IN PVOID Handle, - IN CON_STREAM_MODE Mode, - IN CON_WRITE_FUNC WriteFunc OPTIONAL); - -BOOL -ConStreamInit( - OUT PCON_STREAM Stream, - IN PVOID Handle, - IN CON_STREAM_MODE Mode); - - -/* Console Standard Streams initialization helpers */ -#ifdef _UNICODE - -#ifdef USE_CRT -#define ConInitStdStreams() \ -do { \ - ConStreamInit(StdOut, stdout, UTF16Text); \ - ConStreamInit(StdErr, stderr, UTF16Text); \ -} while(0) -#else -#define ConInitStdStreams() \ -do { \ - ConStreamInit(StdOut, GetStdHandle(STD_OUTPUT_HANDLE), UTF16Text); \ - ConStreamInit(StdErr, GetStdHandle(STD_ERROR_HANDLE) , UTF16Text); \ -} while(0) -#endif /* defined(USE_CRT) */ - -#else - -#ifdef USE_CRT -#define ConInitStdStreams() \ -do { \ - ConStreamInit(StdOut, stdout, AnsiText); \ - ConStreamInit(StdErr, stderr, AnsiText); \ -} while(0) -#else -#define ConInitStdStreams() \ -do { \ - ConStreamInit(StdOut, GetStdHandle(STD_OUTPUT_HANDLE), AnsiText); \ - ConStreamInit(StdErr, GetStdHandle(STD_ERROR_HANDLE) , AnsiText); \ -} while(0) -#endif /* defined(USE_CRT) */ - -#endif /* defined(_UNICODE) */ - - - -/* - * Console I/O utility API - * (for the moment, only Output) - */ - -/*** Redundant defines to keep compat with existing code for now... ***/ -/*** Must be removed later! ***/ - -#define CON_RC_STRING_MAX_SIZE 4096 -#define MAX_BUFFER_SIZE 4096 // some exotic programs set it to 5024 -#define OUTPUT_BUFFER_SIZE 4096 -// MAX_STRING_SIZE - -// #define MAX_MESSAGE_SIZE 512 - - -INT -__stdcall -ConWrite( - 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 -ConResPuts( - IN PCON_STREAM Stream, - IN UINT uID); - -INT -ConResPrintfV( - IN PCON_STREAM Stream, - IN UINT uID, - IN va_list args); // arg_ptr - -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, - ...); - - -// -// TODO: Add Console paged-output printf & ResPrintf functions! -// +#include "utils.h" +#include "stream.h" +#include "screen.h" +#include "pager.h" #endif /* __CONUTILS_H__ */ diff --git a/reactos/sdk/lib/conutils/pager.c b/reactos/sdk/lib/conutils/pager.c new file mode 100644 index 00000000000..9a5bae4bb9b --- /dev/null +++ b/reactos/sdk/lib/conutils/pager.c @@ -0,0 +1,148 @@ +/* + * 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. + */ + +/* FIXME: Temporary HACK before we cleanly support UNICODE functions */ +#define UNICODE +#define _UNICODE + +#include // limits.h // For MB_LEN_MAX + +#include +#include +// #include +#include // Console APIs (only if kernel32 support included) +#include + +#include "conutils.h" +#include "stream.h" +#include "screen.h" +#include "pager.h" + +// Temporary HACK +#define CON_STREAM_WRITE ConStreamWrite + + + +/* Returns TRUE when all the text is displayed, and FALSE if display is stopped */ +BOOL +ConWritePaging( + IN PCON_PAGER Pager, + IN PAGE_PROMPT PagePrompt, + IN BOOL StartPaging, + IN PTCHAR szStr, + IN DWORD len) +{ + CONSOLE_SCREEN_BUFFER_INFO csbi; + + /* Used to see how big the screen is */ + DWORD ScreenLines = 0; + + /* Chars since start of line */ + DWORD CharSL; + + DWORD from = 0, i = 0; + + /* Parameters validation */ + if (!Pager) + return FALSE; + + /* Reset LineCount and return if no string has been given */ + if (StartPaging == TRUE) + Pager->LineCount = 0; + if (szStr == NULL) + return TRUE; + + /* Get the size of the visual screen that can be printed to */ + if (!ConGetScreenInfo(Pager->Screen, &csbi)) + { + /* We assume it's a file handle */ + CON_STREAM_WRITE(Pager->Screen->Stream, szStr, len); + return TRUE; + } + + /* + * Get the number of lines currently displayed on screen, minus 1 + * to account for the "press any key..." prompt from PagePrompt(). + */ + ScreenLines = (csbi.srWindow.Bottom - csbi.srWindow.Top); + CharSL = csbi.dwCursorPosition.X; + + /* Make sure the user doesn't have the screen too small */ + if (ScreenLines < 4) + { + CON_STREAM_WRITE(Pager->Screen->Stream, szStr, len); + return TRUE; + } + + while (i < len) + { + /* Search until the end of a line is reached */ + if (szStr[i++] != TEXT('\n') && ++CharSL < csbi.dwSize.X) + continue; + + Pager->LineCount++; + CharSL = 0; + + if (Pager->LineCount >= ScreenLines) + { + CON_STREAM_WRITE(Pager->Screen->Stream, &szStr[from], i-from); + from = i; + + /* Prompt the user; give him some values for statistics */ + // FIXME TODO: The prompt proc can also take ScreenLines ?? + if (!PagePrompt(Pager, from, len)) + return FALSE; + + // TODO: Recalculate 'ScreenLines' in case the user redimensions + // the window during the prompt. + + /* Reset the number of lines being printed */ + Pager->LineCount = 0; + } + } + if (i > from) + CON_STREAM_WRITE(Pager->Screen->Stream, &szStr[from], i-from); + + return TRUE; +} + +BOOL +ConPutsPaging( + IN PCON_PAGER Pager, + IN PAGE_PROMPT PagePrompt, + IN BOOL StartPaging, + IN LPTSTR szStr) +{ + DWORD len; + + /* Return if no string has been given */ + if (szStr == NULL) + return TRUE; + + len = wcslen(szStr); + return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, len); +} + +BOOL +ConResPaging( + IN PCON_PAGER Pager, + IN PAGE_PROMPT PagePrompt, + IN BOOL StartPaging, + IN UINT uID) +{ + INT Len; + PWCHAR szStr = NULL; + + Len = K32LoadStringW(GetModuleHandleW(NULL), uID, (PWSTR)&szStr, 0); + if (szStr && Len) + return ConWritePaging(Pager, PagePrompt, StartPaging, szStr, Len); + else + return TRUE; +} diff --git a/reactos/sdk/lib/conutils/pager.h b/reactos/sdk/lib/conutils/pager.h new file mode 100644 index 00000000000..9dce8beb82f --- /dev/null +++ b/reactos/sdk/lib/conutils/pager.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef __PAGER_H__ +#define __PAGER_H__ + +#ifndef _UNICODE +#error The ConUtils library at the moment only supports compilation with _UNICODE defined! +#endif + + +// #include + + +typedef struct _CON_PAGER +{ + PCON_SCREEN Screen; + + // TODO: Add more properties. Maybe those extra parameters + // of PAGE_PROMPT could go there? + + /* Used to count number of lines since last pause */ + DWORD LineCount; +} CON_PAGER, *PCON_PAGER; + + // Pager, Done, Total +typedef BOOL (__stdcall *PAGE_PROMPT)(IN PCON_PAGER, IN DWORD, IN DWORD); + +BOOL +ConWritePaging( + IN PCON_PAGER Pager, + IN PAGE_PROMPT PagePrompt, + IN BOOL StartPaging, + IN PTCHAR szStr, + IN DWORD len); + +BOOL +ConPutsPaging( + IN PCON_PAGER Pager, + IN PAGE_PROMPT PagePrompt, + IN BOOL StartPaging, + IN LPTSTR szStr); + +BOOL +ConResPaging( + IN PCON_PAGER Pager, + IN PAGE_PROMPT PagePrompt, + IN BOOL StartPaging, + IN UINT uID); + +#endif /* __PAGER_H__ */ diff --git a/reactos/sdk/lib/conutils/screen.c b/reactos/sdk/lib/conutils/screen.c new file mode 100644 index 00000000000..b6bc8681593 --- /dev/null +++ b/reactos/sdk/lib/conutils/screen.c @@ -0,0 +1,175 @@ +/* + * 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. + */ + +/* FIXME: Temporary HACK before we cleanly support UNICODE functions */ +#define UNICODE +#define _UNICODE + +#include // limits.h // For MB_LEN_MAX + +#include +#include +// #include +#include // Console APIs (only if kernel32 support included) +#include + +#include "conutils.h" +#include "stream.h" +#include "screen.h" + +// Temporary HACK +#define CON_STREAM_WRITE ConStreamWrite + + +#if 0 + +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 +} + +#endif + + +BOOL +ConGetScreenInfo( + IN PCON_SCREEN Screen, + OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi) +{ + BOOL Success; + HANDLE hOutput; + + /* Parameters validation */ + if (!Screen || !pcsbi) + return FALSE; + + hOutput = ConStreamGetOSHandle(Screen->Stream); + + /* Screen handle must be of TTY type (console or TTY) */ + if (!IsTTYHandle(hOutput)) + return FALSE; + + /* Update cached screen information */ + if (IsConsoleHandle(hOutput)) + { + Success = GetConsoleScreenBufferInfo(hOutput, &Screen->csbi); + } + else + { +#if 0 + /* TODO: Do something adequate for TTYs */ + // FIXME: At the moment we return hardcoded info. + Screen->csbi.dwSize.X = 80; + Screen->csbi.dwSize.Y = 25; + + // Screen->csbi.dwCursorPosition; + // Screen->csbi.wAttributes; + // Screen->csbi.srWindow; + Screen->csbi.dwMaximumWindowSize = Screen->csbi.dwSize; +#else + hOutput = CreateFileW(L"CONOUT$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL); + + Success = IsConsoleHandle(hOutput) && + GetConsoleScreenBufferInfo(hOutput, &Screen->csbi); + + CloseHandle(hOutput); +#endif + } + + if (Success) + { + /* Return it to the caller */ + *pcsbi = Screen->csbi; + } + + return Success; +} + +// For real consoles, erase everything, otherwise (TTY) erase just the "screen". +// FIXME: Or we can add a BOOL flag? +VOID +ConClearScreen(IN PCON_SCREEN Screen) +{ + HANDLE hOutput; + + /* Parameters validation */ + if (!Screen) return; + +#if 0 + /* Get the size of the visual screen */ + if (!ConGetScreenInfo(Screen, &csbi)) + { + /* We assume it's a file handle */ + return; + } +#endif + + hOutput = ConStreamGetOSHandle(Screen->Stream); + + if (IsConsoleHandle(hOutput)) + { + CONSOLE_SCREEN_BUFFER_INFO csbi; + COORD coPos; + DWORD dwWritten; + + GetConsoleScreenBufferInfo(hOutput, &csbi); + + coPos.X = 0; + coPos.Y = 0; + FillConsoleOutputAttribute(hOutput, csbi.wAttributes, + csbi.dwSize.X * csbi.dwSize.Y, + coPos, &dwWritten); + FillConsoleOutputCharacterW(hOutput, L' ', + csbi.dwSize.X * csbi.dwSize.Y, + coPos, &dwWritten); + SetConsoleCursorPosition(hOutput, coPos); + } + else if (IsTTYHandle(hOutput)) + { + /* Clear the full screen and move the cursor to (0,0) */ + ConPuts(Screen->Stream, L"\x1B[2J\x1B[1;1H"); + } + else + { + /* Issue a Form-Feed control */ + WCHAR ch = L'\f'; + CON_STREAM_WRITE(Screen->Stream, &ch, 1); + } +} diff --git a/reactos/sdk/lib/conutils/screen.h b/reactos/sdk/lib/conutils/screen.h new file mode 100644 index 00000000000..3365bec2553 --- /dev/null +++ b/reactos/sdk/lib/conutils/screen.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __SCREEN_H__ +#define __SCREEN_H__ + +#ifndef _UNICODE +#error The ConUtils library at the moment only supports compilation with _UNICODE defined! +#endif + + +#if 0 + +VOID +ConClearLine(IN PCON_STREAM Stream); + +#endif + + + +#include + +typedef struct _CON_SCREEN +{ + PCON_STREAM Stream; // Out + // PCON_STREAM In; + CONSOLE_SCREEN_BUFFER_INFO csbi; + CONSOLE_CURSOR_INFO cci; +} CON_SCREEN, *PCON_SCREEN; + +BOOL +ConGetScreenInfo( + IN PCON_SCREEN Screen, + OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi); + +VOID +ConClearScreen(IN PCON_SCREEN Screen); + + +#endif /* __SCREEN_H__ */ diff --git a/reactos/sdk/lib/conutils/conutils.c b/reactos/sdk/lib/conutils/stream.c similarity index 56% rename from reactos/sdk/lib/conutils/conutils.c rename to reactos/sdk/lib/conutils/stream.c index 226b998ed1e..433da40955e 100644 --- a/reactos/sdk/lib/conutils/conutils.c +++ b/reactos/sdk/lib/conutils/stream.c @@ -1,11 +1,11 @@ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Console Utilities Library - * FILE: sdk/lib/conutils/conutils.c - * PURPOSE: Provides simple ready-to-use abstraction wrappers around - * CRT streams or Win32 console API I/O functions, to deal with - * i18n + Unicode related problems. - * PROGRAMMERS: - Hermes Belusca-Maito (for making this 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. */ @@ -22,13 +22,13 @@ #define UNICODE #define _UNICODE -#include // limits.h // For MB_LEN_MAX - #ifdef USE_CRT #include #include #endif /* USE_CRT */ +#include // limits.h // For MB_LEN_MAX + #include #include #include @@ -40,161 +40,16 @@ #include #include "conutils.h" +#include "stream.h" + // #define RC_STRING_MAX_SIZE 4096 -// #define MAX_BUFFER_SIZE 4096 -// #define OUTPUT_BUFFER_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 - -/* - * General-purpose utility functions (wrappers around, - * or reimplementations of, Win32 APIs). - */ - -/* - * 'LoadStringW' API ripped from user32.dll to remove - * any dependency of this library from user32.dll - */ -INT -WINAPI -K32LoadStringW( - IN HINSTANCE hInstance OPTIONAL, - IN UINT uID, - OUT LPWSTR lpBuffer, - IN INT nBufferMax) -{ - HRSRC hrsrc; - HGLOBAL hmem; - WCHAR *p; - UINT i; - - if (!lpBuffer) - return 0; - - /* Use LOWORD (incremented by 1) as ResourceID */ - /* There are always blocks of 16 strings */ - // FindResourceExW(hInstance, RT_STRING, name, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); - // NOTE: Instead of using LANG_NEUTRAL, one might use LANG_USER_DEFAULT... - hrsrc = FindResourceW(hInstance, - MAKEINTRESOURCEW((LOWORD(uID) >> 4) + 1), - (LPWSTR)RT_STRING); - if (!hrsrc) return 0; - - hmem = LoadResource(hInstance, hrsrc); - if (!hmem) return 0; - - p = LockResource(hmem); - // FreeResource(hmem); - - /* Find the string we're looking for */ - uID &= 0x000F; /* Position in the block, same as % 16 */ - for (i = 0; i < uID; i++) - p += *p + 1; - - /* - * If nBufferMax == 0, then return a read-only pointer - * to the resource itself in lpBuffer it is assumed that - * lpBuffer is actually a (LPWSTR *). - */ - if (nBufferMax == 0) - { - *((LPWSTR*)lpBuffer) = p + 1; - return *p; - } - - i = min(nBufferMax - 1, *p); - if (i > 0) - { - memcpy(lpBuffer, p + 1, i * sizeof(WCHAR)); - lpBuffer[i] = L'\0'; - } - else - { - if (nBufferMax > 1) - { - lpBuffer[0] = L'\0'; - return 0; - } - } - - return i; -} - -/* - * "Safe" version of FormatMessageW, that does not crash if a malformed - * source string is retrieved and then being used for formatting. - * It basically wraps calls to FormatMessageW within SEH. - */ -DWORD -WINAPI -FormatMessageSafeW( - IN DWORD dwFlags, - IN LPCVOID lpSource OPTIONAL, - IN DWORD dwMessageId, - IN DWORD dwLanguageId, - OUT LPWSTR lpBuffer, - IN DWORD nSize, - IN va_list *Arguments OPTIONAL) -{ - DWORD dwLength = 0; - - _SEH2_TRY - { - /* - * Retrieve the message string. Wrap in SEH - * to protect from invalid string parameters. - */ - _SEH2_TRY - { - dwLength = FormatMessageW(dwFlags, - lpSource, - dwMessageId, - dwLanguageId, - lpBuffer, - nSize, - Arguments); - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - dwLength = 0; - - /* - * An exception occurred while calling FormatMessage, this is usually - * the sign that a parameter was invalid, either 'lpBuffer' was NULL - * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the - * array pointer 'Arguments' was NULL or did not contain enough elements, - * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the - * message string expected too many inserts. - * In this last case only, we can call again FormatMessage but ignore - * explicitely the inserts. The string that we will return to the user - * will not be pre-formatted. - */ - if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpBuffer) && - !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)) - { - /* Remove any possible harmful flags and always ignore inserts */ - dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY; - dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; - - /* If this call also throws an exception, we are really dead */ - dwLength = FormatMessageW(dwFlags, - lpSource, - dwMessageId, - dwLanguageId, - lpBuffer, - nSize, - NULL /* Arguments */); - } - } - _SEH2_END; - } - _SEH2_FINALLY - { - } - _SEH2_END; - - return dwLength; -} +// #define MAX_MESSAGE_SIZE 512 // See shutdown... /* @@ -208,12 +63,20 @@ typedef struct _CON_STREAM #ifdef USE_CRT FILE* fStream; #else + BOOL IsInitialized; + CRITICAL_SECTION Lock; + HANDLE hHandle; - BOOL bIsConsole; // TRUE if 'hHandle' refers to a console, - // in which case I/O UNICODE is directly used. /* - * 'Mode' flag is used to know the translation mode + * 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; @@ -228,7 +91,7 @@ typedef struct _CON_STREAM #if 0 // FIXME! CON_STREAM StdStreams[3] = { - {0}, // StdIn // TODO! + {0}, // StdIn {0}, // StdOut {0}, // StdErr }; @@ -239,88 +102,124 @@ CON_STREAM csStdErr; #endif -// static -BOOL -IsConsoleHandle(IN HANDLE hHandle) +/* Stream translation modes */ +#ifdef USE_CRT +/* Lookup table to convert CON_STREAM_MODE to CRT mode */ +static int ConToCRTMode[] = { - DWORD dwMode; + _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 - /* Check whether the handle may be that of a console... */ - if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR) - return FALSE; +#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) */ - /* - * It may be. Perform another test. The idea comes from the - * MSDN description of the WriteConsole API: - * - * "WriteConsole fails if it is used with a standard handle - * that is redirected to a file. If an application processes - * multilingual output that can be redirected, determine whether - * the output handle is a console handle (one method is to call - * the GetConsoleMode function and check whether it succeeds). - * If the handle is a console handle, call WriteConsole. If the - * handle is not a console handle, the output is redirected and - * you should call WriteFile to perform the I/O." - */ - return GetConsoleMode(hHandle, &dwMode); -} 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) + if (!Stream || !Handle || (Mode > UTF8Text)) return FALSE; - if (Mode > UTF8Text) - return FALSE; - - /* Use the default 'ConWrite' helper if nothing is specified */ - Stream->WriteFunc = (WriteFunc ? WriteFunc : ConWrite); #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) - }; Stream->fStream = (FILE*)Handle; - /* Set the correct file translation mode */ - // 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?? - if (Mode < ARRAYSIZE(ConToCRTMode)) - _setmode(_fileno(Stream->fStream), ConToCRTMode[Mode]); - else - _setmode(_fileno(Stream->fStream), _O_TEXT); // Default to ANSI text. - // _setmode returns the previous mode, or -1 if failure. -} #else - Stream->hHandle = (HANDLE)Handle; - Stream->bIsConsole = IsConsoleHandle(Stream->hHandle); - Stream->Mode = Mode; + if ((HANDLE)Handle == INVALID_HANDLE_VALUE) + return FALSE; - // NOTE: Or recompute them @ each ConWrite call? - if (Mode == AnsiText) - Stream->CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP - else if (Mode == UTF8Text) - Stream->CodePage = CP_UTF8; - else // Mode == Binary, WideText, UTF16Text - Stream->CodePage = 0; + /* + * 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; } @@ -328,9 +227,116 @@ BOOL ConStreamInit( OUT PCON_STREAM Stream, IN PVOID Handle, - IN CON_STREAM_MODE Mode) + IN CON_STREAM_MODE Mode, + IN UINT CacheCodePage OPTIONAL) { - return ConStreamInitEx(Stream, Handle, Mode, ConWrite); + 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 } @@ -339,7 +345,7 @@ ConStreamInit( * (for the moment, only Output) */ -// ConWriteStr +// NOTE: Should be called with the stream locked. INT __stdcall ConWrite( @@ -359,11 +365,33 @@ ConWrite( /* Check whether we are writing to a console */ // if (IsConsoleHandle(Stream->hHandle)) - if (Stream->bIsConsole) + if (Stream->IsConsole) { // TODO: Check if (ConStream->Mode == WideText or UTF16Text) ?? - WriteConsole(Stream->hHandle, szStr, len, &dwNumBytes, NULL); - return (INT)dwNumBytes; // Really return the number of chars written. + + /* + * 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! } /* @@ -378,16 +406,22 @@ ConWrite( */ if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text)) { -#ifndef _UNICODE +#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(/* Stream->CodePage */ CP_THREAD_ACP /* CP_ACP -- CP_OEMCP */, + 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 /* @@ -430,9 +464,20 @@ ConWrite( } else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText)) { -#ifdef _UNICODE + 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 . - CHAR *buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX); + buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX); if (!buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); @@ -440,6 +485,14 @@ ConWrite( } 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 /* @@ -478,10 +531,13 @@ ConWrite( #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); } @@ -497,18 +553,20 @@ ConWrite( if (!szStr || len == 0) return 0; - /* - * 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. - */ - // _setmode(_fileno(Stream->fStream), _O_U16TEXT); // Normally, already set before. #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 = wprintf(L"%.*s", total, szStr); + 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); @@ -525,12 +583,61 @@ ConWrite( } 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, @@ -538,7 +645,8 @@ ConPuts( { INT Len; - Len = Stream->WriteFunc(Stream, szStr, wcslen(szStr)); + Len = wcslen(szStr); + CON_STREAM_WRITE2(Stream, szStr, Len, Len); /* Fixup returned length in case of errors */ if (Len < 0) @@ -556,15 +664,18 @@ ConPrintfV( INT Len; WCHAR bufSrc[CON_RC_STRING_MAX_SIZE]; -#if 1 /////////////////////////////////////////////////////////////////////// 0 - PWSTR pEnd; - StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), &pEnd, NULL, 0, szStr, args); - Len = pEnd - bufSrc; -#else - StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args); - Len = wcslen(bufSrc); -#endif - Len = Stream->WriteFunc(Stream, bufSrc, Len); + // 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) @@ -583,10 +694,7 @@ ConPrintf( INT Len; va_list args; -#if 0 - Len = vfwprintf(stdout, szMsgBuf, arg_ptr); // vfprintf for direct ANSI - // or: Len = vwprintf(szMsgBuf, arg_ptr); -#endif + // Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI // StringCchPrintfW va_start(args, szStr); @@ -602,23 +710,16 @@ ConResPuts( IN UINT uID) { INT Len; -#if 0 - WCHAR bufSrc[CON_RC_STRING_MAX_SIZE]; - - // NOTE: We may use the special behaviour where nBufMaxSize == 0 - Len = K32LoadStringW(GetModuleHandleW(NULL), uID, bufSrc, ARRAYSIZE(bufSrc)); - if (Len) - Len = ConPuts(Stream, bufSrc); -#else PWCHAR szStr = NULL; + Len = K32LoadStringW(GetModuleHandleW(NULL), uID, (PWSTR)&szStr, 0); - if (Len) - Len = Stream->WriteFunc(Stream, szStr, Len); + 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; -#endif return Len; } @@ -709,7 +810,7 @@ ConMsgPuts( /* lpMsgBuf is NULL-terminated by FormatMessage */ // Len = ConPuts(Stream, lpMsgBuf); - Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength); + CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len); /* Fixup returned length in case of errors */ if (Len < 0) @@ -775,7 +876,7 @@ ConMsgPrintf2V( /* lpMsgBuf is NULL-terminated by FormatMessage */ Len = ConPrintfV(Stream, lpMsgBuf, args); - // Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength); + // CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len); /* Fixup returned length in case of errors */ if (Len < 0) @@ -835,7 +936,7 @@ ConMsgPrintfV( // ASSERT(dwLength != 0); // Len = ConPrintfV(Stream, lpMsgBuf, args); - Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength); + CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len); /* Fixup returned length in case of errors */ if (Len < 0) @@ -874,6 +975,38 @@ ConMsgPrintf( return Len; } -// -// TODO: Add Console paged-output printf & ResPrintf functions! -// + + +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 +} + diff --git a/reactos/sdk/lib/conutils/stream.h b/reactos/sdk/lib/conutils/stream.h new file mode 100644 index 00000000000..bc03d51bf47 --- /dev/null +++ b/reactos/sdk/lib/conutils/stream.h @@ -0,0 +1,246 @@ +/* + * 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. + */ + +#ifndef __STREAM_H__ +#define __STREAM_H__ + +/* + * 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 + +/* + * Console I/O streams + */ + +/* + * See http://archives.miloush.net/michkap/archive/2009/08/14/9869928.html + * for more information. + */ +typedef enum _CON_STREAM_MODE +{ + Binary = 0, // #define _O_BINARY 0x8000 // file mode is binary (untranslated) + // #define _O_RAW _O_BINARY + AnsiText, // #define _O_TEXT 0x4000 // file mode is text (translated) -- "ANSI" + WideText, // #define _O_WTEXT 0x10000 // file mode is UTF16 with BOM (translated) -- "Unicode" of Windows + UTF16Text, // #define _O_U16TEXT 0x20000 // file mode is UTF16 no BOM (translated) -- "" "" + UTF8Text, // #define _O_U8TEXT 0x40000 // file mode is UTF8 no BOM (translated) +} CON_STREAM_MODE, *PCON_STREAM_MODE; + +#define INVALID_CP ((UINT)-1) + +// 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); + +/* + * Standard console streams, initialized by + * calls to ConStreamInit/ConInitStdStreams. + */ +#if 0 // FIXME! +extern CON_STREAM StdStreams[3]; +#define StdIn (&StdStreams[0]) +#define StdOut (&StdStreams[1]) +#define StdErr (&StdStreams[2]) +#else +extern CON_STREAM csStdIn; +extern CON_STREAM csStdOut; +extern CON_STREAM csStdErr; +#define StdIn (&csStdIn ) +#define StdOut (&csStdOut) +#define StdErr (&csStdErr) +#endif + +BOOL +ConStreamInitEx( + OUT PCON_STREAM Stream, + IN PVOID Handle, + IN CON_STREAM_MODE Mode, + IN UINT CacheCodePage OPTIONAL, + // IN ReadWriteMode ???? + // IN CON_READ_FUNC ReadFunc OPTIONAL, + IN CON_WRITE_FUNC WriteFunc OPTIONAL); + +BOOL +ConStreamInit( + OUT PCON_STREAM Stream, + IN PVOID Handle, + // IN ReadWriteMode ???? + IN CON_STREAM_MODE Mode, + IN UINT CacheCodePage OPTIONAL); + + +/* Console Standard Streams initialization helpers */ +#ifdef USE_CRT +#define ConInitStdStreamsAndMode(Mode, CacheCodePage) \ +do { \ + ConStreamInit(StdIn , stdin , (Mode), (CacheCodePage)); \ + ConStreamInit(StdOut, stdout, (Mode), (CacheCodePage)); \ + ConStreamInit(StdErr, stderr, (Mode), (CacheCodePage)); \ +} while(0) +#else +#define ConInitStdStreamsAndMode(Mode, CacheCodePage) \ +do { \ + ConStreamInit(StdIn , GetStdHandle(STD_INPUT_HANDLE) , (Mode), (CacheCodePage)); \ + ConStreamInit(StdOut, GetStdHandle(STD_OUTPUT_HANDLE), (Mode), (CacheCodePage)); \ + ConStreamInit(StdErr, GetStdHandle(STD_ERROR_HANDLE) , (Mode), (CacheCodePage)); \ +} while(0) +#endif /* defined(USE_CRT) */ + +#ifdef _UNICODE +/* + * Use UTF8 by default for file output, because this mode is back-compatible + * with ANSI, and it displays nice on terminals that support UTF8 by default + * (not many terminals support UTF16 on the contrary). + */ +#define ConInitStdStreams() \ + ConInitStdStreamsAndMode(UTF8Text, INVALID_CP); // Cache code page unused +#else +/* Use ANSI by default for file output */ +#define ConInitStdStreams() \ + ConInitStdStreamsAndMode(AnsiText, INVALID_CP); +#endif /* defined(_UNICODE) */ + +/* Stream translation modes */ +BOOL +ConStreamSetMode( + IN PCON_STREAM Stream, + IN CON_STREAM_MODE Mode, + IN UINT CacheCodePage OPTIONAL); + +#ifdef USE_CRT +// FIXME! +#warning The ConStreamSetCacheCodePage function does not make much sense with the CRT! +#else +BOOL +ConStreamSetCacheCodePage( + IN PCON_STREAM Stream, + IN UINT CacheCodePage); +#endif + +HANDLE +ConStreamGetOSHandle( + IN PCON_STREAM Stream); + +BOOL +ConStreamSetOSHandle( + IN PCON_STREAM Stream, + 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 +ConResPuts( + IN PCON_STREAM Stream, + IN UINT uID); + +INT +ConResPrintfV( + IN PCON_STREAM Stream, + IN UINT uID, + IN va_list args); // arg_ptr + +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); + + +#endif /* __STREAM_H__ */ diff --git a/reactos/sdk/lib/conutils/utils.c b/reactos/sdk/lib/conutils/utils.c new file mode 100644 index 00000000000..b46ee911688 --- /dev/null +++ b/reactos/sdk/lib/conutils/utils.c @@ -0,0 +1,234 @@ +/* + * 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. + */ + +/* FIXME: Temporary HACK before we cleanly support UNICODE functions */ +#define UNICODE +#define _UNICODE + +#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 "utils.h" + +/* + * General-purpose utility functions (wrappers around, + * or reimplementations of, Win32 APIs). + */ + +#if 0 // The following function may be useful in the future... + +// Performs MultiByteToWideChar then WideCharToMultiByte . +// See https://github.com/pcman-bbs/pcman-windows/blob/master/Lite/StrUtils.h#l33 +// and http://www.openfoundry.org/svn/pcman/branches/OpenPCMan_2009/Lite/StrUtils.cpp +// for the idea. +int +MultiByteToMultiByte( + // IN WORD wTranslations, + IN DWORD dwFlags, + IN UINT SrcCodePage, + IN LPCSTR lpSrcString, + IN int cbSrcChar, + IN UINT DestCodePage, + OUT LPSTR wDestString OPTIONAL, + IN int cbDestChar +); + +#endif + +/* + * 'LoadStringW' API ripped from user32.dll to remove + * any dependency of this library from user32.dll + */ +INT +WINAPI +K32LoadStringW( + IN HINSTANCE hInstance OPTIONAL, + IN UINT uID, + OUT LPWSTR lpBuffer, + IN INT nBufferMax) +{ + HRSRC hrsrc; + HGLOBAL hmem; + WCHAR *p; + UINT i; + + if (!lpBuffer) + return 0; + + /* Use LOWORD (incremented by 1) as ResourceID */ + /* There are always blocks of 16 strings */ + // FindResourceExW(hInstance, RT_STRING, name, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + // NOTE: Instead of using LANG_NEUTRAL, one might use LANG_USER_DEFAULT... + hrsrc = FindResourceW(hInstance, + MAKEINTRESOURCEW((LOWORD(uID) >> 4) + 1), + (LPWSTR)RT_STRING); + if (!hrsrc) return 0; + + hmem = LoadResource(hInstance, hrsrc); + if (!hmem) return 0; + + p = LockResource(hmem); + // FreeResource(hmem); + + /* Find the string we're looking for */ + uID &= 0x000F; /* Position in the block, same as % 16 */ + for (i = 0; i < uID; i++) + p += *p + 1; + + /* + * If nBufferMax == 0, then return a read-only pointer + * to the resource itself in lpBuffer it is assumed that + * lpBuffer is actually a (LPWSTR *). + */ + if (nBufferMax == 0) + { + *((LPWSTR*)lpBuffer) = p + 1; + return *p; + } + + i = min(nBufferMax - 1, *p); + if (i > 0) + { + memcpy(lpBuffer, p + 1, i * sizeof(WCHAR)); + lpBuffer[i] = L'\0'; + } + else + { + if (nBufferMax > 1) + { + lpBuffer[0] = L'\0'; + return 0; + } + } + + return i; +} + +/* + * "Safe" version of FormatMessageW, that does not crash if a malformed + * source string is retrieved and then being used for formatting. + * It basically wraps calls to FormatMessageW within SEH. + */ +DWORD +WINAPI +FormatMessageSafeW( + IN DWORD dwFlags, + IN LPCVOID lpSource OPTIONAL, + IN DWORD dwMessageId, + IN DWORD dwLanguageId, + OUT LPWSTR lpBuffer, + IN DWORD nSize, + IN va_list *Arguments OPTIONAL) +{ + DWORD dwLength = 0; + + _SEH2_TRY + { + /* + * Retrieve the message string. Wrap in SEH + * to protect from invalid string parameters. + */ + _SEH2_TRY + { + dwLength = FormatMessageW(dwFlags, + lpSource, + dwMessageId, + dwLanguageId, + lpBuffer, + nSize, + Arguments); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + dwLength = 0; + + /* + * An exception occurred while calling FormatMessage, this is usually + * the sign that a parameter was invalid, either 'lpBuffer' was NULL + * but we did not pass the flag FORMAT_MESSAGE_ALLOCATE_BUFFER, or the + * array pointer 'Arguments' was NULL or did not contain enough elements, + * and we did not pass the flag FORMAT_MESSAGE_IGNORE_INSERTS, and the + * message string expected too many inserts. + * In this last case only, we can call again FormatMessage but ignore + * explicitely the inserts. The string that we will return to the user + * will not be pre-formatted. + */ + if (((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) || lpBuffer) && + !(dwFlags & FORMAT_MESSAGE_IGNORE_INSERTS)) + { + /* Remove any possible harmful flags and always ignore inserts */ + dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY; + dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; + + /* If this call also throws an exception, we are really dead */ + dwLength = FormatMessageW(dwFlags, + lpSource, + dwMessageId, + dwLanguageId, + lpBuffer, + nSize, + NULL /* Arguments */); + } + } + _SEH2_END; + } + _SEH2_FINALLY + { + } + _SEH2_END; + + return dwLength; +} + +BOOL +IsTTYHandle(IN HANDLE hHandle) +{ + /* + * More general test than IsConsoleHandle. Consoles, as well as + * serial ports, etc... verify this test, but only consoles verify + * the IsConsoleHandle test: indeed the latter checks whether + * the handle is really handled by the console subsystem. + */ + return ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) == FILE_TYPE_CHAR); +} + +BOOL +IsConsoleHandle(IN HANDLE hHandle) +{ + DWORD dwMode; + + /* Check whether the handle may be that of a console... */ + if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR) + return FALSE; + + /* + * It may be. Perform another test. The idea comes from the + * MSDN description of the WriteConsole API: + * + * "WriteConsole fails if it is used with a standard handle + * that is redirected to a file. If an application processes + * multilingual output that can be redirected, determine whether + * the output handle is a console handle (one method is to call + * the GetConsoleMode function and check whether it succeeds). + * If the handle is a console handle, call WriteConsole. If the + * handle is not a console handle, the output is redirected and + * you should call WriteFile to perform the I/O." + */ + return GetConsoleMode(hHandle, &dwMode); +} diff --git a/reactos/sdk/lib/conutils/utils.h b/reactos/sdk/lib/conutils/utils.h new file mode 100644 index 00000000000..c118ac6502c --- /dev/null +++ b/reactos/sdk/lib/conutils/utils.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#ifndef _UNICODE +#error The ConUtils library at the moment only supports compilation with _UNICODE defined! +#endif + +/* + * General-purpose utility functions (wrappers around, + * or reimplementations of, Win32 APIs). + */ + +INT +WINAPI +K32LoadStringW( + IN HINSTANCE hInstance OPTIONAL, + IN UINT uID, + OUT LPWSTR lpBuffer, + IN INT nBufferMax); + +DWORD +WINAPI +FormatMessageSafeW( + IN DWORD dwFlags, + IN LPCVOID lpSource OPTIONAL, + IN DWORD dwMessageId, + IN DWORD dwLanguageId, + OUT LPWSTR lpBuffer, + IN DWORD nSize, + IN va_list *Arguments OPTIONAL); + +BOOL +IsTTYHandle(IN HANDLE hHandle); + +BOOL +IsConsoleHandle(IN HANDLE hHandle); + + +// #include + + +#endif /* __UTILS_H__ */