/* msvcrt workarounds. Copyright (C) 2025 Free Software Foundation, Inc. This file is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include /* Specification. */ #include #include #include #include /* Outputs N bytes starting at S to FP. These N bytes are known to be followed by a NUL. Finally frees the string at S. Returns the number of written bytes. */ static size_t workaround_fwrite0 (char *s, size_t n, FILE *fp) { const char *ptr = s; /* Use fputs instead of fwrite, which is buggy in msvcrt. */ size_t written = 0; while (n > 0) { size_t l = strlen (ptr); /* 0 <= l <= n */ if (l > 0) { if (fputs (ptr, fp) == EOF) break; written += l; n -= l; } if (n == 0) break; if (fputc ('\0', fp) == EOF) break; written++; n--; ptr += l + 1; } free (s); return written; } size_t gl_consolesafe_fwrite (const void *ptr, size_t size, size_t nmemb, FILE *fp) { size_t nbytes; if (ckd_mul (&nbytes, size, nmemb) || nbytes == 0) /* Overflow, or nothing to do. */ return 0; char *tmp = malloc (nbytes + 1); if (tmp == NULL) return 0; memcpy (tmp, ptr, nbytes); tmp[nbytes] = '\0'; size_t written = workaround_fwrite0 (tmp, nbytes, fp); return written / size; } #if defined __MINGW32__ && __USE_MINGW_ANSI_STDIO # include "fseterr.h" /* Bypass the functions __mingw_[v][f]printf, that trigger a bug in msvcrt, but without losing the support for modern format specifiers added by __mingw_*printf. */ int gl_consolesafe_fprintf (FILE *restrict fp, const char *restrict format, ...) { va_list args; char *tmpstring; va_start (args, format); int result = vasprintf (&tmpstring, format, args); va_end (args); if (result >= 0) { if (workaround_fwrite0 (tmpstring, result, fp) < result) result = -1; } else fseterr (fp); return result; } int gl_consolesafe_printf (const char *restrict format, ...) { va_list args; char *tmpstring; va_start (args, format); int result = vasprintf (&tmpstring, format, args); va_end (args); if (result >= 0) { if (workaround_fwrite0 (tmpstring, result, stdout) < result) result = -1; } else fseterr (stdout); return result; } int gl_consolesafe_vfprintf (FILE *restrict fp, const char *restrict format, va_list args) { char *tmpstring; int result = vasprintf (&tmpstring, format, args); if (result >= 0) { if (workaround_fwrite0 (tmpstring, result, fp) < result) result = -1; } else fseterr (fp); return result; } int gl_consolesafe_vprintf (const char *restrict format, va_list args) { char *tmpstring; int result = vasprintf (&tmpstring, format, args); if (result >= 0) { if (workaround_fwrite0 (tmpstring, result, stdout) < result) result = -1; } else fseterr (stdout); return result; } #endif