1 /* 2 * Copyright 2003-2010, 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 <set> 14 15 #include <syslog.h> 16 17 #include <AppFileInfo.h> 18 #include <Autolock.h> 19 #include <Catalog.h> 20 #include <Collator.h> 21 #include <Debug.h> 22 #include <DefaultCatalog.h> 23 #include <Directory.h> 24 #include <Entry.h> 25 #include <File.h> 26 #include <FindDirectory.h> 27 #include <FormattingConventions.h> 28 #include <Language.h> 29 #include <Locale.h> 30 #include <Node.h> 31 #include <Path.h> 32 #include <Roster.h> 33 #include <String.h> 34 #include <TimeZone.h> 35 36 #include <ICUWrapper.h> 37 38 // ICU includes 39 #include <unicode/locid.h> 40 #include <unicode/timezone.h> 41 42 43 namespace BPrivate { 44 45 46 // #pragma mark - CatalogAddOnInfo 47 48 49 CatalogAddOnInfo::CatalogAddOnInfo(const BString& name, const BString& path, 50 uint8 priority) 51 : 52 fInstantiateFunc(NULL), 53 fInstantiateEmbeddedFunc(NULL), 54 fCreateFunc(NULL), 55 fLanguagesFunc(NULL), 56 fName(name), 57 fPath(path), 58 fAddOnImage(B_NO_INIT), 59 fPriority(priority), 60 fIsEmbedded(path.Length()==0) 61 { 62 } 63 64 65 CatalogAddOnInfo::~CatalogAddOnInfo() 66 { 67 int32 count = fLoadedCatalogs.CountItems(); 68 for (int32 i = 0; i < count; ++i) { 69 BCatalogAddOn* cat 70 = static_cast<BCatalogAddOn*>(fLoadedCatalogs.ItemAt(i)); 71 delete cat; 72 } 73 fLoadedCatalogs.MakeEmpty(); 74 UnloadIfPossible(); 75 } 76 77 78 bool 79 CatalogAddOnInfo::MakeSureItsLoaded() 80 { 81 if (!fIsEmbedded && fAddOnImage < B_OK) { 82 // add-on has not been loaded yet, so we try to load it: 83 BString fullAddOnPath(fPath); 84 fullAddOnPath << "/" << fName; 85 fAddOnImage = load_add_on(fullAddOnPath.String()); 86 if (fAddOnImage >= B_OK) { 87 get_image_symbol(fAddOnImage, "instantiate_catalog", 88 B_SYMBOL_TYPE_TEXT, (void**)&fInstantiateFunc); 89 get_image_symbol(fAddOnImage, "instantiate_embedded_catalog", 90 B_SYMBOL_TYPE_TEXT, (void**)&fInstantiateEmbeddedFunc); 91 get_image_symbol(fAddOnImage, "create_catalog", 92 B_SYMBOL_TYPE_TEXT, (void**)&fCreateFunc); 93 get_image_symbol(fAddOnImage, "get_available_languages", 94 B_SYMBOL_TYPE_TEXT, (void**)&fLanguagesFunc); 95 log_team(LOG_DEBUG, "catalog-add-on %s has been loaded", 96 fName.String()); 97 } else { 98 log_team(LOG_DEBUG, "could not load catalog-add-on %s (%s)", 99 fName.String(), strerror(fAddOnImage)); 100 return false; 101 } 102 } else if (fIsEmbedded) { 103 // The built-in catalog still has to provide this function 104 fLanguagesFunc = default_catalog_get_available_languages; 105 } 106 return true; 107 } 108 109 110 void 111 CatalogAddOnInfo::UnloadIfPossible() 112 { 113 if (!fIsEmbedded && fLoadedCatalogs.IsEmpty()) { 114 unload_add_on(fAddOnImage); 115 fAddOnImage = B_NO_INIT; 116 fInstantiateFunc = NULL; 117 fInstantiateEmbeddedFunc = NULL; 118 fCreateFunc = NULL; 119 fLanguagesFunc = NULL; 120 // log_team(LOG_DEBUG, "catalog-add-on %s has been unloaded", 121 // fName.String()); 122 } 123 } 124 125 126 // #pragma mark - RosterData 127 128 129 static const char* kPriorityAttr = "ADDON:priority"; 130 131 static const char* kLanguageField = "language"; 132 133 static const char* kTimezoneField = "timezone"; 134 static const char* kOffsetField = "offset"; 135 136 static RosterData sRosterData(BLanguage("en_US"), 137 BFormattingConventions("en_US")); 138 139 140 RosterData::RosterData() 141 : 142 fLock("LocaleRosterData"), 143 fAreResourcesLoaded(false) 144 { 145 openlog_team("liblocale.so", LOG_PID, LOG_USER); 146 #ifndef DEBUG 147 setlogmask_team(LOG_UPTO(LOG_WARNING)); 148 #endif 149 150 InitializeCatalogAddOns(); 151 152 Refresh(); 153 } 154 155 156 RosterData::RosterData(const BLanguage& language, 157 const BFormattingConventions& conventions) 158 : 159 fLock("LocaleRosterData"), 160 fDefaultLocale(&language, &conventions), 161 fAreResourcesLoaded(false) 162 { 163 openlog_team("liblocale.so", LOG_PID, LOG_USER); 164 #ifndef DEBUG 165 setlogmask_team(LOG_UPTO(LOG_WARNING)); 166 #endif 167 168 InitializeCatalogAddOns(); 169 170 Refresh(); 171 } 172 173 174 RosterData::~RosterData() 175 { 176 BAutolock lock(fLock); 177 178 CleanupCatalogAddOns(); 179 closelog(); 180 } 181 182 183 /*static*/ RosterData* 184 RosterData::Default() 185 { 186 return &sRosterData; 187 } 188 189 190 int 191 RosterData::CompareInfos(const void* left, const void* right) 192 { 193 return ((CatalogAddOnInfo*)right)->fPriority 194 - ((CatalogAddOnInfo*)left)->fPriority; 195 } 196 197 198 status_t 199 RosterData::Refresh() 200 { 201 BAutolock lock(fLock); 202 if (!lock.IsLocked()) 203 return B_ERROR; 204 205 _LoadLocaleSettings(); 206 _LoadTimeSettings(); 207 208 return B_OK; 209 } 210 211 /* 212 iterate over add-on-folders and collect information about each 213 catalog-add-ons (types of catalogs) into fCatalogAddOnInfos. 214 */ 215 void 216 RosterData::InitializeCatalogAddOns() 217 { 218 BAutolock lock(fLock); 219 if (!lock.IsLocked()) 220 return; 221 222 // add info about embedded default catalog: 223 CatalogAddOnInfo* defaultCatalogAddOnInfo 224 = new(std::nothrow) CatalogAddOnInfo("Default", "", 225 DefaultCatalog::kDefaultCatalogAddOnPriority); 226 if (!defaultCatalogAddOnInfo) 227 return; 228 229 defaultCatalogAddOnInfo->fInstantiateFunc = DefaultCatalog::Instantiate; 230 defaultCatalogAddOnInfo->fInstantiateEmbeddedFunc 231 = DefaultCatalog::InstantiateEmbedded; 232 defaultCatalogAddOnInfo->fCreateFunc = DefaultCatalog::Create; 233 fCatalogAddOnInfos.AddItem((void*)defaultCatalogAddOnInfo); 234 235 directory_which folders[] = { 236 B_COMMON_ADDONS_DIRECTORY, 237 B_SYSTEM_ADDONS_DIRECTORY, 238 static_cast<directory_which>(-1) 239 }; 240 BPath addOnPath; 241 BDirectory addOnFolder; 242 char buf[4096]; 243 status_t err; 244 for (int f = 0; folders[f]>=0; ++f) { 245 find_directory(folders[f], &addOnPath); 246 BString addOnFolderName(addOnPath.Path()); 247 addOnFolderName << "/locale/catalogs"; 248 249 system_info info; 250 if (get_system_info(&info) == B_OK 251 && (info.abi & B_HAIKU_ABI_MAJOR) 252 != (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR)) { 253 switch (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) { 254 case B_HAIKU_ABI_GCC_2: 255 addOnFolderName << "/gcc2"; 256 break; 257 case B_HAIKU_ABI_GCC_4: 258 addOnFolderName << "/gcc4"; 259 break; 260 } 261 } 262 263 264 err = addOnFolder.SetTo(addOnFolderName.String()); 265 if (err != B_OK) 266 continue; 267 268 // scan through all the folder's entries for catalog add-ons: 269 int32 count; 270 int8 priority; 271 entry_ref eref; 272 BNode node; 273 BEntry entry; 274 dirent* dent; 275 while ((count = addOnFolder.GetNextDirents((dirent*)buf, 4096)) > 0) { 276 dent = (dirent*)buf; 277 while (count-- > 0) { 278 if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") 279 && strcmp(dent->d_name, "gcc2") 280 && strcmp(dent->d_name, "gcc4")) { 281 // we have found (what should be) a catalog-add-on: 282 eref.device = dent->d_pdev; 283 eref.directory = dent->d_pino; 284 eref.set_name(dent->d_name); 285 entry.SetTo(&eref, true); 286 // traverse through any links to get to the real thang! 287 node.SetTo(&entry); 288 priority = -1; 289 if (node.ReadAttr(kPriorityAttr, B_INT8_TYPE, 0, 290 &priority, sizeof(int8)) <= 0) { 291 // add-on has no priority-attribute yet, so we load it 292 // to fetch the priority from the corresponding 293 // symbol... 294 BString fullAddOnPath(addOnFolderName); 295 fullAddOnPath << "/" << dent->d_name; 296 image_id image = load_add_on(fullAddOnPath.String()); 297 if (image >= B_OK) { 298 uint8* prioPtr; 299 if (get_image_symbol(image, "gCatalogAddOnPriority", 300 B_SYMBOL_TYPE_DATA, 301 (void**)&prioPtr) == B_OK) { 302 priority = *prioPtr; 303 node.WriteAttr(kPriorityAttr, B_INT8_TYPE, 0, 304 &priority, sizeof(int8)); 305 } else { 306 log_team(LOG_ERR, 307 "couldn't get priority for add-on %s\n", 308 fullAddOnPath.String()); 309 } 310 unload_add_on(image); 311 } else { 312 log_team(LOG_ERR, 313 "couldn't load add-on %s, error: %s\n", 314 fullAddOnPath.String(), strerror(image)); 315 } 316 } 317 318 if (priority >= 0) { 319 // add-ons with priority < 0 will be ignored 320 CatalogAddOnInfo* addOnInfo 321 = new(std::nothrow) CatalogAddOnInfo(dent->d_name, 322 addOnFolderName, priority); 323 if (addOnInfo) 324 fCatalogAddOnInfos.AddItem((void*)addOnInfo); 325 } 326 } 327 // Bump the dirent-pointer by length of the dirent just handled: 328 dent = (dirent*)((char*)dent + dent->d_reclen); 329 } 330 } 331 } 332 fCatalogAddOnInfos.SortItems(CompareInfos); 333 } 334 335 336 /* 337 * unloads all catalog-add-ons (which will throw away all loaded catalogs, too) 338 */ 339 void 340 RosterData::CleanupCatalogAddOns() 341 { 342 BAutolock lock(fLock); 343 if (!lock.IsLocked()) 344 return; 345 346 int32 count = fCatalogAddOnInfos.CountItems(); 347 for (int32 i = 0; i<count; ++i) { 348 CatalogAddOnInfo* info 349 = static_cast<CatalogAddOnInfo*>(fCatalogAddOnInfos.ItemAt(i)); 350 delete info; 351 } 352 fCatalogAddOnInfos.MakeEmpty(); 353 } 354 355 356 status_t 357 RosterData::SetDefaultFormattingConventions( 358 const BFormattingConventions& newFormattingConventions) 359 { 360 status_t status = B_OK; 361 362 BAutolock lock(fLock); 363 if (!lock.IsLocked()) 364 return B_ERROR; 365 366 status = _SetDefaultFormattingConventions(newFormattingConventions); 367 368 if (status == B_OK) 369 status = _SaveLocaleSettings(); 370 371 if (status == B_OK) { 372 BMessage updateMessage(B_LOCALE_CHANGED); 373 status = _AddDefaultFormattingConventionsToMessage(&updateMessage); 374 if (status == B_OK) 375 status = be_roster->Broadcast(&updateMessage); 376 } 377 378 return status; 379 } 380 381 382 status_t 383 RosterData::SetDefaultTimeZone(const BTimeZone& newZone) 384 { 385 status_t status = B_OK; 386 387 BAutolock lock(fLock); 388 if (!lock.IsLocked()) 389 return B_ERROR; 390 391 status = _SetDefaultTimeZone(newZone); 392 393 if (status == B_OK) 394 status = _SaveTimeSettings(); 395 396 if (status == B_OK) { 397 BMessage updateMessage(B_LOCALE_CHANGED); 398 status = _AddDefaultTimeZoneToMessage(&updateMessage); 399 if (status == B_OK) 400 status = be_roster->Broadcast(&updateMessage); 401 } 402 403 return status; 404 } 405 406 407 status_t 408 RosterData::SetPreferredLanguages(const BMessage* languages) 409 { 410 status_t status = B_OK; 411 412 BAutolock lock(fLock); 413 if (!lock.IsLocked()) 414 return B_ERROR; 415 416 status = _SetPreferredLanguages(languages); 417 418 if (status == B_OK) 419 status = _SaveLocaleSettings(); 420 421 if (status == B_OK) { 422 BMessage updateMessage(B_LOCALE_CHANGED); 423 status = _AddPreferredLanguagesToMessage(&updateMessage); 424 if (status == B_OK) 425 status = be_roster->Broadcast(&updateMessage); 426 } 427 428 return status; 429 } 430 431 432 status_t 433 RosterData::_LoadLocaleSettings() 434 { 435 BPath path; 436 BFile file; 437 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 438 if (status == B_OK) { 439 path.Append("Locale settings"); 440 status = file.SetTo(path.Path(), B_READ_ONLY); 441 } 442 BMessage settings; 443 if (status == B_OK) 444 status = settings.Unflatten(&file); 445 446 if (status == B_OK) { 447 BFormattingConventions conventions(&settings); 448 fDefaultLocale.SetFormattingConventions(conventions); 449 450 _SetPreferredLanguages(&settings); 451 452 return B_OK; 453 } 454 455 456 // Something went wrong (no settings file or invalid BMessage), so we 457 // set everything to default values 458 459 fPreferredLanguages.MakeEmpty(); 460 fPreferredLanguages.AddString(kLanguageField, "en"); 461 BLanguage defaultLanguage("en_US"); 462 fDefaultLocale.SetLanguage(defaultLanguage); 463 BFormattingConventions conventions("en_US"); 464 fDefaultLocale.SetFormattingConventions(conventions); 465 466 return status; 467 } 468 469 470 status_t 471 RosterData::_LoadTimeSettings() 472 { 473 BPath path; 474 BFile file; 475 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 476 if (status == B_OK) { 477 path.Append("Time settings"); 478 status = file.SetTo(path.Path(), B_READ_ONLY); 479 } 480 BMessage settings; 481 if (status == B_OK) 482 status = settings.Unflatten(&file); 483 if (status == B_OK) { 484 BString timeZoneID; 485 if (settings.FindString(kTimezoneField, &timeZoneID) == B_OK) 486 _SetDefaultTimeZone(BTimeZone(timeZoneID.String())); 487 else 488 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone)); 489 490 return B_OK; 491 } 492 493 // Something went wrong (no settings file or invalid BMessage), so we 494 // set everything to default values 495 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone)); 496 497 return status; 498 } 499 500 501 status_t 502 RosterData::_SaveLocaleSettings() 503 { 504 BMessage settings; 505 status_t status = _AddDefaultFormattingConventionsToMessage(&settings); 506 if (status == B_OK) 507 _AddPreferredLanguagesToMessage(&settings); 508 509 BPath path; 510 if (status == B_OK) 511 status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 512 513 BFile file; 514 if (status == B_OK) { 515 path.Append("Locale settings"); 516 status = file.SetTo(path.Path(), 517 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 518 } 519 if (status == B_OK) 520 status = settings.Flatten(&file); 521 if (status == B_OK) 522 status = file.Sync(); 523 524 return status; 525 } 526 527 528 status_t 529 RosterData::_SaveTimeSettings() 530 { 531 BMessage settings; 532 status_t status = _AddDefaultTimeZoneToMessage(&settings); 533 534 BPath path; 535 if (status == B_OK) 536 status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 537 538 BFile file; 539 if (status == B_OK) { 540 path.Append("Time settings"); 541 status = file.SetTo(path.Path(), 542 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 543 } 544 if (status == B_OK) 545 status = settings.Flatten(&file); 546 if (status == B_OK) 547 status = file.Sync(); 548 549 return status; 550 } 551 552 553 status_t 554 RosterData::_SetDefaultFormattingConventions( 555 const BFormattingConventions& newFormattingConventions) 556 { 557 fDefaultLocale.SetFormattingConventions(newFormattingConventions); 558 559 UErrorCode icuError = U_ZERO_ERROR; 560 Locale icuLocale = Locale::createCanonical(newFormattingConventions.ID()); 561 if (icuLocale.isBogus()) 562 return B_ERROR; 563 564 Locale::setDefault(icuLocale, icuError); 565 if (!U_SUCCESS(icuError)) 566 return B_ERROR; 567 568 return B_OK; 569 } 570 571 572 status_t 573 RosterData::_SetDefaultTimeZone(const BTimeZone& newZone) 574 { 575 fDefaultTimeZone = newZone; 576 577 TimeZone* timeZone = TimeZone::createTimeZone(newZone.ID().String()); 578 if (timeZone == NULL) 579 return B_ERROR; 580 TimeZone::adoptDefault(timeZone); 581 582 return B_OK; 583 } 584 585 586 status_t 587 RosterData::_SetPreferredLanguages(const BMessage* languages) 588 { 589 BString langName; 590 if (languages != NULL 591 && languages->FindString(kLanguageField, &langName) == B_OK) { 592 fDefaultLocale.SetCollator(BCollator(langName.String())); 593 fDefaultLocale.SetLanguage(BLanguage(langName.String())); 594 595 fPreferredLanguages.RemoveName(kLanguageField); 596 for (int i = 0; languages->FindString(kLanguageField, i, &langName) 597 == B_OK; i++) { 598 fPreferredLanguages.AddString(kLanguageField, langName); 599 } 600 } else { 601 fPreferredLanguages.MakeEmpty(); 602 fPreferredLanguages.AddString(kLanguageField, "en"); 603 fDefaultLocale.SetCollator(BCollator("en")); 604 } 605 606 return B_OK; 607 } 608 609 610 status_t 611 RosterData::_AddDefaultFormattingConventionsToMessage(BMessage* message) const 612 { 613 BFormattingConventions conventions; 614 fDefaultLocale.GetFormattingConventions(&conventions); 615 616 return conventions.Archive(message); 617 } 618 619 620 status_t 621 RosterData::_AddDefaultTimeZoneToMessage(BMessage* message) const 622 { 623 status_t status = message->AddString(kTimezoneField, fDefaultTimeZone.ID()); 624 625 // add the offset, too, since that is used by clockconfig when setting 626 // up timezone state during boot 627 if (status == B_OK) { 628 status = message->AddInt32(kOffsetField, 629 fDefaultTimeZone.OffsetFromGMT()); 630 } 631 632 return status; 633 } 634 635 636 status_t 637 RosterData::_AddPreferredLanguagesToMessage(BMessage* message) const 638 { 639 status_t status = B_OK; 640 641 BString langName; 642 for (int i = 0; fPreferredLanguages.FindString("language", i, 643 &langName) == B_OK; i++) { 644 status = message->AddString(kLanguageField, langName); 645 if (status != B_OK) 646 break; 647 } 648 649 return status; 650 } 651 652 653 // #pragma mark - MutableLocaleRoster 654 655 656 static MutableLocaleRoster sLocaleRoster; 657 658 659 MutableLocaleRoster::MutableLocaleRoster() 660 { 661 } 662 663 664 MutableLocaleRoster::~MutableLocaleRoster() 665 { 666 } 667 668 669 /*static*/ MutableLocaleRoster* 670 MutableLocaleRoster::Default() 671 { 672 return &sLocaleRoster; 673 } 674 675 676 status_t 677 MutableLocaleRoster::SetDefaultFormattingConventions(const BFormattingConventions& newFormattingConventions) 678 { 679 return RosterData::Default()->SetDefaultFormattingConventions( 680 newFormattingConventions); 681 } 682 683 684 status_t 685 MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone) 686 { 687 return RosterData::Default()->SetDefaultTimeZone(newZone); 688 } 689 690 691 status_t 692 MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages) 693 { 694 return RosterData::Default()->SetPreferredLanguages(languages); 695 } 696 697 698 status_t 699 MutableLocaleRoster::GetSystemCatalog(BCatalogAddOn** catalog) const 700 { 701 if (!catalog) 702 return B_BAD_VALUE; 703 *catalog = LoadCatalog("system"); 704 return B_OK; 705 } 706 707 708 /* 709 * creates a new (empty) catalog of the given type (the request is dispatched 710 * to the appropriate add-on). 711 * If the add-on doesn't support catalog-creation or if the creation fails, 712 * NULL is returned, otherwise a pointer to the freshly created catalog. 713 * Any created catalog will be initialized with the given signature and 714 * language-name. 715 */ 716 BCatalogAddOn* 717 MutableLocaleRoster::CreateCatalog(const char* type, const char* signature, 718 const char* language) 719 { 720 if (!type || !signature || !language) 721 return NULL; 722 723 BAutolock lock(RosterData::Default()->fLock); 724 if (!lock.IsLocked()) 725 return NULL; 726 727 int32 count = RosterData::Default()->fCatalogAddOnInfos.CountItems(); 728 for (int32 i = 0; i < count; ++i) { 729 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 730 RosterData::Default()->fCatalogAddOnInfos.ItemAt(i); 731 if (info->fName.ICompare(type)!=0 || !info->MakeSureItsLoaded() 732 || !info->fCreateFunc) 733 continue; 734 735 BCatalogAddOn* catalog = info->fCreateFunc(signature, language); 736 if (catalog) { 737 info->fLoadedCatalogs.AddItem(catalog); 738 info->UnloadIfPossible(); 739 return catalog; 740 } 741 } 742 743 return NULL; 744 } 745 746 747 /* 748 * Loads a catalog for the given signature, language and fingerprint. 749 * The request to load this catalog is dispatched to all add-ons in turn, 750 * until an add-on reports success. 751 * If a catalog depends on another language (as 'english-british' depends 752 * on 'english') the dependant catalogs are automatically loaded, too. 753 * So it is perfectly possible that this method returns a catalog-chain 754 * instead of a single catalog. 755 * NULL is returned if no matching catalog could be found. 756 */ 757 BCatalogAddOn* 758 MutableLocaleRoster::LoadCatalog(const char* signature, const char* language, 759 int32 fingerprint) const 760 { 761 if (!signature) 762 return NULL; 763 764 BAutolock lock(RosterData::Default()->fLock); 765 if (!lock.IsLocked()) 766 return NULL; 767 768 int32 count = RosterData::Default()->fCatalogAddOnInfos.CountItems(); 769 for (int32 i = 0; i < count; ++i) { 770 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 771 RosterData::Default()->fCatalogAddOnInfos.ItemAt(i); 772 773 if (!info->MakeSureItsLoaded() || !info->fInstantiateFunc) 774 continue; 775 BMessage languages; 776 if (language) 777 // try to load catalogs for the given language: 778 languages.AddString("language", language); 779 else 780 // try to load catalogs for one of the preferred languages: 781 GetPreferredLanguages(&languages); 782 783 BCatalogAddOn* catalog = NULL; 784 const char* lang; 785 for (int32 l=0; languages.FindString("language", l, &lang)==B_OK; ++l) { 786 catalog = info->fInstantiateFunc(signature, lang, fingerprint); 787 if (catalog) 788 info->fLoadedCatalogs.AddItem(catalog); 789 // Chain-load catalogs for languages that depend on 790 // other languages. 791 // The current implementation uses the filename in order to 792 // detect dependencies (parenthood) between languages (it 793 // traverses from "english_british_oxford" to "english_british" 794 // to "english"): 795 int32 pos; 796 BString langName(lang); 797 BCatalogAddOn* currCatalog = catalog; 798 BCatalogAddOn* nextCatalog; 799 while ((pos = langName.FindLast('_')) >= 0) { 800 // language is based on parent, so we load that, too: 801 // (even if the parent catalog was not found) 802 langName.Truncate(pos); 803 nextCatalog = info->fInstantiateFunc(signature, 804 langName.String(), fingerprint); 805 if (nextCatalog) { 806 info->fLoadedCatalogs.AddItem(nextCatalog); 807 if(currCatalog) 808 currCatalog->SetNext(nextCatalog); 809 else 810 catalog = nextCatalog; 811 currCatalog = nextCatalog; 812 } 813 } 814 if (catalog != NULL) 815 return catalog; 816 } 817 info->UnloadIfPossible(); 818 } 819 820 return NULL; 821 } 822 823 824 /* 825 * Loads an embedded catalog from the given entry-ref (which is usually an 826 * app- or add-on-file. The request to load the catalog is dispatched to all 827 * add-ons in turn, until an add-on reports success. 828 * NULL is returned if no embedded catalog could be found. 829 */ 830 BCatalogAddOn* 831 MutableLocaleRoster::LoadEmbeddedCatalog(entry_ref* appOrAddOnRef) 832 { 833 if (!appOrAddOnRef) 834 return NULL; 835 836 BAutolock lock(RosterData::Default()->fLock); 837 if (!lock.IsLocked()) 838 return NULL; 839 840 int32 count = RosterData::Default()->fCatalogAddOnInfos.CountItems(); 841 for (int32 i = 0; i < count; ++i) { 842 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 843 RosterData::Default()->fCatalogAddOnInfos.ItemAt(i); 844 845 if (!info->MakeSureItsLoaded() || !info->fInstantiateEmbeddedFunc) 846 continue; 847 848 BCatalogAddOn* catalog = NULL; 849 catalog = info->fInstantiateEmbeddedFunc(appOrAddOnRef); 850 if (catalog) { 851 info->fLoadedCatalogs.AddItem(catalog); 852 return catalog; 853 } 854 info->UnloadIfPossible(); 855 } 856 857 return NULL; 858 } 859 860 861 /* 862 * unloads the given catalog (or rather: catalog-chain). 863 * Every single catalog of the chain will be deleted automatically. 864 * Add-ons that have no more current catalogs are unloaded, too. 865 */ 866 status_t 867 MutableLocaleRoster::UnloadCatalog(BCatalogAddOn* catalog) 868 { 869 if (!catalog) 870 return B_BAD_VALUE; 871 872 BAutolock lock(RosterData::Default()->fLock); 873 if (!lock.IsLocked()) 874 return B_ERROR; 875 876 status_t res = B_ERROR; 877 BCatalogAddOn* nextCatalog; 878 879 while (catalog != NULL) { 880 nextCatalog = catalog->Next(); 881 int32 count = RosterData::Default()->fCatalogAddOnInfos.CountItems(); 882 for (int32 i = 0; i < count; ++i) { 883 CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>( 884 RosterData::Default()->fCatalogAddOnInfos.ItemAt(i)); 885 if (info->fLoadedCatalogs.HasItem(catalog)) { 886 info->fLoadedCatalogs.RemoveItem(catalog); 887 delete catalog; 888 info->UnloadIfPossible(); 889 res = B_OK; 890 break; 891 } 892 } 893 catalog = nextCatalog; 894 } 895 return res; 896 } 897 898 899 } // namespace BPrivate 900