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 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 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 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 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 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 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