Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516/* * PROJECT: ReactOS Console Utilities Library * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Provides basic abstraction wrappers around CRT streams or * Win32 console API I/O functions, to deal with i18n + Unicode * related problems. * COPYRIGHT: Copyright 2017-2018 ReactOS Team * Copyright 2017-2018 Hermes Belusca-Maito */
/** * @file outstream.c * @ingroup ConUtils * * @brief Console I/O utility API -- Output **/
/* * Enable this define if you want to only use CRT functions to output * UNICODE stream to the console, as in the way explained by * http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html *//** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/// #define USE_CRT
/* FIXME: Temporary HACK before we cleanly support UNICODE functions */#define UNICODE#define _UNICODE
#ifdef USE_CRT#include <fcntl.h>#include <io.h>#endif /* USE_CRT */
#include <stdlib.h> // limits.h // For MB_LEN_MAX
#include <windef.h>#include <winbase.h>#include <winnls.h>#include <winuser.h> // MAKEINTRESOURCEW, RT_STRING#include <wincon.h> // Console APIs (only if kernel32 support included)#include <strsafe.h>
/* PSEH for SEH Support */#include <pseh/pseh2.h>
#include "conutils.h"#include "stream.h"#include "stream_private.h"
// Also known as: RC_STRING_MAX_SIZE, MAX_BUFFER_SIZE (some programs:// wlanconf, shutdown, set it to 5024), OUTPUT_BUFFER_SIZE (name given// in cmd/console.c), MAX_STRING_SIZE (name given in diskpart) or// MAX_MESSAGE_SIZE (set to 512 in shutdown).#define CON_RC_STRING_MAX_SIZE 4096
/** * @name ConWrite * Writes a counted string to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] szStr * Pointer to the counted string to write. * * @param[in] len * Length of the string pointed by @p szStr, specified * in number of characters. * * @return * Numbers of characters successfully written to @p Stream. * * @note * This function is used as an internal function. * Use the ConStreamWrite() function instead. * * @remark * Should be called with the stream locked. **/INT__stdcallConWrite( IN PCON_STREAM Stream, IN PCTCH szStr, IN DWORD len){#ifndef USE_CRT DWORD TotalLen = len, dwNumBytes = 0; LPCVOID p;
/* If we do not write anything, just return */ if (!szStr || len == 0) return 0;
/* Check whether we are writing to a console */ // if (IsConsoleHandle(Stream->hHandle)) if (Stream->IsConsole) { // TODO: Check if (Stream->Mode == WideText or UTF16Text) ??
/* * This code is inspired from _cputws, in particular from the fact that, * according to MSDN: https://learn.microsoft.com/en-us/windows/console/writeconsole * the buffer size must be less than 64 KB. * * A similar code can be used for implementing _cputs too. */
DWORD cchWrite; TotalLen = len, dwNumBytes = 0;
while (len > 0) { cchWrite = min(len, 65535 / sizeof(WCHAR));
// FIXME: Check return value! WriteConsole(Stream->hHandle, szStr, cchWrite, &dwNumBytes, NULL);
szStr += cchWrite; len -= cchWrite; }
return (INT)TotalLen; // FIXME: Really return the number of chars written! }
/* * We are redirected and writing to a file or pipe instead of the console. * Convert the string from TCHARs to the desired output format, if the two differ. * * Implementation NOTE: * MultiByteToWideChar (resp. WideCharToMultiByte) are equivalent to * OemToCharBuffW (resp. CharToOemBuffW), but these latter functions * uselessly depend on user32.dll, while MultiByteToWideChar and * WideCharToMultiByte only need kernel32.dll. */ if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text)) {#ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16 /* Convert from the current process/thread's code page to UTF-16 */ PWCHAR buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR)); if (!buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; } len = (DWORD)MultiByteToWideChar(CP_THREAD_ACP, // CP_ACP, CP_OEMCP 0, szStr, (INT)len, buffer, (INT)len); szStr = (PVOID)buffer;#else /* * Do not perform any conversion since we are already in UTF-16, * that is the same encoding as the stream. */#endif
/* * Find any newline character in the buffer, * write the part BEFORE the newline, then emit * a carriage-return + newline sequence and finally * write the remaining part of the buffer. * * This fixes output in files and serial console. */ while (len > 0) { /* Loop until we find a newline character */ p = szStr; while (len > 0 && *(PCWCH)p != L'\n') { /* Advance one character */ p = (LPCVOID)((PCWCH)p + 1); --len; }
/* Write everything up to \n */ dwNumBytes = ((PCWCH)p - (PCWCH)szStr) * sizeof(WCHAR); WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
/* * If we hit a newline and the previous character is not a carriage-return, * emit a carriage-return + newline sequence, otherwise just emit the newline. */ if (len > 0 && *(PCWCH)p == L'\n') { if (p == (LPCVOID)szStr || (p > (LPCVOID)szStr && *((PCWCH)p - 1) != L'\r')) WriteFile(Stream->hHandle, L"\r\n", 2 * sizeof(WCHAR), &dwNumBytes, NULL); else WriteFile(Stream->hHandle, L"\n", sizeof(WCHAR), &dwNumBytes, NULL);
/* Skip \n */ p = (LPCVOID)((PCWCH)p + 1); --len; } szStr = p; }
#ifndef _UNICODE HeapFree(GetProcessHeap(), 0, buffer);#endif } else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText)) { UINT CodePage; PCHAR buffer;
/* * Resolve the current code page if it has not been assigned yet * (we do this only if the stream is in ANSI mode; in UTF8 mode * the code page is always set to CP_UTF8). Otherwise use the * current stream's code page. */ if (/*(Stream->Mode == AnsiText) &&*/ (Stream->CodePage == INVALID_CP)) CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP else CodePage = Stream->CodePage;
#ifdef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16 /* Convert from UTF-16 to either UTF-8 or ANSI, using the stream code page */ // NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h . buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX); if (!buffer) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; } len = WideCharToMultiByte(CodePage, 0, szStr, len, buffer, len * MB_LEN_MAX, NULL, NULL); szStr = (PVOID)buffer;#else /* * Convert from the current process/thread's code page to either * UTF-8 or ANSI, using the stream code page. * We need to perform a double conversion, by going through UTF-16. */ // TODO! #error "Need to implement double conversion!"#endif
/* * Find any newline character in the buffer, * write the part BEFORE the newline, then emit * a carriage-return + newline sequence and finally * write the remaining part of the buffer. * * This fixes output in files and serial console. */ while (len > 0) { /* Loop until we find a newline character */ p = szStr; while (len > 0 && *(PCCH)p != '\n') { /* Advance one character */ p = (LPCVOID)((PCCH)p + 1); --len; }
/* Write everything up to \n */ dwNumBytes = ((PCCH)p - (PCCH)szStr) * sizeof(CHAR); WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
/* * If we hit a newline and the previous character is not a carriage-return, * emit a carriage-return + newline sequence, otherwise just emit the newline. */ if (len > 0 && *(PCCH)p == '\n') { if (p == (LPCVOID)szStr || (p > (LPCVOID)szStr && *((PCCH)p - 1) != '\r')) WriteFile(Stream->hHandle, "\r\n", 2, &dwNumBytes, NULL); else WriteFile(Stream->hHandle, "\n", 1, &dwNumBytes, NULL);
/* Skip \n */ p = (LPCVOID)((PCCH)p + 1); --len; } szStr = p; }
#ifdef _UNICODE HeapFree(GetProcessHeap(), 0, buffer);#else // TODO!#endif } else // if (Stream->Mode == Binary) { /* Directly output the string */ WriteFile(Stream->hHandle, szStr, len, &dwNumBytes, NULL); }
// FIXME! return (INT)TotalLen;
#else /* defined(USE_CRT) */
DWORD total = len; DWORD written = 0;
/* If we do not write anything, just return */ if (!szStr || len == 0) return 0;
#if 1 /* * There is no "counted" printf-to-stream or puts-like function, therefore * we use this trick to output the counted string to the stream. */ while (1) { written = fwprintf(Stream->fStream, L"%.*s", total, szStr); if (written < total) { /* * Some embedded NULL or special character * was encountered, print it apart. */ if (written == 0) { fputwc(*szStr, Stream->fStream); written++; }
szStr += written; total -= written; } else { break; } } return (INT)len;#else /* ANSI text or Binary output only */ _setmode(_fileno(Stream->fStream), _O_TEXT); // _O_BINARY return fwrite(szStr, sizeof(*szStr), len, Stream->fStream);#endif
#endif /* defined(USE_CRT) */}
#define CON_STREAM_WRITE_CALL(Stream, Str, Len) \ (Stream)->WriteFunc((Stream), (Str), (Len))
/* Lock the stream only in non-USE_CRT mode (otherwise use the CRT stream lock) */#ifndef USE_CRT
#define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \do { \ EnterCriticalSection(&(Stream)->Lock); \ (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \ LeaveCriticalSection(&(Stream)->Lock); \} while(0)
#define CON_STREAM_WRITE(Stream, Str, Len) \do { \ EnterCriticalSection(&(Stream)->Lock); \ CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \ LeaveCriticalSection(&(Stream)->Lock); \} while(0)
#else
#define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \do { \ (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \} while(0)
#define CON_STREAM_WRITE(Stream, Str, Len) \ CON_STREAM_WRITE_CALL((Stream), (Str), (Len))
#endif
/** * @name ConStreamWrite * Writes a counted string to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] szStr * Pointer to the counted string to write. * * @param[in] len * Length of the string pointed by @p szStr, specified * in number of characters. * * @return * Numbers of characters successfully written to @p Stream. **/INTConStreamWrite( IN PCON_STREAM Stream, IN PCTCH szStr, IN DWORD len){ INT Len; CON_STREAM_WRITE2(Stream, szStr, len, Len); return Len;}
/** * @name ConPuts * Writes a NULL-terminated string to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] szStr * Pointer to the NULL-terminated string to write. * * @return * Numbers of characters successfully written to @p Stream. * * @remark * Contrary to the CRT puts() function, ConPuts() does not append * a terminating new-line character. In this way it behaves more like * the CRT fputs() function. **/INTConPuts( IN PCON_STREAM Stream, IN PCWSTR szStr){ INT Len;
Len = wcslen(szStr); CON_STREAM_WRITE2(Stream, szStr, Len, Len);
/* Fixup returned length in case of errors */ if (Len < 0) Len = 0;
return Len;}
/** * @name ConPrintfV * Formats and writes a NULL-terminated string to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] szStr * Pointer to the NULL-terminated format string, that follows the same * specifications as the @a szStr format string in ConPrintf(). * * @param[in] args * Parameter describing a variable number of arguments, * initialized with va_start(), that can be expected by the function, * depending on the @p szStr format string. Each argument is used to * replace a <em>format specifier</em> in the format string. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), printf(), vprintf() **/INTConPrintfV( IN PCON_STREAM Stream, IN PCWSTR szStr, IN va_list args){ INT Len; WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
// Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI
/* * Re-use szStr as the pointer to end-of-string, so as * to compute the string length instead of calling wcslen(). */ // StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args); // Len = wcslen(bufSrc); StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), (PWSTR*)&szStr, NULL, 0, szStr, args); Len = szStr - bufSrc;
CON_STREAM_WRITE2(Stream, bufSrc, Len, Len);
/* Fixup returned length in case of errors */ if (Len < 0) Len = 0;
return Len;}
/** * @name ConPrintf * Formats and writes a NULL-terminated string to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] szStr * Pointer to the NULL-terminated format string, that follows the same * specifications as the @a format string in printf(). This string can * optionally contain embedded <em>format specifiers</em> that are * replaced by the values specified in subsequent additional arguments * and formatted as requested. * * @param[in] ... * Additional arguments that can be expected by the function, depending * on the @p szStr format string. Each argument is used to replace a * <em>format specifier</em> in the format string. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintfV(), printf(), vprintf() **/INT__cdeclConPrintf( IN PCON_STREAM Stream, IN PCWSTR szStr, ...){ INT Len; va_list args;
// Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI
// StringCchPrintfW va_start(args, szStr); Len = ConPrintfV(Stream, szStr, args); va_end(args);
return Len;}
/** * @name ConResPutsEx * Writes a string resource to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] hInstance * Optional handle to an instance of the module whose executable file * contains the string resource. Can be set to NULL to get the handle * to the application itself. * * @param[in] uID * The identifier of the string to be written. * * @param[in] LanguageId * The language identifier of the resource. If this parameter is * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language * associated with the calling thread is used. To specify a language other * than the current language, use the @c MAKELANGID macro to create this * parameter. * * @return * Numbers of characters successfully written to @p Stream. * * @remark * Similarly to ConPuts(), no terminating new-line character is appended. * * @see ConPuts(), ConResPuts() **/INTConResPutsEx( IN PCON_STREAM Stream, IN HINSTANCE hInstance OPTIONAL, IN UINT uID, IN LANGID LanguageId){ INT Len; PWCHAR szStr = NULL;
Len = K32LoadStringExW(hInstance, uID, LanguageId, (PWSTR)&szStr, 0); if (szStr && Len) // Len = ConPuts(Stream, szStr); CON_STREAM_WRITE2(Stream, szStr, Len, Len);
/* Fixup returned length in case of errors */ if (Len < 0) Len = 0;
return Len;}
/** * @name ConResPuts * Writes a string resource contained in the current application * to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] uID * The identifier of the string to be written. * * @return * Numbers of characters successfully written to @p Stream. * * @remark * Similarly to ConPuts(), no terminating new-line character is appended. * * @see ConPuts(), ConResPutsEx() **/INTConResPuts( IN PCON_STREAM Stream, IN UINT uID){ return ConResPutsEx(Stream, NULL /*GetModuleHandleW(NULL)*/, uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));}
/** * @name ConResPrintfExV * Formats and writes a string resource to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] hInstance * Optional handle to an instance of the module whose executable file * contains the string resource. Can be set to NULL to get the handle * to the application itself. * * @param[in] uID * The identifier of the format string. The format string follows the * same specifications as the @a szStr format string in ConPrintf(). * * @param[in] LanguageId * The language identifier of the resource. If this parameter is * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language * associated with the calling thread is used. To specify a language other * than the current language, use the @c MAKELANGID macro to create this * parameter. * * @param[in] args * Parameter describing a variable number of arguments, * initialized with va_start(), that can be expected by the function, * depending on the @p szStr format string. Each argument is used to * replace a <em>format specifier</em> in the format string. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintfEx(), ConResPrintfV(), ConResPrintf() **/INTConResPrintfExV( IN PCON_STREAM Stream, IN HINSTANCE hInstance OPTIONAL, IN UINT uID, IN LANGID LanguageId, IN va_list args){ INT Len; WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
// NOTE: We may use the special behaviour where nBufMaxSize == 0 Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc)); if (Len) Len = ConPrintfV(Stream, bufSrc, args);
return Len;}
/** * @name ConResPrintfV * Formats and writes a string resource contained in the * current application to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] uID * The identifier of the format string. The format string follows the * same specifications as the @a szStr format string in ConPrintf(). * * @param[in] args * Parameter describing a variable number of arguments, * initialized with va_start(), that can be expected by the function, * depending on the @p szStr format string. Each argument is used to * replace a <em>format specifier</em> in the format string. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintf() **/INTConResPrintfV( IN PCON_STREAM Stream, IN UINT uID, IN va_list args){ return ConResPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/, uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), args);}
/** * @name ConResPrintfEx * Formats and writes a string resource to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] hInstance * Optional handle to an instance of the module whose executable file * contains the string resource. Can be set to NULL to get the handle * to the application itself. * * @param[in] uID * The identifier of the format string. The format string follows the * same specifications as the @a szStr format string in ConPrintf(). * * @param[in] LanguageId * The language identifier of the resource. If this parameter is * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language * associated with the calling thread is used. To specify a language other * than the current language, use the @c MAKELANGID macro to create this * parameter. * * @param[in] ... * Additional arguments that can be expected by the function, depending * on the @p szStr format string. Each argument is used to replace a * <em>format specifier</em> in the format string. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintfExV(), ConResPrintfV(), ConResPrintf() **/INT__cdeclConResPrintfEx( IN PCON_STREAM Stream, IN HINSTANCE hInstance OPTIONAL, IN UINT uID, IN LANGID LanguageId, ...){ INT Len; va_list args;
va_start(args, LanguageId); Len = ConResPrintfExV(Stream, hInstance, uID, LanguageId, args); va_end(args);
return Len;}
/** * @name ConResPrintf * Formats and writes a string resource contained in the * current application to a stream. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] uID * The identifier of the format string. The format string follows the * same specifications as the @a szStr format string in ConPrintf(). * * @param[in] ... * Additional arguments that can be expected by the function, depending * on the @p szStr format string. Each argument is used to replace a * <em>format specifier</em> in the format string. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintfV() **/INT__cdeclConResPrintf( 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;}
/** * @name ConMsgPuts * Writes a message string to a stream without formatting. The function * requires a message definition as input. The message definition can come * from a buffer passed to the function. It can come from a message table * resource in an already-loaded module, or the caller can ask the function * to search the system's message table resource(s) for the message definition. * Please refer to the Win32 FormatMessage() function for more details. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] dwFlags * The formatting options, and how to interpret the @p lpSource parameter. * See FormatMessage() for more details. The @b FORMAT_MESSAGE_ALLOCATE_BUFFER * and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored. * The function implicitly uses the @b FORMAT_MESSAGE_IGNORE_INSERTS flag * to implement its behaviour. * * @param[in] lpSource * The location of the message definition. The type of this parameter * depends upon the settings in the @p dwFlags parameter. * * @param[in] dwMessageId * The message identifier for the requested message. This parameter * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING. * * @param[in] dwLanguageId * The language identifier for the requested message. This parameter * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING. * * @return * Numbers of characters successfully written to @p Stream. * * @remark * Similarly to ConPuts(), no terminating new-line character is appended. * * @see ConPuts(), ConResPuts() and associated functions, * <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a> **/INTConMsgPuts( IN PCON_STREAM Stream, IN DWORD dwFlags, IN LPCVOID lpSource OPTIONAL, IN DWORD dwMessageId, IN DWORD dwLanguageId){ INT Len; DWORD dwLength = 0; LPWSTR lpMsgBuf = NULL;
/* * Sanitize dwFlags. This version always ignores explicitly the inserts * as we emulate the behaviour of the (f)puts function. */ dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer. dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS; // Ignore inserts for FormatMessage. dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
/* * Retrieve the message string without appending extra newlines. * Wrap in SEH to protect from invalid string parameters. */ _SEH2_TRY { dwLength = FormatMessageW(dwFlags, lpSource, dwMessageId, dwLanguageId, (LPWSTR)&lpMsgBuf, 0, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END;
Len = (INT)dwLength;
if (!lpMsgBuf) { // ASSERT(dwLength == 0); } else { // ASSERT(dwLength != 0);
/* lpMsgBuf is NULL-terminated by FormatMessage */ // Len = ConPuts(Stream, lpMsgBuf); CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */ if (Len < 0) Len = 0;
/* Free the buffer allocated by FormatMessage */ LocalFree(lpMsgBuf); }
return Len;}
/** * @name ConMsgPrintf2V * Formats and writes a message string to a stream. * * @remark For internal use only. * * @see ConMsgPrintfV() **/INTConMsgPrintf2V( IN PCON_STREAM Stream, IN DWORD dwFlags, IN LPCVOID lpSource OPTIONAL, IN DWORD dwMessageId, IN DWORD dwLanguageId, IN va_list args){ INT Len; DWORD dwLength = 0; LPWSTR lpMsgBuf = NULL;
/* * Sanitize dwFlags. This version always ignores explicitly 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;
/* * Retrieve the message string without appending extra newlines. * Wrap in SEH to protect from invalid string parameters. */ _SEH2_TRY { dwLength = FormatMessageW(dwFlags, lpSource, dwMessageId, dwLanguageId, (LPWSTR)&lpMsgBuf, 0, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END;
Len = (INT)dwLength;
if (!lpMsgBuf) { // ASSERT(dwLength == 0); } else { // ASSERT(dwLength != 0);
/* lpMsgBuf is NULL-terminated by FormatMessage */ Len = ConPrintfV(Stream, lpMsgBuf, args); // CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */ if (Len < 0) Len = 0;
/* Free the buffer allocated by FormatMessage */ LocalFree(lpMsgBuf); }
return Len;}
/** * @name ConMsgPrintfV * Formats and writes a message string to a stream. The function requires * a message definition as input. The message definition can come from a * buffer passed to the function. It can come from a message table resource * in an already-loaded module, or the caller can ask the function to search * the system's message table resource(s) for the message definition. * Please refer to the Win32 FormatMessage() function for more details. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] dwFlags * The formatting options, and how to interpret the @p lpSource parameter. * See FormatMessage() for more details. * The @b FORMAT_MESSAGE_ALLOCATE_BUFFER flag is always ignored. * * @param[in] lpSource * The location of the message definition. The type of this parameter * depends upon the settings in the @p dwFlags parameter. * * @param[in] dwMessageId * The message identifier for the requested message. This parameter * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING. * * @param[in] dwLanguageId * The language identifier for the requested message. This parameter * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING. * * @param[in] Arguments * Optional pointer to an array of values describing a variable number of * arguments, depending on the message string. Each argument is used to * replace an <em>insert sequence</em> in the message string. * By default, the @p Arguments parameter is of type @c va_list*, initialized * with va_start(). The state of the @c va_list argument is undefined upon * return from the function. To use the @c va_list again, destroy the variable * argument list pointer using va_end() and reinitialize it with va_start(). * If you do not have a pointer of type @c va_list*, then specify the * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array * of @c DWORD_PTR values; those values are input to the message formatted * as the insert values. Each insert must have a corresponding element in * the array. * * @remark * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions, * the ConMsg* functions work on format strings that contain <em>insert sequences</em>. * These sequences extend the standard <em>format specifiers</em> as they * allow to specify an <em>insert number</em> referring which precise value * given in arguments to use. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(), * <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a> **/INTConMsgPrintfV( IN PCON_STREAM Stream, IN DWORD dwFlags, IN LPCVOID lpSource OPTIONAL, IN DWORD dwMessageId, IN DWORD dwLanguageId, IN va_list *Arguments OPTIONAL){ INT Len; DWORD dwLength = 0; LPWSTR lpMsgBuf = NULL;
/* Sanitize dwFlags */ dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
/* * 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, Arguments);
Len = (INT)dwLength;
if (!lpMsgBuf) { // ASSERT(dwLength == 0); } else { // ASSERT(dwLength != 0);
CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */ if (Len < 0) Len = 0;
/* Free the buffer allocated by FormatMessage */ LocalFree(lpMsgBuf); }
return Len;}
/** * @name ConMsgPrintf * Formats and writes a message string to a stream. The function requires * a message definition as input. The message definition can come from a * buffer passed to the function. It can come from a message table resource * in an already-loaded module, or the caller can ask the function to search * the system's message table resource(s) for the message definition. * Please refer to the Win32 FormatMessage() function for more details. * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] dwFlags * The formatting options, and how to interpret the @p lpSource parameter. * See FormatMessage() for more details. The @b FORMAT_MESSAGE_ALLOCATE_BUFFER * and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored. * * @param[in] lpSource * The location of the message definition. The type of this parameter * depends upon the settings in the @p dwFlags parameter. * * @param[in] dwMessageId * The message identifier for the requested message. This parameter * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING. * * @param[in] dwLanguageId * The language identifier for the requested message. This parameter * is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING. * * @param[in] ... * Additional arguments that can be expected by the function, depending * on the message string. Each argument is used to replace an * <em>insert sequence</em> in the message string. * * @remark * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions, * the ConMsg* functions work on format strings that contain <em>insert sequences</em>. * These sequences extend the standard <em>format specifiers</em> as they * allow to specify an <em>insert number</em> referring which precise value * given in arguments to use. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintfV(), * <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a> **/INT__cdeclConMsgPrintf( IN PCON_STREAM Stream, IN DWORD dwFlags, IN LPCVOID lpSource OPTIONAL, IN DWORD dwMessageId, IN DWORD dwLanguageId, ...){ INT Len; va_list args;
/* Sanitize dwFlags */ dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
va_start(args, dwLanguageId); Len = ConMsgPrintfV(Stream, dwFlags, lpSource, dwMessageId, dwLanguageId, &args); va_end(args);
return Len;}
/** * @name ConResMsgPrintfExV * Formats and writes a message string to a stream. The function requires * a message definition as input. Contrary to the ConMsg* or the Win32 * FormatMessage() functions, the message definition comes from a resource * string table, much like the strings for ConResPrintf(), but is formatted * according to the rules of ConMsgPrintf(). * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] hInstance * Optional handle to an instance of the module whose executable file * contains the string resource. Can be set to NULL to get the handle * to the application itself. * * @param[in] dwFlags * The formatting options, see FormatMessage() for more details. * The only valid flags are @b FORMAT_MESSAGE_ARGUMENT_ARRAY, * @b FORMAT_MESSAGE_IGNORE_INSERTS and @b FORMAT_MESSAGE_MAX_WIDTH_MASK. * All the other flags are internally overridden by the function * to implement its behaviour. * * @param[in] uID * The identifier of the message string. The format string follows the * same specifications as the @a lpSource format string in ConMsgPrintf(). * * @param[in] LanguageId * The language identifier of the resource. If this parameter is * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language * associated with the calling thread is used. To specify a language other * than the current language, use the @c MAKELANGID macro to create this * parameter. * * @param[in] Arguments * Optional pointer to an array of values describing a variable number of * arguments, depending on the message string. Each argument is used to * replace an <em>insert sequence</em> in the message string. * By default, the @p Arguments parameter is of type @c va_list*, initialized * with va_start(). The state of the @c va_list argument is undefined upon * return from the function. To use the @c va_list again, destroy the variable * argument list pointer using va_end() and reinitialize it with va_start(). * If you do not have a pointer of type @c va_list*, then specify the * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array * of @c DWORD_PTR values; those values are input to the message formatted * as the insert values. Each insert must have a corresponding element in * the array. * * @remark * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions, * the ConMsg* functions work on format strings that contain <em>insert sequences</em>. * These sequences extend the standard <em>format specifiers</em> as they * allow to specify an <em>insert number</em> referring which precise value * given in arguments to use. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(), * <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a> **/INTConResMsgPrintfExV( IN PCON_STREAM Stream, IN HINSTANCE hInstance OPTIONAL, IN DWORD dwFlags, IN UINT uID, IN LANGID LanguageId, IN va_list *Arguments OPTIONAL){ INT Len; DWORD dwLength = 0; LPWSTR lpMsgBuf = NULL; WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
/* Retrieve the string from the resource string table */ // NOTE: We may use the special behaviour where nBufMaxSize == 0 Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc)); if (Len == 0) return Len;
/* Sanitize dwFlags */ dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
/* The string has already been manually loaded */ dwFlags &= ~(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM); dwFlags |= FORMAT_MESSAGE_FROM_STRING;
/* * Retrieve the message string without appending extra newlines. * Use the "safe" FormatMessage version (SEH-protected) to protect * from invalid string parameters. */ dwLength = FormatMessageSafeW(dwFlags, bufSrc, 0, 0, (LPWSTR)&lpMsgBuf, 0, Arguments);
Len = (INT)dwLength;
if (!lpMsgBuf) { // ASSERT(dwLength == 0); } else { // ASSERT(dwLength != 0);
CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
/* Fixup returned length in case of errors */ if (Len < 0) Len = 0;
/* Free the buffer allocated by FormatMessage */ LocalFree(lpMsgBuf); }
return Len;}
/** * @name ConResMsgPrintfV * Formats and writes a message string to a stream. The function requires * a message definition as input. Contrary to the ConMsg* or the Win32 * FormatMessage() functions, the message definition comes from a resource * string table, much like the strings for ConResPrintf(), but is formatted * according to the rules of ConMsgPrintf(). * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] dwFlags * The formatting options, see FormatMessage() for more details. * The only valid flags are @b FORMAT_MESSAGE_ARGUMENT_ARRAY, * @b FORMAT_MESSAGE_IGNORE_INSERTS and @b FORMAT_MESSAGE_MAX_WIDTH_MASK. * All the other flags are internally overridden by the function * to implement its behaviour. * * @param[in] uID * The identifier of the message string. The format string follows the * same specifications as the @a lpSource format string in ConMsgPrintf(). * * @param[in] Arguments * Optional pointer to an array of values describing a variable number of * arguments, depending on the message string. Each argument is used to * replace an <em>insert sequence</em> in the message string. * By default, the @p Arguments parameter is of type @c va_list*, initialized * with va_start(). The state of the @c va_list argument is undefined upon * return from the function. To use the @c va_list again, destroy the variable * argument list pointer using va_end() and reinitialize it with va_start(). * If you do not have a pointer of type @c va_list*, then specify the * @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array * of @c DWORD_PTR values; those values are input to the message formatted * as the insert values. Each insert must have a corresponding element in * the array. * * @remark * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions, * the ConMsg* functions work on format strings that contain <em>insert sequences</em>. * These sequences extend the standard <em>format specifiers</em> as they * allow to specify an <em>insert number</em> referring which precise value * given in arguments to use. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(), * <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a> **/INTConResMsgPrintfV( IN PCON_STREAM Stream, IN DWORD dwFlags, IN UINT uID, IN va_list *Arguments OPTIONAL){ return ConResMsgPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/, dwFlags, uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), Arguments);}
/** * @name ConResMsgPrintfEx * Formats and writes a message string to a stream. The function requires * a message definition as input. Contrary to the ConMsg* or the Win32 * FormatMessage() functions, the message definition comes from a resource * string table, much like the strings for ConResPrintf(), but is formatted * according to the rules of ConMsgPrintf(). * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] hInstance * Optional handle to an instance of the module whose executable file * contains the string resource. Can be set to NULL to get the handle * to the application itself. * * @param[in] dwFlags * The formatting options, see FormatMessage() for more details. * The only valid flags are @b FORMAT_MESSAGE_IGNORE_INSERTS and * @b FORMAT_MESSAGE_MAX_WIDTH_MASK. All the other flags are internally * overridden by the function to implement its behaviour. * * @param[in] uID * The identifier of the message string. The format string follows the * same specifications as the @a lpSource format string in ConMsgPrintf(). * * @param[in] LanguageId * The language identifier of the resource. If this parameter is * <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language * associated with the calling thread is used. To specify a language other * than the current language, use the @c MAKELANGID macro to create this * parameter. * * @param[in] ... * Additional arguments that can be expected by the function, depending * on the message string. Each argument is used to replace an * <em>insert sequence</em> in the message string. * * @remark * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions, * the ConMsg* functions work on format strings that contain <em>insert sequences</em>. * These sequences extend the standard <em>format specifiers</em> as they * allow to specify an <em>insert number</em> referring which precise value * given in arguments to use. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(), * <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a> **/INT__cdeclConResMsgPrintfEx( IN PCON_STREAM Stream, IN HINSTANCE hInstance OPTIONAL, IN DWORD dwFlags, IN UINT uID, IN LANGID LanguageId, ...){ INT Len; va_list args;
/* Sanitize dwFlags */ dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
va_start(args, LanguageId); Len = ConResMsgPrintfExV(Stream, hInstance, dwFlags, uID, LanguageId, &args); va_end(args);
return Len;}
/** * @name ConResMsgPrintf * Formats and writes a message string to a stream. The function requires * a message definition as input. Contrary to the ConMsg* or the Win32 * FormatMessage() functions, the message definition comes from a resource * string table, much like the strings for ConResPrintf(), but is formatted * according to the rules of ConMsgPrintf(). * * @param[in] Stream * Stream to which the write operation is issued. * * @param[in] dwFlags * The formatting options, see FormatMessage() for more details. * The only valid flags are @b FORMAT_MESSAGE_IGNORE_INSERTS and * @b FORMAT_MESSAGE_MAX_WIDTH_MASK. All the other flags are internally * overridden by the function to implement its behaviour. * * @param[in] uID * The identifier of the message string. The format string follows the * same specifications as the @a lpSource format string in ConMsgPrintf(). * * @param[in] ... * Additional arguments that can be expected by the function, depending * on the message string. Each argument is used to replace an * <em>insert sequence</em> in the message string. * * @remark * Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions, * the ConMsg* functions work on format strings that contain <em>insert sequences</em>. * These sequences extend the standard <em>format specifiers</em> as they * allow to specify an <em>insert number</em> referring which precise value * given in arguments to use. * * @return * Numbers of characters successfully written to @p Stream. * * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(), * <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a> **/INT__cdeclConResMsgPrintf( IN PCON_STREAM Stream, IN DWORD dwFlags, IN UINT uID, ...){ INT Len; va_list args;
/* Sanitize dwFlags */ dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
va_start(args, uID); Len = ConResMsgPrintfV(Stream, dwFlags, uID, &args); va_end(args);
return Len;}
VOIDConClearLine(IN PCON_STREAM Stream){ HANDLE hOutput = ConStreamGetOSHandle(Stream);
/* * Erase the full line where the cursor is, and move * the cursor back to the beginning of the line. */
if (IsConsoleHandle(hOutput)) { CONSOLE_SCREEN_BUFFER_INFO csbi; DWORD dwWritten;
GetConsoleScreenBufferInfo(hOutput, &csbi);
csbi.dwCursorPosition.X = 0; // csbi.dwCursorPosition.Y;
FillConsoleOutputCharacterW(hOutput, L' ', csbi.dwSize.X, csbi.dwCursorPosition, &dwWritten); SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition); } else if (IsTTYHandle(hOutput)) { ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile } // else, do nothing for files}
/* EOF */