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 <LocaleRoster.h> 12 13 #include <set> 14 15 #include <assert.h> 16 #include <stdio.h> // for debug only 17 #include <syslog.h> 18 19 #include <Autolock.h> 20 #include <Catalog.h> 21 #include <Collator.h> 22 #include <Country.h> 23 #include <DefaultCatalog.h> 24 #include <Directory.h> 25 #include <Entry.h> 26 #include <File.h> 27 #include <FindDirectory.h> 28 #include <Language.h> 29 #include <Locale.h> 30 #include <Node.h> 31 #include <Path.h> 32 #include <String.h> 33 34 #include <ICUWrapper.h> 35 36 // ICU includes 37 #include <unicode/locid.h> 38 39 40 /* 41 * several attributes/resource-IDs used within the Locale Kit: 42 */ 43 static const char *kPriorityAttr = "ADDON:priority"; 44 45 const char *BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE"; 46 // name of catalog language, lives in every catalog file 47 const char *BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE"; 48 // catalog signature, lives in every catalog file 49 const char *BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT"; 50 // catalog fingerprint, may live in catalog file 51 52 const char *BLocaleRoster::kCatManagerMimeType 53 = "application/x-vnd.Be.locale.catalog-manager"; 54 // signature of catalog managing app 55 const char *BLocaleRoster::kCatEditorMimeType 56 = "application/x-vnd.Be.locale.catalog-editor"; 57 // signature of catalog editor app 58 59 const char *BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG"; 60 // attribute which contains flattened data of embedded catalog 61 // this may live in an app- or add-on-file 62 int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA; 63 // a unique value used to identify the resource (=> embedded CAtalog DAta) 64 // which contains flattened data of embedded catalog. 65 // this may live in an app- or add-on-file 66 67 68 69 typedef BCatalogAddOn *(*InstantiateCatalogFunc)(const char *name, 70 const char *language, uint32 fingerprint); 71 72 typedef BCatalogAddOn *(*CreateCatalogFunc)(const char *name, 73 const char *language); 74 75 typedef BCatalogAddOn *(*InstantiateEmbeddedCatalogFunc)( 76 entry_ref *appOrAddOnRef); 77 78 typedef status_t (*GetAvailableLanguagesFunc)(BMessage*, const char*, 79 const char*, int32); 80 81 static BLocaleRoster gLocaleRoster; 82 BLocaleRoster *be_locale_roster = &gLocaleRoster; 83 84 85 /* 86 * info about a single catalog-add-on (representing a catalog type): 87 */ 88 struct BCatalogAddOnInfo { 89 BString fName; 90 BString fPath; 91 image_id fAddOnImage; 92 InstantiateCatalogFunc fInstantiateFunc; 93 InstantiateEmbeddedCatalogFunc fInstantiateEmbeddedFunc; 94 CreateCatalogFunc fCreateFunc; 95 GetAvailableLanguagesFunc fLanguagesFunc; 96 uint8 fPriority; 97 BList fLoadedCatalogs; 98 bool fIsEmbedded; 99 // an embedded add-on actually isn't an add-on, it is included 100 // as part of the library. The DefaultCatalog is such a beast! 101 102 BCatalogAddOnInfo(const BString& name, const BString& path, uint8 priority); 103 ~BCatalogAddOnInfo(); 104 bool MakeSureItsLoaded(); 105 void UnloadIfPossible(); 106 }; 107 108 109 BCatalogAddOnInfo::BCatalogAddOnInfo(const BString& name, const BString& path, 110 uint8 priority) 111 : 112 fName(name), 113 fPath(path), 114 fAddOnImage(B_NO_INIT), 115 fInstantiateFunc(NULL), 116 fInstantiateEmbeddedFunc(NULL), 117 fCreateFunc(NULL), 118 fLanguagesFunc(NULL), 119 fPriority(priority), 120 fIsEmbedded(path.Length()==0) 121 { 122 } 123 124 125 BCatalogAddOnInfo::~BCatalogAddOnInfo() 126 { 127 int32 count = fLoadedCatalogs.CountItems(); 128 for (int32 i = 0; i < count; ++i) { 129 BCatalogAddOn* cat 130 = static_cast<BCatalogAddOn*>(fLoadedCatalogs.ItemAt(i)); 131 delete cat; 132 } 133 fLoadedCatalogs.MakeEmpty(); 134 UnloadIfPossible(); 135 } 136 137 138 bool 139 BCatalogAddOnInfo::MakeSureItsLoaded() 140 { 141 if (!fIsEmbedded && fAddOnImage < B_OK) { 142 // add-on has not been loaded yet, so we try to load it: 143 BString fullAddOnPath(fPath); 144 fullAddOnPath << "/" << fName; 145 fAddOnImage = load_add_on(fullAddOnPath.String()); 146 if (fAddOnImage >= B_OK) { 147 get_image_symbol(fAddOnImage, "instantiate_catalog", 148 B_SYMBOL_TYPE_TEXT, (void **)&fInstantiateFunc); 149 get_image_symbol(fAddOnImage, "instantiate_embedded_catalog", 150 B_SYMBOL_TYPE_TEXT, (void **)&fInstantiateEmbeddedFunc); 151 get_image_symbol(fAddOnImage, "create_catalog", 152 B_SYMBOL_TYPE_TEXT, (void **)&fCreateFunc); 153 get_image_symbol(fAddOnImage, "get_available_languages", 154 B_SYMBOL_TYPE_TEXT, (void **)&fLanguagesFunc); 155 log_team(LOG_DEBUG, "catalog-add-on %s has been loaded", 156 fName.String()); 157 } else { 158 log_team(LOG_DEBUG, "could not load catalog-add-on %s (%s)", 159 fName.String(), strerror(fAddOnImage)); 160 return false; 161 } 162 } else if (fIsEmbedded) { 163 // The built-in catalog still has to provide this function 164 fLanguagesFunc = default_catalog_get_available_languages; 165 } 166 return true; 167 } 168 169 170 void 171 BCatalogAddOnInfo::UnloadIfPossible() 172 { 173 if (!fIsEmbedded && fLoadedCatalogs.IsEmpty()) { 174 unload_add_on(fAddOnImage); 175 fAddOnImage = B_NO_INIT; 176 fInstantiateFunc = NULL; 177 fInstantiateEmbeddedFunc = NULL; 178 fCreateFunc = NULL; 179 fLanguagesFunc = NULL; 180 log_team(LOG_DEBUG, "catalog-add-on %s has been unloaded", 181 fName.String()); 182 } 183 } 184 185 186 /* 187 * the global data that is shared between all roster-objects: 188 */ 189 struct RosterData { 190 BLocker fLock; 191 BList fCatalogAddOnInfos; 192 BMessage fPreferredLanguages; 193 BString fCountryCodeName; 194 BString fCountryDateFormat; 195 196 RosterData(); 197 ~RosterData(); 198 void InitializeCatalogAddOns(); 199 void CleanupCatalogAddOns(); 200 static int CompareInfos(const void *left, const void *right); 201 }; 202 static RosterData gRosterData; 203 204 205 RosterData::RosterData() 206 : 207 fLock("LocaleRosterData") 208 { 209 BAutolock lock(fLock); 210 assert(lock.IsLocked()); 211 212 openlog_team("liblocale.so", LOG_PID, LOG_USER); 213 #ifndef DEBUG 214 setlogmask_team(LOG_UPTO(LOG_WARNING)); 215 #endif 216 217 InitializeCatalogAddOns(); 218 219 // Load preferences to get the preferred languages 220 BPath path; 221 BFile file; 222 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 223 path.Append("Locale settings"); 224 BMessage settingsMessage; 225 if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK 226 && settingsMessage.Unflatten(&file) == B_OK) { 227 BString langName; 228 if (settingsMessage.FindString("language", &langName) == B_OK) { 229 UErrorCode icuError = U_ZERO_ERROR; 230 Locale icuLocale = Locale::createCanonical(langName.String()); 231 assert(!icuLocale.isBogus()); 232 UnicodeString ustr; 233 BString bstr; 234 BStringByteSink bbs(&bstr); 235 icuLocale.getDisplayName(ustr); 236 ustr.toUTF8(bbs); 237 238 Locale::setDefault(icuLocale,icuError); 239 assert(icuError == U_ZERO_ERROR); 240 fPreferredLanguages.RemoveName("language"); 241 for (int i = 0; settingsMessage.FindString("language", i, 242 &langName) == B_OK; i++) { 243 fPreferredLanguages.AddString("language", langName); 244 } 245 } else 246 fPreferredLanguages.AddString("language", "en"); 247 248 if (settingsMessage.FindString("country", &fCountryCodeName) != B_OK) 249 fCountryCodeName = "en_US"; 250 return; 251 } 252 } 253 254 // Something went wrong (no settings file or invalid BMessage 255 // set everything to default values 256 fPreferredLanguages.AddString("language", "en"); 257 fCountryCodeName = "en_US"; 258 log_team(LOG_ERR,"*** No language preference found!\n"); 259 } 260 261 262 RosterData::~RosterData() 263 { 264 BAutolock lock(fLock); 265 assert(lock.IsLocked()); 266 CleanupCatalogAddOns(); 267 closelog(); 268 } 269 270 271 int 272 RosterData::CompareInfos(const void *left, const void *right) 273 { 274 return ((BCatalogAddOnInfo*)right)->fPriority 275 - ((BCatalogAddOnInfo*)left)->fPriority; 276 } 277 278 279 /* 280 * iterate over add-on-folders and collect information about each 281 * catalog-add-ons (types of catalogs) into fCatalogAddOnInfos. 282 */ 283 void 284 RosterData::InitializeCatalogAddOns() 285 { 286 BAutolock lock(fLock); 287 assert(lock.IsLocked()); 288 289 // add info about embedded default catalog: 290 BCatalogAddOnInfo *defaultCatalogAddOnInfo 291 = new(std::nothrow) BCatalogAddOnInfo("Default", "", 292 DefaultCatalog::kDefaultCatalogAddOnPriority); 293 if (!defaultCatalogAddOnInfo) 294 return; 295 296 defaultCatalogAddOnInfo->fInstantiateFunc = DefaultCatalog::Instantiate; 297 defaultCatalogAddOnInfo->fInstantiateEmbeddedFunc 298 = DefaultCatalog::InstantiateEmbedded; 299 defaultCatalogAddOnInfo->fCreateFunc = DefaultCatalog::Create; 300 fCatalogAddOnInfos.AddItem((void*)defaultCatalogAddOnInfo); 301 302 directory_which folders[] = { 303 B_COMMON_ADDONS_DIRECTORY, 304 B_SYSTEM_ADDONS_DIRECTORY, 305 static_cast<directory_which>(-1) 306 }; 307 BPath addOnPath; 308 BDirectory addOnFolder; 309 char buf[4096]; 310 status_t err; 311 for (int f=0; folders[f]>=0; ++f) { 312 find_directory(folders[f], &addOnPath); 313 BString addOnFolderName(addOnPath.Path()); 314 addOnFolderName << "/locale/catalogs"; 315 err = addOnFolder.SetTo(addOnFolderName.String()); 316 if (err != B_OK) 317 continue; 318 319 // scan through all the folder's entries for catalog add-ons: 320 int32 count; 321 int8 priority; 322 entry_ref eref; 323 BNode node; 324 BEntry entry; 325 dirent *dent; 326 while ((count = addOnFolder.GetNextDirents((dirent *)buf, 4096)) > 0) { 327 dent = (dirent *)buf; 328 while (count-- > 0) { 329 if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) { 330 // we have found (what should be) a catalog-add-on: 331 eref.device = dent->d_pdev; 332 eref.directory = dent->d_pino; 333 eref.set_name(dent->d_name); 334 entry.SetTo(&eref, true); 335 // traverse through any links to get to the real thang! 336 node.SetTo(&entry); 337 priority = -1; 338 if (node.ReadAttr(kPriorityAttr, B_INT8_TYPE, 0, 339 &priority, sizeof(int8)) <= 0) { 340 // add-on has no priority-attribute yet, so we load it 341 // to fetch the priority from the corresponding 342 // symbol... 343 BString fullAddOnPath(addOnFolderName); 344 fullAddOnPath << "/" << dent->d_name; 345 image_id image = load_add_on(fullAddOnPath.String()); 346 if (image >= B_OK) { 347 uint8 *prioPtr; 348 if (get_image_symbol(image, "gCatalogAddOnPriority", 349 B_SYMBOL_TYPE_DATA, 350 (void **)&prioPtr) == B_OK) { 351 priority = *prioPtr; 352 node.WriteAttr(kPriorityAttr, B_INT8_TYPE, 0, 353 &priority, sizeof(int8)); 354 } else { 355 log_team(LOG_ERR, 356 "couldn't get priority for add-on %s\n", 357 fullAddOnPath.String()); 358 } 359 unload_add_on(image); 360 } else { 361 log_team(LOG_ERR, 362 "couldn't load add-on %s, error: %s\n", 363 fullAddOnPath.String(), strerror(image)); 364 } 365 } 366 367 log_team(LOG_ERR, "Found : %s priority: %d\n", 368 dent->d_name,priority); 369 370 if (priority >= 0) { 371 // add-ons with priority < 0 will be ignored 372 BCatalogAddOnInfo* addOnInfo 373 = new(std::nothrow) BCatalogAddOnInfo(dent->d_name, 374 addOnFolderName, priority); 375 if (addOnInfo) 376 fCatalogAddOnInfos.AddItem((void*)addOnInfo); 377 } 378 } 379 // Bump the dirent-pointer by length of the dirent just handled: 380 dent = (dirent *)((char *)dent + dent->d_reclen); 381 } 382 } 383 } 384 fCatalogAddOnInfos.SortItems(CompareInfos); 385 386 for (int32 i=0; i<fCatalogAddOnInfos.CountItems(); ++i) { 387 BCatalogAddOnInfo *info 388 = static_cast<BCatalogAddOnInfo*>(fCatalogAddOnInfos.ItemAt(i)); 389 log_team(LOG_INFO, 390 "roster uses catalog-add-on %s/%s with priority %d", 391 info->fIsEmbedded ? "(embedded)" : info->fPath.String(), 392 info->fName.String(), info->fPriority); 393 } 394 } 395 396 397 /* 398 * unloads all catalog-add-ons (which will throw away all loaded catalogs, too) 399 */ 400 void 401 RosterData::CleanupCatalogAddOns() 402 { 403 BAutolock lock(fLock); 404 assert(lock.IsLocked()); 405 int32 count = fCatalogAddOnInfos.CountItems(); 406 for (int32 i=0; i<count; ++i) { 407 BCatalogAddOnInfo *info 408 = static_cast<BCatalogAddOnInfo*>(fCatalogAddOnInfos.ItemAt(i)); 409 delete info; 410 } 411 fCatalogAddOnInfos.MakeEmpty(); 412 } 413 414 415 // #pragma mark - BLocaleRoster 416 417 418 BLocaleRoster::BLocaleRoster() 419 { 420 } 421 422 423 BLocaleRoster::~BLocaleRoster() 424 { 425 } 426 427 428 status_t 429 BLocaleRoster::GetSystemCatalog(BCatalogAddOn **catalog) const 430 { 431 if(!catalog) 432 return B_BAD_VALUE; 433 *catalog = be_locale_roster->LoadCatalog("system"); 434 return B_OK; 435 } 436 437 438 status_t 439 BLocaleRoster::GetDefaultCollator(BCollator **collator) const 440 { 441 // It should just use the archived collator from the locale settings; 442 // if that is not available, just return the standard collator 443 if (!collator) 444 return B_BAD_VALUE; 445 *collator = new(std::nothrow) BCollator(); 446 return B_OK; 447 } 448 449 450 status_t 451 BLocaleRoster::GetDefaultLanguage(BLanguage **language) const 452 { 453 if (!language) 454 return B_BAD_VALUE; 455 *language = new(std::nothrow) BLanguage(NULL); 456 return B_OK; 457 } 458 459 460 status_t 461 BLocaleRoster::GetDefaultCountry(BCountry **country) const 462 { 463 if (!country) 464 return B_BAD_VALUE; 465 466 BAutolock lock(gRosterData.fLock); 467 assert(lock.IsLocked()); 468 469 *country = new(std::nothrow) BCountry( 470 gRosterData.fCountryCodeName.String()); 471 if (gRosterData.fCountryDateFormat.Length() > 0) 472 (*country)->SetDateFormat(gRosterData.fCountryDateFormat.String()); 473 return B_OK; 474 } 475 476 477 status_t 478 BLocaleRoster::GetLanguage(const char* languageCode, 479 BLanguage** _language) const 480 { 481 if (_language == NULL || languageCode == NULL || languageCode[0] == '\0') 482 return B_BAD_VALUE; 483 484 BLanguage* language = new(std::nothrow) BLanguage(languageCode); 485 if (language == NULL) 486 return B_NO_MEMORY; 487 488 *_language = language; 489 return B_OK; 490 } 491 492 493 void 494 BLocaleRoster::SetDefaultCountry(BCountry* newDefault) const 495 { 496 gRosterData.fCountryCodeName = newDefault->Code(); 497 newDefault->DateFormat(gRosterData.fCountryDateFormat, true); 498 } 499 500 501 status_t 502 BLocaleRoster::GetPreferredLanguages(BMessage* languages) const 503 { 504 if (!languages) 505 return B_BAD_VALUE; 506 507 BAutolock lock(gRosterData.fLock); 508 assert(lock.IsLocked()); 509 510 *languages = gRosterData.fPreferredLanguages; 511 return B_OK; 512 } 513 514 515 status_t 516 BLocaleRoster::SetPreferredLanguages(BMessage *languages) 517 { 518 BAutolock lock(gRosterData.fLock); 519 assert(lock.IsLocked()); 520 521 if (languages) 522 gRosterData.fPreferredLanguages = *languages; 523 else 524 gRosterData.fPreferredLanguages.MakeEmpty(); 525 return B_OK; 526 } 527 528 529 // Get all the available languages from ICU 530 status_t 531 BLocaleRoster::GetInstalledLanguages(BMessage *languages) const 532 { 533 if (!languages) 534 return B_BAD_VALUE; 535 536 int32 i; 537 UnicodeString icuLanguageName; 538 BString languageName; 539 540 int32_t localeCount; 541 const Locale* icuLocaleList 542 = Locale::getAvailableLocales(localeCount); 543 544 // Loop over the strings and add them to an std::set to remove duplicates 545 for (i = 0; i < localeCount; i++) { 546 languages->AddString("langs", icuLocaleList[i].getName()); 547 } 548 549 return B_OK; 550 } 551 552 553 status_t 554 BLocaleRoster::GetInstalledCatalogs(BMessage * languageList, const char* sigPattern, 555 const char* langPattern, int32 fingerprint) const 556 { 557 if (languageList == NULL) 558 return B_BAD_VALUE; 559 560 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 561 for (int32 i = 0; i < count; ++i) { 562 BCatalogAddOnInfo *info 563 = (BCatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 564 565 if (!info->MakeSureItsLoaded() || !info->fLanguagesFunc) 566 continue; 567 568 info->fLanguagesFunc(languageList, sigPattern, langPattern, 569 fingerprint); 570 } 571 572 return B_OK; 573 } 574 575 576 /* 577 * creates a new (empty) catalog of the given type (the request is dispatched 578 * to the appropriate add-on). 579 * If the add-on doesn't support catalog-creation or if the creation fails, 580 * NULL is returned, otherwise a pointer to the freshly created catalog. 581 * Any created catalog will be initialized with the given signature and 582 * language-name. 583 */ 584 BCatalogAddOn* 585 BLocaleRoster::CreateCatalog(const char *type, const char *signature, 586 const char *language) 587 { 588 if (!type || !signature || !language) 589 return NULL; 590 591 BAutolock lock(gRosterData.fLock); 592 assert(lock.IsLocked()); 593 594 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 595 for (int32 i = 0; i < count; ++i) { 596 BCatalogAddOnInfo *info 597 = (BCatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 598 if (info->fName.ICompare(type)!=0 || !info->MakeSureItsLoaded() 599 || !info->fCreateFunc) 600 continue; 601 602 BCatalogAddOn *catalog = info->fCreateFunc(signature, language); 603 if (catalog) { 604 info->fLoadedCatalogs.AddItem(catalog); 605 info->UnloadIfPossible(); 606 return catalog; 607 } 608 } 609 610 return NULL; 611 } 612 613 614 /* 615 * Loads a catalog for the given signature, language and fingerprint. 616 * The request to load this catalog is dispatched to all add-ons in turn, 617 * until an add-on reports success. 618 * If a catalog depends on another language (as 'english-british' depends 619 * on 'english') the dependant catalogs are automatically loaded, too. 620 * So it is perfectly possible that this method returns a catalog-chain 621 * instead of a single catalog. 622 * NULL is returned if no matching catalog could be found. 623 */ 624 BCatalogAddOn* 625 BLocaleRoster::LoadCatalog(const char *signature, const char *language, 626 int32 fingerprint) 627 { 628 if (!signature) 629 return NULL; 630 631 BAutolock lock(gRosterData.fLock); 632 assert(lock.IsLocked()); 633 634 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 635 for (int32 i = 0; i < count; ++i) { 636 BCatalogAddOnInfo *info 637 = (BCatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 638 639 if (!info->MakeSureItsLoaded() || !info->fInstantiateFunc) 640 continue; 641 BMessage languages; 642 if (language) 643 // try to load catalogs for the given language: 644 languages.AddString("language", language); 645 else 646 // try to load catalogs for one of the preferred languages: 647 GetPreferredLanguages(&languages); 648 649 BCatalogAddOn *catalog = NULL; 650 const char *lang; 651 for (int32 l=0; languages.FindString("language", l, &lang)==B_OK; ++l) { 652 catalog = info->fInstantiateFunc(signature, lang, fingerprint); 653 if (catalog) 654 info->fLoadedCatalogs.AddItem(catalog); 655 // Chain-load catalogs for languages that depend on 656 // other languages. 657 // The current implementation uses the filename in order to 658 // detect dependencies (parenthood) between languages (it 659 // traverses from "english_british_oxford" to "english_british" 660 // to "english"): 661 // TODO: use ICU facilities instead, so we can handle more 662 // complex things such as fr_FR@euro, or whatever, encodings 663 // and so on. 664 int32 pos; 665 BString langName(lang); 666 BCatalogAddOn *currCatalog=catalog, *nextCatalog; 667 while ((pos = langName.FindLast('_')) >= 0) { 668 // language is based on parent, so we load that, too: 669 // (even if the parent catalog was not found) 670 langName.Truncate(pos); 671 nextCatalog = info->fInstantiateFunc(signature, 672 langName.String(), fingerprint); 673 if (nextCatalog) { 674 info->fLoadedCatalogs.AddItem(nextCatalog); 675 if(currCatalog) 676 currCatalog->fNext = nextCatalog; 677 else 678 catalog = nextCatalog; 679 currCatalog = nextCatalog; 680 } 681 } 682 return catalog; 683 } 684 info->UnloadIfPossible(); 685 } 686 687 return NULL; 688 } 689 690 691 /* 692 * Loads an embedded catalog from the given entry-ref (which is usually an 693 * app- or add-on-file. The request to load the catalog is dispatched to all 694 * add-ons in turn, until an add-on reports success. 695 * NULL is returned if no embedded catalog could be found. 696 */ 697 BCatalogAddOn* 698 BLocaleRoster::LoadEmbeddedCatalog(entry_ref *appOrAddOnRef) 699 { 700 if (!appOrAddOnRef) 701 return NULL; 702 703 BAutolock lock(gRosterData.fLock); 704 assert(lock.IsLocked()); 705 706 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 707 for (int32 i = 0; i < count; ++i) { 708 BCatalogAddOnInfo *info 709 = (BCatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 710 711 if (!info->MakeSureItsLoaded() || !info->fInstantiateEmbeddedFunc) 712 continue; 713 714 BCatalogAddOn *catalog = NULL; 715 catalog = info->fInstantiateEmbeddedFunc(appOrAddOnRef); 716 if (catalog) { 717 info->fLoadedCatalogs.AddItem(catalog); 718 return catalog; 719 } 720 info->UnloadIfPossible(); 721 } 722 723 return NULL; 724 } 725 726 727 /* 728 * unloads the given catalog (or rather: catalog-chain). 729 * Every single catalog of the chain will be deleted automatically. 730 * Add-ons that have no more current catalogs are unloaded, too. 731 */ 732 status_t 733 BLocaleRoster::UnloadCatalog(BCatalogAddOn *catalog) 734 { 735 if (!catalog) 736 return B_BAD_VALUE; 737 738 BAutolock lock(gRosterData.fLock); 739 assert(lock.IsLocked()); 740 741 status_t res = B_ERROR; 742 BCatalogAddOn *nextCatalog; 743 // note: as we currently aren't chainloading catalogs, there is only 744 // one catalog to unload... 745 while (catalog) { 746 nextCatalog = catalog->fNext; 747 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 748 for (int32 i = 0; i < count; ++i) { 749 BCatalogAddOnInfo *info 750 = static_cast<BCatalogAddOnInfo*>( 751 gRosterData.fCatalogAddOnInfos.ItemAt(i) 752 ); 753 if (info->fLoadedCatalogs.HasItem(catalog)) { 754 info->fLoadedCatalogs.RemoveItem(catalog); 755 delete catalog; 756 info->UnloadIfPossible(); 757 res = B_OK; 758 } 759 } 760 catalog = nextCatalog; 761 } 762 return res; 763 } 764