On Fri, 2025-07-11 at 11:48 +1200, Thomas Munro wrote: > On Fri, Jul 11, 2025 at 6:22 AM Jeff Davis <pg...@j-davis.com> wrote: > > I don't have a great windows development environment, and it > > appears CI > > and the buildfarm don't offer great coverage either. Can I ask for > > a > > volunteer to do the windows side of this work? > > Me neither but I'm willing to help with that, and have done lots of > closely related things through trial-by-CI...
Attached a patch to separate the message translation (both gettext and strerror translations) from setlocale(). That's a step towards thread safety, and also a step toward setting LC_CTYPE=C permanently (more work still required there). The patch feels a bit over-engineered, but I'd like to know what you think. It would be great if you could test/debug the windows NLS- enabled paths. I'm also not sure what to do about the NetBSD path. NetBSD has no uselocale(), so I have to fall bad to temporary setlocale(), which is not thread safe. And I'm getting a mysterious error in test_aio for NetBSD, which I haven't investigated yet. Regards, Jeff Davis
From 8bd59e7d52351fadeb4fe26023aa0ff57735e03f Mon Sep 17 00:00:00 2001 From: Jeff Davis <j...@j-davis.com> Date: Fri, 18 Jul 2025 14:06:45 -0700 Subject: [PATCH v5] Create wrapper for managing NLS locale. Message translation depends on LC_CTYPE and LC_MESSAGES. Use wrapper functions to control those settings rather than relying on the permanent setlocale() settings. Improves thread safety by using "_l()" variants of functions or uselocale() where available. On windows, setlocale() can be made thread-safe. There is still at least one platform (NetBSD) where none of those options are available, in which case it still depends on thread-unsafe setlocale(). Also separates message translation behavior from other, unrelated behaviors like tolower(). Discussion: https://postgr.es/m/f040113cf384ada69558ec004a04a3ddb3e40a26.ca...@j-davis.com --- configure.ac | 2 + meson.build | 2 + src/backend/main/main.c | 13 +- src/backend/utils/adt/Makefile | 1 + src/backend/utils/adt/meson.build | 1 + src/backend/utils/adt/pg_locale.c | 39 +-- src/backend/utils/adt/pg_nls.c | 417 ++++++++++++++++++++++++++++++ src/backend/utils/init/postinit.c | 4 + src/include/c.h | 19 +- src/include/pg_config.h.in | 8 + src/include/port.h | 10 + src/include/utils/pg_nls.h | 29 +++ src/tools/pg_bsd_indent/err.c | 2 + 13 files changed, 524 insertions(+), 23 deletions(-) create mode 100644 src/backend/utils/adt/pg_nls.c create mode 100644 src/include/utils/pg_nls.h diff --git a/configure.ac b/configure.ac index c2877e36935..493014302cd 100644 --- a/configure.ac +++ b/configure.ac @@ -1791,6 +1791,8 @@ AC_CHECK_FUNCS(m4_normalize([ backtrace_symbols copyfile copy_file_range + dgettext_l + dngettext_l elf_aux_info getauxval getifaddrs diff --git a/meson.build b/meson.build index 5365aaf95e6..d3c285b2d54 100644 --- a/meson.build +++ b/meson.build @@ -2882,6 +2882,8 @@ func_checks = [ # when enabling asan the dlopen check doesn't notice that -ldl is actually # required. Just checking for dlsym() ought to suffice. ['dlsym', {'dependencies': [dl_dep], 'define': false}], + ['dgettext_l'], + ['dngettext_l'], ['elf_aux_info'], ['explicit_bzero'], ['getauxval'], diff --git a/src/backend/main/main.c b/src/backend/main/main.c index bdcb5e4f261..fbef0245b28 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -38,6 +38,7 @@ #include "utils/help_config.h" #include "utils/memutils.h" #include "utils/pg_locale.h" +#include "utils/pg_nls.h" #include "utils/ps_status.h" @@ -139,14 +140,16 @@ main(int argc, char *argv[]) init_locale("LC_CTYPE", LC_CTYPE, ""); /* - * LC_MESSAGES will get set later during GUC option processing, but we set - * it here to allow startup error messages to be localized. + * Initialize NLS locale's LC_CTYPE and LC_MESSAGES from the environment. + * It will be updated later during GUC option processing, but we set it + * here to allow startup error messages to be localized. */ -#ifdef LC_MESSAGES - init_locale("LC_MESSAGES", LC_MESSAGES, ""); -#endif + pg_nls_set_locale("", ""); /* We keep these set to "C" always. See pg_locale.c for explanation. */ +#ifdef LC_MESSAGES + init_locale("LC_MESSAGES", LC_MESSAGES, "C"); +#endif init_locale("LC_MONETARY", LC_MONETARY, "C"); init_locale("LC_NUMERIC", LC_NUMERIC, "C"); init_locale("LC_TIME", LC_TIME, "C"); diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index ffeacf2b819..38e395b7de9 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -84,6 +84,7 @@ OBJS = \ pg_locale_icu.o \ pg_locale_libc.o \ pg_lsn.o \ + pg_nls.o \ pg_upgrade_support.o \ pgstatfuncs.o \ pseudorandomfuncs.o \ diff --git a/src/backend/utils/adt/meson.build b/src/backend/utils/adt/meson.build index ed9bbd7b926..f85436cd766 100644 --- a/src/backend/utils/adt/meson.build +++ b/src/backend/utils/adt/meson.build @@ -71,6 +71,7 @@ backend_sources += files( 'pg_locale_icu.c', 'pg_locale_libc.c', 'pg_lsn.c', + 'pg_nls.c', 'pg_upgrade_support.c', 'pgstatfuncs.c', 'pseudorandomfuncs.c', diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index 97c2ac1faf9..39c06b91e7d 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -10,21 +10,29 @@ */ /*---------- - * Here is how the locale stuff is handled: LC_COLLATE and LC_CTYPE - * are fixed at CREATE DATABASE time, stored in pg_database, and cannot - * be changed. Thus, the effects of strcoll(), strxfrm(), isupper(), - * toupper(), etc. are always in the same fixed locale. + * Here is how the locale stuff is handled: * - * LC_MESSAGES is settable at run time and will take effect - * immediately. + * LC_COLLATE is permanently set to "C" with setlocale(), and collation + * behavior is defined entirely by pg_locale_t, which has provider-dependent + * behavior. If the provider is libc, then it holds a locale_t object with + * LC_COLLATE set appropriately. * - * The other categories, LC_MONETARY, LC_NUMERIC, and LC_TIME are - * permanently set to "C", and then we use temporary locale_t - * objects when we need to look up locale data based on the GUCs - * of the same name. Information is cached when the GUCs change. - * The cached information is only used by the formatting functions - * (to_char, etc.) and the money type. For the user, this should all be - * transparent. + * LC_CTYPE is fixed at CREATE DATABASE time, stored in pg_database, set at + * database connection time with setlocale(), and cannot be changed. The + * effects are limited, because casing and character classification is mostly + * defined by pg_locale_t, and message encoding is controlled by + * pg_nls_set_locale(). LC_CTYPE does affect a few places in the backend, such + * as case conversions where a pg_locale_t object is unavailable. + * + * LC_MESSAGES is permanently set to "C" with setlocale(), and NLS behavior is + * controlled with pg_nls_set_locale(). + * + * The other categories, LC_MONETARY, LC_NUMERIC, and LC_TIME are permanently + * set to "C" with setlocale(), and then we use temporary locale_t objects + * when we need to look up locale data based on the GUCs of the same name. + * Information is cached when the GUCs change. The cached information is only + * used by the formatting functions (to_char, etc.) and the money type. For + * the user, this should all be transparent. *---------- */ @@ -45,6 +53,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/pg_locale.h" +#include "utils/pg_nls.h" #include "utils/relcache.h" #include "utils/syscache.h" @@ -405,9 +414,7 @@ assign_locale_messages(const char *newval, void *extra) * LC_MESSAGES category does not exist everywhere, but accept it anyway. * We ignore failure, as per comment above. */ -#ifdef LC_MESSAGES - (void) pg_perm_setlocale(LC_MESSAGES, newval); -#endif + pg_nls_set_locale(NULL, newval); } diff --git a/src/backend/utils/adt/pg_nls.c b/src/backend/utils/adt/pg_nls.c new file mode 100644 index 00000000000..0276da880d6 --- /dev/null +++ b/src/backend/utils/adt/pg_nls.c @@ -0,0 +1,417 @@ +/*----------------------------------------------------------------------- + * + * PostgreSQL NLS utilities + * + * Portions Copyright (c) 2002-2025, PostgreSQL Global Development Group + * + * src/backend/utils/adt/pg_nls.c + * + * Platform-independent wrappers for message translation functions. The + * LC_CTYPE and LC_MESSAGES settings are set with pg_nls_set_locale() and the + * state is managed internally to this file, regardless of the outside + * settings from setlocale() or uselocale(). + * + * The implementation prefers the "_l()" variants of functions, then + * secondarily a temporary uselocale() setting (thread safe), and lastly a + * temporary setlocale() setting (which can be made thread safe on windows). + * + * This mechanism improves thread safety (on most platforms), and provides + * better separation between the behavior of NLS and other behaviors like + * isupper(), etc. + * + *----------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "utils/memutils.h" +#include "utils/pg_locale.h" +#include "utils/pg_nls.h" + +/* + * Represents global LC_CTYPE and LC_MESSAGES settings, for the purpose of + * message translation. LC_CTYPE in the postmaster comes from the environment, + * and in a backend comes from pg_database.datctype. LC_MESSAGES comes from a + * GUC, and must be kept up to date. + * + * If there's no uselocale(), keep the string values instead, and use + * setlocale(). + */ +#ifdef HAVE_USELOCALE + +static locale_t nls_locale = (locale_t) 0; + +#else + +static char *nls_lc_ctype = NULL; +static char *nls_lc_messages = NULL; + +typedef struct SaveLocale +{ +#ifndef WIN32 + char *lc_ctype; + char *lc_messages; +#else + int config_thread_locale; + wchar_t *lc_ctype; + wchar_t *lc_messages; +#endif /* WIN32 */ +} SaveLocale; + +#endif /* !HAVE_USELOCALE */ + +/* + * Set the LC_CTYPE and LC_MESSAGES to be used for message translation. + */ +void +pg_nls_set_locale(const char *ctype, const char *messages) +{ + if (ctype) + { +#ifdef HAVE_USELOCALE + locale_t loc = 0; + + errno = 0; + loc = newlocale(LC_CTYPE_MASK, ctype, nls_locale); + if (!loc) + report_newlocale_failure(ctype); + nls_locale = loc; +#else + if (!check_locale(LC_CTYPE, ctype, NULL)) + report_newlocale_failure(ctype); + if (nls_lc_ctype) + pfree(nls_lc_ctype); + nls_lc_ctype = MemoryContextStrdup(TopMemoryContext, ctype); +#endif + + /* + * Use the right encoding in translated messages. Under ENABLE_NLS, + * let pg_bind_textdomain_codeset() figure it out. Under !ENABLE_NLS, + * message format strings are ASCII, but database-encoding strings may + * enter the message via %s. This makes the overall message encoding + * equal to the database encoding. + */ +#ifdef ENABLE_NLS + SetMessageEncoding(pg_bind_textdomain_codeset(textdomain(NULL))); +#else + SetMessageEncoding(GetDatabaseEncoding()); +#endif + } + + if (messages) + { +#ifdef HAVE_USELOCALE + locale_t loc = 0; + + errno = 0; + loc = newlocale(LC_MESSAGES_MASK, messages, nls_locale); + if (!loc) + report_newlocale_failure(messages); + nls_locale = loc; +#else +#ifdef LC_MESSAGES + if (!check_locale(LC_MESSAGES, messages, NULL)) + report_newlocale_failure(messages); +#endif + if (nls_lc_messages) + pfree(nls_lc_messages); + nls_lc_messages = MemoryContextStrdup(TopMemoryContext, messages); +#endif + } +} + +#ifdef ENABLE_NLS + +#ifdef HAVE_USELOCALE + +#ifndef HAVE_DGETTEXT_L +static char * +dgettext_l(const char *domainname, const char *msgid, locale_t loc) +{ + char *result; + locale_t save_loc = uselocale(loc); + + result = dcgettext(domainname, msgid, LC_MESSAGES); + uselocale(save_loc); + return result; +} +#endif /* HAVE_DGETTEXT_L */ + +#ifndef HAVE_DNGETTEXT_L +static char * +dngettext_l(const char *domainname, const char *s, const char *p, + unsigned long int n, locale_t loc) +{ + char *result; + locale_t save_loc = uselocale(loc); + + result = dcngettext(domainname, s, p, n, LC_MESSAGES); + uselocale(save_loc); + return result; +} +#endif /* HAVE_DNGETTEXT_L */ + +static char * +pg_strerror_l(int errnum, locale_t loc) +{ + char *result; + locale_t save_loc = uselocale(loc); + + result = pg_strerror(errnum); + uselocale(save_loc); + return result; +} + +static char * +pg_strerror_r_l(int errnum, char *buf, size_t buflen, locale_t loc) +{ + char *result; + locale_t save_loc = uselocale(loc); + + result = pg_strerror_r(errnum, buf, buflen); + uselocale(save_loc); + return result; +} + +#else /* !HAVE_USELOCALE */ + +static bool +save_message_locale(SaveLocale * save) +{ +#ifndef WIN32 + char *tmp; + + /* + * This path -- ENABLE_NLS, !HAVE_USELOCALE, !WIN32 -- is not thread safe, + * but is only known to be used on NetBSD. + */ + tmp = setlocale(LC_CTYPE, NULL); + if (!tmp) + return false; + save->lc_ctype = pstrdup(tmp); + + tmp = setlocale(LC_MESSAGES, NULL); + if (!tmp) + return false; + save->lc_messages = pstrdup(tmp); + + return true; +#else + wchar_t *tmp; + + /* Put setlocale() into thread-local mode. */ + save->config_thread_locale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + + /* + * Capture the current values as wide strings. Otherwise, we might not be + * able to restore them if their names contain non-ASCII characters and + * the intermediate locale changes the expected encoding. We don't want + * to leave the caller in an unexpected state by failing to restore, or + * crash the runtime library. + */ + tmp = _wsetlocale(LC_CTYPE, NULL); + if (!tmp || !(tmp = wcsdup(tmp))) + return false; + *save->lc_ctype = tmp; + + tmp = _wsetlocale(LC_MESSAGES, NULL); + if (!tmp || !(tmp = wcsdup(tmp))) + return false; + *save->lc_messages = tmp; + + return true; +#endif +} + +static void +restore_message_locale(SaveLocale * save) +{ +#ifndef WIN32 + if (save->lc_ctype) + { + setlocale(LC_CTYPE, save->lc_ctype); + pfree(save->lc_ctype); + save->lc_ctype = NULL; + } + if (save->lc_messages) + { + setlocale(LC_MESSAGES, save->lc_messages); + pfree(save->lc_messages); + save->lc_messages = NULL; + } +#else + if (save->lc_ctype) + { + _wsetlocale(LC_CTYPE, save->lc_ctype); + free(save->lc_ctype); + save->lc_ctype = NULL; + } + if (save->lc_messages) + { + _wsetlocale(LC_MESSAGES, save->lc_messages); + free(save->lc_messages); + save->lc_messages = NULL; + } + _configthreadlocale(save->config_thread_locale); +#endif +} + +static char * +dgettext_l(const char *domainname, const char *msgid, const char *lc_ctype, + const char *lc_messages) +{ + SaveLocale save; + + if (save_message_locale(&save)) + { + char *result; + + (void) setlocale(LC_CTYPE, lc_ctype); + (void) setlocale(LC_MESSAGES, lc_messages); + + result = dcgettext(domainname, msgid, LC_MESSAGES); + restore_message_locale(&save); + return result; + } + else + return dcgettext(domainname, msgid, LC_MESSAGES); +} + +static char * +dngettext_l(const char *domainname, const char *s, const char *p, + unsigned long int n, const char *lc_ctype, + const char *lc_messages) +{ + SaveLocale save; + + if (save_message_locale(&save)) + { + char *result; + + (void) setlocale(LC_CTYPE, lc_ctype); + (void) setlocale(LC_MESSAGES, lc_messages); + + result = dcngettext(domainname, s, p, n, LC_MESSAGES); + restore_message_locale(&save); + return result; + } + else + return dcngettext(domainname, s, p, n, LC_MESSAGES); +} + +static char * +pg_strerror_l(int errnum, const char *lc_ctype, const char *lc_messages) +{ + SaveLocale save; + + if (save_message_locale(&save)) + { + char *result; + + (void) setlocale(LC_CTYPE, lc_ctype); + (void) setlocale(LC_MESSAGES, lc_messages); + + result = pg_strerror(errnum); + restore_message_locale(&save); + return result; + } + else + return pg_strerror(errnum); +} + +static char * +pg_strerror_r_l(int errnum, char *buf, size_t buflen, const char *lc_ctype, + const char *lc_messages) +{ + SaveLocale save; + + if (save_message_locale(&save)) + { + char *result; + + (void) setlocale(LC_CTYPE, lc_ctype); + (void) setlocale(LC_MESSAGES, lc_messages); + + result = pg_strerror_r(errnum, buf, buflen); + restore_message_locale(&save); + return result; + } + else + return pg_strerror_r(errnum, buf, buflen); +} + +#endif /* !HAVE_USELOCALE */ + +/* + * dgettext() with nls_locale, if set. + */ +char * +pg_nls_dgettext(const char *domainname, const char *msgid) +{ +#ifdef HAVE_USELOCALE + if (nls_locale) + return dgettext_l(domainname, msgid, nls_locale); +#else + if (nls_lc_ctype) + return dgettext_l(domainname, msgid, nls_lc_ctype, + nls_lc_messages); +#endif + else + return dcgettext(domainname, msgid, LC_MESSAGES); +} + +/* + * dngettext() with nls_locale, if set. + */ +char * +pg_nls_dngettext(const char *domainname, const char *s, const char *p, + unsigned long int n) +{ +#ifdef HAVE_USELOCALE + if (nls_locale) + return dngettext_l(domainname, s, p, n, nls_locale); +#else + if (nls_lc_ctype) + return dngettext_l(domainname, s, p, n, nls_lc_ctype, + nls_lc_messages); +#endif + else + return dcngettext(domainname, s, p, n, LC_MESSAGES); +} + +/* + * pg_strerror() with nls_locale, if set. + */ +char * +pg_nls_strerror(int errnum) +{ +#ifdef HAVE_USELOCALE + if (nls_locale) + return pg_strerror_l(errnum, nls_locale); +#else + if (nls_lc_ctype) + return pg_strerror_l(errnum, nls_lc_ctype, nls_lc_messages); +#endif + else + return pg_strerror(errnum); +} + +/* + * pg_strerror_r() with nls_locale, if set. + */ +char * +pg_nls_strerror_r(int errnum, char *buf, size_t buflen) +{ +#ifdef HAVE_USELOCALE + if (nls_locale) + return pg_strerror_r_l(errnum, buf, buflen, nls_locale); +#else + if (nls_lc_ctype) + return pg_strerror_r_l(errnum, buf, buflen, nls_lc_ctype, + nls_lc_messages); +#endif + else + return pg_strerror_r(errnum, buf, buflen); +} + +#endif /* ENABLE_NLS */ diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 641e535a73c..3206dd121ed 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -65,6 +65,7 @@ #include "utils/memutils.h" #include "utils/pg_locale.h" #include "utils/portal.h" +#include "utils/pg_nls.h" #include "utils/ps_status.h" #include "utils/snapmgr.h" #include "utils/syscache.h" @@ -430,6 +431,9 @@ CheckMyDatabase(const char *name, bool am_superuser, bool override_allow_connect " which is not recognized by setlocale().", ctype), errhint("Recreate the database with another locale or install the missing locale."))); + /* set global_message_locale for this database to datctype */ + pg_nls_set_locale(ctype, NULL); + if (strcmp(ctype, "C") == 0 || strcmp(ctype, "POSIX") == 0) database_ctype_is_c = true; diff --git a/src/include/c.h b/src/include/c.h index 6d4495bdd9f..2edb8e2f63d 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -1159,8 +1159,23 @@ typedef union PGAlignedXLogBlock * gettext support */ -#ifndef ENABLE_NLS -/* stuff we'd otherwise get from <libintl.h> */ +#if defined(ENABLE_NLS) && !defined(FRONTEND) +/* use backend's global message locale setting */ +#include "utils/pg_nls.h" + +#undef gettext +#undef dgettext +#undef ngettext +#undef dngettext + +#define gettext(x) pg_nls_dgettext(NULL, x) +#define dgettext(d,x) pg_nls_dgettext(d, x) +#define ngettext(s,p,n) pg_nls_dngettext(NULL, s, p, n) +#define dngettext(d,s,p,n) pg_nls_dngettext(d, s, p, n) +#elif defined(ENABLE_NLS) +/* use <libintl.h> directly */ +#else +/* no-op */ #define gettext(x) (x) #define dgettext(d,x) (x) #define ngettext(s,p,n) ((n) == 1 ? (s) : (p)) diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index c4dc5d72bdb..f2fa336bd95 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -111,6 +111,14 @@ don't. */ #undef HAVE_DECL_STRCHRNUL +/* Define to 1 if you have the declaration of `dgettext_l', and to 0 if you + don't. */ +#undef HAVE_DGETTEXT_L + +/* Define to 1 if you have the declaration of `dngettext_l', and to 0 if you + don't. */ +#undef HAVE_DNGETTEXT_L + /* Define to 1 if you have the declaration of `strlcat', and to 0 if you don't. */ #undef HAVE_DECL_STRLCAT diff --git a/src/include/port.h b/src/include/port.h index 3964d3b1293..c00d84d0dbf 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -249,11 +249,21 @@ extern int pg_strfromd(char *str, size_t count, int precision, double value); /* Replace strerror() with our own, somewhat more robust wrapper */ extern char *pg_strerror(int errnum); +#if defined(ENABLE_NLS) && !defined(FRONTEND) +extern char *pg_nls_strerror(int errnum); +#define strerror pg_nls_strerror +#else #define strerror pg_strerror +#endif /* Likewise for strerror_r(); note we prefer the GNU API for that */ extern char *pg_strerror_r(int errnum, char *buf, size_t buflen); +#if defined(ENABLE_NLS) && !defined(FRONTEND) +extern char *pg_nls_strerror_r(int errnum, char *buf, size_t buflen); +#define strerror_r pg_nls_strerror_r +#else #define strerror_r pg_strerror_r +#endif #define PG_STRERROR_R_BUFLEN 256 /* Recommended buffer size for strerror_r */ /* Wrap strsignal(), or provide our own version if necessary */ diff --git a/src/include/utils/pg_nls.h b/src/include/utils/pg_nls.h new file mode 100644 index 00000000000..c8c605e9f26 --- /dev/null +++ b/src/include/utils/pg_nls.h @@ -0,0 +1,29 @@ +/*----------------------------------------------------------------------- + * + * PostgreSQL NLS utilities + * + * src/include/utils/pg_nls.h + * + * Copyright (c) 2002-2025, PostgreSQL Global Development Group + * + *----------------------------------------------------------------------- + */ + +#ifndef _PG_NLS_ +#define _PG_NLS_ + +extern void pg_nls_set_locale(const char *ctype, const char *messages); + +#ifdef ENABLE_NLS + +extern char *pg_nls_dgettext(const char *domainname, const char *msgid) + pg_attribute_format_arg(2); +extern char *pg_nls_dngettext(const char *domainname, const char *s, + const char *p, unsigned long int n) + pg_attribute_format_arg(2) pg_attribute_format_arg(3); +extern char *pg_nls_strerror(int errnum); +extern char *pg_nls_strerror_r(int errnum, char *buf, size_t buflen); + +#endif + +#endif /* _PG_NLS_ */ diff --git a/src/tools/pg_bsd_indent/err.c b/src/tools/pg_bsd_indent/err.c index 807319334bc..fe153aa3dcd 100644 --- a/src/tools/pg_bsd_indent/err.c +++ b/src/tools/pg_bsd_indent/err.c @@ -27,6 +27,8 @@ * SUCH DAMAGE. */ +#define FRONTEND 1 + /* * This is cut down to just the minimum that we need to build indent. */ -- 2.43.0