* lib/setlocale-fixes.h: Renamed from lib/setlocale-messages.h.
(setlocale_fixed, setlocale_fixed_null): New declarations.
* lib/setlocale-fixes.c: Renamed from lib/setlocale-messages.h.
(lc_cat_name): New variable.
(cat_to_index, index_to_cat): New macro.
(lc_all_name): New variable.
(cat_names): New constant.
(fill_lc_all_name, extract_single_name, setlocale_fixed,
setlocale_fixed_null): New functions.
* m4/setlocale.m4 (gl_FUNC_SETLOCALE): Set NEED_SETLOCALE_IMPROVED to 1
on Android.
* lib/setlocale.c: Include setlocale-fixes.h instead of
setlocale-messages.h.
(setlocale_unixlike) [__ANDROID__]: Use setlocale_fixed instead of
setlocale_mtsafe.
(setlocale_improved): On Android, use setlocale_fixed_null instead of
setlocale.
* lib/getlocalename_l-unsafe.c: Include setlocale-fixes.h instead of
setlocale-messages.h.
(getlocalename_l_unsafe): On Android, use setlocale_fixed_null instead
of setlocale_null.
* modules/setlocale-fixes: Renamed from modules/setlocale-messages.
(Files): Update.
(configure.ac): Compile the .c file also on Android.
(Include): Include setlocale-fixes.h instead of setlocale-messages.h.
* modules/setlocale (Depends-on): Add setlocale-fixes. Remove
setlocale-messages.
* modules/getlocalename_l-unsafe (Depends-on): Likewise.
* doc/posix-functions/setlocale.texi: Mention the Android problem.
+2025-03-22 Bruno Haible <bruno@clisp.org>
+
+ setlocale-messages: Rename to setlocale-fixes and add Android support.
+ * lib/setlocale-fixes.h: Renamed from lib/setlocale-messages.h.
+ (setlocale_fixed, setlocale_fixed_null): New declarations.
+ * lib/setlocale-fixes.c: Renamed from lib/setlocale-messages.h.
+ (lc_cat_name): New variable.
+ (cat_to_index, index_to_cat): New macro.
+ (lc_all_name): New variable.
+ (cat_names): New constant.
+ (fill_lc_all_name, extract_single_name, setlocale_fixed,
+ setlocale_fixed_null): New functions.
+ * m4/setlocale.m4 (gl_FUNC_SETLOCALE): Set NEED_SETLOCALE_IMPROVED to 1
+ on Android.
+ * lib/setlocale.c: Include setlocale-fixes.h instead of
+ setlocale-messages.h.
+ (setlocale_unixlike) [__ANDROID__]: Use setlocale_fixed instead of
+ setlocale_mtsafe.
+ (setlocale_improved): On Android, use setlocale_fixed_null instead of
+ setlocale.
+ * lib/getlocalename_l-unsafe.c: Include setlocale-fixes.h instead of
+ setlocale-messages.h.
+ (getlocalename_l_unsafe): On Android, use setlocale_fixed_null instead
+ of setlocale_null.
+ * modules/setlocale-fixes: Renamed from modules/setlocale-messages.
+ (Files): Update.
+ (configure.ac): Compile the .c file also on Android.
+ (Include): Include setlocale-fixes.h instead of setlocale-messages.h.
+ * modules/setlocale (Depends-on): Add setlocale-fixes. Remove
+ setlocale-messages.
+ * modules/getlocalename_l-unsafe (Depends-on): Likewise.
+ * doc/posix-functions/setlocale.texi: Mention the Android problem.
+
2025-03-21 Bruno Haible <bruno@clisp.org>
getloadavg: Improve Linux and Android support.
locale names, that are not based on ISO 639 language names and ISO 3166 country
names.
@item
+On Android 5.0 and newer, setting any category other than @code{LC_CTYPE} of
+the global locale affects the @code{LC_CTYPE} category as well.
+@item
On Android < 5.0, which doesn't have locales, the @code{setlocale} function
always fails. The replacement, however, supports only the locale names
@code{"C"} and @code{"POSIX"}.
#include <stdlib.h>
#include <string.h>
-#if LC_MESSAGES == 1729
-# include "setlocale-messages.h"
+#if LC_MESSAGES == 1729 || defined __ANDROID__
+# include "setlocale-fixes.h"
#endif
#include "setlocale_null.h"
name = setlocale_messages_null ();
else
#endif
+#if defined __ANDROID__
+ name = setlocale_fixed_null (category);
+#else
name = setlocale_null (category);
+#endif
if (name != NULL)
return (struct string_with_storage) { name, STORAGE_GLOBAL };
else
--- /dev/null
+/* Make the global locale minimally POSIX compliant.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "setlocale-fixes.h"
+
+
+#if defined _WIN32 && !defined __CYGWIN__
+
+# include <stddef.h>
+# include <string.h>
+
+/* The system does not store an LC_MESSAGES locale category. Do it here. */
+static char lc_messages_name[64] = "C";
+
+const char *
+setlocale_messages (const char *name)
+{
+ if (name != NULL)
+ {
+ lc_messages_name[sizeof (lc_messages_name) - 1] = '\0';
+ strncpy (lc_messages_name, name, sizeof (lc_messages_name) - 1);
+ }
+ return lc_messages_name;
+}
+
+const char *
+setlocale_messages_null (void)
+{
+ /* This implementation is multithread-safe, assuming no other thread changes
+ the LC_MESSAGES locale category. */
+ return lc_messages_name;
+}
+
+#endif
+
+
+#if defined __ANDROID__
+
+# include <stddef.h>
+# include <stdlib.h>
+# include <locale.h>
+# include <string.h>
+
+/* Use the system's setlocale() function, not the gnulib override, here. */
+# undef setlocale
+
+/* Storage for the name of each category of the global locale.
+ Regarding the size, cf. SETLOCALE_NULL_MAX. */
+static char lc_cat_name[12][256+1] =
+ { "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C" };
+/* Mapping from the valid categories to the indices 0..12-1. */
+#define cat_to_index(cat) ((cat) - ((cat) >= LC_ALL))
+/* Mapping from the indicess 0..12-1 to the valid categories. */
+#define index_to_cat(i) ((i) + ((i) >= LC_ALL))
+
+/* Storage for the name of the entire global locale.
+ For mixed locales, use the syntax from glibc:
+ LC_CTYPE=...;LC_NUMERIC=...;......;LC_IDENTIFICATION=...
+ Regarding the size, cf. SETLOCALE_NULL_ALL_MAX. */
+static char lc_all_name[148+12*256+1];
+
+/* Category names. */
+static const char cat_names[12][17+1] =
+ {
+ [cat_to_index (LC_CTYPE)] = "LC_CTYPE",
+ [cat_to_index (LC_NUMERIC)] = "LC_NUMERIC",
+ [cat_to_index (LC_TIME)] = "LC_TIME",
+ [cat_to_index (LC_COLLATE)] = "LC_COLLATE",
+ [cat_to_index (LC_MONETARY)] = "LC_MONETARY",
+ [cat_to_index (LC_MESSAGES)] = "LC_MESSAGES",
+ [cat_to_index (LC_PAPER)] = "LC_PAPER",
+ [cat_to_index (LC_NAME)] = "LC_NAME",
+ [cat_to_index (LC_ADDRESS)] = "LC_ADDRESS",
+ [cat_to_index (LC_TELEPHONE)] = "LC_TELEPHONE",
+ [cat_to_index (LC_MEASUREMENT)] = "LC_MEASUREMENT",
+ [cat_to_index (LC_IDENTIFICATION)] = "LC_IDENTIFICATION",
+ };
+
+/* Combines the contents of all lc_cat_name[], filling lc_all_name. */
+static void
+fill_lc_all_name (void)
+{
+ bool all_same = true;
+ for (size_t i = 1; i < 12; i++)
+ if (strcmp (lc_cat_name[i], lc_cat_name[0]) != 0)
+ {
+ all_same = false;
+ break;
+ }
+ if (all_same)
+ /* Produce a simple locale name. */
+ strcpy (lc_all_name, lc_cat_name[0]);
+ else
+ {
+ /* Produce a mixed locale name. */
+ size_t i;
+ char *q;
+ for (i = 0, q = lc_all_name; i < 12; i++)
+ {
+ const char *p = cat_names[i];
+ size_t n = strlen (p);
+ memcpy (q, p, n); q += n;
+ *q++ = '=';
+ p = lc_cat_name[i];
+ n = strlen (p);
+ memcpy (q, p, n); q += n;
+ *q++ = ';';
+ }
+ *--q = '\0';
+ }
+}
+
+/* Extracts the name of a single category from NAME and stores it in
+ SINGLE_NAME. CATEGORY must be a valid category name != LC_ALL.
+ This is basically the opposite of fill_lc_all_name. */
+static int
+extract_single_name (char single_name[256+1], int category, const char *name)
+{
+ if (strchr (name, ';') != NULL)
+ {
+ /* A mixed locale name. */
+ const char *cat_name = cat_names[cat_to_index (category)];
+ size_t cat_name_len = strlen (cat_name);
+ const char *p = name;
+ for (;;)
+ {
+ const char *q = strchr (p, ';');
+ if (strncmp (p, cat_name, cat_name_len) == 0
+ && p[cat_name_len] == '=')
+ {
+ p += cat_name_len + 1;
+ size_t n = (q == NULL ? strlen (p) : q - p);
+ if (n >= 256+1)
+ n = 256;
+ memcpy (single_name, p, n);
+ single_name[n] = '\0';
+ return 0;
+ }
+ if (q == NULL)
+ break;
+ p = q + 1;
+ }
+ return -1;
+ }
+ else
+ {
+ /* A simple locale name. */
+ size_t n = strlen (name);
+ if (n >= 256+1)
+ n = 256;
+ memcpy (single_name, name, n);
+ single_name[n] = '\0';
+ return 0;
+ }
+}
+
+const char *
+setlocale_fixed (int category, const char *name)
+{
+ if (category == LC_ALL)
+ {
+ if (name != NULL)
+ {
+ char single_name[256+1];
+ int i;
+
+ /* Test whether NAME is valid. */
+ for (i = 12-1; i >= 0; i--)
+ if (extract_single_name (single_name, index_to_cat (i), name) < 0)
+ return NULL;
+ /* Now single_name contains the one for the index 0,
+ i.e. for LC_CTYPE. */
+ if (setlocale (LC_CTYPE, single_name) == NULL)
+ return NULL;
+
+ /* Fill lc_cat_name[]. */
+ for (i = 12-1; i >= 0; i--)
+ {
+ if (extract_single_name (single_name, index_to_cat (i), name) < 0)
+ abort ();
+ strcpy (lc_cat_name[i], single_name);
+ }
+ /* Update lc_all_name. */
+ fill_lc_all_name ();
+ }
+
+ return lc_all_name;
+ }
+ if (category >= LC_CTYPE && category <= LC_IDENTIFICATION)
+ {
+ if (name != NULL)
+ {
+ char single_name[256+1];
+
+ /* Test whether NAME is valid. */
+ if (extract_single_name (single_name, category, name) < 0)
+ return NULL;
+ if (category == LC_CTYPE)
+ if (setlocale (category, single_name) == NULL)
+ return NULL;
+
+ /* Fill lc_cat_name[]. */
+ strcpy (lc_cat_name[cat_to_index (category)], single_name);
+ /* Update lc_all_name. */
+ fill_lc_all_name ();
+ }
+
+ return lc_cat_name[cat_to_index (category)];
+ }
+ return NULL;
+}
+
+const char *
+setlocale_fixed_null (int category)
+{
+ /* This implementation is multithread-safe, assuming no other thread changes
+ any locale category. */
+ if (category == LC_ALL)
+ return lc_all_name;
+ if (category >= LC_CTYPE && category <= LC_IDENTIFICATION)
+ return lc_cat_name[cat_to_index (category)];
+ return NULL;
+}
+
+#endif
--- /dev/null
+/* Make the global locale minimally POSIX compliant.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ This file is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
+
+#ifndef _SETLOCALE_FIXES_H
+#define _SETLOCALE_FIXES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if defined _WIN32 && !defined __CYGWIN__
+
+/* This section is only relevant on platforms that lack LC_MESSAGES, namely
+ native Windows. */
+
+/* setlocale_messages (NAME) is like setlocale (LC_MESSAGES, NAME). */
+extern const char *setlocale_messages (const char *name);
+
+/* setlocale_messages_null () is like setlocale (LC_MESSAGES, NULL), except that
+ it is guaranteed to be multithread-safe. */
+extern const char *setlocale_messages_null (void);
+
+#endif
+
+
+#if defined __ANDROID__
+
+/* This section is only relevant on Android.
+ While OpenBSD ≥ 6.2 and Android API level >= 21 have a fake locale_t type,
+ regarding the global locale, OpenBSD at least stores the name of each
+ category of the global locale. Android doesn't do this, and additionally
+ has a bug: setting any category != LC_CTYPE of the global locale affects
+ the LC_CTYPE category as well. */
+
+/* Like setlocale (CATEGORY, NAME), but fixes these two Android bugs. */
+extern const char *setlocale_fixed (int category, const char *name);
+
+/* Like setlocale_null (CATEGORY), but consistent with setlocale_fixed. */
+extern const char *setlocale_fixed_null (int category);
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SETLOCALE_FIXES_H */
+++ /dev/null
-/* Extension of the global locale with LC_MESSAGES.
- Copyright (C) 2025 Free Software Foundation, Inc.
-
- This file is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- This file is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-/* Specification. */
-#include "setlocale-messages.h"
-
-#include <stddef.h>
-#include <string.h>
-
-/* The system does not store an LC_MESSAGES locale category. Do it here. */
-static char lc_messages_name[64] = "C";
-
-const char *
-setlocale_messages (const char *name)
-{
- if (name != NULL)
- {
- lc_messages_name[sizeof (lc_messages_name) - 1] = '\0';
- strncpy (lc_messages_name, name, sizeof (lc_messages_name) - 1);
- }
- return lc_messages_name;
-}
-
-const char *
-setlocale_messages_null (void)
-{
- /* This implementation is multithread-safe, assuming no other thread changes
- the LC_MESSAGES locale category. */
- return lc_messages_name;
-}
+++ /dev/null
-/* Extension of the global locale with LC_MESSAGES.
- Copyright (C) 2025 Free Software Foundation, Inc.
-
- This file is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- This file is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
-
-/* Written by Bruno Haible <bruno@clisp.org>, 2025. */
-
-#ifndef _SETLOCALE_MESSAGES_H
-#define _SETLOCALE_MESSAGES_H
-
-/* This file is only relevant on platforms that lack LC_MESSAGES, namely
- native Windows. */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* setlocale_messages (NAME) is like setlocale (LC_MESSAGES, NAME). */
-extern const char *setlocale_messages (const char *name);
-
-/* setlocale_messages_null () is like setlocale (LC_MESSAGES, NULL), except that
- it is guaranteed to be multithread-safe. */
-extern const char *setlocale_messages_null (void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SETLOCALE_MESSAGES_H */
#include <stdlib.h>
#include <string.h>
-#include "setlocale-messages.h"
+#include "setlocale-fixes.h"
#include "localename.h"
#if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE
static char *
setlocale_unixlike (int category, const char *locale)
{
- char *result = setlocale_mtsafe (category, locale);
+ char *result = setlocale_fixed (category, locale);
if (result == NULL)
switch (category)
{
return resultbuf;
}
}
+# elif defined __ANDROID__
+ return setlocale_fixed_null (LC_ALL);
# else
return setlocale (LC_ALL, NULL);
# endif
# setlocale.m4
-# serial 10
+# serial 11
dnl Copyright (C) 2011-2025 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl On native Windows systems, setlocale(category,NULL) does not look at
dnl the environment variables LC_ALL, category, and LANG.
mingw* | windows*) NEED_SETLOCALE_IMPROVED=1 ;;
+ dnl On Android, setlocale(category,name) treats all categories as equivalent
+ dnl to LC_CTYPE. And on Android 4.3, setlocale(category,"C") always fails.
+ *-android*) NEED_SETLOCALE_IMPROVED=1 ;;
dnl On Cygwin 1.5.x, setlocale always succeeds but setlocale(LC_CTYPE,NULL)
dnl is then still "C".
cygwin*)
1.5.*) NEED_SETLOCALE_IMPROVED=1 ;;
esac
;;
- dnl On Android 4.3, setlocale(category,"C") always fails.
- *)
- AC_CACHE_CHECK([whether setlocale supports the C locale],
- [gl_cv_func_setlocale_works],
- [AC_RUN_IFELSE(
- [AC_LANG_SOURCE([[
-#include <locale.h>
-int main ()
-{
- return setlocale (LC_ALL, "C") == NULL;
-}]])],
- [gl_cv_func_setlocale_works=yes],
- [gl_cv_func_setlocale_works=no],
- [case "$host_os" in
- # Guess no on Android.
- linux*-android*) gl_cv_func_setlocale_works="guessing no";;
- # Guess yes otherwise.
- *) gl_cv_func_setlocale_works="guessing yes";;
- esac
- ])
- ])
- case "$gl_cv_func_setlocale_works" in
- *yes) ;;
- *) NEED_SETLOCALE_IMPROVED=1 ;;
- esac
- ;;
esac
AC_DEFINE_UNQUOTED([NEED_SETLOCALE_IMPROVED], [$NEED_SETLOCALE_IMPROVED],
[Define to 1 to enable general improvements of setlocale.])
lock
bool
thread-optim
-setlocale-messages
+setlocale-fixes
setlocale-null
free-posix
Depends-on:
locale-h
-setlocale-messages [test $NEED_SETLOCALE_IMPROVED = 1]
+setlocale-fixes [test $NEED_SETLOCALE_IMPROVED = 1]
localename [test $NEED_SETLOCALE_IMPROVED = 1]
localename-environ [test $NEED_SETLOCALE_IMPROVED = 1]
setlocale-null [test $NEED_SETLOCALE_MTSAFE = 1]
--- /dev/null
+Description:
+Make the global locale minimally POSIX compliant.
+
+Files:
+lib/setlocale-fixes.h
+lib/setlocale-fixes.c
+
+Depends-on:
+
+configure.ac:
+AC_REQUIRE([AC_CANONICAL_HOST])
+gl_CONDITIONAL([GL_COND_OBJ_SETLOCALE_FIXES],
+ [case "$host_os" in mingw* | windows* | *-android*) true;; *) false;; esac])
+
+Makefile.am:
+if GL_COND_OBJ_SETLOCALE_FIXES
+lib_SOURCES += setlocale-fixes.c
+endif
+
+Include:
+"setlocale-fixes.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
+++ /dev/null
-Description:
-Extension of the global locale with LC_MESSAGES.
-
-Files:
-lib/setlocale-messages.h
-lib/setlocale-messages.c
-
-Depends-on:
-
-configure.ac:
-AC_REQUIRE([AC_CANONICAL_HOST])
-gl_CONDITIONAL([GL_COND_OBJ_SETLOCALE_MESSAGES],
- [case "$host_os" in mingw* | windows*) true;; *) false;; esac])
-
-Makefile.am:
-if GL_COND_OBJ_SETLOCALE_MESSAGES
-lib_SOURCES += setlocale-messages.c
-endif
-
-Include:
-"setlocale-messages.h"
-
-License:
-LGPLv2+
-
-Maintainer:
-all