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 const uint8 DefaultCatalog::kDefaultCatalogAddOnPriority = 1; 62 // give highest priority to our embedded catalog-add-on 63 64 65 /*! Constructs a DefaultCatalog with given signature and language and reads 66 the catalog from disk. 67 InitCheck() will be B_OK if catalog could be loaded successfully, it will 68 give an appropriate error-code otherwise. 69 */ 70 DefaultCatalog::DefaultCatalog(const entry_ref &catalogOwner, 71 const char *language, uint32 fingerprint) 72 : 73 HashMapCatalog("", language, fingerprint) 74 { 75 fInitCheck = B_NOT_SUPPORTED; 76 fprintf(stderr, 77 "trying to load default-catalog(lang=%s) results in %s", 78 language, strerror(fInitCheck)); 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 HashMapCatalog("", "", 0) 90 { 91 fInitCheck = ReadFromResource(*appOrAddOnRef); 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 %" B_PRIdOFF " chars\n", 155 sz); 156 return B_NO_MEMORY; 157 } 158 res = catalogFile.Read(buf, 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 %" B_PRIdOFF " bytes " 166 "from catalog-file %s\n", 167 res, sz, path); 168 return res; 169 } 170 BMemoryIO memIO(buf, sz); 171 res = Unflatten(&memIO); 172 173 if (res == B_OK) { 174 // some information living in member variables needs to be copied 175 // to attributes. Although these attributes should have been written 176 // when creating the catalog, we make sure that they exist there: 177 UpdateAttributes(catalogFile); 178 } 179 180 return res; 181 } 182 183 184 status_t 185 DefaultCatalog::ReadFromResource(const entry_ref &appOrAddOnRef) 186 { 187 return B_NOT_SUPPORTED; 188 } 189 190 191 status_t 192 DefaultCatalog::WriteToFile(const char *path) 193 { 194 BFile catalogFile; 195 if (path) 196 fPath = path; 197 status_t status = catalogFile.SetTo(fPath.String(), 198 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 199 if (status != B_OK) 200 return status; 201 202 BMallocIO mallocIO; 203 mallocIO.SetBlockSize(max_c(fCatMap.Size() * 20, 256)); 204 // set a largish block-size in order to avoid reallocs 205 status = Flatten(&mallocIO); 206 if (status != B_OK) 207 return status; 208 209 ssize_t bytesWritten 210 = catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength()); 211 if (bytesWritten < 0) 212 return bytesWritten; 213 if (bytesWritten != (ssize_t)mallocIO.BufferLength()) 214 return B_IO_ERROR; 215 216 // set mimetype-, language- and signature-attributes: 217 UpdateAttributes(catalogFile); 218 219 return B_OK; 220 } 221 222 223 status_t 224 DefaultCatalog::WriteToResource(const entry_ref &appOrAddOnRef) 225 { 226 return B_NOT_SUPPORTED; 227 } 228 229 230 /*! Writes mimetype, language-name and signature of catalog into the 231 catalog-file. 232 */ 233 void 234 DefaultCatalog::UpdateAttributes(BFile& catalogFile) 235 { 236 static const int bufSize = 256; 237 char buf[bufSize]; 238 uint32 temp; 239 if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf, 240 bufSize) <= 0 241 || strcmp(kCatMimeType, buf) != 0) { 242 catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, 243 kCatMimeType, strlen(kCatMimeType)+1); 244 } 245 if (catalogFile.ReadAttr(kCatLangAttr, B_STRING_TYPE, 0, 246 &buf, bufSize) <= 0 247 || fLanguageName != buf) { 248 catalogFile.WriteAttr(kCatLangAttr, B_STRING_TYPE, 0, 249 fLanguageName.String(), fLanguageName.Length()+1); 250 } 251 if (catalogFile.ReadAttr(kCatSigAttr, B_STRING_TYPE, 0, 252 &buf, bufSize) <= 0 253 || fSignature != buf) { 254 catalogFile.WriteAttr(kCatSigAttr, B_STRING_TYPE, 0, 255 fSignature.String(), fSignature.Length()+1); 256 } 257 if (catalogFile.ReadAttr(kCatFingerprintAttr, B_UINT32_TYPE, 258 0, &temp, sizeof(uint32)) <= 0) { 259 catalogFile.WriteAttr(kCatFingerprintAttr, B_UINT32_TYPE, 260 0, &fFingerprint, sizeof(uint32)); 261 } 262 } 263 264 265 status_t 266 DefaultCatalog::Flatten(BDataIO *dataIO) 267 { 268 UpdateFingerprint(); 269 // make sure we have the correct fingerprint before we flatten it 270 271 status_t res; 272 BMessage archive; 273 int32 count = fCatMap.Size(); 274 res = archive.AddString("class", "DefaultCatalog"); 275 if (res == B_OK) 276 res = archive.AddInt32("c:sz", count); 277 if (res == B_OK) 278 res = archive.AddInt16("c:ver", kCatArchiveVersion); 279 if (res == B_OK) 280 res = archive.AddString("c:lang", fLanguageName.String()); 281 if (res == B_OK) 282 res = archive.AddString("c:sig", fSignature.String()); 283 if (res == B_OK) 284 res = archive.AddInt32("c:fpr", fFingerprint); 285 if (res == B_OK) 286 res = archive.Flatten(dataIO); 287 288 CatMap::Iterator iter = fCatMap.GetIterator(); 289 CatMap::Entry entry; 290 while (res == B_OK && iter.HasNext()) { 291 entry = iter.Next(); 292 archive.MakeEmpty(); 293 res = archive.AddString("c:ostr", entry.key.fString.String()); 294 if (res == B_OK) 295 res = archive.AddString("c:ctxt", entry.key.fContext.String()); 296 if (res == B_OK) 297 res = archive.AddString("c:comt", entry.key.fComment.String()); 298 if (res == B_OK) 299 res = archive.AddInt32("c:hash", entry.key.fHashVal); 300 if (res == B_OK) 301 res = archive.AddString("c:tstr", entry.value.String()); 302 if (res == B_OK) 303 res = archive.Flatten(dataIO); 304 } 305 306 return res; 307 } 308 309 310 status_t 311 DefaultCatalog::Unflatten(BDataIO *dataIO) 312 { 313 fCatMap.Clear(); 314 int32 count = 0; 315 int16 version; 316 BMessage archiveMsg; 317 status_t res = archiveMsg.Unflatten(dataIO); 318 319 if (res == B_OK) { 320 res = archiveMsg.FindInt16("c:ver", &version) 321 || archiveMsg.FindInt32("c:sz", &count); 322 } 323 if (res == B_OK) { 324 fLanguageName = archiveMsg.FindString("c:lang"); 325 fSignature = archiveMsg.FindString("c:sig"); 326 uint32 foundFingerprint = archiveMsg.FindInt32("c:fpr"); 327 328 // if a specific fingerprint has been requested and the catalog does in 329 // fact have a fingerprint, both are compared. If they mismatch, we do 330 // not accept this catalog: 331 if (foundFingerprint != 0 && fFingerprint != 0 332 && foundFingerprint != fFingerprint) { 333 fprintf(stderr, "default-catalog(sig=%s, lang=%s) " 334 "has mismatching fingerprint (%d instead of the requested %d)" 335 ", so this catalog is skipped.\n", 336 fSignature.String(), fLanguageName.String(), foundFingerprint, 337 fFingerprint); 338 res = B_MISMATCHED_VALUES; 339 } else 340 fFingerprint = foundFingerprint; 341 } 342 343 if (res == B_OK && count > 0) { 344 CatKey key; 345 const char *keyStr; 346 const char *keyCtx; 347 const char *keyCmt; 348 const char *translated; 349 350 // fCatMap.resize(count); 351 // There is no resize method in Haiku's HashMap to preallocate 352 // memory. 353 for (int i=0; res == B_OK && i < count; ++i) { 354 res = archiveMsg.Unflatten(dataIO); 355 if (res == B_OK) 356 res = archiveMsg.FindString("c:ostr", &keyStr); 357 if (res == B_OK) 358 res = archiveMsg.FindString("c:ctxt", &keyCtx); 359 if (res == B_OK) 360 res = archiveMsg.FindString("c:comt", &keyCmt); 361 if (res == B_OK) 362 res = archiveMsg.FindInt32("c:hash", (int32*)&key.fHashVal); 363 if (res == B_OK) 364 res = archiveMsg.FindString("c:tstr", &translated); 365 if (res == B_OK) { 366 key.fString = keyStr; 367 key.fContext = keyCtx; 368 key.fComment = keyCmt; 369 fCatMap.Put(key, translated); 370 } 371 } 372 uint32 checkFP = ComputeFingerprint(); 373 if (fFingerprint != checkFP) { 374 fprintf(stderr, "default-catalog(sig=%s, lang=%s) " 375 "has wrong fingerprint after load (%d instead of %d). " 376 "The catalog data may be corrupted, so this catalog is " 377 "skipped.\n", 378 fSignature.String(), fLanguageName.String(), checkFP, 379 fFingerprint); 380 return B_BAD_DATA; 381 } 382 } 383 return res; 384 } 385 386 387 BCatalogData * 388 DefaultCatalog::Instantiate(const entry_ref &catalogOwner, const char *language, 389 uint32 fingerprint) 390 { 391 DefaultCatalog *catalog 392 = new(std::nothrow) DefaultCatalog(catalogOwner, language, fingerprint); 393 if (catalog && catalog->InitCheck() != B_OK) { 394 delete catalog; 395 return NULL; 396 } 397 return catalog; 398 } 399 400 401 BCatalogData * 402 DefaultCatalog::Create(const char *signature, const char *language) 403 { 404 DefaultCatalog *catalog 405 = new(std::nothrow) DefaultCatalog("", signature, language); 406 if (catalog && catalog->InitCheck() != B_OK) { 407 delete catalog; 408 return NULL; 409 } 410 return catalog; 411 } 412 413 414 } // namespace BPrivate 415