+2024-02-15 Bruno Haible <bruno@clisp.org>
+
+ setlocale_null-unlocked: New module.
+ * lib/setlocale_null.h (setlocale_null_r_unlocked,
+ setlocale_null_unlocked): New declarations.
+ * lib/setlocale_null-unlocked.c: New file, based on
+ lib/setlocale_null.c.
+ * lib/setlocale_null.c: Don't include <wchar.h>.
+ (setlocale_null_unlocked, setlocale_null_r_unlocked): Remove functions.
+ * modules/setlocale-null-unlocked: New file.
+ * modules/setlocale-null (Depends-on): Add setlocale-null-unlocked.
+
2024-02-15 Bruno Haible <bruno@clisp.org>
setlocale-null: Refactor.
--- /dev/null
+/* Query the name of the current global locale, without locking.
+ Copyright (C) 2019-2024 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>, 2019. */
+
+#include <config.h>
+
+/* Specification. */
+#include "setlocale_null.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#if defined _WIN32 && !defined __CYGWIN__
+# include <wchar.h>
+#endif
+
+/* Use the system's setlocale() function, not the gnulib override, here. */
+#undef setlocale
+
+const char *
+setlocale_null_unlocked (int category)
+{
+ const char *result = setlocale (category, NULL);
+
+#ifdef __ANDROID__
+ if (result == NULL)
+ switch (category)
+ {
+ case LC_CTYPE:
+ case LC_NUMERIC:
+ case LC_TIME:
+ case LC_COLLATE:
+ case LC_MONETARY:
+ case LC_MESSAGES:
+ case LC_ALL:
+ case LC_PAPER:
+ case LC_NAME:
+ case LC_ADDRESS:
+ case LC_TELEPHONE:
+ case LC_MEASUREMENT:
+ result = "C";
+ break;
+ default:
+ break;
+ }
+#endif
+
+ return result;
+}
+
+int
+setlocale_null_r_unlocked (int category, char *buf, size_t bufsize)
+{
+#if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER
+ /* On native Windows, nowadays, the setlocale() implementation is based
+ on _wsetlocale() and uses malloc() for the result. We are better off
+ using _wsetlocale() directly. */
+ const wchar_t *result = _wsetlocale (category, NULL);
+
+ if (result == NULL)
+ {
+ /* CATEGORY is invalid. */
+ if (bufsize > 0)
+ /* Return an empty string in BUF.
+ This is a convenience for callers that don't want to write explicit
+ code for handling EINVAL. */
+ buf[0] = '\0';
+ return EINVAL;
+ }
+ else
+ {
+ size_t length = wcslen (result);
+ if (length < bufsize)
+ {
+ size_t i;
+
+ /* Convert wchar_t[] -> char[], assuming plain ASCII. */
+ for (i = 0; i <= length; i++)
+ buf[i] = result[i];
+
+ return 0;
+ }
+ else
+ {
+ if (bufsize > 0)
+ {
+ /* Return a truncated result in BUF.
+ This is a convenience for callers that don't want to write
+ explicit code for handling ERANGE. */
+ size_t i;
+
+ /* Convert wchar_t[] -> char[], assuming plain ASCII. */
+ for (i = 0; i < bufsize; i++)
+ buf[i] = result[i];
+ buf[bufsize - 1] = '\0';
+ }
+ return ERANGE;
+ }
+ }
+#else
+ const char *result = setlocale_null_unlocked (category);
+
+ if (result == NULL)
+ {
+ /* CATEGORY is invalid. */
+ if (bufsize > 0)
+ /* Return an empty string in BUF.
+ This is a convenience for callers that don't want to write explicit
+ code for handling EINVAL. */
+ buf[0] = '\0';
+ return EINVAL;
+ }
+ else
+ {
+ size_t length = strlen (result);
+ if (length < bufsize)
+ {
+ memcpy (buf, result, length + 1);
+ return 0;
+ }
+ else
+ {
+ if (bufsize > 0)
+ {
+ /* Return a truncated result in BUF.
+ This is a convenience for callers that don't want to write
+ explicit code for handling ERANGE. */
+ memcpy (buf, result, bufsize - 1);
+ buf[bufsize - 1] = '\0';
+ }
+ return ERANGE;
+ }
+ }
+#endif
+}
#include <locale.h>
#include <stdlib.h>
#include <string.h>
-#if defined _WIN32 && !defined __CYGWIN__
-# include <wchar.h>
-#endif
#if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE)
#endif
-/* Use the system's setlocale() function, not the gnulib override, here. */
-#undef setlocale
-
-static const char *
-setlocale_null_unlocked (int category)
-{
- const char *result = setlocale (category, NULL);
-
-#ifdef __ANDROID__
- if (result == NULL)
- switch (category)
- {
- case LC_CTYPE:
- case LC_NUMERIC:
- case LC_TIME:
- case LC_COLLATE:
- case LC_MONETARY:
- case LC_MESSAGES:
- case LC_ALL:
- case LC_PAPER:
- case LC_NAME:
- case LC_ADDRESS:
- case LC_TELEPHONE:
- case LC_MEASUREMENT:
- result = "C";
- break;
- default:
- break;
- }
-#endif
-
- return result;
-}
-
-static int
-setlocale_null_r_unlocked (int category, char *buf, size_t bufsize)
-{
-#if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER
- /* On native Windows, nowadays, the setlocale() implementation is based
- on _wsetlocale() and uses malloc() for the result. We are better off
- using _wsetlocale() directly. */
- const wchar_t *result = _wsetlocale (category, NULL);
-
- if (result == NULL)
- {
- /* CATEGORY is invalid. */
- if (bufsize > 0)
- /* Return an empty string in BUF.
- This is a convenience for callers that don't want to write explicit
- code for handling EINVAL. */
- buf[0] = '\0';
- return EINVAL;
- }
- else
- {
- size_t length = wcslen (result);
- if (length < bufsize)
- {
- size_t i;
-
- /* Convert wchar_t[] -> char[], assuming plain ASCII. */
- for (i = 0; i <= length; i++)
- buf[i] = result[i];
-
- return 0;
- }
- else
- {
- if (bufsize > 0)
- {
- /* Return a truncated result in BUF.
- This is a convenience for callers that don't want to write
- explicit code for handling ERANGE. */
- size_t i;
-
- /* Convert wchar_t[] -> char[], assuming plain ASCII. */
- for (i = 0; i < bufsize; i++)
- buf[i] = result[i];
- buf[bufsize - 1] = '\0';
- }
- return ERANGE;
- }
- }
-#else
- const char *result = setlocale_null_unlocked (category);
-
- if (result == NULL)
- {
- /* CATEGORY is invalid. */
- if (bufsize > 0)
- /* Return an empty string in BUF.
- This is a convenience for callers that don't want to write explicit
- code for handling EINVAL. */
- buf[0] = '\0';
- return EINVAL;
- }
- else
- {
- size_t length = strlen (result);
- if (length < bufsize)
- {
- memcpy (buf, result, length + 1);
- return 0;
- }
- else
- {
- if (bufsize > 0)
- {
- /* Return a truncated result in BUF.
- This is a convenience for callers that don't want to write
- explicit code for handling ERANGE. */
- memcpy (buf, result, bufsize - 1);
- buf[bufsize - 1] = '\0';
- }
- return ERANGE;
- }
- }
-#endif
-}
-
#if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */
/* Use a lock, so that no two threads can invoke setlocale_null_r_unlocked
55+5*58. */
#define SETLOCALE_NULL_ALL_MAX (148+12*256+1)
+/* setlocale_null_r_unlocked (CATEGORY, BUF, BUFSIZE) is like
+ setlocale (CATEGORY, NULL), except that
+ - it returns the resulting locale category name or locale name in the
+ user-supplied buffer BUF, which must be BUFSIZE bytes long.
+ The recommended minimum buffer size is
+ - SETLOCALE_NULL_MAX for CATEGORY != LC_ALL, and
+ - SETLOCALE_NULL_ALL_MAX for CATEGORY == LC_ALL.
+ The return value is an error code: 0 if the call is successful, EINVAL if
+ CATEGORY is invalid, or ERANGE if BUFSIZE is smaller than the length needed
+ size (including the trailing NUL byte). In the latter case, a truncated
+ result is returned in BUF, but still NUL-terminated if BUFSIZE > 0.
+ This call is guaranteed to be multithread-safe only if
+ - CATEGORY != LC_ALL and SETLOCALE_NULL_ONE_MTSAFE is true, or
+ - CATEGORY == LC_ALL and SETLOCALE_NULL_ALL_MTSAFE is true,
+ and the other threads must not make other setlocale invocations (since
+ changing the global locale has side effects on all threads). */
+extern int setlocale_null_r_unlocked (int category, char *buf, size_t bufsize)
+ _GL_ARG_NONNULL ((2));
+
+/* setlocale_null_unlocked (CATEGORY) is like setlocale (CATEGORY, NULL).
+ The return value is NULL if CATEGORY is invalid.
+ This call is guaranteed to be multithread-safe only if
+ - CATEGORY != LC_ALL and SETLOCALE_NULL_ONE_MTSAFE is true, or
+ - CATEGORY == LC_ALL and SETLOCALE_NULL_ALL_MTSAFE is true,
+ and the other threads must not make other setlocale invocations (since
+ changing the global locale has side effects on all threads). */
+extern const char *setlocale_null_unlocked (int category);
+
/* setlocale_null_r (CATEGORY, BUF, BUFSIZE) is like setlocale (CATEGORY, NULL),
except that
- it is guaranteed to be multithread-safe,
Depends-on:
locale
snippet/arg-nonnull
+setlocale-null-unlocked
configure.ac:
gl_FUNC_SETLOCALE_NULL
--- /dev/null
+Description:
+setlocale_null_unlocked() function: query the name of the current global locale,
+without locking.
+
+Files:
+lib/setlocale_null.h
+lib/setlocale_null-unlocked.c
+
+Depends-on:
+locale
+snippet/arg-nonnull
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += setlocale_null-unlocked.c
+
+Include:
+#include "setlocale_null.h"
+
+Link:
+
+License:
+LGPLv2+
+
+Maintainer:
+Bruno Haible