| /* 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 |