unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Paul Eggert <eggert@cs.ucla.edu>
To: Eli Zaretskii <eliz@gnu.org>, Lars Ingebrigtsen <larsi@gnus.org>
Cc: daniele@grinta.net, emacs-devel@gnu.org
Subject: Re: `message' not outputting the newline "atomically"
Date: Wed, 3 Jul 2019 00:31:18 -0700	[thread overview]
Message-ID: <7a39d680-6234-1301-74e5-62d599f500f6@cs.ucla.edu> (raw)
In-Reply-To: <83zhm3i285.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 1384 bytes --]

Eli Zaretskii wrote:
> I'm okay with making 'message' write in one go to stderr, but I don't
> want to pay the price of having stderr buffered globally.

I came up with a fix that does all that. Although the fix does not alter the 
buffering of the stderr stream, it causes 'message' and similar functions to 
write in one go to avoid interleaving output. It also fixes the problem on AIX 
and Solaris where a single call to fprintf is implemented by multiple calls to 
'write' even if the diagnostic is short, botching interleaving. It also fixes 
the INT_MAX overflow and memory-allocation issues of the current master's 
implementation of 'message'.

Proposed patches attached. The first one merely refactors and simplifies the 
C-level code, without changing behavior; this simplifies later patches. The 
second patch fixes buffering of most C-level diagnostics in Emacs, so that they 
interleave well with other processes' diagnostics. The third patch similarly 
fixes buffering of most Lisp-level diagnostics in Emacs. The last patch is one 
more simplification of the C code.

I still far prefer the three-line patch I submitted earlier, as it was much 
simpler and the objections to it were theoretical (i.e., not based on actual 
Emacs code). However, the attached patches work nearly as well, and they do so 
while obeying the constraints you imposed on fixing the problem.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-New-function-errprintf-for-printf-to-stderr.patch --]
[-- Type: text/x-patch; name="0001-New-function-errprintf-for-printf-to-stderr.patch", Size: 79840 bytes --]

From 3739f4b6821435c9feb74b098e95f668d604c768 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 1 Jul 2019 09:46:11 -0700
Subject: [PATCH 1/4] New function errprintf for printf to stderr
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This doesn’t change behavior; it just simplifies callers a bit
and makes further changes easier.
* src/alloc.c (mark_memory, test_setjmp, die):
* src/bidi.c (bidi_dump_cached_states):
* src/buffer.c (mmap_free_1, mmap_enlarge, mmap_alloc)
(init_buffer):
* src/charset.c (init_charset):
* src/dispextern.h (TRACE):
* src/dispnew.c (Fdump_redisplay_history)
(init_display_interactive):
* src/emacs-module.c (module_abort):
* src/emacs.c (main, sort_args, Fdump_emacs):
* src/gmalloc.c (mabort):
* src/gtkutil.c (my_log_handler, xg_set_geometry)
(xg_create_widget):
* src/image.c (convert_mono_to_color_image):
* src/lread.c (dir_warning):
* src/nsfont.m (ns_descriptor_to_entity, ns_findfonts)
(nsfont_list_family, nsfont_open, ns_uni_to_glyphs)
(ns_glyph_metrics, ns_dump_glyphstring):
* src/nsmenu.m (ns_update_menubar):
* src/nsterm.h (NSTRACE_MSG_NO_DASHES):
* src/nsterm.m (ns_mouse_position, ns_default, ns_term_init)
(sendEvent:, keyDown:, performDragOperation:, mouseDown:):
* src/pdumper.c (dump_trace, dump_fingerprint)
(print_paths_to_root_1, Fdump_emacs_portable):
* src/print.c (debug_print, safe_debug_print):
* src/regex-emacs.c (DEBUG_PRINT, debug_putchar)
(print_fastmap, print_partial_compiled_pattern)
(print_compiled_pattern, print_double_string, regex_compile):
* src/region-cache.c (pp_cache):
* src/systhread.c (sys_mutex_init, sys_cond_init):
* src/term.c (vfatal):
* src/unexaix.c (CHECK_SCNHDR):
* src/unexelf.c (DEBUG_LOG, unexec):
* src/unexhp9k800.c (read_header):
* src/unexmacosx.c (unexec_error):
* src/widget.c (EmacsFrameInitialize):
* src/xdisp.c (TRACE_MOVE, pos_visible_p, message_to_stderr)
(vmessage, debug_method_add, dump_glyph, dump_glyph_row)
(Fdump_glyph_matrix, dump_glyph_string, expose_window)
(expose_frame):
* src/xfaces.c (Fdump_colors, dump_realized_face)
(Fdump_face, Fshow_face_resources):
* src/xfns.c (print_fontset_result):
* src/xmenu.c (x_menu_show) [XDEBUG]:
* src/xrdb.c (fatal):
* src/xselect.c (TRACE0, TRACE1, TRACE2, TRACE3)
(x_clipboard_manager_error_2):
* src/xsmfns.c (x_session_initialize):
* src/xterm.c (x_trace_wire, x_connection_closed)
(my_log_handler, x_initialize):
Prefer the new functions to using fprintf etc. to stderr.
All files changed to include sysstdio.h instead if stdio.h
if they weren’t doing so already.
* src/dispextern.h (TRACE):
* src/xdisp.c (TRACE_MOVE):
All callers changed to omit stderr, since macro now arranges
for stderr itself.
* src/sysdep.c (errputc, errputs, errprintf, verrprintf):
New functions.
---
 src/alloc.c        |  12 +-
 src/bidi.c         |  24 ++--
 src/buffer.c       |  16 +--
 src/charset.c      |   2 +-
 src/dispnew.c      |  13 +-
 src/emacs-module.c |   8 +-
 src/emacs.c        |  57 ++++----
 src/gmalloc.c      |   2 +-
 src/gtkutil.c      |   9 +-
 src/image.c        |   2 +-
 src/lread.c        |   2 +-
 src/nsfont.m       |  41 +++---
 src/nsmenu.m       |  12 +-
 src/nsterm.h       |  11 +-
 src/nsterm.m       |  27 ++--
 src/pdumper.c      |  34 +++--
 src/print.c        |   8 +-
 src/regex-emacs.c  | 134 +++++++++----------
 src/region-cache.c |  15 +--
 src/sysdep.c       |  29 ++++
 src/sysstdio.h     |   5 +
 src/systhread.c    |   7 +-
 src/term.c         |   6 +-
 src/unexaix.c      |   7 +-
 src/unexelf.c      |   8 +-
 src/unexhp9k800.c  |   2 +-
 src/unexmacosx.c   |   6 +-
 src/widget.c       |   3 +-
 src/xdisp.c        | 324 ++++++++++++++++++++++-----------------------
 src/xfaces.c       |  64 ++++-----
 src/xfns.c         |   6 +-
 src/xmenu.c        |   2 +-
 src/xrdb.c         |   2 +-
 src/xselect.c      |  12 +-
 src/xsmfns.c       |   6 +-
 src/xterm.c        |  15 +--
 36 files changed, 473 insertions(+), 460 deletions(-)

diff --git a/src/alloc.c b/src/alloc.c
index 64aaa8acdf..9610f01c05 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -22,7 +22,6 @@ Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2019 Free Software
 
 #include <errno.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>		/* For CHAR_BIT.  */
 #include <signal.h>		/* For SIGABRT, SIGDANGER.  */
@@ -38,6 +37,7 @@ Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2019 Free Software
 #include "ptr-bounds.h"
 #include "puresize.h"
 #include "sheap.h"
+#include "sysstdio.h"
 #include "systime.h"
 #include "character.h"
 #include "buffer.h"
@@ -4717,7 +4717,7 @@ mark_memory (void const *start, void const *end)
        Lisp_Object obj = build_string ("test");
        struct Lisp_String *s = XSTRING (obj);
        garbage_collect ();
-       fprintf (stderr, "test '%s'\n", s->u.s.data);
+       errprintf ("test '%s'\n", s->u.s.data);
        return Qnil;
      }
 
@@ -4813,10 +4813,10 @@ test_setjmp (void)
          taking place, or the setjmp just didn't save the register.  */
 
       if (x == 1)
-	fprintf (stderr, SETJMP_WILL_LIKELY_WORK);
+	errputs (SETJMP_WILL_LIKELY_WORK);
       else
 	{
-	  fprintf (stderr, SETJMP_WILL_NOT_WORK);
+	  errputs (SETJMP_WILL_NOT_WORK);
 	  exit (1);
 	}
     }
@@ -7189,8 +7189,8 @@ DEFUN ("suspicious-object", Fsuspicious_object, Ssuspicious_object, 1, 1, 0,
 void
 die (const char *msg, const char *file, int line)
 {
-  fprintf (stderr, "\r\n%s:%d: Emacs fatal error: assertion failed: %s\r\n",
-	   file, line, msg);
+  errprintf ("\r\n%s:%d: Emacs fatal error: assertion failed: %s\r\n",
+	     file, line, msg);
   terminate_due_to_signal (SIGABRT, INT_MAX);
 }
 
diff --git a/src/bidi.c b/src/bidi.c
index c530d49c10..991894c4e0 100644
--- a/src/bidi.c
+++ b/src/bidi.c
@@ -238,13 +238,13 @@ Copyright (C) 2000-2001, 2004-2005, 2009-2019 Free Software Foundation, Inc.
    necessary.  */
 
 #include <config.h>
-#include <stdio.h>
 
 #include "lisp.h"
 #include "character.h"
 #include "buffer.h"
 #include "dispextern.h"
 #include "region-cache.h"
+#include "sysstdio.h"
 
 static bool bidi_initialized = 0;
 
@@ -3586,24 +3586,22 @@ bidi_dump_cached_states (void)
 
   if (bidi_cache_idx == 0)
     {
-      fprintf (stderr, "The cache is empty.\n");
+      errputs ("The cache is empty.\n");
       return;
     }
-  fprintf (stderr, "Total of  %"pD"d state%s in cache:\n",
-	   bidi_cache_idx, bidi_cache_idx == 1 ? "" : "s");
+  errprintf ("Total of  %"pD"d state%s in cache:\n",
+	     bidi_cache_idx, bidi_cache_idx == 1 ? "" : "s");
 
   for (i = bidi_cache[bidi_cache_idx - 1].charpos; i > 0; i /= 10)
     ndigits++;
-  fputs ("ch  ", stderr);
+  errputs ("ch  ");
   for (i = 0; i < bidi_cache_idx; i++)
-    fprintf (stderr, "%*c", ndigits, bidi_cache[i].ch);
-  fputs ("\n", stderr);
-  fputs ("lvl ", stderr);
+    errprintf ("%*c", ndigits, bidi_cache[i].ch);
+  errputs ("\nlvl ");
   for (i = 0; i < bidi_cache_idx; i++)
-    fprintf (stderr, "%*d", ndigits, bidi_cache[i].resolved_level);
-  fputs ("\n", stderr);
-  fputs ("pos ", stderr);
+    errprintf ("%*d", ndigits, bidi_cache[i].resolved_level);
+  errputs ("\npos ");
   for (i = 0; i < bidi_cache_idx; i++)
-    fprintf (stderr, "%*"pD"d", ndigits, bidi_cache[i].charpos);
-  fputs ("\n", stderr);
+    errprintf ("%*"pD"d", ndigits, bidi_cache[i].charpos);
+  errputc ('\n');
 }
diff --git a/src/buffer.c b/src/buffer.c
index 209e29f0f1..14477d4ae1 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -24,7 +24,6 @@ Copyright (C) 1985-1989, 1993-1995, 1997-2019 Free Software Foundation,
 #include <sys/stat.h>
 #include <sys/param.h>
 #include <errno.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -33,6 +32,7 @@ Copyright (C) 1985-1989, 1993-1995, 1997-2019 Free Software Foundation,
 #include "lisp.h"
 #include "intervals.h"
 #include "process.h"
+#include "sysstdio.h"
 #include "systime.h"
 #include "window.h"
 #include "commands.h"
@@ -4781,7 +4781,7 @@ mmap_free_1 (struct mmap_region *r)
     mmap_regions = r->next;
 
   if (munmap (r, r->nbytes_mapped) == -1)
-    fprintf (stderr, "munmap: %s\n", emacs_strerror (errno));
+    errprintf ("munmap: %s\n", emacs_strerror (errno));
 }
 
 
@@ -4800,7 +4800,7 @@ mmap_enlarge (struct mmap_region *r, int npages)
       /* Unmap pages at the end of the region.  */
       nbytes = - npages * mmap_page_size;
       if (munmap (region_end - nbytes, nbytes) == -1)
-	fprintf (stderr, "munmap: %s\n", emacs_strerror (errno));
+	errprintf ("munmap: %s\n", emacs_strerror (errno));
       else
 	{
 	  r->nbytes_mapped -= nbytes;
@@ -4822,14 +4822,14 @@ mmap_enlarge (struct mmap_region *r, int npages)
 	  p = mmap (region_end, nbytes, PROT_READ | PROT_WRITE,
 		    MAP_ANON | MAP_PRIVATE | MAP_FIXED, mmap_fd, 0);
 	  if (p == MAP_FAILED)
-	    ; /* fprintf (stderr, "mmap: %s\n", emacs_strerror (errno)); */
+	    ; /* errprintf ("mmap: %s\n", emacs_strerror (errno)); */
 	  else if (p != region_end)
 	    {
 	      /* Kernels are free to choose a different address.  In
 		 that case, unmap what we've mapped above; we have
 		 no use for it.  */
 	      if (munmap (p, nbytes) == -1)
-		fprintf (stderr, "munmap: %s\n", emacs_strerror (errno));
+		errprintf ("munmap: %s\n", emacs_strerror (errno));
 	    }
 	  else
 	    {
@@ -4867,7 +4867,7 @@ mmap_alloc (void **var, size_t nbytes)
   if (p == MAP_FAILED)
     {
       if (errno != ENOMEM)
-	fprintf (stderr, "mmap: %s\n", emacs_strerror (errno));
+	errprintf ("mmap: %s\n", emacs_strerror (errno));
       p = NULL;
     }
   else
@@ -5378,8 +5378,8 @@ init_buffer (void)
 
   if (!pwd)
     {
-      fprintf (stderr, "Error getting directory: %s\n",
-               emacs_strerror (errno));
+      errprintf ("Error getting directory: %s\n",
+		 emacs_strerror (errno));
       bset_directory (current_buffer, Qnil);
     }
   else
diff --git a/src/charset.c b/src/charset.c
index c0700f972e..f2efc7946a 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -2293,7 +2293,7 @@ init_charset (void)
       /* This used to be non-fatal (dir_warning), but it should not
          happen, and if it does sooner or later it will cause some
          obscure problem (eg bug#6401), so better abort.  */
-      fprintf (stderr, "Error: charsets directory not found:\n\
+      errprintf ("Error: charsets directory not found:\n\
 %s\n\
 Emacs will not function correctly without the character map files.\n%s\
 Please check your installation!\n",
diff --git a/src/dispnew.c b/src/dispnew.c
index 52a7b6d6ee..28f0c49c0b 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -220,7 +220,7 @@ DEFUN ("dump-redisplay-history", Fdump_redisplay_history,
     {
       if (i < 0)
 	i = REDISPLAY_HISTORY_SIZE - 1;
-      fprintf (stderr, "%s\n", redisplay_history[i].trace);
+      errprintf ("%s\n", redisplay_history[i].trace);
     }
 
   return Qnil;
@@ -6153,8 +6153,7 @@ init_display_interactive (void)
 
       if (display_arg && !x_display_ok (display))
 	{
-	  fprintf (stderr, "Display %s unavailable, simulating -nw\n",
-		   display);
+	  errprintf ("Display %s unavailable, simulating -nw\n", display);
 	  inhibit_window_system = 1;
 	}
     }
@@ -6204,12 +6203,14 @@ init_display_interactive (void)
 #endif
   if (!terminal_type)
     {
+      char const *msg = ("Please set the environment variable "
+			 "TERM; see 'tset'.");
 #ifdef HAVE_WINDOW_SYSTEM
       if (! inhibit_window_system)
-	fprintf (stderr, "Please set the environment variable DISPLAY or TERM (see 'tset').\n");
-      else
+	msg = ("Please set the environment variable "
+	       "DISPLAY or TERM (see 'tset').");
 #endif /* HAVE_WINDOW_SYSTEM */
-	fprintf (stderr, "Please set the environment variable TERM; see 'tset'.\n");
+      errprintf ("%s\n", msg);
       exit (1);
     }
 
diff --git a/src/emacs-module.c b/src/emacs-module.c
index c856663d2f..7faad14adc 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -81,7 +81,6 @@ #define EMACS_MODULE_GMP
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
 
@@ -91,6 +90,7 @@ #define EMACS_MODULE_GMP
 #include "coding.h"
 #include "keyboard.h"
 #include "syssignal.h"
+#include "sysstdio.h"
 #include "thread.h"
 
 #include <intprops.h>
@@ -1299,12 +1299,12 @@ value_storage_contains_p (const struct emacs_value_storage *storage,
 static AVOID ATTRIBUTE_FORMAT_PRINTF (1, 2)
 module_abort (const char *format, ...)
 {
-  fputs ("Emacs module assertion: ", stderr);
+  errputs ("Emacs module assertion: ");
   va_list args;
   va_start (args, format);
-  vfprintf (stderr, format, args);
+  verrprintf (format, args);
   va_end (args);
-  fputc ('\n', stderr);
+  errputc ('\n');
   fflush (NULL);
   emacs_abort ();
 }
diff --git a/src/emacs.c b/src/emacs.c
index 32bb57e272..4fd45497fc 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1145,12 +1145,12 @@ main (int argc, char **argv)
 	  tem2 = Fsymbol_value (intern_c_string ("emacs-copyright"));
 	  if (!STRINGP (tem))
 	    {
-	      fprintf (stderr, "Invalid value of 'emacs-version'\n");
+	      errputs ("Invalid value of 'emacs-version'\n");
 	      exit (1);
 	    }
 	  if (!STRINGP (tem2))
 	    {
-	      fprintf (stderr, "Invalid value of 'emacs-copyright'\n");
+	      errputs ("Invalid value of 'emacs-copyright'\n");
 	      exit (1);
 	    }
 	  else
@@ -1192,8 +1192,8 @@ main (int argc, char **argv)
 #endif
       if (chdir (ch_to_dir) != 0)
         {
-          fprintf (stderr, "%s: Can't chdir to %s: %s\n",
-                   argv[0], ch_to_dir, strerror (errno));
+          errprintf ("%s: Can't chdir to %s: %s\n",
+		     argv[0], ch_to_dir, strerror (errno));
           exit (1);
         }
       original_pwd = emacs_wd;
@@ -1317,15 +1317,15 @@ main (int argc, char **argv)
 		  != STDOUT_FILENO))
 	    {
 	      char *errstring = strerror (errno);
-	      fprintf (stderr, "%s: %s: %s\n", argv[0], term, errstring);
+	      errprintf ("%s: %s: %s\n", argv[0], term, errstring);
 	      exit (EXIT_FAILURE);
 	    }
 	  if (! isatty (STDIN_FILENO))
 	    {
-	      fprintf (stderr, "%s: %s: not a tty\n", argv[0], term);
+	      errprintf ("%s: %s: not a tty\n", argv[0], term);
 	      exit (EXIT_FAILURE);
 	    }
-	  fprintf (stderr, "Using %s\n", term);
+	  errprintf ("Using %s\n", term);
 #ifdef HAVE_WINDOW_SYSTEM
 	  inhibit_window_system = true; /* -t => -nw */
 #endif
@@ -1422,7 +1422,7 @@ main (int argc, char **argv)
              before exiting.  */
           if (emacs_pipe (daemon_pipe) != 0)
             {
-              fprintf (stderr, "Cannot pipe!\n");
+              errputs ("Cannot pipe!\n");
               exit (1);
             }
         } /* daemon_type == 2 */
@@ -1432,8 +1432,7 @@ main (int argc, char **argv)
       int systemd_socket = sd_listen_fds (1);
 
       if (systemd_socket > 1)
-        fprintf (stderr,
-		 ("\n"
+	errputs (("\n"
 		  "Warning: systemd passed more than one socket to Emacs.\n"
 		  "Try 'Accept=false' in the Emacs socket unit file.\n"));
       else if (systemd_socket == 1
@@ -1443,7 +1442,7 @@ main (int argc, char **argv)
 #endif /* HAVE_LIBSYSTEMD */
 
 #ifdef USE_GTK
-      fprintf (stderr, "\nWarning: due to a long standing Gtk+ bug\nhttps://gitlab.gnome.org/GNOME/gtk/issues/221\n\
+      errputs ("\nWarning: due to a long standing Gtk+ bug\nhttps://gitlab.gnome.org/GNOME/gtk/issues/221\n\
 Emacs might crash when run in daemon mode and the X11 connection is unexpectedly lost.\n\
 Using an Emacs configured with --with-x-toolkit=lucid does not have this problem.\n");
 #endif /* USE_GTK */
@@ -1477,12 +1476,12 @@ main (int argc, char **argv)
 
               if (retval < 0)
                 {
-                  fprintf (stderr, "Error reading status from child\n");
+                  errputs ("Error reading status from child\n");
                   exit (1);
                 }
               else if (retval == 0)
                 {
-                  fprintf (stderr, "Error: server did not start correctly\n");
+                  errputs ("Error: server did not start correctly\n");
                   exit (1);
                 }
 
@@ -1508,7 +1507,7 @@ main (int argc, char **argv)
 
                 if (! (0 <= fdStrlen && fdStrlen < sizeof fdStr))
                   {
-                    fprintf (stderr, "daemon: child name too long\n");
+                    errputs ("daemon: child name too long\n");
                     exit (EXIT_CANNOT_INVOKE);
                   }
 
@@ -1525,7 +1524,7 @@ main (int argc, char **argv)
             if (!dname_arg || !*dname_arg || strnlen (dname_arg, 71) == 71
 		|| !strchr (dname_arg, '\n'))
           {
-            fprintf (stderr, "emacs daemon: daemon name absent or too long\n");
+            errputs ("emacs daemon: daemon name absent or too long\n");
             exit (EXIT_CANNOT_INVOKE);
           }
             dname_arg2[0] = '\0';
@@ -1546,12 +1545,12 @@ main (int argc, char **argv)
       w32_daemon_event = CreateEvent (NULL, TRUE, FALSE, W32_DAEMON_EVENT);
       if (w32_daemon_event == NULL)
         {
-          fprintf (stderr, "Couldn't create MS-Windows event for daemon: %s\n",
-		   w32_strerror (0));
+          errprintf ("Couldn't create MS-Windows event for daemon: %s\n",
+		     w32_strerror (0));
           exit (1);
         }
 #else /* MSDOS */
-      fprintf (stderr, "This platform does not support daemon mode.\n");
+      errputs ("This platform does not support daemon mode.\n");
       exit (1);
 #endif /* MSDOS */
       if (dname_arg)
@@ -1662,7 +1661,7 @@ main (int argc, char **argv)
                 NULL, &skip_args);
   if (will_dump_p () && module_assertions)
     {
-      fputs ("Module assertions are not supported during dumping\n", stderr);
+      errputs ("Module assertions are not supported during dumping\n");
       exit (1);
     }
   init_module_assertions (module_assertions);
@@ -2329,7 +2328,9 @@ sort_args (int argc, char **argv)
 		{
 		  /* This is an internal error.
 		     Eg if one long option is a prefix of another.  */
-		  fprintf (stderr, "Option '%s' matched multiple standard arguments\n", argv[from]);
+		  errprintf
+		    ("Option '%s' matched multiple standard arguments\n",
+		     argv[from]);
 		}
 	      /* Should we not also warn if there was no match?	 */
 	    }
@@ -2566,14 +2567,14 @@ DEFUN ("dump-emacs", Fdump_emacs, Sdump_emacs, 2, 2, 0,
 
   if (heap_bss_diff > MAX_HEAP_BSS_DIFF)
     {
-      fprintf (stderr, "**************************************************\n");
-      fprintf (stderr, "Warning: Your system has a gap between BSS and the\n");
-      fprintf (stderr, "heap (%"pMu" bytes).  This usually means that exec-shield\n",
-               heap_bss_diff);
-      fprintf (stderr, "or something similar is in effect.  The dump may\n");
-      fprintf (stderr, "fail because of this.  See the section about\n");
-      fprintf (stderr, "exec-shield in etc/PROBLEMS for more information.\n");
-      fprintf (stderr, "**************************************************\n");
+      errprintf (("**************************************************\n"
+		  "Warning: Your system has a gap between BSS and the\n"
+		  "heap (%"pMu" bytes).  This usually means that exec-shield\n"
+		  "or something similar is in effect.  The dump may\n"
+		  "fail because of this.  See the section about\n"
+		  "exec-shield in etc/PROBLEMS for more information.\n"
+		  "**************************************************\n"),
+		 heap_bss_diff);
     }
 # endif
 
diff --git a/src/gmalloc.c b/src/gmalloc.c
index bac3ffb7e5..57ebe12d3c 100644
--- a/src/gmalloc.c
+++ b/src/gmalloc.c
@@ -2012,7 +2012,7 @@ mabort (enum mcheck_status status)
 #ifdef __GNU_LIBRARY__
   __libc_fatal (msg);
 #else
-  fprintf (stderr, "mcheck: %s\n", msg);
+  errprintf ("mcheck: %s\n", msg);
   emacs_abort ();
 #endif
 }
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 1d15aec253..3c8447a0c4 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -21,13 +21,13 @@ Copyright (C) 2003-2019 Free Software Foundation, Inc.
 
 #ifdef USE_GTK
 #include <float.h>
-#include <stdio.h>
 
 #include <c-ctype.h>
 
 #include "lisp.h"
 #include "dispextern.h"
 #include "frame.h"
+#include "sysstdio.h"
 #include "systime.h"
 #include "xterm.h"
 #include "blockinput.h"
@@ -829,7 +829,7 @@ my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
 		const gchar *msg, gpointer user_data)
 {
   if (!strstr (msg, "visible children"))
-    fprintf (stderr, "XX %s-WARNING **: %s\n", log_domain, msg);
+    errprintf ("XX %s-WARNING **: %s\n", log_domain, msg);
 }
 #endif
 
@@ -894,7 +894,7 @@ xg_set_geometry (struct frame *f)
 
 	  if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 					  geom_str))
-	    fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
+	    errprintf ("Failed to parse: '%s'\n", geom_str);
 
 	  g_log_remove_handler ("Gtk", id);
 	}
@@ -2850,8 +2850,7 @@ xg_create_widget (const char *type, const char *name, struct frame *f,
     }
   else
     {
-      fprintf (stderr, "bad type in xg_create_widget: %s, doing nothing\n",
-               type);
+      errprintf ("bad type in xg_create_widget: %s, doing nothing\n", type);
     }
 
   return w;
diff --git a/src/image.c b/src/image.c
index d44a9d3dc2..864f73f8ef 100644
--- a/src/image.c
+++ b/src/image.c
@@ -3335,7 +3335,7 @@ convert_mono_to_color_image (struct frame *f, struct image *img,
   DeleteDC (new_img_dc);
   DeleteObject (img->pixmap);
   if (new_pixmap == 0)
-    fprintf (stderr, "Failed to convert image to color.\n");
+    errputs ("Failed to convert image to color.\n");
   else
     img->pixmap = new_pixmap;
 }
diff --git a/src/lread.c b/src/lread.c
index 5fa90cad3f..da8dd774dc 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -4721,7 +4721,7 @@ dir_warning (char const *use, Lisp_Object dirname)
 {
   static char const format[] = "Warning: %s '%s': %s\n";
   char *diagnostic = emacs_strerror (errno);
-  fprintf (stderr, format, use, SSDATA (ENCODE_SYSTEM (dirname)), diagnostic);
+  errprintf (format, use, SSDATA (ENCODE_SYSTEM (dirname)), diagnostic);
 
   /* Don't log the warning before we've initialized!!  */
   if (initialized)
diff --git a/src/nsfont.m b/src/nsfont.m
index eca97ab86c..0bda357e3a 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -212,7 +212,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 
     if (NSFONT_TRACE)
       {
-	fprintf (stderr, "created font_entity:\n    ");
+	errputs ("created font_entity:\n    ");
 	debug_print (font_entity);
       }
 
@@ -313,7 +313,7 @@ static void ns_glyph_metrics (struct nsfont_info *font_info,
 	    if (*bytes1 == 0)  // *bytes1 & *bytes2 != *bytes2
 		off++;
 	  }
-    // fprintf(stderr, "off = %d\ttot = %d\n", off,tot);
+    // errprintf ("off = %d\ttot = %d\n", off,tot);
     return (float)off / tot < 1.0F - pct;
 }
 
@@ -542,8 +542,8 @@ but also for ascii (which causes unnecessary font substitution).  */
     block_input ();
     if (NSFONT_TRACE)
       {
-	fprintf (stderr, "nsfont: %s for fontspec:\n    ",
-		 (isMatch ? "match" : "list"));
+	errprintf ("nsfont: %s for fontspec:\n    ",
+		   (isMatch ? "match" : "list"));
 	debug_print (font_spec);
       }
 
@@ -596,8 +596,8 @@ but also for ascii (which causes unnecessary font substitution).  */
       return ns_fallback_entity ();
 
     if (NSFONT_TRACE)
-	fprintf (stderr, "    Returning %"pD"d entities.\n",
-		 list_length (list));
+	errprintf ("    Returning %"pD"d entities.\n",
+		   list_length (list));
 
     return list;
 }
@@ -668,8 +668,8 @@ Properties to be considered are same as for list().  */
   /* FIXME: escape the name?  */
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "nsfont: list families returning %"pD"d entries\n",
-	     list_length (list));
+    errprintf ("nsfont: list families returning %"pD"d entries\n",
+	       list_length (list));
 
   unblock_input ();
   return list;
@@ -698,7 +698,7 @@ Properties to be considered are same as for list().  */
 
   if (NSFONT_TRACE)
     {
-      fprintf (stderr, "nsfont: open size %d of fontentity:\n    ", pixel_size);
+      errprintf ("nsfont: open size %d of fontentity:\n    ", pixel_size);
       debug_print (font_entity);
     }
 
@@ -1272,8 +1272,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
   unsigned short *glyphs;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tFinding glyphs for glyphs in block %d\n",
-            font_info, block);
+    errprintf ("%p\tFinding glyphs for glyphs in block %d\n",
+	       font_info, block);
 
   block_input ();
 
@@ -1348,8 +1348,8 @@ is false when (FROM > 0 || TO < S->nchars).  */
   struct font_metrics *metrics;
 
   if (NSFONT_TRACE)
-    fprintf (stderr, "%p\tComputing metrics for glyphs in block %d\n",
-            font_info, block);
+    errprintf ("%p\tComputing metrics for glyphs in block %d\n",
+	       font_info, block);
 
 #ifdef NS_IMPL_GNUSTEP
   /* not implemented yet (as of startup 0.18), so punt */
@@ -1472,16 +1472,13 @@ - (void)setIntAttribute: (NSInteger)attributeTag value: (NSInteger)val
 {
   int i;
 
-  fprintf (stderr, "Glyph string len = %d at (%d, %d) overhang (%d, %d),"
-"overlap = %d, bg_filled = %d:",
-           s->nchars, s->x, s->y, s->left_overhang, s->right_overhang,
-           s->row->overlapping_p, s->background_filled_p);
+  errprintf (("Glyph string len = %d at (%d, %d) overhang (%d, %d),"
+	      "overlap = %d, bg_filled = %d:"),
+	     s->nchars, s->x, s->y, s->left_overhang, s->right_overhang,
+	     s->row->overlapping_p, s->background_filled_p);
   for (i =0; i<s->nchars; i++)
-    {
-      int c = s->first_glyph[i].u.ch;
-      fprintf (stderr, "%c", c);
-    }
-  fprintf (stderr, "\n");
+    errputc (s->first_glyph[i].u.ch);
+  errputc ('\n');
 }
 
 static void syms_of_nsfont_for_pdumper (void);
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 817f8cff18..099f20bb0d 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -119,7 +119,7 @@
   if (f != SELECTED_FRAME ())
       return;
   XSETFRAME (Vmenu_updating_frame, f);
-/*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
+/*errprintf ("ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
 
   block_input ();
   pool = [[NSAutoreleasePool alloc] init];
@@ -236,8 +236,8 @@
       if (submenu && n == 0)
         {
           /* should have found a menu for this one but didn't */
-          fprintf (stderr, "ERROR: did not find lisp menu for submenu '%s'.\n",
-                  [[submenu title] UTF8String]);
+          errprintf ("ERROR: did not find lisp menu for submenu '%s'.\n",
+		     [[submenu title] UTF8String]);
 	  discard_menu_items ();
 	  unbind_to (specpdl_count, Qnil);
           [pool release];
@@ -292,7 +292,7 @@
 #if NSMENUPROFILE
               ftime (&tb);
               t += 1000*tb.time+tb.millitm;
-              fprintf (stderr, "NO CHANGE!  CUTTING OUT after %ld msec.\n", t);
+              errprintf ("NO CHANGE!  CUTTING OUT after %ld msec.\n", t);
 #endif
 
               free_menubar_widget_value_tree (first_wv);
@@ -447,7 +447,7 @@
 #if NSMENUPROFILE
   ftime (&tb);
   t += 1000*tb.time+tb.millitm;
-  fprintf (stderr, "Menu update took %ld msec.\n", t);
+  errprintf ("Menu update took %ld msec.\n", t);
 #endif
 
   /* set main menu */
@@ -581,7 +581,7 @@ - (void)menuNeedsUpdate: (NSMenu *)menu
   */
   if (trackingMenu == 0)
     return;
-/*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
+/*errprintf ("Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
 #ifdef NS_IMPL_GNUSTEP
   /* Don't know how to do this for anything other than Mac OS X 10.5 and later.
      This is wrong, as it might run Lisp code in the event loop.  */
diff --git a/src/nsterm.h b/src/nsterm.h
index 567f462ec6..7e15b9cd34 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -23,6 +23,7 @@
 #include "character.h"
 #include "font.h"
 #include "sysselect.h"
+#include "sysstdio.h"
 
 #ifdef HAVE_NS
 #ifdef __OBJC__
@@ -176,11 +177,11 @@ #define NSTRACE_MSG_NO_DASHES(...)                                          \
     {                                                                       \
       if (nstrace_enabled)                                                  \
         {                                                                   \
-          fprintf (stderr, "%-10s:%5d: [%5d]%.*s",                          \
-                   __FILE__, __LINE__, nstrace_num++,                       \
-                   2*nstrace_depth, "  | | | | | | | | | | | | | | | ..");  \
-          fprintf (stderr, __VA_ARGS__);                                    \
-          fprintf (stderr, "\n");                                           \
+	  errprintf ("%-10s:%5d: [%5d]%.*s",				    \
+		     __FILE__, __LINE__, nstrace_num++,			    \
+		     2*nstrace_depth, "  | | | | | | | | | | | | | | | .."); \
+	  errprintf (__VA_ARGS__);					    \
+	  errputc ('\n');						    \
         }                                                                   \
     }                                                                       \
   while(0)
diff --git a/src/nsterm.m b/src/nsterm.m
index bc1c7860aa..91eab1b0d2 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -2121,7 +2121,7 @@ so some key presses (TAB) are swallowed by the system.  */
 
   color_table->colors[idx] = color;
   [color retain];
-  /* fprintf(stderr, "color_table: allocated %d\n",idx); */
+  /* errprintf ("color_table: allocated %d\n", idx); */
   return idx;
 }
 
@@ -2466,7 +2466,7 @@ so some key presses (TAB) are swallowed by the system.  */
 
   if (*fp == NULL)
     {
-      fprintf (stderr, "Warning: ns_mouse_position () called with null *fp.\n");
+      errputs ("Warning: ns_mouse_position () called with null *fp.\n");
       return;
     }
 
@@ -5032,8 +5032,8 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
         *result = make_float (f);
       else if (is_modstring && value)
         *result = ns_string_to_lispmod (value);
-      else fprintf (stderr,
-                   "Bad value for default \"%s\": \"%s\"\n", parameter, value);
+      else
+	errprintf ("Bad value for default \"%s\": \"%s\"\n", parameter, value);
     }
 }
 
@@ -5222,8 +5222,7 @@ static Lisp_Object ns_new_font (struct frame *f, Lisp_Object font_object,
     {
       if (emacs_pipe (selfds) != 0)
         {
-          fprintf (stderr, "Failed to create pipe: %s\n",
-                   emacs_strerror (errno));
+          errprintf ("Failed to create pipe: %s\n", emacs_strerror (errno));
           emacs_abort ();
         }
 
@@ -5613,7 +5612,7 @@ - (void)sendEvent: (NSEvent *)theEvent
 
   if (type == NSEventTypeCursorUpdate && window == nil)
     {
-      fprintf (stderr, "Dropping external cursor update event.\n");
+      errputs ("Dropping external cursor update event.\n");
       return;
     }
 
@@ -6288,8 +6287,8 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
         emacs_event->modifiers ^= parse_solitary_modifier (ns_function_modifier);
 
       if (NS_KEYLOG)
-        fprintf (stderr, "keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
-                 code, fnKeysym, flags, emacs_event->modifiers);
+        errprintf ("keyDown: code =%x\tfnKey =%x\tflags = %x\tmods = %x\n",
+		   code, fnKeysym, flags, emacs_event->modifiers);
 
       /* If it was a function key or had control-like modifiers, pass
          it directly to Emacs.  */
@@ -6333,7 +6332,7 @@ In that case we use UCKeyTranslate (ns_get_shifted_character)
      https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html.  */
 
   if (NS_KEYLOG && !processingCompose)
-    fprintf (stderr, "keyDown: Begin compose sequence.\n");
+    errputs ("keyDown: Begin compose sequence.\n");
 
   /* FIXME: interpretKeyEvents doesn’t seem to send insertText if ⌘ is
      used as shift-like modifier, at least on El Capitan.  Mask it
@@ -8291,7 +8290,7 @@ -(BOOL)performDragOperation: (id <NSDraggingInfo>) sender
     }
   else
     {
-      fprintf (stderr, "Invalid data type in dragging pasteboard\n");
+      errputs ("Invalid data type in dragging pasteboard\n");
       return NO;
     }
 
@@ -9035,8 +9034,8 @@ - (void)mouseDown: (NSEvent *)e
     case NSScrollerKnobSlot:  /* GNUstep-only */
       last_hit_part = scroll_bar_move_ratio; break;
     default:  /* NSScrollerNoPart? */
-      fprintf (stderr, "EmacsScroller-mouseDown: unexpected part %ld\n",
-               (long) part);
+      errprintf ("EmacsScroller-mouseDown: unexpected part %ld\n",
+		 (long) part);
       return;
     }
 
@@ -9319,7 +9318,7 @@ Convert an X font name (XLFD) to an NS font name.
             name[i+1] = c_toupper (name[i+1]);
         }
     }
-  /* fprintf (stderr, "converted '%s' to '%s'\n",xlfd,name); */
+  /* errprintf ("converted '%s' to '%s'\n", xlfd, name); */
   ret = [[NSString stringWithUTF8String: name] UTF8String];
   xfree (name);
   return ret;
diff --git a/src/pdumper.c b/src/pdumper.c
index c00f8a0af5..bfa0dba7f0 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -23,7 +23,6 @@
 #include <math.h>
 #include <stdarg.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <sys/param.h>
@@ -42,6 +41,7 @@
 #include "lisp.h"
 #include "pdumper.h"
 #include "window.h"
+#include "sysstdio.h"
 #include "systime.h"
 #include "thread.h"
 #include "bignum.h"
@@ -158,7 +158,7 @@ dump_trace (const char *fmt, ...)
     {
       va_list args;
       va_start (args, fmt);
-      vfprintf (stderr, fmt, args);
+      verrprintf (fmt, args);
       va_end (args);
     }
 }
@@ -326,10 +326,10 @@ dump_reloc_set_offset (struct dump_reloc *reloc, dump_off offset)
 static void
 dump_fingerprint (const char *label, unsigned char const *xfingerprint)
 {
-  fprintf (stderr, "%s: ", label);
+  errprintf ("%s: ", label);
   for (int i = 0; i < 32; ++i)
-    fprintf (stderr, "%02x", (unsigned) xfingerprint[i]);
-  fprintf (stderr, "\n");
+    errprintf ("%02x", (unsigned) xfingerprint[i]);
+  errputc ('\n');
 }
 
 /* Format of an Emacs portable dump file.  All offsets are relative to
@@ -1403,10 +1403,9 @@ print_paths_to_root_1 (struct dump_context *ctx,
       Lisp_Object referrer = XCAR (referrers);
       referrers = XCDR (referrers);
       Lisp_Object repr = Fprin1_to_string (referrer, Qnil);
-      for (int i = 0; i < level; ++i)
-        fputc (' ', stderr);
+      errprintf ("%*s", level, "");
       fwrite (SDATA (repr), 1, SBYTES (repr), stderr);
-      fputc ('\n', stderr);
+      errputc ('\n');
       print_paths_to_root_1 (ctx, referrer, level + 1);
     }
 }
@@ -4226,16 +4225,15 @@ DEFUN ("dump-emacs-portable",
   dump_seek (ctx, 0);
   dump_write (ctx, &ctx->header, sizeof (ctx->header));
 
-  fprintf (stderr, "Dump complete\n");
-  fprintf (stderr,
-           "Byte counts: header=%lu hot=%lu discardable=%lu cold=%lu\n",
-           (unsigned long) (header_end - header_start),
-           (unsigned long) (hot_end - hot_start),
-           (unsigned long) (discardable_end - ctx->header.discardable_start),
-           (unsigned long) (cold_end - ctx->header.cold_start));
-  fprintf (stderr, "Reloc counts: hot=%u discardable=%u\n",
-           number_hot_relocations,
-           number_discardable_relocations);
+  errputs ("Dump complete\n");
+  errprintf ("Byte counts: header=%lu hot=%lu discardable=%lu cold=%lu\n",
+	     (unsigned long) (header_end - header_start),
+	     (unsigned long) (hot_end - hot_start),
+	     (unsigned long) (discardable_end - ctx->header.discardable_start),
+	     (unsigned long) (cold_end - ctx->header.cold_start));
+  errprintf ("Reloc counts: hot=%u discardable=%u\n",
+	     number_hot_relocations,
+	     number_discardable_relocations);
 
   unblock_input ();
   return unbind_to (count, Qnil);
diff --git a/src/print.c b/src/print.c
index 406abbf4a3..df45cb27ce 100644
--- a/src/print.c
+++ b/src/print.c
@@ -840,7 +840,7 @@ DEFUN ("redirect-debugging-output", Fredirect_debugging_output, Sredirect_debugg
 debug_print (Lisp_Object arg)
 {
   Fprin1 (arg, Qexternal_debugging_output);
-  fprintf (stderr, "\r\n");
+  errputs ("\r\n");
 }
 
 void safe_debug_print (Lisp_Object) EXTERNALLY_VISIBLE;
@@ -854,9 +854,9 @@ safe_debug_print (Lisp_Object arg)
   else
     {
       EMACS_UINT n = XLI (arg);
-      fprintf (stderr, "#<%s_LISP_OBJECT 0x%08"pI"x>\r\n",
-	       !valid ? "INVALID" : "SOME",
-	       n);
+      errprintf ("#<%s_LISP_OBJECT 0x%08"pI"x>\r\n",
+		 !valid ? "INVALID" : "SOME",
+		 n);
     }
 }
 
diff --git a/src/regex-emacs.c b/src/regex-emacs.c
index ac9f91dacb..83850b8b7d 100644
--- a/src/regex-emacs.c
+++ b/src/regex-emacs.c
@@ -442,7 +442,7 @@ #define CHARSET_RANGE_TABLE_END(range_table, count)	\
 
 # define DEBUG_STATEMENT(e) e
 # define DEBUG_PRINT(...)                                       \
-  if (regex_emacs_debug > 0) fprintf (stderr, __VA_ARGS__)
+  if (regex_emacs_debug > 0) errprintf (__VA_ARGS__)
 # define DEBUG_COMPILES_ARGUMENTS
 # define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)				\
   if (regex_emacs_debug > 0) print_partial_compiled_pattern (s, e)
@@ -453,11 +453,11 @@ #define CHARSET_RANGE_TABLE_END(range_table, count)	\
 debug_putchar (int c)
 {
   if (c >= 32 && c <= 126)
-    fputc (c, stderr);
+    errputc (c);
   else
     {
       unsigned int uc = c;
-      fprintf (stderr, "{%02x}", uc);
+      errprintf ("{%02x}", uc);
     }
 }
 
@@ -482,12 +482,12 @@ print_fastmap (char *fastmap)
 	    }
 	  if (was_a_range)
 	    {
-	      fprintf (stderr, "-");
+	      errputs ("-");
 	      debug_putchar (i - 1);
 	    }
 	}
     }
-  fputc ('\n', stderr);
+  errputc ('\n');
 }
 
 
@@ -503,50 +503,50 @@ print_partial_compiled_pattern (re_char *start, re_char *end)
 
   if (start == NULL)
     {
-      fprintf (stderr, "(null)\n");
+      errputs ("(null)\n");
       return;
     }
 
   /* Loop over pattern commands.  */
   while (p < pend)
     {
-      fprintf (stderr, "%td:\t", p - start);
+      errprintf ("%td:\t", p - start);
 
       switch ((re_opcode_t) *p++)
 	{
 	case no_op:
-	  fprintf (stderr, "/no_op");
+	  errputs ("/no_op");
 	  break;
 
 	case succeed:
-	  fprintf (stderr, "/succeed");
+	  errputs ("/succeed");
 	  break;
 
 	case exactn:
 	  mcnt = *p++;
-	  fprintf (stderr, "/exactn/%d", mcnt);
+	  errprintf ("/exactn/%d", mcnt);
 	  do
 	    {
-	      fprintf (stderr, "/");
+	      errputc ('/');
 	      debug_putchar (*p++);
 	    }
 	  while (--mcnt);
 	  break;
 
 	case start_memory:
-	  fprintf (stderr, "/start_memory/%d", *p++);
+	  errprintf ("/start_memory/%d", *p++);
 	  break;
 
 	case stop_memory:
-	  fprintf (stderr, "/stop_memory/%d", *p++);
+	  errprintf ("/stop_memory/%d", *p++);
 	  break;
 
 	case duplicate:
-	  fprintf (stderr, "/duplicate/%d", *p++);
+	  errprintf ("/duplicate/%d", *p++);
 	  break;
 
 	case anychar:
-	  fprintf (stderr, "/anychar");
+	  errputs ("/anychar");
 	  break;
 
 	case charset:
@@ -557,11 +557,11 @@ print_partial_compiled_pattern (re_char *start, re_char *end)
 	    int length = CHARSET_BITMAP_SIZE (p - 1);
 	    bool has_range_table = CHARSET_RANGE_TABLE_EXISTS_P (p - 1);
 
-	    fprintf (stderr, "/charset [%s",
-		     (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
+	    errprintf ("/charset [%s",
+		       (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
 
 	    if (p + *p >= pend)
-	      fprintf (stderr, " !extends past end of pattern! ");
+	      errputs (" !extends past end of pattern! ");
 
 	    for (c = 0; c < 256; c++)
 	      if (c / 8 < length
@@ -570,7 +570,7 @@ print_partial_compiled_pattern (re_char *start, re_char *end)
 		  /* Are we starting a range?  */
 		  if (last + 1 == c && ! in_range)
 		    {
-		      fprintf (stderr, "-");
+		      errputc ('-');
 		      in_range = true;
 		    }
 		  /* Have we broken a range?  */
@@ -589,14 +589,14 @@ print_partial_compiled_pattern (re_char *start, re_char *end)
 	    if (in_range)
 	      debug_putchar (last);
 
-	    fprintf (stderr, "]");
+	    errputc (']');
 
 	    p += 1 + length;
 
 	    if (has_range_table)
 	      {
 		int count;
-		fprintf (stderr, "has-range-table");
+		errputs ("has-range-table");
 
 		/* ??? Should print the range table; for now, just skip it.  */
 		p += 2;		/* skip range table bits */
@@ -607,136 +607,136 @@ print_partial_compiled_pattern (re_char *start, re_char *end)
 	  break;
 
 	case begline:
-	  fprintf (stderr, "/begline");
+	  errputs ("/begline");
 	  break;
 
 	case endline:
-	  fprintf (stderr, "/endline");
+	  errputs ("/endline");
 	  break;
 
 	case on_failure_jump:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  fprintf (stderr, "/on_failure_jump to %td", p + mcnt - start);
+	  errprintf ("/on_failure_jump to %td", p + mcnt - start);
 	  break;
 
 	case on_failure_keep_string_jump:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  fprintf (stderr, "/on_failure_keep_string_jump to %td",
-		   p + mcnt - start);
+	  errprintf ("/on_failure_keep_string_jump to %td",
+		     p + mcnt - start);
 	  break;
 
 	case on_failure_jump_nastyloop:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  fprintf (stderr, "/on_failure_jump_nastyloop to %td",
-		   p + mcnt - start);
+	  errprintf ("/on_failure_jump_nastyloop to %td",
+		     p + mcnt - start);
 	  break;
 
 	case on_failure_jump_loop:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  fprintf (stderr, "/on_failure_jump_loop to %td",
-		   p + mcnt - start);
+	  errprintf ("/on_failure_jump_loop to %td",
+		     p + mcnt - start);
 	  break;
 
 	case on_failure_jump_smart:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  fprintf (stderr, "/on_failure_jump_smart to %td",
-		   p + mcnt - start);
+	  errprintf ("/on_failure_jump_smart to %td",
+		     p + mcnt - start);
 	  break;
 
 	case jump:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
-	  fprintf (stderr, "/jump to %td", p + mcnt - start);
+	  errprintf ("/jump to %td", p + mcnt - start);
 	  break;
 
 	case succeed_n:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
 	  EXTRACT_NUMBER_AND_INCR (mcnt2, p);
-	  fprintf (stderr, "/succeed_n to %td, %d times",
-		   p - 2 + mcnt - start, mcnt2);
+	  errprintf ("/succeed_n to %td, %d times",
+		     p - 2 + mcnt - start, mcnt2);
 	  break;
 
 	case jump_n:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
 	  EXTRACT_NUMBER_AND_INCR (mcnt2, p);
-	  fprintf (stderr, "/jump_n to %td, %d times",
-		   p - 2 + mcnt - start, mcnt2);
+	  errprintf ("/jump_n to %td, %d times",
+		     p - 2 + mcnt - start, mcnt2);
 	  break;
 
 	case set_number_at:
 	  EXTRACT_NUMBER_AND_INCR (mcnt, p);
 	  EXTRACT_NUMBER_AND_INCR (mcnt2, p);
-	  fprintf (stderr, "/set_number_at location %td to %d",
-		   p - 2 + mcnt - start, mcnt2);
+	  errprintf ("/set_number_at location %td to %d",
+		     p - 2 + mcnt - start, mcnt2);
 	  break;
 
 	case wordbound:
-	  fprintf (stderr, "/wordbound");
+	  errputs ("/wordbound");
 	  break;
 
 	case notwordbound:
-	  fprintf (stderr, "/notwordbound");
+	  errputs ("/notwordbound");
 	  break;
 
 	case wordbeg:
-	  fprintf (stderr, "/wordbeg");
+	  errputs ("/wordbeg");
 	  break;
 
 	case wordend:
-	  fprintf (stderr, "/wordend");
+	  errputs ("/wordend");
 	  break;
 
 	case symbeg:
-	  fprintf (stderr, "/symbeg");
+	  errputs ("/symbeg");
 	  break;
 
 	case symend:
-	  fprintf (stderr, "/symend");
+	  errputs ("/symend");
 	  break;
 
 	case syntaxspec:
-	  fprintf (stderr, "/syntaxspec");
+	  errputs ("/syntaxspec");
 	  mcnt = *p++;
-	  fprintf (stderr, "/%d", mcnt);
+	  errprintf ("/%d", mcnt);
 	  break;
 
 	case notsyntaxspec:
-	  fprintf (stderr, "/notsyntaxspec");
+	  errputs ("/notsyntaxspec");
 	  mcnt = *p++;
-	  fprintf (stderr, "/%d", mcnt);
+	  errprintf ("/%d", mcnt);
 	  break;
 
 	case at_dot:
-	  fprintf (stderr, "/at_dot");
+	  errputs ("/at_dot");
 	  break;
 
 	case categoryspec:
-	  fprintf (stderr, "/categoryspec");
+	  errputs ("/categoryspec");
 	  mcnt = *p++;
-	  fprintf (stderr, "/%d", mcnt);
+	  errprintf ("/%d", mcnt);
 	  break;
 
 	case notcategoryspec:
-	  fprintf (stderr, "/notcategoryspec");
+	  errputs ("/notcategoryspec");
 	  mcnt = *p++;
-	  fprintf (stderr, "/%d", mcnt);
+	  errprintf ("/%d", mcnt);
 	  break;
 
 	case begbuf:
-	  fprintf (stderr, "/begbuf");
+	  errputs ("/begbuf");
 	  break;
 
 	case endbuf:
-	  fprintf (stderr, "/endbuf");
+	  errputs ("/endbuf");
 	  break;
 
 	default:
-	  fprintf (stderr, "?%d", *(p-1));
+	  errprintf ("?%d", *(p-1));
 	}
 
-      fprintf (stderr, "\n");
+      errputc ('\n');
     }
 
-  fprintf (stderr, "%td:\tend of pattern.\n", p - start);
+  errprintf ("%td:\tend of pattern.\n", p - start);
 }
 
 
@@ -746,18 +746,18 @@ print_compiled_pattern (struct re_pattern_buffer *bufp)
   re_char *buffer = bufp->buffer;
 
   print_partial_compiled_pattern (buffer, buffer + bufp->used);
-  fprintf (stderr, "%td bytes used/%td bytes allocated.\n",
-           bufp->used, bufp->allocated);
+  errprintf ("%td bytes used/%td bytes allocated.\n",
+	     bufp->used, bufp->allocated);
 
   if (bufp->fastmap_accurate && bufp->fastmap)
     {
-      fprintf (stderr, "fastmap: ");
+      errputs ("fastmap: ");
       print_fastmap (bufp->fastmap);
     }
 
-  fprintf (stderr, "re_nsub: %td\t", bufp->re_nsub);
-  fprintf (stderr, "regs_alloc: %d\t", bufp->regs_allocated);
-  fprintf (stderr, "can_be_null: %d\n", bufp->can_be_null);
+  errprintf ("re_nsub: %td\t", bufp->re_nsub);
+  errprintf ("regs_alloc: %d\t", bufp->regs_allocated);
+  errprintf ("can_be_null: %d\n", bufp->can_be_null);
   fflush (stderr);
   /* Perhaps we should print the translate table?  */
 }
@@ -768,7 +768,7 @@ print_double_string (re_char *where, re_char *string1, ptrdiff_t size1,
 		     re_char *string2, ptrdiff_t size2)
 {
   if (where == NULL)
-    fprintf (stderr, "(null)");
+    errputs ("(null)");
   else
     {
       int i;
@@ -1751,7 +1751,7 @@ regex_compile (re_char *pattern, ptrdiff_t size,
     {
       for (ptrdiff_t debug_count = 0; debug_count < size; debug_count++)
 	debug_putchar (pattern[debug_count]);
-      fputc ('\n', stderr);
+      errputc ('\n');
     }
 #endif
 
diff --git a/src/region-cache.c b/src/region-cache.c
index 57a26f2fa6..51604e815f 100644
--- a/src/region-cache.c
+++ b/src/region-cache.c
@@ -762,19 +762,18 @@ pp_cache (struct region_cache *c)
   ptrdiff_t beg_u = c->buffer_beg + c->beg_unchanged;
   ptrdiff_t end_u = c->buffer_end - c->end_unchanged;
 
-  fprintf (stderr,
-           "basis: %"pD"d..%"pD"d    modified: %"pD"d..%"pD"d\n",
-           c->buffer_beg, c->buffer_end,
-           beg_u, end_u);
+  errprintf ("basis: %"pD"d..%"pD"d    modified: %"pD"d..%"pD"d\n",
+	     c->buffer_beg, c->buffer_end,
+	     beg_u, end_u);
 
   for (ptrdiff_t i = 0; i < c->cache_len; i++)
     {
       ptrdiff_t pos = BOUNDARY_POS (c, i);
 
-      fprintf (stderr, "%c%c%"pD"d : %d\n",
-	       pos < beg_u ? 'v' : pos == beg_u ? '-' : ' ',
-	       pos > end_u ? '^' : pos == end_u ? '-' : ' ',
-	       pos, BOUNDARY_VALUE (c, i));
+      errprintf ("%c%c%"pD"d : %d\n",
+		 pos < beg_u ? 'v' : pos == beg_u ? '-' : ' ',
+		 pos > end_u ? '^' : pos == end_u ? '-' : ' ',
+		 pos, BOUNDARY_VALUE (c, i));
     }
 }
 
diff --git a/src/sysdep.c b/src/sysdep.c
index 4f89e8aba1..beccc3c537 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2767,6 +2767,35 @@ safe_strsignal (int code)
 
   return signame;
 }
+
+/* Output to stderr.  */
+
+void
+errputc (int c)
+{
+  fputc_unlocked (c, stderr);
+}
+
+void
+errputs (char const *str)
+{
+  fputs_unlocked (str, stderr);
+}
+
+void
+errprintf (char const *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  verrprintf (fmt, ap);
+  va_end (ap);
+}
+
+void
+verrprintf (char const *fmt, va_list ap)
+{
+  vfprintf (stderr, fmt, ap);
+}
 \f
 #ifndef DOS_NT
 /* For make-serial-process  */
diff --git a/src/sysstdio.h b/src/sysstdio.h
index 3ff1d6a572..ebe0845d40 100644
--- a/src/sysstdio.h
+++ b/src/sysstdio.h
@@ -25,6 +25,11 @@ #define EMACS_SYSSTDIO_H
 
 extern FILE *emacs_fopen (char const *, char const *);
 
+extern void errputc (int);
+extern void errputs (char const *);
+extern void errprintf (char const *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
+extern void verrprintf (char const *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
+
 #if O_BINARY
 # define FOPEN_BINARY "b"
 # define FOPEN_TEXT "t"
diff --git a/src/systhread.c b/src/systhread.c
index 6f4de536fb..ef57c0e8b3 100644
--- a/src/systhread.c
+++ b/src/systhread.c
@@ -18,9 +18,10 @@ Copyright (C) 2012-2019 Free Software Foundation, Inc.
 
 #include <config.h>
 #include <setjmp.h>
-#include <stdio.h>
 #include <string.h>
+
 #include "lisp.h"
+#include "sysstdio.h"
 
 #ifdef HAVE_NS
 #include "nsterm.h"
@@ -122,7 +123,7 @@ sys_mutex_init (sys_mutex_t *mutex)
   /* We could get ENOMEM.  Can't do anything except aborting.  */
   if (error != 0)
     {
-      fprintf (stderr, "\npthread_mutex_init failed: %s\n", strerror (error));
+      errprintf ("\npthread_mutex_init failed: %s\n", strerror (error));
       emacs_abort ();
     }
 #ifdef ENABLE_CHECKING
@@ -152,7 +153,7 @@ sys_cond_init (sys_cond_t *cond)
   /* We could get ENOMEM.  Can't do anything except aborting.  */
   if (error != 0)
     {
-      fprintf (stderr, "\npthread_cond_init failed: %s\n", strerror (error));
+      errprintf ("\npthread_cond_init failed: %s\n", strerror (error));
       emacs_abort ();
     }
 }
diff --git a/src/term.c b/src/term.c
index 8b5a710d80..5b9c010520 100644
--- a/src/term.c
+++ b/src/term.c
@@ -4398,10 +4398,10 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed)
 static void
 vfatal (const char *str, va_list ap)
 {
-  fprintf (stderr, "emacs: ");
-  vfprintf (stderr, str, ap);
+  errputs ("emacs: ");
+  verrprintf (str, ap);
   if (! (str[0] && str[strlen (str) - 1] == '\n'))
-    fprintf (stderr, "\n");
+    errputc ('\n');
   exit (1);
 }
 
diff --git a/src/unexaix.c b/src/unexaix.c
index 349d365383..a273ba1adb 100644
--- a/src/unexaix.c
+++ b/src/unexaix.c
@@ -225,12 +225,11 @@ make_hdr (int new, int a_out,
 #define CHECK_SCNHDR(ptr, name, flags) \
   if (strcmp (s->s_name, name) == 0) { \
     if (s->s_flags != flags) { \
-      fprintf (stderr, "unexec: %lx flags where %x expected in %s section.\n", \
-               (unsigned long)s->s_flags, flags, name);                 \
+      errprintf ("unexec: %lx flags where %x expected in %s section.\n", \
+		 (unsigned long) s->s_flags, flags, name);		\
     } \
     if (ptr) { \
-      fprintf (stderr, "unexec: duplicate section header for section %s.\n", \
-               name);                                                   \
+      errprintf ("unexec: duplicate section header for section %s.\n", name); \
     } \
     ptr = s; \
   }
diff --git a/src/unexelf.c b/src/unexelf.c
index 6d19bf1fb9..d09a5f5aac 100644
--- a/src/unexelf.c
+++ b/src/unexelf.c
@@ -187,7 +187,7 @@ verify ((! TYPE_SIGNED (ElfW (Half))
 	&& TYPE_MAXIMUM (ElfW (Half)) <= PTRDIFF_MAX);
 
 #ifdef UNEXELF_DEBUG
-# define DEBUG_LOG(expr) fprintf (stderr, #expr " 0x%jx\n", (uintmax_t) (expr))
+# define DEBUG_LOG(expr) errprintf (#expr " 0x%jx\n", (uintmax_t) (expr))
 #endif
 
 /* Get the address of a particular section or program header entry,
@@ -344,7 +344,7 @@ unexec (const char *new_name, const char *old_name)
   new_data2_offset = old_bss_offset;
 
 #ifdef UNEXELF_DEBUG
-  fprintf (stderr, "old_bss_index %td\n", old_bss_index);
+  errprintf ("old_bss_index %td\n", old_bss_index);
   DEBUG_LOG (old_bss_addr);
   DEBUG_LOG (old_bss_size);
   DEBUG_LOG (old_bss_offset);
@@ -395,9 +395,9 @@ unexec (const char *new_name, const char *old_name)
 
 #ifdef UNEXELF_DEBUG
   DEBUG_LOG (old_file_h->e_shoff);
-  fprintf (stderr, "Old section count %td\n", (ptrdiff_t) old_file_h->e_shnum);
+  errprintf ("Old section count %td\n", (ptrdiff_t) old_file_h->e_shnum);
   DEBUG_LOG (new_file_h->e_shoff);
-  fprintf (stderr, "New section count %td\n", (ptrdiff_t) new_file_h->e_shnum);
+  errprintf ("New section count %td\n", (ptrdiff_t) new_file_h->e_shnum);
 #endif
 
   /* Fix up program header.  Extend the writable data segment so
diff --git a/src/unexhp9k800.c b/src/unexhp9k800.c
index cbf1835b9e..48d516fa2f 100644
--- a/src/unexhp9k800.c
+++ b/src/unexhp9k800.c
@@ -167,7 +167,7 @@ read_header (int file, struct header *hdr, struct som_exec_auxhdr *auxhdr)
   if (hdr->a_magic != EXEC_MAGIC && hdr->a_magic != SHARE_MAGIC
       &&  hdr->a_magic != DEMAND_MAGIC)
     {
-      fprintf (stderr, "a.out file doesn't have valid magic number\n");
+      errputs ("a.out file doesn't have valid magic number\n");
       exit (1);
     }
 
diff --git a/src/unexmacosx.c b/src/unexmacosx.c
index a94c0cccb6..4c93301369 100644
--- a/src/unexmacosx.c
+++ b/src/unexmacosx.c
@@ -303,9 +303,9 @@ unexec_error (const char *format, ...)
   va_list ap;
 
   va_start (ap, format);
-  fprintf (stderr, "unexec: ");
-  vfprintf (stderr, format, ap);
-  fprintf (stderr, "\n");
+  errputs ("unexec: ");
+  verrprintf (format, ap);
+  errputc ('\n');
   va_end (ap);
   exit (1);
 }
diff --git a/src/widget.c b/src/widget.c
index e662dd3ecd..a150088c93 100644
--- a/src/widget.c
+++ b/src/widget.c
@@ -364,8 +364,7 @@ EmacsFrameInitialize (Widget request, Widget new, ArgList dum1, Cardinal *dum2)
 
   if (!ew->emacs_frame.frame)
     {
-      fprintf (stderr,
-	       "can't create an emacs frame widget without a frame\n");
+      errputs ("can't create an emacs frame widget without a frame\n");
       exit (1);
     }
 
diff --git a/src/xdisp.c b/src/xdisp.c
index c13a950e3a..4dbb183bd5 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 <config.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <math.h>
@@ -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"
@@ -1762,10 +1762,10 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int *x, int *y,
 #if false
   /* Debugging code.  */
   if (visible_p)
-    fprintf (stderr, "+pv pt=%d vs=%d --> x=%d y=%d rt=%d rb=%d rh=%d vp=%d\n",
-	     charpos, w->vscroll, *x, *y, *rtop, *rbot, *rowh, *vpos);
+    errprintf ("+pv pt=%d vs=%d --> x=%d y=%d rt=%d rb=%d rh=%d vp=%d\n",
+	       charpos, w->vscroll, *x, *y, *rtop, *rbot, *rowh, *vpos);
   else
-    fprintf (stderr, "-pv pt=%d vs=%d\n", charpos, w->vscroll);
+    errprintf ("-pv pt=%d vs=%d\n", charpos, w->vscroll);
 #endif
 
   /* Restore potentially overwritten values.  */
@@ -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))
     {
@@ -10741,7 +10741,7 @@ message_to_stderr (Lisp_Object m)
       }
     }
   else if (!cursor_in_echo_area)
-    fputc ('\n', stderr);
+    errputc ('\n');
 
   fflush (stderr);
 }
@@ -10888,11 +10888,11 @@ vmessage (const char *m, va_list ap)
       if (m)
 	{
 	  if (noninteractive_need_newline)
-	    fputc ('\n', stderr);
+	    errputc ('\n');
 	  noninteractive_need_newline = false;
-	  vfprintf (stderr, m, ap);
+	  verrprintf (m, ap);
 	  if (!cursor_in_echo_area)
-	    fputc ('\n', stderr);
+	    errputc ('\n');
 	  fflush (stderr);
 	}
     }
@@ -13679,13 +13679,13 @@ debug_method_add (struct window *w, char const *fmt, ...)
   va_end (ap);
 
   if (trace_redisplay_p)
-    fprintf (stderr, "%p (%s): %s\n",
-	     ptr,
-	     ((BUFFERP (w->contents)
-	       && STRINGP (BVAR (XBUFFER (w->contents), name)))
-	      ? SSDATA (BVAR (XBUFFER (w->contents), name))
-	      : "no buffer"),
-	     method + len);
+    errprintf ("%p (%s): %s\n",
+	       ptr,
+	       ((BUFFERP (w->contents)
+		 && STRINGP (BVAR (XBUFFER (w->contents), name)))
+		? SSDATA (BVAR (XBUFFER (w->contents), name))
+		: "no buffer"),
+	       method + len);
 }
 
 #endif /* GLYPH_DEBUG */
@@ -19521,117 +19521,111 @@ dump_glyph (struct glyph_row *row, struct glyph *glyph, int area)
   if (glyph->type == CHAR_GLYPH
       || glyph->type == GLYPHLESS_GLYPH)
     {
-      fprintf (stderr,
-	       "  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x      %c %4d %1.1d%1.1d\n",
-	       glyph - row->glyphs[TEXT_AREA],
-	       (glyph->type == CHAR_GLYPH
-		? 'C'
-		: 'G'),
-	       glyph->charpos,
-	       (BUFFERP (glyph->object)
-		? 'B'
-		: (STRINGP (glyph->object)
-		   ? 'S'
-		   : (NILP (glyph->object)
-		      ? '0'
-		      : '-'))),
-	       glyph->pixel_width,
-	       glyph->u.ch,
-	       (glyph->u.ch < 0x80 && glyph->u.ch >= ' '
-		? (int) glyph->u.ch
-		: '.'),
-	       glyph->face_id,
-	       glyph->left_box_line_p,
-	       glyph->right_box_line_p);
+      errprintf ("  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x      %c %4d %1.1d%1.1d\n",
+		 glyph - row->glyphs[TEXT_AREA],
+		 (glyph->type == CHAR_GLYPH
+		  ? 'C'
+		  : 'G'),
+		 glyph->charpos,
+		 (BUFFERP (glyph->object)
+		  ? 'B'
+		  : (STRINGP (glyph->object)
+		     ? 'S'
+		     : (NILP (glyph->object)
+			? '0'
+			: '-'))),
+		 glyph->pixel_width,
+		 glyph->u.ch,
+		 (glyph->u.ch < 0x80 && glyph->u.ch >= ' '
+		  ? (int) glyph->u.ch
+		  : '.'),
+		 glyph->face_id,
+		 glyph->left_box_line_p,
+		 glyph->right_box_line_p);
     }
   else if (glyph->type == STRETCH_GLYPH)
     {
-      fprintf (stderr,
-	       "  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x      %c %4d %1.1d%1.1d\n",
-	       glyph - row->glyphs[TEXT_AREA],
-	       'S',
-	       glyph->charpos,
-	       (BUFFERP (glyph->object)
-		? 'B'
-		: (STRINGP (glyph->object)
-		   ? 'S'
-		   : (NILP (glyph->object)
-		      ? '0'
-		      : '-'))),
-	       glyph->pixel_width,
-	       0u,
-	       ' ',
-	       glyph->face_id,
-	       glyph->left_box_line_p,
-	       glyph->right_box_line_p);
+      errprintf ("  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x      %c %4d %1.1d%1.1d\n",
+		 glyph - row->glyphs[TEXT_AREA],
+		 'S',
+		 glyph->charpos,
+		 (BUFFERP (glyph->object)
+		  ? 'B'
+		  : (STRINGP (glyph->object)
+		     ? 'S'
+		     : (NILP (glyph->object)
+			? '0'
+			: '-'))),
+		 glyph->pixel_width,
+		 0u,
+		 ' ',
+		 glyph->face_id,
+		 glyph->left_box_line_p,
+		 glyph->right_box_line_p);
     }
   else if (glyph->type == IMAGE_GLYPH)
     {
-      fprintf (stderr,
-	       "  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x      %c %4d %1.1d%1.1d\n",
-	       glyph - row->glyphs[TEXT_AREA],
-	       'I',
-	       glyph->charpos,
-	       (BUFFERP (glyph->object)
-		? 'B'
-		: (STRINGP (glyph->object)
-		   ? 'S'
-		   : (NILP (glyph->object)
-		      ? '0'
-		      : '-'))),
-	       glyph->pixel_width,
-	       (unsigned int) glyph->u.img_id,
-	       '.',
-	       glyph->face_id,
-	       glyph->left_box_line_p,
-	       glyph->right_box_line_p);
+      errprintf ("  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x      %c %4d %1.1d%1.1d\n",
+		 glyph - row->glyphs[TEXT_AREA],
+		 'I',
+		 glyph->charpos,
+		 (BUFFERP (glyph->object)
+		  ? 'B'
+		  : (STRINGP (glyph->object)
+		     ? 'S'
+		     : (NILP (glyph->object)
+			? '0'
+			: '-'))),
+		 glyph->pixel_width,
+		 (unsigned int) glyph->u.img_id,
+		 '.',
+		 glyph->face_id,
+		 glyph->left_box_line_p,
+		 glyph->right_box_line_p);
     }
   else if (glyph->type == COMPOSITE_GLYPH)
     {
-      fprintf (stderr,
-	       "  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x",
-	       glyph - row->glyphs[TEXT_AREA],
-	       '+',
-	       glyph->charpos,
-	       (BUFFERP (glyph->object)
-		? 'B'
-		: (STRINGP (glyph->object)
-		   ? 'S'
-		   : (NILP (glyph->object)
-		      ? '0'
-		      : '-'))),
-	       glyph->pixel_width,
-	       (unsigned int) glyph->u.cmp.id);
+      errprintf ("  %5"pD"d     %c %9"pD"d   %c %3d 0x%06x",
+		 glyph - row->glyphs[TEXT_AREA],
+		 '+',
+		 glyph->charpos,
+		 (BUFFERP (glyph->object)
+		  ? 'B'
+		  : (STRINGP (glyph->object)
+		     ? 'S'
+		     : (NILP (glyph->object)
+			? '0'
+			: '-'))),
+		 glyph->pixel_width,
+		 (unsigned int) glyph->u.cmp.id);
       if (glyph->u.cmp.automatic)
-	fprintf (stderr,
-		 "[%d-%d]",
-		 glyph->slice.cmp.from, glyph->slice.cmp.to);
-      fprintf (stderr, " . %4d %1.1d%1.1d\n",
-	       glyph->face_id,
-	       glyph->left_box_line_p,
-	       glyph->right_box_line_p);
+	errprintf ("[%d-%d]",
+		   glyph->slice.cmp.from, glyph->slice.cmp.to);
+      errprintf (" . %4d %1.1d%1.1d\n",
+		 glyph->face_id,
+		 glyph->left_box_line_p,
+		 glyph->right_box_line_p);
     }
   else if (glyph->type == XWIDGET_GLYPH)
     {
 #ifndef HAVE_XWIDGETS
       eassume (false);
 #else
-      fprintf (stderr,
-	       "  %5td %4c %6td %c %3d %7p %c %4d %1.1d%1.1d\n",
-	       glyph - row->glyphs[TEXT_AREA],
-	       'X',
-	       glyph->charpos,
-	       (BUFFERP (glyph->object)
-		? 'B'
-		: (STRINGP (glyph->object)
-		   ? 'S'
-		   : '-')),
-	       glyph->pixel_width,
-	       glyph->u.xwidget,
-	       '.',
-	       glyph->face_id,
-	       glyph->left_box_line_p,
-	       glyph->right_box_line_p);
+      errprintf ("  %5td %4c %6td %c %3d %7p %c %4d %1.1d%1.1d\n",
+		 glyph - row->glyphs[TEXT_AREA],
+		 'X',
+		 glyph->charpos,
+		 (BUFFERP (glyph->object)
+		  ? 'B'
+		  : (STRINGP (glyph->object)
+		     ? 'S'
+		     : '-')),
+		 glyph->pixel_width,
+		 glyph->u.xwidget,
+		 '.',
+		 glyph->face_id,
+		 glyph->left_box_line_p,
+		 glyph->right_box_line_p);
 #endif
     }
 }
@@ -19647,43 +19641,43 @@ dump_glyph_row (struct glyph_row *row, int vpos, int glyphs)
 {
   if (glyphs != 1)
     {
-      fprintf (stderr, "Row     Start       End Used oE><\\CTZFesm     X    Y    W    H    V    A    P\n");
-      fprintf (stderr, "==============================================================================\n");
+      errputs ("Row     Start       End Used oE><\\CTZFesm     X    Y    W    H    V    A    P\n");
+      errputs ("==============================================================================\n");
 
-      fprintf (stderr, "%3d %9"pD"d %9"pD"d %4d %1.1d%1.1d%1.1d%1.1d\
+      errprintf ("%3d %9"pD"d %9"pD"d %4d %1.1d%1.1d%1.1d%1.1d\
 %1.1d%1.1d%1.1d%1.1d%1.1d%1.1d%1.1d%1.1d  %4d %4d %4d %4d %4d %4d %4d\n",
-	       vpos,
-	       MATRIX_ROW_START_CHARPOS (row),
-	       MATRIX_ROW_END_CHARPOS (row),
-	       row->used[TEXT_AREA],
-	       row->contains_overlapping_glyphs_p,
-	       row->enabled_p,
-	       row->truncated_on_left_p,
-	       row->truncated_on_right_p,
-	       row->continued_p,
-	       MATRIX_ROW_CONTINUATION_LINE_P (row),
-	       MATRIX_ROW_DISPLAYS_TEXT_P (row),
-	       row->ends_at_zv_p,
-	       row->fill_line_p,
-	       row->ends_in_middle_of_char_p,
-	       row->starts_in_middle_of_char_p,
-	       row->mouse_face_p,
-	       row->x,
-	       row->y,
-	       row->pixel_width,
-	       row->height,
-	       row->visible_height,
-	       row->ascent,
-	       row->phys_ascent);
+		 vpos,
+		 MATRIX_ROW_START_CHARPOS (row),
+		 MATRIX_ROW_END_CHARPOS (row),
+		 row->used[TEXT_AREA],
+		 row->contains_overlapping_glyphs_p,
+		 row->enabled_p,
+		 row->truncated_on_left_p,
+		 row->truncated_on_right_p,
+		 row->continued_p,
+		 MATRIX_ROW_CONTINUATION_LINE_P (row),
+		 MATRIX_ROW_DISPLAYS_TEXT_P (row),
+		 row->ends_at_zv_p,
+		 row->fill_line_p,
+		 row->ends_in_middle_of_char_p,
+		 row->starts_in_middle_of_char_p,
+		 row->mouse_face_p,
+		 row->x,
+		 row->y,
+		 row->pixel_width,
+		 row->height,
+		 row->visible_height,
+		 row->ascent,
+		 row->phys_ascent);
       /* The next 3 lines should align to "Start" in the header.  */
-      fprintf (stderr, "    %9"pD"d %9"pD"d\t%5d\n", row->start.overlay_string_index,
-	       row->end.overlay_string_index,
-	       row->continuation_lines_width);
-      fprintf (stderr, "    %9"pD"d %9"pD"d\n",
-	       CHARPOS (row->start.string_pos),
-	       CHARPOS (row->end.string_pos));
-      fprintf (stderr, "    %9d %9d\n", row->start.dpvec_index,
-	       row->end.dpvec_index);
+      errprintf ("    %9"pD"d %9"pD"d\t%5d\n", row->start.overlay_string_index,
+		 row->end.overlay_string_index,
+		 row->continuation_lines_width);
+      errprintf ("    %9"pD"d %9"pD"d\n",
+		 CHARPOS (row->start.string_pos),
+		 CHARPOS (row->end.string_pos));
+      errprintf ("    %9d %9d\n", row->start.dpvec_index,
+		 row->end.dpvec_index);
     }
 
   if (glyphs > 1)
@@ -19700,7 +19694,7 @@ dump_glyph_row (struct glyph_row *row, int vpos, int glyphs)
 	    ++glyph_end;
 
 	  if (glyph < glyph_end)
-	    fprintf (stderr, " Glyph#  Type       Pos   O   W     Code      C Face LR\n");
+	    errputs (" Glyph#  Type       Pos   O   W     Code      C Face LR\n");
 
 	  for (; glyph < glyph_end; ++glyph)
 	    dump_glyph (row, glyph, area);
@@ -19736,7 +19730,7 @@ dump_glyph_row (struct glyph_row *row, int vpos, int glyphs)
 	    }
 
 	  s[i] = '\0';
-	  fprintf (stderr, "%3d: (%d) '%s'\n", vpos, row->enabled_p, s);
+	  errprintf ("%3d: (%d) '%s'\n", vpos, row->enabled_p, s);
 	}
     }
 }
@@ -19756,11 +19750,11 @@ DEFUN ("dump-glyph-matrix", Fdump_glyph_matrix,
   struct window *w = XWINDOW (selected_window);
   struct buffer *buffer = XBUFFER (w->contents);
 
-  fprintf (stderr, "PT = %"pD"d, BEGV = %"pD"d. ZV = %"pD"d\n",
-	   BUF_PT (buffer), BUF_BEGV (buffer), BUF_ZV (buffer));
-  fprintf (stderr, "Cursor x = %d, y = %d, hpos = %d, vpos = %d\n",
-	   w->cursor.x, w->cursor.y, w->cursor.hpos, w->cursor.vpos);
-  fprintf (stderr, "=============================================\n");
+  errprintf ("PT = %"pD"d, BEGV = %"pD"d. ZV = %"pD"d\n",
+	     BUF_PT (buffer), BUF_BEGV (buffer), BUF_ZV (buffer));
+  errprintf ("Cursor x = %d, y = %d, hpos = %d, vpos = %d\n",
+	     w->cursor.x, w->cursor.y, w->cursor.hpos, w->cursor.vpos);
+  errputs ("=============================================\n");
   dump_glyph_matrix (w->current_matrix,
 		     TYPE_RANGED_FIXNUMP (int, glyphs) ? XFIXNUM (glyphs) : 0);
   return Qnil;
@@ -19777,7 +19771,7 @@ DEFUN ("dump-frame-glyph-matrix", Fdump_frame_glyph_matrix,
   if (f->current_matrix)
     dump_glyph_matrix (f->current_matrix, 1);
   else
-    fprintf (stderr, "*** This frame doesn't have a frame glyph matrix ***\n");
+    errputs ("*** This frame doesn't have a frame glyph matrix ***\n");
   return Qnil;
 }
 
@@ -25973,18 +25967,18 @@ get_font_ascent_descent (struct font *font, int *ascent, int *descent)
 void
 dump_glyph_string (struct glyph_string *s)
 {
-  fprintf (stderr, "glyph string\n");
-  fprintf (stderr, "  x, y, w, h = %d, %d, %d, %d\n",
-	   s->x, s->y, s->width, s->height);
-  fprintf (stderr, "  ybase = %d\n", s->ybase);
-  fprintf (stderr, "  hl = %u\n", s->hl);
-  fprintf (stderr, "  left overhang = %d, right = %d\n",
-	   s->left_overhang, s->right_overhang);
-  fprintf (stderr, "  nchars = %d\n", s->nchars);
-  fprintf (stderr, "  extends to end of line = %d\n",
-	   s->extends_to_end_of_line_p);
-  fprintf (stderr, "  font height = %d\n", FONT_HEIGHT (s->font));
-  fprintf (stderr, "  bg width = %d\n", s->background_width);
+  errputs ("glyph string\n");
+  errprintf ("  x, y, w, h = %d, %d, %d, %d\n",
+	     s->x, s->y, s->width, s->height);
+  errprintf ("  ybase = %d\n", s->ybase);
+  errprintf ("  hl = %u\n", s->hl);
+  errprintf ("  left overhang = %d, right = %d\n",
+	     s->left_overhang, s->right_overhang);
+  errprintf ("  nchars = %d\n", s->nchars);
+  errprintf ("  extends to end of line = %d\n",
+	     s->extends_to_end_of_line_p);
+  errprintf ("  font height = %d\n", FONT_HEIGHT (s->font));
+  errprintf ("  bg width = %d\n", s->background_width);
 }
 
 #endif /* GLYPH_DEBUG */
diff --git a/src/xfaces.c b/src/xfaces.c
index d9e66eaf2d..ed3bb0945e 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -418,21 +418,15 @@ DEFUN ("dump-colors", Fdump_colors, Sdump_colors, 0, 0, 0,
 {
   int i, n;
 
-  fputc ('\n', stderr);
+  errputc ('\n');
 
   for (i = n = 0; i < ARRAYELTS (color_count); ++i)
     if (color_count[i])
-      {
-	fprintf (stderr, "%3d: %5d", i, color_count[i]);
-	++n;
-	if (n % 5 == 0)
-	  fputc ('\n', stderr);
-	else
-	  fputc ('\t', stderr);
-      }
+      errprintf ("%3d: %5d%c", i, color_count[i],
+		 ++n % 5 == 0 ? '\n' : '\t');
 
   if (n % 5 != 0)
-    fputc ('\n', stderr);
+    errputc ('\n');
   return Qnil;
 }
 
@@ -6397,28 +6391,28 @@ DEFUN ("x-load-color-file", Fx_load_color_file,
 static void
 dump_realized_face (struct face *face)
 {
-  fprintf (stderr, "ID: %d\n", face->id);
+  errprintf ("ID: %d\n", face->id);
 #ifdef HAVE_X_WINDOWS
-  fprintf (stderr, "gc: %p\n", face->gc);
+  errprintf ("gc: %p\n", face->gc);
 #endif
-  fprintf (stderr, "foreground: 0x%lx (%s)\n",
-	   face->foreground,
-	   SDATA (face->lface[LFACE_FOREGROUND_INDEX]));
-  fprintf (stderr, "background: 0x%lx (%s)\n",
-	   face->background,
-	   SDATA (face->lface[LFACE_BACKGROUND_INDEX]));
+  errprintf ("foreground: 0x%lx (%s)\n",
+	     face->foreground,
+	     SDATA (face->lface[LFACE_FOREGROUND_INDEX]));
+  errprintf ("background: 0x%lx (%s)\n",
+	     face->background,
+	     SDATA (face->lface[LFACE_BACKGROUND_INDEX]));
   if (face->font)
-    fprintf (stderr, "font_name: %s (%s)\n",
-	     SDATA (face->font->props[FONT_NAME_INDEX]),
-	     SDATA (face->lface[LFACE_FAMILY_INDEX]));
+    errprintf ("font_name: %s (%s)\n",
+	       SDATA (face->font->props[FONT_NAME_INDEX]),
+	       SDATA (face->lface[LFACE_FAMILY_INDEX]));
 #ifdef HAVE_X_WINDOWS
-  fprintf (stderr, "font = %p\n", face->font);
+  errprintf ("font = %p\n", face->font);
 #endif
-  fprintf (stderr, "fontset: %d\n", face->fontset);
-  fprintf (stderr, "underline: %d (%s)\n",
-	   face->underline_p,
-	   SDATA (Fsymbol_name (face->lface[LFACE_UNDERLINE_INDEX])));
-  fprintf (stderr, "hash: %" PRIuPTR "\n", face->hash);
+  errprintf ("fontset: %d\n", face->fontset);
+  errprintf ("underline: %d (%s)\n",
+	     face->underline_p,
+	     SDATA (Fsymbol_name (face->lface[LFACE_UNDERLINE_INDEX])));
+  errprintf ("hash: %" PRIuPTR "\n", face->hash);
 }
 
 
@@ -6429,14 +6423,14 @@ DEFUN ("dump-face", Fdump_face, Sdump_face, 0, 1, 0, doc: /* */)
     {
       int i;
 
-      fprintf (stderr, "font selection order: ");
+      errputs ("font selection order: ");
       for (i = 0; i < ARRAYELTS (font_sort_order); ++i)
-	fprintf (stderr, "%d ", font_sort_order[i]);
-      fprintf (stderr, "\n");
+	errprintf ("%d ", font_sort_order[i]);
+      errputc ('\n');
 
-      fprintf (stderr, "alternative fonts: ");
+      errputs ("alternative fonts: ");
       debug_print (Vface_alternative_font_family_alist);
-      fprintf (stderr, "\n");
+      errputc ('\n');
 
       for (i = 0; i < FRAME_FACE_CACHE (SELECTED_FRAME ())->used; ++i)
 	Fdump_face (make_fixnum (i));
@@ -6459,9 +6453,9 @@ DEFUN ("show-face-resources", Fshow_face_resources, Sshow_face_resources,
        0, 0, 0, doc: /* */)
   (void)
 {
-  fprintf (stderr, "number of colors = %d\n", ncolors_allocated);
-  fprintf (stderr, "number of pixmaps = %d\n", npixmaps_allocated);
-  fprintf (stderr, "number of GCs = %d\n", ngcs);
+  errprintf ("number of colors = %d\n", ncolors_allocated);
+  errprintf ("number of pixmaps = %d\n", npixmaps_allocated);
+  errprintf ("number of GCs = %d\n", ngcs);
   return Qnil;
 }
 
diff --git a/src/xfns.c b/src/xfns.c
index b8a1914186..24c01ace58 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -2384,13 +2384,13 @@ print_fontset_result (XFontSet xfs, char *name, char **missing_list,
 		      int missing_count)
 {
   if (xfs)
-    fprintf (stderr, "XIC Fontset created: %s\n", name);
+    errprintf ("XIC Fontset created: %s\n", name);
   else
     {
-      fprintf (stderr, "XIC Fontset failed: %s\n", name);
+      errprintf ("XIC Fontset failed: %s\n", name);
       while (missing_count-- > 0)
 	{
-	  fprintf (stderr, "  missing: %s\n", *missing_list);
+	  errprintf ("  missing: %s\n", *missing_list);
 	  missing_list++;
 	}
     }
diff --git a/src/xmenu.c b/src/xmenu.c
index 22d1cc21aa..2f952f363e 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -2321,7 +2321,7 @@ x_menu_show (struct frame *f, int x, int y, int menuflags,
     {
     case XM_SUCCESS:
 #ifdef XDEBUG
-      fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
+      errprintf ("pane= %d line = %d\n", panes, selidx);
 #endif
 
       /* Find the item number SELIDX in pane number PANE.  */
diff --git a/src/xrdb.c b/src/xrdb.c
index 9c625e9821..4dbc7cee30 100644
--- a/src/xrdb.c
+++ b/src/xrdb.c
@@ -589,7 +589,7 @@ member (char *elt, List list)
 static void
 fatal (char *msg, char *prog)
 {
-  fprintf (stderr, msg, prog);
+  errprintf (msg, prog);
   exit (1);
 }
 
diff --git a/src/xselect.c b/src/xselect.c
index 5f0bb44cc9..6268e74d8e 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -21,7 +21,6 @@
 
 #include <config.h>
 #include <limits.h>
-#include <stdio.h>      /* termhooks.h needs this */
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -33,6 +32,7 @@
 #include "xterm.h"	/* for all of the X includes */
 #include "frame.h"	/* Need this to get the X window of selected_frame */
 #include "blockinput.h"
+#include "sysstdio.h"
 #include "termhooks.h"
 #include "keyboard.h"
 #include "pdumper.h"
@@ -63,13 +63,13 @@
 
 #ifdef TRACE_SELECTION
 #define TRACE0(fmt) \
-  fprintf (stderr, "%"pMd": " fmt "\n", (printmax_t) getpid ())
+  errprintf ("%"pMd": " fmt "\n", (printmax_t) getpid ())
 #define TRACE1(fmt, a0) \
-  fprintf (stderr, "%"pMd": " fmt "\n", (printmax_t) getpid (), a0)
+  errprintf ("%"pMd": " fmt "\n", (printmax_t) getpid (), a0)
 #define TRACE2(fmt, a0, a1) \
-  fprintf (stderr, "%"pMd": " fmt "\n", (printmax_t) getpid (), a0, a1)
+  errprintf ("%"pMd": " fmt "\n", (printmax_t) getpid (), a0, a1)
 #define TRACE3(fmt, a0, a1, a2) \
-  fprintf (stderr, "%"pMd": " fmt "\n", (printmax_t) getpid (), a0, a1, a2)
+  errprintf ("%"pMd": " fmt "\n", (printmax_t) getpid (), a0, a1, a2)
 #else
 #define TRACE0(fmt)		(void) 0
 #define TRACE1(fmt, a0)		(void) 0
@@ -2172,7 +2172,7 @@ x_clipboard_manager_error_1 (Lisp_Object err)
 static Lisp_Object
 x_clipboard_manager_error_2 (Lisp_Object err)
 {
-  fprintf (stderr, "Error saving to X clipboard manager.\n\
+  errprintf ("Error saving to X clipboard manager.\n\
 If the problem persists, set '%s' \
 to nil.\n", "x-select-enable-clipboard-manager");
   return Qnil;
diff --git a/src/xsmfns.c b/src/xsmfns.c
index 1706cddf27..e2e544516c 100644
--- a/src/xsmfns.c
+++ b/src/xsmfns.c
@@ -29,10 +29,10 @@ Copyright (C) 2002-2019 Free Software Foundation, Inc.
 #include <unistd.h>
 #include <sys/param.h>
 #include <errno.h>
-#include <stdio.h>
 
 #include "lisp.h"
 #include "frame.h"
+#include "sysstdio.h"
 #include "termhooks.h"
 #include "xterm.h"
 #include "process.h"
@@ -404,8 +404,8 @@ #define SM_ERRORSTRING_LEN 512
   char *pwd = emacs_get_current_dir_name ();
   if (!pwd)
     {
-      fprintf (stderr, "Disabling session management due to pwd error: %s\n",
-               emacs_strerror (errno));
+      errprintf ("Disabling session management due to pwd error: %s\n",
+		 emacs_strerror (errno));
       return;
     }
   xfree (pwd);
diff --git a/src/xterm.c b/src/xterm.c
index 38bc17de97..7fea1946eb 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -21,12 +21,12 @@ Copyright (C) 1989, 1993-2019 Free Software Foundation, Inc.
 /* Xt features made by Fred Pierresteguy.  */
 
 #include <config.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
 
 #include "lisp.h"
 #include "blockinput.h"
+#include "sysstdio.h"
 
 /* This may include sys/types.h, and that somehow loses
    if this is not done before the other system files.  */
@@ -9876,7 +9876,7 @@ x_fully_uncatch_errors (void)
 static unsigned int x_wire_count;
 x_trace_wire (void)
 {
-  fprintf (stderr, "Lib call: %d\n", ++x_wire_count);
+  errprintf ("Lib call: %d\n", ++x_wire_count);
 }
 #endif
 
@@ -9954,11 +9954,11 @@ x_connection_closed (Display *dpy, const char *error_message, bool ioerror)
 	 the resulting Glib error message loop filled a user's disk.
 	 To avoid this, kill Emacs unconditionally on disconnect.  */
       shut_down_emacs (0, Qnil);
-      fprintf (stderr, "%s\n\
+      errprintf ("%s\n\
 When compiled with GTK, Emacs cannot recover from X disconnects.\n\
 This is a GTK bug: https://gitlab.gnome.org/GNOME/gtk/issues/221\n\
 For details, see etc/PROBLEMS.\n",
-	       error_msg);
+		 error_msg);
       emacs_abort ();
 #endif /* USE_GTK */
 
@@ -9980,7 +9980,7 @@ x_connection_closed (Display *dpy, const char *error_message, bool ioerror)
 
   if (terminal_list == 0)
     {
-      fprintf (stderr, "%s\n", error_msg);
+      errprintf ("%s\n", error_msg);
       Fkill_emacs (make_fixnum (70));
       /* NOTREACHED */
     }
@@ -12465,7 +12465,7 @@ my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
 		const gchar *msg, gpointer user_data)
 {
   if (!strstr (msg, "g_set_prgname"))
-      fprintf (stderr, "%s-WARNING **: %s\n", log_domain, msg);
+    errprintf ("%s-WARNING **: %s\n", log_domain, msg);
 }
 #endif
 
@@ -13441,8 +13441,7 @@ x_initialize (void)
 #if THREADS_ENABLED
   /* This must be called before any other Xlib routines.  */
   if (XInitThreads () == 0)
-    fprintf (stderr,
-	     "Warning: An error occurred initializing X11 thread support!\n");
+    errputs ("Warning: An error occurred initializing X11 thread support!\n");
 #endif
 
 #ifdef USE_X_TOOLKIT
-- 
2.21.0


[-- Attachment #3: 0002-Avoid-interleaving-stderr-in-most-of-C-code-Emacs.patch --]
[-- Type: text/x-patch, Size: 5955 bytes --]

From 560b8b7cc2ad44f2d2a6e8d43432ed8b28c14e7e Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Tue, 2 Jul 2019 23:44:12 -0700
Subject: [PATCH 2/4] Avoid interleaving stderr in most of C-code Emacs

To avoid interleaving stderr messages with those of other processes,
line-buffer C-level diagnostics separately where this is safe,
i.e., when various parts of a diagnostic are output right away.
This change does not affect stderr buffering;
that is, the stderr stream still uses its default buffering,
which is typically unbuffered (although POSIX allows line buffering).
* src/pdumper.c (print_paths_to_root_1):
Use errwrite instead of fwrite to stderr; this avoids interleaving.
* src/sysdep.c (buferr): New static var.
(init_standard_fds) [!DOS_NT]: Initialize it.
(errstream): New function.
(errputc, errputs, verrprintf): Use it.
(errwrite): New function.
* src/xdisp.c (message_to_stderr):
Use errwrite instead of fwrite to stderr; this avoids interleaving
in a simpler and better way than allocating a temporary heap buffer.
* src/xdisp.c (vmessage): Simplify; make it more like message_to_stderr.
---
 src/pdumper.c  |  2 +-
 src/sysdep.c   | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
 src/sysstdio.h |  1 +
 src/xdisp.c    | 27 +++++++--------------------
 4 files changed, 53 insertions(+), 26 deletions(-)

diff --git a/src/pdumper.c b/src/pdumper.c
index bfa0dba7f0..ff092e2e49 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -1404,7 +1404,7 @@ print_paths_to_root_1 (struct dump_context *ctx,
       referrers = XCDR (referrers);
       Lisp_Object repr = Fprin1_to_string (referrer, Qnil);
       errprintf ("%*s", level, "");
-      fwrite (SDATA (repr), 1, SBYTES (repr), stderr);
+      errwrite (SDATA (repr), SBYTES (repr));
       errputc ('\n');
       print_paths_to_root_1 (ctx, referrer, level + 1);
     }
diff --git a/src/sysdep.c b/src/sysdep.c
index beccc3c537..2bd7f1afd2 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -231,8 +231,13 @@ 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.  */
+   their file descriptors are not hijacked by later system calls.
+   Initialize buferr too.  */
 void
 init_standard_fds (void)
 {
@@ -243,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
@@ -2767,19 +2781,38 @@ safe_strsignal (int code)
 
   return signame;
 }
+\f
+/* Return the error output stream.  */
 
-/* Output to stderr.  */
+static FILE *
+errstream (void)
+{
+  FILE *err = buferr;
+  if (!err)
+    return stderr;
+  fflush_unlocked (stderr);
+  return err;
+}
+
+/* These functions are like fputc, fputs, fprintf, 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, stderr);
+  fputc_unlocked (c, errstream ());
 }
 
 void
 errputs (char const *str)
 {
-  fputs_unlocked (str, stderr);
+  fputs_unlocked (str, errstream ());
 }
 
 void
@@ -2794,7 +2827,13 @@ errprintf (char const *fmt, ...)
 void
 verrprintf (char const *fmt, va_list ap)
 {
-  vfprintf (stderr, fmt, ap);
+  vfprintf (errstream (), fmt, ap);
+}
+
+void
+errwrite (void const *buf, ptrdiff_t nbuf)
+{
+  fwrite_unlocked (buf, 1, nbuf, errstream ());
 }
 \f
 #ifndef DOS_NT
diff --git a/src/sysstdio.h b/src/sysstdio.h
index ebe0845d40..87568c86fd 100644
--- a/src/sysstdio.h
+++ b/src/sysstdio.h
@@ -29,6 +29,7 @@ #define EMACS_SYSSTDIO_H
 extern void errputs (char const *);
 extern void errprintf (char const *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
 extern void verrprintf (char const *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
+extern void errwrite (void const *, ptrdiff_t);
 
 #if O_BINARY
 # define FOPEN_BINARY "b"
diff --git a/src/xdisp.c b/src/xdisp.c
index 4dbb183bd5..12ab7d9c03 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -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)
+  if (STRINGP (m) || !cursor_in_echo_area)
     errputc ('\n');
-
-  fflush (stderr);
 }
 
 /* The non-logging version of message3.
@@ -10888,12 +10875,12 @@ vmessage (const char *m, va_list ap)
       if (m)
 	{
 	  if (noninteractive_need_newline)
-	    errputc ('\n');
-	  noninteractive_need_newline = false;
+	    {
+	      noninteractive_need_newline = false;
+	      errputc ('\n');
+	    }
 	  verrprintf (m, ap);
-	  if (!cursor_in_echo_area)
-	    errputc ('\n');
-	  fflush (stderr);
+	  errputc ('\n');
 	}
     }
   else if (INTERACTIVE)
-- 
2.21.0


[-- Attachment #4: 0003-Avoid-interleaving-stderr-in-most-of-Lisp-code-Emacs.patch --]
[-- Type: text/x-patch, Size: 7488 bytes --]

From 7350126db02071f7a1dbf2406082c395f9d5073d Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Tue, 2 Jul 2019 23:44:12 -0700
Subject: [PATCH 3/4] Avoid interleaving stderr in most of Lisp-code Emacs

* lisp/startup.el (command-line):
* src/keyboard.c (Fcommand_error_default_function):
* src/print.c (debug_print):
Use line-buffered-debugging-output.
* src/print.c (Fline_buffered_debugging_output): New function.
(init_print_once): Define it.
* src/sysdep.c (errstream): Now extern.
---
 doc/lispref/streams.texi | 15 +++++++++++++--
 etc/NEWS                 |  6 ++++++
 lisp/startup.el          |  6 +++---
 src/keyboard.c           |  4 ++--
 src/print.c              | 21 +++++++++++++++++----
 src/sysdep.c             |  2 +-
 src/sysstdio.h           |  1 +
 7 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/doc/lispref/streams.texi b/doc/lispref/streams.texi
index 600639f244..761c7d40f0 100644
--- a/doc/lispref/streams.texi
+++ b/doc/lispref/streams.texi
@@ -535,11 +535,21 @@ Output Streams
 @defun external-debugging-output character
 This function can be useful as an output stream when debugging.  It
 writes @var{character} to the standard error stream.
+It uses the system default buffering (either unbuffered or line-buffered).
+@end defun
+
+@anchor{line-buffered-debugging-output}
+@defun line-buffered-debugging-output character
+This function can be useful as an output stream when debugging.
+It writes @var{character} to the standard error stream.
+It uses line buffering if available, as this works better than
+unbuffered when processes other than Emacs are simultaneously writing
+to the error stream.
 
 For example
 @example
 @group
-(print "This is the output" #'external-debugging-output)
+(print "This is the output" #'line-buffered-debugging-output)
 @print{} This is the output
 @result{} "This is the output"
 @end group
@@ -587,7 +597,8 @@ Output Functions
 
   In the functions below, @var{stream} stands for an output stream.
 (See the previous section for a description of output streams.  Also
-@xref{external-debugging-output}, a useful stream value for debugging.)
+@xref{external-debugging-output} and @xref{line-buffered-debugging-output},
+useful stream values for debugging.)
 If @var{stream} is @code{nil} or omitted, it defaults to the value of
 @code{standard-output}.
 
diff --git a/etc/NEWS b/etc/NEWS
index abbece374a..1dfec126ed 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2031,6 +2031,12 @@ between two strings.
 ** 'print-quoted' now defaults to t, so if you want to see
 '(quote x)' instead of 'x you will have to bind it to nil where applicable.
 
++++
+** New function line-buffered-debugging-output which outputs to the
+standard error stream using line-buffering than available.  This works
+better than external-debugging-output when processes other than Emacs
+are simultaneously writing to the standard error stream.
+
 +++
 ** Numbers formatted via '%o' or '%x' are now formatted as signed integers.
 This avoids problems in calls like '(read (format "#x%x" -1))', and is
diff --git a/lisp/startup.el b/lisp/startup.el
index 7759ed5aed..977591c02a 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -1016,7 +1016,7 @@ command-line
     ;; Although in most usage we are going to cryptically abort a moment
     ;; later anyway, due to missing required bidi data files (eg bug#13430).
     (if (null simple-file-name)
-	(let ((standard-output 'external-debugging-output)
+	(let ((standard-output 'line-buffered-debugging-output)
 	      (lispdir (expand-file-name "../lisp" data-directory)))
 	  (princ "Warning: Could not find simple.el or simple.elc")
 	  (terpri)
@@ -1244,8 +1244,8 @@ command-line
                   (get (car error) 'error-message)
                   (mapconcat (lambda (obj) (prin1-to-string obj t))
                              (cdr error) ", "))))
-      'external-debugging-output)
-     (terpri 'external-debugging-output)
+      'line-buffered-debugging-output)
+     (terpri 'line-buffered-debugging-output)
      (setq initial-window-system nil)
      (kill-emacs)))
 
diff --git a/src/keyboard.c b/src/keyboard.c
index 56916e0cb4..c635372d15 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1014,9 +1014,9 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
 	   || (!IS_DAEMON && FRAME_INITIAL_P (sf))
 	   || noninteractive)
     {
-      print_error_message (data, Qexternal_debugging_output,
+      print_error_message (data, Qline_buffered_debugging_output,
 			   SSDATA (context), signal);
-      Fterpri (Qexternal_debugging_output, Qnil);
+      Fterpri (Qline_buffered_debugging_output, Qnil);
       Fkill_emacs (make_fixnum (-1));
     }
   else
diff --git a/src/print.c b/src/print.c
index df45cb27ce..012c535644 100644
--- a/src/print.c
+++ b/src/print.c
@@ -771,7 +771,7 @@ DEFUN ("print", Fprint, Sprint, 1, 2, 0,
 }
 
 DEFUN ("external-debugging-output", Fexternal_debugging_output, Sexternal_debugging_output, 1, 1, 0,
-       doc: /* Write CHARACTER to stderr.
+       doc: /* Write CHARACTER to stderr using system-dependent buffering.
 You can call `print' while debugging emacs, and pass it this function
 to make it write to the debugging output.  */)
   (Lisp_Object character)
@@ -781,6 +781,18 @@ DEFUN ("external-debugging-output", Fexternal_debugging_output, Sexternal_debugg
   return character;
 }
 
+DEFUN ("line-buffered-debugging-output", Fline_buffered_debugging_output,
+       Sline_buffered_debugging_output, 1, 1, 0,
+       doc: /* Write CHARACTER to stderr using line-buffering if possible.
+You can call `print' while debugging emacs, and pass it this function
+to make it write to the debugging output.  */)
+  (Lisp_Object character)
+{
+  CHECK_FIXNUM (character);
+  printchar_to_stream (XFIXNUM (character), errstream ());
+  return character;
+}
+
 /* This function is never called.  Its purpose is to prevent
    print_output_debug_flag from being optimized away.  */
 
@@ -839,7 +851,7 @@ DEFUN ("redirect-debugging-output", Fredirect_debugging_output, Sredirect_debugg
 void
 debug_print (Lisp_Object arg)
 {
-  Fprin1 (arg, Qexternal_debugging_output);
+  Fprin1 (arg, Qline_buffered_debugging_output);
   errputs ("\r\n");
 }
 
@@ -2206,11 +2218,12 @@ print_interval (INTERVAL interval, Lisp_Object printcharfun)
 void
 init_print_once (void)
 {
-  /* The subroutine object for external-debugging-output is kept here
-     for the convenience of the debugger.  */
+  /* These subroutine objects are kept here for debugger convenience.  */
   DEFSYM (Qexternal_debugging_output, "external-debugging-output");
+  DEFSYM (Qline_buffered_debugging_output, "line-buffered-debugging-output");
 
   defsubr (&Sexternal_debugging_output);
+  defsubr (&Sline_buffered_debugging_output);
 }
 
 void
diff --git a/src/sysdep.c b/src/sysdep.c
index 2bd7f1afd2..cf7699d43e 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2784,7 +2784,7 @@ safe_strsignal (int code)
 \f
 /* Return the error output stream.  */
 
-static FILE *
+FILE *
 errstream (void)
 {
   FILE *err = buferr;
diff --git a/src/sysstdio.h b/src/sysstdio.h
index 87568c86fd..966a5abd7f 100644
--- a/src/sysstdio.h
+++ b/src/sysstdio.h
@@ -25,6 +25,7 @@ #define EMACS_SYSSTDIO_H
 
 extern FILE *emacs_fopen (char const *, char const *);
 
+extern FILE *errstream (void);
 extern void errputc (int);
 extern void errputs (char const *);
 extern void errprintf (char const *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
-- 
2.21.0


[-- Attachment #5: 0004-Simplify-emacs_perror.patch --]
[-- Type: text/x-patch, Size: 1834 bytes --]

From 6adaa706cbf4da6c45782b5107a6a3fa423c8487 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Tue, 2 Jul 2019 23:44:12 -0700
Subject: [PATCH 4/4] Simplify emacs_perror

* src/sysdep.c (emacs_perror): Simplify by using errprintf.
---
 src/sysdep.c | 19 ++-----------------
 1 file changed, 2 insertions(+), 17 deletions(-)

diff --git a/src/sysdep.c b/src/sysdep.c
index cf7699d43e..640d4908e1 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2701,7 +2701,7 @@ emacs_write_quit (int fd, void const *buf, ptrdiff_t nbyte)
 }
 
 /* Write a diagnostic to standard error that contains MESSAGE and a
-   string derived from errno.  Preserve errno.  Do not buffer stderr.
+   string derived from errno.  Preserve errno.  Flush the output.
    Do not process quits or pending signals if interrupted.  */
 void
 emacs_perror (char const *message)
@@ -2710,22 +2710,7 @@ emacs_perror (char const *message)
   char const *error_string = emacs_strerror (err);
   char const *command = (initial_argv && initial_argv[0]
 			 ? initial_argv[0] : "emacs");
-  /* Write it out all at once, if it's short; this is less likely to
-     be interleaved with other output.  */
-  char buf[BUFSIZ];
-  int nbytes = snprintf (buf, sizeof buf, "%s: %s: %s\n",
-			 command, message, error_string);
-  if (0 <= nbytes && nbytes < BUFSIZ)
-    emacs_write (STDERR_FILENO, buf, nbytes);
-  else
-    {
-      emacs_write (STDERR_FILENO, command, strlen (command));
-      emacs_write (STDERR_FILENO, ": ", 2);
-      emacs_write (STDERR_FILENO, message, strlen (message));
-      emacs_write (STDERR_FILENO, ": ", 2);
-      emacs_write (STDERR_FILENO, error_string, strlen (error_string));
-      emacs_write (STDERR_FILENO, "\n", 1);
-    }
+  errprintf ("%s: %s: %s\n", command, message, error_string);
   errno = err;
 }
 \f
-- 
2.21.0


  parent reply	other threads:[~2019-07-03  7:31 UTC|newest]

Thread overview: 108+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-06-19 14:12 `message' not outputting the newline "atomically" Lars Ingebrigtsen
2019-06-19 14:28 ` Andreas Schwab
2019-06-19 15:41 ` Eli Zaretskii
2019-06-19 15:47   ` Lars Ingebrigtsen
2019-06-19 16:05     ` Andreas Schwab
2019-06-19 23:22       ` Paul Eggert
2019-06-20  2:35         ` Eli Zaretskii
2019-06-20  7:47           ` Paul Eggert
2019-06-20  9:35             ` Lars Ingebrigtsen
2019-06-20 12:52             ` Eli Zaretskii
2019-06-20 12:55               ` Lars Ingebrigtsen
2019-06-20 13:13                 ` Eli Zaretskii
2019-06-20 14:05                 ` Andreas Schwab
2019-06-20 16:26               ` Paul Eggert
2019-06-20 16:45                 ` Eli Zaretskii
2019-06-20 17:41                   ` Paul Eggert
2019-06-20 18:06                     ` Eli Zaretskii
2019-06-20 19:33                       ` Paul Eggert
2019-06-21  5:46                         ` Eli Zaretskii
2019-06-21  6:06                           ` Eli Zaretskii
2019-06-22  0:20                           ` Paul Eggert
2019-06-22  7:32                             ` Eli Zaretskii
2019-06-22 19:14                               ` Paul Eggert
2019-06-23  2:25                                 ` Eli Zaretskii
2019-06-23  8:34                                   ` Paul Eggert
2019-06-23 11:37                                     ` Lars Ingebrigtsen
2019-06-23 14:47                                     ` Eli Zaretskii
2019-06-23 17:32                                       ` Paul Eggert
2019-06-23 18:28                                         ` Eli Zaretskii
2019-06-23 12:53                                   ` Stefan Monnier
2019-06-23 14:51                                     ` Eli Zaretskii
2019-06-24  4:09                                       ` Stefan Monnier
2019-06-22  8:26                             ` Andreas Schwab
2019-06-22 18:53                               ` Paul Eggert
2019-06-22 19:00                                 ` Eli Zaretskii
2019-06-22 19:15                                   ` Paul Eggert
2019-06-22 19:48                                 ` Andreas Schwab
2019-06-20 13:32             ` Stefan Monnier
2019-06-20 16:28               ` Paul Eggert
2019-06-23 18:59                 ` Daniele Nicolodi
2019-06-23 20:34                   ` Paul Eggert
2019-06-23 20:42                     ` Lars Ingebrigtsen
2019-06-23 21:00                       ` Paul Eggert
2019-06-23 22:18                         ` Lars Ingebrigtsen
2019-06-23 20:48                     ` Daniele Nicolodi
2019-06-24  2:32                     ` Eli Zaretskii
2019-06-24  2:51                     ` HaiJun Zhang
2019-06-24 19:48 ` Lars Ingebrigtsen
2019-06-24 20:03   ` Daniele Nicolodi
2019-06-24 20:17     ` Lars Ingebrigtsen
2019-06-24 21:11       ` Paul Eggert
2019-06-24 21:33         ` Lars Ingebrigtsen
2019-06-24 22:03           ` Paul Eggert
2019-06-24 22:06             ` Paul Eggert
2019-06-24 22:28             ` Lars Ingebrigtsen
2019-06-24 22:47               ` Lars Ingebrigtsen
2019-06-25 16:03                 ` Eli Zaretskii
2019-06-26  9:15                   ` Lars Ingebrigtsen
2019-06-26 15:22                     ` Eli Zaretskii
2019-06-27 10:52                       ` Lars Ingebrigtsen
2019-06-26 18:27                   ` Paul Eggert
2019-06-26 18:41                     ` Eli Zaretskii
2019-06-26 18:58                       ` Paul Eggert
2019-06-26 19:11                         ` Eli Zaretskii
2019-06-26 19:36                           ` Daniele Nicolodi
2019-06-27  2:34                             ` Eli Zaretskii
2019-06-27  5:43                               ` Paul Eggert
2019-06-30 20:11                               ` Daniele Nicolodi
2019-07-01  7:41                               ` Daniele Nicolodi
2019-07-01 14:39                                 ` Eli Zaretskii
2019-07-01 17:01                                   ` Daniele Nicolodi
2019-07-02  2:28                                     ` Eli Zaretskii
2019-07-02  7:58                                       ` Daniele Nicolodi
2019-07-02 14:47                                         ` Eli Zaretskii
2019-07-02 20:56                                           ` Daniele Nicolodi
2019-07-03  5:23                                             ` Eli Zaretskii
2019-07-01 17:03                                   ` Daniele Nicolodi
2019-07-02  2:26                                     ` Eli Zaretskii
2019-06-26 19:38                           ` Paul Eggert
2019-06-25 16:06             ` Eli Zaretskii
2019-06-26  9:21               ` Lars Ingebrigtsen
2019-06-26 15:23                 ` Eli Zaretskii
2019-06-27 11:03                   ` Lars Ingebrigtsen
2019-06-27 13:31                     ` Eli Zaretskii
2019-06-28  8:30                       ` Lars Ingebrigtsen
2019-07-03  7:31                       ` Paul Eggert [this message]
2019-07-03  7:41                         ` Eli Zaretskii
2019-07-03  7:47                           ` Eli Zaretskii
2019-07-03  7:57                             ` Eli Zaretskii
2019-07-03  8:45                               ` Paul Eggert
2019-07-03  9:30                                 ` Eli Zaretskii
2019-07-03 23:08                                   ` Paul Eggert
2019-07-04 13:24                                     ` Eli Zaretskii
2019-07-07  1:16                                       ` Paul Eggert
2019-07-07 14:51                                         ` Eli Zaretskii
2019-07-08 22:35                                           ` Richard Copley
2019-07-09  2:33                                             ` Eli Zaretskii
2019-07-09 13:45                                               ` Richard Copley
2019-07-09 15:16                                                 ` Eli Zaretskii
2019-07-09  2:47                                           ` Paul Eggert
2019-07-09 16:39                                             ` Eli Zaretskii
2019-07-09 18:12                                               ` Paul Eggert
2019-07-09 18:32                                                 ` Eli Zaretskii
2019-07-09 18:44                                                   ` Lars Ingebrigtsen
2019-07-09 19:17                                                     ` Eli Zaretskii
2019-07-14  0:42                                                   ` Paul Eggert
2019-07-14  6:01                                                     ` Eli Zaretskii
2019-06-25 16:08         ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7a39d680-6234-1301-74e5-62d599f500f6@cs.ucla.edu \
    --to=eggert@cs.ucla.edu \
    --cc=daniele@grinta.net \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    --cc=larsi@gnus.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).