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 34using 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 43namespace BPrivate { 44 45 46// several attributes/resource-IDs used within the Locale Kit: 47 48const char *kCatLangAttr = "BEOS:LOCALE_LANGUAGE"; 49 // name of catalog language, lives in every catalog file 50const char *kCatSigAttr = "BEOS:LOCALE_SIGNATURE"; 51 // catalog signature, lives in every catalog file 52const char *kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT"; 53 // catalog fingerprint, may live in catalog file 54 55const char *DefaultCatalog::kCatMimeType 56 = "locale/x-vnd.Be.locale-catalog.default"; 57 58static int16 kCatArchiveVersion = 1; 59 // version of the catalog archive structure, bump this if you change it! 60 61const 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*/ 70DefaultCatalog::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*/ 87DefaultCatalog::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*/ 99DefaultCatalog::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 109DefaultCatalog::~DefaultCatalog() 110{ 111} 112 113 114void 115DefaultCatalog::SetSignature(const entry_ref &catalogOwner) 116{ 117 // Not allowed for the build-tool version. 118 return; 119} 120 121 122status_t 123DefaultCatalog::SetRawString(const CatKey& key, const char *translated) 124{ 125 return fCatMap.Put(key, translated); 126} 127 128 129status_t 130DefaultCatalog::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 184status_t 185DefaultCatalog::ReadFromResource(const entry_ref &appOrAddOnRef) 186{ 187 return B_NOT_SUPPORTED; 188} 189 190 191status_t 192DefaultCatalog::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 223status_t 224DefaultCatalog::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*/ 233void 234DefaultCatalog::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 265status_t 266DefaultCatalog::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 310status_t 311DefaultCatalog::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 387BCatalogData * 388DefaultCatalog::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 401BCatalogData * 402DefaultCatalog::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