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