| /* POSIX compatible FILE stream write function. | |
| Copyright (C) 2008-2022 Free Software Foundation, Inc. | |
| Written by Bruno Haible <bruno@clisp.org>, 2008. | |
| 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 <https://www.gnu.org/licenses/>. */ | |
| #include <config.h> | |
| /* Specification. */ | |
| #include <stdio.h> | |
| /* Replace these functions only if module 'nonblocking' or module 'sigpipe' is | |
| requested. */ | |
| #if GNULIB_NONBLOCKING || GNULIB_SIGPIPE | |
| /* On native Windows platforms, SIGPIPE does not exist. When write() is | |
| called on a pipe with no readers, WriteFile() fails with error | |
| GetLastError() = ERROR_NO_DATA, and write() in consequence fails with | |
| error EINVAL. This write() function is at the basis of the function | |
| which flushes the buffer of a FILE stream. */ | |
| # if defined _WIN32 && ! defined __CYGWIN__ | |
| # include <errno.h> | |
| # include <signal.h> | |
| # include <io.h> | |
| # define WIN32_LEAN_AND_MEAN /* avoid including junk */ | |
| # include <windows.h> | |
| # if GNULIB_MSVC_NOTHROW | |
| # include "msvc-nothrow.h" | |
| # else | |
| # include <io.h> | |
| # endif | |
| /* Don't assume that UNICODE is not defined. */ | |
| # undef GetNamedPipeHandleState | |
| # define GetNamedPipeHandleState GetNamedPipeHandleStateA | |
| # if GNULIB_NONBLOCKING | |
| # define CLEAR_ERRNO \ | |
| errno = 0; | |
| # define HANDLE_ENOSPC \ | |
| if (errno == ENOSPC && ferror (stream)) \ | |
| { \ | |
| int fd = fileno (stream); \ | |
| if (fd >= 0) \ | |
| { \ | |
| HANDLE h = (HANDLE) _get_osfhandle (fd); \ | |
| if (GetFileType (h) == FILE_TYPE_PIPE) \ | |
| { \ | |
| /* h is a pipe or socket. */ \ | |
| DWORD state; \ | |
| if (GetNamedPipeHandleState (h, &state, NULL, NULL, \ | |
| NULL, NULL, 0) \ | |
| && (state & PIPE_NOWAIT) != 0) \ | |
| /* h is a pipe in non-blocking mode. \ | |
| Change errno from ENOSPC to EAGAIN. */ \ | |
| errno = EAGAIN; \ | |
| } \ | |
| } \ | |
| } \ | |
| else | |
| # else | |
| # define CLEAR_ERRNO | |
| # define HANDLE_ENOSPC | |
| # endif | |
| # if GNULIB_SIGPIPE | |
| # define CLEAR_LastError \ | |
| SetLastError (0); | |
| # define HANDLE_ERROR_NO_DATA \ | |
| if (GetLastError () == ERROR_NO_DATA && ferror (stream)) \ | |
| { \ | |
| int fd = fileno (stream); \ | |
| if (fd >= 0 \ | |
| && GetFileType ((HANDLE) _get_osfhandle (fd)) \ | |
| == FILE_TYPE_PIPE) \ | |
| { \ | |
| /* Try to raise signal SIGPIPE. */ \ | |
| raise (SIGPIPE); \ | |
| /* If it is currently blocked or ignored, change errno from \ | |
| EINVAL to EPIPE. */ \ | |
| errno = EPIPE; \ | |
| } \ | |
| } \ | |
| else | |
| # else | |
| # define CLEAR_LastError | |
| # define HANDLE_ERROR_NO_DATA | |
| # endif | |
| # define CALL_WITH_SIGPIPE_EMULATION(RETTYPE, EXPRESSION, FAILED) \ | |
| if (ferror (stream)) \ | |
| return (EXPRESSION); \ | |
| else \ | |
| { \ | |
| RETTYPE ret; \ | |
| CLEAR_ERRNO \ | |
| CLEAR_LastError \ | |
| ret = (EXPRESSION); \ | |
| if (FAILED) \ | |
| { \ | |
| HANDLE_ENOSPC \ | |
| HANDLE_ERROR_NO_DATA \ | |
| ; \ | |
| } \ | |
| return ret; \ | |
| } | |
| # if !REPLACE_PRINTF_POSIX /* avoid collision with printf.c */ | |
| int | |
| printf (const char *format, ...) | |
| { | |
| int retval; | |
| va_list args; | |
| va_start (args, format); | |
| retval = vfprintf (stdout, format, args); | |
| va_end (args); | |
| return retval; | |
| } | |
| # endif | |
| # if !REPLACE_FPRINTF_POSIX /* avoid collision with fprintf.c */ | |
| int | |
| fprintf (FILE *stream, const char *format, ...) | |
| { | |
| int retval; | |
| va_list args; | |
| va_start (args, format); | |
| retval = vfprintf (stream, format, args); | |
| va_end (args); | |
| return retval; | |
| } | |
| # endif | |
| # if !REPLACE_VPRINTF_POSIX /* avoid collision with vprintf.c */ | |
| int | |
| vprintf (const char *format, va_list args) | |
| { | |
| return vfprintf (stdout, format, args); | |
| } | |
| # endif | |
| # if !REPLACE_VFPRINTF_POSIX /* avoid collision with vfprintf.c */ | |
| int | |
| vfprintf (FILE *stream, const char *format, va_list args) | |
| #undef vfprintf | |
| { | |
| CALL_WITH_SIGPIPE_EMULATION (int, vfprintf (stream, format, args), ret == EOF) | |
| } | |
| # endif | |
| int | |
| putchar (int c) | |
| { | |
| return fputc (c, stdout); | |
| } | |
| int | |
| fputc (int c, FILE *stream) | |
| #undef fputc | |
| { | |
| CALL_WITH_SIGPIPE_EMULATION (int, fputc (c, stream), ret == EOF) | |
| } | |
| int | |
| fputs (const char *string, FILE *stream) | |
| #undef fputs | |
| { | |
| CALL_WITH_SIGPIPE_EMULATION (int, fputs (string, stream), ret == EOF) | |
| } | |
| int | |
| puts (const char *string) | |
| #undef puts | |
| { | |
| FILE *stream = stdout; | |
| CALL_WITH_SIGPIPE_EMULATION (int, puts (string), ret == EOF) | |
| } | |
| size_t | |
| fwrite (const void *ptr, size_t s, size_t n, FILE *stream) | |
| #undef fwrite | |
| { | |
| CALL_WITH_SIGPIPE_EMULATION (size_t, fwrite (ptr, s, n, stream), ret < n) | |
| } | |
| # endif | |
| #endif |