Thread: Allowing printf("%m") only where it actually works
<digression> For amusement's sake, I was playing around with NetBSD-current (9-to-be) today, and tried to compile Postgres on it. It works OK --- and I can even confirm that our new code for using ARM v8 CRC instructions works there --- but I got a boatload of compile warnings like this: latch.c:1180:4: warning: %m is only allowed in syslog(3) like functions [-Wformat=] ereport(ERROR, ^~~~~~~ A bit of googling turned up the patch that caused this [1], which was soon followed by some well-reasoned push-back [2]; but the warning's still there, so evidently the forces of bullheadedness won. I was ready to discount the whole thing as being another badly designed no-wonder-gcc-upstream-won't-take-it compiler warning, when I noticed that the last few warnings in my output were pointing out a live bug, to wit using %m with plain old printf rather than elog/ereport. So I fixed that [3], but I'm thinking that we need to take a bit more care here. </digression> Looking around, we have circa seventy-five functions declared with pg_attribute_printf in our tree right now, and of those, *only* the elog/ereport support functions can be relied on to process %m correctly. However, anyone who's accustomed to working in backend code is likely to not think hard about using %m in an error message, as indeed the authors and reviewers of pg_verify_checksums did not. Worse yet, such cases actually will work as long as you're testing on glibc platforms, only to fail everywhere else. So I think we need to try to make provisions for getting compiler warnings when %m is used in a function that doesn't support it. gcc on Linux-ish platforms isn't going to be very helpful with this, but that doesn't mean that we should confuse %m-supporting and not-%m-supporting functions, as we do right now. Hence, I think we need something roughly like the attached patch, which arranges to use "gnu_printf" (if available) as the format archetype for the elog/ereport functions, and plain "printf" for all the rest. With some additional hackery not included here, this can be ju-jitsu'd into compiling warning-free on NetBSD-current. (The basic idea is to extend PGAC_C_PRINTF_ARCHETYPE so it will select __syslog__ as the archetype if available; but then you need some hack to suppress the follow-on warnings complained of in [2]. I haven't decided what's the least ugly solution for the latter, so I'm not proposing such a patch yet.) What I'm mainly concerned about at this stage is what effects this'll have on Windows builds. The existing comment for PGAC_C_PRINTF_ARCHETYPE claims that using gnu_printf silences complaints about "%lld" and related formats on Windows, but I wonder whether that is still true on Windows versions we still support. As I mentioned in [4], I don't think we really work any longer on platforms that don't use "%lld" for "long long" values, and it seems that Windows does accept that in post-XP versions --- but has gcc gotten the word? If this does work as desired on Windows, then that would be a fairly mainstream platform that can produce warnings about wrong uses of %m, even if gcc-on-Linux doesn't. If worst comes to worst, somebody could crank up a buildfarm machine with a newer NetBSD release. Anyway, I don't feel a need to cram this into v11, since I just fixed the live bugs of this ilk in HEAD; it seems like it can wait for v12. So I'll add this to the next commitfest. regards, tom lane [1] https://mail-index.netbsd.org/tech-userlevel/2015/08/21/msg009282.html [2] https://mail-index.netbsd.org/tech-userlevel/2015/10/23/msg009371.html [3] https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=a13b47a59ffce6f3c13c8b777738a3aab1db10d3 [4] https://www.postgresql.org/message-id/13103.1526749980@sss.pgh.pa.us diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index ba5c40d..5cd8994 100644 --- a/config/c-compiler.m4 +++ b/config/c-compiler.m4 @@ -19,10 +19,10 @@ fi])# PGAC_C_SIGNED # PGAC_C_PRINTF_ARCHETYPE # ----------------------- -# Set the format archetype used by gcc to check printf type functions. We -# prefer "gnu_printf", which includes what glibc uses, such as %m for error -# strings and %lld for 64 bit long longs. GCC 4.4 introduced it. It makes a -# dramatic difference on Windows. +# Set the format archetype used by gcc to check elog/ereport functions. +# This should accept %m, whether or not the platform's printf does. +# We use "gnu_printf" if possible, which does that, although in some cases +# it might do more than we could wish. 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 @@ -34,8 +34,8 @@ __attribute__((format(gnu_printf, 2, 3)));], [])], [pgac_cv_printf_archetype=gnu_printf], [pgac_cv_printf_archetype=printf]) ac_c_werror_flag=$ac_save_c_werror_flag]) -AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype], - [Define to gnu_printf if compiler supports it, else printf.]) +AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE_M], [$pgac_cv_printf_archetype], + [Define as a format archetype that accepts %m, if available, else printf.]) ])# PGAC_PRINTF_ARCHETYPE diff --git a/configure b/configure index b244fc3..df12bb5 100755 --- a/configure +++ b/configure @@ -13305,7 +13305,7 @@ fi $as_echo "$pgac_cv_printf_archetype" >&6; } cat >>confdefs.h <<_ACEOF -#define PG_PRINTF_ATTRIBUTE $pgac_cv_printf_archetype +#define PG_PRINTF_ATTRIBUTE_M $pgac_cv_printf_archetype _ACEOF diff --git a/src/include/c.h b/src/include/c.h index 1e50103..0a4757e 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -126,10 +126,14 @@ /* GCC and XLC support format attributes */ #if defined(__GNUC__) || defined(__IBMC__) #define pg_attribute_format_arg(a) __attribute__((format_arg(a))) -#define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a))) +/* Use for functions wrapping stdio's printf, which often doesn't take %m: */ +#define pg_attribute_printf(f,a) __attribute__((format(printf, f, a))) +/* Use for elog/ereport, which implement %m for themselves: */ +#define pg_attribute_printf_m(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE_M, f, a))) #else #define pg_attribute_format_arg(a) #define pg_attribute_printf(f,a) +#define pg_attribute_printf_m(f,a) #endif /* GCC, Sunpro and XLC support aligned, packed and noreturn */ diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index c3320f2..92daabc 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -799,8 +799,8 @@ /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION -/* Define to gnu_printf if compiler supports it, else printf. */ -#undef PG_PRINTF_ATTRIBUTE +/* Define as a format archetype that accepts %m, if available, else printf. */ +#undef PG_PRINTF_ATTRIBUTE_M /* PostgreSQL version as a string */ #undef PG_VERSION diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7a9ba7f..4f4091d 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -133,25 +133,25 @@ extern int errcode(int sqlerrcode); extern int errcode_for_file_access(void); extern int errcode_for_socket_access(void); -extern int errmsg(const char *fmt,...) pg_attribute_printf(1, 2); -extern int errmsg_internal(const char *fmt,...) pg_attribute_printf(1, 2); +extern int errmsg(const char *fmt,...) pg_attribute_printf_m(1, 2); +extern int errmsg_internal(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural, - unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); + unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); -extern int errdetail(const char *fmt,...) pg_attribute_printf(1, 2); -extern int errdetail_internal(const char *fmt,...) pg_attribute_printf(1, 2); +extern int errdetail(const char *fmt,...) pg_attribute_printf_m(1, 2); +extern int errdetail_internal(const char *fmt,...) pg_attribute_printf_m(1, 2); -extern int errdetail_log(const char *fmt,...) pg_attribute_printf(1, 2); +extern int errdetail_log(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, - unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); + unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural, - unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); + unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); -extern int errhint(const char *fmt,...) pg_attribute_printf(1, 2); +extern int errhint(const char *fmt,...) pg_attribute_printf_m(1, 2); /* * errcontext() is typically called in error context callback functions, not @@ -165,7 +165,7 @@ extern int errhint(const char *fmt,...) pg_attribute_printf(1, 2); extern int set_errcontext_domain(const char *domain); -extern int errcontext_msg(const char *fmt,...) pg_attribute_printf(1, 2); +extern int errcontext_msg(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errhidestmt(bool hide_stmt); extern int errhidecontext(bool hide_ctx); @@ -222,13 +222,13 @@ extern int getinternalerrposition(void); #endif /* HAVE__VA_ARGS */ extern void elog_start(const char *filename, int lineno, const char *funcname); -extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf(2, 3); +extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf_m(2, 3); /* Support for constructing error strings separately from ereport() calls */ extern void pre_format_elog_string(int errnumber, const char *domain); -extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2); +extern char *format_elog_string(const char *fmt,...) pg_attribute_printf_m(1, 2); /* Support for attaching context information to error reports */ @@ -407,9 +407,9 @@ extern void set_syslog_parameters(const char *ident, int facility); #endif /* - * Write errors to stderr (or by equal means when stderr is - * not available). Used before ereport/elog can be used - * safely (memory context, GUC load etc) + * Write errors to stderr (or by comparable means when stderr is not + * available). Used before ereport/elog can be used safely (memory context, + * GUC load etc). Note that this does *not* accept "%m". */ extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
On Mon, May 21, 2018 at 12:30 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > For amusement's sake, I was playing around with NetBSD-current (9-to-be) > today, and tried to compile Postgres on it. It works OK --- and I can > even confirm that our new code for using ARM v8 CRC instructions works Excellent news. > there --- but I got a boatload of compile warnings like this: > > latch.c:1180:4: warning: %m is only allowed in syslog(3) like functions [-Wformat=] > ereport(ERROR, > ^~~~~~~ > > A bit of googling turned up the patch that caused this [1], which was > soon followed by some well-reasoned push-back [2]; but the warning's > still there, so evidently the forces of bullheadedness won. I was > ready to discount the whole thing as being another badly designed > no-wonder-gcc-upstream-won't-take-it compiler warning, when I noticed that > the last few warnings in my output were pointing out a live bug, to wit > using %m with plain old printf rather than elog/ereport. So I fixed > that [3], but I'm thinking that we need to take a bit more care here. I tried this on macOS and FreeBSD using GCC and Clang: both accept printf("%m") without warning and then just print out "m". It'll be interesting to see if the NetBSD patch/idea travels further or some other solution can be found. I've raised this on the freebsd-hackers list, let's see... I bet there's other software out there that just prints out "m" when things go wrong. It's arguably something that you'd want the complier to understand as a C dialect thing. -- Thomas Munro http://www.enterprisedb.com
Thomas Munro <thomas.munro@enterprisedb.com> writes: > I tried this on macOS and FreeBSD using GCC and Clang: both accept > printf("%m") without warning and then just print out "m". It'll be > interesting to see if the NetBSD patch/idea travels further or some > other solution can be found. I've raised this on the freebsd-hackers > list, let's see... I bet there's other software out there that just > prints out "m" when things go wrong. It's arguably something that > you'd want the complier to understand as a C dialect thing. Yeah. ISTM that the netbsd guys didn't get this quite right. The gcc docs are perfectly clear about what they think the semantics should be: The parameter archetype determines how the format string is interpreted, and should be printf, scanf, strftime, gnu_printf, gnu_scanf, gnu_strftime or strfmon. ... archetype values such as printf refer to the formats accepted by the system’s C runtime library, while values prefixed with ‘gnu_’ always refer to the formats accepted by the GNU C Library. Therefore, what netbsd should have done was leave the semantics of "gnu_printf" alone (because glibc undoubtedly does take %m), and just emit a warning with the "printf" archetype --- which is supposed to describe the behavior of the platform's stdio, which in their case is known not to take %m. If they'd done it that way then they'd have a patch that gcc upstream certainly ought to accept, because it follows gcc's own stated semantics for the check. This would also partially resolve the complaint Roy Marples had in the thread I alluded to, ie he could use "gnu_printf" to describe a function that accepts %m. (There might still need to be some work to be done to avoid bogus -Wmissing-format-attribute complaints, not sure.) I'm not sure whether a separate archetype for syslog is really needed. Formally you could say that distinguishing syslog from GNU printf is wise, but I'm not sure I see a practical need for it. regards, tom lane
... and, while we're thinking about this, how can we prevent the reverse problem of using strerror(errno) where you should have used %m? That is evidently not academic either, cf 81256cd. I am wondering whether the elog/ereport macros can locally define some version of "errno" that would cause a compile failure if it's referenced within the macro args. But I'm too tired to work it out in detail. regards, tom lane
On Mon, May 21, 2018 at 4:36 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > ... and, while we're thinking about this, how can we prevent the reverse > problem of using strerror(errno) where you should have used %m? That > is evidently not academic either, cf 81256cd. > > I am wondering whether the elog/ereport macros can locally define some > version of "errno" that would cause a compile failure if it's referenced > within the macro args. But I'm too tired to work it out in detail. Here's an experimental way to do that, if you don't mind depending on gory details of libc implementations (ie knowledge of what it expands too). Not sure how to avoid that since it's a macro on all modern systems, and we don't have a way to temporarily redefine a macro. If you enable it for just ereport(), it compiles cleanly after 81256cd (but fails on earlier commits). If you enable it for elog() too then it finds problems with exec.c. Another idea: if there are any systems in the build farm where it isn't a macro as permitted by the standard (#ifndef errno), you could perhaps define it as something uncompilable and then undefined it at the end of the scope, so we'd at least have a post-commit canary. -- Thomas Munro http://www.enterprisedb.com
Attachment
Thomas Munro <thomas.munro@enterprisedb.com> writes: > On Mon, May 21, 2018 at 4:36 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> I am wondering whether the elog/ereport macros can locally define some >> version of "errno" that would cause a compile failure if it's referenced >> within the macro args. But I'm too tired to work it out in detail. > Here's an experimental way to do that, if you don't mind depending on > gory details of libc implementations (ie knowledge of what it expands > too). Not sure how to avoid that since it's a macro on all modern > systems, and we don't have a way to temporarily redefine a macro. If > you enable it for just ereport(), it compiles cleanly after 81256cd > (but fails on earlier commits). If you enable it for elog() too then > it finds problems with exec.c. Hmm ... that's pretty duff code in exec.c, isn't it. Aside from the question of errno unsafety, it's using elog where it really ought to be using ereport, it's not taking any thought for the reported SQLSTATE, etc. I'm hesitant to mess with it mere hours before the beta wrap, but we really oughta improve that. I noticed another can of worms here, too: on Windows, doesn't use of GetLastError() in elog/ereport have exactly the same hazard as errno? Or is there some reason to think it can't change value during errstart()? regards, tom lane
On Mon, May 21, 2018 at 2:41 PM, Thomas Munro <thomas.munro@enterprisedb.com> wrote: > I've raised this on the freebsd-hackers > list, let's see... I bet there's other software out there that just > prints out "m" when things go wrong. It's arguably something that > you'd want the complier to understand as a C dialect thing. Result: FreeBSD printf() now supports %m, and an issue is being raised with clang about lack of warnings on OSes that don't grok %m, and lack of warnings when you say -Wformat-non-iso on any OS. Let's see how that goes. -- Thomas Munro http://www.enterprisedb.com
I wrote: > Thomas Munro <thomas.munro@enterprisedb.com> writes: >> Here's an experimental way to do that, if you don't mind depending on >> gory details of libc implementations (ie knowledge of what it expands >> too). Not sure how to avoid that since it's a macro on all modern >> systems, and we don't have a way to temporarily redefine a macro. If >> you enable it for just ereport(), it compiles cleanly after 81256cd >> (but fails on earlier commits). If you enable it for elog() too then >> it finds problems with exec.c. > Hmm ... that's pretty duff code in exec.c, isn't it. Aside from the > question of errno unsafety, it's using elog where it really ought to be > using ereport, it's not taking any thought for the reported SQLSTATE, > etc. I'm hesitant to mess with it mere hours before the beta wrap, > but we really oughta improve that. I wrote up a patch that makes src/common/exec.c do error reporting more like other frontend/backend-common files (attached). Now that I've done so, though, I'm having second thoughts. The thing that I don't like about this is that it doubles the number of translatable strings created by this file. While there's not *that* many of them, translators have to deal with each one several times because this file is included by several different frontend programs. So that seems like a rather high price to pay to deal with what, at present, is a purely hypothetical hazard. (Basically what this would protect against is elog_start changing errno, which it doesn't.) Improving the errcode situation is somewhat useful, but still maybe it's not worth the cost. Another approach we could consider is keeping exec.c's one-off approach to error handling and letting it redefine pg_prevent_errno_in_scope() as empty. But that's ugly. Or we could make the affected call sites work like this: int save_errno = errno; log_error(_("could not identify current directory: %s"), strerror(save_errno)); which on the whole might be the most expedient thing. regards, tom lane diff --git a/src/common/exec.c b/src/common/exec.c index 4df16cd..f0d52e1 100644 *** a/src/common/exec.c --- b/src/common/exec.c *************** *** 25,40 **** #include <sys/wait.h> #include <unistd.h> - #ifndef FRONTEND - /* We use only 3- and 4-parameter elog calls in this file, for simplicity */ - /* NOTE: caller must provide gettext call around str! */ - #define log_error(str, param) elog(LOG, str, param) - #define log_error4(str, param, arg1) elog(LOG, str, param, arg1) - #else - #define log_error(str, param) (fprintf(stderr, str, param), fputc('\n', stderr)) - #define log_error4(str, param, arg1) (fprintf(stderr, str, param, arg1), fputc('\n', stderr)) - #endif - #ifdef _MSC_VER #define getcwd(cwd,len) GetCurrentDirectory(len, cwd) #endif --- 25,30 ---- *************** find_my_exec(const char *argv0, char *re *** 124,131 **** if (!getcwd(cwd, MAXPGPATH)) { ! log_error(_("could not identify current directory: %s"), ! strerror(errno)); return -1; } --- 114,127 ---- if (!getcwd(cwd, MAXPGPATH)) { ! #ifndef FRONTEND ! ereport(LOG, ! (errcode_for_file_access(), ! errmsg("could not identify current directory: %m"))); ! #else ! fprintf(stderr, _("could not identify current directory: %s\n"), ! strerror(errno)); ! #endif return -1; } *************** find_my_exec(const char *argv0, char *re *** 143,149 **** if (validate_exec(retpath) == 0) return resolve_symlinks(retpath); ! log_error(_("invalid binary \"%s\""), retpath); return -1; } --- 139,149 ---- if (validate_exec(retpath) == 0) return resolve_symlinks(retpath); ! #ifndef FRONTEND ! ereport(LOG, (errmsg("invalid binary \"%s\"", retpath))); ! #else ! fprintf(stderr, _("invalid binary \"%s\"\n"), retpath); ! #endif return -1; } *************** find_my_exec(const char *argv0, char *re *** 192,205 **** case -1: /* wasn't even a candidate, keep looking */ break; case -2: /* found but disqualified */ ! log_error(_("could not read binary \"%s\""), ! retpath); break; } } while (*endp); } ! log_error(_("could not find a \"%s\" to execute"), argv0); return -1; } --- 192,214 ---- case -1: /* wasn't even a candidate, keep looking */ break; case -2: /* found but disqualified */ ! #ifndef FRONTEND ! ereport(LOG, (errmsg("could not read binary \"%s\"", ! retpath))); ! #else ! fprintf(stderr, _("could not read binary \"%s\"\n"), ! retpath); ! #endif break; } } while (*endp); } ! #ifndef FRONTEND ! ereport(LOG, (errmsg("could not find a \"%s\" to execute", argv0))); ! #else ! fprintf(stderr, _("could not find a \"%s\" to execute\n"), argv0); ! #endif return -1; } *************** resolve_symlinks(char *path) *** 238,245 **** */ if (!getcwd(orig_wd, MAXPGPATH)) { ! log_error(_("could not identify current directory: %s"), ! strerror(errno)); return -1; } --- 247,260 ---- */ if (!getcwd(orig_wd, MAXPGPATH)) { ! #ifndef FRONTEND ! ereport(LOG, ! (errcode_for_file_access(), ! errmsg("could not identify current directory: %m"))); ! #else ! fprintf(stderr, _("could not identify current directory: %s\n"), ! strerror(errno)); ! #endif return -1; } *************** resolve_symlinks(char *path) *** 254,260 **** *lsep = '\0'; if (chdir(path) == -1) { ! log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno)); return -1; } fname = lsep + 1; --- 269,283 ---- *lsep = '\0'; if (chdir(path) == -1) { ! #ifndef FRONTEND ! ereport(LOG, ! (errcode_for_file_access(), ! errmsg("could not change directory to \"%s\": %m", ! path))); ! #else ! fprintf(stderr, _("could not change directory to \"%s\": %s\n"), ! path, strerror(errno)); ! #endif return -1; } fname = lsep + 1; *************** resolve_symlinks(char *path) *** 269,275 **** rllen = readlink(fname, link_buf, sizeof(link_buf)); if (rllen < 0 || rllen >= sizeof(link_buf)) { ! log_error(_("could not read symbolic link \"%s\""), fname); return -1; } link_buf[rllen] = '\0'; --- 292,303 ---- rllen = readlink(fname, link_buf, sizeof(link_buf)); if (rllen < 0 || rllen >= sizeof(link_buf)) { ! #ifndef FRONTEND ! ereport(LOG, ! (errmsg("could not read symbolic link \"%s\"", fname))); ! #else ! fprintf(stderr, _("could not read symbolic link \"%s\"\n"), fname); ! #endif return -1; } link_buf[rllen] = '\0'; *************** resolve_symlinks(char *path) *** 281,288 **** if (!getcwd(path, MAXPGPATH)) { ! log_error(_("could not identify current directory: %s"), ! strerror(errno)); return -1; } join_path_components(path, path, link_buf); --- 309,322 ---- if (!getcwd(path, MAXPGPATH)) { ! #ifndef FRONTEND ! ereport(LOG, ! (errcode_for_file_access(), ! errmsg("could not identify current directory: %m"))); ! #else ! fprintf(stderr, _("could not identify current directory: %s\n"), ! strerror(errno)); ! #endif return -1; } join_path_components(path, path, link_buf); *************** resolve_symlinks(char *path) *** 290,296 **** if (chdir(orig_wd) == -1) { ! log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno)); return -1; } #endif /* HAVE_READLINK */ --- 324,338 ---- if (chdir(orig_wd) == -1) { ! #ifndef FRONTEND ! ereport(LOG, ! (errcode_for_file_access(), ! errmsg("could not change directory to \"%s\": %m", ! orig_wd))); ! #else ! fprintf(stderr, _("could not change directory to \"%s\": %s\n"), ! orig_wd, strerror(errno)); ! #endif return -1; } #endif /* HAVE_READLINK */ *************** pclose_check(FILE *stream) *** 520,536 **** if (exitstatus == -1) { /* pclose() itself failed, and hopefully set errno */ ! log_error(_("pclose failed: %s"), strerror(errno)); } else { reason = wait_result_to_str(exitstatus); ! log_error("%s", reason); ! #ifdef FRONTEND ! free(reason); #else ! pfree(reason); #endif } return exitstatus; } --- 562,582 ---- if (exitstatus == -1) { /* pclose() itself failed, and hopefully set errno */ ! #ifndef FRONTEND ! elog(LOG, "pclose failed: %m"); ! #else ! fprintf(stderr, "pclose failed: %s\n", strerror(errno)); ! #endif } else { reason = wait_result_to_str(exitstatus); ! #ifndef FRONTEND ! elog(LOG, "%s", reason); #else ! fprintf(stderr, "%s\n", reason); #endif + pfree(reason); } return exitstatus; } *************** AddUserToTokenDacl(HANDLE hToken) *** 651,669 **** ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize); if (ptdd == NULL) { ! log_error("could not allocate %lu bytes of memory", dwSize); goto cleanup; } if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize)) { ! log_error("could not get token information: error code %lu", GetLastError()); goto cleanup; } } else { ! log_error("could not get token information buffer size: error code %lu", GetLastError()); goto cleanup; } } --- 697,727 ---- ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize); if (ptdd == NULL) { ! #ifndef FRONTEND ! elog(LOG, "out of memory"); ! #else ! fprintf(stderr, "out of memory\n"); ! #endif goto cleanup; } if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize)) { ! #ifndef FRONTEND ! elog(LOG, "could not get token information: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not get token information: error code %lu\n", GetLastError()); ! #endif goto cleanup; } } else { ! #ifndef FRONTEND ! elog(LOG, "could not get token information buffer size: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not get token information buffer size: error code %lu\n", GetLastError()); ! #endif goto cleanup; } } *************** AddUserToTokenDacl(HANDLE hToken) *** 673,679 **** (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) { ! log_error("could not get ACL information: error code %lu", GetLastError()); goto cleanup; } --- 731,741 ---- (DWORD) sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) { ! #ifndef FRONTEND ! elog(LOG, "could not get ACL information: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not get ACL information: error code %lu\n", GetLastError()); ! #endif goto cleanup; } *************** AddUserToTokenDacl(HANDLE hToken) *** 689,701 **** pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize); if (pacl == NULL) { ! log_error("could not allocate %lu bytes of memory", dwNewAclSize); goto cleanup; } if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION)) { ! log_error("could not initialize ACL: error code %lu", GetLastError()); goto cleanup; } --- 751,771 ---- pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize); if (pacl == NULL) { ! #ifndef FRONTEND ! elog(LOG, "out of memory"); ! #else ! fprintf(stderr, "out of memory\n"); ! #endif goto cleanup; } if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION)) { ! #ifndef FRONTEND ! elog(LOG, "could not initialize ACL: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not initialize ACL: error code %lu\n", GetLastError()); ! #endif goto cleanup; } *************** AddUserToTokenDacl(HANDLE hToken) *** 704,716 **** { if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace)) { ! log_error("could not get ACE: error code %lu", GetLastError()); goto cleanup; } if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize)) { ! log_error("could not add ACE: error code %lu", GetLastError()); goto cleanup; } } --- 774,794 ---- { if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) &pace)) { ! #ifndef FRONTEND ! elog(LOG, "could not get ACE: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not get ACE: error code %lu\n", GetLastError()); ! #endif goto cleanup; } if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize)) { ! #ifndef FRONTEND ! elog(LOG, "could not add ACE: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not add ACE: error code %lu\n", GetLastError()); ! #endif goto cleanup; } } *************** AddUserToTokenDacl(HANDLE hToken) *** 718,724 **** /* Add the new ACE for the current user */ if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid)) { ! log_error("could not add access allowed ACE: error code %lu", GetLastError()); goto cleanup; } --- 796,806 ---- /* Add the new ACE for the current user */ if (!AddAccessAllowedAceEx(pacl, ACL_REVISION, OBJECT_INHERIT_ACE, GENERIC_ALL, pTokenUser->User.Sid)) { ! #ifndef FRONTEND ! elog(LOG, "could not add access allowed ACE: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not add access allowed ACE: error code %lu\n", GetLastError()); ! #endif goto cleanup; } *************** AddUserToTokenDacl(HANDLE hToken) *** 727,733 **** if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize)) { ! log_error("could not set token information: error code %lu", GetLastError()); goto cleanup; } --- 809,819 ---- if (!SetTokenInformation(hToken, tic, (LPVOID) &tddNew, dwNewAclSize)) { ! #ifndef FRONTEND ! elog(LOG, "could not set token information: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not set token information: error code %lu\n", GetLastError()); ! #endif goto cleanup; } *************** GetTokenUser(HANDLE hToken, PTOKEN_USER *** 773,785 **** if (*ppTokenUser == NULL) { ! log_error("could not allocate %lu bytes of memory", dwLength); return FALSE; } } else { ! log_error("could not get token information buffer size: error code %lu", GetLastError()); return FALSE; } } --- 859,879 ---- if (*ppTokenUser == NULL) { ! #ifndef FRONTEND ! elog(LOG, "out of memory"); ! #else ! fprintf(stderr, "out of memory\n"); ! #endif return FALSE; } } else { ! #ifndef FRONTEND ! elog(LOG, "could not get token information buffer size: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not get token information buffer size: error code %lu\n", GetLastError()); ! #endif return FALSE; } } *************** GetTokenUser(HANDLE hToken, PTOKEN_USER *** 793,799 **** LocalFree(*ppTokenUser); *ppTokenUser = NULL; ! log_error("could not get token information: error code %lu", GetLastError()); return FALSE; } --- 887,897 ---- LocalFree(*ppTokenUser); *ppTokenUser = NULL; ! #ifndef FRONTEND ! elog(LOG, "could not get token information: error code %lu", GetLastError()); ! #else ! fprintf(stderr, "could not get token information: error code %lu\n", GetLastError()); ! #endif return FALSE; }
On Sun, May 27, 2018 at 4:21 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > ... So that seems like a rather high price to > pay to deal with what, at present, is a purely hypothetical hazard. > (Basically what this would protect against is elog_start changing errno, > which it doesn't.) Hmm. It looks like errstart() preserves errno to protect %m not from itself, but from the caller's other arguments to the elog facility. That seems reasonable, but do we really need to prohibit direct use of errno in expressions? The only rogue actor likely to trash errno is you, the caller. I mean, if you call elog(LOG, "foo %d %d", errno, fsync(bar)) it's obviously UB and your own fault, but who would do anything like that? Or maybe I misunderstood the motivation. > Another approach we could consider is keeping exec.c's one-off approach > to error handling and letting it redefine pg_prevent_errno_in_scope() as > empty. But that's ugly. > > Or we could make the affected call sites work like this: > > int save_errno = errno; > > log_error(_("could not identify current directory: %s"), > strerror(save_errno)); > > which on the whole might be the most expedient thing. That was what I was going to propose, until I started wondering why we need to do anything here. -- Thomas Munro http://www.enterprisedb.com
Thomas Munro <thomas.munro@enterprisedb.com> writes: > On Sun, May 27, 2018 at 4:21 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote: >> (Basically what this would protect against is elog_start changing errno, >> which it doesn't.) > Hmm. It looks like errstart() preserves errno to protect %m not from > itself, but from the caller's other arguments to the elog facility. > That seems reasonable, but do we really need to prohibit direct use of > errno in expressions? The only rogue actor likely to trash errno is > you, the caller. I mean, if you call elog(LOG, "foo %d %d", errno, > fsync(bar)) it's obviously UB and your own fault, but who would do > anything like that? Or maybe I misunderstood the motivation. Right, the concern is really about things like elog(..., f(x), strerror(errno)); If f() trashes errno --- perhaps only some of the time --- then this is problematic. It's especially problematic because whether f() is evaluated before or after strerror() is platform-dependent. So even if the original author had tested things thoroughly, it might break for somebody else. The cases in exec.c all seem safe enough, but we have lots of examples in the backend where we call nontrivial functions in the arguments of elog/ereport. It doesn't take much to make one nontrivial either. If memory serves, malloc() can trash errno on some platforms such as macOS, so even just a palloc creates a hazard of a hard-to-reproduce problem. At least in the case of ereport, all it takes to create a hazard is more than one sub-function, eg this is risky: ereport(..., errmsg(..., strerror(errno)), errdetail(...)); because errdetail() might run first and malloc some memory for its constructed string. So I think a blanket policy of "don't trust errno within the arguments" is a good idea, even though it might be safe to violate it in the existing cases in exec.c. regards, tom lane
I wrote: > ... It doesn't take much to make one nontrivial either. If > memory serves, malloc() can trash errno on some platforms such as macOS, > so even just a palloc creates a hazard of a hard-to-reproduce problem. After digging around in the archives, the closest thing that we seem to know for certain is that some versions of free() can trash errno, cf https://www.postgresql.org/message-id/flat/E1UcmpR-0004nn-2i%40wrigleys.postgresql.org as a result of possibly calling madvise() which might or might not succeed. But in the light of that knowledge, how much do you want to bet that malloc() can't change errno? And there's the possibility that a called function does both a palloc and a pfree ... regards, tom lane
On Sun, May 27, 2018 at 12:38 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > At least in the case of ereport, all it takes to create a hazard is > more than one sub-function, eg this is risky: > > ereport(..., errmsg(..., strerror(errno)), errdetail(...)); > > because errdetail() might run first and malloc some memory for its > constructed string. > > So I think a blanket policy of "don't trust errno within the arguments" > is a good idea, even though it might be safe to violate it in the > existing cases in exec.c. Right, malloc() is a hazard I didn't think about. I see that my local malloc() makes an effort to save and restore errno around syscalls, but even if all allocators were so thoughtful, which apparently they aren't, there is also the problem that malloc itself can deliberately set errno to ENOMEM per spec. I take your more general point that you can't rely on anything we didn't write not trashing errno, even libc. On Tue, May 22, 2018 at 4:03 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > I noticed another can of worms here, too: on Windows, doesn't use of > GetLastError() in elog/ereport have exactly the same hazard as errno? > Or is there some reason to think it can't change value during errstart()? Yeah, on Windows the same must apply, not in errstart() itself but any time you pass more than one value to elog() using expressions that call functions we can't audit for last-error-stomping. Out of curiosity I tried adding a GetLastError variable for Windows (to hide the function of that name and break callers) to the earlier experimental patch (attached). I had to give it an initial value to get rid of a warning about an unused variable (by my reading of the documentation, __pragma(warning(suppress:4101)) can be used in macros (unlike #pragma) and should shut that warning up, but it doesn't work for me, not sure why). Of course that produces many errors since we do that all over the place: https://ci.appveyor.com/project/macdice/postgres/build/1.0.184 -- Thomas Munro http://www.enterprisedb.com
Attachment
On 2018-May-27, Thomas Munro wrote: > Out of curiosity I tried adding a GetLastError variable for Windows > (to hide the function of that name and break callers) to the earlier > experimental patch (attached). I had to give it an initial value to > get rid of a warning about an unused variable (by my reading of the > documentation, __pragma(warning(suppress:4101)) can be used in macros > (unlike #pragma) and should shut that warning up, but it doesn't work > for me, not sure why). Of course that produces many errors since we > do that all over the place: > > https://ci.appveyor.com/project/macdice/postgres/build/1.0.184 Ouch. This seems to say that we oughta assign GetLastError() to saved_errno during errstart, then use %m in the errmsg() instead. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Alvaro Herrera <alvherre@2ndquadrant.com> writes: > This seems to say that we oughta assign GetLastError() to saved_errno > during errstart, then use %m in the errmsg() instead. No, because in some parts of the code, errno does mean something, even in Windows builds. I think the right fix is to leave %m alone, and instead: (1) save GetLastError's result along with errno during errstart. (2) provide a function, say errlastwinerror(), to be used where elog/ereport calls currently call GetLastError(): elog(ERROR, "something went wrong: error code %lu", errlastwinerror()); What it does, of course, is to reach into the current error stack level and retrieve the saved result. We could use some hack along the line of what Thomas suggested to enforce that this function is used rather than GetLastError(). It's amusing to speculate about whether we could actually cause GetLastError() appearing within elog/ereport to get mapped into this function, thus removing the need to change any code in call sites. But I suspect there's no adequately compiler-independent way to do that. I wonder whether we need to back-patch this. I don't recall seeing any field reports of misleading Windows error codes, but I wonder how many people even look those up ... regards, tom lane
In the hopes of getting the cfbot un-stuck (it's currently trying to test a known-not-to-work patch), here are updated versions of the two live patches we have in this thread. 0001 is the patch I originally proposed to adjust printf archetypes. 0002 is Thomas's patch to blow up on errno references in ereport/elog, plus changes in src/common/exec.c to prevent that from blowing up. (I went with the minimum-footprint way, for now; making exec.c's error handling generally nicer seems like a task for another day.) I think 0002 is probably pushable, really. The unresolved issue about 0001 is whether it will create a spate of warnings on Windows builds, and if so what to do about it. We might learn something from the cfbot about that, but I think the full buildfarm is going to be the only really authoritative answer. There's also the matter of providing similar safety for GetLastError calls, but I think that deserves to be a separate patch ... and I don't really want to take point on it since I lack a Windows machine. regards, tom lane diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index 9731a51..5e56ca5 100644 *** a/config/c-compiler.m4 --- b/config/c-compiler.m4 *************** fi])# PGAC_C_SIGNED *** 19,28 **** # PGAC_C_PRINTF_ARCHETYPE # ----------------------- ! # Set the format archetype used by gcc to check printf type functions. We ! # prefer "gnu_printf", which includes what glibc uses, such as %m for error ! # strings and %lld for 64 bit long longs. GCC 4.4 introduced it. It makes a ! # dramatic difference on Windows. 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 --- 19,28 ---- # PGAC_C_PRINTF_ARCHETYPE # ----------------------- ! # Set the format archetype used by gcc to check elog/ereport functions. ! # This should accept %m, whether or not the platform's printf does. ! # We use "gnu_printf" if possible, which does that, although in some cases ! # it might do more than we could wish. 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 *************** __attribute__((format(gnu_printf, 2, 3)) *** 34,41 **** [pgac_cv_printf_archetype=gnu_printf], [pgac_cv_printf_archetype=printf]) ac_c_werror_flag=$ac_save_c_werror_flag]) ! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype], ! [Define to gnu_printf if compiler supports it, else printf.]) ])# PGAC_PRINTF_ARCHETYPE --- 34,41 ---- [pgac_cv_printf_archetype=gnu_printf], [pgac_cv_printf_archetype=printf]) ac_c_werror_flag=$ac_save_c_werror_flag]) ! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE_M], [$pgac_cv_printf_archetype], ! [Define as a format archetype that accepts %m, if available, else printf.]) ])# PGAC_PRINTF_ARCHETYPE diff --git a/configure b/configure index 2665213..d4b4742 100755 *** a/configure --- b/configure *************** fi *** 13394,13400 **** $as_echo "$pgac_cv_printf_archetype" >&6; } cat >>confdefs.h <<_ACEOF ! #define PG_PRINTF_ATTRIBUTE $pgac_cv_printf_archetype _ACEOF --- 13394,13400 ---- $as_echo "$pgac_cv_printf_archetype" >&6; } cat >>confdefs.h <<_ACEOF ! #define PG_PRINTF_ATTRIBUTE_M $pgac_cv_printf_archetype _ACEOF diff --git a/src/include/c.h b/src/include/c.h index 1e50103..0a4757e 100644 *** a/src/include/c.h --- b/src/include/c.h *************** *** 126,135 **** /* GCC and XLC support format attributes */ #if defined(__GNUC__) || defined(__IBMC__) #define pg_attribute_format_arg(a) __attribute__((format_arg(a))) ! #define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a))) #else #define pg_attribute_format_arg(a) #define pg_attribute_printf(f,a) #endif /* GCC, Sunpro and XLC support aligned, packed and noreturn */ --- 126,139 ---- /* GCC and XLC support format attributes */ #if defined(__GNUC__) || defined(__IBMC__) #define pg_attribute_format_arg(a) __attribute__((format_arg(a))) ! /* Use for functions wrapping stdio's printf, which often doesn't take %m: */ ! #define pg_attribute_printf(f,a) __attribute__((format(printf, f, a))) ! /* Use for elog/ereport, which implement %m for themselves: */ ! #define pg_attribute_printf_m(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE_M, f, a))) #else #define pg_attribute_format_arg(a) #define pg_attribute_printf(f,a) + #define pg_attribute_printf_m(f,a) #endif /* GCC, Sunpro and XLC support aligned, packed and noreturn */ diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index b7e4696..05775a3 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 809,816 **** /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define to gnu_printf if compiler supports it, else printf. */ ! #undef PG_PRINTF_ATTRIBUTE /* PostgreSQL version as a string */ #undef PG_VERSION --- 809,816 ---- /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define as a format archetype that accepts %m, if available, else printf. */ ! #undef PG_PRINTF_ATTRIBUTE_M /* PostgreSQL version as a string */ #undef PG_VERSION diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7a9ba7f..4f4091d 100644 *** a/src/include/utils/elog.h --- b/src/include/utils/elog.h *************** extern int errcode(int sqlerrcode); *** 133,157 **** extern int errcode_for_file_access(void); extern int errcode_for_socket_access(void); ! extern int errmsg(const char *fmt,...) pg_attribute_printf(1, 2); ! extern int errmsg_internal(const char *fmt,...) pg_attribute_printf(1, 2); extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); ! extern int errdetail(const char *fmt,...) pg_attribute_printf(1, 2); ! extern int errdetail_internal(const char *fmt,...) pg_attribute_printf(1, 2); ! extern int errdetail_log(const char *fmt,...) pg_attribute_printf(1, 2); extern int errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf(1, 4) pg_attribute_printf(2, 4); ! extern int errhint(const char *fmt,...) pg_attribute_printf(1, 2); /* * errcontext() is typically called in error context callback functions, not --- 133,157 ---- extern int errcode_for_file_access(void); extern int errcode_for_socket_access(void); ! extern int errmsg(const char *fmt,...) pg_attribute_printf_m(1, 2); ! extern int errmsg_internal(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errmsg_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); ! extern int errdetail(const char *fmt,...) pg_attribute_printf_m(1, 2); ! extern int errdetail_internal(const char *fmt,...) pg_attribute_printf_m(1, 2); ! extern int errdetail_log(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); extern int errdetail_plural(const char *fmt_singular, const char *fmt_plural, ! unsigned long n,...) pg_attribute_printf_m(1, 4) pg_attribute_printf_m(2, 4); ! extern int errhint(const char *fmt,...) pg_attribute_printf_m(1, 2); /* * errcontext() is typically called in error context callback functions, not *************** extern int errhint(const char *fmt,...) *** 165,171 **** extern int set_errcontext_domain(const char *domain); ! extern int errcontext_msg(const char *fmt,...) pg_attribute_printf(1, 2); extern int errhidestmt(bool hide_stmt); extern int errhidecontext(bool hide_ctx); --- 165,171 ---- extern int set_errcontext_domain(const char *domain); ! extern int errcontext_msg(const char *fmt,...) pg_attribute_printf_m(1, 2); extern int errhidestmt(bool hide_stmt); extern int errhidecontext(bool hide_ctx); *************** extern int getinternalerrposition(void); *** 222,234 **** #endif /* HAVE__VA_ARGS */ extern void elog_start(const char *filename, int lineno, const char *funcname); ! extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf(2, 3); /* Support for constructing error strings separately from ereport() calls */ extern void pre_format_elog_string(int errnumber, const char *domain); ! extern char *format_elog_string(const char *fmt,...) pg_attribute_printf(1, 2); /* Support for attaching context information to error reports */ --- 222,234 ---- #endif /* HAVE__VA_ARGS */ extern void elog_start(const char *filename, int lineno, const char *funcname); ! extern void elog_finish(int elevel, const char *fmt,...) pg_attribute_printf_m(2, 3); /* Support for constructing error strings separately from ereport() calls */ extern void pre_format_elog_string(int errnumber, const char *domain); ! extern char *format_elog_string(const char *fmt,...) pg_attribute_printf_m(1, 2); /* Support for attaching context information to error reports */ *************** extern void set_syslog_parameters(const *** 407,415 **** #endif /* ! * Write errors to stderr (or by equal means when stderr is ! * not available). Used before ereport/elog can be used ! * safely (memory context, GUC load etc) */ extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2); --- 407,415 ---- #endif /* ! * Write errors to stderr (or by comparable means when stderr is not ! * available). Used before ereport/elog can be used safely (memory context, ! * GUC load etc). Note that this does *not* accept "%m". */ extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2); diff --git a/src/common/exec.c b/src/common/exec.c index 4df16cd..c207c02 100644 --- a/src/common/exec.c +++ b/src/common/exec.c @@ -124,8 +124,10 @@ find_my_exec(const char *argv0, char *retpath) if (!getcwd(cwd, MAXPGPATH)) { + int save_errno = errno; + log_error(_("could not identify current directory: %s"), - strerror(errno)); + strerror(save_errno)); return -1; } @@ -238,8 +240,10 @@ resolve_symlinks(char *path) */ if (!getcwd(orig_wd, MAXPGPATH)) { + int save_errno = errno; + log_error(_("could not identify current directory: %s"), - strerror(errno)); + strerror(save_errno)); return -1; } @@ -254,7 +258,10 @@ resolve_symlinks(char *path) *lsep = '\0'; if (chdir(path) == -1) { - log_error4(_("could not change directory to \"%s\": %s"), path, strerror(errno)); + int save_errno = errno; + + log_error4(_("could not change directory to \"%s\": %s"), + path, strerror(save_errno)); return -1; } fname = lsep + 1; @@ -281,8 +288,10 @@ resolve_symlinks(char *path) if (!getcwd(path, MAXPGPATH)) { + int save_errno = errno; + log_error(_("could not identify current directory: %s"), - strerror(errno)); + strerror(save_errno)); return -1; } join_path_components(path, path, link_buf); @@ -290,7 +299,10 @@ resolve_symlinks(char *path) if (chdir(orig_wd) == -1) { - log_error4(_("could not change directory to \"%s\": %s"), orig_wd, strerror(errno)); + int save_errno = errno; + + log_error4(_("could not change directory to \"%s\": %s"), + orig_wd, strerror(save_errno)); return -1; } #endif /* HAVE_READLINK */ @@ -520,7 +532,10 @@ pclose_check(FILE *stream) if (exitstatus == -1) { /* pclose() itself failed, and hopefully set errno */ - log_error(_("pclose failed: %s"), strerror(errno)); + int save_errno = errno; + + log_error(_("pclose failed: %s"), + strerror(save_errno)); } else { diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7a9ba7f..4350b12 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -70,6 +70,23 @@ /* SQLSTATE codes for errors are defined in a separate file */ #include "utils/errcodes.h" +/* + * Provide a way to prevent "errno" from being accidentally used inside an + * elog() or ereport() invocation. Since we know that some operating systems + * define errno as something involving a function call, we'll put a local + * variable of the same name as that function in the local scope to force a + * compile error. On platforms that don't define errno in that way, nothing + * happens, so we get no warning ... but we can live with that as long as it + * happens on some popular platforms. + */ +#if defined(errno) && defined(__linux__) +#define pg_prevent_errno_in_scope() int __errno_location pg_attribute_unused() +#elif defined(errno) && (defined(__darwin__) || defined(__freebsd__)) +#define pg_prevent_errno_in_scope() int __error pg_attribute_unused() +#else +#define pg_prevent_errno_in_scope() +#endif + /*---------- * New-style error reporting API: to be used in this way: @@ -103,6 +120,7 @@ #ifdef HAVE__BUILTIN_CONSTANT_P #define ereport_domain(elevel, domain, rest) \ do { \ + pg_prevent_errno_in_scope(); \ if (errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \ errfinish rest; \ if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \ @@ -112,6 +130,7 @@ #define ereport_domain(elevel, domain, rest) \ do { \ const int elevel_ = (elevel); \ + pg_prevent_errno_in_scope(); \ if (errstart(elevel_, __FILE__, __LINE__, PG_FUNCNAME_MACRO, domain)) \ errfinish rest; \ if (elevel_ >= ERROR) \ @@ -198,6 +217,7 @@ extern int getinternalerrposition(void); #ifdef HAVE__BUILTIN_CONSTANT_P #define elog(elevel, ...) \ do { \ + pg_prevent_errno_in_scope(); \ elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ elog_finish(elevel, __VA_ARGS__); \ if (__builtin_constant_p(elevel) && (elevel) >= ERROR) \ @@ -206,6 +226,7 @@ extern int getinternalerrposition(void); #else /* !HAVE__BUILTIN_CONSTANT_P */ #define elog(elevel, ...) \ do { \ + pg_prevent_errno_in_scope(); \ elog_start(__FILE__, __LINE__, PG_FUNCNAME_MACRO); \ { \ const int elevel_ = (elevel); \
I wrote: > I think 0002 is probably pushable, really. The unresolved issue about > 0001 is whether it will create a spate of warnings on Windows builds, > and if so what to do about it. We might learn something from the > cfbot about that, but I think the full buildfarm is going to be the > only really authoritative answer. Ah, cfbot has a run already, and it reports no warnings or errors in its Windows build. At this point I'm inclined to push both of those patches so we can see what the buildfarm makes of them. regards, tom lane
I wrote: > At this point I'm inclined to push both of those patches so we can > see what the buildfarm makes of them. So I did that, and while not all of the buildfarm has reported in, enough of it has that I think we can draw conclusions. The only member that's showing any new warnings, AFAICT, is jacana (gcc 4.9 on Windows). It had no format-related warnings yesterday, but now it has a boatload of 'em, and it appears that every single one traces to not believing that printf and friends understand 'l' and 'z' length modifiers. The reason for this seems to be that we unconditionally replace the printf function family with snprintf.c on Windows, and port.h causes those functions to be marked with pg_attribute_printf, which this patch caused to mean just "printf" not "gnu_printf". So this gcc evidently thinks the platform printf doesn't know 'l' and 'z' (which may or may not be true in reality, but it's irrelevant) and it complains. There are also interesting warnings showing up in elog.c, such as Aug 11 14:26:32 c:/mingw/msys/1.0/home/pgrunner/bf/root/HEAD/pgsql.build/../pgsql/src/backend/utils/error/elog.c:807:2: warning:function might be possible candidate for 'ms_printf' format attribute [-Wsuggest-attribute=format] I think what is happening here is that gcc notices that those functions call appendStringInfoVA, which is now annotated with the printf archetype not gnu_printf, so it decides that maybe we marked the elog.c functions with the wrong archetype. I have no idea why it's suggesting "ms_printf" though --- I can find nothing on the web that even admits that gcc knows such an archetype. So this is all pretty much of a mess. If we annotate the elog functions differently from printf's annotation then we risk getting these complaints in elog.c, but if we don't do that then we can't really describe their semantics correctly. We could possibly mark the replacement snprintf functions with gnu_printf, but that's a lie with respect to the very point at issue about %m. Unless we were to teach snprintf.c about %m ... which probably wouldn't be hard, but I'm not sure I want to go there. That line of thought leads to deciding that we should treat "printf doesn't know %m" as a reason to use snprintf.c over the native printf; and I think we probably do not want to do that, if only because the native printf is probably more efficient than snprintf.c. (There are other reasons to question that too: we probably can't tell without a run-time test in configure, and even if we detect it correctly, gcc might be misconfigured to believe the opposite thing about %m support and hence warn, or fail to warn, anyway. clang at least seems to get this wrong frequently.) But if we do not do such replacement then we still end up wondering how to mark printf wrapper functions such as appendStringInfoVA. At this point I'm inclined to give up and revert 3a60c8ff8. It's not clear that we can really make the warning situation better, as opposed to just moving the warnings from one platform to another. regards, tom lane
> [...] > > At this point I'm inclined to give up and revert 3a60c8ff8. It's not > clear that we can really make the warning situation better, as opposed > to just moving the warnings from one platform to another. Indeed, there are hundreds of warnings around "pg_printf_attribute_m" added with gcc 7.3.0 on by ubuntu 18.04 laptop, thanks to 3a60c8ff. A revert would help. -- Fabien.
Fabien COELHO <coelho@cri.ensmp.fr> writes: > Indeed, there are hundreds of warnings around "pg_printf_attribute_m" > added with gcc 7.3.0 on by ubuntu 18.04 laptop, thanks to 3a60c8ff. Oh? What warnings exactly? I would not expect any new warnings except on platforms where gcc believes the local printf is non POSIX compliant, which certainly ought not happen on any non-obsolete Linux. regards, tom lane
I wrote: > So this is all pretty much of a mess. If we annotate the elog functions > differently from printf's annotation then we risk getting these complaints > in elog.c, but if we don't do that then we can't really describe their > semantics correctly. We could possibly mark the replacement snprintf > functions with gnu_printf, but that's a lie with respect to the very > point at issue about %m. Unless we were to teach snprintf.c about %m > ... which probably wouldn't be hard, but I'm not sure I want to go there. Actually ... the more I think about this, the less insane that idea seems. Consider the following approach: 1. Teach src/port/snprintf.c about %m. While I've not written a patch for this, it looks pretty trivial. 2. Teach configure to test for %m and if it's not there, use the replacement snprintf. (Note: we're already forcing snprintf replacement in cross-compiles, so the added run-time test isn't losing anything.) 3. Get rid of elog.c's hand-made substitution of %m strings, and instead just let it pass the correct errno value down. (We'd likely need to do some fooling in appendStringInfoVA and related functions to preserve call-time errno, but that's not complicated, nor expensive.) 4. (Optional) Get rid of strerror(errno) calls in favor of %m, even in frontend code. Once we've done this, we have uniform printf semantics across all platforms, which is kind of nice from a testing standpoint, as well as being less of a cognitive load for developers. And we can stick with the existing approach of using the gnu_printf archetype across the board; that's no longer a lie for the snprintf.c code. One objection to this is the possible performance advantage of the native printf functions over snprintf.c. I did a bit of investigation of that using the attached testbed, and found that the quality of implementation of the native functions seems pretty variable: RHEL6's glibc on x86_64 (this is just a comparison point, since we'd not be replacing glibc's printf anyway): snprintf time = 756.795 ms total, 0.000756795 ms per iteration pg_snprintf time = 824.643 ms total, 0.000824643 ms per iteration macOS High Sierra on x86_64: snprintf time = 264.071 ms total, 0.000264071 ms per iteration pg_snprintf time = 348.41 ms total, 0.00034841 ms per iteration FreeBSD 11.0 on x86_64: snprintf time = 628.873 ms total, 0.000628873 ms per iteration pg_snprintf time = 606.684 ms total, 0.000606684 ms per iteration OpenBSD 6.0 on x86_64 (same hardware as FreeBSD test): snprintf time = 331.245 ms total, 0.000331245 ms per iteration pg_snprintf time = 539.849 ms total, 0.000539849 ms per iteration NetBSD 8.99 on armv6: snprintf time = 2423.39 ms total, 0.00242339 ms per iteration pg_snprintf time = 3769.16 ms total, 0.00376916 ms per iteration So we would be taking a hit on most platforms, but I've not really seen sprintf as a major component of very many profiles. Moreover, at least for the elog/ereport use-case, we'd be buying back some nontrivial part of that hit by getting rid of expand_fmt_string(). Also worth noting is that we've never made any effort at all to micro-optimize snprintf.c, so maybe there's some gold to be mined there to reduce the penalty. A different objection, possibly more serious than the performance one, is that if we get in the habit of using %m in frontend code then at some point we'd inevitably back-patch such a usage. (Worse, it'd pass testing on glibc platforms, only to fail elsewhere.) I don't see a bulletproof answer to that except to back-patch this set of changes, which might be a bridge too far. Aside from the back-patching angle, though, this seems pretty promising. Thoughts? regards, tom lane PS: here's the testbed I used for the above numbers. Feel free to try other platforms or other test-case formats. Compile this with something like gcc -Wall -O2 -I pgsql/src/include -I pgsql/src/port timeprintf.c (point the -I switches into a configured PG source tree); you might need to add "-lrt" or some such to get clock_gettime(). Then run with "./a.out 1000000" or so. #include "postgres_fe.h" #include "portability/instr_time.h" #include "snprintf.c" int main(int argc, char **argv) { int count = 0; char buffer[1000]; instr_time start; instr_time stop; double elapsed; int i; if (argc > 1) count = atoi(argv[1]); if (count <= 0) count = 1000000; INSTR_TIME_SET_CURRENT(start); for (i = 0; i < count; i++) { snprintf(buffer, sizeof(buffer), "%d %g %s", 42, 42.2, "01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890"); } INSTR_TIME_SET_CURRENT(stop); INSTR_TIME_SUBTRACT(stop, start); elapsed = INSTR_TIME_GET_MILLISEC(stop); printf("snprintf time = %g ms total, %g ms per iteration\n", elapsed, elapsed / count); INSTR_TIME_SET_CURRENT(start); for (i = 0; i < count; i++) { pg_snprintf(buffer, sizeof(buffer), "%d %g %s", 42, 42.2, "01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890"); } INSTR_TIME_SET_CURRENT(stop); INSTR_TIME_SUBTRACT(stop, start); elapsed = INSTR_TIME_GET_MILLISEC(stop); printf("pg_snprintf time = %g ms total, %g ms per iteration\n", elapsed, elapsed / count); return 0; }
I wrote: > ... So we would be taking a hit on most platforms, but I've not really > seen sprintf as a major component of very many profiles. Moreover, > at least for the elog/ereport use-case, we'd be buying back some > nontrivial part of that hit by getting rid of expand_fmt_string(). > Also worth noting is that we've never made any effort at all to > micro-optimize snprintf.c, so maybe there's some gold to be mined > there to reduce the penalty. Oh, the plot thickens: apparently, a very large chunk of the time in that test scenario went into the %g format item, which I think we can all agree isn't performance-critical for Postgres. Changing the test case to test %lld in place of %g, I get (on the same five platforms as before) RHEL6: snprintf time = 357.634 ms total, 0.000357634 ms per iteration pg_snprintf time = 281.708 ms total, 0.000281708 ms per iteration ratio = 0.788 macOS: snprintf time = 155.86 ms total, 0.00015586 ms per iteration pg_snprintf time = 104.074 ms total, 0.000104074 ms per iteration ratio = 0.668 FreeBSD: snprintf time = 268.883 ms total, 0.000268883 ms per iteration pg_snprintf time = 185.294 ms total, 0.000185294 ms per iteration ratio = 0.689 OpenBSD: snprintf time = 276.418 ms total, 0.000276418 ms per iteration pg_snprintf time = 153.334 ms total, 0.000153334 ms per iteration ratio = 0.555 NetBSD: snprintf time = 1174.13 ms total, 0.00117413 ms per iteration pg_snprintf time = 1508.82 ms total, 0.00150882 ms per iteration ratio = 1.285 So there's actually a plausible argument to be made that we'd get a net speed win on most platforms and test cases. regards, tom lane #include "postgres_fe.h" #include "portability/instr_time.h" #include "snprintf.c" int main(int argc, char **argv) { int count = 0; char buffer[1000]; instr_time start; instr_time stop; double elapsed; double elapsed2; int i; if (argc > 1) count = atoi(argv[1]); if (count <= 0) count = 1000000; INSTR_TIME_SET_CURRENT(start); for (i = 0; i < count; i++) { snprintf(buffer, sizeof(buffer), "%d %lld %s", 42, (long long) 42, "01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890"); } INSTR_TIME_SET_CURRENT(stop); INSTR_TIME_SUBTRACT(stop, start); elapsed = INSTR_TIME_GET_MILLISEC(stop); printf("snprintf time = %g ms total, %g ms per iteration\n", elapsed, elapsed / count); INSTR_TIME_SET_CURRENT(start); for (i = 0; i < count; i++) { pg_snprintf(buffer, sizeof(buffer), "%d %lld %s", 42, (long long) 42, "01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890"); } INSTR_TIME_SET_CURRENT(stop); INSTR_TIME_SUBTRACT(stop, start); elapsed2 = INSTR_TIME_GET_MILLISEC(stop); printf("pg_snprintf time = %g ms total, %g ms per iteration\n", elapsed2, elapsed2 / count); printf("ratio = %.3f\n", elapsed2 / elapsed); return 0; }
On Sun, Aug 12, 2018 at 3:08 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote: > Moreover, > at least for the elog/ereport use-case, we'd be buying back some > nontrivial part of that hit by getting rid of expand_fmt_string(). Yeah. I think expand_fmt_string() is pretty costly if you are doing a lot of errors (e.g. write a function that uses an EXCEPTION block to map ERROR -> NULL return and then do SELECT catch_errors(blah) FROM generate_series(1,10000000) g or so. It seems altogether likely to me that getting rid of the need for expand_fmt_string() will make for a net win. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
>> Indeed, there are hundreds of warnings around "pg_printf_attribute_m" >> added with gcc 7.3.0 on by ubuntu 18.04 laptop, thanks to 3a60c8ff. > > Oh? What warnings exactly? I would not expect any new warnings except > on platforms where gcc believes the local printf is non POSIX compliant, > which certainly ought not happen on any non-obsolete Linux. Hmmm. Strange. The good news, is sthat it does not show anymore. Maybe this was because I had not done a "configure" before recompiling. Sorry for the noise. -- Fabien.
I wrote: > Consider the following approach: > 1. Teach src/port/snprintf.c about %m. While I've not written a patch > for this, it looks pretty trivial. > 2. Teach configure to test for %m and if it's not there, use the > replacement snprintf. (Note: we're already forcing snprintf replacement > in cross-compiles, so the added run-time test isn't losing anything.) > 3. Get rid of elog.c's hand-made substitution of %m strings, and instead > just let it pass the correct errno value down. (We'd likely need to do > some fooling in appendStringInfoVA and related functions to preserve > call-time errno, but that's not complicated, nor expensive.) > 4. (Optional) Get rid of strerror(errno) calls in favor of %m, even in > frontend code. So I started to hack on this, and soon noticed that actually, what elog.c replaces %m with is *not* the result of strerror(), it's the result of useful_strerror(). Which, primarily, does this: /* * 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. */ I don't know offhand whether glibc's implementation delivers anything useful for out-of-range errno, but I do know that we've seen the transcoding problem with it, cf commit 8e68816cc which arose from this discussion: https://www.postgresql.org/message-id/flat/2782A2665E8342DF8695F396DBA80C88%40maumau We could easily move useful_strerror() into snprintf.c, I think (might need to move pgwin32_socket_strerror there too). But then we'd lose its functionality when using glibc. So now I'm about ready to propose that we just *always* use snprintf.c, and forget all of the related configure probing. This'd have some advantages, notably that we'd get the useful_strerror() behavior in frontend as well as backend, assuming we converted all our frontend code to use %m. And we'd not exactly be the first project to decide that. But it's kind of a big move from where we are today. Thoughts? regards, tom lane
On Sat, Aug 18, 2018 at 04:34:50PM -0400, Tom Lane wrote: > So now I'm about ready to propose that we just *always* use > snprintf.c, and forget all of the related configure probing. Yes. > This'd have some advantages, notably that we'd get the > useful_strerror() behavior in frontend as well as backend, > assuming we converted all our frontend code to use %m. You'd also get to ensure that all uses from *die() are async-signal-safe. You'd also ensure that snprintf.c gets maximal testing. > And we'd not exactly be the first project to decide that. > But it's kind of a big move from where we are today. > > Thoughts? I think that is the best approach.
Nico Williams <nico@cryptonector.com> writes: > On Sat, Aug 18, 2018 at 04:34:50PM -0400, Tom Lane wrote: >> So now I'm about ready to propose that we just *always* use >> snprintf.c, and forget all of the related configure probing. > You'd also get to ensure that all uses from *die() are > async-signal-safe. [ raised eyebrow... ] That seems like more than I care to promise here. But even if snprintf itself were unconditionally safe, there's plenty of other stuff in that code path that isn't. regards, tom lane
On Sun, Aug 19, 2018 at 01:15:58AM -0400, Tom Lane wrote: > Nico Williams <nico@cryptonector.com> writes: > > On Sat, Aug 18, 2018 at 04:34:50PM -0400, Tom Lane wrote: > >> So now I'm about ready to propose that we just *always* use > >> snprintf.c, and forget all of the related configure probing. > > > You'd also get to ensure that all uses from *die() are > > async-signal-safe. > > [ raised eyebrow... ] That seems like more than I care to promise > here. But even if snprintf itself were unconditionally safe, > there's plenty of other stuff in that code path that isn't. One step at a time, no? And there's the other benefits.
I wrote: >> Consider the following approach: >> 1. Teach src/port/snprintf.c about %m. While I've not written a patch >> for this, it looks pretty trivial. >> 2. Teach configure to test for %m and if it's not there, use the >> replacement snprintf. (Note: we're already forcing snprintf replacement >> in cross-compiles, so the added run-time test isn't losing anything.) >> 3. Get rid of elog.c's hand-made substitution of %m strings, and instead >> just let it pass the correct errno value down. (We'd likely need to do >> some fooling in appendStringInfoVA and related functions to preserve >> call-time errno, but that's not complicated, nor expensive.) >> 4. (Optional) Get rid of strerror(errno) calls in favor of %m, even in >> frontend code. > So I started to hack on this, and soon noticed that actually, what elog.c > replaces %m with is *not* the result of strerror(), it's the result of > useful_strerror(). After further thought, I realized that the best way to handle that is to turn useful_strerror() into a globally available wrapper pg_strerror() that replaces strerror() everyplace. That way we get its protections in frontend code as well as backend, and we ensure that the results of printing strerror(errno) match what %m would do (so that step 4 above is just cosmetic code simplification and doesn't change behavior). We'd speculated about doing that back when 8e68816cc went in, but not actually pulled the trigger. So the first attached patch does that, and then the second one implements %m in snprintf.c and causes it to be used all the time. I've not touched step 4 yet, that could be done later/piecemeal. Although the attached causes strerror.c to be included in libpq, I think it's actually dead code at the moment, because on any reasonably modern platform (including *all* of the buildfarm) libpq does not depend on strerror but strerror_r, cf pqStrerror(). It's tempting to expand strerror.c to include a similar wrapper for strerror_r, so that the extra functionality exists for libpq's usage too. (Also, it'd likely be better for snprintf.c to depend on strerror_r not strerror, to avoid unnecessary thread-unsafety.) But I've left that for later. A couple of additional notes for review: * The 0002 patch will conflict with my snprintf-speedup patch, but resolving that is simple (just need to move one of the %m hunks around). * src/port/strerror.c already exists, but as far as I can see it's been dead code for decades; no ANSI-C-compliant platform lacks strerror() altogether. Moreover, ecpg never got taught to include it, so obviously we've not built on a platform with that problem anytime recently. So I just removed the former contents of that file. * The most nervous-making aspect of this patch, IMO, is that there's an addition to the API spec for appendStringInfoVA and pvsnprintf: callers have to preserve errno when looping. Fortunately there are very few direct callers of those, but I'm slightly worried that extensions might do so. I don't see any way to avoid that change though. * I dropped configure's checks for existence/declaration of snprintf and vsnprintf, since (a) we no longer care, and (b) those are pretty much useless anyway; no active buildfarm member fails those checks. * The Windows aspects of this are untested. It seems like importing pgwin32_socket_strerror's behavior into the frontend ought to be a bug fix, though: win32_port.h redefines socket error symbols whether FRONTEND is set or not, so aren't we printing bogus info for socket errors in frontend right now? regards, tom lane diff --git a/configure b/configure index 836d68d..fadd06e 100755 *** a/configure --- b/configure *************** esac *** 15537,15555 **** 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 --- 15537,15542 ---- diff --git a/configure.in b/configure.in index 6e14106..3adec10 100644 *** a/configure.in --- b/configure.in *************** else *** 1649,1655 **** AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break]) fi ! AC_REPLACE_FUNCS([crypt fls getopt getrusage inet_aton mkdtemp random rint srandom strerror strlcat strlcpy strnlen]) case $host_os in --- 1649,1655 ---- AC_CHECK_FUNCS([fpclass fp_class fp_class_d class], [break]) fi ! AC_REPLACE_FUNCS([crypt 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 827574e..2861b1f 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 519,527 **** /* 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 --- 519,524 ---- diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 46ce49d..4c68fa8 100644 *** a/src/include/pg_config.h.win32 --- b/src/include/pg_config.h.win32 *************** *** 390,400 **** /* 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 */ --- 390,395 ---- diff --git a/src/include/port.h b/src/include/port.h index 74a9dc4..dae80cb 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 abe0a50..9af7cc0 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 4543d87..24e9ddf 100644 *** a/src/tools/msvc/Mkvcbuild.pm --- b/src/tools/msvc/Mkvcbuild.pm *************** sub mkvcbuild *** 98,104 **** 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 win32env.c win32error.c win32security.c win32setlocale.c); push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00'); --- 98,104 ---- 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 strerror.c tar.c thread.c getopt.c getopt_long.c dirent.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 67675a3..80e4f8c 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 fadd06e..58f223f 100755 *** a/configure --- b/configure *************** $as_echo "#define HAVE_PS_STRINGS 1" >>c *** 15273,15369 **** 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 : --- 15273,15278 ---- *************** fi *** 16068,16120 **** # 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; } --- 15977,15982 ---- *************** _ACEOF *** 16294,16301 **** # 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 --- 16156,16161 ---- *************** cat >>confdefs.h <<_ACEOF *** 16308,16427 **** _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 --- 16168,16173 ---- diff --git a/configure.in b/configure.in index 3adec10..aca5503 100644 *** a/configure.in --- b/configure.in *************** if test "$pgac_cv_var_PS_STRINGS" = yes *** 1584,1636 **** 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([ --- 1584,1589 ---- *************** for the exact reason.]])], *** 1800,1815 **** # 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 --- 1753,1758 ---- *************** AC_DEFINE_UNQUOTED(PG_INT64_TYPE, $pg_in *** 1832,1839 **** [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 --- 1775,1780 ---- *************** fi *** 1843,1872 **** 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 --- 1784,1789 ---- 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 45a391b..7f3b4ba 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 1e50103..6a8a01c 100644 *** a/src/include/c.h --- b/src/include/c.h *************** extern void ExceptionalCondition(const c *** 1088,1101 **** * 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 --- 1088,1093 ---- diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 2861b1f..16c148b 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 154,163 **** don't. */ #undef HAVE_DECL_POSIX_FADVISE - /* 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 --- 154,159 ---- *************** *** 182,191 **** 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 --- 178,183 ---- *************** *** 495,503 **** /* 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 --- 487,492 ---- *************** *** 700,708 **** /* 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 --- 689,694 ---- *************** *** 914,922 **** /* 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 --- 900,905 ---- diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 4c68fa8..e080270 100644 *** a/src/include/pg_config.h.win32 --- b/src/include/pg_config.h.win32 *************** *** 123,132 **** to 0 if you don't. */ #define HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN 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 --- 123,128 ---- *************** *** 139,148 **** 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 */ --- 135,140 ---- *************** *** 361,369 **** /* 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 --- 353,358 ---- *************** *** 541,549 **** /* 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 --- 530,535 ---- *************** *** 703,711 **** /* 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 --- 689,694 ---- diff --git a/src/include/port.h b/src/include/port.h index dae80cb..3a186b4 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 9af7cc0..a694560 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/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;
On Sun, Aug 19, 2018 at 03:12:00PM -0400, Tom Lane wrote: > * The Windows aspects of this are untested. It seems like importing > pgwin32_socket_strerror's behavior into the frontend ought to be a > bug fix, though: win32_port.h redefines socket error symbols whether > FRONTEND is set or not, so aren't we printing bogus info for socket > errors in frontend right now? I had a look at that this morning for some other Windows patch, and I think that HEAD is flat wrong to not expose pgwin32_socket_strerror to the frontend. I would have liked to look at this patch in details, but it failed to apply. Could you rebase? -- Michael
Attachment
Michael Paquier <michael@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.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 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.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/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;
On Wed, Sep 12, 2018 at 01:40:01PM -0400, Tom Lane wrote: > Michael Paquier <michael@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. - 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... And about 0002. I am surprised by the amount of cleanup that the removal of all the special wrappers for %m gives, especially expand_fmt_string. So +1 for the concept. I have been testing both patches individually on Windows and compilation, as well as tests, do not show any issues. The tests have been done only with MSVC. 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. -- Michael
Attachment
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 ----
On Mon, Sep 24, 2018 at 01:18:47PM -0400, Tom Lane wrote: > 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. Ok. I won't fight hard on that. Why changing the error message from "could not load netmsg.dll" to "unrecognized winsock error" then? The original error string is much more verbose to grab the context. > 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. Thanks for the new versions. The only thing I could find to complain about is the error message above, the rest looks in nice shape. -- Michael
Attachment
Michael Paquier <michael@paquier.xyz> writes: > On Mon, Sep 24, 2018 at 01:18:47PM -0400, Tom Lane wrote: >> 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. > Ok. I won't fight hard on that. Why changing the error message from > "could not load netmsg.dll" to "unrecognized winsock error" then? The > original error string is much more verbose to grab the context. As the code stands, what you'll get told about is the error code returned by the failed LoadLibrary call; the original winsock error code is reported nowhere. I think that's backwards. We could possibly write something like sprintf(wserrbuf, "winsock error %d (could not load netmsg.dll to translate: error code %lu)", err, GetLastError()))); but I'm unconvinced that that's useful. regards, tom lane
On 2018-Sep-25, Tom Lane wrote: > Michael Paquier <michael@paquier.xyz> writes: > > Ok. I won't fight hard on that. Why changing the error message from > > "could not load netmsg.dll" to "unrecognized winsock error" then? The > > original error string is much more verbose to grab the context. > > As the code stands, what you'll get told about is the error code > returned by the failed LoadLibrary call; the original winsock error > code is reported nowhere. I think that's backwards. I agree that the winsock problem is the main one we should be reporting, including its numeric error code. Even if we can't translate it, the numeric value can be translated by web-searching, if it comes to that. > We could possibly write something like > > sprintf(wserrbuf, "winsock error %d (could not load netmsg.dll to translate: error code %lu)", err, GetLastError()))); > > but I'm unconvinced that that's useful. Actually I think it *is* useful to do it like this, because then the user knows to fix the netmsg.dll problem so that they can continue to investigate the winsock problem. If we don't report the secondary error message, how are users going to figure out how to fix the problem? -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Alvaro Herrera <alvherre@2ndquadrant.com> writes: > On 2018-Sep-25, Tom Lane wrote: >> We could possibly write something like >> >> sprintf(wserrbuf, "winsock error %d (could not load netmsg.dll to translate: error code %lu)", err, GetLastError()))); >> >> but I'm unconvinced that that's useful. > Actually I think it *is* useful to do it like this, because then the > user knows to fix the netmsg.dll problem so that they can continue to > investigate the winsock problem. If we don't report the secondary error > message, how are users going to figure out how to fix the problem? OK, I'm fine with doing it like that if people want it. regards, tom lane
On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote: > Alvaro Herrera <alvherre@2ndquadrant.com> writes: >> Actually I think it *is* useful to do it like this, because then the >> user knows to fix the netmsg.dll problem so that they can continue to >> investigate the winsock problem. If we don't report the secondary error >> message, how are users going to figure out how to fix the problem? > > OK, I'm fine with doing it like that if people want it. +1. -- Michael
Attachment
Michael Paquier <michael@paquier.xyz> writes: > On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote: >> Alvaro Herrera <alvherre@2ndquadrant.com> writes: >>> Actually I think it *is* useful to do it like this, because then the >>> user knows to fix the netmsg.dll problem so that they can continue to >>> investigate the winsock problem. If we don't report the secondary error >>> message, how are users going to figure out how to fix the problem? >> OK, I'm fine with doing it like that if people want it. > +1. OK, pushed 0001 with that adjustment. While looking over the thread, I remembered I wanted to convert strerror_r into a wrapper as well. Think I'll go do that next, because really it'd be better for snprintf.c to be calling strerror_r not strerror. regards, tom lane
I wrote: > While looking over the thread, I remembered I wanted to convert > strerror_r into a wrapper as well. Think I'll go do that next, > because really it'd be better for snprintf.c to be calling strerror_r > not strerror. So while chasing that, I realized that libpq contains its own version of the backend's win32_socket_strerror code, in libpq/win32.c. This probably explains why we've not heard complaints about bogus socket error reports from libpq; it's that code that's handling it. What I think ought to happen is to merge win32.c's version of that code into strerror.c, which'd allow removing win32.c and win32.h altogether. However, not having a Windows environment, I can't test such changes and probably shouldn't be the one to take point on making the change. Anybody? regards, tom lane
Hi, On 2018-09-24 13:18:47 -0400, Tom Lane wrote: > 0002 changes things so that we always use our snprintf, removing all the > configure logic associated with that. In the commit message you wrote: > Preliminary performance testing suggests that as it stands, snprintf.c is > faster than the native printf functions for some tasks on some platforms, > and slower for other cases. A pending patch will improve that, though > cases with floating-point conversions will doubtless remain slower unless > we want to put a *lot* of effort into that. Still, we've not observed > that *printf is really a performance bottleneck for most workloads, so > I doubt this matters much. I severely doubt the last sentence. I've *many* times seen *printf be a significant bottleneck. In particular just about any pg_dump of a database that has large tables with even just a single float column is commonly bottlenecked on float -> string conversion. A trivial bad benchmark: CREATE TABLE somefloats(id serial, data1 float8, data2 float8, data3 float8); INSERT INTO somefloats(data1, data2, data3) SELECT random(), random(), random() FROM generate_series(1, 10000000); VACUUM FREEZE somefloats; postgres[12850][1]=# \dt+ somefloats List of relations ┌────────┬────────────┬───────┬────────┬────────┬─────────────┐ │ Schema │ Name │ Type │ Owner │ Size │ Description │ ├────────┼────────────┼───────┼────────┼────────┼─────────────┤ │ public │ somefloats │ table │ andres │ 575 MB │ │ └────────┴────────────┴───────┴────────┴────────┴─────────────┘ 96bf88d52711ad3a0a4cc2d1d9cb0e2acab85e63: COPY somefloats TO '/dev/null'; COPY 10000000 Time: 24575.770 ms (00:24.576) 96bf88d52711ad3a0a4cc2d1d9cb0e2acab85e63^: COPY somefloats TO '/dev/null'; COPY 10000000 Time: 12877.037 ms (00:12.877) IOW, we regress copy performance by about 2x. And one int and three floats isn't a particularly insane table layout. I'm not saying we shouldn't default to our printf - in fact I think we probably past due to use a faster float->string conversion than we portably get from the OS - but I don't think we can default to our sprintf without doing something about the float conversion performance. Greetings, Andres Freund
On 2018-09-26 11:09:59 -0400, Tom Lane wrote: > Michael Paquier <michael@paquier.xyz> writes: > > On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote: > >> Alvaro Herrera <alvherre@2ndquadrant.com> writes: > >>> Actually I think it *is* useful to do it like this, because then the > >>> user knows to fix the netmsg.dll problem so that they can continue to > >>> investigate the winsock problem. If we don't report the secondary error > >>> message, how are users going to figure out how to fix the problem? > > >> OK, I'm fine with doing it like that if people want it. > > > +1. > > OK, pushed 0001 with that adjustment. > > While looking over the thread, I remembered I wanted to convert > strerror_r into a wrapper as well. Think I'll go do that next, > because really it'd be better for snprintf.c to be calling strerror_r > not strerror. The strerror push, I assume it's that at least, broke something on icc: https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=fulmar&dt=2018-09-26%2018%3A00%3A16 ================== pgsql.build/src/test/regress/regression.diffs =================== *** /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/expected/create_function_1.out Wed Sep 26 20:10:35 2018 --- /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/results/create_function_1.out Wed Sep 26 20:10:43 2018 *************** *** 86,92 **** ERROR: only one AS item needed for language "sql" CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C AS 'nosuchfile'; ! ERROR: could not access file "nosuchfile": No such file or directory CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol'; ERROR: could not find function "nosuchsymbol" in file "/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so" --- 86,92 ---- ERROR: only one AS item needed for language "sql" CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C AS 'nosuchfile'; ! ERROR: could not access file "nosuchfile": ENOENT CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol'; ERROR: could not find function "nosuchsymbol" in file "/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so" ====================================================================== Greetings, Andres Freund
On 2018-09-26 11:57:34 -0700, Andres Freund wrote: > On 2018-09-26 11:09:59 -0400, Tom Lane wrote: > > Michael Paquier <michael@paquier.xyz> writes: > > > On Tue, Sep 25, 2018 at 12:05:42PM -0400, Tom Lane wrote: > > >> Alvaro Herrera <alvherre@2ndquadrant.com> writes: > > >>> Actually I think it *is* useful to do it like this, because then the > > >>> user knows to fix the netmsg.dll problem so that they can continue to > > >>> investigate the winsock problem. If we don't report the secondary error > > >>> message, how are users going to figure out how to fix the problem? > > > > >> OK, I'm fine with doing it like that if people want it. > > > > > +1. > > > > OK, pushed 0001 with that adjustment. > > > > While looking over the thread, I remembered I wanted to convert > > strerror_r into a wrapper as well. Think I'll go do that next, > > because really it'd be better for snprintf.c to be calling strerror_r > > not strerror. > > The strerror push, I assume it's that at least, broke something on icc: > https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=fulmar&dt=2018-09-26%2018%3A00%3A16 > > ================== pgsql.build/src/test/regress/regression.diffs =================== > *** /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/expected/create_function_1.out Wed Sep 26 20:10:352018 > --- /var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/results/create_function_1.out Wed Sep 26 20:10:432018 > *************** > *** 86,92 **** > ERROR: only one AS item needed for language "sql" > CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C > AS 'nosuchfile'; > ! ERROR: could not access file "nosuchfile": No such file or directory > CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C > AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol'; > ERROR: could not find function "nosuchsymbol" in file "/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so" > --- 86,92 ---- > ERROR: only one AS item needed for language "sql" > CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C > AS 'nosuchfile'; > ! ERROR: could not access file "nosuchfile": ENOENT > CREATE FUNCTION test1 (int) RETURNS int LANGUAGE C > AS '/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so', 'nosuchsymbol'; > ERROR: could not find function "nosuchsymbol" in file "/var/buildfarm/fulmar/build/HEAD/pgsql.build/src/test/regress/regress.so" > > ====================================================================== Mandrill as well: https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=mandrill&dt=2018-09-26%2018%3A30%3A26 *Lots* of new warnings like: xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY -qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg -DFRONTEND-I../../src/include -I/home/nm/sw/nopath/icu58.2-32/include -I/home/nm/sw/nopath/libxml2-32/include/libxml2 -I/home/nm/sw/nopath/uuid-32/include-I/home/nm/sw/nopath/openldap-32/include -I/home/nm/sw/nopath/icu58.2-32/include -I/home/nm/sw/nopath/libxml2-32/include-DVAL_CONFIGURE="\"'--enable-cassert' '--enable-debug' '--enable-nls' '--enable-tap-tests''--with-icu' '--with-ldap' '--with-libxml' '--with-libxslt' '--with-openssl' '--with-ossp-uuid' '--with-perl''--with-python' '--with-includes=/home/nm/sw/nopath/uuid-32/include /home/nm/sw/nopath/openldap-32/include /home/nm/sw/nopath/icu58.2-32/include/home/nm/sw/nopath/libxml2-32/include' '--with-libraries=/home/nm/sw/nopath/uuid-32/lib/home/nm/sw/nopath/openldap-32/lib /home/nm/sw/nopath/icu58.2-32/lib /home/nm/sw/nopath/libxml2-32/lib''--with-tcl' '--prefix=/home/nm/farm/xlc32/HEAD/inst' '--with-pgport=7678' 'CC=xlc_r -qmaxmem=33554432-D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY' 'PKG_CONFIG_PATH=/home/nm/sw/nopath/icu58.2-32/lib/pkgconfig'\""-DVAL_CC="\"xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY\""-DVAL_CPPFLAGS="\"-I/home/nm/sw/nopath/icu58.2-32/include -I/home/nm/sw/nopath/libxml2-32/include/libxml2-I/home/nm/sw/nopath/uuid-32/include -I/home/nm/sw/nopath/openldap-32/include-I/home/nm/sw/nopath/icu58.2-32/include -I/home/nm/sw/nopath/libxml2-32/include\""-DVAL_CFLAGS="\"-qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg\"" -DVAL_CFLAGS_SL="\"\""-DVAL_LDFLAGS="\"-L/home/nm/sw/nopath/libxml2-32/lib -L/home/nm/sw/nopath/uuid-32/lib -L/home/nm/sw/nopath/openldap-32/lib-L/home/nm/sw/nopath/icu58.2-32/lib -L/home/nm/sw/nopath/libxml2-32/lib -Wl,-blibpath:'/home/nm/farm/xlc32/HEAD/inst/lib:/home/nm/sw/nopath/libxml2-32/lib:/home/nm/sw/nopath/uuid-32/lib:/home/nm/sw/nopath/openldap-32/lib:/home/nm/sw/nopath/icu58.2-32/lib:/home/nm/sw/nopath/libxml2-32/lib:/usr/lib:/lib'\"" -DVAL_LDFLAGS_EX="\"\""-DVAL_LDFLAGS_SL="\" -Wl,-bnoentry -Wl,-H512 -Wl,-bM:SRE\"" -DVAL_LIBS="\"-lpgcommon -lpgport -lintl-lxslt -lxml2 -lssl -lcrypto -lz -lreadline -lm \"" -c -o base64.o base64.c "../../src/include/common/fe_memutils.h", line 41.44: 1506-946 (W) Incorrect argument type specified for attribute "format";this attribute is ignored. "../../src/include/common/fe_memutils.h", line 42.80: 1506-946 (W) Incorrect argument type specified for attribute "format"; this attribute is ignored. and then fails with: xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY -qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg -D_REENTRANT-D_THREAD_SAFE -D_POSIX_PTHREAD_SEMANTICS -o libpq.so.5 libpq.a -Wl,-bE:libpq.exp -L../../../src/port -L../../../src/common -L/home/nm/sw/nopath/libxml2-32/lib -L/home/nm/sw/nopath/uuid-32/lib -L/home/nm/sw/nopath/openldap-32/lib-L/home/nm/sw/nopath/icu58.2-32/lib -L/home/nm/sw/nopath/libxml2-32/lib -Wl,-blibpath:'/home/nm/farm/xlc32/HEAD/inst/lib:/home/nm/sw/nopath/libxml2-32/lib:/home/nm/sw/nopath/uuid-32/lib:/home/nm/sw/nopath/openldap-32/lib:/home/nm/sw/nopath/icu58.2-32/lib:/home/nm/sw/nopath/libxml2-32/lib:/usr/lib:/lib' -Wl,-bnoentry -Wl,-H512 -Wl,-bM:SRE -lintl -lssl -lcrypto -lldap_r -llber -lpthreads ld: 0711-317 ERROR: Undefined symbol: ._isnan ld: 0711-345 Use the -bloadmap or -bnoquiet option to obtain more information. ../../../src/Makefile.shlib:339: recipe for target 'libpq.so.5' failed but I think the latter is more likely to be caused by 2e2a392de3 Wed Sep 26 08:25:24 2018 UTC Fix problems in handling the line data type rather than this thread. Greetings, Andres Freund
On 2018-09-26 12:09:34 -0700, Andres Freund wrote: > and then fails with: > xlc_r -qmaxmem=33554432 -D_LARGE_FILES=1 -DRANDOMIZE_ALLOCATED_MEMORY -qnoansialias -g -O2 -qmaxmem=16384 -qsrcmsg -D_REENTRANT-D_THREAD_SAFE -D_POSIX_PTHREAD_SEMANTICS -o libpq.so.5 libpq.a -Wl,-bE:libpq.exp -L../../../src/port -L../../../src/common -L/home/nm/sw/nopath/libxml2-32/lib -L/home/nm/sw/nopath/uuid-32/lib -L/home/nm/sw/nopath/openldap-32/lib-L/home/nm/sw/nopath/icu58.2-32/lib -L/home/nm/sw/nopath/libxml2-32/lib -Wl,-blibpath:'/home/nm/farm/xlc32/HEAD/inst/lib:/home/nm/sw/nopath/libxml2-32/lib:/home/nm/sw/nopath/uuid-32/lib:/home/nm/sw/nopath/openldap-32/lib:/home/nm/sw/nopath/icu58.2-32/lib:/home/nm/sw/nopath/libxml2-32/lib:/usr/lib:/lib' -Wl,-bnoentry -Wl,-H512 -Wl,-bM:SRE -lintl -lssl -lcrypto -lldap_r -llber -lpthreads > ld: 0711-317 ERROR: Undefined symbol: ._isnan > ld: 0711-345 Use the -bloadmap or -bnoquiet option to obtain more information. > ../../../src/Makefile.shlib:339: recipe for target 'libpq.so.5' failed > > but I think the latter is more likely to be caused by > 2e2a392de3 Wed Sep 26 08:25:24 2018 UTC Fix problems in handling the line data type > rather than this thread. Err, no, that commit was backend code, this fails in frontend code... Greetings, Andres Freund
Andres Freund <andres@anarazel.de> writes: > The strerror push, I assume it's that at least, broke something on icc: > https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=fulmar&dt=2018-09-26%2018%3A00%3A16 Yeah. It looks like the problem is that configure's test for strerror_r's return type does not work on icc: onfigure:10784: checking whether strerror_r returns int configure:10805: icc -std=gnu99 -c -mp1 -fno-strict-aliasing -g -O2 -pthread -D_REENTRANT -D_THREAD_SAFE -D_POSIX_PTHREAD_SEMANTICS -D_GNU_SOURCE -I/usr/include/libxml2 conftest.c >&5 conftest.c(45): warning #159: declaration is incompatible with previous "strerror_r" (declared at line 438 of "/usr/include/string.h") int strerror_r(int, char *, size_t); ^ configure:10805: $? = 0 configure:10812: result: yes Configure is expecting this to throw a hard error (if the platform declares strerror_r to return char*) but icc only makes it a warning. What I am not quite figuring out here is how the code seemed to work before. Before this patch, it only mattered in libpq because that was the only place using pqStrerror. Maybe the regression tests don't ever produce a strerror output from libpq? regards, tom lane
Andres Freund <andres@anarazel.de> writes: > I'm not saying we shouldn't default to our printf - in fact I think we > probably past due to use a faster float->string conversion than we > portably get from the OS - but I don't think we can default to our > sprintf without doing something about the float conversion performance. Well, if you're unhappy about snprintf.c's performance, you could review https://commitfest.postgresql.org/19/1763/ so I can push that. In my tests, that got us down to circa 10% penalty for float conversions. More generally, I'm not averse to having our own float conversion code if someone wants to put in the effort. Performance aside, it'd be nice to eliminate cross-platform differences in float output so we could get rid of some of the Windows-specific regression result files. regards, tom lane
Hi, On 2018-09-26 17:41:36 -0400, Tom Lane wrote: > Andres Freund <andres@anarazel.de> writes: > > I'm not saying we shouldn't default to our printf - in fact I think we > > probably past due to use a faster float->string conversion than we > > portably get from the OS - but I don't think we can default to our > > sprintf without doing something about the float conversion performance. > > Well, if you're unhappy about snprintf.c's performance, you could review > https://commitfest.postgresql.org/19/1763/ > so I can push that. In my tests, that got us down to circa 10% penalty > for float conversions. Uh, I can do that, but the fact remains that your commit slowed down COPY and other conversion intensive workloads by a *significant* amount. I'm ok helping with improving/winning-back performance, but I do think the obligation to do so remains with the committer/authors that caused a performance regression. Greetings, Andres Freund
Andres Freund <andres@anarazel.de> writes: > On 2018-09-26 17:41:36 -0400, Tom Lane wrote: >> Well, if you're unhappy about snprintf.c's performance, you could review >> https://commitfest.postgresql.org/19/1763/ >> so I can push that. In my tests, that got us down to circa 10% penalty >> for float conversions. > Uh, I can do that, but the fact remains that your commit slowed down > COPY and other conversion intensive workloads by a *significant* amount. [ shrug... ] There are other cases that got faster (particularly after the above-mentioned patch). I do not wish to consider floating-point conversion speed as the sole figure of merit for this change. If we are to consider only the worst-case, we should be reverting JIT. regards, tom lane
On 2018-09-26 18:31:07 -0400, Tom Lane wrote: > Andres Freund <andres@anarazel.de> writes: > > On 2018-09-26 17:41:36 -0400, Tom Lane wrote: > >> Well, if you're unhappy about snprintf.c's performance, you could review > >> https://commitfest.postgresql.org/19/1763/ > >> so I can push that. In my tests, that got us down to circa 10% penalty > >> for float conversions. > > > Uh, I can do that, but the fact remains that your commit slowed down > > COPY and other conversion intensive workloads by a *significant* amount. > > [ shrug... ] There are other cases that got faster (particularly after > the above-mentioned patch). I do not wish to consider floating-point > conversion speed as the sole figure of merit for this change. If we > are to consider only the worst-case, we should be reverting JIT. Oh, come on. One can be disabled with a GUC, has (although not good enough) intelligence when it switches on, the other has ... none of that. Obviously performance is always a balancing act, but you'd be pretty pissed at anybody else regressing performance in a non-fringe case, and then refused responsibility. And as I said, I'm willing to help. Greetings, Andres Freund
Andres Freund <andres@anarazel.de> writes: > Oh, come on. One can be disabled with a GUC, has (although not good > enough) intelligence when it switches on, the other has ... none of > that. Obviously performance is always a balancing act, but you'd be > pretty pissed at anybody else regressing performance in a non-fringe > case, and then refused responsibility. And as I said, I'm willing to > help. Well, fine, let's work on it. Did you note Alexander's comparison to some "stb" library in the other thread? I wonder if we could borrow code or at least ideas from that. regards, tom lane
So, circling back to the very beginning of this thread where I worried about all the compile warnings we get on NetBSD-current, I'm pleased to report that HEAD compiles warning-free so long as you define PG_PRINTF_ATTRIBUTE to "__syslog__" rather than "gnu_printf". So attached is a proposed patch to make configure check whether %m works without a warning, and try "__syslog__" if "gnu_printf" does not work for that. (I did it in that order so that if NetBSD get their heads screwed back on straight and stop complaining about perfectly GNU-compliant code, we'll go back to selecting "gnu_printf".) Any objections? regards, tom lane diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index fb58c94..af2dea1 100644 *** a/config/c-compiler.m4 --- b/config/c-compiler.m4 *************** fi])# PGAC_C_SIGNED *** 21,41 **** # ----------------------- # 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 ac_c_werror_flag=yes AC_COMPILE_IFELSE([AC_LANG_PROGRAM( ! [extern int ! pgac_write(int ignore, const char *fmt,...) ! __attribute__((format(gnu_printf, 2, 3)));], [])], ! [pgac_cv_printf_archetype=gnu_printf], ! [pgac_cv_printf_archetype=printf]) ! ac_c_werror_flag=$ac_save_c_werror_flag]) ! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype], ! [Define to gnu_printf if compiler supports it, else printf.]) ! ])# PGAC_PRINTF_ARCHETYPE # PGAC_TYPE_64BIT_INT(TYPE) --- 21,56 ---- # ----------------------- # 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). However, ! # on some NetBSD versions, that doesn't work while "__syslog__" does. ! # If all else fails, use "printf". AC_DEFUN([PGAC_PRINTF_ARCHETYPE], [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype, + [pgac_cv_printf_archetype=gnu_printf + PGAC_TEST_PRINTF_ARCHETYPE + if [[ "$ac_archetype_ok" = no ]]; then + pgac_cv_printf_archetype=__syslog__ + PGAC_TEST_PRINTF_ARCHETYPE + if [[ "$ac_archetype_ok" = no ]]; then + pgac_cv_printf_archetype=printf + fi + fi]) + AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype], + [Define to best printf format archetype, usually gnu_printf if available.]) + ])# PGAC_PRINTF_ARCHETYPE + + # Subroutine: test $pgac_cv_printf_archetype, set $ac_archetype_ok to yes or no + AC_DEFUN([PGAC_TEST_PRINTF_ARCHETYPE], [ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes AC_COMPILE_IFELSE([AC_LANG_PROGRAM( ! [extern void pgac_write(int ignore, const char *fmt,...) ! __attribute__((format($pgac_cv_printf_archetype, 2, 3)));], ! [pgac_write(0, "error %s: %m", "foo");])], ! [ac_archetype_ok=yes], ! [ac_archetype_ok=no]) ! ac_c_werror_flag=$ac_save_c_werror_flag ! ])# PGAC_TEST_PRINTF_ARCHETYPE # PGAC_TYPE_64BIT_INT(TYPE) diff --git a/configure b/configure index 0448c6b..b7250d7 100755 *** a/configure --- b/configure *************** $as_echo_n "checking for printf format a *** 13583,13610 **** if ${pgac_cv_printf_archetype+:} false; then : $as_echo_n "(cached) " >&6 else ! ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ ! extern int ! pgac_write(int ignore, const char *fmt,...) ! __attribute__((format(gnu_printf, 2, 3))); int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ! pgac_cv_printf_archetype=gnu_printf else ! pgac_cv_printf_archetype=printf fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5 $as_echo "$pgac_cv_printf_archetype" >&6; } --- 13583,13639 ---- if ${pgac_cv_printf_archetype+:} false; then : $as_echo_n "(cached) " >&6 else ! pgac_cv_printf_archetype=gnu_printf ! ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ ! extern void pgac_write(int ignore, const char *fmt,...) ! __attribute__((format($pgac_cv_printf_archetype, 2, 3))); int main () { + pgac_write(0, "error %s: %m", "foo"); + ; + return 0; + } + _ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + ac_archetype_ok=yes + else + ac_archetype_ok=no + fi + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag + if [ "$ac_archetype_ok" = no ]; then + pgac_cv_printf_archetype=__syslog__ + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + extern void pgac_write(int ignore, const char *fmt,...) + __attribute__((format($pgac_cv_printf_archetype, 2, 3))); + int + main () + { + pgac_write(0, "error %s: %m", "foo"); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ! ac_archetype_ok=yes else ! ac_archetype_ok=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag + + if [ "$ac_archetype_ok" = no ]; then + pgac_cv_printf_archetype=printf + fi + fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5 $as_echo "$pgac_cv_printf_archetype" >&6; } diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 7894caa..9798bd2 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 804,810 **** /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define to gnu_printf if compiler supports it, else printf. */ #undef PG_PRINTF_ATTRIBUTE /* PostgreSQL version as a string */ --- 804,810 ---- /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define to best printf format archetype, usually gnu_printf if available. */ #undef PG_PRINTF_ATTRIBUTE /* PostgreSQL version as a string */
So, circling back to the very beginning of this thread where I worried about all the compile warnings we get on NetBSD-current, I'm pleased to report that HEAD compiles warning-free so long as you define PG_PRINTF_ATTRIBUTE to "__syslog__" rather than "gnu_printf". So attached is a proposed patch to make configure check whether %m works without a warning, and try "__syslog__" if "gnu_printf" does not work for that. (I did it in that order so that if NetBSD get their heads screwed back on straight and stop complaining about perfectly GNU-compliant code, we'll go back to selecting "gnu_printf".) Any objections? regards, tom lane diff --git a/config/c-compiler.m4 b/config/c-compiler.m4 index fb58c94..af2dea1 100644 *** a/config/c-compiler.m4 --- b/config/c-compiler.m4 *************** fi])# PGAC_C_SIGNED *** 21,41 **** # ----------------------- # 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 ac_c_werror_flag=yes AC_COMPILE_IFELSE([AC_LANG_PROGRAM( ! [extern int ! pgac_write(int ignore, const char *fmt,...) ! __attribute__((format(gnu_printf, 2, 3)));], [])], ! [pgac_cv_printf_archetype=gnu_printf], ! [pgac_cv_printf_archetype=printf]) ! ac_c_werror_flag=$ac_save_c_werror_flag]) ! AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype], ! [Define to gnu_printf if compiler supports it, else printf.]) ! ])# PGAC_PRINTF_ARCHETYPE # PGAC_TYPE_64BIT_INT(TYPE) --- 21,56 ---- # ----------------------- # 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). However, ! # on some NetBSD versions, that doesn't work while "__syslog__" does. ! # If all else fails, use "printf". AC_DEFUN([PGAC_PRINTF_ARCHETYPE], [AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype, + [pgac_cv_printf_archetype=gnu_printf + PGAC_TEST_PRINTF_ARCHETYPE + if [[ "$ac_archetype_ok" = no ]]; then + pgac_cv_printf_archetype=__syslog__ + PGAC_TEST_PRINTF_ARCHETYPE + if [[ "$ac_archetype_ok" = no ]]; then + pgac_cv_printf_archetype=printf + fi + fi]) + AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype], + [Define to best printf format archetype, usually gnu_printf if available.]) + ])# PGAC_PRINTF_ARCHETYPE + + # Subroutine: test $pgac_cv_printf_archetype, set $ac_archetype_ok to yes or no + AC_DEFUN([PGAC_TEST_PRINTF_ARCHETYPE], [ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes AC_COMPILE_IFELSE([AC_LANG_PROGRAM( ! [extern void pgac_write(int ignore, const char *fmt,...) ! __attribute__((format($pgac_cv_printf_archetype, 2, 3)));], ! [pgac_write(0, "error %s: %m", "foo");])], ! [ac_archetype_ok=yes], ! [ac_archetype_ok=no]) ! ac_c_werror_flag=$ac_save_c_werror_flag ! ])# PGAC_TEST_PRINTF_ARCHETYPE # PGAC_TYPE_64BIT_INT(TYPE) diff --git a/configure b/configure index 0448c6b..b7250d7 100755 *** a/configure --- b/configure *************** $as_echo_n "checking for printf format a *** 13583,13610 **** if ${pgac_cv_printf_archetype+:} false; then : $as_echo_n "(cached) " >&6 else ! ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ ! extern int ! pgac_write(int ignore, const char *fmt,...) ! __attribute__((format(gnu_printf, 2, 3))); int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ! pgac_cv_printf_archetype=gnu_printf else ! pgac_cv_printf_archetype=printf fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5 $as_echo "$pgac_cv_printf_archetype" >&6; } --- 13583,13639 ---- if ${pgac_cv_printf_archetype+:} false; then : $as_echo_n "(cached) " >&6 else ! pgac_cv_printf_archetype=gnu_printf ! ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ ! extern void pgac_write(int ignore, const char *fmt,...) ! __attribute__((format($pgac_cv_printf_archetype, 2, 3))); int main () { + pgac_write(0, "error %s: %m", "foo"); + ; + return 0; + } + _ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + ac_archetype_ok=yes + else + ac_archetype_ok=no + fi + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag + if [ "$ac_archetype_ok" = no ]; then + pgac_cv_printf_archetype=__syslog__ + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + extern void pgac_write(int ignore, const char *fmt,...) + __attribute__((format($pgac_cv_printf_archetype, 2, 3))); + int + main () + { + pgac_write(0, "error %s: %m", "foo"); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ! ac_archetype_ok=yes else ! ac_archetype_ok=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag + + if [ "$ac_archetype_ok" = no ]; then + pgac_cv_printf_archetype=printf + fi + fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_printf_archetype" >&5 $as_echo "$pgac_cv_printf_archetype" >&6; } diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 7894caa..9798bd2 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 804,810 **** /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define to gnu_printf if compiler supports it, else printf. */ #undef PG_PRINTF_ATTRIBUTE /* PostgreSQL version as a string */ --- 804,810 ---- /* PostgreSQL major version as a string */ #undef PG_MAJORVERSION ! /* Define to best printf format archetype, usually gnu_printf if available. */ #undef PG_PRINTF_ATTRIBUTE /* PostgreSQL version as a string */