>From b88da6aca8d12d0aee9703e54eee4f8a97c4d297 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 6 Jul 2019 17:25:24 -0700 Subject: [PATCH] Avoid interleaving stderr in a few cases * src/sysdep.c (buferr): New static var. (init_standard_fds) [!DOS_NT]: Initialize it. (errstream, errputc, verrprintf, errwrite): New functions. (close_output_streams): Close buferr too. * src/xdisp.c: Include sysstdio.h instead of stdio.h. (message_to_stderr, vmessage): Use the new functions to avoid interleaving stderr. --- src/sysdep.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++--- src/sysstdio.h | 3 +++ src/xdisp.c | 35 +++++++++-------------------- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/sysdep.c b/src/sysdep.c index 48eebb594f..ee17e08482 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -232,6 +232,10 @@ force_open (int fd, int flags) } } +/* A stream that is like stderr, except line buffered when possible. + It is NULL during startup or if initializing it failed. */ +static FILE *buferr; + /* Make sure stdin, stdout, and stderr are open to something, so that their file descriptors are not hijacked by later system calls. */ void @@ -244,6 +248,15 @@ init_standard_fds (void) force_open (STDIN_FILENO, O_WRONLY); force_open (STDOUT_FILENO, O_RDONLY); force_open (STDERR_FILENO, O_RDONLY); + +#ifndef DOS_NT /* _IOLBF does not work on MS-Windows. */ + buferr = fdopen (STDERR_FILENO, "w"); + if (buferr && setvbuf (buferr, NULL, _IOLBF, 0) != 0) + { + fclose (buferr); + buferr = NULL; + } +#endif } /* Return the current working directory. The result should be freed @@ -2769,6 +2782,46 @@ safe_strsignal (int code) return signame; } +/* Output to stderr. */ + +/* Return the error output stream. */ +static FILE * +errstream (void) +{ + FILE *err = buferr; + if (!err) + return stderr; + fflush_unlocked (stderr); + return err; +} + +/* These functions are like fputc, vfprintf, and fwrite, + except that they output to stderr and buffer better on + platforms that support line buffering. This avoids interleaving + output when Emacs and other processes write to stderr + simultaneously, so long as the lines are short enough. When a + single diagnostic is emitted via a sequence of calls of one or more + of these functions, the caller should arrange for the last called + function to output a newline at the end. */ + +void +errputc (int c) +{ + fputc_unlocked (c, errstream ()); +} + +void +verrprintf (char const *fmt, va_list ap) +{ + vfprintf (errstream (), fmt, ap); +} + +void +errwrite (void const *buf, ptrdiff_t nbuf) +{ + fwrite_unlocked (buf, 1, nbuf, errstream ()); +} + /* Close standard output and standard error, reporting any write errors as best we can. This is intended for use with atexit. */ void @@ -2782,9 +2835,10 @@ close_output_streams (void) /* Do not close stderr if addresses are being sanitized, as the sanitizer might report to stderr after this function is invoked. */ - if (ADDRESS_SANITIZER - ? fflush_unlocked (stderr) != 0 || ferror (stderr) - : close_stream (stderr) != 0) + bool err = buferr && (fflush_unlocked (buferr) != 0 || ferror (buferr)); + if (err | (ADDRESS_SANITIZER + ? fflush_unlocked (stderr) != 0 || ferror (stderr) + : close_stream (stderr) != 0)) _exit (EXIT_FAILURE); } diff --git a/src/sysstdio.h b/src/sysstdio.h index a2364c4e1f..4dc9287e43 100644 --- a/src/sysstdio.h +++ b/src/sysstdio.h @@ -24,6 +24,9 @@ #define EMACS_SYSSTDIO_H #include extern FILE *emacs_fopen (char const *, char const *); +extern void errputc (int); +extern void verrprintf (char const *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0); +extern void errwrite (void const *, ptrdiff_t); extern void close_output_streams (void); #if O_BINARY diff --git a/src/xdisp.c b/src/xdisp.c index 5fb690e746..85cac3cd29 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -302,7 +302,6 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2019 Free Software Foundation, buffer_posn_from_coords in dispnew.c for how this is handled. */ #include -#include #include #include #include @@ -311,6 +310,7 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2019 Free Software Foundation, #include "atimer.h" #include "composite.h" #include "keyboard.h" +#include "sysstdio.h" #include "systime.h" #include "frame.h" #include "window.h" @@ -10713,7 +10713,7 @@ message_to_stderr (Lisp_Object m) if (noninteractive_need_newline) { noninteractive_need_newline = false; - fputc ('\n', stderr); + errputc ('\n'); } if (STRINGP (m)) { @@ -10727,23 +10727,10 @@ message_to_stderr (Lisp_Object m) else s = m; - /* We want to write this out with a single fwrite call so that - output doesn't interleave with other processes writing to - stderr at the same time. */ - { - int length = min (INT_MAX, SBYTES (s) + 1); - char *string = xmalloc (length); - - memcpy (string, SSDATA (s), length - 1); - string[length - 1] = '\n'; - fwrite (string, 1, length, stderr); - xfree (string); - } + errwrite (SDATA (s), SBYTES (s)); } - else if (!cursor_in_echo_area) - fputc ('\n', stderr); - - fflush (stderr); + if (STRINGP (m) || !cursor_in_echo_area) + errputc ('\n'); } /* The non-logging version of message3. @@ -10888,12 +10875,12 @@ vmessage (const char *m, va_list ap) if (m) { if (noninteractive_need_newline) - fputc ('\n', stderr); - noninteractive_need_newline = false; - vfprintf (stderr, m, ap); - if (!cursor_in_echo_area) - fputc ('\n', stderr); - fflush (stderr); + { + noninteractive_need_newline = false; + errputc ('\n'); + } + verrprintf (m, ap); + errputc ('\n'); } } else if (INTERACTIVE) -- 2.21.0