root/maint/gnulib/lib/setlocale_null.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. setlocale_null_androidfix
  2. setlocale_null_unlocked
  3. __declspec
  4. setlocale_null_with_lock
  5. setlocale_null_with_lock
  6. setlocale_null_r
  7. setlocale_null

   1 /* Query the name of the current global locale.
   2    Copyright (C) 2019-2021 Free Software Foundation, Inc.
   3 
   4    This file is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU Lesser General Public License as
   6    published by the Free Software Foundation; either version 2.1 of the
   7    License, or (at your option) any later version.
   8 
   9    This file is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU Lesser General Public License for more details.
  13 
  14    You should have received a copy of the GNU Lesser General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Bruno Haible <bruno@clisp.org>, 2019.  */
  18 
  19 #include <config.h>
  20 
  21 /* Specification.  */
  22 #include "setlocale_null.h"
  23 
  24 #include <errno.h>
  25 #include <locale.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 #if defined _WIN32 && !defined __CYGWIN__
  29 # include <wchar.h>
  30 #endif
  31 
  32 #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE)
  33 # if defined _WIN32 && !defined __CYGWIN__
  34 
  35 #  define WIN32_LEAN_AND_MEAN  /* avoid including junk */
  36 #  include <windows.h>
  37 
  38 # elif HAVE_PTHREAD_API
  39 
  40 #  include <pthread.h>
  41 #  if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
  42 #   include <threads.h>
  43 #   pragma weak thrd_exit
  44 #   define c11_threads_in_use() (thrd_exit != NULL)
  45 #  else
  46 #   define c11_threads_in_use() 0
  47 #  endif
  48 
  49 # elif HAVE_THREADS_H
  50 
  51 #  include <threads.h>
  52 
  53 # endif
  54 #endif
  55 
  56 /* Use the system's setlocale() function, not the gnulib override, here.  */
  57 #undef setlocale
  58 
  59 static const char *
  60 setlocale_null_androidfix (int category)
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62   const char *result = setlocale (category, NULL);
  63 
  64 #ifdef __ANDROID__
  65   if (result == NULL)
  66     switch (category)
  67       {
  68       case LC_CTYPE:
  69       case LC_NUMERIC:
  70       case LC_TIME:
  71       case LC_COLLATE:
  72       case LC_MONETARY:
  73       case LC_MESSAGES:
  74       case LC_ALL:
  75       case LC_PAPER:
  76       case LC_NAME:
  77       case LC_ADDRESS:
  78       case LC_TELEPHONE:
  79       case LC_MEASUREMENT:
  80         result = "C";
  81         break;
  82       default:
  83         break;
  84       }
  85 #endif
  86 
  87   return result;
  88 }
  89 
  90 static int
  91 setlocale_null_unlocked (int category, char *buf, size_t bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93 #if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER
  94   /* On native Windows, nowadays, the setlocale() implementation is based
  95      on _wsetlocale() and uses malloc() for the result.  We are better off
  96      using _wsetlocale() directly.  */
  97   const wchar_t *result = _wsetlocale (category, NULL);
  98 
  99   if (result == NULL)
 100     {
 101       /* CATEGORY is invalid.  */
 102       if (bufsize > 0)
 103         /* Return an empty string in BUF.
 104            This is a convenience for callers that don't want to write explicit
 105            code for handling EINVAL.  */
 106         buf[0] = '\0';
 107       return EINVAL;
 108     }
 109   else
 110     {
 111       size_t length = wcslen (result);
 112       if (length < bufsize)
 113         {
 114           size_t i;
 115 
 116           /* Convert wchar_t[] -> char[], assuming plain ASCII.  */
 117           for (i = 0; i <= length; i++)
 118             buf[i] = result[i];
 119 
 120           return 0;
 121         }
 122       else
 123         {
 124           if (bufsize > 0)
 125             {
 126               /* Return a truncated result in BUF.
 127                  This is a convenience for callers that don't want to write
 128                  explicit code for handling ERANGE.  */
 129               size_t i;
 130 
 131               /* Convert wchar_t[] -> char[], assuming plain ASCII.  */
 132               for (i = 0; i < bufsize; i++)
 133                 buf[i] = result[i];
 134               buf[bufsize - 1] = '\0';
 135             }
 136           return ERANGE;
 137         }
 138     }
 139 #else
 140   const char *result = setlocale_null_androidfix (category);
 141 
 142   if (result == NULL)
 143     {
 144       /* CATEGORY is invalid.  */
 145       if (bufsize > 0)
 146         /* Return an empty string in BUF.
 147            This is a convenience for callers that don't want to write explicit
 148            code for handling EINVAL.  */
 149         buf[0] = '\0';
 150       return EINVAL;
 151     }
 152   else
 153     {
 154       size_t length = strlen (result);
 155       if (length < bufsize)
 156         {
 157           memcpy (buf, result, length + 1);
 158           return 0;
 159         }
 160       else
 161         {
 162           if (bufsize > 0)
 163             {
 164               /* Return a truncated result in BUF.
 165                  This is a convenience for callers that don't want to write
 166                  explicit code for handling ERANGE.  */
 167               memcpy (buf, result, bufsize - 1);
 168               buf[bufsize - 1] = '\0';
 169             }
 170           return ERANGE;
 171         }
 172     }
 173 #endif
 174 }
 175 
 176 #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin */
 177 
 178 /* Use a lock, so that no two threads can invoke setlocale_null_unlocked
 179    at the same time.  */
 180 
 181 /* Prohibit renaming this symbol.  */
 182 # undef gl_get_setlocale_null_lock
 183 
 184 # if defined _WIN32 && !defined __CYGWIN__
 185 
 186 extern __declspec(dllimport) CRITICAL_SECTION *gl_get_setlocale_null_lock (void);
     /* [previous][next][first][last][top][bottom][index][help] */
 187 
 188 static int
 189 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
 190 {
 191   CRITICAL_SECTION *lock = gl_get_setlocale_null_lock ();
 192   int ret;
 193 
 194   EnterCriticalSection (lock);
 195   ret = setlocale_null_unlocked (category, buf, bufsize);
 196   LeaveCriticalSection (lock);
 197 
 198   return ret;
 199 }
 200 
 201 # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin */
 202 
 203 extern
 204 #  if defined _WIN32 || defined __CYGWIN__
 205   __declspec(dllimport)
 206 #  endif
 207   pthread_mutex_t *gl_get_setlocale_null_lock (void);
 208 
 209 #  if HAVE_WEAK_SYMBOLS /* musl libc, FreeBSD, NetBSD, OpenBSD, Haiku */
 210 
 211     /* Avoid the need to link with '-lpthread'.  */
 212 #   pragma weak pthread_mutex_lock
 213 #   pragma weak pthread_mutex_unlock
 214 
 215     /* Determine whether libpthread is in use.  */
 216 #   pragma weak pthread_mutexattr_gettype
 217     /* See the comments in lock.h.  */
 218 #   define pthread_in_use() \
 219       (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
 220 
 221 #  else
 222 #   define pthread_in_use() 1
 223 #  endif
 224 
 225 static int
 226 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228   if (pthread_in_use())
 229     {
 230       pthread_mutex_t *lock = gl_get_setlocale_null_lock ();
 231       int ret;
 232 
 233       if (pthread_mutex_lock (lock))
 234         abort ();
 235       ret = setlocale_null_unlocked (category, buf, bufsize);
 236       if (pthread_mutex_unlock (lock))
 237         abort ();
 238 
 239       return ret;
 240     }
 241   else
 242     return setlocale_null_unlocked (category, buf, bufsize);
 243 }
 244 
 245 # elif HAVE_THREADS_H
 246 
 247 extern mtx_t *gl_get_setlocale_null_lock (void);
 248 
 249 static int
 250 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 {
 252   mtx_t *lock = gl_get_setlocale_null_lock ();
 253   int ret;
 254 
 255   if (mtx_lock (lock) != thrd_success)
 256     abort ();
 257   ret = setlocale_null_unlocked (category, buf, bufsize);
 258   if (mtx_unlock (lock) != thrd_success)
 259     abort ();
 260 
 261   return ret;
 262 }
 263 
 264 # endif
 265 
 266 #endif
 267 
 268 int
 269 setlocale_null_r (int category, char *buf, size_t bufsize)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271 #if SETLOCALE_NULL_ALL_MTSAFE
 272 # if SETLOCALE_NULL_ONE_MTSAFE
 273 
 274   return setlocale_null_unlocked (category, buf, bufsize);
 275 
 276 # else
 277 
 278   if (category == LC_ALL)
 279     return setlocale_null_unlocked (category, buf, bufsize);
 280   else
 281     return setlocale_null_with_lock (category, buf, bufsize);
 282 
 283 # endif
 284 #else
 285 # if SETLOCALE_NULL_ONE_MTSAFE
 286 
 287   if (category == LC_ALL)
 288     return setlocale_null_with_lock (category, buf, bufsize);
 289   else
 290     return setlocale_null_unlocked (category, buf, bufsize);
 291 
 292 # else
 293 
 294   return setlocale_null_with_lock (category, buf, bufsize);
 295 
 296 # endif
 297 #endif
 298 }
 299 
 300 const char *
 301 setlocale_null (int category)
     /* [previous][next][first][last][top][bottom][index][help] */
 302 {
 303 #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
 304   return setlocale_null_androidfix (category);
 305 #else
 306 
 307   /* This call must be multithread-safe.  To achieve this without using
 308      thread-local storage:
 309        1. We use a specific static buffer for each possible CATEGORY
 310           argument.  So that different threads can call setlocale_mtsafe
 311           with different CATEGORY arguments, without interfering.
 312        2. We use a simple strcpy or memcpy to fill this static buffer.
 313           Filling it through, for example, strcpy + strcat would not be
 314           guaranteed to leave the buffer's contents intact if another thread
 315           is currently accessing it.  If necessary, the contents is first
 316           assembled in a stack-allocated buffer.  */
 317   if (category == LC_ALL)
 318     {
 319 # if SETLOCALE_NULL_ALL_MTSAFE
 320       return setlocale_null_androidfix (LC_ALL);
 321 # else
 322       char buf[SETLOCALE_NULL_ALL_MAX];
 323       static char resultbuf[SETLOCALE_NULL_ALL_MAX];
 324 
 325       if (setlocale_null_r (LC_ALL, buf, sizeof (buf)))
 326         return "C";
 327       strcpy (resultbuf, buf);
 328       return resultbuf;
 329 # endif
 330     }
 331   else
 332     {
 333 # if SETLOCALE_NULL_ONE_MTSAFE
 334       return setlocale_null_androidfix (category);
 335 # else
 336       enum
 337         {
 338           LC_CTYPE_INDEX,
 339           LC_NUMERIC_INDEX,
 340           LC_TIME_INDEX,
 341           LC_COLLATE_INDEX,
 342           LC_MONETARY_INDEX,
 343           LC_MESSAGES_INDEX,
 344 #  ifdef LC_PAPER
 345           LC_PAPER_INDEX,
 346 #  endif
 347 #  ifdef LC_NAME
 348           LC_NAME_INDEX,
 349 #  endif
 350 #  ifdef LC_ADDRESS
 351           LC_ADDRESS_INDEX,
 352 #  endif
 353 #  ifdef LC_TELEPHONE
 354           LC_TELEPHONE_INDEX,
 355 #  endif
 356 #  ifdef LC_MEASUREMENT
 357           LC_MEASUREMENT_INDEX,
 358 #  endif
 359 #  ifdef LC_IDENTIFICATION
 360           LC_IDENTIFICATION_INDEX,
 361 #  endif
 362           LC_INDICES_COUNT
 363         }
 364         i;
 365       char buf[SETLOCALE_NULL_MAX];
 366       static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
 367       int err;
 368 
 369       err = setlocale_null_r (category, buf, sizeof (buf));
 370       if (err == EINVAL)
 371         return NULL;
 372       if (err)
 373         return "C";
 374 
 375       switch (category)
 376         {
 377         case LC_CTYPE:          i = LC_CTYPE_INDEX;          break;
 378         case LC_NUMERIC:        i = LC_NUMERIC_INDEX;        break;
 379         case LC_TIME:           i = LC_TIME_INDEX;           break;
 380         case LC_COLLATE:        i = LC_COLLATE_INDEX;        break;
 381         case LC_MONETARY:       i = LC_MONETARY_INDEX;       break;
 382         case LC_MESSAGES:       i = LC_MESSAGES_INDEX;       break;
 383 #  ifdef LC_PAPER
 384         case LC_PAPER:          i = LC_PAPER_INDEX;          break;
 385 #  endif
 386 #  ifdef LC_NAME
 387         case LC_NAME:           i = LC_NAME_INDEX;           break;
 388 #  endif
 389 #  ifdef LC_ADDRESS
 390         case LC_ADDRESS:        i = LC_ADDRESS_INDEX;        break;
 391 #  endif
 392 #  ifdef LC_TELEPHONE
 393         case LC_TELEPHONE:      i = LC_TELEPHONE_INDEX;      break;
 394 #  endif
 395 #  ifdef LC_MEASUREMENT
 396         case LC_MEASUREMENT:    i = LC_MEASUREMENT_INDEX;    break;
 397 #  endif
 398 #  ifdef LC_IDENTIFICATION
 399         case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
 400 #  endif
 401         default:
 402           /* If you get here, a #ifdef LC_xxx is missing.  */
 403           abort ();
 404         }
 405 
 406       strcpy (resultbuf[i], buf);
 407       return resultbuf[i];
 408 # endif
 409     }
 410 #endif
 411 }

/* [previous][next][first][last][top][bottom][index][help] */