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 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 } 143 } else { 144 if (category_mask == LC_ALL_MASK) { 145 locales[LC_ALL] = locale; 146 } 147 for (int lc = 1; lc <= LC_LAST; ++lc) { 148 if (category_mask & (1 << (lc - 1))) { 149 locales[lc] = locale; 150 } 151 } 152 } 153 154 if (backend == NULL) { 155 // for any locale other than POSIX/C, we try to activate the ICU 156 // backend 157 bool needBackend = false; 158 for (int lc = 0; lc <= LC_LAST; lc++) { 159 if (locales[lc] != NULL && strcasecmp(locales[lc], "POSIX") != 0 160 && strcasecmp(locales[lc], "C") != 0) { 161 needBackend = true; 162 break; 163 } 164 } 165 if (needBackend) { 166 status_t status = LocaleBackend::CreateBackend(backend); 167 if (backend == NULL) { 168 errno = status; 169 if (newObject) { 170 delete localeObject; 171 } 172 return (locale_t)0; 173 } 174 databridge = new (std::nothrow) LocaleDataBridge(false); 175 if (databridge == NULL) { 176 errno = ENOMEM; 177 LocaleBackend::DestroyBackend(backend); 178 if (newObject) { 179 delete localeObject; 180 } 181 return (locale_t)0; 182 } 183 backend->Initialize(databridge); 184 } 185 } 186 187 BPrivate::ErrnoMaintainer errnoMaintainer; 188 189 if (backend != NULL) { 190 for (int lc = 0; lc <= LC_LAST; lc++) { 191 if (locales[lc] != NULL) { 192 locale = backend->SetLocale(lc, locales[lc]); 193 if (lc == LC_ALL) { 194 // skip the rest, LC_ALL overrides 195 break; 196 } 197 } 198 } 199 } 200 201 return (locale_t)localeObject; 202 } 203 204 205 extern "C" locale_t 206 uselocale(locale_t newLoc) 207 { 208 locale_t oldLoc = (locale_t)GetCurrentLocaleInfo(); 209 if (oldLoc == NULL) { 210 oldLoc = LC_GLOBAL_LOCALE; 211 } 212 213 if (newLoc != (locale_t)0) { 214 // Avoid expensive TLS reads with a local variable. 215 locale_t appliedLoc = oldLoc; 216 217 if (newLoc == LC_GLOBAL_LOCALE) { 218 appliedLoc = NULL; 219 } else { 220 if (((LocaleBackendData*)newLoc)->magic != LOCALE_T_MAGIC) { 221 errno = EINVAL; 222 return (locale_t)0; 223 } 224 appliedLoc = newLoc; 225 } 226 227 SetCurrentLocaleInfo((LocaleBackendData*)appliedLoc); 228 229 if (appliedLoc != NULL) { 230 LocaleDataBridge*& databridge = ((LocaleBackendData*)appliedLoc)->databridge; 231 // Happens when appliedLoc represents the C locale. 232 if (databridge == NULL) { 233 LocaleBackend*& backend = ((LocaleBackendData*)appliedLoc)->backend; 234 status_t status = LocaleBackend::CreateBackend(backend); 235 if (backend == NULL) { 236 if (status == B_MISSING_LIBRARY) { 237 // This means libroot-addon-icu is not available. 238 // Therefore, the global locale is still the C locale 239 // and cannot be set to any other locale. Do nothing. 240 return oldLoc; 241 } 242 errno = status; 243 return (locale_t)0; 244 } 245 246 databridge = new (std::nothrow) LocaleDataBridge(false); 247 if (databridge == NULL) { 248 LocaleBackend::DestroyBackend(backend); 249 errno = ENOMEM; 250 return (locale_t)0; 251 } 252 253 backend->Initialize(databridge); 254 } 255 databridge->ApplyToCurrentThread(); 256 } else { 257 gGlobalLocaleDataBridge.ApplyToCurrentThread(); 258 } 259 } 260 261 return oldLoc; 262 } 263 264 265 extern "C" locale_t 266 __current_locale_t() 267 { 268 locale_t locale = (locale_t)GetCurrentLocaleInfo(); 269 if (locale == NULL) { 270 static LocaleBackendData global_locale_t; 271 global_locale_t.backend = gGlobalLocaleBackend; 272 global_locale_t.databridge = &gGlobalLocaleDataBridge; 273 return (locale_t)&global_locale_t; 274 } 275 276 return locale; 277 } 278