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 38 LocaleBackend* backend = (l == LC_GLOBAL_LOCALE) ? 39 gGlobalLocaleBackend : (LocaleBackend*)locObj->backend; 40 41 if (backend == NULL) { 42 newObj->backend = NULL; 43 return (locale_t)newObj; 44 } 45 46 // Check if everything is set to "C" or "POSIX", 47 // and avoid making a backend. 48 const char* localeDescription = backend->SetLocale(LC_ALL, NULL); 49 50 if ((strcasecmp(localeDescription, "POSIX") == 0) 51 || (strcasecmp(localeDescription, "C") == 0)) { 52 newObj->backend = NULL; 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 newObj->magic = LOCALE_T_MAGIC; 85 86 return (locale_t)newObj; 87 } 88 89 90 extern "C" void 91 freelocale(locale_t l) 92 { 93 LocaleBackendData* locobj = (LocaleBackendData*)l; 94 95 if (locobj->backend) { 96 LocaleBackend::DestroyBackend(locobj->backend); 97 LocaleDataBridge* databridge = locobj->databridge; 98 delete databridge; 99 } 100 delete locobj; 101 } 102 103 104 extern "C" locale_t 105 newlocale(int category_mask, const char* locale, locale_t base) 106 { 107 if (((category_mask | LC_ALL_MASK) != LC_ALL_MASK) || (locale == NULL)) { 108 errno = EINVAL; 109 return (locale_t)0; 110 } 111 112 bool newObject = false; 113 LocaleBackendData* localeObject = (LocaleBackendData*)base; 114 115 if (localeObject == NULL) { 116 localeObject = new (std::nothrow) LocaleBackendData; 117 if (localeObject == NULL) { 118 errno = ENOMEM; 119 return (locale_t)0; 120 } 121 localeObject->magic = LOCALE_T_MAGIC; 122 localeObject->backend = NULL; 123 localeObject->databridge = NULL; 124 125 newObject = true; 126 } 127 128 LocaleBackend*& backend = localeObject->backend; 129 LocaleDataBridge*& databridge = localeObject->databridge; 130 131 const char* locales[LC_LAST + 1]; 132 for (int lc = 0; lc <= LC_LAST; lc++) 133 locales[lc] = NULL; 134 135 if (*locale == '\0') { 136 if (category_mask == LC_ALL_MASK) { 137 GetLocalesFromEnvironment(LC_ALL, locales); 138 } else { 139 for (int lc = 1; lc <= LC_LAST; ++lc) { 140 if (category_mask & (1 << (lc - 1))) { 141 GetLocalesFromEnvironment(lc, locales); 142 } 143 } 144 } 145 } else { 146 if (category_mask == LC_ALL_MASK) { 147 locales[LC_ALL] = locale; 148 } 149 for (int lc = 1; lc <= LC_LAST; ++lc) { 150 if (category_mask & (1 << (lc - 1))) { 151 locales[lc] = locale; 152 } 153 } 154 } 155 156 if (backend == NULL) { 157 // for any locale other than POSIX/C, we try to activate the ICU 158 // backend 159 bool needBackend = false; 160 for (int lc = 0; lc <= LC_LAST; lc++) { 161 if (locales[lc] != NULL && strcasecmp(locales[lc], "POSIX") != 0 162 && strcasecmp(locales[lc], "C") != 0) { 163 needBackend = true; 164 break; 165 } 166 } 167 if (needBackend) { 168 status_t status = LocaleBackend::CreateBackend(backend); 169 if (backend == NULL) { 170 errno = status; 171 if (newObject) { 172 delete localeObject; 173 } 174 return (locale_t)0; 175 } 176 databridge = new (std::nothrow) LocaleDataBridge(false); 177 if (databridge == NULL) { 178 errno = ENOMEM; 179 LocaleBackend::DestroyBackend(backend); 180 if (newObject) { 181 delete localeObject; 182 } 183 return (locale_t)0; 184 } 185 backend->Initialize(databridge); 186 } 187 } 188 189 BPrivate::ErrnoMaintainer errnoMaintainer; 190 191 if (backend != NULL) { 192 for (int lc = 0; lc <= LC_LAST; lc++) { 193 if (locales[lc] != NULL) { 194 locale = backend->SetLocale(lc, locales[lc]); 195 if (lc == LC_ALL) { 196 // skip the rest, LC_ALL overrides 197 break; 198 } 199 } 200 } 201 } 202 203 return (locale_t)localeObject; 204 } 205 206 207 extern "C" locale_t 208 uselocale(locale_t newLoc) 209 { 210 locale_t oldLoc = (locale_t)GetCurrentLocaleInfo(); 211 if (oldLoc == NULL) { 212 oldLoc = LC_GLOBAL_LOCALE; 213 } 214 215 if (newLoc != (locale_t)0) { 216 // Avoid expensive TLS reads with a local variable. 217 locale_t appliedLoc = oldLoc; 218 219 if (newLoc == LC_GLOBAL_LOCALE) { 220 appliedLoc = NULL; 221 } else { 222 if (((LocaleBackendData*)newLoc)->magic != LOCALE_T_MAGIC) { 223 errno = EINVAL; 224 return (locale_t)0; 225 } 226 appliedLoc = newLoc; 227 } 228 229 SetCurrentLocaleInfo((LocaleBackendData*)appliedLoc); 230 231 if (appliedLoc != NULL) { 232 LocaleDataBridge*& databridge = ((LocaleBackendData*)appliedLoc)->databridge; 233 // Happens when appliedLoc represents the C locale. 234 if (databridge == NULL) { 235 LocaleBackend*& backend = ((LocaleBackendData*)appliedLoc)->backend; 236 status_t status = LocaleBackend::CreateBackend(backend); 237 if (backend == NULL) { 238 if (status == B_MISSING_LIBRARY) { 239 // This means libroot-addon-icu is not available. 240 // Therefore, the global locale is still the C locale 241 // and cannot be set to any other locale. Do nothing. 242 return oldLoc; 243 } 244 errno = status; 245 return (locale_t)0; 246 } 247 248 databridge = new (std::nothrow) LocaleDataBridge(false); 249 if (databridge == NULL) { 250 LocaleBackend::DestroyBackend(backend); 251 errno = ENOMEM; 252 return (locale_t)0; 253 } 254 255 backend->Initialize(databridge); 256 } 257 databridge->ApplyToCurrentThread(); 258 } else { 259 gGlobalLocaleDataBridge.ApplyToCurrentThread(); 260 } 261 } 262 263 return oldLoc; 264 } 265 266 267 extern "C" locale_t 268 __current_locale_t() 269 { 270 locale_t locale = (locale_t)GetCurrentLocaleInfo(); 271 if (locale == NULL) { 272 static LocaleBackendData global_locale_t; 273 global_locale_t.backend = gGlobalLocaleBackend; 274 global_locale_t.databridge = &gGlobalLocaleDataBridge; 275 return (locale_t)&global_locale_t; 276 } 277 278 return locale; 279 } 280