* lib/locale.in.h (setlocale): Change the return type to 'const char *'.
* lib/setlocale.c (setlocale_mtsafe, setlocale_unixlike): Change the
return type to 'const char *'. Remove a cast.
(setlocale_single): Change the return type to 'const char *'.
(setlocale_improved): Change the return type to 'const char *'. Change
three variables from 'char *' to 'const char *'. Add local variable
old_locale.
* lib/localcharset.c (locale_charset): Change two variables from
'char *' to 'const char *'.
* doc/posix-functions/setlocale.texi: Document that Gnulib uses the
return type 'const char *'.
* NEWS: Mention the change.
* m4/mbrtowc.m4 (gl_MBRTOWC_C_LOCALE): Change a variable from 'char *'
to 'const char *'.
* m4/mbrtoc32.m4 (gl_MBRTOC32_C_LOCALE, gl_MBRTOC32_UTF8_LOCALE):
Likewise.
* m4/mbrtoc16.m4 (gl_MBRTOC16_C_LOCALE): Likewise.
* tests/test-setlocale-w32utf8.c (main): Likewise.
* tests/test-setlocale1.c (setlocale): Update expected signature.
* tests/test-locale-h-c++.cc (GNULIB_NAMESPACE::setlocale): Likewise.
+2026-06-07 Bruno Haible <bruno@clisp.org>
+
+ setlocale: Detect invalid writes to the returned string in some cases.
+ * lib/locale.in.h (setlocale): Change the return type to 'const char *'.
+ * lib/setlocale.c (setlocale_mtsafe, setlocale_unixlike): Change the
+ return type to 'const char *'. Remove a cast.
+ (setlocale_single): Change the return type to 'const char *'.
+ (setlocale_improved): Change the return type to 'const char *'. Change
+ three variables from 'char *' to 'const char *'. Add local variable
+ old_locale.
+ * lib/localcharset.c (locale_charset): Change two variables from
+ 'char *' to 'const char *'.
+ * doc/posix-functions/setlocale.texi: Document that Gnulib uses the
+ return type 'const char *'.
+ * NEWS: Mention the change.
+ * m4/mbrtowc.m4 (gl_MBRTOWC_C_LOCALE): Change a variable from 'char *'
+ to 'const char *'.
+ * m4/mbrtoc32.m4 (gl_MBRTOC32_C_LOCALE, gl_MBRTOC32_UTF8_LOCALE):
+ Likewise.
+ * m4/mbrtoc16.m4 (gl_MBRTOC16_C_LOCALE): Likewise.
+ * tests/test-setlocale-w32utf8.c (main): Likewise.
+ * tests/test-setlocale1.c (setlocale): Update expected signature.
+ * tests/test-locale-h-c++.cc (GNULIB_NAMESPACE::setlocale): Likewise.
+
2026-06-07 Bruno Haible <bruno@clisp.org>
tests: Avoid conflict between mingw standard C++ library and Gnulib.
Date Modules Changes
+2026-06-07 setlocale The return type of the setlocale function is now
+ 'const char *' on some platforms. You may need to
+ adjust assignments of the form
+ char *l = setlocale (...);
+ to
+ const char *l = setlocale (...);
+
2026-03-31 lock This module no longer defines the once-only
execution primitives. For these, request the
'once' module and #include "glthread/once.h".
@code{"C.UTF-8"} locale.
@end itemize
+Note: The return type of the @code{setlocale} function
+is @code{char *} in POSIX, with the constraint that
+``The application shall not modify the string returned.''
+The Gnulib-overridden @code{setlocale} function
+has a return type of @code{const char *}.
+This helps detecting code that attempts to write into that string.
+
Note: The names of locales with UTF-8 encoding are platform dependent:
@itemize
@item
'setlocale' call specified. So we use it as a last resort, in
case the string returned by 'setlocale' doesn't specify the
codepage. */
- char *current_locale = setlocale (LC_CTYPE, NULL);
- char *pdot = strrchr (current_locale, '.');
+ const char *current_locale = setlocale (LC_CTYPE, NULL);
+ const char *pdot = strrchr (current_locale, '.');
if (pdot && 2 + strlen (pdot + 1) + 1 <= sizeof (buf))
sprintf (buf, "CP%s", pdot + 1);
#endif
#if @GNULIB_SETLOCALE@
+/* The return type 'const char *' serves the purpose of producing warnings
+ for invalid uses of the value returned from this function. */
# if @REPLACE_SETLOCALE@
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef setlocale
# define setlocale rpl_setlocale
# define GNULIB_defined_setlocale 1
# endif
-_GL_FUNCDECL_RPL (setlocale, char *, (int category, const char *locale), );
-_GL_CXXALIAS_RPL (setlocale, char *, (int category, const char *locale));
+_GL_FUNCDECL_RPL (setlocale, const char *,
+ (int category, const char *locale), );
+_GL_CXXALIAS_RPL (setlocale, const char *,
+ (int category, const char *locale));
# else
-_GL_CXXALIAS_SYS (setlocale, char *, (int category, const char *locale));
+_GL_CXXALIAS_SYS_CAST (setlocale, const char *,
+ (int category, const char *locale));
# endif
# if __GLIBC__ >= 2
_GL_CXXALIASWARN (setlocale);
# if NEED_SETLOCALE_IMPROVED
static
# endif
-char *
+const char *
setlocale_mtsafe (int category, const char *locale)
{
if (locale == NULL)
- return (char *) setlocale_null (category);
+ return setlocale_null (category);
else
return setlocale (category, locale);
}
/* Like setlocale, but accept also locale names in the form ll or ll_CC,
where ll is an ISO 639 language code and CC is an ISO 3166 country code. */
-static char *
+static const char *
setlocale_unixlike (int category, const char *locale)
{
int is_utf8 = (GetACP () == 65001);
locale = "English_United States.65001";
/* First, try setlocale with the original argument unchanged. */
- char *result = setlocale_mtsafe (category, locale);
+ const char *result = setlocale_mtsafe (category, locale);
if (result != NULL)
return result;
# elif defined __ANDROID__
/* Like setlocale, but accept also the locale names "C" and "POSIX". */
-static char *
+static const char *
setlocale_unixlike (int category, const char *locale)
{
- char *result = setlocale_fixed (category, locale);
+ const char *result = setlocale_fixed (category, locale);
if (result == NULL)
switch (category)
{
case LC_MEASUREMENT:
if (locale == NULL
|| streq (locale, "C") || streq (locale, "POSIX"))
- result = (char *) "C";
+ result = "C";
break;
default:
break;
# if LC_MESSAGES == 1729
/* Like setlocale, but support also LC_MESSAGES. */
-static char *
+static const char *
setlocale_single (int category, const char *locale)
{
if (category == LC_MESSAGES)
# endif
-char *
+const char *
setlocale_improved (int category, const char *locale)
{
if (locale != NULL && locale[0] == '\0')
};
/* Back up the old locale, in case one of the steps fails. */
- char *saved_locale = setlocale (LC_ALL, NULL);
- if (saved_locale == NULL)
+ const char *old_locale = setlocale (LC_ALL, NULL);
+ if (old_locale == NULL)
return NULL;
- saved_locale = strdup (saved_locale);
+ char *saved_locale = strdup (old_locale);
if (saved_locale == NULL)
return NULL;
/* In the underlying implementation, LC_ALL does not contain
LC_MESSAGES. Therefore we need to handle LC_MESSAGES
separately. */
- char *result;
+ const char *result;
# if defined _WIN32 && ! defined __CYGWIN__
if (strchr (native_locale, '.') != NULL)
{
- char *saved_locale;
-
/* Back up the old locale. */
- saved_locale = setlocale (LC_ALL, NULL);
- if (saved_locale == NULL)
+ const char *old_locale = setlocale (LC_ALL, NULL);
+ if (old_locale == NULL)
return NULL;
- saved_locale = strdup (saved_locale);
+ char *saved_locale = strdup (old_locale);
if (saved_locale == NULL)
return NULL;
"LC_COLLATE=...;LC_CTYPE=...;LC_MONETARY=...;LC_NUMERIC=...;LC_TIME=..."
If necessary, add ";LC_MESSAGES=..." at the end. */
{
- char *name1 = setlocale (LC_ALL, NULL);
- char *name2 = setlocale_single (LC_MESSAGES, NULL);
+ const char *name1 = setlocale (LC_ALL, NULL);
+ const char *name2 = setlocale_single (LC_MESSAGES, NULL);
if (streq (name1, name2))
/* Not a mixed locale. */
return name1;
# mbrtoc16.m4
-# serial 4
+# serial 5
dnl Copyright (C) 2014-2026 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
#include <uchar.h>
]], [[
int i;
- char *locale = setlocale (LC_ALL, "C");
+ const char *locale = setlocale (LC_ALL, "C");
if (! locale)
return 2;
for (i = CHAR_MIN; i <= CHAR_MAX; i++)
# mbrtoc32.m4
-# serial 24
+# serial 25
dnl Copyright (C) 2014-2026 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
#include <uchar.h>
]], [[
int i;
- char *locale = setlocale (LC_ALL, "C");
+ const char *locale = setlocale (LC_ALL, "C");
if (! locale)
return 2;
for (i = CHAR_MIN; i <= CHAR_MAX; i++)
#endif
#include <uchar.h>
]], [[
- char *locale = setlocale (LC_ALL, "en_US.UTF-8");
+ const char *locale = setlocale (LC_ALL, "en_US.UTF-8");
if (locale)
{
/* This test fails on Cygwin 3.5.3. */
# mbrtowc.m4
-# serial 50
+# serial 51
dnl Copyright (C) 2001-2002, 2004-2005, 2008-2026 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
#include <wchar.h>
]], [[
int i;
- char *locale = setlocale (LC_ALL, "C");
+ const char *locale = setlocale (LC_ALL, "C");
if (! locale)
return 2;
for (i = CHAR_MIN; i <= CHAR_MAX; i++)
#endif
#if GNULIB_TEST_SETLOCALE
-SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, char *, (int, const char *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, const char *, (int, const char *));
#endif
#if GNULIB_TEST_NEWLOCALE
{
#ifdef _UCRT
/* Test that setlocale() works as expected in a UTF-8 locale. */
- char *name;
+ const char *name;
/* This looks at all LC_*, LANG environment variables, which are all unset
at this point. */
#include <locale.h>
#include "signature.h"
+#if GNULIB_defined_setlocale
+SIGNATURE_CHECK (setlocale, const char *, (int, const char *));
+#else
SIGNATURE_CHECK (setlocale, char *, (int, const char *));
+#endif
#include <stdlib.h>
#include <string.h>