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