xref: /haiku/src/kits/locale/LocaleRoster.cpp (revision 97901ec593ec4dd50ac115c1c35a6d72f6e489a5)
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