1 /* 2 * Copyright 2003-2012, 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 <pthread.h> 14 15 #include <Application.h> 16 #include <Autolock.h> 17 #include <Catalog.h> 18 #include <CatalogData.h> 19 #include <DefaultCatalog.h> 20 #include <Debug.h> 21 #include <Entry.h> 22 #include <FormattingConventions.h> 23 #include <Language.h> 24 #include <LocaleRosterData.h> 25 #include <String.h> 26 27 28 namespace BPrivate { 29 30 31 namespace { 32 33 34 static MutableLocaleRoster* sLocaleRoster; 35 36 static pthread_once_t sLocaleRosterInitOnce = PTHREAD_ONCE_INIT; 37 38 39 } // anonymous namespace 40 41 42 static void 43 InitializeLocaleRoster() 44 { 45 sLocaleRoster = new (std::nothrow) MutableLocaleRoster(); 46 } 47 48 49 MutableLocaleRoster::MutableLocaleRoster() 50 { 51 } 52 53 54 MutableLocaleRoster::~MutableLocaleRoster() 55 { 56 } 57 58 59 /*static*/ MutableLocaleRoster* 60 MutableLocaleRoster::Default() 61 { 62 if (sLocaleRoster == NULL) 63 pthread_once(&sLocaleRosterInitOnce, &InitializeLocaleRoster); 64 65 return sLocaleRoster; 66 } 67 68 69 status_t 70 MutableLocaleRoster::SetDefaultFormattingConventions( 71 const BFormattingConventions& newFormattingConventions) 72 { 73 return fData->SetDefaultFormattingConventions(newFormattingConventions); 74 } 75 76 77 status_t 78 MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone) 79 { 80 return fData->SetDefaultTimeZone(newZone); 81 } 82 83 84 status_t 85 MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages) 86 { 87 return fData->SetPreferredLanguages(languages); 88 } 89 90 91 status_t 92 MutableLocaleRoster::SetFilesystemTranslationPreferred(bool preferred) 93 { 94 return fData->SetFilesystemTranslationPreferred(preferred); 95 } 96 97 98 status_t 99 MutableLocaleRoster::LoadSystemCatalog(BCatalog* catalog) const 100 { 101 if (!catalog) 102 return B_BAD_VALUE; 103 104 // figure out libbe-image (shared object) by name 105 image_info info; 106 int32 cookie = 0; 107 bool found = false; 108 109 while (get_next_image_info(0, &cookie, &info) == B_OK) { 110 if (info.data < (void*)&be_app 111 && (char*)info.data + info.data_size > (void*)&be_app) { 112 found = true; 113 break; 114 } 115 } 116 117 if (!found) 118 return B_ERROR; 119 120 // load the catalog for libbe into the given catalog 121 entry_ref ref; 122 status_t status = BEntry(info.name).GetRef(&ref); 123 if (status != B_OK) 124 return status; 125 126 return catalog->SetTo(ref); 127 } 128 129 130 /* 131 * creates a new (empty) catalog of the given type (the request is dispatched 132 * to the appropriate add-on). 133 * If the add-on doesn't support catalog-creation or if the creation fails, 134 * NULL is returned, otherwise a pointer to the freshly created catalog. 135 * Any created catalog will be initialized with the given signature and 136 * language-name. 137 */ 138 BCatalogData* 139 MutableLocaleRoster::CreateCatalog(const char* type, const char* signature, 140 const char* language) 141 { 142 if (!type || !signature || !language) 143 return NULL; 144 145 BAutolock lock(fData->fLock); 146 if (!lock.IsLocked()) 147 return NULL; 148 149 int32 count = fData->fCatalogAddOnInfos.CountItems(); 150 for (int32 i = 0; i < count; ++i) { 151 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 152 fData->fCatalogAddOnInfos.ItemAt(i); 153 if (info->fName.ICompare(type) != 0 || !info->fCreateFunc) 154 continue; 155 156 BCatalogData* catalog = info->fCreateFunc(signature, language); 157 if (catalog != NULL) { 158 info->fLoadedCatalogs.AddItem(catalog); 159 return catalog; 160 } 161 } 162 163 return NULL; 164 } 165 166 167 /* 168 * Loads a catalog for the given signature, language and fingerprint. 169 * The request to load this catalog is dispatched to all add-ons in turn, 170 * until an add-on reports success. 171 * If a catalog depends on another language (as 'english-british' depends 172 * on 'english') the dependant catalogs are automatically loaded, too. 173 * So it is perfectly possible that this method returns a catalog-chain 174 * instead of a single catalog. 175 * NULL is returned if no matching catalog could be found. 176 */ 177 BCatalogData* 178 MutableLocaleRoster::LoadCatalog(const entry_ref& catalogOwner, 179 const char* language, int32 fingerprint) const 180 { 181 BAutolock lock(fData->fLock); 182 if (!lock.IsLocked()) 183 return NULL; 184 185 int32 count = fData->fCatalogAddOnInfos.CountItems(); 186 for (int32 i = 0; i < count; ++i) { 187 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 188 fData->fCatalogAddOnInfos.ItemAt(i); 189 190 if (!info->fInstantiateFunc) 191 continue; 192 BMessage languages; 193 if (language != NULL) { 194 // try to load catalogs for the given language: 195 languages.AddString("language", language); 196 } else { 197 // try to load catalogs for one of the preferred languages: 198 GetPreferredLanguages(&languages); 199 } 200 201 BCatalogData* catalog = NULL; 202 const char* lang; 203 for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK; 204 ++l) { 205 catalog = info->fInstantiateFunc(catalogOwner, lang, fingerprint); 206 if (catalog != NULL) 207 info->fLoadedCatalogs.AddItem(catalog); 208 // Chain-load catalogs for languages that depend on 209 // other languages. 210 // The current implementation uses the filename in order to 211 // detect dependencies (parenthood) between languages (it 212 // traverses from "english_british_oxford" to "english_british" 213 // to "english"): 214 int32 pos; 215 BString langName(lang); 216 BCatalogData* currentCatalog = catalog; 217 BCatalogData* nextCatalog = NULL; 218 while ((pos = langName.FindLast('_')) >= 0) { 219 // language is based on parent, so we load that, too: 220 // (even if the parent catalog was not found) 221 langName.Truncate(pos); 222 nextCatalog = info->fInstantiateFunc(catalogOwner, 223 langName.String(), fingerprint); 224 if (nextCatalog != NULL) { 225 info->fLoadedCatalogs.AddItem(nextCatalog); 226 if (currentCatalog != NULL) 227 currentCatalog->SetNext(nextCatalog); 228 else 229 catalog = nextCatalog; 230 currentCatalog = nextCatalog; 231 } 232 } 233 if (catalog != NULL) 234 return catalog; 235 } 236 } 237 238 return NULL; 239 } 240 241 242 /* 243 * Loads a catalog for the given signature and language. 244 * 245 * Only the default catalog type is searched, and only the standard system 246 * directories. 247 * 248 * If a catalog depends on another language (as 'english-british' depends 249 * on 'english') the dependant catalogs are automatically loaded, too. 250 * So it is perfectly possible that this method returns a catalog-chain 251 * instead of a single catalog. 252 * NULL is returned if no matching catalog could be found. 253 */ 254 BCatalogData* 255 MutableLocaleRoster::LoadCatalog(const char* signature, 256 const char* language) const 257 { 258 BAutolock lock(fData->fLock); 259 if (!lock.IsLocked()) 260 return NULL; 261 262 BMessage languages; 263 if (language != NULL) { 264 // try to load catalogs for the given language: 265 languages.AddString("language", language); 266 } else { 267 // try to load catalogs for one of the preferred languages: 268 GetPreferredLanguages(&languages); 269 } 270 271 272 int32 count = fData->fCatalogAddOnInfos.CountItems(); 273 CatalogAddOnInfo* defaultCatalogInfo = NULL; 274 for (int32 i = 0; i < count; ++i) { 275 CatalogAddOnInfo* info = (CatalogAddOnInfo*) 276 fData->fCatalogAddOnInfos.ItemAt(i); 277 if (info->fInstantiateFunc 278 == BPrivate::DefaultCatalog::Instantiate) { 279 defaultCatalogInfo = info; 280 break; 281 } 282 } 283 284 if (defaultCatalogInfo == NULL) 285 return NULL; 286 287 BPrivate::DefaultCatalog* catalog = NULL; 288 const char* lang; 289 for (int32 l = 0; languages.FindString("language", l, &lang) == B_OK; ++l) { 290 catalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL, signature, 291 lang); 292 293 if (catalog != NULL) { 294 if (catalog->InitCheck() != B_OK 295 || catalog->ReadFromStandardLocations() != B_OK) { 296 delete catalog; 297 catalog = NULL; 298 } else { 299 defaultCatalogInfo->fLoadedCatalogs.AddItem(catalog); 300 } 301 } 302 303 // Chain-load catalogs for languages that depend on 304 // other languages. 305 // The current implementation uses the filename in order to 306 // detect dependencies (parenthood) between languages (it 307 // traverses from "english_british_oxford" to "english_british" 308 // to "english"): 309 int32 pos; 310 BString langName(lang); 311 BCatalogData* currentCatalog = catalog; 312 BPrivate::DefaultCatalog* nextCatalog = NULL; 313 while ((pos = langName.FindLast('_')) >= 0) { 314 // language is based on parent, so we load that, too: 315 // (even if the parent catalog was not found) 316 langName.Truncate(pos); 317 nextCatalog = new (std::nothrow) BPrivate::DefaultCatalog(NULL, 318 signature, langName.String()); 319 320 if (nextCatalog == NULL) 321 continue; 322 323 if (nextCatalog->InitCheck() != B_OK 324 || nextCatalog->ReadFromStandardLocations() != B_OK) { 325 delete nextCatalog; 326 continue; 327 } 328 329 defaultCatalogInfo->fLoadedCatalogs.AddItem(nextCatalog); 330 331 if (currentCatalog != NULL) 332 currentCatalog->SetNext(nextCatalog); 333 else 334 catalog = nextCatalog; 335 currentCatalog = nextCatalog; 336 } 337 if (catalog != NULL) 338 return catalog; 339 } 340 341 return NULL; 342 } 343 344 345 /* 346 * unloads the given catalog (or rather: catalog-chain). 347 * Every single catalog of the chain will be deleted automatically. 348 */ 349 status_t 350 MutableLocaleRoster::UnloadCatalog(BCatalogData* catalog) 351 { 352 if (!catalog) 353 return B_BAD_VALUE; 354 355 BAutolock lock(fData->fLock); 356 if (!lock.IsLocked()) 357 return B_ERROR; 358 359 status_t res = B_ERROR; 360 BCatalogData* nextCatalog; 361 362 while (catalog != NULL) { 363 nextCatalog = catalog->Next(); 364 int32 count = fData->fCatalogAddOnInfos.CountItems(); 365 for (int32 i = 0; i < count; ++i) { 366 CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>( 367 fData->fCatalogAddOnInfos.ItemAt(i)); 368 if (info->fLoadedCatalogs.HasItem(catalog)) { 369 info->fLoadedCatalogs.RemoveItem(catalog); 370 delete catalog; 371 res = B_OK; 372 break; 373 } 374 } 375 catalog = nextCatalog; 376 } 377 return res; 378 } 379 380 381 } // namespace BPrivate 382