Michael Paquier <mich...@paquier.xyz> writes:
> I would have liked to look at this patch in details, but it failed to
> apply.  Could you rebase?

Ah, yeah, the dlopen patch touched a couple of the same places.
Rebase attached --- no substantive changes.

                        regards, tom lane

diff --git a/configure b/configure
index dd77742..1aefc57 100755
*** a/configure
--- b/configure
*************** esac
*** 15602,15620 ****
  
  fi
  
- ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror"
- if test "x$ac_cv_func_strerror" = xyes; then :
-   $as_echo "#define HAVE_STRERROR 1" >>confdefs.h
- 
- else
-   case " $LIBOBJS " in
-   *" strerror.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS strerror.$ac_objext"
-  ;;
- esac
- 
- fi
- 
  ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
  if test "x$ac_cv_func_strlcat" = xyes; then :
    $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
--- 15602,15607 ----
diff --git a/configure.in b/configure.in
index 3ada48b..3a23913 100644
*** a/configure.in
--- b/configure.in
*************** else
*** 1660,1666 ****
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi
  
! AC_REPLACE_FUNCS([crypt dlopen fls getopt getrusage inet_aton mkdtemp random rint srandom strerror strlcat strlcpy strnlen])
  
  case $host_os in
  
--- 1660,1666 ----
    AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break])
  fi
  
! AC_REPLACE_FUNCS([crypt dlopen fls getopt getrusage inet_aton mkdtemp random rint srandom strlcat strlcpy strnlen])
  
  case $host_os in
  
diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index f4356fe..af35cfb 100644
*** a/src/backend/port/win32/socket.c
--- b/src/backend/port/win32/socket.c
*************** pgwin32_select(int nfds, fd_set *readfds
*** 690,728 ****
  		memcpy(writefds, &outwritefds, sizeof(fd_set));
  	return nummatches;
  }
- 
- 
- /*
-  * Return win32 error string, since strerror can't
-  * handle winsock codes
-  */
- static char wserrbuf[256];
- const char *
- pgwin32_socket_strerror(int err)
- {
- 	static HANDLE handleDLL = INVALID_HANDLE_VALUE;
- 
- 	if (handleDLL == INVALID_HANDLE_VALUE)
- 	{
- 		handleDLL = LoadLibraryEx("netmsg.dll", NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
- 		if (handleDLL == NULL)
- 			ereport(FATAL,
- 					(errmsg_internal("could not load netmsg.dll: error code %lu", GetLastError())));
- 	}
- 
- 	ZeroMemory(&wserrbuf, sizeof(wserrbuf));
- 	if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
- 					  FORMAT_MESSAGE_FROM_SYSTEM |
- 					  FORMAT_MESSAGE_FROM_HMODULE,
- 					  handleDLL,
- 					  err,
- 					  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
- 					  wserrbuf,
- 					  sizeof(wserrbuf) - 1,
- 					  NULL) == 0)
- 	{
- 		/* Failed to get id */
- 		sprintf(wserrbuf, "unrecognized winsock error %d", err);
- 	}
- 	return wserrbuf;
- }
--- 690,692 ----
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 16531f7..22e5d87 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void send_message_to_server_log(E
*** 178,185 ****
  static void write_pipe_chunks(char *data, int len, int dest);
  static void send_message_to_frontend(ErrorData *edata);
  static char *expand_fmt_string(const char *fmt, ErrorData *edata);
- static const char *useful_strerror(int errnum);
- static const char *get_errno_symbol(int errnum);
  static const char *error_severity(int elevel);
  static void append_with_tabs(StringInfo buf, const char *str);
  static bool is_log_level_output(int elevel, int log_min_level);
--- 178,183 ----
*************** expand_fmt_string(const char *fmt, Error
*** 3360,3366 ****
  				 */
  				const char *cp2;
  
! 				cp2 = useful_strerror(edata->saved_errno);
  				for (; *cp2; cp2++)
  				{
  					if (*cp2 == '%')
--- 3358,3364 ----
  				 */
  				const char *cp2;
  
! 				cp2 = strerror(edata->saved_errno);
  				for (; *cp2; cp2++)
  				{
  					if (*cp2 == '%')
*************** expand_fmt_string(const char *fmt, Error
*** 3384,3602 ****
  
  
  /*
-  * A slightly cleaned-up version of strerror()
-  */
- static const char *
- useful_strerror(int errnum)
- {
- 	/* this buffer is only used if strerror() and get_errno_symbol() fail */
- 	static char errorstr_buf[48];
- 	const char *str;
- 
- #ifdef WIN32
- 	/* Winsock error code range, per WinError.h */
- 	if (errnum >= 10000 && errnum <= 11999)
- 		return pgwin32_socket_strerror(errnum);
- #endif
- 	str = strerror(errnum);
- 
- 	/*
- 	 * Some strerror()s return an empty string for out-of-range errno.  This
- 	 * is ANSI C spec compliant, but not exactly useful.  Also, we may get
- 	 * back strings of question marks if libc cannot transcode the message to
- 	 * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
- 	 * get_errno_symbol(), and if that fails, print the numeric errno.
- 	 */
- 	if (str == NULL || *str == '\0' || *str == '?')
- 		str = get_errno_symbol(errnum);
- 
- 	if (str == NULL)
- 	{
- 		snprintf(errorstr_buf, sizeof(errorstr_buf),
- 		/*------
- 		  translator: This string will be truncated at 47
- 		  characters expanded. */
- 				 _("operating system error %d"), errnum);
- 		str = errorstr_buf;
- 	}
- 
- 	return str;
- }
- 
- /*
-  * Returns a symbol (e.g. "ENOENT") for an errno code.
-  * Returns NULL if the code is unrecognized.
-  */
- static const char *
- get_errno_symbol(int errnum)
- {
- 	switch (errnum)
- 	{
- 		case E2BIG:
- 			return "E2BIG";
- 		case EACCES:
- 			return "EACCES";
- #ifdef EADDRINUSE
- 		case EADDRINUSE:
- 			return "EADDRINUSE";
- #endif
- #ifdef EADDRNOTAVAIL
- 		case EADDRNOTAVAIL:
- 			return "EADDRNOTAVAIL";
- #endif
- 		case EAFNOSUPPORT:
- 			return "EAFNOSUPPORT";
- #ifdef EAGAIN
- 		case EAGAIN:
- 			return "EAGAIN";
- #endif
- #ifdef EALREADY
- 		case EALREADY:
- 			return "EALREADY";
- #endif
- 		case EBADF:
- 			return "EBADF";
- #ifdef EBADMSG
- 		case EBADMSG:
- 			return "EBADMSG";
- #endif
- 		case EBUSY:
- 			return "EBUSY";
- 		case ECHILD:
- 			return "ECHILD";
- #ifdef ECONNABORTED
- 		case ECONNABORTED:
- 			return "ECONNABORTED";
- #endif
- 		case ECONNREFUSED:
- 			return "ECONNREFUSED";
- #ifdef ECONNRESET
- 		case ECONNRESET:
- 			return "ECONNRESET";
- #endif
- 		case EDEADLK:
- 			return "EDEADLK";
- 		case EDOM:
- 			return "EDOM";
- 		case EEXIST:
- 			return "EEXIST";
- 		case EFAULT:
- 			return "EFAULT";
- 		case EFBIG:
- 			return "EFBIG";
- #ifdef EHOSTUNREACH
- 		case EHOSTUNREACH:
- 			return "EHOSTUNREACH";
- #endif
- 		case EIDRM:
- 			return "EIDRM";
- 		case EINPROGRESS:
- 			return "EINPROGRESS";
- 		case EINTR:
- 			return "EINTR";
- 		case EINVAL:
- 			return "EINVAL";
- 		case EIO:
- 			return "EIO";
- #ifdef EISCONN
- 		case EISCONN:
- 			return "EISCONN";
- #endif
- 		case EISDIR:
- 			return "EISDIR";
- #ifdef ELOOP
- 		case ELOOP:
- 			return "ELOOP";
- #endif
- 		case EMFILE:
- 			return "EMFILE";
- 		case EMLINK:
- 			return "EMLINK";
- 		case EMSGSIZE:
- 			return "EMSGSIZE";
- 		case ENAMETOOLONG:
- 			return "ENAMETOOLONG";
- 		case ENFILE:
- 			return "ENFILE";
- 		case ENOBUFS:
- 			return "ENOBUFS";
- 		case ENODEV:
- 			return "ENODEV";
- 		case ENOENT:
- 			return "ENOENT";
- 		case ENOEXEC:
- 			return "ENOEXEC";
- 		case ENOMEM:
- 			return "ENOMEM";
- 		case ENOSPC:
- 			return "ENOSPC";
- 		case ENOSYS:
- 			return "ENOSYS";
- #ifdef ENOTCONN
- 		case ENOTCONN:
- 			return "ENOTCONN";
- #endif
- 		case ENOTDIR:
- 			return "ENOTDIR";
- #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
- 		case ENOTEMPTY:
- 			return "ENOTEMPTY";
- #endif
- #ifdef ENOTSOCK
- 		case ENOTSOCK:
- 			return "ENOTSOCK";
- #endif
- #ifdef ENOTSUP
- 		case ENOTSUP:
- 			return "ENOTSUP";
- #endif
- 		case ENOTTY:
- 			return "ENOTTY";
- 		case ENXIO:
- 			return "ENXIO";
- #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
- 		case EOPNOTSUPP:
- 			return "EOPNOTSUPP";
- #endif
- #ifdef EOVERFLOW
- 		case EOVERFLOW:
- 			return "EOVERFLOW";
- #endif
- 		case EPERM:
- 			return "EPERM";
- 		case EPIPE:
- 			return "EPIPE";
- 		case EPROTONOSUPPORT:
- 			return "EPROTONOSUPPORT";
- 		case ERANGE:
- 			return "ERANGE";
- #ifdef EROFS
- 		case EROFS:
- 			return "EROFS";
- #endif
- 		case ESRCH:
- 			return "ESRCH";
- #ifdef ETIMEDOUT
- 		case ETIMEDOUT:
- 			return "ETIMEDOUT";
- #endif
- #ifdef ETXTBSY
- 		case ETXTBSY:
- 			return "ETXTBSY";
- #endif
- #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- 		case EWOULDBLOCK:
- 			return "EWOULDBLOCK";
- #endif
- 		case EXDEV:
- 			return "EXDEV";
- 	}
- 
- 	return NULL;
- }
- 
- 
- /*
   * error_severity --- get string representing elevel
   *
   * The string is not localized here, but we mark the strings for translation
--- 3382,3387 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 4094e22..705c52b 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 531,539 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #undef HAVE_STDLIB_H
  
- /* Define to 1 if you have the `strerror' function. */
- #undef HAVE_STRERROR
- 
  /* Define to 1 if you have the `strerror_r' function. */
  #undef HAVE_STRERROR_R
  
--- 531,536 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 6618b43..eafe377 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 402,412 ****
  /* Define to 1 if you have the <stdlib.h> header file. */
  #define HAVE_STDLIB_H 1
  
- /* Define to 1 if you have the `strerror' function. */
- #ifndef HAVE_STRERROR
- #define HAVE_STRERROR 1
- #endif
- 
  /* Define to 1 if you have the `strerror_r' function. */
  /* #undef HAVE_STRERROR_R */
  
--- 402,407 ----
diff --git a/src/include/port.h b/src/include/port.h
index d927561..bffc773 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern int	pg_printf(const char *fmt,...
*** 189,194 ****
--- 189,198 ----
  #endif
  #endif							/* USE_REPL_SNPRINTF */
  
+ /* Replace strerror() with our own, somewhat more robust wrapper */
+ extern char *pg_strerror(int errnum);
+ #define strerror pg_strerror
+ 
  /* Portable prompt handling */
  extern void simple_prompt(const char *prompt, char *destination, size_t destlen,
  			  bool echo);
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index b398cd3..360dbdf 100644
*** a/src/include/port/win32_port.h
--- b/src/include/port/win32_port.h
*************** extern int	pgwin32_safestat(const char *
*** 322,329 ****
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants.  This allows elog.c to recognize them as being in the Winsock
!  * error code range and pass them off to pgwin32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
--- 322,329 ----
   * Supplement to <errno.h>.
   *
   * We redefine network-related Berkeley error symbols as the corresponding WSA
!  * constants. This allows strerror.c to recognize them as being in the Winsock
!  * error code range and pass them off to win32_socket_strerror(), since
   * Windows' version of plain strerror() won't cope.  Note that this will break
   * if these names are used for anything else besides Windows Sockets errors.
   * See TranslateSocketError() when changing this list.
*************** int			pgwin32_connect(SOCKET s, const st
*** 456,463 ****
  int			pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
  int			pgwin32_recv(SOCKET s, char *buf, int len, int flags);
  int			pgwin32_send(SOCKET s, const void *buf, int len, int flags);
- 
- const char *pgwin32_socket_strerror(int err);
  int			pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);
  
  extern int	pgwin32_noblock;
--- 456,461 ----
diff --git a/src/interfaces/ecpg/compatlib/.gitignore b/src/interfaces/ecpg/compatlib/.gitignore
index ad5ba13..8b9aa95 100644
*** a/src/interfaces/ecpg/compatlib/.gitignore
--- b/src/interfaces/ecpg/compatlib/.gitignore
***************
*** 2,5 ****
--- 2,6 ----
  /blibecpg_compatdll.def
  /exports.list
  /snprintf.c
+ /strerror.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index ebfd895..b7bd162 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))
  
! OBJS= informix.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)
  
  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes
  
--- 31,37 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))
  
! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)
  
  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes
  
*************** submake-pgtypeslib:
*** 48,54 ****
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib
  
! snprintf.c strnlen.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
  
  install: all installdirs install-lib
--- 48,54 ----
  # Shared library stuff
  include $(top_srcdir)/src/Makefile.shlib
  
! snprintf.c strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
  
  install: all installdirs install-lib
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib
  
  clean distclean: clean-lib
! 	rm -f $(OBJS) snprintf.c strnlen.c
  
  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib
  
  clean distclean: clean-lib
! 	rm -f $(OBJS) snprintf.c strerror.c strnlen.c
  
  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/ecpg/ecpglib/.gitignore b/src/interfaces/ecpg/ecpglib/.gitignore
index 1619e97..545c106 100644
*** a/src/interfaces/ecpg/ecpglib/.gitignore
--- b/src/interfaces/ecpg/ecpglib/.gitignore
***************
*** 4,9 ****
--- 4,10 ----
  /path.c
  /pgstrcasecmp.c
  /snprintf.c
+ /strerror.c
  /strlcpy.c
  /strnlen.c
  /thread.c
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index d25d248..005d25a 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,32 ****
  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 \
  	$(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
  	$(WIN32RES)
  
--- 26,32 ----
  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 strerror.o \
  	$(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
  	$(WIN32RES)
  
*************** include $(top_srcdir)/src/Makefile.shlib
*** 57,63 ****
  # 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 strnlen.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
--- 57,63 ----
  # 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 strerror.c strlcpy.c strnlen.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
*************** uninstall: uninstall-lib
*** 74,79 ****
  
  clean distclean: clean-lib
  	rm -f $(OBJS)
! 	rm -f path.c pgstrcasecmp.c snprintf.c strlcpy.c strnlen.c thread.c win32setlocale.c isinf.c
  
  maintainer-clean: distclean maintainer-clean-lib
--- 74,79 ----
  
  clean distclean: clean-lib
  	rm -f $(OBJS)
! 	rm -f path.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c strnlen.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 d5f0fae..b3fae08 100644
*** a/src/interfaces/ecpg/pgtypeslib/.gitignore
--- b/src/interfaces/ecpg/pgtypeslib/.gitignore
***************
*** 4,8 ****
--- 4,9 ----
  /pgstrcasecmp.c
  /rint.c
  /snprintf.c
+ /strerror.c
  /string.c
  /strnlen.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 29264ce..18b2402 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,36 ****
  SHLIB_EXPORTS = exports.txt
  
  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
! 	pgstrcasecmp.o \
  	$(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
  	string.o \
  	$(WIN32RES)
--- 30,36 ----
  SHLIB_EXPORTS = exports.txt
  
  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
! 	pgstrcasecmp.o strerror.o \
  	$(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
  	string.o \
  	$(WIN32RES)
*************** include $(top_srcdir)/src/Makefile.shlib
*** 45,51 ****
  # 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 strnlen.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
  
  string.c: % : $(top_srcdir)/src/common/%
--- 45,51 ----
  # 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 strerror.c strnlen.c: % : $(top_srcdir)/src/port/%
  	rm -f $@ && $(LN_S) $< .
  
  string.c: % : $(top_srcdir)/src/common/%
*************** installdirs: installdirs-lib
*** 58,63 ****
  uninstall: uninstall-lib
  
  clean distclean: clean-lib
! 	rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strnlen.c string.c
  
  maintainer-clean: distclean maintainer-clean-lib
--- 58,63 ----
  uninstall: uninstall-lib
  
  clean distclean: clean-lib
! 	rm -f $(OBJS) pgstrcasecmp.c rint.c snprintf.c strerror.c strnlen.c string.c
  
  maintainer-clean: distclean maintainer-clean-lib
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index ec0122a..8324f4f 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=	fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
  	libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.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 strnlen.o win32error.o win32setlocale.o, $(LIBOBJS))
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
  	libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
! 	strerror.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 strlcpy.o strnlen.o win32error.o win32setlocale.o, $(LIBOBJS))
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 5c2e6a8..6cc323a 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 27,33 ****
   */
  #undef _POSIX_C_SOURCE
  #undef _XOPEN_SOURCE
- #undef HAVE_STRERROR
  #undef HAVE_TZNAME
  
  /*
--- 27,32 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index d7467fb..b3a10ba 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  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
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,39 ----
  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 strerror.o tar.o thread.o
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/strerror.c b/src/port/strerror.c
index e92ebc9..e3393eb 100644
*** a/src/port/strerror.c
--- b/src/port/strerror.c
***************
*** 1,30 ****
! /* src/port/strerror.c */
! 
! /*
!  * strerror - map error number to descriptive string
   *
!  * This version is obviously somewhat Unix-specific.
   *
!  * based on code by Henry Spencer
!  * modified for ANSI by D'Arcy J.M. Cain
   */
- 
  #include "c.h"
  
  
! extern const char *const sys_errlist[];
! extern int	sys_nerr;
  
! const char *
! strerror(int errnum)
  {
! 	static char buf[24];
  
! 	if (errnum < 0 || errnum > sys_nerr)
  	{
! 		sprintf(buf, _("unrecognized error %d"), errnum);
! 		return buf;
  	}
  
! 	return sys_errlist[errnum];
  }
--- 1,285 ----
! /*-------------------------------------------------------------------------
   *
!  * strerror.c
!  *	  Replacement for standard strerror() function
   *
!  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
!  * Portions Copyright (c) 1994, Regents of the University of California
!  *
!  *
!  * IDENTIFICATION
!  *	  src/port/strerror.c
!  *
!  *-------------------------------------------------------------------------
   */
  #include "c.h"
  
+ /*
+  * Within this file, "strerror" means the platform's function not pg_strerror
+  */
+ #undef strerror
  
! static char *get_errno_symbol(int errnum);
! #ifdef WIN32
! static char *win32_socket_strerror(int errnum);
! #endif
  
! 
! /*
!  * A slightly cleaned-up version of strerror()
!  */
! char *
! pg_strerror(int errnum)
  {
! 	/* this buffer is only used if strerror() and get_errno_symbol() fail */
! 	static char errorstr_buf[48];
! 	char	   *str;
  
! 	/* If it's a Windows Winsock error, that needs special handling */
! #ifdef WIN32
! 	/* Winsock error code range, per WinError.h */
! 	if (errnum >= 10000 && errnum <= 11999)
! 		return win32_socket_strerror(errnum);
! #endif
! 
! 	/* Try the platform's strerror() */
! 	str = strerror(errnum);
! 
! 	/*
! 	 * Some strerror()s return an empty string for out-of-range errno.  This
! 	 * is ANSI C spec compliant, but not exactly useful.  Also, we may get
! 	 * back strings of question marks if libc cannot transcode the message to
! 	 * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
! 	 * get_errno_symbol(), and if that fails, print the numeric errno.
! 	 */
! 	if (str == NULL || *str == '\0' || *str == '?')
! 		str = get_errno_symbol(errnum);
! 
! 	if (str == NULL)
  	{
! 		snprintf(errorstr_buf, sizeof(errorstr_buf),
! 		/*------
! 		  translator: This string will be truncated at 47
! 		  characters expanded. */
! 				 _("operating system error %d"), errnum);
! 		str = errorstr_buf;
  	}
  
! 	return str;
  }
+ 
+ /*
+  * Returns a symbol (e.g. "ENOENT") for an errno code.
+  * Returns NULL if the code is unrecognized.
+  */
+ static char *
+ get_errno_symbol(int errnum)
+ {
+ 	switch (errnum)
+ 	{
+ 		case E2BIG:
+ 			return "E2BIG";
+ 		case EACCES:
+ 			return "EACCES";
+ #ifdef EADDRINUSE
+ 		case EADDRINUSE:
+ 			return "EADDRINUSE";
+ #endif
+ #ifdef EADDRNOTAVAIL
+ 		case EADDRNOTAVAIL:
+ 			return "EADDRNOTAVAIL";
+ #endif
+ 		case EAFNOSUPPORT:
+ 			return "EAFNOSUPPORT";
+ #ifdef EAGAIN
+ 		case EAGAIN:
+ 			return "EAGAIN";
+ #endif
+ #ifdef EALREADY
+ 		case EALREADY:
+ 			return "EALREADY";
+ #endif
+ 		case EBADF:
+ 			return "EBADF";
+ #ifdef EBADMSG
+ 		case EBADMSG:
+ 			return "EBADMSG";
+ #endif
+ 		case EBUSY:
+ 			return "EBUSY";
+ 		case ECHILD:
+ 			return "ECHILD";
+ #ifdef ECONNABORTED
+ 		case ECONNABORTED:
+ 			return "ECONNABORTED";
+ #endif
+ 		case ECONNREFUSED:
+ 			return "ECONNREFUSED";
+ #ifdef ECONNRESET
+ 		case ECONNRESET:
+ 			return "ECONNRESET";
+ #endif
+ 		case EDEADLK:
+ 			return "EDEADLK";
+ 		case EDOM:
+ 			return "EDOM";
+ 		case EEXIST:
+ 			return "EEXIST";
+ 		case EFAULT:
+ 			return "EFAULT";
+ 		case EFBIG:
+ 			return "EFBIG";
+ #ifdef EHOSTUNREACH
+ 		case EHOSTUNREACH:
+ 			return "EHOSTUNREACH";
+ #endif
+ 		case EIDRM:
+ 			return "EIDRM";
+ 		case EINPROGRESS:
+ 			return "EINPROGRESS";
+ 		case EINTR:
+ 			return "EINTR";
+ 		case EINVAL:
+ 			return "EINVAL";
+ 		case EIO:
+ 			return "EIO";
+ #ifdef EISCONN
+ 		case EISCONN:
+ 			return "EISCONN";
+ #endif
+ 		case EISDIR:
+ 			return "EISDIR";
+ #ifdef ELOOP
+ 		case ELOOP:
+ 			return "ELOOP";
+ #endif
+ 		case EMFILE:
+ 			return "EMFILE";
+ 		case EMLINK:
+ 			return "EMLINK";
+ 		case EMSGSIZE:
+ 			return "EMSGSIZE";
+ 		case ENAMETOOLONG:
+ 			return "ENAMETOOLONG";
+ 		case ENFILE:
+ 			return "ENFILE";
+ 		case ENOBUFS:
+ 			return "ENOBUFS";
+ 		case ENODEV:
+ 			return "ENODEV";
+ 		case ENOENT:
+ 			return "ENOENT";
+ 		case ENOEXEC:
+ 			return "ENOEXEC";
+ 		case ENOMEM:
+ 			return "ENOMEM";
+ 		case ENOSPC:
+ 			return "ENOSPC";
+ 		case ENOSYS:
+ 			return "ENOSYS";
+ #ifdef ENOTCONN
+ 		case ENOTCONN:
+ 			return "ENOTCONN";
+ #endif
+ 		case ENOTDIR:
+ 			return "ENOTDIR";
+ #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
+ 		case ENOTEMPTY:
+ 			return "ENOTEMPTY";
+ #endif
+ #ifdef ENOTSOCK
+ 		case ENOTSOCK:
+ 			return "ENOTSOCK";
+ #endif
+ #ifdef ENOTSUP
+ 		case ENOTSUP:
+ 			return "ENOTSUP";
+ #endif
+ 		case ENOTTY:
+ 			return "ENOTTY";
+ 		case ENXIO:
+ 			return "ENXIO";
+ #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
+ 		case EOPNOTSUPP:
+ 			return "EOPNOTSUPP";
+ #endif
+ #ifdef EOVERFLOW
+ 		case EOVERFLOW:
+ 			return "EOVERFLOW";
+ #endif
+ 		case EPERM:
+ 			return "EPERM";
+ 		case EPIPE:
+ 			return "EPIPE";
+ 		case EPROTONOSUPPORT:
+ 			return "EPROTONOSUPPORT";
+ 		case ERANGE:
+ 			return "ERANGE";
+ #ifdef EROFS
+ 		case EROFS:
+ 			return "EROFS";
+ #endif
+ 		case ESRCH:
+ 			return "ESRCH";
+ #ifdef ETIMEDOUT
+ 		case ETIMEDOUT:
+ 			return "ETIMEDOUT";
+ #endif
+ #ifdef ETXTBSY
+ 		case ETXTBSY:
+ 			return "ETXTBSY";
+ #endif
+ #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ 		case EWOULDBLOCK:
+ 			return "EWOULDBLOCK";
+ #endif
+ 		case EXDEV:
+ 			return "EXDEV";
+ 	}
+ 
+ 	return NULL;
+ }
+ 
+ 
+ #ifdef WIN32
+ 
+ /*
+  * Windows' strerror() doesn't know the Winsock codes, so handle them this way
+  */
+ static char *
+ win32_socket_strerror(int errnum)
+ {
+ 	static char wserrbuf[256];
+ 	static HANDLE handleDLL = INVALID_HANDLE_VALUE;
+ 
+ 	if (handleDLL == INVALID_HANDLE_VALUE)
+ 	{
+ 		handleDLL = LoadLibraryEx("netmsg.dll", NULL,
+ 								  DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
+ 		if (handleDLL == NULL)
+ 		{
+ 			/* just treat this as an unrecognized error ... */
+ 			sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+ 			return wserrbuf;
+ 		}
+ 	}
+ 
+ 	ZeroMemory(&wserrbuf, sizeof(wserrbuf));
+ 	if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
+ 					  FORMAT_MESSAGE_FROM_SYSTEM |
+ 					  FORMAT_MESSAGE_FROM_HMODULE,
+ 					  handleDLL,
+ 					  errnum,
+ 					  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+ 					  wserrbuf,
+ 					  sizeof(wserrbuf) - 1,
+ 					  NULL) == 0)
+ 	{
+ 		/* Failed to get id */
+ 		sprintf(wserrbuf, "unrecognized winsock error %d", errnum);
+ 	}
+ 
+ 	return wserrbuf;
+ }
+ 
+ #endif							/* WIN32 */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 9ce03ce..2be72f8 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 96,104 ****
  	  chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
  	  srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
  	  erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
  	  pg_strong_random.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 dlopen.c
  	  win32env.c win32error.c win32security.c win32setlocale.c);
  
  	push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
--- 96,105 ----
  	  chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
  	  srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
  	  erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
+ 	  dirent.c dlopen.c getopt.c getopt_long.c
  	  pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c
  	  pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
! 	  sprompt.c strerror.c tar.c thread.c
  	  win32env.c win32error.c win32security.c win32setlocale.c);
  
  	push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00');
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index eedaf12..fb58c94 100644
*** a/config/c-compiler.m4
--- b/config/c-compiler.m4
*************** fi])# PGAC_C_SIGNED
*** 20,34 ****
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", which matches the features glibc supports, notably
! # %m, 'z' and 'll' width modifiers ('ll' only matters if int64 requires it),
! # and argument order control if we're doing --enable-nls.  On platforms where
! # the native printf doesn't have 'z'/'ll' or arg control, we replace it with
! # src/port/snprintf.c which does, so that the only potential mismatch here is
! # whether or not %m is supported.  We need that for elog/ereport, so we live
! # with the fact that erroneous use of %m in plain printf calls won't be
! # detected.  (It appears that many versions of gcc/clang wouldn't report it
! # even if told to check according to plain printf archetype, anyway.)
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
--- 20,27 ----
  # PGAC_C_PRINTF_ARCHETYPE
  # -----------------------
  # Select the format archetype to be used by gcc to check printf-type functions.
! # We prefer "gnu_printf", as that most closely matches the features supported
! # by src/port/snprintf.c (particularly the %m conversion spec).
  AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
  [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
  [ac_save_c_werror_flag=$ac_c_werror_flag
diff --git a/config/c-library.m4 b/config/c-library.m4
index da7fa77..d371f1b 100644
*** a/config/c-library.m4
--- b/config/c-library.m4
*************** AC_DEFUN([PGAC_STRUCT_ADDRINFO],
*** 171,276 ****
  ])])# PGAC_STRUCT_ADDRINFO
  
  
- # PGAC_FUNC_SNPRINTF_ARG_CONTROL
- # ---------------------------------------
- # Determine if snprintf supports %1$ argument selection, e.g. %5$ selects
- # the fifth argument after the printf format string.
- # This is not in the C99 standard, but in the Single Unix Specification (SUS).
- # It is used in our language translation strings.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_ARG_CONTROL],
- [AC_MSG_CHECKING([whether snprintf supports argument control])
- AC_CACHE_VAL(pgac_cv_snprintf_arg_control,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
- 
- int main()
- {
-   char buf[100];
- 
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_arg_control=yes],
- [pgac_cv_snprintf_arg_control=no],
- [pgac_cv_snprintf_arg_control=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_arg_control])
- ])# PGAC_FUNC_SNPRINTF_ARG_CONTROL
- 
- # PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
- # ---------------------------------
- # Determine if snprintf supports the z length modifier for printing
- # size_t-sized variables. That's supported by C99 and POSIX but not
- # all platforms play ball, so we must test whether it's working.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT],
- [AC_MSG_CHECKING([whether snprintf supports the %z modifier])
- AC_CACHE_VAL(pgac_cv_snprintf_size_t_support,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
- 
- int main()
- {
-   char bufz[100];
-   char buf64[100];
- 
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_size_t_support=yes],
- [pgac_cv_snprintf_size_t_support=no],
- [pgac_cv_snprintf_size_t_support=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_size_t_support])
- ])# PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
- 
- # PGAC_FUNC_SNPRINTF_C99_RESULT
- # -----------------------------
- # Determine whether snprintf returns the desired buffer length when
- # it overruns the actual buffer length.  That's required by C99 and POSIX
- # but ancient platforms don't behave that way, so we must test.
- # While we're at it, let's just verify that it doesn't physically overrun
- # the buffer.
- #
- AC_DEFUN([PGAC_FUNC_SNPRINTF_C99_RESULT],
- [AC_MSG_CHECKING([whether snprintf handles buffer overrun per C99])
- AC_CACHE_VAL(pgac_cv_snprintf_c99_result,
- [AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
- #include <string.h>
- 
- int main()
- {
-   char buf[10];
- 
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }]])],
- [pgac_cv_snprintf_c99_result=yes],
- [pgac_cv_snprintf_c99_result=no],
- [pgac_cv_snprintf_c99_result=cross])
- ])dnl AC_CACHE_VAL
- AC_MSG_RESULT([$pgac_cv_snprintf_c99_result])
- ])# PGAC_FUNC_SNPRINTF_C99_RESULT
- 
- 
  # PGAC_TYPE_LOCALE_T
  # ------------------
  # Check for the locale_t type and find the right header file.  macOS
--- 171,176 ----
diff --git a/configure b/configure
index 1aefc57..4eed2f4 100755
*** a/configure
--- b/configure
*************** $as_echo "#define HAVE_PS_STRINGS 1" >>c
*** 15325,15421 ****
  fi
  
  
- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
- 
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   for ac_func in snprintf
- do :
-   ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
- if test "x$ac_cv_func_snprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_SNPRINTF 1
- _ACEOF
- 
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
- 
-   for ac_func in vsnprintf
- do :
-   ac_fn_c_check_func "$LINENO" "vsnprintf" "ac_cv_func_vsnprintf"
- if test "x$ac_cv_func_vsnprintf" = xyes; then :
-   cat >>confdefs.h <<_ACEOF
- #define HAVE_VSNPRINTF 1
- _ACEOF
- 
- else
-   pgac_need_repl_snprintf=yes
- fi
- done
- 
- fi
- 
- 
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
- 
- ac_fn_c_check_decl "$LINENO" "snprintf" "ac_cv_have_decl_snprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_snprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
- 
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_SNPRINTF $ac_have_decl
- _ACEOF
- ac_fn_c_check_decl "$LINENO" "vsnprintf" "ac_cv_have_decl_vsnprintf" "$ac_includes_default"
- if test "x$ac_cv_have_decl_vsnprintf" = xyes; then :
-   ac_have_decl=1
- else
-   ac_have_decl=0
- fi
- 
- cat >>confdefs.h <<_ACEOF
- #define HAVE_DECL_VSNPRINTF $ac_have_decl
- _ACEOF
- 
- 
- 
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for isinf" >&5
  $as_echo_n "checking for isinf... " >&6; }
  if ${ac_cv_func_isinf+:} false; then :
--- 15325,15330 ----
*************** fi
*** 16133,16185 ****
  # Run tests below here
  # --------------------
  
- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports argument control" >&5
- $as_echo_n "checking whether snprintf supports argument control... " >&6; }
- if ${pgac_cv_snprintf_arg_control+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_arg_control=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
- 
- int main()
- {
-   char buf[100];
- 
-   /* can it swap arguments? */
-   snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
-   if (strcmp(buf, "4 3") != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_arg_control=yes
- else
-   pgac_cv_snprintf_arg_control=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
- 
- 
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_arg_control" >&5
- $as_echo "$pgac_cv_snprintf_arg_control" >&6; }
- 
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
- 
- 
  
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether long int is 64 bits" >&5
  $as_echo_n "checking whether long int is 64 bits... " >&6; }
--- 16042,16047 ----
*************** _ACEOF
*** 16359,16366 ****
  
  
  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 16221,16226 ----
*************** cat >>confdefs.h <<_ACEOF
*** 16373,16492 ****
  _ACEOF
  
  
- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf supports the %z modifier" >&5
- $as_echo_n "checking whether snprintf supports the %z modifier... " >&6; }
- if ${pgac_cv_snprintf_size_t_support+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_size_t_support=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
- 
- int main()
- {
-   char bufz[100];
-   char buf64[100];
- 
-   /*
-    * Print the largest unsigned number fitting in a size_t using both %zu
-    * and the previously-determined format for 64-bit integers.  Note that
-    * we don't run this code unless we know snprintf handles 64-bit ints.
-    */
-   bufz[0] = '\0';  /* in case snprintf fails to emit anything */
-   snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
-   snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
-     (unsigned PG_INT64_TYPE) ~((size_t) 0));
-   if (strcmp(bufz, buf64) != 0)
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_size_t_support=yes
- else
-   pgac_cv_snprintf_size_t_support=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
- 
- 
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_size_t_support" >&5
- $as_echo "$pgac_cv_snprintf_size_t_support" >&6; }
- 
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
- 
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether snprintf handles buffer overrun per C99" >&5
- $as_echo_n "checking whether snprintf handles buffer overrun per C99... " >&6; }
- if ${pgac_cv_snprintf_c99_result+:} false; then :
-   $as_echo_n "(cached) " >&6
- else
-   if test "$cross_compiling" = yes; then :
-   pgac_cv_snprintf_c99_result=cross
- else
-   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
- /* end confdefs.h.  */
- #include <stdio.h>
- #include <string.h>
- 
- int main()
- {
-   char buf[10];
- 
-   strcpy(buf, "abcdefghi");
-   if (snprintf(buf, 4, "%d", 123456) != 6)
-     return 1;
-   if (strcmp(buf, "123") != 0 || buf[4] != 'e')
-     return 1;
-   return 0;
- }
- _ACEOF
- if ac_fn_c_try_run "$LINENO"; then :
-   pgac_cv_snprintf_c99_result=yes
- else
-   pgac_cv_snprintf_c99_result=no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-   conftest.$ac_objext conftest.beam conftest.$ac_ext
- fi
- 
- 
- fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_snprintf_c99_result" >&5
- $as_echo "$pgac_cv_snprintf_c99_result" >&6; }
- 
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
- 
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
- 
- $as_echo "#define USE_REPL_SNPRINTF 1" >>confdefs.h
- 
-   case " $LIBOBJS " in
-   *" snprintf.$ac_objext "* ) ;;
-   *) LIBOBJS="$LIBOBJS snprintf.$ac_objext"
-  ;;
- esac
- 
- fi
- 
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_mul_overflow" >&5
--- 16233,16238 ----
diff --git a/configure.in b/configure.in
index 3a23913..f4e0b07 100644
*** a/configure.in
--- b/configure.in
*************** if test "$pgac_cv_var_PS_STRINGS" = yes 
*** 1595,1647 ****
  fi
  
  
- # We use our snprintf.c emulation if either snprintf() or vsnprintf()
- # is missing.  Yes, there are machines that have only one.  We may
- # also decide to use snprintf.c if snprintf() is present but does not
- # have all the features we need --- see below.
- 
- if test "$PORTNAME" = "win32"; then
-   # Win32 gets snprintf.c built unconditionally.
-   #
-   # To properly translate all NLS languages strings, we must support the
-   # *printf() %$ format, which allows *printf() arguments to be selected
-   # by position in the translated string.
-   #
-   # libintl versions < 0.13 use the native *printf() functions, and Win32
-   # *printf() doesn't understand %$, so we must use our /port versions,
-   # which do understand %$. libintl versions >= 0.13 include their own
-   # *printf versions on Win32.  The libintl 0.13 release note text is:
-   #
-   #   C format strings with positions, as they arise when a translator
-   #   needs to reorder a sentence, are now supported on all platforms.
-   #   On those few platforms (NetBSD and Woe32) for which the native
-   #   printf()/fprintf()/... functions don't support such format
-   #   strings, replacements are provided through <libintl.h>.
-   #
-   # We could use libintl >= 0.13's *printf() if we were sure that we had
-   # a libintl >= 0.13 at runtime, but seeing that there is no clean way
-   # to guarantee that, it is best to just use our own, so we are sure to
-   # get %$ support. In include/port.h we disable the *printf() macros
-   # that might have been defined by libintl.
-   #
-   # We do this unconditionally whether NLS is used or not so we are sure
-   # that all Win32 libraries and binaries behave the same.
-   pgac_need_repl_snprintf=yes
- else
-   pgac_need_repl_snprintf=no
-   AC_CHECK_FUNCS(snprintf, [], pgac_need_repl_snprintf=yes)
-   AC_CHECK_FUNCS(vsnprintf, [], pgac_need_repl_snprintf=yes)
- fi
- 
- 
- # Check whether <stdio.h> declares snprintf() and vsnprintf(); if not,
- # include/c.h will provide declarations.  Note this is a separate test
- # from whether the functions exist in the C library --- there are
- # systems that have the functions but don't bother to declare them :-(
- 
- AC_CHECK_DECLS([snprintf, vsnprintf])
- 
- 
  dnl Cannot use AC_CHECK_FUNC because isinf may be a macro
  AC_CACHE_CHECK([for isinf], ac_cv_func_isinf,
  [AC_LINK_IFELSE([AC_LANG_PROGRAM([
--- 1595,1600 ----
*************** for the exact reason.]])],
*** 1811,1826 ****
  # Run tests below here
  # --------------------
  
- # For NLS, force use of our snprintf if system's doesn't do arg control.
- # See comment above at snprintf test for details.
- if test "$enable_nls" = yes -a "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_ARG_CONTROL
-   if test $pgac_cv_snprintf_arg_control != yes ; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
- 
- 
  dnl Check to see if we have a working 64-bit integer type.
  dnl Since Postgres 8.4, we no longer support compilers without a working
  dnl 64-bit type; but we have to determine whether that type is called
--- 1764,1769 ----
*************** AC_DEFINE_UNQUOTED(PG_INT64_TYPE, $pg_in
*** 1843,1850 ****
    [Define to the name of a signed 64-bit integer type.])
  
  # Select the printf length modifier that goes with that, too.
- # (This used to be bound up with replacement-snprintf selection, but now
- # we assume that the native *printf functions use standard length modifiers.)
  if test x"$pg_int64_type" = x"long long int" ; then
    INT64_MODIFIER='"ll"'
  else
--- 1786,1791 ----
*************** fi
*** 1854,1883 ****
  AC_DEFINE_UNQUOTED(INT64_MODIFIER, $INT64_MODIFIER,
                     [Define to the appropriate printf length modifier for 64-bit ints.])
  
- # Force use of our snprintf if the system's doesn't support the %z flag.
- # (Note this test uses PG_INT64_TYPE and INT64_MODIFIER.)
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
-   if test "$pgac_cv_snprintf_size_t_support" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
- 
- # Force use of our snprintf if the system's doesn't handle buffer overrun
- # as specified by C99.
- if test "$pgac_need_repl_snprintf" = no; then
-   PGAC_FUNC_SNPRINTF_C99_RESULT
-   if test "$pgac_cv_snprintf_c99_result" != yes; then
-     pgac_need_repl_snprintf=yes
-   fi
- fi
- 
- # Now we have checked all the reasons to replace snprintf
- if test $pgac_need_repl_snprintf = yes; then
-   AC_DEFINE(USE_REPL_SNPRINTF, 1, [Use replacement snprintf() functions.])
-   AC_LIBOBJ(snprintf)
- fi
- 
  # has to be down here, rather than with the other builtins, because
  # the test uses PG_INT64_TYPE.
  PGAC_C_BUILTIN_OP_OVERFLOW
--- 1795,1800 ----
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
index 798a823..df7e01f 100644
*** a/src/backend/lib/stringinfo.c
--- b/src/backend/lib/stringinfo.c
*************** resetStringInfo(StringInfo str)
*** 77,88 ****
--- 77,91 ----
  void
  appendStringInfo(StringInfo str, const char *fmt,...)
  {
+ 	int			save_errno = errno;
+ 
  	for (;;)
  	{
  		va_list		args;
  		int			needed;
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		needed = appendStringInfoVA(str, fmt, args);
  		va_end(args);
*************** appendStringInfo(StringInfo str, const c
*** 105,110 ****
--- 108,116 ----
   * pass the return value to enlargeStringInfo() before trying again; see
   * appendStringInfo for standard usage pattern.
   *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
+  *
   * XXX This API is ugly, but there seems no alternative given the C spec's
   * restrictions on what can portably be done with va_list arguments: you have
   * to redo va_start before you can rescan the argument list, and we can't do
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 22e5d87..b9c1130 100644
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
*************** static void write_csvlog(ErrorData *edat
*** 177,183 ****
  static void send_message_to_server_log(ErrorData *edata);
  static void write_pipe_chunks(char *data, int len, int dest);
  static void send_message_to_frontend(ErrorData *edata);
- static char *expand_fmt_string(const char *fmt, ErrorData *edata);
  static const char *error_severity(int elevel);
  static void append_with_tabs(StringInfo buf, const char *str);
  static bool is_log_level_output(int elevel, int log_min_level);
--- 177,182 ----
*************** errcode_for_socket_access(void)
*** 705,717 ****
   */
  #define EVALUATE_MESSAGE(domain, targetfield, appendval, translateit)	\
  	{ \
- 		char		   *fmtbuf; \
  		StringInfoData	buf; \
  		/* Internationalize the error format string */ \
  		if ((translateit) && !in_error_recursion_trouble()) \
  			fmt = dgettext((domain), fmt);				  \
- 		/* Expand %m in format string */ \
- 		fmtbuf = expand_fmt_string(fmt, edata); \
  		initStringInfo(&buf); \
  		if ((appendval) && edata->targetfield) { \
  			appendStringInfoString(&buf, edata->targetfield); \
--- 704,713 ----
*************** errcode_for_socket_access(void)
*** 722,736 ****
  		{ \
  			va_list		args; \
  			int			needed; \
  			va_start(args, fmt); \
! 			needed = appendStringInfoVA(&buf, fmtbuf, args); \
  			va_end(args); \
  			if (needed == 0) \
  				break; \
  			enlargeStringInfo(&buf, needed); \
  		} \
- 		/* Done with expanded fmt */ \
- 		pfree(fmtbuf); \
  		/* Save the completed message into the stack item */ \
  		if (edata->targetfield) \
  			pfree(edata->targetfield); \
--- 718,731 ----
  		{ \
  			va_list		args; \
  			int			needed; \
+ 			errno = edata->saved_errno; \
  			va_start(args, fmt); \
! 			needed = appendStringInfoVA(&buf, fmt, args); \
  			va_end(args); \
  			if (needed == 0) \
  				break; \
  			enlargeStringInfo(&buf, needed); \
  		} \
  		/* Save the completed message into the stack item */ \
  		if (edata->targetfield) \
  			pfree(edata->targetfield); \
*************** errcode_for_socket_access(void)
*** 746,760 ****
  #define EVALUATE_MESSAGE_PLURAL(domain, targetfield, appendval)  \
  	{ \
  		const char	   *fmt; \
- 		char		   *fmtbuf; \
  		StringInfoData	buf; \
  		/* Internationalize the error format string */ \
  		if (!in_error_recursion_trouble()) \
  			fmt = dngettext((domain), fmt_singular, fmt_plural, n); \
  		else \
  			fmt = (n == 1 ? fmt_singular : fmt_plural); \
- 		/* Expand %m in format string */ \
- 		fmtbuf = expand_fmt_string(fmt, edata); \
  		initStringInfo(&buf); \
  		if ((appendval) && edata->targetfield) { \
  			appendStringInfoString(&buf, edata->targetfield); \
--- 741,752 ----
*************** errcode_for_socket_access(void)
*** 765,779 ****
  		{ \
  			va_list		args; \
  			int			needed; \
  			va_start(args, n); \
! 			needed = appendStringInfoVA(&buf, fmtbuf, args); \
  			va_end(args); \
  			if (needed == 0) \
  				break; \
  			enlargeStringInfo(&buf, needed); \
  		} \
- 		/* Done with expanded fmt */ \
- 		pfree(fmtbuf); \
  		/* Save the completed message into the stack item */ \
  		if (edata->targetfield) \
  			pfree(edata->targetfield); \
--- 757,770 ----
  		{ \
  			va_list		args; \
  			int			needed; \
+ 			errno = edata->saved_errno; \
  			va_start(args, n); \
! 			needed = appendStringInfoVA(&buf, fmt, args); \
  			va_end(args); \
  			if (needed == 0) \
  				break; \
  			enlargeStringInfo(&buf, needed); \
  		} \
  		/* Save the completed message into the stack item */ \
  		if (edata->targetfield) \
  			pfree(edata->targetfield); \
*************** send_message_to_frontend(ErrorData *edat
*** 3329,3387 ****
  
  
  /*
-  * expand_fmt_string --- process special format codes in a format string
-  *
-  * We must replace %m with the appropriate strerror string, since vsnprintf
-  * won't know what to do with it.
-  *
-  * The result is a palloc'd string.
-  */
- static char *
- expand_fmt_string(const char *fmt, ErrorData *edata)
- {
- 	StringInfoData buf;
- 	const char *cp;
- 
- 	initStringInfo(&buf);
- 
- 	for (cp = fmt; *cp; cp++)
- 	{
- 		if (cp[0] == '%' && cp[1] != '\0')
- 		{
- 			cp++;
- 			if (*cp == 'm')
- 			{
- 				/*
- 				 * Replace %m by system error string.  If there are any %'s in
- 				 * the string, we'd better double them so that vsnprintf won't
- 				 * misinterpret.
- 				 */
- 				const char *cp2;
- 
- 				cp2 = strerror(edata->saved_errno);
- 				for (; *cp2; cp2++)
- 				{
- 					if (*cp2 == '%')
- 						appendStringInfoCharMacro(&buf, '%');
- 					appendStringInfoCharMacro(&buf, *cp2);
- 				}
- 			}
- 			else
- 			{
- 				/* copy % and next char --- this avoids trouble with %%m */
- 				appendStringInfoCharMacro(&buf, '%');
- 				appendStringInfoCharMacro(&buf, *cp);
- 			}
- 		}
- 		else
- 			appendStringInfoCharMacro(&buf, *cp);
- 	}
- 
- 	return buf.data;
- }
- 
- 
- /*
   * error_severity --- get string representing elevel
   *
   * The string is not localized here, but we mark the strings for translation
--- 3320,3325 ----
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 36e3383..267d86a 100644
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** archputs(const char *s, Archive *AH)
*** 1471,1476 ****
--- 1471,1477 ----
  int
  archprintf(Archive *AH, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *p;
  	size_t		len = 128;		/* initial assumption about buffer size */
  	size_t		cnt;
*************** archprintf(Archive *AH, const char *fmt,
*** 1483,1488 ****
--- 1484,1490 ----
  		p = (char *) pg_malloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		cnt = pvsnprintf(p, len, fmt, args);
  		va_end(args);
*************** RestoreOutput(ArchiveHandle *AH, OutputC
*** 1604,1609 ****
--- 1606,1612 ----
  int
  ahprintf(ArchiveHandle *AH, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *p;
  	size_t		len = 128;		/* initial assumption about buffer size */
  	size_t		cnt;
*************** ahprintf(ArchiveHandle *AH, const char *
*** 1616,1621 ****
--- 1619,1625 ----
  		p = (char *) pg_malloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		cnt = pvsnprintf(p, len, fmt, args);
  		va_end(args);
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 007be12..407a56d 100644
*** a/src/bin/pg_dump/pg_backup_tar.c
--- b/src/bin/pg_dump/pg_backup_tar.c
*************** _EndBlobs(ArchiveHandle *AH, TocEntry *t
*** 1026,1031 ****
--- 1026,1032 ----
  static int
  tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *p;
  	size_t		len = 128;		/* initial assumption about buffer size */
  	size_t		cnt;
*************** tarPrintf(ArchiveHandle *AH, TAR_MEMBER 
*** 1038,1043 ****
--- 1039,1045 ----
  		p = (char *) pg_malloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		cnt = pvsnprintf(p, len, fmt, args);
  		va_end(args);
diff --git a/src/common/psprintf.c b/src/common/psprintf.c
index 04465a1..2cf100f 100644
*** a/src/common/psprintf.c
--- b/src/common/psprintf.c
***************
*** 45,50 ****
--- 45,51 ----
  char *
  psprintf(const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	size_t		len = 128;		/* initial assumption about buffer size */
  
  	for (;;)
*************** psprintf(const char *fmt,...)
*** 60,65 ****
--- 61,67 ----
  		result = (char *) palloc(len);
  
  		/* Try to format the data. */
+ 		errno = save_errno;
  		va_start(args, fmt);
  		newlen = pvsnprintf(result, len, fmt, args);
  		va_end(args);
*************** psprintf(const char *fmt,...)
*** 89,94 ****
--- 91,99 ----
   * Other error cases do not return, but exit via elog(ERROR) or exit().
   * Hence, this shouldn't be used inside libpq.
   *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
+  *
   * Note that the semantics of the return value are not exactly C99's.
   * First, we don't promise that the estimated buffer size is exactly right;
   * callers must be prepared to loop multiple times to get the right size.
diff --git a/src/include/c.h b/src/include/c.h
index 901d791..b255da6 100644
*** a/src/include/c.h
--- b/src/include/c.h
*************** typedef union PGAlignedXLogBlock
*** 1114,1127 ****
   * standard C library.
   */
  
- #if !HAVE_DECL_SNPRINTF
- extern int	snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4);
- #endif
- 
- #if !HAVE_DECL_VSNPRINTF
- extern int	vsnprintf(char *str, size_t count, const char *fmt, va_list args);
- #endif
- 
  #if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC
  extern int	fdatasync(int fildes);
  #endif
--- 1114,1119 ----
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 705c52b..254fa80 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 166,175 ****
     don't. */
  #undef HAVE_DECL_RTLD_NOW
  
- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_SNPRINTF
- 
  /* Define to 1 if you have the declaration of `strlcat', and to 0 if you
     don't. */
  #undef HAVE_DECL_STRLCAT
--- 166,171 ----
***************
*** 194,203 ****
     don't. */
  #undef HAVE_DECL_SYS_SIGLIST
  
- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #undef HAVE_DECL_VSNPRINTF
- 
  /* Define to 1 if you have the `dlopen' function. */
  #undef HAVE_DLOPEN
  
--- 190,195 ----
***************
*** 507,515 ****
  /* Define to 1 if you have the `shm_open' function. */
  #undef HAVE_SHM_OPEN
  
- /* Define to 1 if you have the `snprintf' function. */
- #undef HAVE_SNPRINTF
- 
  /* Define to 1 if you have spinlocks. */
  #undef HAVE_SPINLOCKS
  
--- 499,504 ----
***************
*** 712,720 ****
  /* Define to 1 if you have the <uuid/uuid.h> header file. */
  #undef HAVE_UUID_UUID_H
  
- /* Define to 1 if you have the `vsnprintf' function. */
- #undef HAVE_VSNPRINTF
- 
  /* Define to 1 if you have the <wchar.h> header file. */
  #undef HAVE_WCHAR_H
  
--- 701,706 ----
***************
*** 923,931 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  #undef USE_PAM
  
- /* Use replacement snprintf() functions. */
- #undef USE_REPL_SNPRINTF
- 
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #undef USE_SLICING_BY_8_CRC32C
  
--- 909,914 ----
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index eafe377..92d648e 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 135,144 ****
     don't. */
  #define HAVE_DECL_RTLD_NOW 0
  
- /* Define to 1 if you have the declaration of `snprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_SNPRINTF 1
- 
  /* Define to 1 if you have the declaration of `strnlen', and to 0 if you
     don't. */
  #define HAVE_DECL_STRNLEN 1
--- 135,140 ----
***************
*** 151,160 ****
     don't. */
  #define HAVE_DECL_STRTOULL 1
  
- /* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
-    don't. */
- #define HAVE_DECL_VSNPRINTF 1
- 
  /* Define to 1 if you have the `dlopen' function. */
  /* #undef HAVE_DLOPEN */
  
--- 147,152 ----
***************
*** 373,381 ****
  /* Define to 1 if you have the `setsid' function. */
  /* #undef HAVE_SETSID */
  
- /* Define to 1 if you have the `snprintf' function. */
- /* #undef HAVE_SNPRINTF */
- 
  /* Define to 1 if you have spinlocks. */
  #define HAVE_SPINLOCKS 1
  
--- 365,370 ----
***************
*** 553,561 ****
  /* Define to 1 if you have the <utime.h> header file. */
  #define HAVE_UTIME_H 1
  
- /* Define to 1 if you have the `vsnprintf' function. */
- #define HAVE_VSNPRINTF 1
- 
  /* Define to 1 if you have the <wchar.h> header file. */
  #define HAVE_WCHAR_H 1
  
--- 542,547 ----
***************
*** 712,720 ****
  /* Define to 1 to build with PAM support. (--with-pam) */
  /* #undef USE_PAM */
  
- /* Use replacement snprintf() functions. */
- #define USE_REPL_SNPRINTF 1
- 
  /* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
  #if (_MSC_VER < 1500)
  #define USE_SLICING_BY_8_CRC32C 1
--- 698,703 ----
diff --git a/src/include/port.h b/src/include/port.h
index bffc773..0f0cd28 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern unsigned char pg_tolower(unsigned
*** 134,140 ****
  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
--- 134,145 ----
  extern unsigned char pg_ascii_toupper(unsigned char ch);
  extern unsigned char pg_ascii_tolower(unsigned char ch);
  
! /*
!  * Beginning in v12, we always replace snprintf() and friends with our own
!  * implementation.  This symbol is no longer consulted by the core code,
!  * but keep it defined anyway in case any extensions are looking at it.
!  */
! #define USE_REPL_SNPRINTF 1
  
  /*
   * Versions of libintl >= 0.13 try to replace printf() and friends with
*************** extern int	pg_printf(const char *fmt,...
*** 187,193 ****
  #define fprintf			pg_fprintf
  #define printf			pg_printf
  #endif
- #endif							/* USE_REPL_SNPRINTF */
  
  /* Replace strerror() with our own, somewhat more robust wrapper */
  extern char *pg_strerror(int errnum);
--- 192,197 ----
diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile
index b7bd162..e07a7fa 100644
*** a/src/interfaces/ecpg/compatlib/Makefile
--- b/src/interfaces/ecpg/compatlib/Makefile
*************** SHLIB_EXPORTS = exports.txt
*** 31,37 ****
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))
  
! OBJS= informix.o strerror.o $(filter snprintf.o strnlen.o, $(LIBOBJS)) $(WIN32RES)
  
  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes
  
--- 31,38 ----
  # Need to recompile any libpgport object files
  LIBS := $(filter-out -lpgport, $(LIBS))
  
! OBJS= informix.o snprintf.o strerror.o \
! 	$(filter strnlen.o, $(LIBOBJS)) $(WIN32RES)
  
  PKG_CONFIG_REQUIRES_PRIVATE = libecpg libpgtypes
  
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index 005d25a..b381623 100644
*** a/src/interfaces/ecpg/ecpglib/Makefile
--- b/src/interfaces/ecpg/ecpglib/Makefile
*************** override CFLAGS += $(PTHREAD_CFLAGS)
*** 26,33 ****
  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 strerror.o \
! 	$(filter snprintf.o strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
  	$(WIN32RES)
  
  # thread.c is needed only for non-WIN32 implementation of path.c
--- 26,33 ----
  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 snprintf.o strerror.o \
! 	$(filter strlcpy.o strnlen.o win32setlocale.o isinf.o, $(LIBOBJS)) \
  	$(WIN32RES)
  
  # thread.c is needed only for non-WIN32 implementation of path.c
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
index 18b2402..15d7f33 100644
*** a/src/interfaces/ecpg/pgtypeslib/Makefile
--- b/src/interfaces/ecpg/pgtypeslib/Makefile
*************** SHLIB_LINK += $(filter -lm, $(LIBS))
*** 30,37 ****
  SHLIB_EXPORTS = exports.txt
  
  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
! 	pgstrcasecmp.o strerror.o \
! 	$(filter rint.o snprintf.o strnlen.o, $(LIBOBJS)) \
  	string.o \
  	$(WIN32RES)
  
--- 30,37 ----
  SHLIB_EXPORTS = exports.txt
  
  OBJS= numeric.o datetime.o common.o dt_common.o timestamp.o interval.o \
! 	pgstrcasecmp.o snprintf.o strerror.o \
! 	$(filter rint.o strnlen.o, $(LIBOBJS)) \
  	string.o \
  	$(WIN32RES)
  
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 8324f4f..a106088 100644
*** a/src/interfaces/libpq/Makefile
--- b/src/interfaces/libpq/Makefile
*************** OBJS=	fe-auth.o fe-auth-scram.o fe-conne
*** 36,44 ****
  	libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
! 	strerror.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 strlcpy.o strnlen.o win32error.o win32setlocale.o, $(LIBOBJS))
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 36,44 ----
  	libpq-events.o
  # libpgport C files we always use
  OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \
! 	snprintf.o strerror.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 strlcpy.o strnlen.o win32error.o win32setlocale.o, $(LIBOBJS))
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
index 0814ec6..43c36c3 100644
*** a/src/interfaces/libpq/pqexpbuffer.c
--- b/src/interfaces/libpq/pqexpbuffer.c
*************** enlargePQExpBuffer(PQExpBuffer str, size
*** 233,238 ****
--- 233,239 ----
  void
  printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	va_list		args;
  	bool		done;
  
*************** printfPQExpBuffer(PQExpBuffer str, const
*** 244,249 ****
--- 245,251 ----
  	/* Loop in case we have to retry after enlarging the buffer. */
  	do
  	{
+ 		errno = save_errno;
  		va_start(args, fmt);
  		done = appendPQExpBufferVA(str, fmt, args);
  		va_end(args);
*************** printfPQExpBuffer(PQExpBuffer str, const
*** 261,266 ****
--- 263,269 ----
  void
  appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	va_list		args;
  	bool		done;
  
*************** appendPQExpBuffer(PQExpBuffer str, const
*** 270,275 ****
--- 273,279 ----
  	/* Loop in case we have to retry after enlarging the buffer. */
  	do
  	{
+ 		errno = save_errno;
  		va_start(args, fmt);
  		done = appendPQExpBufferVA(str, fmt, args);
  		va_end(args);
*************** appendPQExpBuffer(PQExpBuffer str, const
*** 281,286 ****
--- 285,293 ----
   * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
   * Attempt to format data and append it to str.  Returns true if done
   * (either successful or hard failure), false if need to retry.
+  *
+  * Caution: callers must be sure to preserve their entry-time errno
+  * when looping, in case the fmt contains "%m".
   */
  static bool
  appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index e6241f0..f8888a4 100644
*** a/src/pl/plperl/plperl.h
--- b/src/pl/plperl/plperl.h
***************
*** 29,39 ****
   * Sometimes perl carefully scribbles on our *printf macros.
   * So we undefine them here and redefine them after it's done its dirty deed.
   */
- 
- #ifdef USE_REPL_SNPRINTF
  #undef snprintf
  #undef vsnprintf
- #endif
  
  /*
   * ActivePerl 5.18 and later are MinGW-built, and their headers use GCC's
--- 29,36 ----
***************
*** 99,105 ****
  #endif
  
  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 96,101 ----
***************
*** 113,119 ****
  #define vsnprintf		pg_vsnprintf
  #define snprintf		pg_snprintf
  #endif							/* __GNUC__ */
- #endif							/* USE_REPL_SNPRINTF */
  
  /* perl version and platform portability */
  #define NEED_eval_pv
--- 109,114 ----
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index e244104..3814a6c 100644
*** a/src/pl/plpython/plpy_elog.c
--- b/src/pl/plpython/plpy_elog.c
*************** static bool set_string_attr(PyObject *ob
*** 46,51 ****
--- 46,52 ----
  void
  PLy_elog_impl(int elevel, const char *fmt,...)
  {
+ 	int			save_errno = errno;
  	char	   *xmsg;
  	char	   *tbmsg;
  	int			tb_depth;
*************** PLy_elog_impl(int elevel, const char *fm
*** 96,101 ****
--- 97,103 ----
  			va_list		ap;
  			int			needed;
  
+ 			errno = save_errno;
  			va_start(ap, fmt);
  			needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
  			va_end(ap);
diff --git a/src/pl/plpython/plpython.h b/src/pl/plpython/plpython.h
index 6cc323a..aefbfc2 100644
*** a/src/pl/plpython/plpython.h
--- b/src/pl/plpython/plpython.h
***************
*** 33,43 ****
   * Sometimes python carefully scribbles on our *printf macros.
   * 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
--- 33,40 ----
*************** typedef int Py_ssize_t;
*** 124,130 ****
  #include <eval.h>
  
  /* put back our snprintf and vsnprintf */
- #ifdef USE_REPL_SNPRINTF
  #ifdef snprintf
  #undef snprintf
  #endif
--- 121,126 ----
*************** typedef int Py_ssize_t;
*** 138,144 ****
  #define vsnprintf				pg_vsnprintf
  #define snprintf				pg_snprintf
  #endif							/* __GNUC__ */
- #endif							/* USE_REPL_SNPRINTF */
  
  /*
   * Used throughout, and also by the Python 2/3 porting layer, so it's easier to
--- 134,139 ----
diff --git a/src/port/Makefile b/src/port/Makefile
index b3a10ba..a2ee8e2 100644
*** a/src/port/Makefile
--- b/src/port/Makefile
*************** LIBS += $(PTHREAD_LIBS)
*** 33,39 ****
  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 strerror.o tar.o thread.o
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
--- 33,40 ----
  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 snprintf.o sprompt.o strerror.o \
! 	tar.o thread.o
  
  ifeq ($(enable_strong_random), yes)
  OBJS += pg_strong_random.o
diff --git a/src/port/README b/src/port/README
index 4ae96da..c446b46 100644
*** a/src/port/README
--- b/src/port/README
*************** and adding infrastructure to recompile t
*** 18,24 ****
  
          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter snprintf.o, $(LIBOBJS))
  
  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
--- 18,24 ----
  
          OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
                  connect.o misc.o path.o exec.o \
!                 $(filter strlcat.o, $(LIBOBJS))
  
  The problem is that there is no testing of which object files need to be
  added, but missing functions usually show up when linking user
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 851e2ae..63cec5d 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
***************
*** 64,69 ****
--- 64,77 ----
   *
   * 5. Space and '#' flags are not implemented.
   *
+  * In addition, we support some extensions over C99:
+  *
+  * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
+  *
+  * 2. "%m" expands to the value of strerror(errno), where errno is the
+  * value that variable had at the start of the call.  This is a glibc
+  * extension, but a very useful one.
+  *
   *
   * Historically the result values of sprintf/snprintf varied across platforms.
   * This implementation now follows the C99 standard:
*************** static void flushbuffer(PrintfTarget *ta
*** 155,160 ****
--- 163,175 ----
  static void dopr(PrintfTarget *target, const char *format, va_list args);
  
  
+ /*
+  * Externally visible entry points.
+  *
+  * All of these are just wrappers around dopr().  Note it's essential that
+  * they not change the value of "errno" before reaching dopr().
+  */
+ 
  int
  pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
  {
*************** static void trailing_pad(int *padlen, Pr
*** 315,325 ****
  
  
  /*
!  * dopr(): poor man's version of doprintf
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
  	const char *format_start = format;
  	int			ch;
  	bool		have_dollar;
--- 330,341 ----
  
  
  /*
!  * dopr(): the guts of *printf for all cases.
   */
  static void
  dopr(PrintfTarget *target, const char *format, va_list args)
  {
+ 	int			save_errno = errno;
  	const char *format_start = format;
  	int			ch;
  	bool		have_dollar;
*************** nextch1:
*** 497,502 ****
--- 513,519 ----
  				else
  					have_non_dollar = true;
  				break;
+ 			case 'm':
  			case '%':
  				break;
  		}
*************** nextch2:
*** 802,807 ****
--- 819,831 ----
  						 precision, pointflag,
  						 target);
  				break;
+ 			case 'm':
+ 				{
+ 					const char *errm = strerror(save_errno);
+ 
+ 					dostr(errm, strlen(errm), target);
+ 				}
+ 				break;
  			case '%':
  				dopr_outch('%', target);
  				break;

Reply via email to