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