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