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