summaryrefslogtreecommitdiff
path: root/lib/setlocale_null.c
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2019-12-15 21:39:55 +0100
committerBruno Haible <bruno@clisp.org>2019-12-15 21:39:55 +0100
commit567591f3b194927a4d7eefa71007be2709796c4e (patch)
treeef33b9280bae9b45180f150f0386876bfe350596 /lib/setlocale_null.c
parentfdff8bd09a7f053381f8bdb107ab5280b7c95959 (diff)
downloadgnulib-567591f3b194927a4d7eefa71007be2709796c4e.tar.gz
setlocale-null: New module.
* lib/locale.in.h (SETLOCALE_NULL_MAX, SETLOCALE_NULL_ALL_MAX, setlocale_null): New declarations. * lib/setlocale_null.c: New file. * lib/setlocale-lock.c: New file. * m4/threadlib.m4 (gl_PTHREADLIB_BODY): Define C macro HAVE_PTHREAD_API. * m4/setlocale_null.m4: New file. * m4/locale_h.m4 (gl_LOCALE_H_DEFAULTS): Initialize GNULIB_SETLOCALE_NULL. * modules/locale (Makefile.am): Substitute GNULIB_SETLOCALE_NULL. * modules/setlocale-null: New file. * doc/posix-functions/setlocale.texi: Mention the new module.
Diffstat (limited to 'lib/setlocale_null.c')
-rw-r--r--lib/setlocale_null.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/lib/setlocale_null.c b/lib/setlocale_null.c
new file mode 100644
index 0000000000..b0506b9a12
--- /dev/null
+++ b/lib/setlocale_null.c
@@ -0,0 +1,204 @@
+/* Query the name of the current global locale.
+ Copyright (C) 2019 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU 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 <locale.h>
+
+#include <errno.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)
+# if defined _WIN32 && !defined __CYGWIN__
+# define WIN32_LEAN_AND_MEAN /* avoid including junk */
+# include <windows.h>
+# elif HAVE_PTHREAD_API
+# include <pthread.h>
+# elif HAVE_THREADS_H
+# include <threads.h>
+# endif
+#endif
+
+/* Use the system's setlocale() function, not the gnulib override, here. */
+#undef setlocale
+
+static int
+setlocale_null_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);
+ 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 (category, NULL);
+ 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)
+
+/* Use a lock, so that no two threads can invoke setlocale_null_unlocked
+ at the same time. */
+
+/* Prohibit renaming this symbol. */
+# undef gl_get_setlocale_null_lock
+
+# if defined _WIN32 && !defined __CYGWIN__
+
+extern __declspec(dllimport) CRITICAL_SECTION *gl_get_setlocale_null_lock (void);
+
+static int
+setlocale_null_with_lock (int category, char *buf, size_t bufsize)
+{
+ CRITICAL_SECTION *lock = gl_get_setlocale_null_lock ();
+ int ret;
+
+ EnterCriticalSection (lock);
+ ret = setlocale_null_unlocked (category, buf, bufsize);
+ LeaveCriticalSection (lock);
+
+ return ret;
+}
+
+# elif HAVE_PTHREAD_API
+
+extern
+# if defined _WIN32 || defined __CYGWIN__
+ __declspec(dllimport)
+# endif
+ pthread_mutex_t *gl_get_setlocale_null_lock (void);
+
+static int
+setlocale_null_with_lock (int category, char *buf, size_t bufsize)
+{
+ pthread_mutex_t *lock = gl_get_setlocale_null_lock ();
+ int ret;
+
+ if (pthread_mutex_lock (lock))
+ abort ();
+ ret = setlocale_null_unlocked (category, buf, bufsize);
+ if (pthread_mutex_unlock (lock))
+ abort ();
+
+ return ret;
+}
+
+# elif HAVE_THREADS_H
+
+extern mtx_t *gl_get_setlocale_null_lock (void);
+
+static int
+setlocale_null_with_lock (int category, char *buf, size_t bufsize)
+{
+ mtx_t *lock = gl_get_setlocale_null_lock ();
+ int ret;
+
+ if (mtx_lock (lock) != thrd_success)
+ abort ();
+ ret = setlocale_null_unlocked (category, buf, bufsize);
+ if (mtx_unlock (lock) != thrd_success)
+ abort ();
+
+ return ret;
+}
+
+# endif
+
+#endif
+
+int
+setlocale_null (int category, char *buf, size_t bufsize)
+{
+#if SETLOCALE_NULL_ALL_MTSAFE
+# if SETLOCALE_NULL_ONE_MTSAFE
+
+ return setlocale_null_unlocked (category, buf, bufsize);
+
+# else
+
+ if (category == LC_ALL)
+ return setlocale_null_unlocked (category, buf, bufsize);
+ else
+ return setlocale_null_with_lock (category, buf, bufsize);
+
+# endif
+#else
+# if SETLOCALE_NULL_ONE_MTSAFE
+
+ if (category == LC_ALL)
+ return setlocale_null_with_lock (category, buf, bufsize);
+ else
+ return setlocale_null_unlocked (category, buf, bufsize);
+
+# else
+
+ return setlocale_null_with_lock (category, buf, bufsize);
+
+# endif
+#endif
+}