1 /* 2 * Copyright 2003-2009, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Oliver Tappe, zooey@hirschkaefer.de 7 * Adrien Destugues, pulkomandy@gmail.com 8 */ 9 10 11 #include <memory> 12 #include <new> 13 #include <syslog.h> 14 15 #include <Application.h> 16 #include <DataIO.h> 17 #include <Directory.h> 18 #include <File.h> 19 #include <FindDirectory.h> 20 #include <fs_attr.h> 21 #include <Message.h> 22 #include <Mime.h> 23 #include <Path.h> 24 #include <Resources.h> 25 #include <Roster.h> 26 27 #include <DefaultCatalog.h> 28 #include <LocaleRoster.h> 29 30 #include <cstdio> 31 32 33 using std::auto_ptr; 34 using std::min; 35 using std::max; 36 using std::pair; 37 38 39 /* 40 * This file implements the default catalog-type for the opentracker locale 41 * kit. Alternatively, this could be used as a full add-on, but currently this 42 * is provided as part of liblocale.so. 43 */ 44 45 46 /* 47 * several attributes/resource-IDs used within the Locale Kit: 48 */ 49 const char *kCatLangAttr = "BEOS:LOCALE_LANGUAGE"; 50 // name of catalog language, lives in every catalog file 51 const char *kCatSigAttr = "BEOS:LOCALE_SIGNATURE"; 52 // catalog signature, lives in every catalog file 53 const char *kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT"; 54 // catalog fingerprint, may live in catalog file 55 56 const char *DefaultCatalog::kCatMimeType 57 = "locale/x-vnd.Be.locale-catalog.default"; 58 59 static int16 kCatArchiveVersion = 1; 60 // version of the catalog archive structure, bump this if you change it! 61 62 63 /* 64 * constructs a DefaultCatalog with given signature and language and reads 65 * the catalog from disk. 66 * InitCheck() will be B_OK if catalog could be loaded successfully, it will 67 * give an appropriate error-code otherwise. 68 */ 69 DefaultCatalog::DefaultCatalog(const char *signature, const char *language, 70 uint32 fingerprint) 71 : 72 BHashMapCatalog(signature, language, fingerprint) 73 { 74 fInitCheck = B_NOT_SUPPORTED; 75 fprintf(stderr, 76 "trying to load default-catalog(sig=%s, lang=%s) results in %s", 77 signature, language, strerror(fInitCheck)); 78 } 79 80 81 /* 82 * constructs a DefaultCatalog and reads it from the resources of the 83 * given entry-ref (which usually is an app- or add-on-file). 84 * InitCheck() will be B_OK if catalog could be loaded successfully, it will 85 * give an appropriate error-code otherwise. 86 */ 87 DefaultCatalog::DefaultCatalog(entry_ref *appOrAddOnRef) 88 : 89 BHashMapCatalog("", "", 0) 90 { 91 fInitCheck = ReadFromResource(appOrAddOnRef); 92 fprintf(stderr, 93 "trying to load embedded catalog from resources results in %s", 94 strerror(fInitCheck)); 95 } 96 97 98 /* 99 * constructs an empty DefaultCatalog with given sig and language. 100 * This is used for editing/testing purposes. 101 * InitCheck() will always be B_OK. 102 */ 103 DefaultCatalog::DefaultCatalog(const char *path, const char *signature, 104 const char *language) 105 : 106 BHashMapCatalog(signature, language, 0), 107 fPath(path) 108 { 109 fInitCheck = B_OK; 110 } 111 112 113 DefaultCatalog::~DefaultCatalog() 114 { 115 } 116 117 118 status_t 119 DefaultCatalog::ReadFromFile(const char *path) 120 { 121 if (!path) 122 path = fPath.String(); 123 124 BFile catalogFile; 125 status_t res = catalogFile.SetTo(path, B_READ_ONLY); 126 if (res != B_OK) { 127 fprintf(stderr, "no catalog at %s\n", path); 128 return B_ENTRY_NOT_FOUND; 129 } 130 131 fPath = path; 132 fprintf(stderr, "found catalog at %s\n", path); 133 134 off_t sz = 0; 135 res = catalogFile.GetSize(&sz); 136 if (res != B_OK) { 137 fprintf(stderr, "couldn't get size for catalog-file %s\n", path); 138 return res; 139 } 140 141 auto_ptr<char> buf(new(std::nothrow) char [sz]); 142 if (buf.get() == NULL) { 143 fprintf(stderr, "couldn't allocate array of %Ld chars\n", sz); 144 return B_NO_MEMORY; 145 } 146 res = catalogFile.Read(buf.get(), sz); 147 if (res < B_OK) { 148 fprintf(stderr, "couldn't read from catalog-file %s\n", path); 149 return res; 150 } 151 if (res < sz) { 152 fprintf(stderr, 153 "only got %lu instead of %Lu bytes from catalog-file %s\n", res, sz, 154 path); 155 return res; 156 } 157 BMemoryIO memIO(buf.get(), sz); 158 res = Unflatten(&memIO); 159 160 if (res == B_OK) { 161 // some information living in member variables needs to be copied 162 // to attributes. Although these attributes should have been written 163 // when creating the catalog, we make sure that they exist there: 164 UpdateAttributes(catalogFile); 165 } 166 167 return res; 168 } 169 170 171 /* 172 * this method is not currently being used, but it may be useful in the 173 * future... 174 */ 175 status_t 176 DefaultCatalog::ReadFromAttribute(entry_ref *appOrAddOnRef) 177 { 178 return B_NOT_SUPPORTED; 179 } 180 181 182 status_t 183 DefaultCatalog::ReadFromResource(entry_ref *appOrAddOnRef) 184 { 185 return B_NOT_SUPPORTED; 186 } 187 188 189 status_t 190 DefaultCatalog::WriteToFile(const char *path) 191 { 192 BFile catalogFile; 193 if (path) 194 fPath = path; 195 status_t res = catalogFile.SetTo(fPath.String(), 196 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 197 if (res != B_OK) 198 return res; 199 200 BMallocIO mallocIO; 201 mallocIO.SetBlockSize(max(fCatMap.Size()*20, 256L)); 202 // set a largish block-size in order to avoid reallocs 203 res = Flatten(&mallocIO); 204 if (res == B_OK) { 205 ssize_t wsz; 206 wsz = catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength()); 207 if (wsz != (ssize_t)mallocIO.BufferLength()) 208 return B_FILE_ERROR; 209 210 // set mimetype-, language- and signature-attributes: 211 UpdateAttributes(catalogFile); 212 } 213 if (res == B_OK) 214 UpdateAttributes(catalogFile); 215 return res; 216 } 217 218 219 /* 220 * this method is not currently being used, but it may be useful in the 221 * future... 222 */ 223 status_t 224 DefaultCatalog::WriteToAttribute(entry_ref *appOrAddOnRef) 225 { 226 return B_NOT_SUPPORTED; 227 } 228 229 230 status_t 231 DefaultCatalog::WriteToResource(entry_ref *appOrAddOnRef) 232 { 233 return B_NOT_SUPPORTED; 234 } 235 236 237 /* 238 * writes mimetype, language-name and signature of catalog into the 239 * catalog-file. 240 */ 241 void 242 DefaultCatalog::UpdateAttributes(BFile& catalogFile) 243 { 244 static const int bufSize = 256; 245 char buf[bufSize]; 246 uint32 temp; 247 if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf, 248 bufSize) <= 0 249 || strcmp(kCatMimeType, buf) != 0) { 250 catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, 251 kCatMimeType, strlen(kCatMimeType)+1); 252 } 253 if (catalogFile.ReadAttr(kCatLangAttr, B_STRING_TYPE, 0, 254 &buf, bufSize) <= 0 255 || fLanguageName != buf) { 256 catalogFile.WriteAttr(kCatLangAttr, B_STRING_TYPE, 0, 257 fLanguageName.String(), fLanguageName.Length()+1); 258 } 259 if (catalogFile.ReadAttr(kCatSigAttr, B_STRING_TYPE, 0, 260 &buf, bufSize) <= 0 261 || fSignature != buf) { 262 catalogFile.WriteAttr(kCatSigAttr, B_STRING_TYPE, 0, 263 fSignature.String(), fSignature.Length()+1); 264 } 265 if (catalogFile.ReadAttr(kCatFingerprintAttr, B_UINT32_TYPE, 266 0, &temp, sizeof(uint32)) <= 0) { 267 catalogFile.WriteAttr(kCatFingerprintAttr, B_UINT32_TYPE, 268 0, &fFingerprint, sizeof(uint32)); 269 } 270 } 271 272 273 status_t 274 DefaultCatalog::Flatten(BDataIO *dataIO) 275 { 276 UpdateFingerprint(); 277 // make sure we have the correct fingerprint before we flatten it 278 279 status_t res; 280 BMessage archive; 281 uint32 count = fCatMap.Size(); 282 res = archive.AddString("class", "DefaultCatalog"); 283 if (res == B_OK) 284 res = archive.AddInt32("c:sz", count); 285 if (res == B_OK) 286 res = archive.AddInt16("c:ver", kCatArchiveVersion); 287 if (res == B_OK) 288 res = archive.AddString("c:lang", fLanguageName.String()); 289 if (res == B_OK) 290 res = archive.AddString("c:sig", fSignature.String()); 291 if (res == B_OK) 292 res = archive.AddInt32("c:fpr", fFingerprint); 293 if (res == B_OK) 294 res = archive.Flatten(dataIO); 295 296 CatMap::Iterator iter = fCatMap.GetIterator(); 297 CatMap::Entry entry; 298 while (res == B_OK && iter.HasNext()) { 299 entry = iter.Next(); 300 archive.MakeEmpty(); 301 res = archive.AddString("c:ostr", entry.key.fString.String()); 302 if (res == B_OK) 303 res = archive.AddString("c:ctxt", entry.key.fContext.String()); 304 if (res == B_OK) 305 res = archive.AddString("c:comt", entry.key.fComment.String()); 306 if (res == B_OK) 307 res = archive.AddInt32("c:hash", entry.key.fHashVal); 308 if (res == B_OK) 309 res = archive.AddString("c:tstr", entry.value.String()); 310 if (res == B_OK) 311 res = archive.Flatten(dataIO); 312 } 313 return res; 314 } 315 316 317 status_t 318 DefaultCatalog::Unflatten(BDataIO *dataIO) 319 { 320 fCatMap.Clear(); 321 int32 count = 0; 322 int16 version; 323 BMessage archiveMsg; 324 status_t res = archiveMsg.Unflatten(dataIO); 325 326 if (res == B_OK) { 327 res = archiveMsg.FindInt16("c:ver", &version) 328 || archiveMsg.FindInt32("c:sz", &count); 329 } 330 if (res == B_OK) { 331 fLanguageName = archiveMsg.FindString("c:lang"); 332 fSignature = archiveMsg.FindString("c:sig"); 333 uint32 foundFingerprint = archiveMsg.FindInt32("c:fpr"); 334 335 // if a specific fingerprint has been requested and the catalog does in 336 // fact have a fingerprint, both are compared. If they mismatch, we do 337 // not accept this catalog: 338 if (foundFingerprint != 0 && fFingerprint != 0 339 && foundFingerprint != fFingerprint) { 340 fprintf(stderr, "default-catalog(sig=%s, lang=%s) " 341 "has mismatching fingerprint (%ld instead of the requested %ld)" 342 ", so this catalog is skipped.\n", 343 fSignature.String(), fLanguageName.String(), foundFingerprint, 344 fFingerprint); 345 res = B_MISMATCHED_VALUES; 346 } else 347 fFingerprint = foundFingerprint; 348 } 349 350 if (res == B_OK && count > 0) { 351 CatKey key; 352 const char *keyStr; 353 const char *keyCtx; 354 const char *keyCmt; 355 const char *translated; 356 357 // fCatMap.resize(count); 358 // There is no resize method in Haiku Hash Map to prealloc space 359 for (int i=0; res == B_OK && i < count; ++i) { 360 res = archiveMsg.Unflatten(dataIO); 361 if (res == B_OK) 362 res = archiveMsg.FindString("c:ostr", &keyStr); 363 if (res == B_OK) 364 res = archiveMsg.FindString("c:ctxt", &keyCtx); 365 if (res == B_OK) 366 res = archiveMsg.FindString("c:comt", &keyCmt); 367 if (res == B_OK) 368 res = archiveMsg.FindInt32("c:hash", (int32*)&key.fHashVal); 369 if (res == B_OK) 370 res = archiveMsg.FindString("c:tstr", &translated); 371 if (res == B_OK) { 372 key.fString = keyStr; 373 key.fContext = keyCtx; 374 key.fComment = keyCmt; 375 fCatMap.Put(key, translated); 376 } 377 } 378 uint32 checkFP = ComputeFingerprint(); 379 if (fFingerprint != checkFP) { 380 fprintf(stderr, "default-catalog(sig=%s, lang=%s) " 381 "has wrong fingerprint after load (%ld instead of the %ld). " 382 "The catalog data may be corrupted, so this catalog is " 383 "skipped.\n", 384 fSignature.String(), fLanguageName.String(), checkFP, 385 fFingerprint); 386 return B_BAD_DATA; 387 } 388 } 389 return res; 390 } 391 392 393 BCatalogAddOn * 394 DefaultCatalog::Instantiate(const char *signature, const char *language, 395 uint32 fingerprint) 396 { 397 DefaultCatalog *catalog 398 = new(std::nothrow) DefaultCatalog(signature, language, fingerprint); 399 if (catalog && catalog->InitCheck() != B_OK) { 400 delete catalog; 401 return NULL; 402 } 403 return catalog; 404 } 405 406 407 BCatalogAddOn * 408 DefaultCatalog::InstantiateEmbedded(entry_ref *appOrAddOnRef) 409 { 410 DefaultCatalog *catalog = new(std::nothrow) DefaultCatalog(appOrAddOnRef); 411 if (catalog && catalog->InitCheck() != B_OK) { 412 delete catalog; 413 return NULL; 414 } 415 return catalog; 416 } 417 418 419 BCatalogAddOn * 420 DefaultCatalog::Create(const char *signature, const char *language) 421 { 422 DefaultCatalog *catalog 423 = new(std::nothrow) DefaultCatalog("", signature, language); 424 if (catalog && catalog->InitCheck() != B_OK) { 425 delete catalog; 426 return NULL; 427 } 428 return catalog; 429 } 430 431 432 const uint8 DefaultCatalog::kDefaultCatalogAddOnPriority = 1; 433 // give highest priority to our embedded catalog-add-on 434