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