VirtualBox

source: kBuild/trunk/src/lib/msc_buffered_printf.c@ 3669

Last change on this file since 3669 was 3669, checked in by bird, 2 months ago

kmk: More stdout close error debugging

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.2 KB
Line 
1/* $Id: msc_buffered_printf.c 3669 2025-02-22 01:31:14Z bird $ */
2/** @file
3 * printf, vprintf, fprintf, puts, fputs console optimizations for Windows/MSC.
4 */
5
6/*
7 * Copyright (c) 2016 knut st. osmundsen <[email protected]>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#include <Windows.h>
36#include <stdio.h>
37#include <stdarg.h>
38#include <io.h>
39#include <conio.h>
40#include <malloc.h>
41#include <locale.h>
42#include "console.h"
43
44#pragma warning(disable: 4273) /* inconsistent dll linkage*/
45
46#ifndef KWORKER
47# define DLL_IMPORT __declspec(dllexport)
48#else
49# define DLL_IMPORT
50#endif
51
52
53
54#if _MSC_VER >= 1400
55typedef int (__cdecl *PFN_STDIO_COMMON_VFPRINTF_T)(unsigned __int64, FILE *, const char *, _locale_t, va_list);
56
57static PFN_STDIO_COMMON_VFPRINTF_T g_pfnFallback_vfprintf = NULL;
58
59DLL_IMPORT
60int __cdecl __stdio_common_vfprintf(unsigned __int64 fOptions, FILE *pFile, const char *pszFormat, _locale_t hLocale, va_list va)
61{
62 /*
63 * Make sure we've got the fallback function before we start.
64 */
65 PFN_STDIO_COMMON_VFPRINTF_T pfnFallback = g_pfnFallback_vfprintf;
66 if (g_pfnFallback_vfprintf)
67 { /* likely */ }
68 else
69 {
70 HANDLE hmodCrt = GetModuleHandleW(L"api-ms-win-crt-stdio-l1-1-0.dll");
71 if (!hmodCrt)
72 {
73 hmodCrt = GetModuleHandleW(L"ucrtbase.dll");
74 if (!hmodCrt)
75 {
76 hmodCrt = LoadLibraryW(L"api-ms-win-crt-stdio-l1-1-0.dll");
77 if (!hmodCrt)
78 {
79 static char const s_szMsg[] = "fatal error! Failed to load the UCRT DLL and therefore no __stdio_common_vfprintf fallback!\r\n";
80 DWORD cbIgn = 0;
81 WriteFile(GetStdHandle(STD_ERROR_HANDLE ), s_szMsg, sizeof(s_szMsg) - 1, &cbIgn, NULL);
82 TerminateProcess(GetCurrentProcess(), 998);
83 }
84 }
85 }
86
87 pfnFallback = (PFN_STDIO_COMMON_VFPRINTF_T)GetProcAddress(hmodCrt, "__stdio_common_vfprintf");
88 if (!pfnFallback)
89 {
90 static char const s_szMsg[] = "fatal error! Failed resolve __stdio_common_vfprintf in the UCRT DLL!\r\n";
91 DWORD cbIgn = 0;
92 WriteFile(GetStdHandle(STD_ERROR_HANDLE ), s_szMsg, sizeof(s_szMsg) - 1, &cbIgn, NULL);
93 TerminateProcess(GetCurrentProcess(), 997);
94 }
95
96 g_pfnFallback_vfprintf = pfnFallback;
97 }
98
99 /*
100 * If it's a TTY, try format into a stack buffer and output using our
101 * console optimized fwrite wrapper.
102 */
103 if (*pszFormat != '\0')
104 {
105 if (hLocale == NULL)
106 {
107 int fd = fileno(pFile);
108 if (fd >= 0)
109 {
110 if (is_console(fd))
111 {
112 char *pszTmp = (char *)alloca(16384);
113 va_list va2 = va;
114 int cchRet = vsnprintf(pszTmp, 16384, pszFormat, va2);
115 if (cchRet < 16384 - 1)
116 return (int)maybe_con_fwrite(pszTmp, cchRet, 1, pFile);
117 }
118 }
119 }
120 }
121
122 /*
123 * Fallback.
124 */
125# ifdef DEBUG_STDOUT_CLOSE_ISSUE
126 extern void my_check_stdout(const char *pszWhere);
127 if (pFile == stdout)
128 my_check_stdout("__stdio_common_vfprintf/entry");
129 int rcRet = g_pfnFallback_vfprintf(fOptions, pFile, pszFormat, hLocale, va);
130 if (pFile == stdout)
131 my_check_stdout("__stdio_common_vfprintf/exit");
132 return rcRet;
133# else
134 return g_pfnFallback_vfprintf(fOptions, pFile, pszFormat, hLocale, va);
135# endif
136}
137
138
139void * const __imp___stdio_common_vfprintf = (void *)(uintptr_t)__stdio_common_vfprintf;
140
141
142#else /* < _MSC_VER <= 1400 */
143
144# undef printf
145# undef vprintf
146# undef fprintf
147# undef puts
148# undef fputs
149
150/**
151 * Replaces printf for MSC to speed up console output.
152 *
153 * @returns chars written on success, -1 and errno on failure.
154 * @param pszFormat The format string.
155 * @param ... Format arguments.
156 */
157DLL_IMPORT
158int __cdecl printf(const char *pszFormat, ...)
159{
160 int cchRet;
161 va_list va;
162 va_start(va, pszFormat);
163 cchRet = vprintf(pszFormat, va);
164 va_end(va);
165 return cchRet;
166}
167
168
169/**
170 * Replaces vprintf for MSC to speed up console output.
171 *
172 * @returns chars written on success, -1 and errno on failure.
173 * @param pszFormat The format string.
174 * @param va Format arguments.
175 */
176DLL_IMPORT
177int __cdecl vprintf(const char *pszFormat, va_list va)
178{
179 /*
180 * If it's a TTY, try format into a stack buffer and output using our
181 * console optimized fwrite wrapper.
182 */
183 if (*pszFormat != '\0')
184 {
185 int fd = fileno(stdout);
186 if (fd >= 0)
187 {
188 if (is_console(fd))
189 {
190 char *pszTmp = (char *)alloca(16384);
191 va_list va2 = va;
192 int cchRet = vsnprintf(pszTmp, 16384, pszFormat, va2);
193 if (cchRet < 16384 - 1)
194 return (int)maybe_con_fwrite(pszTmp, cchRet, 1, stdout);
195 }
196 }
197 }
198
199 /*
200 * Fallback.
201 */
202 return vfprintf(stdout, pszFormat, va);
203}
204
205
206/**
207 * Replaces fprintf for MSC to speed up console output.
208 *
209 * @returns chars written on success, -1 and errno on failure.
210 * @param pFile The output file/stream.
211 * @param pszFormat The format string.
212 * @param va Format arguments.
213 */
214DLL_IMPORT
215int __cdecl fprintf(FILE *pFile, const char *pszFormat, ...)
216{
217 va_list va;
218 int cchRet;
219
220 /*
221 * If it's a TTY, try format into a stack buffer and output using our
222 * console optimized fwrite wrapper.
223 */
224 if (*pszFormat != '\0')
225 {
226 int fd = fileno(pFile);
227 if (fd >= 0)
228 {
229 if (is_console(fd))
230 {
231 char *pszTmp = (char *)alloca(16384);
232 if (pszTmp)
233 {
234 va_start(va, pszFormat);
235 cchRet = vsnprintf(pszTmp, 16384, pszFormat, va);
236 va_end(va);
237 if (cchRet < 16384 - 1)
238 return (int)maybe_con_fwrite(pszTmp, cchRet, 1, pFile);
239 }
240 }
241 }
242 }
243
244 /*
245 * Fallback.
246 */
247 va_start(va, pszFormat);
248 cchRet = vfprintf(pFile, pszFormat, va);
249 va_end(va);
250 return cchRet;
251}
252
253
254/**
255 * Replaces puts for MSC to speed up console output.
256 *
257 * @returns Units written; 0 & errno on failure.
258 * @param pszString The string to write. (newline is appended)
259 */
260DLL_IMPORT
261int __cdecl puts(const char *pszString)
262{
263 /*
264 * If it's a TTY, we convert it to a wide char string with a newline
265 * appended right here. Going thru maybe_con_fwrite is just extra
266 * buffering due to the added newline.
267 */
268 size_t cchString = strlen(pszString);
269 size_t cch;
270 if (cchString > 0 && cchString < INT_MAX / 2)
271 {
272 int fd = fileno(stdout);
273 if (fd >= 0)
274 {
275 if (is_console(fd))
276 {
277 HANDLE hCon = (HANDLE)_get_osfhandle(fd);
278 if ( hCon != INVALID_HANDLE_VALUE
279 && hCon != NULL)
280 {
281 wchar_t awcBuf[1024];
282 wchar_t *pawcBuf;
283 wchar_t *pawcBufFree = NULL;
284 size_t cwcBuf = cchString * 2 + 16 + 1; /* +1 for added newline */
285 if (cwcBuf < sizeof(awcBuf) / sizeof(awcBuf[0]))
286 {
287 pawcBuf = awcBuf;
288 cwcBuf = sizeof(awcBuf) / sizeof(awcBuf[0]);
289 }
290 else
291 pawcBufFree = pawcBuf = (wchar_t *)malloc(cwcBuf * sizeof(wchar_t));
292 if (pawcBuf)
293 {
294 int cwcToWrite = MultiByteToWideChar(get_crt_codepage(), 0 /*dwFlags*/,
295 pszString, (int)cchString,
296 pawcBuf, (int)(cwcBuf - 1));
297 if (cwcToWrite > 0)
298 {
299 int rc;
300 pawcBuf[cwcToWrite++] = '\n';
301 pawcBuf[cwcToWrite] = '\0';
302
303 /* Let the CRT do the rest. At least the Visual C++ 2010 CRT
304 sources indicates _cputws will do the right thing. */
305 fflush(stdout);
306 rc = _cputws(pawcBuf);
307 if (pawcBufFree)
308 free(pawcBufFree);
309 if (rc >= 0)
310 return 0;
311 return -1;
312 }
313 free(pawcBufFree);
314 }
315 }
316 }
317 }
318 }
319
320 /*
321 * Fallback.
322 */
323 cch = fwrite(pszString, cchString, 1, stdout);
324 if (cch == cchString)
325 {
326 if (putc('\n', stdout) != EOF)
327 return 0;
328 }
329 return -1;
330}
331
332
333/**
334 * Replaces puts for MSC to speed up console output.
335 *
336 * @returns Units written; 0 & errno on failure.
337 * @param pszString The string to write (no newline added).
338 * @param pFile The output file.
339 */
340DLL_IMPORT
341int __cdecl fputs(const char *pszString, FILE *pFile)
342{
343 size_t cchString = strlen(pszString);
344 size_t cch = maybe_con_fwrite(pszString, cchString, 1, pFile);
345 if (cch == cchString)
346 return 0;
347 return -1;
348}
349
350
351
352void * const __imp_printf = (void *)(uintptr_t)printf;
353void * const __imp_vprintf = (void *)(uintptr_t)vprintf;
354void * const __imp_fprintf = (void *)(uintptr_t)fprintf;
355void * const __imp_puts = (void *)(uintptr_t)puts;
356void * const __imp_fputs = (void *)(uintptr_t)fputs;
357
358#endif /* Old MSC */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette