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