summaryrefslogtreecommitdiffstats
path: root/gl/stdio-consolesafe.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/stdio-consolesafe.c')
-rw-r--r--gl/stdio-consolesafe.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/gl/stdio-consolesafe.c b/gl/stdio-consolesafe.c
new file mode 100644
index 00000000..f634de13
--- /dev/null
+++ b/gl/stdio-consolesafe.c
@@ -0,0 +1,202 @@
1/* msvcrt workarounds.
2 Copyright (C) 2025-2026 Free Software Foundation, Inc.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17#include <config.h>
18
19/* Specification. */
20#include <stdio.h>
21
22#include <stdckdint.h>
23#include <stdlib.h>
24#include <string.h>
25
26/* Outputs N bytes starting at S to FP.
27 These N bytes are known to be followed by a NUL.
28 Finally frees the string at S.
29 Returns the number of written bytes. */
30static size_t
31workaround_fwrite0 (char *s, size_t n, FILE *fp)
32{
33 const char *ptr = s;
34 /* Use fputs instead of fwrite, which is buggy in msvcrt. */
35 size_t written = 0;
36 while (n > 0)
37 {
38 size_t l = strlen (ptr); /* 0 <= l <= n */
39 if (l > 0)
40 {
41 if (fputs (ptr, fp) == EOF)
42 break;
43 written += l;
44 n -= l;
45 }
46 if (n == 0)
47 break;
48 if (fputc ('\0', fp) == EOF)
49 break;
50 written++;
51 n--;
52 ptr += l + 1;
53 }
54 free (s);
55 return written;
56}
57
58size_t
59gl_consolesafe_fwrite (const void *ptr, size_t size, size_t nmemb, FILE *fp)
60{
61 size_t nbytes;
62 if (ckd_mul (&nbytes, size, nmemb) || nbytes == 0)
63 /* Overflow, or nothing to do. */
64 return 0;
65 char *tmp = malloc (nbytes + 1);
66 if (tmp == NULL)
67 return 0;
68 memcpy (tmp, ptr, nbytes);
69 tmp[nbytes] = '\0';
70 size_t written = workaround_fwrite0 (tmp, nbytes, fp);
71 return written / size;
72}
73
74#if defined __MINGW32__ && __USE_MINGW_ANSI_STDIO
75
76# include "fseterr.h"
77# include <stdarg.h>
78
79# if !HAVE_VASPRINTF
80
81# include <errno.h>
82
83/* The old mingw (before mingw-w64) does not have the vasprintf function.
84 Define a suitable replacement here, that supports the same format
85 specifiers as the mingw *printf functions. */
86
87static int
88local_vasprintf (char **resultp, const char *format, va_list args)
89{
90 /* First try: Use a stack-allocated buffer. */
91 char buf[2048];
92 size_t bufsize = sizeof (buf);
93 int ret = __mingw_vsnprintf (buf, bufsize, format, args);
94 if (ret < 0)
95 return -1;
96 size_t nbytes = ret;
97 char *mem = (char *) malloc (nbytes + 1);
98 if (mem == NULL)
99 {
100 errno = ENOMEM;
101 return -1;
102 }
103 if (ret < bufsize)
104 {
105 /* The buffer was sufficiently large. */
106 memcpy (mem, buf, nbytes + 1);
107 }
108 else
109 {
110 /* Second try: Use the heap-allocated memory. */
111 ret = __mingw_vsnprintf (mem, nbytes + 1, format, args);
112 if (ret < 0)
113 {
114 int saved_errno = errno;
115 free (mem);
116 errno = saved_errno;
117 return -1;
118 }
119 if (ret != nbytes)
120 abort ();
121 }
122 *resultp = mem;
123 return nbytes;
124}
125
126# undef vasprintf
127# define vasprintf local_vasprintf
128
129# endif
130
131/* Bypass the functions __mingw_[v][f]printf, that trigger a bug in msvcrt,
132 but without losing the support for modern format specifiers added by
133 __mingw_*printf. */
134
135int
136gl_consolesafe_fprintf (FILE *restrict fp, const char *restrict format, ...)
137{
138 va_list args;
139 va_start (args, format);
140 char *tmpstring;
141 int result = vasprintf (&tmpstring, format, args);
142 va_end (args);
143 if (result >= 0)
144 {
145 if (workaround_fwrite0 (tmpstring, result, fp) < result)
146 result = -1;
147 }
148 else
149 fseterr (fp);
150 return result;
151}
152
153int
154gl_consolesafe_printf (const char *restrict format, ...)
155{
156 va_list args;
157 va_start (args, format);
158 char *tmpstring;
159 int result = vasprintf (&tmpstring, format, args);
160 va_end (args);
161 if (result >= 0)
162 {
163 if (workaround_fwrite0 (tmpstring, result, stdout) < result)
164 result = -1;
165 }
166 else
167 fseterr (stdout);
168 return result;
169}
170
171int
172gl_consolesafe_vfprintf (FILE *restrict fp,
173 const char *restrict format, va_list args)
174{
175 char *tmpstring;
176 int result = vasprintf (&tmpstring, format, args);
177 if (result >= 0)
178 {
179 if (workaround_fwrite0 (tmpstring, result, fp) < result)
180 result = -1;
181 }
182 else
183 fseterr (fp);
184 return result;
185}
186
187int
188gl_consolesafe_vprintf (const char *restrict format, va_list args)
189{
190 char *tmpstring;
191 int result = vasprintf (&tmpstring, format, args);
192 if (result >= 0)
193 {
194 if (workaround_fwrite0 (tmpstring, result, stdout) < result)
195 result = -1;
196 }
197 else
198 fseterr (stdout);
199 return result;
200}
201
202#endif