[CONUTILS]: Create a C library for console output/input functions, last-error message...
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 5 Oct 2016 17:40:22 +0000 (17:40 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Wed, 5 Oct 2016 17:40:22 +0000 (17:40 +0000)
CORE-10504

[EVENTCREATE]: Adapt EventCreate to use ConUtils library, as an example of how to use this library.

svn path=/trunk/; revision=72913

reactos/base/applications/cmdutils/eventcreate/CMakeLists.txt
reactos/base/applications/cmdutils/eventcreate/eventcreate.c
reactos/sdk/lib/CMakeLists.txt
reactos/sdk/lib/conutils/CMakeLists.txt [new file with mode: 0644]
reactos/sdk/lib/conutils/conutils.c [new file with mode: 0644]
reactos/sdk/lib/conutils/conutils.h [new file with mode: 0644]

index e901b3a..12dcf64 100644 (file)
@@ -1,11 +1,14 @@
 
 PROJECT(eventcreate)
 
 
 PROJECT(eventcreate)
 
+include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
+
 ## The message string templates are in ANSI to reduce binary size
 add_message_headers(ANSI evtmsgstr.mc)
 
 add_executable(eventcreate eventcreate.c eventcreate.rc)
 set_module_type(eventcreate win32cui UNICODE)
 add_dependencies(eventcreate evtmsgstr)
 ## The message string templates are in ANSI to reduce binary size
 add_message_headers(ANSI evtmsgstr.mc)
 
 add_executable(eventcreate eventcreate.c eventcreate.rc)
 set_module_type(eventcreate win32cui UNICODE)
 add_dependencies(eventcreate evtmsgstr)
-add_importlibs(eventcreate advapi32 user32 msvcrt kernel32)
+target_link_libraries(eventcreate conutils ${PSEH_LIB})
+add_importlibs(eventcreate advapi32 msvcrt kernel32)
 add_cd_file(TARGET eventcreate DESTINATION reactos/system32 FOR all)
 add_cd_file(TARGET eventcreate DESTINATION reactos/system32 FOR all)
index 83af32e..d15fc75 100644 (file)
 
 #include <windef.h>
 #include <winbase.h>
 
 #include <windef.h>
 #include <winbase.h>
-#include <winuser.h>
 #include <winreg.h>
 
 #include <winreg.h>
 
+#include <conutils.h>
+
 #include <strsafe.h>
 
 #include "resource.h"
 #include <strsafe.h>
 
 #include "resource.h"
 #define APPLICATION_NAME    L"EventCreate"
 
 
 #define APPLICATION_NAME    L"EventCreate"
 
 
-VOID PrintStringV(LPWSTR szStr, va_list args)
-{
-    WCHAR bufFormatted[RC_STRING_MAX_SIZE];
-    CHAR bufFormattedOem[RC_STRING_MAX_SIZE];
-
-    _vsnwprintf(bufFormatted, ARRAYSIZE(bufFormatted), szStr, args);
-
-    CharToOemW(bufFormatted, bufFormattedOem);
-    // puts(bufFormattedOem);
-    fputs(bufFormattedOem, stdout);
-}
-
-VOID PrintString(LPWSTR szStr, ...)
-{
-    va_list args;
-
-    va_start(args, szStr);
-    PrintStringV(szStr, args);
-    va_end(args);
-}
-
-VOID PrintResourceStringV(UINT uID, va_list args)
-{
-    WCHAR bufSrc[RC_STRING_MAX_SIZE];
-
-    LoadStringW(GetModuleHandleW(NULL), uID, bufSrc, ARRAYSIZE(bufSrc));
-    PrintStringV(bufSrc, args);
-}
-
-VOID PrintResourceString(UINT uID, ...)
-{
-    va_list args;
-
-    va_start(args, uID);
-    PrintResourceStringV(uID, args);
-    va_end(args);
-}
-
 VOID PrintError(DWORD dwError)
 {
 VOID PrintError(DWORD dwError)
 {
-    LPWSTR lpMsgBuf = NULL;
-
     if (dwError == ERROR_SUCCESS)
         return;
 
     if (dwError == ERROR_SUCCESS)
         return;
 
-    FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-                   NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                   (LPWSTR)&lpMsgBuf, 0, NULL);
-    PrintString(L"%s\n", lpMsgBuf);
-    LocalFree(lpMsgBuf);
+    ConMsgPrintf(StdErr,
+                 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                 NULL, dwError, LANG_USER_DEFAULT);
+    ConPrintf(StdErr, L"\n");
 }
 
 
 }
 
 
@@ -594,7 +554,7 @@ Finalize:
     if (LogNameValid && !FoundLog)
     {
         /* We have specified a log but it does not exist! */
     if (LogNameValid && !FoundLog)
     {
         /* We have specified a log but it does not exist! */
-        PrintResourceString(IDS_LOG_NOT_FOUND, EventLogName);
+        ConResPrintf(StdErr, IDS_LOG_NOT_FOUND, EventLogName);
         goto Quit;
     }
 
         goto Quit;
     }
 
@@ -614,7 +574,7 @@ Finalize:
     if (/* LogSourceValid && */ FoundSource && SourceAlreadyExists)
     {
         /* The source is in another log than the specified one */
     if (/* LogSourceValid && */ FoundSource && SourceAlreadyExists)
     {
         /* The source is in another log than the specified one */
-        PrintResourceString(IDS_SOURCE_EXISTS, LogNameErr);
+        ConResPrintf(StdErr, IDS_SOURCE_EXISTS, LogNameErr);
         goto Quit;
     }
 
         goto Quit;
     }
 
@@ -634,7 +594,7 @@ Finalize:
             else
             {
                 /* This is NOT a custom source, we must return an error! */
             else
             {
                 /* This is NOT a custom source, we must return an error! */
-                PrintResourceString(IDS_SOURCE_NOT_CUSTOM);
+                ConResPrintf(StdErr, IDS_SOURCE_NOT_CUSTOM);
                 goto Quit;
             }
         }
                 goto Quit;
             }
         }
@@ -645,7 +605,7 @@ Finalize:
         if (!LogNameValid /* && !FoundLog */)
         {
             /* The log name is not specified, we cannot create the source */
         if (!LogNameValid /* && !FoundLog */)
         {
             /* The log name is not specified, we cannot create the source */
-            PrintResourceString(IDS_SOURCE_NOCREATE);
+            ConResPrintf(StdErr, IDS_SOURCE_NOCREATE);
             goto Quit;
         }
         else // LogNameValid && FoundLog
             goto Quit;
         }
         else // LogNameValid && FoundLog
@@ -667,8 +627,8 @@ Finalize:
             if (lRet != ERROR_SUCCESS)
             {
                 PrintError(lRet);
             if (lRet != ERROR_SUCCESS)
             {
                 PrintError(lRet);
-                PrintString(L"Impossible to create the source `%s' for log `%s'!\n",
-                            EventLogSource, EventLogName);
+                ConPrintf(StdErr, L"Impossible to create the source `%s' for log `%s'!\n",
+                          EventLogSource, EventLogName);
                 goto Quit;
             }
 
                 goto Quit;
             }
 
@@ -1044,15 +1004,15 @@ PrintParserError(PARSER_ERROR Error, ...)
     if (Error < ARRAYSIZE(ErrorIDs))
     {
         va_start(args, Error);
     if (Error < ARRAYSIZE(ErrorIDs))
     {
         va_start(args, Error);
-        PrintResourceStringV(ErrorIDs[Error], args);
+        ConResPrintfV(StdErr, ErrorIDs[Error], args);
         va_end(args);
 
         if (Error != Success)
         va_end(args);
 
         if (Error != Success)
-            PrintResourceString(IDS_USAGE);
+            ConResPrintf(StdErr, IDS_USAGE);
     }
     else
     {
     }
     else
     {
-        PrintString(L"PARSER: Unknown error %d\n", Error);
+        ConPrintf(StdErr, L"PARSER: Unknown error %d\n", Error);
     }
 }
 
     }
 }
 
@@ -1143,6 +1103,9 @@ int wmain(int argc, WCHAR* argv[])
 #define OPT_PASSWD  (Options[3])
 #define OPT_EVTID   (Options[8])
 
 #define OPT_PASSWD  (Options[3])
 #define OPT_EVTID   (Options[8])
 
+    /* Initialize the Console Standard Streams */
+    ConInitStdStreams();
+
     /* Parse command line for options */
     if (!DoParse(argc, argv, Options, ARRAYSIZE(Options), PrintParserError))
         return EXIT_FAILURE;
     /* Parse command line for options */
     if (!DoParse(argc, argv, Options, ARRAYSIZE(Options), PrintParserError))
         return EXIT_FAILURE;
@@ -1158,7 +1121,7 @@ int wmain(int argc, WCHAR* argv[])
             return EXIT_FAILURE;
         }
 
             return EXIT_FAILURE;
         }
 
-        PrintResourceString(IDS_HELP);
+        ConResPrintf(StdOut, IDS_HELP);
         return EXIT_SUCCESS;
     }
 
         return EXIT_SUCCESS;
     }
 
@@ -1166,19 +1129,19 @@ int wmain(int argc, WCHAR* argv[])
     {
         // TODO: Implement!
         if (szSystem)
     {
         // TODO: Implement!
         if (szSystem)
-            PrintResourceString(IDS_SWITCH_UNIMPLEMENTED, OPT_SYSTEM.OptionStr);
+            ConResPrintf(StdOut, IDS_SWITCH_UNIMPLEMENTED, OPT_SYSTEM.OptionStr);
         if (szDomainUser)
         if (szDomainUser)
-            PrintResourceString(IDS_SWITCH_UNIMPLEMENTED, OPT_USER.OptionStr);
+            ConResPrintf(StdOut, IDS_SWITCH_UNIMPLEMENTED, OPT_USER.OptionStr);
         if (szPassword)
         if (szPassword)
-            PrintResourceString(IDS_SWITCH_UNIMPLEMENTED, OPT_PASSWD.OptionStr);
+            ConResPrintf(StdOut, IDS_SWITCH_UNIMPLEMENTED, OPT_PASSWD.OptionStr);
         return EXIT_FAILURE;
     }
 
     if (ulEventIdentifier < EVENT_ID_MIN || ulEventIdentifier > EVENT_ID_MAX)
     {
         /* Invalid event identifier */
         return EXIT_FAILURE;
     }
 
     if (ulEventIdentifier < EVENT_ID_MIN || ulEventIdentifier > EVENT_ID_MAX)
     {
         /* Invalid event identifier */
-        PrintResourceString(IDS_BADSYNTAX_7, OPT_EVTID.OptionStr, EVENT_ID_MIN, EVENT_ID_MAX);
-        PrintResourceString(IDS_USAGE);
+        ConResPrintf(StdErr, IDS_BADSYNTAX_7, OPT_EVTID.OptionStr, EVENT_ID_MIN, EVENT_ID_MAX);
+        ConResPrintf(StdErr, IDS_USAGE);
         return EXIT_FAILURE;
     }
 
         return EXIT_FAILURE;
     }
 
@@ -1275,18 +1238,18 @@ int wmain(int argc, WCHAR* argv[])
         if (!Success)
         {
             PrintError(GetLastError());
         if (!Success)
         {
             PrintError(GetLastError());
-            PrintString(L"Failed to report event!\n");
+            ConPrintf(StdErr, L"Failed to report event!\n");
         }
         else
         {
             /* Show success */
         }
         else
         {
             /* Show success */
-            PrintString(L"\n");
+            ConPrintf(StdOut, L"\n");
             if (!szEventSource)
             if (!szEventSource)
-                PrintResourceString(IDS_SUCCESS_1, szEventType, szLogName);
+                ConResPrintf(StdOut, IDS_SUCCESS_1, szEventType, szLogName);
             else if (!szLogName)
             else if (!szLogName)
-                PrintResourceString(IDS_SUCCESS_2, szEventType, szEventSource);
+                ConResPrintf(StdOut, IDS_SUCCESS_2, szEventType, szEventSource);
             else
             else
-                PrintResourceString(IDS_SUCCESS_3, szEventType, szLogName, szEventSource);
+                ConResPrintf(StdOut, IDS_SUCCESS_3, szEventType, szLogName, szEventSource);
         }
 
         HeapFree(GetProcessHeap(), 0, pUserToken);
         }
 
         HeapFree(GetProcessHeap(), 0, pUserToken);
@@ -1294,7 +1257,7 @@ int wmain(int argc, WCHAR* argv[])
     else
     {
         PrintError(GetLastError());
     else
     {
         PrintError(GetLastError());
-        PrintString(L"GetUserToken() failed!\n");
+        ConPrintf(StdErr, L"GetUserToken() failed!\n");
     }
 
     /* Close the event log */
     }
 
     /* Close the event log */
index 8a28add..f035248 100644 (file)
@@ -7,6 +7,7 @@ if(CMAKE_CROSSCOMPILING)
 add_subdirectory(3rdparty)
 add_subdirectory(atl)
 add_subdirectory(comsupp)
 add_subdirectory(3rdparty)
 add_subdirectory(atl)
 add_subdirectory(comsupp)
+add_subdirectory(conutils)
 add_subdirectory(cportlib)
 add_subdirectory(crt)
 add_subdirectory(cryptlib)
 add_subdirectory(cportlib)
 add_subdirectory(crt)
 add_subdirectory(cryptlib)
diff --git a/reactos/sdk/lib/conutils/CMakeLists.txt b/reactos/sdk/lib/conutils/CMakeLists.txt
new file mode 100644 (file)
index 0000000..22ab601
--- /dev/null
@@ -0,0 +1,8 @@
+
+## Temporary HACK before we cleanly support UNICODE functions
+add_definitions(-DUNICODE -D_UNICODE)
+
+add_library(conutils conutils.c)
+add_dependencies(conutils psdk)
+target_link_libraries(conutils ${PSEH_LIB})
+# add_importlibs(conutils msvcrt kernel32) ## ntdll
diff --git a/reactos/sdk/lib/conutils/conutils.c b/reactos/sdk/lib/conutils/conutils.c
new file mode 100644 (file)
index 0000000..493fbc1
--- /dev/null
@@ -0,0 +1,767 @@
+/*
+ * 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);
+ *                  - All programmers who wrote the different console applications
+ *                    from which I took those functions and improved them.
+ */
+
+/*
+ * Enable this define if you want to only use CRT functions to output
+ * UNICODE stream to the console, as in the way explained by
+ * http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
+ */
+/** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
+// #define USE_CRT
+
+#include <stdlib.h> // limits.h // For MB_LEN_MAX
+
+#ifdef USE_CRT
+#include <fcntl.h>
+#include <io.h>
+#endif /* defined(USE_CRT) */
+
+#include <windef.h>
+#include <winbase.h>
+
+#include <winnls.h>
+#include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
+
+#include <wincon.h>  // Console APIs (only if kernel32 support included)
+
+#include "conutils.h"
+#include <strsafe.h>
+
+/* PSEH for SEH Support */
+#include <pseh/pseh2.h>
+
+
+// #define RC_STRING_MAX_SIZE  4096
+// #define MAX_BUFFER_SIZE     4096
+// #define OUTPUT_BUFFER_SIZE  4096
+
+
+/*
+ * 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;
+}
+
+
+/*
+ * Console I/O streams
+ */
+
+typedef struct _CON_STREAM
+{
+    CON_WRITE_FUNC WriteFunc;
+
+#ifdef USE_CRT
+    FILE* fStream;
+#else
+    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
+     * when 'hHandle' refers to a file or a pipe.
+     */
+    CON_STREAM_MODE Mode;
+    UINT CodePage;  // Used to convert UTF-16 text to some ANSI codepage.
+#endif /* defined(USE_CRT) */
+} CON_STREAM, *PCON_STREAM;
+
+/*
+ * Standard console streams, initialized by
+ * calls to ConStreamInit/ConInitStdStreams.
+ */
+#if 0 // FIXME!
+CON_STREAM StdStreams[3] =
+{
+    {0}, // StdIn // TODO!
+    {0}, // StdOut
+    {0}, // StdErr
+};
+#else
+CON_STREAM csStdIn;
+CON_STREAM csStdOut;
+CON_STREAM csStdErr;
+#endif
+
+
+// static
+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);
+}
+
+BOOL
+ConStreamInitEx(
+    OUT PCON_STREAM Stream,
+    IN  PVOID Handle,
+    IN  CON_STREAM_MODE Mode,
+    IN  CON_WRITE_FUNC WriteFunc OPTIONAL)
+{
+    /* Parameters validation */
+    if (!Stream || !Handle)
+        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;
+
+    // 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;
+
+#endif /* defined(USE_CRT) */
+
+    return TRUE;
+}
+
+BOOL
+ConStreamInit(
+    OUT PCON_STREAM Stream,
+    IN  PVOID Handle,
+    IN  CON_STREAM_MODE Mode)
+{
+    return ConStreamInitEx(Stream, Handle, Mode, ConWrite);
+}
+
+
+/*
+ * Console I/O utility API
+ * (for the moment, only Output)
+ */
+
+// ConWriteStr
+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->bIsConsole)
+    {
+        // 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.
+    }
+
+    /*
+     * 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
+        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 */,
+                                         0, szStr, (INT)len, buffer, (INT)len);
+        szStr = (PVOID)buffer;
+#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))
+    {
+#ifdef _UNICODE
+        // NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h .
+        CHAR *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;
+#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);
+#endif
+    }
+    else // if (Stream->Mode == Binary)
+    {
+        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;
+
+    /*
+     * 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
+    while (1)
+    {
+        written = wprintf(L"%.*s", total, szStr);
+        if (written < total)
+        {
+            if (written == 0)
+            {
+                fputwc(*szStr, Stream->fStream);
+                written++;
+            }
+
+            szStr += written;
+            total -= written;
+        }
+        else
+        {
+            break;
+        }
+    }
+    return (INT)len;
+#else
+    return fwrite(szStr, sizeof(*szStr), len, Stream->fStream);
+#endif
+
+#endif /* defined(USE_CRT) */
+}
+
+INT
+ConPrintfV(
+    IN PCON_STREAM Stream,
+    IN LPWSTR  szStr,
+    IN va_list args) // arg_ptr
+{
+    INT Len;
+    WCHAR bufFormatted[CON_RC_STRING_MAX_SIZE];
+
+#if 1 ///////////////////////////////////////////////////////////////////////  0
+    PWSTR pEnd;
+    StringCchVPrintfExW(bufFormatted, ARRAYSIZE(bufFormatted), &pEnd, NULL, 0, szStr, args);
+    Len = pEnd - bufFormatted;
+#else
+    StringCchVPrintfW(bufFormatted, ARRAYSIZE(bufFormatted), szStr, args);
+    Len = wcslen(bufFormatted);
+#endif
+    Len = Stream->WriteFunc(Stream, bufFormatted, Len);
+
+    /* Fixup returned length in case of errors */
+    if (Len < 0)
+        Len = 0;
+
+    return Len;
+}
+
+INT
+ConPrintf(
+    IN PCON_STREAM Stream,
+    IN LPWSTR szStr,
+    ...)
+{
+    INT Len;
+    va_list args;
+
+#if 0
+    Len = vfwprintf(stdout, szMsgBuf, arg_ptr); // vfprintf for direct ANSI
+    // or: Len = vwprintf(szMsgBuf, arg_ptr);
+#endif
+
+    // StringCchPrintfW
+    va_start(args, szStr);
+    Len = ConPrintfV(Stream, szStr, args);
+    va_end(args);
+
+    return Len;
+}
+
+INT
+ConResPrintfV(
+    IN PCON_STREAM Stream,
+    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(GetModuleHandleW(NULL), uID, bufSrc, ARRAYSIZE(bufSrc));
+    if (Len)
+        Len = ConPrintfV(Stream, bufSrc, args);
+
+    return Len;
+}
+
+INT
+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
+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);
+        // Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength);
+
+        /* 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);
+        Len = Stream->WriteFunc(Stream, lpMsgBuf, dwLength);
+
+        /* Fixup returned length in case of errors */
+        if (Len < 0)
+            Len = 0;
+
+        /* Free the buffer allocated by FormatMessage */
+        LocalFree(lpMsgBuf);
+    }
+
+    return Len;
+}
+
+INT
+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;
+}
+
+//
+// TODO: Add Console paged-output printf & ResPrintf functions!
+//
diff --git a/reactos/sdk/lib/conutils/conutils.h b/reactos/sdk/lib/conutils/conutils.h
new file mode 100644 (file)
index 0000000..7534b60
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * 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);
+ *                  - All programmers who wrote the different console applications
+ *                    from which I took those functions and improved them.
+ */
+
+#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
+ConPrintfV(
+    IN PCON_STREAM Stream,
+    IN LPWSTR  szStr,
+    IN va_list args); // arg_ptr
+
+INT
+ConPrintf(
+    IN PCON_STREAM Stream,
+    IN LPWSTR szStr,
+    ...);
+
+INT
+ConResPrintfV(
+    IN PCON_STREAM Stream,
+    IN UINT    uID,
+    IN va_list args); // arg_ptr
+
+INT
+ConResPrintf(
+    IN PCON_STREAM Stream,
+    IN UINT uID,
+    ...);
+
+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
+ConMsgPrintf(
+    IN PCON_STREAM Stream,
+    IN DWORD   dwFlags,
+    IN LPCVOID lpSource OPTIONAL,
+    IN DWORD   dwMessageId,
+    IN DWORD   dwLanguageId,
+    ...);
+
+
+/*
+ * Those are compatibility #defines for old code!
+ */
+
+/*** tree.c ***/
+
+#define PrintStringV(szStr, args)   \
+    ConPrintfV(StdOut, (szStr), (args))
+#define PrintString(szStr, ...)     \
+    ConPrintf(StdOut, (szStr), ##__VA_ARGS__)
+
+/*** network/net/main.c ***/
+#define PrintToConsole(szStr, ...)  \
+    ConPrintf(StdOut, (szStr), ##__VA_ARGS__)
+
+/*** clip.c, comp.c, help.c, tree.c ***/
+/*** subst.c ***/
+/*** format.c, network/net/main.c, shutdown.c, wlanconf.c, diskpart.c ***/
+
+#define PrintResourceStringV(uID, args) \
+    ConResPrintfV(StdOut, (uID), (args))
+#define PrintResourceString(uID, ...)   \
+    ConResPrintf(StdOut, (uID), ##__VA_ARGS__)
+
+//
+// TODO: Add Console paged-output printf & ResPrintf functions!
+//
+
+#endif  /* __CONUTILS_H__ */