On 2026-08-21 01:14, Kirill Makurin wrote:
I believe that original impression of setting LC_CTYPE to "C" comes from "C"
being the active locale before problematic `setlocale` call.
Are you suggesting something like the attached patch to Gnulib? If not, what
would be a better patch?
Is it possible that sometimes setlocale leaves LC_CTYPE alone, and sometimes sets it to
"C"? If so, the attached code patch would handle either case, but the
documentation patch would need changing.
I have not installed the attached patch, as I don't use MS-Windows and can't
easily test it, and it's not obviously OK.
From 85471114b8e46f200afb94b532f26a58cdfbc05c Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Fri, 21 Aug 2026 09:44:22 -0700
Subject: [PATCH] setlocale: port better to MS-Windows
Problem reported by Kirill Makurin in:
https://lists.gnu.org/r/bug-gnulib/2026-08/msg00247.html
* lib/setlocale.c (setlocale_improved):
When setlocale(LC_ALL,...) misbehaves on MS-Windows, it
can leave LC_CTYPE alone, instead of setting it to "C".
---
ChangeLog | 9 ++++++++
doc/posix-functions/setlocale.texi | 4 ++--
lib/setlocale.c | 36 +++++++++++++++++++++---------
modules/setlocale | 1 +
4 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 44345023a7..6cc55b7015 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2026-08-21 Paul Eggert <[email protected]>
+
+ setlocale: port better to MS-Windows
+ Problem reported by Kirill Makurin in:
+ https://lists.gnu.org/r/bug-gnulib/2026-08/msg00247.html
+ * lib/setlocale.c (setlocale_improved):
+ When setlocale(LC_ALL,...) misbehaves on MS-Windows, it
+ can leave LC_CTYPE alone, instead of setting it to "C".
+
2026-08-21 Bruno Haible <[email protected]>
getugroups: Clarify and optimize.
diff --git a/doc/posix-functions/setlocale.texi b/doc/posix-functions/setlocale.texi
index 25f8a9500a..3426dabe25 100644
--- a/doc/posix-functions/setlocale.texi
+++ b/doc/posix-functions/setlocale.texi
@@ -16,8 +16,8 @@ and @code{setlocale(@var{category},NULL)} both ignore the environment variables
@code{LC_ALL}, @code{@var{category}}, and @code{LANG}.
@item
On Windows platforms (excluding Cygwin) and Cygwin 1.5.x,
-@code{setlocale(LC_ALL,@var{name})} succeeds and sets the LC_CTYPE category to
-@samp{C} when it does not support the encoding, instead of failing.
+when an encoding is not supported @code{setlocale(LC_ALL,@var{name})}
+succeeds without changing the LC_CTYPE category, instead of failing.
@item
On Windows platforms (excluding Cygwin), @code{setlocale} understands different
locale names, that are not based on ISO 639 language names and ISO 3166 country
diff --git a/lib/setlocale.c b/lib/setlocale.c
index f40b6295d4..93322672bd 100644
--- a/lib/setlocale.c
+++ b/lib/setlocale.c
@@ -1452,6 +1452,19 @@ setlocale_improved (int category, const char *locale)
if (base_name == NULL)
base_name = gl_locale_name_default ();
+# if defined _WIN32 && ! defined __CYGWIN__
+ /* On native Windows, setlocale(LC_ALL,...) may succeed but
+ leave the LC_CTYPE category unchanged when it does not
+ support the specified encoding. To detect this below,
+ temporarily set the LC_CTYPE category to "C" first. */
+ bool dotted_locale = strchr (base_name, '.') != NULL;
+ if (dotted_locale && setlocale (LC_CTYPE, "C") == NULL)
+ {
+ free (saved_locale);
+ return NULL;
+ }
+# endif
+
unsigned int i;
if (setlocale_unixlike (LC_ALL, base_name) != NULL)
{
@@ -1469,11 +1482,10 @@ setlocale_improved (int category, const char *locale)
i = 0;
}
# if defined _WIN32 && ! defined __CYGWIN__
- /* On native Windows, setlocale(LC_ALL,...) may succeed but set the
- LC_CTYPE category to an invalid value ("C") when it does not
- support the specified encoding. Report a failure instead. */
- if (strchr (base_name, '.') != NULL
- && streq (setlocale (LC_CTYPE, NULL), "C"))
+ /* Report a failure if setlocale(LC_ALL,...) succeeded but
+ left the LC_CTYPE category unchanged because it does not
+ support the specified encoding. */
+ if (dotted_locale && streq (setlocale (LC_CTYPE, NULL), "C"))
goto fail;
# endif
@@ -1685,16 +1697,20 @@ setlocale_improved (int category, const char *locale)
if (saved_locale == NULL)
return NULL;
- if (setlocale_unixlike (LC_ALL, native_locale) == NULL)
+ /* On native Windows, setlocale(LC_ALL,...) may succeed but
+ leave the LC_CTYPE category unchanged when it does not
+ support the specified encoding. To detect this below,
+ temporarily set the LC_CTYPE category to "C" first. */
+ if (setlocale (LC_CTYPE, "C") == NULL
+ || setlocale_unixlike (LC_ALL, native_locale) == NULL)
{
free (saved_locale);
return NULL;
}
- /* On native Windows, setlocale(LC_ALL,...) may succeed but
- set the LC_CTYPE category to an invalid value ("C") when
- it does not support the specified encoding. Report a
- failure instead. */
+ /* Report a failure if setlocale(LC_ALL,...) succeeded but
+ left the LC_CTYPE category unchanged because it does not
+ support the specified encoding. */
if (streq (setlocale (LC_CTYPE, NULL), "C"))
{
/* Don't risk an endless recursion. */
diff --git a/modules/setlocale b/modules/setlocale
index aff365ac95..76b7dd3c95 100644
--- a/modules/setlocale
+++ b/modules/setlocale
@@ -6,6 +6,7 @@ lib/setlocale.c
m4/setlocale.m4
Depends-on:
+bool
locale-h
stdcountof-h
streq
--
2.53.0