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.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/gl/stdio-consolesafe.c b/gl/stdio-consolesafe.c
new file mode 100644
index 00000000..80561a6d
--- /dev/null
+++ b/gl/stdio-consolesafe.c
@@ -0,0 +1,199 @@
1/* msvcrt workarounds.
2 Copyright (C) 2025 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
88vasprintf (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# endif
127
128/* Bypass the functions __mingw_[v][f]printf, that trigger a bug in msvcrt,
129 but without losing the support for modern format specifiers added by
130 __mingw_*printf. */
131
132int
133gl_consolesafe_fprintf (FILE *restrict fp, const char *restrict format, ...)
134{
135 va_list args;
136 char *tmpstring;
137 va_start (args, format);
138 int result = vasprintf (&tmpstring, format, args);
139 va_end (args);
140 if (result >= 0)
141 {
142 if (workaround_fwrite0 (tmpstring, result, fp) < result)
143 result = -1;
144 }
145 else
146 fseterr (fp);
147 return result;
148}
149
150int
151gl_consolesafe_printf (const char *restrict format, ...)
152{
153 va_list args;
154 char *tmpstring;
155 va_start (args, format);
156 int result = vasprintf (&tmpstring, format, args);
157 va_end (args);
158 if (result >= 0)
159 {
160 if (workaround_fwrite0 (tmpstring, result, stdout) < result)
161 result = -1;
162 }
163 else
164 fseterr (stdout);
165 return result;
166}
167
168int
169gl_consolesafe_vfprintf (FILE *restrict fp,
170 const char *restrict format, va_list args)
171{
172 char *tmpstring;
173 int result = vasprintf (&tmpstring, format, args);
174 if (result >= 0)
175 {
176 if (workaround_fwrite0 (tmpstring, result, fp) < result)
177 result = -1;
178 }
179 else
180 fseterr (fp);
181 return result;
182}
183
184int
185gl_consolesafe_vprintf (const char *restrict format, va_list args)
186{
187 char *tmpstring;
188 int result = vasprintf (&tmpstring, format, args);
189 if (result >= 0)
190 {
191 if (workaround_fwrite0 (tmpstring, result, stdout) < result)
192 result = -1;
193 }
194 else
195 fseterr (stdout);
196 return result;
197}
198
199#endif