diff --git a/src/include/port.h b/src/include/port.h
index f9b4a16c0af647d028fb240ff14cbcc2773b8440..3f187159cb3421b0f0e13a27cf9394a498a44078 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -126,12 +126,11 @@ extern unsigned char pg_tolower(unsigned char ch);
 extern unsigned char pg_ascii_toupper(unsigned char ch);
 extern unsigned char pg_ascii_tolower(unsigned char ch);
 
-#ifdef USE_REPL_SNPRINTF
-
 /*
- * Versions of libintl >= 0.13 try to replace printf() and friends with
- * macros to their own versions that understand the %$ format.  We do the
- * same, so disable their macros, if they exist.
+ * Capture macro-compatible calls to printf() and friends, and redirect them
+ * to wrappers that throw errors in lieu of reporting failure in a return
+ * value.  Versions of libintl >= 0.13 similarly redirect to versions that
+ * understand the %$ format, so disable libintl macros first.
  */
 #ifdef vsnprintf
 #undef vsnprintf
@@ -155,36 +154,63 @@ extern unsigned char pg_ascii_tolower(unsigned char ch);
 #undef printf
 #endif
 
-extern int	pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args);
-extern int	pg_snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4);
-extern int	pg_vsprintf(char *str, const char *fmt, va_list args);
-extern int	pg_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2, 3);
-extern int	pg_vfprintf(FILE *stream, const char *fmt, va_list args);
-extern int	pg_fprintf(FILE *stream, const char *fmt,...) pg_attribute_printf(2, 3);
-extern int	pg_printf(const char *fmt,...) pg_attribute_printf(1, 2);
+extern int
+vsnprintf_throw_on_fail(char *str, size_t count, const char *fmt, va_list args)
+pg_attribute_printf(3, 0);
+extern int
+snprintf_throw_on_fail(char *str, size_t count, const char *fmt,...)
+pg_attribute_printf(3, 4);
+extern int
+vsprintf_throw_on_fail(char *str, const char *fmt, va_list args)
+pg_attribute_printf(2, 0);
+extern int
+sprintf_throw_on_fail(char *str, const char *fmt,...)
+pg_attribute_printf(2, 3);
+extern int
+vfprintf_throw_on_fail(FILE *stream, const char *fmt, va_list args)
+pg_attribute_printf(2, 0);
+extern int
+fprintf_throw_on_fail(FILE *stream, const char *fmt,...)
+pg_attribute_printf(2, 3);
+extern int
+printf_throw_on_fail(const char *fmt,...)
+pg_attribute_printf(1, 2);
 
 /*
  *	The GCC-specific code below prevents the pg_attribute_printf above from
  *	being replaced, and this is required because gcc doesn't know anything
- *	about pg_printf.
+ *	about printf_throw_on_fail.
  */
 #ifdef __GNUC__
-#define vsnprintf(...)	pg_vsnprintf(__VA_ARGS__)
-#define snprintf(...)	pg_snprintf(__VA_ARGS__)
-#define vsprintf(...)	pg_vsprintf(__VA_ARGS__)
-#define sprintf(...)	pg_sprintf(__VA_ARGS__)
-#define vfprintf(...)	pg_vfprintf(__VA_ARGS__)
-#define fprintf(...)	pg_fprintf(__VA_ARGS__)
-#define printf(...)		pg_printf(__VA_ARGS__)
+#define vsnprintf(...)	vsnprintf_throw_on_fail(__VA_ARGS__)
+#define snprintf(...)	snprintf_throw_on_fail(__VA_ARGS__)
+#define vsprintf(...)	vsprintf_throw_on_fail(__VA_ARGS__)
+#define sprintf(...)	sprintf_throw_on_fail(__VA_ARGS__)
+#define vfprintf(...)	vfprintf_throw_on_fail(__VA_ARGS__)
+#define fprintf(...)	fprintf_throw_on_fail(__VA_ARGS__)
+#define printf(...)		printf_throw_on_fail(__VA_ARGS__)
 #else
-#define vsnprintf		pg_vsnprintf
-#define snprintf		pg_snprintf
-#define vsprintf		pg_vsprintf
-#define sprintf			pg_sprintf
-#define vfprintf		pg_vfprintf
-#define fprintf			pg_fprintf
-#define printf			pg_printf
+#define vsnprintf		vsnprintf_throw_on_fail
+#define snprintf		snprintf_throw_on_fail
+#define vsprintf		vsprintf_throw_on_fail
+#define sprintf			sprintf_throw_on_fail
+#define vfprintf		vfprintf_throw_on_fail
+#define fprintf			fprintf_throw_on_fail
+#define printf			printf_throw_on_fail
 #endif
+
+#ifdef USE_REPL_SNPRINTF
+
+/* Code outside syswrap.c should not call these. */
+
+extern int	pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args);
+extern int	pg_snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4);
+extern int	pg_vsprintf(char *str, const char *fmt, va_list args);
+extern int	pg_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2, 3);
+extern int	pg_vfprintf(FILE *stream, const char *fmt, va_list args);
+extern int	pg_fprintf(FILE *stream, const char *fmt,...) pg_attribute_printf(2, 3);
+extern int	pg_printf(const char *fmt,...) pg_attribute_printf(1, 2);
+
 #endif   /* USE_REPL_SNPRINTF */
 
 #if defined(WIN32)
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index ed52bff01ed229954c0de032f51476cfd8cb3dd0..fcbddbf5812f8fab01b6332874af70fe88e32a24 100644
--- a/src/interfaces/ecpg/compatlib/Makefile
+++ b/src/interfaces/ecpg/compatlib/Makefile
@@ -48,6 +48,7 @@ submake-pgtypeslib:
 # Shared library stuff
 include $(top_srcdir)/src/Makefile.shlib
 
+# XXX This library uses no symbols from snprintf.c.
 snprintf.c: % : $(top_srcdir)/src/port/%
 	rm -f $@ && $(LN_S) $< .
 
diff --git a/src/interfaces/ecpg/ecpglib/.gitignore b/src/interfaces/ecpg/ecpglib/.gitignore
index 8ef6401dd0ebc31acc061303cc2f1f11d99f04f3..c28ac74fa9aa50424aa7259a721b3a96a9865f20 100644
--- a/src/interfaces/ecpg/ecpglib/.gitignore
+++ b/src/interfaces/ecpg/ecpglib/.gitignore
@@ -5,6 +5,7 @@
 /pgstrcasecmp.c
 /snprintf.c
 /strlcpy.c
+/syswrap.c
 /thread.c
 /win32setlocale.c
 /isinf.c
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index a4ec8c80e6a5d0c66a914c06d89d521fa161a010..35791168d91a8a4d31292c71bc2eb261cef2dd13 100644
--- a/src/interfaces/ecpg/ecpglib/Makefile
+++ b/src/interfaces/ecpg/ecpglib/Makefile
@@ -26,7 +26,7 @@ override CFLAGS += $(PTHREAD_CFLAGS)
 LIBS := $(filter-out -lpgport, $(LIBS))
 
 OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
-	connect.o misc.o path.o pgstrcasecmp.o \
+	connect.o misc.o path.o pgstrcasecmp.o syswrap.o \
 	$(filter snprintf.o strlcpy.o win32setlocale.o isinf.o, $(LIBOBJS)) $(WIN32RES)
 
 # thread.c is needed only for non-WIN32 implementation of path.c
@@ -55,7 +55,7 @@ include $(top_srcdir)/src/Makefile.shlib
 # necessarily use the same object files as the backend uses. Instead,
 # symlink the source files in here and build our own object file.
 
-path.c pgstrcasecmp.c snprintf.c strlcpy.c thread.c win32setlocale.c isinf.c: % : $(top_srcdir)/src/port/%
+path.c pgstrcasecmp.c snprintf.c strlcpy.c syswrap.c thread.c win32setlocale.c isinf.c: % : $(top_srcdir)/src/port/%
 	rm -f $@ && $(LN_S) $< .
 
 misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
@@ -72,6 +72,6 @@ uninstall: uninstall-lib
 
 clean distclean: clean-lib
 	rm -f $(OBJS)
-	rm -f path.c pgstrcasecmp.c snprintf.c strlcpy.c thread.c win32setlocale.c isinf.c
+	rm -f path.c pgstrcasecmp.c snprintf.c strlcpy.c syswrap.c thread.c win32setlocale.c isinf.c
 
 maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/pgtypeslib/.gitignore b/src/interfaces/ecpg/pgtypeslib/.gitignore
index fbcd68d7d3efd22a7116cc8e3bf31d119a3a7fa8..e33c94d81f6bd66742030f6ce385e18431a3f9c7 100644
--- a/src/interfaces/ecpg/pgtypeslib/.gitignore
+++ b/src/interfaces/ecpg/pgtypeslib/.gitignore
@@ -4,3 +4,4 @@
 /pgstrcasecmp.c
 /rint.c
 /snprintf.c
+/syswrap.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 6c7ae63d4e2f6b2a601e2643f80d68e577c45734..830f47074f53abfd89933a6280a6014208e49a13 100644
--- a/src/interfaces/ecpg/pgtypeslib/Makefile
+++ b/src/interfaces/ecpg/pgtypeslib/Makefile
@@ -30,7 +30,7 @@ SHLIB_LINK += -lm
 SHLIB_EXPORTS = exports.txt
 
 OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
-	pgstrcasecmp.o \
+	pgstrcasecmp.o syswrap.o \
 	$(filter rint.o snprintf.o, $(LIBOBJS)) $(WIN32RES)
 
 all: all-lib
@@ -43,7 +43,7 @@ include $(top_srcdir)/src/Makefile.shlib
 # necessarily use the same object files as the backend uses. Instead,
 # symlink the source files in here and build our own object file.
 
-pgstrcasecmp.c rint.c snprintf.c: % : $(top_srcdir)/src/port/%
+pgstrcasecmp.c rint.c snprintf.c syswrap.c: % : $(top_srcdir)/src/port/%
 	rm -f $@ && $(LN_S) $< .
 
 install: all installdirs install-lib
@@ -53,6 +53,6 @@ installdirs: installdirs-lib
 uninstall: uninstall-lib
 
 clean distclean: clean-lib
-	rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c
+	rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c syswrap.c
 
 maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/libpq/.gitignore b/src/interfaces/libpq/.gitignore
index cb96af717665e776bb4c5d06f86263a9abcae895..5e672f1ae1fdd764f11fdb4e3774d516955c5f6f 100644
--- a/src/interfaces/libpq/.gitignore
+++ b/src/interfaces/libpq/.gitignore
@@ -13,6 +13,7 @@
 /strerror.c
 /strlcpy.c
 /system.c
+/syswrap.c
 /thread.c
 /win32error.c
 /win32setlocale.c
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 6973a204840745a2e08806b7a43902ff2c7e003b..c0afa89161cd83e910ad37310326bba39a41b47f 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -36,7 +36,7 @@ OBJS=	fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
 	libpq-events.o
 # libpgport C files we always use
 OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
-	thread.o
+	syswrap.o thread.o
 # libpgport C files that are needed if identified by configure
 OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strerror.o strlcpy.o win32error.o win32setlocale.o, $(LIBOBJS))
 # backend/libpq
@@ -93,7 +93,7 @@ backend_src = $(top_srcdir)/src/backend
 # For some libpgport modules, this only happens if configure decides
 # the module is needed (see filter hack in OBJS, above).
 
-chklocale.c crypt.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c thread.c win32error.c win32setlocale.c: % : $(top_srcdir)/src/port/%
+chklocale.c crypt.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c syswrap.c thread.c win32error.c win32setlocale.c: % : $(top_srcdir)/src/port/%
 	rm -f $@ && $(LN_S) $< .
 
 ip.c md5.c: % : $(backend_src)/libpq/%
@@ -145,7 +145,7 @@ clean distclean: clean-lib
 # Might be left over from a Win32 client-only build
 	rm -f pg_config_paths.h
 	rm -f inet_net_ntop.c noblock.c pgstrcasecmp.c pqsignal.c thread.c
-	rm -f chklocale.c crypt.c getaddrinfo.c getpeereid.c inet_aton.c open.c system.c snprintf.c strerror.c strlcpy.c win32error.c win32setlocale.c
+	rm -f chklocale.c crypt.c getaddrinfo.c getpeereid.c inet_aton.c open.c system.c snprintf.c strerror.c strlcpy.c syswrap.c win32error.c win32setlocale.c
 	rm -f pgsleep.c
 	rm -f md5.c ip.c
 	rm -f encnames.c wchar.c
diff --git a/src/interfaces/libpq/bcc32.mak b/src/interfaces/libpq/bcc32.mak
index 78102fafd45c948a93bff59100677ddd728fdf53..9bb577a0ed3d4fa2e1fddb81c85995b17444353c 100644
--- a/src/interfaces/libpq/bcc32.mak
+++ b/src/interfaces/libpq/bcc32.mak
@@ -107,6 +107,7 @@ CLEAN :
 	-@erase "$(INTDIR)\pgsleep.obj"
 	-@erase "$(INTDIR)\open.obj"
 	-@erase "$(INTDIR)\system.obj"
+	-@erase "$(INTDIR)\syswrap.obj"
 	-@erase "$(INTDIR)\win32error.obj"
 	-@erase "$(OUTDIR)\$(OUTFILENAME).lib"
 	-@erase "$(OUTDIR)\$(OUTFILENAME)dll.lib"
@@ -151,6 +152,7 @@ LIB32_OBJS= \
 	"$(INTDIR)\pgsleep.obj" \
 	"$(INTDIR)\open.obj" \
 	"$(INTDIR)\system.obj" \
+	"$(INTDIR)\syswrap.obj" \
 	"$(INTDIR)\win32error.obj" \
 	"$(INTDIR)\pthread-win32.obj"
 
@@ -302,6 +304,11 @@ LINK32_FLAGS = -Gn -L$(BCB)\lib;$(INTDIR); -x -Tpd -v
 	$(CPP_PROJ) /I"." ..\..\port\system.c
 <<
 
+"$(INTDIR)\syswrap.obj" : ..\..\port\syswrap.c
+	$(CPP) @<<
+	$(CPP_PROJ) ..\..\port\syswrap.c
+<<
+
 "$(INTDIR)\win32error.obj" : ..\..\port\win32error.c
 	$(CPP) @<<
 	$(CPP_PROJ) /I"." ..\..\port\win32error.c
diff --git a/src/interfaces/libpq/win32.mak b/src/interfaces/libpq/win32.mak
index 1b71ebd387072bb4f59aa27592fb9f7a8cbf54b5..b05fce82ccd8c6431bdfdcccf86c3185f5d7748e 100644
--- a/src/interfaces/libpq/win32.mak
+++ b/src/interfaces/libpq/win32.mak
@@ -114,6 +114,7 @@ CLEAN :
 	-@erase "$(INTDIR)\pgsleep.obj"
 	-@erase "$(INTDIR)\open.obj"
 	-@erase "$(INTDIR)\system.obj"
+	-@erase "$(INTDIR)\syswrap.obj"
 	-@erase "$(INTDIR)\win32error.obj"
 	-@erase "$(INTDIR)\win32setlocale.obj"
 	-@erase "$(OUTDIR)\$(OUTFILENAME).lib"
@@ -164,6 +165,7 @@ LIB32_OBJS= \
 	"$(INTDIR)\pgsleep.obj" \
 	"$(INTDIR)\open.obj" \
 	"$(INTDIR)\system.obj" \
+	"$(INTDIR)\syswrap.obj" \
 	"$(INTDIR)\win32error.obj" \
 	"$(INTDIR)\win32setlocale.obj" \
 	"$(INTDIR)\pthread-win32.obj"
@@ -348,6 +350,11 @@ LINK32_OBJS= \
 	$(CPP_PROJ) /I"." ..\..\port\system.c
 <<
 
+"$(INTDIR)\syswrap.obj" : ..\..\port\syswrap.c
+	$(CPP) @<<
+	$(CPP_PROJ) ..\..\port\syswrap.c
+<<
+
 "$(INTDIR)\win32error.obj" : ..\..\port\win32error.c
 	$(CPP) @<<
 	$(CPP_PROJ) /I"." ..\..\port\win32error.c
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 813d4401bbbf7b56bbeab552c273a3346afc5a7b..cc748ec3c0e8062fdee8ee3ec16cf29f2fa55517 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -37,10 +37,8 @@
  * So we undefine them here and redefine them after it's done its dirty deed.
  */
 
-#ifdef USE_REPL_SNPRINTF
 #undef snprintf
 #undef vsnprintf
-#endif
 
 
 /* required for perl API */
@@ -49,7 +47,6 @@
 #include "XSUB.h"
 
 /* put back our snprintf and vsnprintf */
-#ifdef USE_REPL_SNPRINTF
 #ifdef snprintf
 #undef snprintf
 #endif
@@ -57,13 +54,12 @@
 #undef vsnprintf
 #endif
 #ifdef __GNUC__
-#define vsnprintf(...)	pg_vsnprintf(__VA_ARGS__)
-#define snprintf(...)	pg_snprintf(__VA_ARGS__)
+#define vsnprintf(...)	vsnprintf_throw_on_fail(__VA_ARGS__)
+#define snprintf(...)	snprintf_throw_on_fail(__VA_ARGS__)
 #else
-#define vsnprintf		pg_vsnprintf
-#define snprintf		pg_snprintf
+#define vsnprintf		vsnprintf_throw_on_fail
+#define snprintf		snprintf_throw_on_fail
 #endif   /* __GNUC__ */
-#endif   /* USE_REPL_SNPRINTF */
 
 /* perl version and platform portability */
 #define NEED_eval_pv
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index ea540af39e38666d15f1f98a46b567500c25f770..0f60af68363867ca34842b810dfde1831511c6c1 100644
--- a/src/pl/plpython/plpython.h
+++ b/src/pl/plpython/plpython.h
@@ -35,10 +35,8 @@
  * So we undefine them here and redefine them after it's done its dirty deed.
  */
 
-#ifdef USE_REPL_SNPRINTF
 #undef snprintf
 #undef vsnprintf
-#endif
 
 #if defined(_MSC_VER) && defined(_DEBUG)
 /* Python uses #pragma to bring in a non-default libpython on VC++ if
@@ -125,7 +123,6 @@ typedef int Py_ssize_t;
 #include <eval.h>
 
 /* put back our snprintf and vsnprintf */
-#ifdef USE_REPL_SNPRINTF
 #ifdef snprintf
 #undef snprintf
 #endif
@@ -133,13 +130,12 @@ typedef int Py_ssize_t;
 #undef vsnprintf
 #endif
 #ifdef __GNUC__
-#define vsnprintf(...)	pg_vsnprintf(__VA_ARGS__)
-#define snprintf(...)	pg_snprintf(__VA_ARGS__)
+#define vsnprintf(...)	vsnprintf_throw_on_fail(__VA_ARGS__)
+#define snprintf(...)	snprintf_throw_on_fail(__VA_ARGS__)
 #else
-#define vsnprintf				pg_vsnprintf
-#define snprintf				pg_snprintf
+#define vsnprintf		vsnprintf_throw_on_fail
+#define snprintf		snprintf_throw_on_fail
 #endif   /* __GNUC__ */
-#endif   /* USE_REPL_SNPRINTF */
 
 /*
  * Used throughout, and also by the Python 2/3 porting layer, so it's easier to
diff --git a/src/port/Makefile b/src/port/Makefile
index bc9b63add0479459d146550c0338e1a22a478400..b0fc56ac455bbb5b8858397a79daff8f476f03c1 100644
--- a/src/port/Makefile
+++ b/src/port/Makefile
@@ -33,7 +33,7 @@ LIBS += $(PTHREAD_LIBS)
 OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
 	noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
 	pgstrcasecmp.o pqsignal.o \
-	qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o
+	qsort.o qsort_arg.o quotes.o sprompt.o syswrap.o tar.o thread.o
 
 # foo_srv.o and foo.o are both built from foo.c, but only foo.o has -DFRONTEND
 OBJS_SRV = $(OBJS:%.o=%_srv.o)
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 0c779a601fcfc93b723f1a696adce8923565fde3..91c97d487cdb388129b4831f75e60cce319ad131 100644
--- a/src/port/snprintf.c
+++ b/src/port/snprintf.c
@@ -114,6 +114,7 @@ typedef struct
 	/* bufend == NULL is for sprintf, where we assume buf is big enough */
 	FILE	   *stream;			/* eventual output destination, or NULL */
 	int			nchars;			/* # chars already sent to stream */
+	bool		failed;			/* call is a failure; errno is set */
 } PrintfTarget;
 
 /*
@@ -143,7 +144,7 @@ typedef union
 
 
 static void flushbuffer(PrintfTarget *target);
-static int	dopr(PrintfTarget *target, const char *format, va_list args);
+static void dopr(PrintfTarget *target, const char *format, va_list args);
 
 
 int
@@ -157,14 +158,10 @@ pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
 	target.bufend = str + count - 1;
 	target.stream = NULL;
 	/* target.nchars is unused in this case */
-	if (dopr(&target, fmt, args))
-	{
-		*(target.bufptr) = '\0';
-		errno = EINVAL;			/* bad format */
-		return -1;
-	}
+	target.failed = false;
+	dopr(&target, fmt, args);
 	*(target.bufptr) = '\0';
-	return target.bufptr - target.bufstart;
+	return target.failed ? -1 : (target.bufptr - target.bufstart);
 }
 
 int
@@ -190,14 +187,10 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
 	target.bufend = NULL;
 	target.stream = NULL;
 	/* target.nchars is unused in this case */
-	if (dopr(&target, fmt, args))
-	{
-		*(target.bufptr) = '\0';
-		errno = EINVAL;			/* bad format */
-		return -1;
-	}
+	target.failed = false;
+	dopr(&target, fmt, args);
 	*(target.bufptr) = '\0';
-	return target.bufptr - target.bufstart;
+	return target.failed ? -1 : (target.bufptr - target.bufstart);
 }
 
 int
@@ -227,14 +220,11 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
 	target.bufend = buffer + sizeof(buffer) - 1;
 	target.stream = stream;
 	target.nchars = 0;
-	if (dopr(&target, fmt, args))
-	{
-		errno = EINVAL;			/* bad format */
-		return -1;
-	}
+	target.failed = false;
+	dopr(&target, fmt, args);
 	/* dump any remaining buffer contents */
 	flushbuffer(&target);
-	return target.nchars;
+	return target.failed ? -1 : target.nchars;
 }
 
 int
@@ -261,14 +251,24 @@ pg_printf(const char *fmt,...)
 	return len;
 }
 
-/* call this only when stream is defined */
+/*
+ * Attempt to write the entire buffer to target->stream; discard the entire
+ * buffer in any case.  Call this only when target->stream is defined.
+ */
 static void
 flushbuffer(PrintfTarget *target)
 {
 	size_t		nc = target->bufptr - target->bufstart;
 
-	if (nc > 0)
-		target->nchars += fwrite(target->bufstart, 1, nc, target->stream);
+	if (!target->failed && nc > 0)
+	{
+		size_t		written;
+
+		written = fwrite(target->bufstart, 1, nc, target->stream);
+		target->nchars += written;
+		if (written != nc)
+			target->failed = true;
+	}
 	target->bufptr = target->bufstart;
 }
 
@@ -295,7 +295,7 @@ static void trailing_pad(int *padlen, PrintfTarget *target);
 /*
  * dopr(): poor man's version of doprintf
  */
-static int
+static void
 dopr(PrintfTarget *target, const char *format, va_list args)
 {
 	const char *format_start = format;
@@ -372,12 +372,12 @@ nextch1:
 			case '$':
 				have_dollar = true;
 				if (accum <= 0 || accum > NL_ARGMAX)
-					return -1;
+					goto bad_format;
 				if (afterstar)
 				{
 					if (argtypes[accum] &&
 						argtypes[accum] != ATYPE_INT)
-						return -1;
+						goto bad_format;
 					argtypes[accum] = ATYPE_INT;
 					last_dollar = Max(last_dollar, accum);
 					afterstar = false;
@@ -427,7 +427,7 @@ nextch1:
 						atype = ATYPE_INT;
 					if (argtypes[fmtpos] &&
 						argtypes[fmtpos] != atype)
-						return -1;
+						goto bad_format;
 					argtypes[fmtpos] = atype;
 					last_dollar = Max(last_dollar, fmtpos);
 				}
@@ -439,7 +439,7 @@ nextch1:
 				{
 					if (argtypes[fmtpos] &&
 						argtypes[fmtpos] != ATYPE_INT)
-						return -1;
+						goto bad_format;
 					argtypes[fmtpos] = ATYPE_INT;
 					last_dollar = Max(last_dollar, fmtpos);
 				}
@@ -452,7 +452,7 @@ nextch1:
 				{
 					if (argtypes[fmtpos] &&
 						argtypes[fmtpos] != ATYPE_CHARPTR)
-						return -1;
+						goto bad_format;
 					argtypes[fmtpos] = ATYPE_CHARPTR;
 					last_dollar = Max(last_dollar, fmtpos);
 				}
@@ -468,7 +468,7 @@ nextch1:
 				{
 					if (argtypes[fmtpos] &&
 						argtypes[fmtpos] != ATYPE_DOUBLE)
-						return -1;
+						goto bad_format;
 					argtypes[fmtpos] = ATYPE_DOUBLE;
 					last_dollar = Max(last_dollar, fmtpos);
 				}
@@ -489,7 +489,7 @@ nextch1:
 
 	/* Per spec, you use either all dollar or all not. */
 	if (have_dollar && have_non_dollar)
-		return -1;
+		goto bad_format;
 
 	/*
 	 * In dollar mode, collect the arguments in physical order.
@@ -499,7 +499,7 @@ nextch1:
 		switch (argtypes[i])
 		{
 			case ATYPE_NONE:
-				return -1;		/* invalid format */
+				goto bad_format;
 			case ATYPE_INT:
 				argvalues[i].i = va_arg(args, int);
 				break;
@@ -524,6 +524,9 @@ nextch1:
 	format = format_start;
 	while ((ch = *format++) != '\0')
 	{
+		if (target->failed)
+			break;
+
 		if (ch != '%')
 		{
 			dopr_outch(ch, target);
@@ -781,7 +784,11 @@ nextch2:
 		}
 	}
 
-	return 0;
+	return;
+
+bad_format:
+	errno = EINVAL;
+	target->failed = true;
 }
 
 static size_t
@@ -831,8 +838,10 @@ fmtptr(void *value, PrintfTarget *target)
 
 	/* we rely on regular C library's sprintf to do the basic conversion */
 	vallen = sprintf(convert, "%p", value);
-
-	dostr(convert, vallen, target);
+	if (vallen < 0)
+		target->failed = true;
+	else
+		dostr(convert, vallen, target);
 }
 
 static void
@@ -965,16 +974,19 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
 
 	if (pointflag)
 	{
-		sprintf(fmt, "%%.%d%c", prec, type);
+		if (sprintf(fmt, "%%.%d%c", prec, type) < 0)
+			goto fail;
 		zeropadlen = precision - prec;
 	}
-	else
-		sprintf(fmt, "%%%c", type);
+	else if (sprintf(fmt, "%%%c", type) < 0)
+		goto fail;
 
 	if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))
 		value = -value;
 
 	vallen = sprintf(convert, fmt, value);
+	if (vallen < 0)
+		goto fail;
 
 	/* If it's infinity or NaN, forget about doing any zero-padding */
 	if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
@@ -1014,6 +1026,10 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
 	}
 
 	trailing_pad(&padlen, target);
+	return;
+
+fail:
+	target->failed = true;
 }
 
 static void
diff --git a/src/port/syswrap.c b/src/port/syswrap.c
new file mode 100644
index 0000000000000000000000000000000000000000..8415a336303699eed799bf34ec44c18f0633a9be
--- /dev/null
+++ b/src/port/syswrap.c
@@ -0,0 +1,155 @@
+/*-------------------------------------------------------------------------
+ *
+ * syswrap.c
+ *	  error-throwing wrappers around POSIX functions that rarely fail
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/port/syswrap.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+/* Prevent recursion */
+#undef	vsnprintf
+#undef	snprintf
+#undef	vsprintf
+#undef	sprintf
+#undef	vfprintf
+#undef	fprintf
+#undef	printf
+
+/* When the libc primitives are lacking, use our own. */
+#ifdef USE_REPL_SNPRINTF
+#ifdef __GNUC__
+#define vsnprintf(...)	pg_vsnprintf(__VA_ARGS__)
+#define snprintf(...)	pg_snprintf(__VA_ARGS__)
+#define vsprintf(...)	pg_vsprintf(__VA_ARGS__)
+#define sprintf(...)	pg_sprintf(__VA_ARGS__)
+#define vfprintf(...)	pg_vfprintf(__VA_ARGS__)
+#define fprintf(...)	pg_fprintf(__VA_ARGS__)
+#define printf(...)		pg_printf(__VA_ARGS__)
+#else
+#define vsnprintf		pg_vsnprintf
+#define snprintf		pg_snprintf
+#define vsprintf		pg_vsprintf
+#define sprintf			pg_sprintf
+#define vfprintf		pg_vfprintf
+#define fprintf			pg_fprintf
+#define printf			pg_printf
+#endif
+#endif   /* USE_REPL_SNPRINTF */
+
+/*
+ * We abort() in the frontend, rather than exit(), because libpq in particular
+ * has no business calling exit().  These failures had better be rare.
+ */
+#ifdef FRONTEND
+#define LIB_ERR(func) \
+do { \
+	int discard = fprintf(stderr, "%s failed: %s\n", func, strerror(errno)); \
+	(void) discard; \
+	abort(); \
+} while (0)
+#else
+#define LIB_ERR(func) elog(ERROR, "%s failed: %m", func)
+#endif
+
+int
+vsnprintf_throw_on_fail(char *str, size_t count, const char *fmt, va_list args)
+{
+	int			save_errno;
+	int			ret;
+
+	/*
+	 * On HP-UX B.11.31, a call that truncates output returns -1 without
+	 * setting errno.  (SUSv2 allowed this until the approval of Base Working
+	 * Group Resolution BWG98-006.)  We could avoid the save and restore of
+	 * errno on most platforms.
+	 */
+	save_errno = errno;
+	errno = 0;
+	ret = vsnprintf(str, count, fmt, args);
+	if (ret < 0 && errno != 0)
+		LIB_ERR("vsnprintf");
+	errno = save_errno;
+	return ret;
+}
+
+int
+snprintf_throw_on_fail(char *str, size_t count, const char *fmt,...)
+{
+	int			ret;
+	va_list		args;
+
+	va_start(args, fmt);
+	ret = vsnprintf_throw_on_fail(str, count, fmt, args);
+	va_end(args);
+	return ret;
+}
+
+int
+vsprintf_throw_on_fail(char *str, const char *fmt, va_list args)
+{
+	int			ret;
+
+	ret = vsprintf(str, fmt, args);
+	if (ret < 0)
+		LIB_ERR("vsprintf");
+	return ret;
+}
+
+int
+sprintf_throw_on_fail(char *str, const char *fmt,...)
+{
+	int			ret;
+	va_list		args;
+
+	va_start(args, fmt);
+	ret = vsprintf_throw_on_fail(str, fmt, args);
+	va_end(args);
+	return ret;
+}
+
+int
+vfprintf_throw_on_fail(FILE *stream, const char *fmt, va_list args)
+{
+	int			ret;
+
+	ret = vfprintf(stream, fmt, args);
+	if (ret < 0)
+		LIB_ERR("vfprintf");
+	return ret;
+}
+
+int
+fprintf_throw_on_fail(FILE *stream, const char *fmt,...)
+{
+	int			ret;
+	va_list		args;
+
+	va_start(args, fmt);
+	ret = vfprintf_throw_on_fail(stream, fmt, args);
+	va_end(args);
+	return ret;
+}
+
+int
+printf_throw_on_fail(const char *fmt,...)
+{
+	int			ret;
+	va_list		args;
+
+	va_start(args, fmt);
+	ret = vfprintf_throw_on_fail(stdout, fmt, args);
+	va_end(args);
+	return ret;
+}
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index be06898d1ae8599d3ede12ed31a6d8cfb2bdcd70..35acb52d5829a8643c2f99fb6e03e14860cec332 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -91,7 +91,7 @@ sub mkvcbuild
 	  erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
 	  pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c
 	  mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
-	  sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c
+	  sprompt.c syswrap.c tar.c thread.c getopt.c getopt_long.c dirent.c
 	  win32env.c win32error.c win32setlocale.c);
 
 	push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');