1 /*
2 * Copyright 2022, Trung Nguyen, trungnt282910@gmail.com
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6
7 #include <new>
8 #include <errno.h>
9 #include <locale.h>
10 #include <strings.h>
11
12 #include <ErrnoMaintainer.h>
13
14 #include "LocaleBackend.h"
15 #include "LocaleInternal.h"
16
17
18 using BPrivate::Libroot::gGlobalLocaleBackend;
19 using BPrivate::Libroot::gGlobalLocaleDataBridge;
20 using BPrivate::Libroot::GetCurrentLocaleInfo;
21 using BPrivate::Libroot::GetLocalesFromEnvironment;
22 using BPrivate::Libroot::LocaleBackend;
23 using BPrivate::Libroot::LocaleBackendData;
24 using BPrivate::Libroot::LocaleDataBridge;
25
26
27 extern "C" locale_t
duplocale(locale_t l)28 duplocale(locale_t l)
29 {
30 LocaleBackendData* locObj = (LocaleBackendData*)l;
31
32 LocaleBackendData* newObj = new (std::nothrow) LocaleBackendData;
33 if (newObj == NULL) {
34 errno = ENOMEM;
35 return (locale_t)0;
36 }
37 newObj->magic = LOCALE_T_MAGIC;
38 newObj->backend = NULL;
39 newObj->databridge = NULL;
40
41 LocaleBackend* backend = (l == LC_GLOBAL_LOCALE) ?
42 gGlobalLocaleBackend : (LocaleBackend*)locObj->backend;
43
44 if (backend == NULL)
45 return (locale_t)newObj;
46
47 // Check if everything is set to "C" or "POSIX",
48 // and avoid making a backend.
49 const char* localeDescription = backend->SetLocale(LC_ALL, NULL);
50
51 if ((strcasecmp(localeDescription, "POSIX") == 0)
52 || (strcasecmp(localeDescription, "C") == 0)) {
53 return (locale_t)newObj;
54 }
55
56 LocaleBackend*& newBackend = newObj->backend;
57 LocaleDataBridge*& newDataBridge = newObj->databridge;
58
59 status_t status = LocaleBackend::CreateBackend(newBackend);
60
61 if (newBackend == NULL) {
62 errno = status;
63 delete newObj;
64 return (locale_t)0;
65 }
66
67 newDataBridge = new (std::nothrow) LocaleDataBridge(false);
68
69 if (newDataBridge == NULL) {
70 errno = ENOMEM;
71 LocaleBackend::DestroyBackend(newBackend);
72 delete newObj;
73 return (locale_t)0;
74 }
75
76 newBackend->Initialize(newDataBridge);
77
78 // Skipping LC_ALL. Asking for LC_ALL would force the backend
79 // to query each other value once, anyway.
80 for (int lc = 1; lc <= LC_LAST; ++lc) {
81 newBackend->SetLocale(lc, backend->SetLocale(lc, NULL));
82 }
83
84 return (locale_t)newObj;
85 }
86
87
88 extern "C" void
freelocale(locale_t l)89 freelocale(locale_t l)
90 {
91 LocaleBackendData* locobj = (LocaleBackendData*)l;
92
93 if (locobj->backend) {
94 LocaleBackend::DestroyBackend(locobj->backend);
95 LocaleDataBridge* databridge = locobj->databridge;
96 delete databridge;
97 }
98 delete locobj;
99 }
100
101
102 extern "C" locale_t
newlocale(int category_mask,const char * locale,locale_t base)103 newlocale(int category_mask, const char* locale, locale_t base)
104 {
105 if (((category_mask | LC_ALL_MASK) != LC_ALL_MASK) || (locale == NULL)) {
106 errno = EINVAL;
107 return (locale_t)0;
108 }
109
110 bool newObject = false;
111 LocaleBackendData* localeObject = (LocaleBackendData*)base;
112
113 if (localeObject == NULL) {
114 localeObject = new (std::nothrow) LocaleBackendData;
115 if (localeObject == NULL) {
116 errno = ENOMEM;
117 return (locale_t)0;
118 }
119 localeObject->magic = LOCALE_T_MAGIC;
120 localeObject->backend = NULL;
121 localeObject->databridge = NULL;
122
123 newObject = true;
124 }
125
126 LocaleBackend*& backend = localeObject->backend;
127 LocaleDataBridge*& databridge = localeObject->databridge;
128
129 const char* locales[LC_LAST + 1];
130 for (int lc = 0; lc <= LC_LAST; lc++)
131 locales[lc] = NULL;
132
133 if (*locale == '\0') {
134 if (category_mask == LC_ALL_MASK) {
135 GetLocalesFromEnvironment(LC_ALL, locales);
136 } else {
137 for (int lc = 1; lc <= LC_LAST; ++lc) {
138 if (category_mask & (1 << (lc - 1)))
139 GetLocalesFromEnvironment(lc, locales);
140 }
141 }
142 } else {
143 if (category_mask == LC_ALL_MASK) {
144 locales[LC_ALL] = locale;
145 }
146 for (int lc = 1; lc <= LC_LAST; ++lc) {
147 if (category_mask & (1 << (lc - 1)))
148 locales[lc] = locale;
149 }
150 }
151
152 if (backend == NULL) {
153 // for any locale other than POSIX/C, we try to activate the ICU
154 // backend
155 bool needBackend = false;
156 for (int lc = 0; lc <= LC_LAST; lc++) {
157 if (locales[lc] != NULL && strcasecmp(locales[lc], "POSIX") != 0
158 && strcasecmp(locales[lc], "C") != 0) {
159 needBackend = true;
160 break;
161 }
162 }
163 if (needBackend) {
164 status_t status = LocaleBackend::CreateBackend(backend);
165 if (backend == NULL) {
166 errno = status;
167 if (newObject) {
168 delete localeObject;
169 }
170 return (locale_t)0;
171 }
172 databridge = new (std::nothrow) LocaleDataBridge(false);
173 if (databridge == NULL) {
174 errno = ENOMEM;
175 LocaleBackend::DestroyBackend(backend);
176 if (newObject) {
177 delete localeObject;
178 }
179 return (locale_t)0;
180 }
181 backend->Initialize(databridge);
182 }
183 }
184
185 BPrivate::ErrnoMaintainer errnoMaintainer;
186
187 if (backend != NULL) {
188 for (int lc = 0; lc <= LC_LAST; lc++) {
189 if (locales[lc] != NULL) {
190 locale = backend->SetLocale(lc, locales[lc]);
191 if (lc == LC_ALL) {
192 // skip the rest, LC_ALL overrides
193 break;
194 }
195 }
196 }
197 }
198
199 return (locale_t)localeObject;
200 }
201
202
203 extern "C" locale_t
uselocale(locale_t newLoc)204 uselocale(locale_t newLoc)
205 {
206 locale_t oldLoc = (locale_t)GetCurrentLocaleInfo();
207 if (oldLoc == NULL) {
208 oldLoc = LC_GLOBAL_LOCALE;
209 }
210
211 if (newLoc != (locale_t)0) {
212 // Avoid expensive TLS reads with a local variable.
213 locale_t appliedLoc = oldLoc;
214
215 if (newLoc == LC_GLOBAL_LOCALE) {
216 appliedLoc = NULL;
217 } else {
218 if (((LocaleBackendData*)newLoc)->magic != LOCALE_T_MAGIC) {
219 errno = EINVAL;
220 return (locale_t)0;
221 }
222 appliedLoc = newLoc;
223 }
224
225 SetCurrentLocaleInfo((LocaleBackendData*)appliedLoc);
226
227 if (appliedLoc != NULL) {
228 LocaleDataBridge*& databridge = ((LocaleBackendData*)appliedLoc)->databridge;
229 // Happens when appliedLoc represents the C locale.
230 if (databridge == NULL) {
231 LocaleBackend*& backend = ((LocaleBackendData*)appliedLoc)->backend;
232 status_t status = LocaleBackend::CreateBackend(backend);
233 if (backend == NULL) {
234 if (status == B_MISSING_LIBRARY) {
235 // This means libroot-addon-icu is not available.
236 // Therefore, the global locale is still the C locale
237 // and cannot be set to any other locale. Do nothing.
238 return oldLoc;
239 }
240 errno = status;
241 return (locale_t)0;
242 }
243
244 databridge = new (std::nothrow) LocaleDataBridge(false);
245 if (databridge == NULL) {
246 LocaleBackend::DestroyBackend(backend);
247 errno = ENOMEM;
248 return (locale_t)0;
249 }
250
251 backend->Initialize(databridge);
252 }
253 databridge->ApplyToCurrentThread();
254 } else {
255 gGlobalLocaleDataBridge.ApplyToCurrentThread();
256 }
257 }
258
259 return oldLoc;
260 }
261
262
263 extern "C" locale_t
__current_locale_t()264 __current_locale_t()
265 {
266 locale_t locale = (locale_t)GetCurrentLocaleInfo();
267 if (locale == NULL) {
268 static LocaleBackendData global_locale_t;
269 global_locale_t.backend = gGlobalLocaleBackend;
270 global_locale_t.databridge = &gGlobalLocaleDataBridge;
271 return (locale_t)&global_locale_t;
272 }
273
274 return locale;
275 }
276
277
278 extern "C" locale_t
__posix_locale_t()279 __posix_locale_t()
280 {
281 static LocaleBackendData posix_locale_t;
282 posix_locale_t.backend = NULL;
283 posix_locale_t.databridge = NULL;
284 return &posix_locale_t;
285 }
286