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