xref: /reactos/sdk/lib/conutils/outstream.c (revision 1734f297)
1 /*
2  * PROJECT:     ReactOS Console Utilities Library
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Provides basic abstraction wrappers around CRT streams or
5  *              Win32 console API I/O functions, to deal with i18n + Unicode
6  *              related problems.
7  * COPYRIGHT:   Copyright 2017-2018 ReactOS Team
8  *              Copyright 2017-2018 Hermes Belusca-Maito
9  */
10 
11 /**
12  * @file    outstream.c
13  * @ingroup ConUtils
14  *
15  * @brief   Console I/O utility API -- Output
16  **/
17 
18 /*
19  * Enable this define if you want to only use CRT functions to output
20  * UNICODE stream to the console, as in the way explained by
21  * http://archives.miloush.net/michkap/archive/2008/03/18/8306597.html
22  */
23 /** NOTE: Experimental! Don't use USE_CRT yet because output to console is a bit broken **/
24 // #define USE_CRT
25 
26 /* FIXME: Temporary HACK before we cleanly support UNICODE functions */
27 #define UNICODE
28 #define _UNICODE
29 
30 #ifdef USE_CRT
31 #include <fcntl.h>
32 #include <io.h>
33 #endif /* USE_CRT */
34 
35 #include <stdlib.h> // limits.h // For MB_LEN_MAX
36 
37 #include <windef.h>
38 #include <winbase.h>
39 #include <winnls.h>
40 #include <winuser.h> // MAKEINTRESOURCEW, RT_STRING
41 #include <wincon.h>  // Console APIs (only if kernel32 support included)
42 #include <strsafe.h>
43 
44 /* PSEH for SEH Support */
45 #include <pseh/pseh2.h>
46 
47 #include "conutils.h"
48 #include "stream.h"
49 #include "stream_private.h"
50 
51 
52 // Also known as: RC_STRING_MAX_SIZE, MAX_BUFFER_SIZE (some programs:
53 // wlanconf, shutdown, set it to 5024), OUTPUT_BUFFER_SIZE (name given
54 // in cmd/console.c), MAX_STRING_SIZE (name given in diskpart) or
55 // MAX_MESSAGE_SIZE (set to 512 in shutdown).
56 #define CON_RC_STRING_MAX_SIZE  4096
57 
58 
59 /**
60  * @name ConWrite
61  *     Writes a counted string to a stream.
62  *
63  * @param[in]   Stream
64  *     Stream to which the write operation is issued.
65  *
66  * @param[in]   szStr
67  *     Pointer to the counted string to write.
68  *
69  * @param[in]   len
70  *     Length of the string pointed by @p szStr, specified
71  *     in number of characters.
72  *
73  * @return
74  *     Numbers of characters successfully written to @p Stream.
75  *
76  * @note
77  *     This function is used as an internal function.
78  *     Use the ConStreamWrite() function instead.
79  *
80  * @remark
81  *     Should be called with the stream locked.
82  **/
83 INT
84 __stdcall
85 ConWrite(
86     IN PCON_STREAM Stream,
87     IN PCTCH szStr,
88     IN DWORD len)
89 {
90 #ifndef USE_CRT
91     DWORD TotalLen = len, dwNumBytes = 0;
92     LPCVOID p;
93 
94     /* If we do not write anything, just return */
95     if (!szStr || len == 0)
96         return 0;
97 
98     /* Check whether we are writing to a console */
99     // if (IsConsoleHandle(Stream->hHandle))
100     if (Stream->IsConsole)
101     {
102         // TODO: Check if (Stream->Mode == WideText or UTF16Text) ??
103 
104         /*
105          * This code is inspired from _cputws, in particular from the fact that,
106          * according to MSDN: https://msdn.microsoft.com/en-us/library/ms687401(v=vs.85).aspx
107          * the buffer size must be less than 64 KB.
108          *
109          * A similar code can be used for implementing _cputs too.
110          */
111 
112         DWORD cchWrite;
113         TotalLen = len, dwNumBytes = 0;
114 
115         while (len > 0)
116         {
117             cchWrite = min(len, 65535 / sizeof(WCHAR));
118 
119             // FIXME: Check return value!
120             WriteConsole(Stream->hHandle, szStr, cchWrite, &dwNumBytes, NULL);
121 
122             szStr += cchWrite;
123             len -= cchWrite;
124         }
125 
126         return (INT)TotalLen; // FIXME: Really return the number of chars written!
127     }
128 
129     /*
130      * We are redirected and writing to a file or pipe instead of the console.
131      * Convert the string from TCHARs to the desired output format, if the two differ.
132      *
133      * Implementation NOTE:
134      *   MultiByteToWideChar (resp. WideCharToMultiByte) are equivalent to
135      *   OemToCharBuffW (resp. CharToOemBuffW), but these latter functions
136      *   uselessly depend on user32.dll, while MultiByteToWideChar and
137      *   WideCharToMultiByte only need kernel32.dll.
138      */
139     if ((Stream->Mode == WideText) || (Stream->Mode == UTF16Text))
140     {
141 #ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
142         /* Convert from the current process/thread's code page to UTF-16 */
143         PWCHAR buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
144         if (!buffer)
145         {
146             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
147             return 0;
148         }
149         len = (DWORD)MultiByteToWideChar(CP_THREAD_ACP, // CP_ACP, CP_OEMCP
150                                          0, szStr, (INT)len, buffer, (INT)len);
151         szStr = (PVOID)buffer;
152 #else
153         /*
154          * Do not perform any conversion since we are already in UTF-16,
155          * that is the same encoding as the stream.
156          */
157 #endif
158 
159         /*
160          * Find any newline character in the buffer,
161          * write the part BEFORE the newline, then emit
162          * a carriage-return + newline sequence and finally
163          * write the remaining part of the buffer.
164          *
165          * This fixes output in files and serial console.
166          */
167         while (len > 0)
168         {
169             /* Loop until we find a newline character */
170             p = szStr;
171             while (len > 0 && *(PCWCH)p != L'\n')
172             {
173                 /* Advance one character */
174                 p = (LPCVOID)((PCWCH)p + 1);
175                 --len;
176             }
177 
178             /* Write everything up to \n */
179             dwNumBytes = ((PCWCH)p - (PCWCH)szStr) * sizeof(WCHAR);
180             WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
181 
182             /*
183              * If we hit a newline and the previous character is not a carriage-return,
184              * emit a carriage-return + newline sequence, otherwise just emit the newline.
185              */
186             if (len > 0 && *(PCWCH)p == L'\n')
187             {
188                 if (p == (LPCVOID)szStr || (p > (LPCVOID)szStr && *((PCWCH)p - 1) != L'\r'))
189                     WriteFile(Stream->hHandle, L"\r\n", 2 * sizeof(WCHAR), &dwNumBytes, NULL);
190                 else
191                     WriteFile(Stream->hHandle, L"\n", sizeof(WCHAR), &dwNumBytes, NULL);
192 
193                 /* Skip \n */
194                 p = (LPCVOID)((PCWCH)p + 1);
195                 --len;
196             }
197             szStr = p;
198         }
199 
200 #ifndef _UNICODE
201         HeapFree(GetProcessHeap(), 0, buffer);
202 #endif
203     }
204     else if ((Stream->Mode == UTF8Text) || (Stream->Mode == AnsiText))
205     {
206         UINT CodePage;
207         PCHAR buffer;
208 
209         /*
210          * Resolve the current code page if it has not been assigned yet
211          * (we do this only if the stream is in ANSI mode; in UTF8 mode
212          * the code page is always set to CP_UTF8). Otherwise use the
213          * current stream's code page.
214          */
215         if (/*(Stream->Mode == AnsiText) &&*/ (Stream->CodePage == INVALID_CP))
216             CodePage = GetConsoleOutputCP(); // CP_ACP, CP_OEMCP
217         else
218             CodePage = Stream->CodePage;
219 
220 #ifdef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
221         /* Convert from UTF-16 to either UTF-8 or ANSI, using the stream code page */
222         // NOTE: MB_LEN_MAX defined either in limits.h or in stdlib.h .
223         buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * MB_LEN_MAX);
224         if (!buffer)
225         {
226             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
227             return 0;
228         }
229         len = WideCharToMultiByte(CodePage, 0,
230                                   szStr, len, buffer, len * MB_LEN_MAX,
231                                   NULL, NULL);
232         szStr = (PVOID)buffer;
233 #else
234         /*
235          * Convert from the current process/thread's code page to either
236          * UTF-8 or ANSI, using the stream code page.
237          * We need to perform a double conversion, by going through UTF-16.
238          */
239         // TODO!
240         #error "Need to implement double conversion!"
241 #endif
242 
243         /*
244          * Find any newline character in the buffer,
245          * write the part BEFORE the newline, then emit
246          * a carriage-return + newline sequence and finally
247          * write the remaining part of the buffer.
248          *
249          * This fixes output in files and serial console.
250          */
251         while (len > 0)
252         {
253             /* Loop until we find a newline character */
254             p = szStr;
255             while (len > 0 && *(PCCH)p != '\n')
256             {
257                 /* Advance one character */
258                 p = (LPCVOID)((PCCH)p + 1);
259                 --len;
260             }
261 
262             /* Write everything up to \n */
263             dwNumBytes = ((PCCH)p - (PCCH)szStr) * sizeof(CHAR);
264             WriteFile(Stream->hHandle, szStr, dwNumBytes, &dwNumBytes, NULL);
265 
266             /*
267              * If we hit a newline and the previous character is not a carriage-return,
268              * emit a carriage-return + newline sequence, otherwise just emit the newline.
269              */
270             if (len > 0 && *(PCCH)p == '\n')
271             {
272                 if (p == (LPCVOID)szStr || (p > (LPCVOID)szStr && *((PCCH)p - 1) != '\r'))
273                     WriteFile(Stream->hHandle, "\r\n", 2, &dwNumBytes, NULL);
274                 else
275                     WriteFile(Stream->hHandle, "\n", 1, &dwNumBytes, NULL);
276 
277                 /* Skip \n */
278                 p = (LPCVOID)((PCCH)p + 1);
279                 --len;
280             }
281             szStr = p;
282         }
283 
284 #ifdef _UNICODE
285         HeapFree(GetProcessHeap(), 0, buffer);
286 #else
287         // TODO!
288 #endif
289     }
290     else // if (Stream->Mode == Binary)
291     {
292         /* Directly output the string */
293         WriteFile(Stream->hHandle, szStr, len, &dwNumBytes, NULL);
294     }
295 
296     // FIXME!
297     return (INT)TotalLen;
298 
299 #else /* defined(USE_CRT) */
300 
301     DWORD total = len;
302     DWORD written = 0;
303 
304     /* If we do not write anything, just return */
305     if (!szStr || len == 0)
306         return 0;
307 
308 #if 1
309     /*
310      * There is no "counted" printf-to-stream or puts-like function, therefore
311      * we use this trick to output the counted string to the stream.
312      */
313     while (1)
314     {
315         written = fwprintf(Stream->fStream, L"%.*s", total, szStr);
316         if (written < total)
317         {
318             /*
319              * Some embedded NULL or special character
320              * was encountered, print it apart.
321              */
322             if (written == 0)
323             {
324                 fputwc(*szStr, Stream->fStream);
325                 written++;
326             }
327 
328             szStr += written;
329             total -= written;
330         }
331         else
332         {
333             break;
334         }
335     }
336     return (INT)len;
337 #else
338     /* ANSI text or Binary output only */
339     _setmode(_fileno(Stream->fStream), _O_TEXT); // _O_BINARY
340     return fwrite(szStr, sizeof(*szStr), len, Stream->fStream);
341 #endif
342 
343 #endif /* defined(USE_CRT) */
344 }
345 
346 
347 #define CON_STREAM_WRITE_CALL(Stream, Str, Len) \
348     (Stream)->WriteFunc((Stream), (Str), (Len))
349 
350 /* Lock the stream only in non-USE_CRT mode (otherwise use the CRT stream lock) */
351 #ifndef USE_CRT
352 
353 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
354 do { \
355     EnterCriticalSection(&(Stream)->Lock); \
356     (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
357     LeaveCriticalSection(&(Stream)->Lock); \
358 } while(0)
359 
360 #define CON_STREAM_WRITE(Stream, Str, Len) \
361 do { \
362     EnterCriticalSection(&(Stream)->Lock); \
363     CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
364     LeaveCriticalSection(&(Stream)->Lock); \
365 } while(0)
366 
367 #else
368 
369 #define CON_STREAM_WRITE2(Stream, Str, Len, RetLen) \
370 do { \
371     (RetLen) = CON_STREAM_WRITE_CALL((Stream), (Str), (Len)); \
372 } while(0)
373 
374 #define CON_STREAM_WRITE(Stream, Str, Len) \
375     CON_STREAM_WRITE_CALL((Stream), (Str), (Len))
376 
377 #endif
378 
379 
380 /**
381  * @name ConStreamWrite
382  *     Writes a counted string to a stream.
383  *
384  * @param[in]   Stream
385  *     Stream to which the write operation is issued.
386  *
387  * @param[in]   szStr
388  *     Pointer to the counted string to write.
389  *
390  * @param[in]   len
391  *     Length of the string pointed by @p szStr, specified
392  *     in number of characters.
393  *
394  * @return
395  *     Numbers of characters successfully written to @p Stream.
396  **/
397 INT
398 ConStreamWrite(
399     IN PCON_STREAM Stream,
400     IN PCTCH szStr,
401     IN DWORD len)
402 {
403     INT Len;
404     CON_STREAM_WRITE2(Stream, szStr, len, Len);
405     return Len;
406 }
407 
408 /**
409  * @name ConPuts
410  *     Writes a NULL-terminated string to a stream.
411  *
412  * @param[in]   Stream
413  *     Stream to which the write operation is issued.
414  *
415  * @param[in]   szStr
416  *     Pointer to the NULL-terminated string to write.
417  *
418  * @return
419  *     Numbers of characters successfully written to @p Stream.
420  *
421  * @remark
422  *     Contrary to the CRT puts() function, ConPuts() does not append
423  *     a terminating new-line character. In this way it behaves more like
424  *     the CRT fputs() function.
425  **/
426 INT
427 ConPuts(
428     IN PCON_STREAM Stream,
429     IN PCWSTR szStr)
430 {
431     INT Len;
432 
433     Len = wcslen(szStr);
434     CON_STREAM_WRITE2(Stream, szStr, Len, Len);
435 
436     /* Fixup returned length in case of errors */
437     if (Len < 0)
438         Len = 0;
439 
440     return Len;
441 }
442 
443 /**
444  * @name ConPrintfV
445  *     Formats and writes a NULL-terminated string to a stream.
446  *
447  * @param[in]   Stream
448  *     Stream to which the write operation is issued.
449  *
450  * @param[in]   szStr
451  *     Pointer to the NULL-terminated format string, that follows the same
452  *     specifications as the @a szStr format string in ConPrintf().
453  *
454  * @param[in]   args
455  *     Parameter describing a variable number of arguments,
456  *     initialized with va_start(), that can be expected by the function,
457  *     depending on the @p szStr format string. Each argument is used to
458  *     replace a <em>format specifier</em> in the format string.
459  *
460  * @return
461  *     Numbers of characters successfully written to @p Stream.
462  *
463  * @see ConPrintf(), printf(), vprintf()
464  **/
465 INT
466 ConPrintfV(
467     IN PCON_STREAM Stream,
468     IN PCWSTR  szStr,
469     IN va_list args)
470 {
471     INT Len;
472     WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
473 
474     // Len = vfwprintf(Stream->fStream, szStr, args); // vfprintf for direct ANSI
475 
476     /*
477      * Re-use szStr as the pointer to end-of-string, so as
478      * to compute the string length instead of calling wcslen().
479      */
480     // StringCchVPrintfW(bufSrc, ARRAYSIZE(bufSrc), szStr, args);
481     // Len = wcslen(bufSrc);
482     StringCchVPrintfExW(bufSrc, ARRAYSIZE(bufSrc), (PWSTR*)&szStr, NULL, 0, szStr, args);
483     Len = szStr - bufSrc;
484 
485     CON_STREAM_WRITE2(Stream, bufSrc, Len, Len);
486 
487     /* Fixup returned length in case of errors */
488     if (Len < 0)
489         Len = 0;
490 
491     return Len;
492 }
493 
494 /**
495  * @name ConPrintf
496  *     Formats and writes a NULL-terminated string to a stream.
497  *
498  * @param[in]   Stream
499  *     Stream to which the write operation is issued.
500  *
501  * @param[in]   szStr
502  *     Pointer to the NULL-terminated format string, that follows the same
503  *     specifications as the @a format string in printf(). This string can
504  *     optionally contain embedded <em>format specifiers</em> that are
505  *     replaced by the values specified in subsequent additional arguments
506  *     and formatted as requested.
507  *
508  * @param[in]   ...
509  *     Additional arguments that can be expected by the function, depending
510  *     on the @p szStr format string. Each argument is used to replace a
511  *     <em>format specifier</em> in the format string.
512  *
513  * @return
514  *     Numbers of characters successfully written to @p Stream.
515  *
516  * @see ConPrintfV(), printf(), vprintf()
517  **/
518 INT
519 __cdecl
520 ConPrintf(
521     IN PCON_STREAM Stream,
522     IN PCWSTR szStr,
523     ...)
524 {
525     INT Len;
526     va_list args;
527 
528     // Len = vfwprintf(Stream->fStream, szMsgBuf, args); // vfprintf for direct ANSI
529 
530     // StringCchPrintfW
531     va_start(args, szStr);
532     Len = ConPrintfV(Stream, szStr, args);
533     va_end(args);
534 
535     return Len;
536 }
537 
538 /**
539  * @name ConResPutsEx
540  *     Writes a string resource to a stream.
541  *
542  * @param[in]   Stream
543  *     Stream to which the write operation is issued.
544  *
545  * @param[in]   hInstance
546  *     Optional handle to an instance of the module whose executable file
547  *     contains the string resource. Can be set to NULL to get the handle
548  *     to the application itself.
549  *
550  * @param[in]   uID
551  *     The identifier of the string to be written.
552  *
553  * @param[in]   LanguageId
554  *     The language identifier of the resource. If this parameter is
555  *     <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
556  *     associated with the calling thread is used. To specify a language other
557  *     than the current language, use the @c MAKELANGID macro to create this
558  *     parameter.
559  *
560  * @return
561  *     Numbers of characters successfully written to @p Stream.
562  *
563  * @remark
564  *     Similarly to ConPuts(), no terminating new-line character is appended.
565  *
566  * @see ConPuts(), ConResPuts()
567  **/
568 INT
569 ConResPutsEx(
570     IN PCON_STREAM Stream,
571     IN HINSTANCE hInstance OPTIONAL,
572     IN UINT   uID,
573     IN LANGID LanguageId)
574 {
575     INT Len;
576     PWCHAR szStr = NULL;
577 
578     Len = K32LoadStringExW(hInstance, uID, LanguageId, (PWSTR)&szStr, 0);
579     if (szStr && Len)
580         // Len = ConPuts(Stream, szStr);
581         CON_STREAM_WRITE2(Stream, szStr, Len, Len);
582 
583     /* Fixup returned length in case of errors */
584     if (Len < 0)
585         Len = 0;
586 
587     return Len;
588 }
589 
590 /**
591  * @name ConResPuts
592  *     Writes a string resource contained in the current application
593  *     to a stream.
594  *
595  * @param[in]   Stream
596  *     Stream to which the write operation is issued.
597  *
598  * @param[in]   uID
599  *     The identifier of the string to be written.
600  *
601  * @return
602  *     Numbers of characters successfully written to @p Stream.
603  *
604  * @remark
605  *     Similarly to ConPuts(), no terminating new-line character is appended.
606  *
607  * @see ConPuts(), ConResPutsEx()
608  **/
609 INT
610 ConResPuts(
611     IN PCON_STREAM Stream,
612     IN UINT uID)
613 {
614     return ConResPutsEx(Stream, NULL /*GetModuleHandleW(NULL)*/,
615                         uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
616 }
617 
618 /**
619  * @name ConResPrintfExV
620  *     Formats and writes a string resource to a stream.
621  *
622  * @param[in]   Stream
623  *     Stream to which the write operation is issued.
624  *
625  * @param[in]   hInstance
626  *     Optional handle to an instance of the module whose executable file
627  *     contains the string resource. Can be set to NULL to get the handle
628  *     to the application itself.
629  *
630  * @param[in]   uID
631  *     The identifier of the format string. The format string follows the
632  *     same specifications as the @a szStr format string in ConPrintf().
633  *
634  * @param[in]   LanguageId
635  *     The language identifier of the resource. If this parameter is
636  *     <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
637  *     associated with the calling thread is used. To specify a language other
638  *     than the current language, use the @c MAKELANGID macro to create this
639  *     parameter.
640  *
641  * @param[in]   args
642  *     Parameter describing a variable number of arguments,
643  *     initialized with va_start(), that can be expected by the function,
644  *     depending on the @p szStr format string. Each argument is used to
645  *     replace a <em>format specifier</em> in the format string.
646  *
647  * @return
648  *     Numbers of characters successfully written to @p Stream.
649  *
650  * @see ConPrintf(), ConResPrintfEx(), ConResPrintfV(), ConResPrintf()
651  **/
652 INT
653 ConResPrintfExV(
654     IN PCON_STREAM Stream,
655     IN HINSTANCE hInstance OPTIONAL,
656     IN UINT    uID,
657     IN LANGID  LanguageId,
658     IN va_list args)
659 {
660     INT Len;
661     WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
662 
663     // NOTE: We may use the special behaviour where nBufMaxSize == 0
664     Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc));
665     if (Len)
666         Len = ConPrintfV(Stream, bufSrc, args);
667 
668     return Len;
669 }
670 
671 /**
672  * @name ConResPrintfV
673  *     Formats and writes a string resource contained in the
674  *     current application to a stream.
675  *
676  * @param[in]   Stream
677  *     Stream to which the write operation is issued.
678  *
679  * @param[in]   uID
680  *     The identifier of the format string. The format string follows the
681  *     same specifications as the @a szStr format string in ConPrintf().
682  *
683  * @param[in]   args
684  *     Parameter describing a variable number of arguments,
685  *     initialized with va_start(), that can be expected by the function,
686  *     depending on the @p szStr format string. Each argument is used to
687  *     replace a <em>format specifier</em> in the format string.
688  *
689  * @return
690  *     Numbers of characters successfully written to @p Stream.
691  *
692  * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintf()
693  **/
694 INT
695 ConResPrintfV(
696     IN PCON_STREAM Stream,
697     IN UINT    uID,
698     IN va_list args)
699 {
700     return ConResPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/,
701                            uID, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
702                            args);
703 }
704 
705 /**
706  * @name ConResPrintfEx
707  *     Formats and writes a string resource to a stream.
708  *
709  * @param[in]   Stream
710  *     Stream to which the write operation is issued.
711  *
712  * @param[in]   hInstance
713  *     Optional handle to an instance of the module whose executable file
714  *     contains the string resource. Can be set to NULL to get the handle
715  *     to the application itself.
716  *
717  * @param[in]   uID
718  *     The identifier of the format string. The format string follows the
719  *     same specifications as the @a szStr format string in ConPrintf().
720  *
721  * @param[in]   LanguageId
722  *     The language identifier of the resource. If this parameter is
723  *     <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
724  *     associated with the calling thread is used. To specify a language other
725  *     than the current language, use the @c MAKELANGID macro to create this
726  *     parameter.
727  *
728  * @param[in]   ...
729  *     Additional arguments that can be expected by the function, depending
730  *     on the @p szStr format string. Each argument is used to replace a
731  *     <em>format specifier</em> in the format string.
732  *
733  * @return
734  *     Numbers of characters successfully written to @p Stream.
735  *
736  * @see ConPrintf(), ConResPrintfExV(), ConResPrintfV(), ConResPrintf()
737  **/
738 INT
739 __cdecl
740 ConResPrintfEx(
741     IN PCON_STREAM Stream,
742     IN HINSTANCE hInstance OPTIONAL,
743     IN UINT   uID,
744     IN LANGID LanguageId,
745     ...)
746 {
747     INT Len;
748     va_list args;
749 
750     va_start(args, LanguageId);
751     Len = ConResPrintfExV(Stream, hInstance, uID, LanguageId, args);
752     va_end(args);
753 
754     return Len;
755 }
756 
757 /**
758  * @name ConResPrintf
759  *     Formats and writes a string resource contained in the
760  *     current application to a stream.
761  *
762  * @param[in]   Stream
763  *     Stream to which the write operation is issued.
764  *
765  * @param[in]   uID
766  *     The identifier of the format string. The format string follows the
767  *     same specifications as the @a szStr format string in ConPrintf().
768  *
769  * @param[in]   ...
770  *     Additional arguments that can be expected by the function, depending
771  *     on the @p szStr format string. Each argument is used to replace a
772  *     <em>format specifier</em> in the format string.
773  *
774  * @return
775  *     Numbers of characters successfully written to @p Stream.
776  *
777  * @see ConPrintf(), ConResPrintfExV(), ConResPrintfEx(), ConResPrintfV()
778  **/
779 INT
780 __cdecl
781 ConResPrintf(
782     IN PCON_STREAM Stream,
783     IN UINT uID,
784     ...)
785 {
786     INT Len;
787     va_list args;
788 
789     va_start(args, uID);
790     Len = ConResPrintfV(Stream, uID, args);
791     va_end(args);
792 
793     return Len;
794 }
795 
796 /**
797  * @name ConMsgPuts
798  *     Writes a message string to a stream without formatting. The function
799  *     requires a message definition as input. The message definition can come
800  *     from a buffer passed to the function. It can come from a message table
801  *     resource in an already-loaded module, or the caller can ask the function
802  *     to search the system's message table resource(s) for the message definition.
803  *     Please refer to the Win32 FormatMessage() function for more details.
804  *
805  * @param[in]   Stream
806  *     Stream to which the write operation is issued.
807  *
808  * @param[in]   dwFlags
809  *     The formatting options, and how to interpret the @p lpSource parameter.
810  *     See FormatMessage() for more details. The @b FORMAT_MESSAGE_ALLOCATE_BUFFER
811  *     and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
812  *     The function implicitly uses the @b FORMAT_MESSAGE_IGNORE_INSERTS flag
813  *     to implement its behaviour.
814  *
815  * @param[in]   lpSource
816  *     The location of the message definition. The type of this parameter
817  *     depends upon the settings in the @p dwFlags parameter.
818  *
819  * @param[in]   dwMessageId
820  *     The message identifier for the requested message. This parameter
821  *     is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
822  *
823  * @param[in]   dwLanguageId
824  *     The language identifier for the requested message. This parameter
825  *     is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
826  *
827  * @return
828  *     Numbers of characters successfully written to @p Stream.
829  *
830  * @remark
831  *     Similarly to ConPuts(), no terminating new-line character is appended.
832  *
833  * @see ConPuts(), ConResPuts() and associated functions,
834  *      <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a>
835  **/
836 INT
837 ConMsgPuts(
838     IN PCON_STREAM Stream,
839     IN DWORD   dwFlags,
840     IN LPCVOID lpSource OPTIONAL,
841     IN DWORD   dwMessageId,
842     IN DWORD   dwLanguageId)
843 {
844     INT Len;
845     DWORD dwLength  = 0;
846     LPWSTR lpMsgBuf = NULL;
847 
848     /*
849      * Sanitize dwFlags. This version always ignores explicitly the inserts
850      * as we emulate the behaviour of the (f)puts function.
851      */
852     dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
853     dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;  // Ignore inserts for FormatMessage.
854     dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
855 
856     /*
857      * Retrieve the message string without appending extra newlines.
858      * Wrap in SEH to protect from invalid string parameters.
859      */
860     _SEH2_TRY
861     {
862         dwLength = FormatMessageW(dwFlags,
863                                   lpSource,
864                                   dwMessageId,
865                                   dwLanguageId,
866                                   (LPWSTR)&lpMsgBuf,
867                                   0,
868                                   NULL);
869     }
870     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
871     {
872     }
873     _SEH2_END;
874 
875     Len = (INT)dwLength;
876 
877     if (!lpMsgBuf)
878     {
879         // ASSERT(dwLength == 0);
880     }
881     else
882     {
883         // ASSERT(dwLength != 0);
884 
885         /* lpMsgBuf is NULL-terminated by FormatMessage */
886         // Len = ConPuts(Stream, lpMsgBuf);
887         CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
888 
889         /* Fixup returned length in case of errors */
890         if (Len < 0)
891             Len = 0;
892 
893         /* Free the buffer allocated by FormatMessage */
894         LocalFree(lpMsgBuf);
895     }
896 
897     return Len;
898 }
899 
900 /**
901  * @name ConMsgPrintf2V
902  *     Formats and writes a message string to a stream.
903  *
904  * @remark For internal use only.
905  *
906  * @see ConMsgPrintfV()
907  **/
908 INT
909 ConMsgPrintf2V(
910     IN PCON_STREAM Stream,
911     IN DWORD   dwFlags,
912     IN LPCVOID lpSource OPTIONAL,
913     IN DWORD   dwMessageId,
914     IN DWORD   dwLanguageId,
915     IN va_list args)
916 {
917     INT Len;
918     DWORD dwLength  = 0;
919     LPWSTR lpMsgBuf = NULL;
920 
921     /*
922      * Sanitize dwFlags. This version always ignores explicitly the inserts.
923      * The string that we will return to the user will not be pre-formatted.
924      */
925     dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
926     dwFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;  // Ignore inserts for FormatMessage.
927     dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
928 
929     /*
930      * Retrieve the message string without appending extra newlines.
931      * Wrap in SEH to protect from invalid string parameters.
932      */
933     _SEH2_TRY
934     {
935         dwLength = FormatMessageW(dwFlags,
936                                   lpSource,
937                                   dwMessageId,
938                                   dwLanguageId,
939                                   (LPWSTR)&lpMsgBuf,
940                                   0,
941                                   NULL);
942     }
943     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
944     {
945     }
946     _SEH2_END;
947 
948     Len = (INT)dwLength;
949 
950     if (!lpMsgBuf)
951     {
952         // ASSERT(dwLength == 0);
953     }
954     else
955     {
956         // ASSERT(dwLength != 0);
957 
958         /* lpMsgBuf is NULL-terminated by FormatMessage */
959         Len = ConPrintfV(Stream, lpMsgBuf, args);
960         // CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
961 
962         /* Fixup returned length in case of errors */
963         if (Len < 0)
964             Len = 0;
965 
966         /* Free the buffer allocated by FormatMessage */
967         LocalFree(lpMsgBuf);
968     }
969 
970     return Len;
971 }
972 
973 /**
974  * @name ConMsgPrintfV
975  *     Formats and writes a message string to a stream. The function requires
976  *     a message definition as input. The message definition can come from a
977  *     buffer passed to the function. It can come from a message table resource
978  *     in an already-loaded module, or the caller can ask the function to search
979  *     the system's message table resource(s) for the message definition.
980  *     Please refer to the Win32 FormatMessage() function for more details.
981  *
982  * @param[in]   Stream
983  *     Stream to which the write operation is issued.
984  *
985  * @param[in]   dwFlags
986  *     The formatting options, and how to interpret the @p lpSource parameter.
987  *     See FormatMessage() for more details.
988  *     The @b FORMAT_MESSAGE_ALLOCATE_BUFFER flag is always ignored.
989  *
990  * @param[in]   lpSource
991  *     The location of the message definition. The type of this parameter
992  *     depends upon the settings in the @p dwFlags parameter.
993  *
994  * @param[in]   dwMessageId
995  *     The message identifier for the requested message. This parameter
996  *     is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
997  *
998  * @param[in]   dwLanguageId
999  *     The language identifier for the requested message. This parameter
1000  *     is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
1001  *
1002  * @param[in]   Arguments
1003  *     Optional pointer to an array of values describing a variable number of
1004  *     arguments, depending on the message string. Each argument is used to
1005  *     replace an <em>insert sequence</em> in the message string.
1006  *     By default, the @p Arguments parameter is of type @c va_list*, initialized
1007  *     with va_start(). The state of the @c va_list argument is undefined upon
1008  *     return from the function. To use the @c va_list again, destroy the variable
1009  *     argument list pointer using va_end() and reinitialize it with va_start().
1010  *     If you do not have a pointer of type @c va_list*, then specify the
1011  *     @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1012  *     of @c DWORD_PTR values; those values are input to the message formatted
1013  *     as the insert values. Each insert must have a corresponding element in
1014  *     the array.
1015  *
1016  * @remark
1017  *     Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1018  *     the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1019  *     These sequences extend the standard <em>format specifiers</em> as they
1020  *     allow to specify an <em>insert number</em> referring which precise value
1021  *     given in arguments to use.
1022  *
1023  * @return
1024  *     Numbers of characters successfully written to @p Stream.
1025  *
1026  * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1027  *      <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a>
1028  **/
1029 INT
1030 ConMsgPrintfV(
1031     IN PCON_STREAM Stream,
1032     IN DWORD   dwFlags,
1033     IN LPCVOID lpSource OPTIONAL,
1034     IN DWORD   dwMessageId,
1035     IN DWORD   dwLanguageId,
1036     IN va_list *Arguments OPTIONAL)
1037 {
1038     INT Len;
1039     DWORD dwLength  = 0;
1040     LPWSTR lpMsgBuf = NULL;
1041 
1042     /* Sanitize dwFlags */
1043     dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
1044 
1045     /*
1046      * Retrieve the message string without appending extra newlines.
1047      * Use the "safe" FormatMessage version (SEH-protected) to protect
1048      * from invalid string parameters.
1049      */
1050     dwLength = FormatMessageSafeW(dwFlags,
1051                                   lpSource,
1052                                   dwMessageId,
1053                                   dwLanguageId,
1054                                   (LPWSTR)&lpMsgBuf,
1055                                   0,
1056                                   Arguments);
1057 
1058     Len = (INT)dwLength;
1059 
1060     if (!lpMsgBuf)
1061     {
1062         // ASSERT(dwLength == 0);
1063     }
1064     else
1065     {
1066         // ASSERT(dwLength != 0);
1067 
1068         CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
1069 
1070         /* Fixup returned length in case of errors */
1071         if (Len < 0)
1072             Len = 0;
1073 
1074         /* Free the buffer allocated by FormatMessage */
1075         LocalFree(lpMsgBuf);
1076     }
1077 
1078     return Len;
1079 }
1080 
1081 /**
1082  * @name ConMsgPrintf
1083  *     Formats and writes a message string to a stream. The function requires
1084  *     a message definition as input. The message definition can come from a
1085  *     buffer passed to the function. It can come from a message table resource
1086  *     in an already-loaded module, or the caller can ask the function to search
1087  *     the system's message table resource(s) for the message definition.
1088  *     Please refer to the Win32 FormatMessage() function for more details.
1089  *
1090  * @param[in]   Stream
1091  *     Stream to which the write operation is issued.
1092  *
1093  * @param[in]   dwFlags
1094  *     The formatting options, and how to interpret the @p lpSource parameter.
1095  *     See FormatMessage() for more details. The @b FORMAT_MESSAGE_ALLOCATE_BUFFER
1096  *     and @b FORMAT_MESSAGE_ARGUMENT_ARRAY flags are always ignored.
1097  *
1098  * @param[in]   lpSource
1099  *     The location of the message definition. The type of this parameter
1100  *     depends upon the settings in the @p dwFlags parameter.
1101  *
1102  * @param[in]   dwMessageId
1103  *     The message identifier for the requested message. This parameter
1104  *     is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
1105  *
1106  * @param[in]   dwLanguageId
1107  *     The language identifier for the requested message. This parameter
1108  *     is ignored if @p dwFlags includes @b FORMAT_MESSAGE_FROM_STRING.
1109  *
1110  * @param[in]   ...
1111  *     Additional arguments that can be expected by the function, depending
1112  *     on the message string. Each argument is used to replace an
1113  *     <em>insert sequence</em> in the message string.
1114  *
1115  * @remark
1116  *     Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1117  *     the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1118  *     These sequences extend the standard <em>format specifiers</em> as they
1119  *     allow to specify an <em>insert number</em> referring which precise value
1120  *     given in arguments to use.
1121  *
1122  * @return
1123  *     Numbers of characters successfully written to @p Stream.
1124  *
1125  * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintfV(),
1126  *      <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a>
1127  **/
1128 INT
1129 __cdecl
1130 ConMsgPrintf(
1131     IN PCON_STREAM Stream,
1132     IN DWORD   dwFlags,
1133     IN LPCVOID lpSource OPTIONAL,
1134     IN DWORD   dwMessageId,
1135     IN DWORD   dwLanguageId,
1136     ...)
1137 {
1138     INT Len;
1139     va_list args;
1140 
1141     /* Sanitize dwFlags */
1142     dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1143 
1144     va_start(args, dwLanguageId);
1145     Len = ConMsgPrintfV(Stream,
1146                         dwFlags,
1147                         lpSource,
1148                         dwMessageId,
1149                         dwLanguageId,
1150                         &args);
1151     va_end(args);
1152 
1153     return Len;
1154 }
1155 
1156 /**
1157  * @name ConResMsgPrintfExV
1158  *     Formats and writes a message string to a stream. The function requires
1159  *     a message definition as input. Contrary to the ConMsg* or the Win32
1160  *     FormatMessage() functions, the message definition comes from a resource
1161  *     string table, much like the strings for ConResPrintf(), but is formatted
1162  *     according to the rules of ConMsgPrintf().
1163  *
1164  * @param[in]   Stream
1165  *     Stream to which the write operation is issued.
1166  *
1167  * @param[in]   hInstance
1168  *     Optional handle to an instance of the module whose executable file
1169  *     contains the string resource. Can be set to NULL to get the handle
1170  *     to the application itself.
1171  *
1172  * @param[in]   dwFlags
1173  *     The formatting options, see FormatMessage() for more details.
1174  *     The only valid flags are @b FORMAT_MESSAGE_ARGUMENT_ARRAY,
1175  *     @b FORMAT_MESSAGE_IGNORE_INSERTS and @b FORMAT_MESSAGE_MAX_WIDTH_MASK.
1176  *     All the other flags are internally overridden by the function
1177  *     to implement its behaviour.
1178  *
1179  * @param[in]   uID
1180  *     The identifier of the message string. The format string follows the
1181  *     same specifications as the @a lpSource format string in ConMsgPrintf().
1182  *
1183  * @param[in]   LanguageId
1184  *     The language identifier of the resource. If this parameter is
1185  *     <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
1186  *     associated with the calling thread is used. To specify a language other
1187  *     than the current language, use the @c MAKELANGID macro to create this
1188  *     parameter.
1189  *
1190  * @param[in]   Arguments
1191  *     Optional pointer to an array of values describing a variable number of
1192  *     arguments, depending on the message string. Each argument is used to
1193  *     replace an <em>insert sequence</em> in the message string.
1194  *     By default, the @p Arguments parameter is of type @c va_list*, initialized
1195  *     with va_start(). The state of the @c va_list argument is undefined upon
1196  *     return from the function. To use the @c va_list again, destroy the variable
1197  *     argument list pointer using va_end() and reinitialize it with va_start().
1198  *     If you do not have a pointer of type @c va_list*, then specify the
1199  *     @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1200  *     of @c DWORD_PTR values; those values are input to the message formatted
1201  *     as the insert values. Each insert must have a corresponding element in
1202  *     the array.
1203  *
1204  * @remark
1205  *     Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1206  *     the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1207  *     These sequences extend the standard <em>format specifiers</em> as they
1208  *     allow to specify an <em>insert number</em> referring which precise value
1209  *     given in arguments to use.
1210  *
1211  * @return
1212  *     Numbers of characters successfully written to @p Stream.
1213  *
1214  * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1215  *      <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a>
1216  **/
1217 INT
1218 ConResMsgPrintfExV(
1219     IN PCON_STREAM Stream,
1220     IN HINSTANCE hInstance OPTIONAL,
1221     IN DWORD   dwFlags,
1222     IN UINT    uID,
1223     IN LANGID  LanguageId,
1224     IN va_list *Arguments OPTIONAL)
1225 {
1226     INT Len;
1227     DWORD dwLength  = 0;
1228     LPWSTR lpMsgBuf = NULL;
1229     WCHAR bufSrc[CON_RC_STRING_MAX_SIZE];
1230 
1231     /* Retrieve the string from the resource string table */
1232     // NOTE: We may use the special behaviour where nBufMaxSize == 0
1233     Len = K32LoadStringExW(hInstance, uID, LanguageId, bufSrc, ARRAYSIZE(bufSrc));
1234     if (Len == 0)
1235         return Len;
1236 
1237     /* Sanitize dwFlags */
1238     dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; // Always allocate an internal buffer.
1239 
1240     /* The string has already been manually loaded */
1241     dwFlags &= ~(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM);
1242     dwFlags |= FORMAT_MESSAGE_FROM_STRING;
1243 
1244     /*
1245      * Retrieve the message string without appending extra newlines.
1246      * Use the "safe" FormatMessage version (SEH-protected) to protect
1247      * from invalid string parameters.
1248      */
1249     dwLength = FormatMessageSafeW(dwFlags,
1250                                   bufSrc,
1251                                   0, 0,
1252                                   (LPWSTR)&lpMsgBuf,
1253                                   0,
1254                                   Arguments);
1255 
1256     Len = (INT)dwLength;
1257 
1258     if (!lpMsgBuf)
1259     {
1260         // ASSERT(dwLength == 0);
1261     }
1262     else
1263     {
1264         // ASSERT(dwLength != 0);
1265 
1266         CON_STREAM_WRITE2(Stream, lpMsgBuf, dwLength, Len);
1267 
1268         /* Fixup returned length in case of errors */
1269         if (Len < 0)
1270             Len = 0;
1271 
1272         /* Free the buffer allocated by FormatMessage */
1273         LocalFree(lpMsgBuf);
1274     }
1275 
1276     return Len;
1277 }
1278 
1279 /**
1280  * @name ConResMsgPrintfV
1281  *     Formats and writes a message string to a stream. The function requires
1282  *     a message definition as input. Contrary to the ConMsg* or the Win32
1283  *     FormatMessage() functions, the message definition comes from a resource
1284  *     string table, much like the strings for ConResPrintf(), but is formatted
1285  *     according to the rules of ConMsgPrintf().
1286  *
1287  * @param[in]   Stream
1288  *     Stream to which the write operation is issued.
1289  *
1290  * @param[in]   dwFlags
1291  *     The formatting options, see FormatMessage() for more details.
1292  *     The only valid flags are @b FORMAT_MESSAGE_ARGUMENT_ARRAY,
1293  *     @b FORMAT_MESSAGE_IGNORE_INSERTS and @b FORMAT_MESSAGE_MAX_WIDTH_MASK.
1294  *     All the other flags are internally overridden by the function
1295  *     to implement its behaviour.
1296  *
1297  * @param[in]   uID
1298  *     The identifier of the message string. The format string follows the
1299  *     same specifications as the @a lpSource format string in ConMsgPrintf().
1300  *
1301  * @param[in]   Arguments
1302  *     Optional pointer to an array of values describing a variable number of
1303  *     arguments, depending on the message string. Each argument is used to
1304  *     replace an <em>insert sequence</em> in the message string.
1305  *     By default, the @p Arguments parameter is of type @c va_list*, initialized
1306  *     with va_start(). The state of the @c va_list argument is undefined upon
1307  *     return from the function. To use the @c va_list again, destroy the variable
1308  *     argument list pointer using va_end() and reinitialize it with va_start().
1309  *     If you do not have a pointer of type @c va_list*, then specify the
1310  *     @b FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array
1311  *     of @c DWORD_PTR values; those values are input to the message formatted
1312  *     as the insert values. Each insert must have a corresponding element in
1313  *     the array.
1314  *
1315  * @remark
1316  *     Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1317  *     the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1318  *     These sequences extend the standard <em>format specifiers</em> as they
1319  *     allow to specify an <em>insert number</em> referring which precise value
1320  *     given in arguments to use.
1321  *
1322  * @return
1323  *     Numbers of characters successfully written to @p Stream.
1324  *
1325  * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1326  *      <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a>
1327  **/
1328 INT
1329 ConResMsgPrintfV(
1330     IN PCON_STREAM Stream,
1331     IN DWORD   dwFlags,
1332     IN UINT    uID,
1333     IN va_list *Arguments OPTIONAL)
1334 {
1335     return ConResMsgPrintfExV(Stream, NULL /*GetModuleHandleW(NULL)*/,
1336                               dwFlags, uID,
1337                               MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
1338                               Arguments);
1339 }
1340 
1341 /**
1342  * @name ConResMsgPrintfEx
1343  *     Formats and writes a message string to a stream. The function requires
1344  *     a message definition as input. Contrary to the ConMsg* or the Win32
1345  *     FormatMessage() functions, the message definition comes from a resource
1346  *     string table, much like the strings for ConResPrintf(), but is formatted
1347  *     according to the rules of ConMsgPrintf().
1348  *
1349  * @param[in]   Stream
1350  *     Stream to which the write operation is issued.
1351  *
1352  * @param[in]   hInstance
1353  *     Optional handle to an instance of the module whose executable file
1354  *     contains the string resource. Can be set to NULL to get the handle
1355  *     to the application itself.
1356  *
1357  * @param[in]   dwFlags
1358  *     The formatting options, see FormatMessage() for more details.
1359  *     The only valid flags are @b FORMAT_MESSAGE_IGNORE_INSERTS and
1360  *     @b FORMAT_MESSAGE_MAX_WIDTH_MASK. All the other flags are internally
1361  *     overridden by the function to implement its behaviour.
1362  *
1363  * @param[in]   uID
1364  *     The identifier of the message string. The format string follows the
1365  *     same specifications as the @a lpSource format string in ConMsgPrintf().
1366  *
1367  * @param[in]   LanguageId
1368  *     The language identifier of the resource. If this parameter is
1369  *     <tt>MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)</tt>, the current language
1370  *     associated with the calling thread is used. To specify a language other
1371  *     than the current language, use the @c MAKELANGID macro to create this
1372  *     parameter.
1373  *
1374  * @param[in]   ...
1375  *     Additional arguments that can be expected by the function, depending
1376  *     on the message string. Each argument is used to replace an
1377  *     <em>insert sequence</em> in the message string.
1378  *
1379  * @remark
1380  *     Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1381  *     the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1382  *     These sequences extend the standard <em>format specifiers</em> as they
1383  *     allow to specify an <em>insert number</em> referring which precise value
1384  *     given in arguments to use.
1385  *
1386  * @return
1387  *     Numbers of characters successfully written to @p Stream.
1388  *
1389  * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1390  *      <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a>
1391  **/
1392 INT
1393 __cdecl
1394 ConResMsgPrintfEx(
1395     IN PCON_STREAM Stream,
1396     IN HINSTANCE hInstance OPTIONAL,
1397     IN DWORD  dwFlags,
1398     IN UINT   uID,
1399     IN LANGID LanguageId,
1400     ...)
1401 {
1402     INT Len;
1403     va_list args;
1404 
1405     /* Sanitize dwFlags */
1406     dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1407 
1408     va_start(args, LanguageId);
1409     Len = ConResMsgPrintfExV(Stream,
1410                              hInstance,
1411                              dwFlags,
1412                              uID,
1413                              LanguageId,
1414                              &args);
1415     va_end(args);
1416 
1417     return Len;
1418 }
1419 
1420 /**
1421  * @name ConResMsgPrintf
1422  *     Formats and writes a message string to a stream. The function requires
1423  *     a message definition as input. Contrary to the ConMsg* or the Win32
1424  *     FormatMessage() functions, the message definition comes from a resource
1425  *     string table, much like the strings for ConResPrintf(), but is formatted
1426  *     according to the rules of ConMsgPrintf().
1427  *
1428  * @param[in]   Stream
1429  *     Stream to which the write operation is issued.
1430  *
1431  * @param[in]   dwFlags
1432  *     The formatting options, see FormatMessage() for more details.
1433  *     The only valid flags are @b FORMAT_MESSAGE_IGNORE_INSERTS and
1434  *     @b FORMAT_MESSAGE_MAX_WIDTH_MASK. All the other flags are internally
1435  *     overridden by the function to implement its behaviour.
1436  *
1437  * @param[in]   uID
1438  *     The identifier of the message string. The format string follows the
1439  *     same specifications as the @a lpSource format string in ConMsgPrintf().
1440  *
1441  * @param[in]   ...
1442  *     Additional arguments that can be expected by the function, depending
1443  *     on the message string. Each argument is used to replace an
1444  *     <em>insert sequence</em> in the message string.
1445  *
1446  * @remark
1447  *     Contrary to printf(), ConPrintf(), ConResPrintf() and associated functions,
1448  *     the ConMsg* functions work on format strings that contain <em>insert sequences</em>.
1449  *     These sequences extend the standard <em>format specifiers</em> as they
1450  *     allow to specify an <em>insert number</em> referring which precise value
1451  *     given in arguments to use.
1452  *
1453  * @return
1454  *     Numbers of characters successfully written to @p Stream.
1455  *
1456  * @see ConPrintf(), ConResPrintf() and associated functions, ConMsgPrintf(),
1457  *      <a href="https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage">FormatMessage() (on MSDN)</a>
1458  **/
1459 INT
1460 __cdecl
1461 ConResMsgPrintf(
1462     IN PCON_STREAM Stream,
1463     IN DWORD dwFlags,
1464     IN UINT  uID,
1465     ...)
1466 {
1467     INT Len;
1468     va_list args;
1469 
1470     /* Sanitize dwFlags */
1471     dwFlags &= ~FORMAT_MESSAGE_ARGUMENT_ARRAY;
1472 
1473     va_start(args, uID);
1474     Len = ConResMsgPrintfV(Stream, dwFlags, uID, &args);
1475     va_end(args);
1476 
1477     return Len;
1478 }
1479 
1480 
1481 
1482 VOID
1483 ConClearLine(IN PCON_STREAM Stream)
1484 {
1485     HANDLE hOutput = ConStreamGetOSHandle(Stream);
1486 
1487     /*
1488      * Erase the full line where the cursor is, and move
1489      * the cursor back to the beginning of the line.
1490      */
1491 
1492     if (IsConsoleHandle(hOutput))
1493     {
1494         CONSOLE_SCREEN_BUFFER_INFO csbi;
1495         DWORD dwWritten;
1496 
1497         GetConsoleScreenBufferInfo(hOutput, &csbi);
1498 
1499         csbi.dwCursorPosition.X = 0;
1500         // csbi.dwCursorPosition.Y;
1501 
1502         FillConsoleOutputCharacterW(hOutput, L' ',
1503                                     csbi.dwSize.X,
1504                                     csbi.dwCursorPosition,
1505                                     &dwWritten);
1506         SetConsoleCursorPosition(hOutput, csbi.dwCursorPosition);
1507     }
1508     else if (IsTTYHandle(hOutput))
1509     {
1510         ConPuts(Stream, L"\x1B[2K\x1B[1G"); // FIXME: Just use WriteFile
1511     }
1512     // else, do nothing for files
1513 }
1514 
1515 /* EOF */
1516