setlocale_null.c 11 KB

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