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