Re: Allowing printf("%m") only where it actually works - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: Allowing printf("%m") only where it actually works |
Date | |
Msg-id | 13585.1537809527@sss.pgh.pa.us Whole thread Raw |
In response to | Re: Allowing printf("%m") only where it actually works (Michael Paquier <michael@paquier.xyz>) |
Responses |
Re: Allowing printf("%m") only where it actually works
Re: Allowing printf("%m") only where it actually works |
List | pgsql-hackers |
Michael Paquier <michael@paquier.xyz> writes: > On Wed, Sep 12, 2018 at 01:40:01PM -0400, Tom Lane wrote: >> Rebase attached --- no substantive changes. > - if (handleDLL == NULL) > - ereport(FATAL, > - (errmsg_internal("could not load netmsg.dll: error > - code %lu", GetLastError()))); > In 0001, this is replaced by a non-FATAL error for the backend, which > does not seem like a good idea to me because the user loses visibility > with this DDL which canot be loaded. I still have to see this error... Well, we have to change the code somehow to make it usable in frontend as well as backend. And we can *not* have it do exit(1) in libpq. So the solution I chose was to make it act the same as if FormatMessage were to fail. I don't find this behavior unreasonable: what is really important is the original error code, not whether we were able to pretty-print it. I think the ereport(FATAL) coding is a pretty darn bad idea even in the backend. > Could you drop the configure checks for snprintf and vsnprintf in a > separate patch? The cleanup of %m and the removal of those switches > should be treated separatly in my opinion. Seems a bit make-worky, but here you go. 0001 is the same as before (but rebased up to today, so some line numbers change). 0002 changes things so that we always use our snprintf, removing all the configure logic associated with that. 0003 implements %m in snprintf.c and adjusts our various printf-wrapper functions to ensure that they pass errno through reliably. 0004 changes elog.c to rely on %m being implemented below it. regards, tom lane diff --git a/configure b/configure index 9b30402..afbc142 100755 *** a/configure --- b/configure *************** esac *** 15635,15653 **** 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 --- 15635,15640 ---- diff --git a/configure.in b/configure.in index 2e60a89..6973b77 100644 *** a/configure.in --- b/configure.in *************** else *** 1678,1684 **** 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 --- 1678,1684 ---- 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 2522ebc..cc2648c 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.owin32error.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.owin32setlocale.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 6484a67..640a4b7 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 afbc142..31082da 100755 *** a/configure --- b/configure *************** $as_echo "#define HAVE_PS_STRINGS 1" >>c *** 15358,15454 **** 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 : --- 15358,15363 ---- *************** fi *** 16166,16218 **** # 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; } --- 16075,16080 ---- *************** _ACEOF *** 16392,16399 **** # 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 --- 16254,16259 ---- *************** cat >>confdefs.h <<_ACEOF *** 16406,16525 **** _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 --- 16266,16271 ---- diff --git a/configure.in b/configure.in index 6973b77..251759e 100644 *** a/configure.in --- b/configure.in *************** if test "$pgac_cv_var_PS_STRINGS" = yes *** 1613,1665 **** 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([ --- 1613,1618 ---- *************** for the exact reason.]])], *** 1829,1844 **** # 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 --- 1782,1787 ---- *************** AC_DEFINE_UNQUOTED(PG_INT64_TYPE, $pg_in *** 1861,1868 **** [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 --- 1804,1809 ---- *************** fi *** 1872,1901 **** 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 --- 1813,1818 ---- diff --git a/src/include/c.h b/src/include/c.h index 13c794d..25d7d60 100644 *** a/src/include/c.h --- b/src/include/c.h *************** typedef union PGAlignedXLogBlock *** 1147,1160 **** * 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 --- 1147,1152 ---- 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 cc2648c..522bd11 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.owin32setlocale.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/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/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/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/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 7d1d439..ca73a55 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) *** 1507,1512 **** --- 1507,1513 ---- 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, *** 1519,1524 **** --- 1520,1526 ---- 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 *** 1640,1645 **** --- 1642,1648 ---- 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 * *** 1652,1657 **** --- 1655,1661 ---- 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/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/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/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; 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 ----
pgsql-hackers by date: