setlocale_null.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. /* Query the name of the current global locale.
  2. Copyright (C) 2019-2021 Free Software Foundation, Inc.
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program 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. }