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 %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 status_t 183 DefaultCatalog::ReadFromResource(const entry_ref &appOrAddOnRef) 184 { 185 return B_NOT_SUPPORTED; 186 } 187 188 189 status_t 190 DefaultCatalog::WriteToFile(const char *path) 191 { 192 BFile catalogFile; 193 if (path) 194 fPath = path; 195 status_t status = catalogFile.SetTo(fPath.String(), 196 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 197 if (status != B_OK) 198 return status; 199 200 BMallocIO mallocIO; 201 mallocIO.SetBlockSize(max_c(fCatMap.Size() * 20, 256)); 202 // set a largish block-size in order to avoid reallocs 203 status = Flatten(&mallocIO); 204 if (status != B_OK) 205 return status; 206 207 ssize_t bytesWritten 208 = catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength()); 209 if (bytesWritten < 0) 210 return bytesWritten; 211 if (bytesWritten != (ssize_t)mallocIO.BufferLength()) 212 return B_IO_ERROR; 213 214 // set mimetype-, language- and signature-attributes: 215 UpdateAttributes(catalogFile); 216 217 return B_OK; 218 } 219 220 221 status_t 222 DefaultCatalog::WriteToResource(const entry_ref &appOrAddOnRef) 223 { 224 return B_NOT_SUPPORTED; 225 } 226 227 228 /*! Writes mimetype, language-name and signature of catalog into the 229 catalog-file. 230 */ 231 void 232 DefaultCatalog::UpdateAttributes(BFile& catalogFile) 233 { 234 static const int bufSize = 256; 235 char buf[bufSize]; 236 uint32 temp; 237 if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf, 238 bufSize) <= 0 239 || strcmp(kCatMimeType, buf) != 0) { 240 catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, 241 kCatMimeType, strlen(kCatMimeType)+1); 242 } 243 if (catalogFile.ReadAttr(kCatLangAttr, B_STRING_TYPE, 0, 244 &buf, bufSize) <= 0 245 || fLanguageName != buf) { 246 catalogFile.WriteAttr(kCatLangAttr, B_STRING_TYPE, 0, 247 fLanguageName.String(), fLanguageName.Length()+1); 248 } 249 if (catalogFile.ReadAttr(kCatSigAttr, B_STRING_TYPE, 0, 250 &buf, bufSize) <= 0 251 || fSignature != buf) { 252 catalogFile.WriteAttr(kCatSigAttr, B_STRING_TYPE, 0, 253 fSignature.String(), fSignature.Length()+1); 254 } 255 if (catalogFile.ReadAttr(kCatFingerprintAttr, B_UINT32_TYPE, 256 0, &temp, sizeof(uint32)) <= 0) { 257 catalogFile.WriteAttr(kCatFingerprintAttr, B_UINT32_TYPE, 258 0, &fFingerprint, sizeof(uint32)); 259 } 260 } 261 262 263 status_t 264 DefaultCatalog::Flatten(BDataIO *dataIO) 265 { 266 UpdateFingerprint(); 267 // make sure we have the correct fingerprint before we flatten it 268 269 status_t res; 270 BMessage archive; 271 int32 count = fCatMap.Size(); 272 res = archive.AddString("class", "DefaultCatalog"); 273 if (res == B_OK) 274 res = archive.AddInt32("c:sz", count); 275 if (res == B_OK) 276 res = archive.AddInt16("c:ver", kCatArchiveVersion); 277 if (res == B_OK) 278 res = archive.AddString("c:lang", fLanguageName.String()); 279 if (res == B_OK) 280 res = archive.AddString("c:sig", fSignature.String()); 281 if (res == B_OK) 282 res = archive.AddInt32("c:fpr", fFingerprint); 283 if (res == B_OK) 284 res = archive.Flatten(dataIO); 285 286 CatMap::Iterator iter = fCatMap.GetIterator(); 287 CatMap::Entry entry; 288 while (res == B_OK && iter.HasNext()) { 289 entry = iter.Next(); 290 archive.MakeEmpty(); 291 res = archive.AddString("c:ostr", entry.key.fString.String()); 292 if (res == B_OK) 293 res = archive.AddString("c:ctxt", entry.key.fContext.String()); 294 if (res == B_OK) 295 res = archive.AddString("c:comt", entry.key.fComment.String()); 296 if (res == B_OK) 297 res = archive.AddInt32("c:hash", entry.key.fHashVal); 298 if (res == B_OK) 299 res = archive.AddString("c:tstr", entry.value.String()); 300 if (res == B_OK) 301 res = archive.Flatten(dataIO); 302 } 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 (%d instead of the requested %d)" 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's HashMap to preallocate 350 // memory. 351 for (int i=0; res == B_OK && i < count; ++i) { 352 res = archiveMsg.Unflatten(dataIO); 353 if (res == B_OK) 354 res = archiveMsg.FindString("c:ostr", &keyStr); 355 if (res == B_OK) 356 res = archiveMsg.FindString("c:ctxt", &keyCtx); 357 if (res == B_OK) 358 res = archiveMsg.FindString("c:comt", &keyCmt); 359 if (res == B_OK) 360 res = archiveMsg.FindInt32("c:hash", (int32*)&key.fHashVal); 361 if (res == B_OK) 362 res = archiveMsg.FindString("c:tstr", &translated); 363 if (res == B_OK) { 364 key.fString = keyStr; 365 key.fContext = keyCtx; 366 key.fComment = keyCmt; 367 fCatMap.Put(key, translated); 368 } 369 } 370 uint32 checkFP = ComputeFingerprint(); 371 if (fFingerprint != checkFP) { 372 fprintf(stderr, "default-catalog(sig=%s, lang=%s) " 373 "has wrong fingerprint after load (%d instead of %d). " 374 "The catalog data may be corrupted, so this catalog is " 375 "skipped.\n", 376 fSignature.String(), fLanguageName.String(), checkFP, 377 fFingerprint); 378 return B_BAD_DATA; 379 } 380 } 381 return res; 382 } 383 384 385 BCatalogData * 386 DefaultCatalog::Instantiate(const entry_ref &catalogOwner, const char *language, 387 uint32 fingerprint) 388 { 389 DefaultCatalog *catalog 390 = new(std::nothrow) DefaultCatalog(catalogOwner, language, fingerprint); 391 if (catalog && catalog->InitCheck() != B_OK) { 392 delete catalog; 393 return NULL; 394 } 395 return catalog; 396 } 397 398 399 BCatalogData * 400 DefaultCatalog::Create(const char *signature, const char *language) 401 { 402 DefaultCatalog *catalog 403 = new(std::nothrow) DefaultCatalog("", signature, language); 404 if (catalog && catalog->InitCheck() != B_OK) { 405 delete catalog; 406 return NULL; 407 } 408 return catalog; 409 } 410 411 412 } // namespace BPrivate 413