Re: Incorrect information about setlocale behavior on native windows

Paul Eggert <[email protected]>
Newsgroups gmane.comp.lib.gnulib.bugs
Organization UCLA Computer Science Department
Message-ID <[email protected]>
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.
0001-setlocale-port-better-to-MS-Windows.patch (text/x-patch, 5.5 KB)
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.