From 18d6e83d27648c508f982159c0e4595519ae7758 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 13 Aug 2024 12:27:33 +1200
Subject: [PATCH v1 2/3] Use thread-safe nl_langinfo_l(), not nl_langinfo().

This gets rid of some setlocale() calls and makes the returned value
unclobberable with a defined lifetime.  The remaining call to
setlocale() is only a query of the name of the current local (in a
multi-threaded future this would have to be changed, perhaps to use a
per-database or per-backend locale_t instead of LC_GLOBAL_LOCALE).

All known non-Windows targets have nl_langinfo_l(), from POSIX 2018.
---
 src/port/chklocale.c | 67 +++++++++++++-------------------------------
 1 file changed, 19 insertions(+), 48 deletions(-)

diff --git a/src/port/chklocale.c b/src/port/chklocale.c
index a0cc52c38df..c5e987c19c2 100644
--- a/src/port/chklocale.c
+++ b/src/port/chklocale.c
@@ -306,63 +306,34 @@ pg_get_encoding_from_locale(const char *ctype, bool write_message)
 	char	   *sys;
 	int			i;
 
-	/* Get the CODESET property, and also LC_CTYPE if not passed in */
-	if (ctype)
-	{
-		char	   *save;
-		char	   *name;
-
-		/* If locale is C or POSIX, we can allow all encodings */
-		if (pg_strcasecmp(ctype, "C") == 0 ||
-			pg_strcasecmp(ctype, "POSIX") == 0)
-			return PG_SQL_ASCII;
-
-		save = setlocale(LC_CTYPE, NULL);
-		if (!save)
-			return -1;			/* setlocale() broken? */
-		/* must copy result, or it might change after setlocale */
-		save = strdup(save);
-		if (!save)
-			return -1;			/* out of memory; unlikely */
-
-		name = setlocale(LC_CTYPE, ctype);
-		if (!name)
-		{
-			free(save);
-			return -1;			/* bogus ctype passed in? */
-		}
-
 #ifndef WIN32
-		sys = nl_langinfo(CODESET);
-		if (sys)
-			sys = strdup(sys);
-#else
-		sys = win32_langinfo(name);
+	locale_t	loc;
 #endif
 
-		setlocale(LC_CTYPE, save);
-		free(save);
-	}
-	else
-	{
-		/* much easier... */
+	/* Get the CODESET property, and also LC_CTYPE if not passed in */
+	if (!ctype)
 		ctype = setlocale(LC_CTYPE, NULL);
-		if (!ctype)
-			return -1;			/* setlocale() broken? */
 
-		/* If locale is C or POSIX, we can allow all encodings */
-		if (pg_strcasecmp(ctype, "C") == 0 ||
-			pg_strcasecmp(ctype, "POSIX") == 0)
-			return PG_SQL_ASCII;
+
+	/* If locale is C or POSIX, we can allow all encodings */
+	if (pg_strcasecmp(ctype, "C") == 0 ||
+		pg_strcasecmp(ctype, "POSIX") == 0)
+		return PG_SQL_ASCII;
+
 
 #ifndef WIN32
-		sys = nl_langinfo(CODESET);
-		if (sys)
-			sys = strdup(sys);
+	loc = newlocale(LC_CTYPE_MASK, ctype, (locale_t) 0);
+	if (loc == (locale_t) 0)
+		return -1;			/* bogus ctype passed in? */
+
+	sys = nl_langinfo_l(CODESET, loc);
+	if (sys)
+		sys = strdup(sys);
+
+	freelocale(loc);
 #else
-		sys = win32_langinfo(ctype);
+	sys = win32_langinfo(ctype);
 #endif
-	}
 
 	if (!sys)
 		return -1;				/* out of memory; unlikely */
-- 
2.46.0

