summaryrefslogtreecommitdiff
path: root/tests/setlocale.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff.email>2025-10-18 19:06:52 +0200
committerJörg Frings-Fürst <debian@jff.email>2025-10-18 19:06:52 +0200
commit27dae84ed92f1ef0300263091972338d12e78348 (patch)
tree7c52931f474fafb8a4bd4fd15ca3461c77cdecc2 /tests/setlocale.c
parent4682deeb62247d34de87f8e777f99e2d337fd377 (diff)
New upstream version 1.4.1upstream/1.4.1upstream
Diffstat (limited to 'tests/setlocale.c')
-rw-r--r--tests/setlocale.c254
1 files changed, 199 insertions, 55 deletions
diff --git a/tests/setlocale.c b/tests/setlocale.c
index eb263617..814a9bd8 100644
--- a/tests/setlocale.c
+++ b/tests/setlocale.c
@@ -1,9 +1,9 @@
/* Set the current locale. -*- coding: utf-8 -*-
- Copyright (C) 2009, 2011-2024 Free Software Foundation, Inc.
+ Copyright (C) 2009, 2011-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 3 of the
+ 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,
@@ -33,6 +33,7 @@
#include <stdlib.h>
#include <string.h>
+#include "setlocale-fixes.h"
#include "localename.h"
#if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE
@@ -47,6 +48,11 @@
extern void gl_locale_name_canonicalize (char *name);
#endif
+#if defined _WIN32 && !defined __CYGWIN__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
#if 1
# undef setlocale
@@ -458,7 +464,7 @@ static const struct table_entry language_table[] =
/* Table from ISO 3166 country code to English name.
Keep in sync with the gl_locale_name_from_win32_LANGID function in
- localename.c! */
+ localename-unsafe.c! */
static const struct table_entry country_table[] =
{
{ "AE", "U.A.E." },
@@ -672,6 +678,7 @@ search (const struct table_entry *table, size_t table_size, const char *string,
static char *
setlocale_unixlike (int category, const char *locale)
{
+ int is_utf8 = (GetACP () == 65001);
char *result;
char llCC_buf[64];
char ll_buf[64];
@@ -679,9 +686,18 @@ setlocale_unixlike (int category, const char *locale)
/* The native Windows implementation of setlocale understands the special
locale name "C", but not "POSIX". Therefore map "POSIX" to "C". */
- if (locale != NULL && strcmp (locale, "POSIX") == 0)
+ if (locale != NULL && streq (locale, "POSIX"))
locale = "C";
+ /* The native Windows implementation of setlocale, in the UTF-8 environment,
+ does not understand the locale names "C.UTF-8" or "C.utf8" or "C.65001",
+ but it understands "English_United States.65001", which is functionally
+ equivalent. */
+ if (locale != NULL
+ && ((is_utf8 && streq (locale, "C"))
+ || streq (locale, "C.UTF-8")))
+ locale = "English_United States.65001";
+
/* First, try setlocale with the original argument unchanged. */
result = setlocale_mtsafe (category, locale);
if (result != NULL)
@@ -712,9 +728,17 @@ setlocale_unixlike (int category, const char *locale)
/* llCC_buf now contains
language[_territory][@modifier]
*/
- if (strcmp (llCC_buf, locale) != 0)
+ if (!streq (llCC_buf, locale))
{
- result = setlocale (category, llCC_buf);
+ if (is_utf8)
+ {
+ char buf[64+6];
+ strcpy (buf, llCC_buf);
+ strcat (buf, ".65001");
+ result = setlocale (category, buf);
+ }
+ else
+ result = setlocale (category, llCC_buf);
if (result != NULL)
return result;
}
@@ -731,7 +755,15 @@ setlocale_unixlike (int category, const char *locale)
for (i = range.lo; i < range.hi; i++)
{
/* Try the replacement in language_table[i]. */
- result = setlocale (category, language_table[i].english);
+ if (is_utf8)
+ {
+ char buf[64+6];
+ strcpy (buf, language_table[i].english);
+ strcat (buf, ".65001");
+ result = setlocale (category, buf);
+ }
+ else
+ result = setlocale (category, language_table[i].english);
if (result != NULL)
return result;
}
@@ -785,13 +817,15 @@ setlocale_unixlike (int category, const char *locale)
size_t part1_len = strlen (part1);
const char *part2 = country_table[j].english;
size_t part2_len = strlen (part2) + 1;
- char buf[64+64];
+ char buf[64+64+6];
- if (!(part1_len + 1 + part2_len <= sizeof (buf)))
+ if (!(part1_len + 1 + part2_len + 6 <= sizeof (buf)))
abort ();
memcpy (buf, part1, part1_len);
buf[part1_len] = '_';
memcpy (buf + part1_len + 1, part2, part2_len);
+ if (is_utf8)
+ strcat (buf, ".65001");
/* Try the concatenated replacements. */
result = setlocale (category, buf);
@@ -809,8 +843,16 @@ setlocale_unixlike (int category, const char *locale)
for (i = language_range.lo; i < language_range.hi; i++)
{
/* Try only the language replacement. */
- result =
- setlocale (category, language_table[i].english);
+ if (is_utf8)
+ {
+ char buf[64+6];
+ strcpy (buf, language_table[i].english);
+ strcat (buf, ".65001");
+ result = setlocale (category, buf);
+ }
+ else
+ result =
+ setlocale (category, language_table[i].english);
if (result != NULL)
return result;
}
@@ -831,7 +873,7 @@ setlocale_unixlike (int category, const char *locale)
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)
{
@@ -848,7 +890,7 @@ setlocale_unixlike (int category, const char *locale)
case LC_TELEPHONE:
case LC_MEASUREMENT:
if (locale == NULL
- || strcmp (locale, "C") == 0 || strcmp (locale, "POSIX") == 0)
+ || streq (locale, "C") || streq (locale, "POSIX"))
result = (char *) "C";
break;
default:
@@ -864,22 +906,12 @@ setlocale_unixlike (int category, const char *locale)
# if LC_MESSAGES == 1729
-/* The system does not store an LC_MESSAGES locale category. Do it here. */
-static char lc_messages_name[64] = "C";
-
/* Like setlocale, but support also LC_MESSAGES. */
static char *
setlocale_single (int category, const char *locale)
{
if (category == LC_MESSAGES)
- {
- if (locale != NULL)
- {
- lc_messages_name[sizeof (lc_messages_name) - 1] = '\0';
- strncpy (lc_messages_name, locale, sizeof (lc_messages_name) - 1);
- }
- return lc_messages_name;
- }
+ return setlocale_messages (locale);
else
return setlocale_unixlike (category, locale);
}
@@ -955,6 +987,7 @@ static char const locales_with_principal_territory[][6 + 1] =
"fy_NL", /* Western Frisian Netherlands */
"ga_IE", /* Irish Ireland */
"gd_GB", /* Scottish Gaelic Britain */
+ "gl_ES", /* Galician Spain */
"gon_IN", /* Gondi India */
"gsw_CH", /* Swiss German Switzerland */
"gu_IN", /* Gujarati India */
@@ -1073,6 +1106,7 @@ static char const locales_with_principal_territory[][6 + 1] =
"suk_TZ", /* Sukuma Tanzania */
"sus_GN", /* Susu Guinea */
"sv_SE", /* Swedish Sweden */
+ "ta_IN", /* Tamil India */
"te_IN", /* Telugu India */
"tem_SL", /* Timne Sierra Leone */
"tet_ID", /* Tetum Indonesia */
@@ -1467,7 +1501,7 @@ setlocale_improved (int category, const char *locale)
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
- && strcmp (setlocale (LC_CTYPE, NULL), "C") == 0)
+ && streq (setlocale (LC_CTYPE, NULL), "C"))
goto fail;
# endif
@@ -1482,7 +1516,7 @@ setlocale_improved (int category, const char *locale)
/* If name is the same as base_name, it has already been set
through the setlocale call before the loop. */
- if (strcmp (name, base_name) != 0
+ if (!streq (name, base_name)
# if LC_MESSAGES == 1729
|| cat == LC_MESSAGES
# endif
@@ -1608,7 +1642,7 @@ setlocale_improved (int category, const char *locale)
/* All steps were successful. */
free (saved_locale);
- return setlocale (LC_ALL, NULL);
+ goto ret_all;
fail:
if (saved_locale[0] != '\0') /* don't risk an endless recursion */
@@ -1628,44 +1662,154 @@ setlocale_improved (int category, const char *locale)
}
else
{
-# if defined _WIN32 && ! defined __CYGWIN__
- if (category == LC_ALL && locale != NULL && strchr (locale, '.') != NULL)
+# if LC_MESSAGES == 1729
+ if (locale != NULL)
{
- char *saved_locale;
-
- /* Back up the old locale. */
- saved_locale = setlocale (LC_ALL, NULL);
- if (saved_locale == NULL)
- return NULL;
- saved_locale = strdup (saved_locale);
- if (saved_locale == NULL)
- return NULL;
+ char truncated_locale[SETLOCALE_NULL_ALL_MAX];
+ const char *native_locale;
+ const char *messages_locale;
- if (setlocale_unixlike (LC_ALL, locale) == NULL)
+ if (strncmp (locale, "LC_COLLATE=", 11) == 0)
{
- free (saved_locale);
- return NULL;
+ /* The locale argument indicates a mixed locale. It must be of
+ the form
+ "LC_COLLATE=...;LC_CTYPE=...;LC_MONETARY=...;LC_NUMERIC=...;LC_TIME=...;LC_MESSAGES=..."
+ since that is what this function returns (see ret_all below).
+ Decompose it. */
+ const char *last_semicolon = strrchr (locale, ';');
+ if (!(last_semicolon != NULL
+ && strncmp (last_semicolon + 1, "LC_MESSAGES=", 12) == 0))
+ return NULL;
+ if (category == LC_MESSAGES)
+ return setlocale_single (category, last_semicolon + 13);
+ size_t truncated_locale_len = last_semicolon - locale;
+ if (truncated_locale_len >= sizeof (truncated_locale))
+ return NULL;
+ memcpy (truncated_locale, locale, truncated_locale_len);
+ truncated_locale[truncated_locale_len] = '\0';
+ native_locale = truncated_locale;
+ messages_locale = last_semicolon + 13;
}
-
- /* 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 (strcmp (setlocale (LC_CTYPE, NULL), "C") == 0)
+ else
{
- if (saved_locale[0] != '\0') /* don't risk an endless recursion */
- setlocale (LC_ALL, saved_locale);
- free (saved_locale);
- return NULL;
+ native_locale = locale;
+ messages_locale = locale;
}
- /* It was really successful. */
- free (saved_locale);
- return setlocale (LC_ALL, NULL);
+ if (category == LC_ALL)
+ {
+ /* In the underlying implementation, LC_ALL does not contain
+ LC_MESSAGES. Therefore we need to handle LC_MESSAGES
+ separately. */
+ 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)
+ return NULL;
+ saved_locale = strdup (saved_locale);
+ if (saved_locale == NULL)
+ return NULL;
+
+ if (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. */
+ if (streq (setlocale (LC_CTYPE, NULL), "C"))
+ {
+ /* Don't risk an endless recursion. */
+ if (saved_locale[0] != '\0')
+ setlocale (LC_ALL, saved_locale);
+ free (saved_locale);
+ return NULL;
+ }
+
+ /* It was really successful. */
+ free (saved_locale);
+ result = setlocale (LC_ALL, NULL);
+ }
+ else
+# endif
+ result = setlocale_single (LC_ALL, native_locale);
+ if (result == NULL)
+ return NULL;
+
+ setlocale_single (LC_MESSAGES, messages_locale);
+
+ goto ret_all;
+ }
+ else
+ {
+ if (category == LC_MESSAGES)
+ return setlocale_single (category, messages_locale);
+ else
+ return setlocale_single (category, native_locale);
+ }
}
- else
+ else /* locale == NULL */
+ {
+ if (category == LC_ALL)
+ goto ret_all;
+ else
+ return setlocale_single (category, NULL);
+ }
+# else
+ return setlocale_single (category, locale);
# endif
- return setlocale_single (category, locale);
}
+
+ ret_all:
+ /* Return the name of all categories of the current locale. */
+# if LC_MESSAGES == 1729 /* native Windows */
+ /* The locale name for mixed locales looks like this:
+ "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);
+ if (streq (name1, name2))
+ /* Not a mixed locale. */
+ return name1;
+ else
+ {
+ static char resultbuf[SETLOCALE_NULL_ALL_MAX];
+ /* Prepare the result in a stack-allocated buffer, in order to reduce
+ race conditions in a multithreaded situation. */
+ char stackbuf[SETLOCALE_NULL_ALL_MAX];
+ if (strncmp (name1, "LC_COLLATE=", 11) == 0)
+ {
+ if (strlen (name1) + strlen (name2) + 13 >= sizeof (stackbuf))
+ return NULL;
+ sprintf (stackbuf, "%s;LC_MESSAGES=%s", name1, name2);
+ }
+ else
+ {
+ if (5 * strlen (name1) + strlen (name2) + 68 >= sizeof (stackbuf))
+ return NULL;
+ sprintf (stackbuf,
+ "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s;LC_MESSAGES=%s",
+ name1, name1, name1, name1, name1, name2);
+ }
+ strcpy (resultbuf, stackbuf);
+ return resultbuf;
+ }
+ }
+# elif defined __ANDROID__
+ return setlocale_fixed_null (LC_ALL);
+# else
+ return setlocale (LC_ALL, NULL);
+# endif
}
# endif /* NEED_SETLOCALE_IMPROVED */