2 * Copyright 2003-2009, Haiku.
3 * Distributed under the terms of the MIT License.
6 * Oliver Tappe, zooey@hirschkaefer.de
7 * Adrien Destugues, pulkomandy@gmail.com
14 #include <AppFileInfo.h>
15 #include <Application.h>
17 #include <Directory.h>
19 #include <FindDirectory.h>
24 #include <Resources.h>
27 #include <DefaultCatalog.h>
28 #include <MutableLocaleRoster.h>
39 /*! This file implements the default catalog-type for the opentracker locale
40 kit. Alternatively, this could be used as a full add-on, but currently this
41 is provided as part of liblocale.so.
45 static const char *kCatFolder
= "catalogs";
46 static const char *kCatExtension
= ".catalog";
52 const char *DefaultCatalog::kCatMimeType
53 = "locale/x-vnd.Be.locale-catalog.default";
55 static int16 kCatArchiveVersion
= 1;
56 // version of the catalog archive structure, bump this if you change it!
58 const uint8
DefaultCatalog::kDefaultCatalogAddOnPriority
= 1;
59 // give highest priority to our embedded catalog-add-on
62 /*! Constructs a DefaultCatalog with given signature and language and reads
63 the catalog from disk.
64 InitCheck() will be B_OK if catalog could be loaded successfully, it will
65 give an appropriate error-code otherwise.
67 DefaultCatalog::DefaultCatalog(const entry_ref
&catalogOwner
, const char *language
,
70 HashMapCatalog("", language
, fingerprint
)
72 // We created the catalog with an invalid signature, but we fix that now.
73 SetSignature(catalogOwner
);
76 // search for catalog living in sub-folder of app's folder:
78 nref
.device
= catalogOwner
.device
;
79 nref
.node
= catalogOwner
.directory
;
80 BDirectory
appDir(&nref
);
81 BString
catalogName("locale/");
82 catalogName
<< kCatFolder
84 << "/" << fLanguageName
86 BPath
catalogPath(&appDir
, catalogName
.String());
87 status
= ReadFromFile(catalogPath
.Path());
90 // search in data folders
92 directory_which which
[] = {
93 B_USER_NONPACKAGED_DATA_DIRECTORY
,
94 B_USER_DATA_DIRECTORY
,
95 B_SYSTEM_NONPACKAGED_DATA_DIRECTORY
,
96 B_SYSTEM_DATA_DIRECTORY
99 for (size_t i
= 0; i
< sizeof(which
) / sizeof(which
[0]); i
++) {
101 if (find_directory(which
[i
], &path
) == B_OK
) {
102 BString
catalogName(path
.Path());
103 catalogName
<< "/locale/" << kCatFolder
105 << "/" << fLanguageName
107 status
= ReadFromFile(catalogName
.String());
114 if (status
!= B_OK
) {
115 // give lowest priority to catalog embedded as resource in application
116 // executable, so they can be overridden easily.
117 status
= ReadFromResource(catalogOwner
);
124 /*! Constructs a DefaultCatalog and reads it from the resources of the
125 given entry-ref (which usually is an app- or add-on-file).
126 InitCheck() will be B_OK if catalog could be loaded successfully, it will
127 give an appropriate error-code otherwise.
129 DefaultCatalog::DefaultCatalog(entry_ref
*appOrAddOnRef
)
131 HashMapCatalog("", "", 0)
133 fInitCheck
= ReadFromResource(*appOrAddOnRef
);
137 /*! Constructs an empty DefaultCatalog with given sig and language.
138 This is used for editing/testing purposes.
139 InitCheck() will always be B_OK.
141 DefaultCatalog::DefaultCatalog(const char *path
, const char *signature
,
142 const char *language
)
144 HashMapCatalog(signature
, language
, 0),
151 DefaultCatalog::~DefaultCatalog()
157 DefaultCatalog::SetSignature(const entry_ref
&catalogOwner
)
159 // figure out mimetype from image
160 BFile
objectFile(&catalogOwner
, B_READ_ONLY
);
161 BAppFileInfo
objectInfo(&objectFile
);
162 char objectSignature
[B_MIME_TYPE_LENGTH
];
163 if (objectInfo
.GetSignature(objectSignature
) != B_OK
) {
168 // drop supertype from mimetype (should be "application/"):
169 char* stripSignature
= objectSignature
;
170 while (*stripSignature
!= '/' && *stripSignature
!= '\0')
173 if (*stripSignature
== '\0')
174 stripSignature
= objectSignature
;
178 fSignature
= stripSignature
;
183 DefaultCatalog::SetRawString(const CatKey
& key
, const char *translated
)
185 return fCatMap
.Put(key
, translated
);
190 DefaultCatalog::ReadFromFile(const char *path
)
193 path
= fPath
.String();
196 status_t res
= catalogFile
.SetTo(path
, B_READ_ONLY
);
198 return B_ENTRY_NOT_FOUND
;
203 res
= catalogFile
.GetSize(&sz
);
208 auto_ptr
<char> buf(new(std::nothrow
) char [sz
]);
209 if (buf
.get() == NULL
)
211 res
= catalogFile
.Read(buf
.get(), sz
);
216 BMemoryIO
memIO(buf
.get(), sz
);
217 res
= Unflatten(&memIO
);
220 // some information living in member variables needs to be copied
221 // to attributes. Although these attributes should have been written
222 // when creating the catalog, we make sure that they exist there:
223 UpdateAttributes(catalogFile
);
231 * this method is not currently being used, but it may be useful in the future...
234 DefaultCatalog::ReadFromAttribute(const entry_ref
&appOrAddOnRef
)
237 status_t res
= node
.SetTo(&appOrAddOnRef
);
239 return B_ENTRY_NOT_FOUND
;
242 res
= node
.GetAttrInfo(BLocaleRoster::kEmbeddedCatAttr
, &attrInfo
);
244 return B_NAME_NOT_FOUND
;
245 if (attrInfo
.type
!= B_MESSAGE_TYPE
)
248 size_t size
= attrInfo
.size
;
249 auto_ptr
<char> buf(new(std::nothrow
) char [size
]);
250 if (buf
.get() == NULL
)
252 res
= node
.ReadAttr(BLocaleRoster::kEmbeddedCatAttr
, B_MESSAGE_TYPE
, 0,
254 if (res
< (ssize_t
)size
)
255 return res
< B_OK
? res
: B_BAD_DATA
;
257 BMemoryIO
memIO(buf
.get(), size
);
258 res
= Unflatten(&memIO
);
265 DefaultCatalog::ReadFromResource(const entry_ref
&appOrAddOnRef
)
268 status_t res
= file
.SetTo(&appOrAddOnRef
, B_READ_ONLY
);
270 return B_ENTRY_NOT_FOUND
;
273 res
= rsrc
.SetTo(&file
);
278 const void *buf
= rsrc
.LoadResource('CADA', fLanguageName
, &sz
);
280 return B_NAME_NOT_FOUND
;
282 BMemoryIO
memIO(buf
, sz
);
283 res
= Unflatten(&memIO
);
290 DefaultCatalog::WriteToFile(const char *path
)
295 status_t res
= catalogFile
.SetTo(fPath
.String(),
296 B_WRITE_ONLY
| B_CREATE_FILE
| B_ERASE_FILE
);
301 mallocIO
.SetBlockSize(max(fCatMap
.Size() * 20, (int32
)256));
302 // set a largish block-size in order to avoid reallocs
303 res
= Flatten(&mallocIO
);
306 wsz
= catalogFile
.Write(mallocIO
.Buffer(), mallocIO
.BufferLength());
307 if (wsz
!= (ssize_t
)mallocIO
.BufferLength())
310 // set mimetype-, language- and signature-attributes:
311 UpdateAttributes(catalogFile
);
314 UpdateAttributes(catalogFile
);
320 * this method is not currently being used, but it may be useful in the
324 DefaultCatalog::WriteToAttribute(const entry_ref
&appOrAddOnRef
)
327 status_t res
= node
.SetTo(&appOrAddOnRef
);
332 mallocIO
.SetBlockSize(max(fCatMap
.Size() * 20, (int32
)256));
333 // set a largish block-size in order to avoid reallocs
334 res
= Flatten(&mallocIO
);
338 wsz
= node
.WriteAttr(BLocaleRoster::kEmbeddedCatAttr
, B_MESSAGE_TYPE
, 0,
339 mallocIO
.Buffer(), mallocIO
.BufferLength());
342 else if (wsz
!= (ssize_t
)mallocIO
.BufferLength())
350 DefaultCatalog::WriteToResource(const entry_ref
&appOrAddOnRef
)
353 status_t res
= file
.SetTo(&appOrAddOnRef
, B_READ_WRITE
);
358 res
= rsrc
.SetTo(&file
);
363 mallocIO
.SetBlockSize(max(fCatMap
.Size() * 20, (int32
)256));
364 // set a largish block-size in order to avoid reallocs
365 res
= Flatten(&mallocIO
);
367 int mangledLanguage
= CatKey::HashFun(fLanguageName
.String(), 0);
370 res
= rsrc
.AddResource('CADA', mangledLanguage
,
371 mallocIO
.Buffer(), mallocIO
.BufferLength(),
372 BString(fLanguageName
));
379 /*! Writes mimetype, language-name and signature of catalog into the
383 DefaultCatalog::UpdateAttributes(BFile
& catalogFile
)
385 static const int bufSize
= 256;
388 if (catalogFile
.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE
, 0, &buf
,
390 || strcmp(kCatMimeType
, buf
) != 0) {
391 catalogFile
.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE
, 0,
392 kCatMimeType
, strlen(kCatMimeType
)+1);
394 if (catalogFile
.ReadAttr(BLocaleRoster::kCatLangAttr
, B_STRING_TYPE
, 0,
396 || fLanguageName
!= buf
) {
397 catalogFile
.WriteAttr(BLocaleRoster::kCatLangAttr
, B_STRING_TYPE
, 0,
398 fLanguageName
.String(), fLanguageName
.Length()+1);
400 if (catalogFile
.ReadAttr(BLocaleRoster::kCatSigAttr
, B_STRING_TYPE
, 0,
402 || fSignature
!= buf
) {
403 catalogFile
.WriteAttr(BLocaleRoster::kCatSigAttr
, B_STRING_TYPE
, 0,
404 fSignature
.String(), fSignature
.Length()+1);
406 if (catalogFile
.ReadAttr(BLocaleRoster::kCatFingerprintAttr
, B_UINT32_TYPE
,
407 0, &temp
, sizeof(uint32
)) <= 0) {
408 catalogFile
.WriteAttr(BLocaleRoster::kCatFingerprintAttr
, B_UINT32_TYPE
,
409 0, &fFingerprint
, sizeof(uint32
));
415 DefaultCatalog::Flatten(BDataIO
*dataIO
)
418 // make sure we have the correct fingerprint before we flatten it
422 int32 count
= fCatMap
.Size();
423 res
= archive
.AddString("class", "DefaultCatalog");
425 res
= archive
.AddInt32("c:sz", count
);
427 res
= archive
.AddInt16("c:ver", kCatArchiveVersion
);
429 res
= archive
.AddString("c:lang", fLanguageName
.String());
431 res
= archive
.AddString("c:sig", fSignature
.String());
433 res
= archive
.AddInt32("c:fpr", fFingerprint
);
435 res
= archive
.Flatten(dataIO
);
437 CatMap::Iterator iter
= fCatMap
.GetIterator();
439 while (res
== B_OK
&& iter
.HasNext()) {
442 res
= archive
.AddString("c:ostr", entry
.key
.fString
.String());
444 res
= archive
.AddString("c:ctxt", entry
.key
.fContext
.String());
446 res
= archive
.AddString("c:comt", entry
.key
.fComment
.String());
448 res
= archive
.AddInt32("c:hash", entry
.key
.fHashVal
);
450 res
= archive
.AddString("c:tstr", entry
.value
.String());
452 res
= archive
.Flatten(dataIO
);
460 DefaultCatalog::Unflatten(BDataIO
*dataIO
)
466 status_t res
= archiveMsg
.Unflatten(dataIO
);
469 res
= archiveMsg
.FindInt16("c:ver", &version
)
470 || archiveMsg
.FindInt32("c:sz", &count
);
473 fLanguageName
= archiveMsg
.FindString("c:lang");
474 fSignature
= archiveMsg
.FindString("c:sig");
475 uint32 foundFingerprint
= archiveMsg
.FindInt32("c:fpr");
477 // if a specific fingerprint has been requested and the catalog does in
478 // fact have a fingerprint, both are compared. If they mismatch, we do
479 // not accept this catalog:
480 if (foundFingerprint
!= 0 && fFingerprint
!= 0
481 && foundFingerprint
!= fFingerprint
) {
482 res
= B_MISMATCHED_VALUES
;
484 fFingerprint
= foundFingerprint
;
487 if (res
== B_OK
&& count
> 0) {
492 const char *translated
;
494 // fCatMap.resize(count);
495 // There is no resize method in Haiku's HashMap to preallocate
497 for (int i
=0; res
== B_OK
&& i
< count
; ++i
) {
498 res
= archiveMsg
.Unflatten(dataIO
);
500 res
= archiveMsg
.FindString("c:ostr", &keyStr
);
502 res
= archiveMsg
.FindString("c:ctxt", &keyCtx
);
504 res
= archiveMsg
.FindString("c:comt", &keyCmt
);
506 res
= archiveMsg
.FindInt32("c:hash", (int32
*)&key
.fHashVal
);
508 res
= archiveMsg
.FindString("c:tstr", &translated
);
510 key
.fString
= keyStr
;
511 key
.fContext
= keyCtx
;
512 key
.fComment
= keyCmt
;
513 fCatMap
.Put(key
, translated
);
516 uint32 checkFP
= ComputeFingerprint();
517 if (fFingerprint
!= checkFP
)
525 DefaultCatalog::Instantiate(const entry_ref
&catalogOwner
, const char *language
,
528 DefaultCatalog
*catalog
529 = new(std::nothrow
) DefaultCatalog(catalogOwner
, language
, fingerprint
);
530 if (catalog
&& catalog
->InitCheck() != B_OK
) {
539 DefaultCatalog::Create(const char *signature
, const char *language
)
541 DefaultCatalog
*catalog
542 = new(std::nothrow
) DefaultCatalog("", signature
, language
);
543 if (catalog
&& catalog
->InitCheck() != B_OK
) {
551 } // namespace BPrivate
555 default_catalog_get_available_languages(BMessage
* availableLanguages
,
556 const char* sigPattern
, const char* langPattern
, int32 fingerprint
)
558 if (availableLanguages
== NULL
|| sigPattern
== NULL
)
562 be_app
->GetAppInfo(&appInfo
);
564 nref
.device
= appInfo
.ref
.device
;
565 nref
.node
= appInfo
.ref
.directory
;
566 BDirectory
appDir(&nref
);
567 BString
catalogName("locale/");
568 catalogName
<< kCatFolder
569 << "/" << sigPattern
;
570 BPath
catalogPath(&appDir
, catalogName
.String());
571 BEntry
file(catalogPath
.Path());
572 BDirectory
dir(&file
);
574 char fileName
[B_FILE_NAME_LENGTH
];
575 while(dir
.GetNextEntry(&file
) == B_OK
) {
576 file
.GetName(fileName
);
577 BString
langName(fileName
);
578 langName
.Replace(kCatExtension
, "", 1);
579 availableLanguages
->AddString("language", langName
);
582 // search in data folders
584 directory_which which
[] = {
585 B_USER_NONPACKAGED_DATA_DIRECTORY
,
586 B_USER_DATA_DIRECTORY
,
587 B_SYSTEM_NONPACKAGED_DATA_DIRECTORY
,
588 B_SYSTEM_DATA_DIRECTORY
591 for (size_t i
= 0; i
< sizeof(which
) / sizeof(which
[0]); i
++) {
593 if (find_directory(which
[i
], &path
) == B_OK
) {
594 catalogName
= BString("locale/")
596 << "/" << sigPattern
;
598 BPath
catalogPath(path
.Path(), catalogName
.String());
599 BEntry
file(catalogPath
.Path());
600 BDirectory
dir(&file
);
602 char fileName
[B_FILE_NAME_LENGTH
];
603 while(dir
.GetNextEntry(&file
) == B_OK
) {
604 file
.GetName(fileName
);
605 BString
langName(fileName
);
606 langName
.Replace(kCatExtension
, "", 1);
607 availableLanguages
->AddString("language", langName
);