1 /* 2 * Copyright 2003-2012, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Oliver Tappe, zooey@hirschkaefer.de 8 */ 9 10 11 #include <MutableLocaleRoster.h> 12 13 #include <pthread.h> 14 15 #include <Application.h> 16 #include <Autolock.h> 17 #include <Catalog.h> 18 #include <CatalogData.h> 19 #include <DefaultCatalog.h> 20 #include <Debug.h> 21 #include <Entry.h> 22 #include <FormattingConventions.h> 23 #include <Language.h> 24 #include <LocaleRosterData.h> 25 #include <String.h> 26 27 28 namespace BPrivate { 29 30 31 namespace { 32 33 34 static MutableLocaleRoster* sLocaleRoster; 35 36 static pthread_once_t sLocaleRosterInitOnce = PTHREAD_ONCE_INIT; 37 38 39 } // anonymous namespace 40 41 42 static void 43 InitializeLocaleRoster() 44 { 45 sLocaleRoster = new (std::nothrow) MutableLocaleRoster(); 46 } 47 48 49 MutableLocaleRoster::MutableLocaleRoster() 50 { 51 } 52 53 54 MutableLocaleRoster::~MutableLocaleRoster() 55 { 56 } 57 58 59 /*static*/ MutableLocaleRoster* 60 MutableLocaleRoster::Default() 61 { 62 if (sLocaleRoster == NULL) 63 pthread_once(&sLocaleRosterInitOnce, &InitializeLocaleRoster); 64 65 return sLocaleRoster; 66 } 67 68 69 status_t 70 MutableLocaleRoster::SetDefaultFormattingConventions( 71 const BFormattingConventions& newFormattingConventions) 72 { 73 return fData->SetDefaultFormattingConventions(newFormattingConventions); 74 } 75 76 77 status_t 78 MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone) 79 { 80 return fData->SetDefaultTimeZone(newZone); 81 } 82 83 84 status_t 85 MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages) 86 { 87 return fData->SetPreferredLanguages(languages); 88 } 89 90 91 status_t 92 MutableLocaleRoster::SetFilesystemTranslationPreferred(bool preferred) 93 { 94 return fData->SetFilesystemTranslationPreferred(preferred); 95 } 96 97 98 status_t 99 MutableLocaleRoster::LoadSystemCatalog(BCatalog* catalog) const 100 { 101 if (!catalog) 102 return B_BAD_VALUE; 103 104 // figure out libbe-image (shared object) by name 105 image_info info; 106 int32 cookie = 0; 107 bool found = false; 108 109 while (get_next_image_info(0, &cookie, &info) == B_OK) { 110 if (info.data < (void*)&be_app 111 && (char*)info.data + info.data_size > (void*)&be_app) { 112 found = true; 113 break; 114 } 115 } 116 117 if (!found) 118 return B_ERROR; 119 120 // load the catalog for libbe into the given catalog 121 entry_ref ref; 122 status_t status = BEntry(info.name).GetRef(&ref); 123 if (status != B_OK) 124 return status; 125 126 return catalog->SetTo(ref); 127 } 128 129 130 /* 131 * creates a new (empty) catalog of the given type (the request is dispatched 132 * to the appropriate add-on). 133 * If the add-on doesn't support catalog-creation or if the creation fails, 134 * NULL is returned, otherwise a pointer to the freshly created catalog. 135 * Any created catalog will be initialized with the given signature and 136 * language-name. 137 */ 138 BCatalogData* 139 MutableLocaleRoster::CreateCatalog(const char* type, const char* signature, 140 const char* language) 141 { 142 if (!type || !signature || !language) 143 return NULL; 144 145 BAutolock lock(fData->fLock); 146 if (!lock.IsLocked()) 147 return NULL; 148 149 int32 count = fData->fCatalogAddOnInfos.CountItems(); 150 for (int32 i = 0; i < count; ++i) { 151 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 152 fData->fCatalogAddOnInfos.ItemAt(i); 153 if (info->fName.ICompare(type) != 0 || !info->MakeSureItsLoaded() 154 || !info->fCreateFunc) 155 continue; 156 157 BCatalogData* catalog = info->fCreateFunc(signature, language); 158 if (catalog != NULL) { 159 info->fLoadedCatalogs.AddItem(catalog); 160 info->UnloadIfPossible(); 161 return catalog; 162 } 163 } 164 165 return NULL; 166 } 167 168 169 /* 170 * Loads a catalog for the given signature, language and fingerprint. 171 * The request to load this catalog is dispatched to all add-ons in turn, 172 * until an add-on reports success. 173 * If a catalog depends on another language (as 'english-british' depends 174 * on 'english') the dependant catalogs are automatically loaded, too. 175 * So it is perfectly possible that this method returns a catalog-chain 176 * instead of a single catalog. 177 * NULL is returned if no matching catalog could be found. 178 */ 179 BCatalogData* 180 MutableLocaleRoster::LoadCatalog(const entry_ref& catalogOwner, 181 const char* language, int32 fingerprint) const 182 { 183 BAutolock lock(fData->fLock); 184 if (!lock.IsLocked()) 185 return NULL; 186 187 int32 count = fData->fCatalogAddOnInfos.CountItems(); 188 for (int32 i = 0; i < count; ++i) { 189 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 190 fData->fCatalogAddOnInfos.ItemAt(i); 191 192 if (!info->MakeSureItsLoaded() || !info->fInstantiateFunc) 193 continue; 194 BMessage languages; 195 if (language != NULL) { 196 // try to load catalogs for the given language: 197 languages.AddString("language", language); 198 } else { 199 // try to load catalogs for one of the preferred languages: 200 GetPreferredLanguages(&languages); 201 } 202 203 BCatalogData* catalog = NULL; 204 const char* lang; 205 for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK; 206 ++l) { 207 catalog = info->fInstantiateFunc(catalogOwner, lang, fingerprint); 208 if (catalog != NULL) 209 info->fLoadedCatalogs.AddItem(catalog); 210 // Chain-load catalogs for languages that depend on 211 // other languages. 212 // The current implementation uses the filename in order to 213 // detect dependencies (parenthood) between languages (it 214 // traverses from "english_british_oxford" to "english_british" 215 // to "english"): 216 int32 pos; 217 BString langName(lang); 218 BCatalogData* currentCatalog = catalog; 219 BCatalogData* nextCatalog = NULL; 220 while ((pos = langName.FindLast('_')) >= 0) { 221 // language is based on parent, so we load that, too: 222 // (even if the parent catalog was not found) 223 langName.Truncate(pos); 224 nextCatalog = info->fInstantiateFunc(catalogOwner, 225 langName.String(), fingerprint); 226 if (nextCatalog != NULL) { 227 info->fLoadedCatalogs.AddItem(nextCatalog); 228 if (currentCatalog != NULL) 229 currentCatalog->SetNext(nextCatalog); 230 else 231 catalog = nextCatalog; 232 currentCatalog = nextCatalog; 233 } 234 } 235 if (catalog != NULL) 236 return catalog; 237 } 238 info->UnloadIfPossible(); 239 } 240 241 return NULL; 242 } 243 244 245 /* 246 * Loads a catalog for the given signature and language. 247 * 248 * Only the default catalog type is searched, and only the standard system 249 * directories. 250 * 251 * If a catalog depends on another language (as 'english-british' depends 252 * on 'english') the dependant catalogs are automatically loaded, too. 253 * So it is perfectly possible that this method returns a catalog-chain 254 * instead of a single catalog. 255 * NULL is returned if no matching catalog could be found. 256 */ 257 BCatalogData* 258 MutableLocaleRoster::LoadCatalog(const char* signature, 259 const char* language) const 260 { 261 BAutolock lock(fData->fLock); 262 if (!lock.IsLocked()) 263 return NULL; 264 265 BMessage languages; 266 if (language != NULL) { 267 // try to load catalogs for the given language: 268 languages.AddString("language", language); 269 } else { 270 // try to load catalogs for one of the preferred languages: 271 GetPreferredLanguages(&languages); 272 } 273 274 275 int32 count = fData->fCatalogAddOnInfos.CountItems(); 276 CatalogAddOnInfo* defaultCatalogInfo = NULL; 277 for (int32 i = 0; i < count; ++i) { 278 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 279 fData->fCatalogAddOnInfos.ItemAt(i); 280 if (info->MakeSureItsLoaded() 281 && info->fInstantiateFunc 282 == BPrivate::DefaultCatalog::Instantiate) { 283 defaultCatalogInfo = info; 284 break; 285 } 286 } 287 288 if (defaultCatalogInfo == NULL) 289 return NULL; 290 291 BPrivate::DefaultCatalog* catalog = NULL; 292 const char* lang; 293 for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK; ++l) { 294 catalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL, signature, 295 lang); 296 if (catalog == NULL) 297 continue; 298 299 if (catalog->InitCheck() != B_OK 300 || catalog->ReadFromStandardLocations() != B_OK) { 301 delete catalog; 302 continue; 303 } 304 305 defaultCatalogInfo->fLoadedCatalogs.AddItem(catalog); 306 307 // Chain-load catalogs for languages that depend on 308 // other languages. 309 // The current implementation uses the filename in order to 310 // detect dependencies (parenthood) between languages (it 311 // traverses from "english_british_oxford" to "english_british" 312 // to "english"): 313 int32 pos; 314 BString langName(lang); 315 BCatalogData* currentCatalog = catalog; 316 BPrivate::DefaultCatalog* nextCatalog = NULL; 317 while ((pos = langName.FindLast('_')) >= 0) { 318 // language is based on parent, so we load that, too: 319 // (even if the parent catalog was not found) 320 langName.Truncate(pos); 321 nextCatalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL, 322 signature, langName.String()); 323 324 if (nextCatalog == NULL) 325 continue; 326 327 if (nextCatalog->InitCheck() != B_OK 328 || nextCatalog->ReadFromStandardLocations() != B_OK) { 329 delete nextCatalog; 330 continue; 331 } 332 333 defaultCatalogInfo->fLoadedCatalogs.AddItem(nextCatalog); 334 335 if (currentCatalog != NULL) 336 currentCatalog->SetNext(nextCatalog); 337 else 338 catalog = nextCatalog; 339 currentCatalog = nextCatalog; 340 } 341 if (catalog != NULL) 342 return catalog; 343 } 344 345 return NULL; 346 } 347 348 349 /* 350 * unloads the given catalog (or rather: catalog-chain). 351 * Every single catalog of the chain will be deleted automatically. 352 * Add-ons that have no more current catalogs are unloaded, too. 353 */ 354 status_t 355 MutableLocaleRoster::UnloadCatalog(BCatalogData* catalog) 356 { 357 if (!catalog) 358 return B_BAD_VALUE; 359 360 BAutolock lock(fData->fLock); 361 if (!lock.IsLocked()) 362 return B_ERROR; 363 364 status_t res = B_ERROR; 365 BCatalogData* nextCatalog; 366 367 while (catalog != NULL) { 368 nextCatalog = catalog->Next(); 369 int32 count = fData->fCatalogAddOnInfos.CountItems(); 370 for (int32 i = 0; i < count; ++i) { 371 CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>( 372 fData->fCatalogAddOnInfos.ItemAt(i)); 373 if (info->fLoadedCatalogs.HasItem(catalog)) { 374 info->fLoadedCatalogs.RemoveItem(catalog); 375 delete catalog; 376 info->UnloadIfPossible(); 377 res = B_OK; 378 break; 379 } 380 } 381 catalog = nextCatalog; 382 } 383 return res; 384 } 385 386 387 } // namespace BPrivate 388