* Copyright 2003-2009, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Oliver Tappe, zooey@hirschkaefer.de
* Adrien Destugues, pulkomandy@gmail.com
*/
#include <algorithm>
#include <new>
#include <AppFileInfo.h>
#include <Application.h>
#include <DataIO.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <fs_attr.h>
#include <Message.h>
#include <Mime.h>
#include <Path.h>
#include <Resources.h>
#include <Roster.h>
#include <StackOrHeapArray.h>
#include <DefaultCatalog.h>
#include <MutableLocaleRoster.h>
#include <cstdio>
using std::min;
using std::max;
using std::pair;
kit. Alternatively, this could be used as a full add-on, but currently this
is provided as part of liblocale.so.
*/
static const char *kCatFolder = "catalogs";
static const char *kCatExtension = ".catalog";
namespace BPrivate {
const char *DefaultCatalog::kCatMimeType
= "locale/x-vnd.Be.locale-catalog.default";
static int16 kCatArchiveVersion = 1;
const uint8 DefaultCatalog::kDefaultCatalogAddOnPriority = 1;
the catalog from disk.
InitCheck() will be B_OK if catalog could be loaded successfully, it will
give an appropriate error-code otherwise.
*/
DefaultCatalog::DefaultCatalog(const entry_ref &catalogOwner,
const char *language, uint32 fingerprint)
:
HashMapCatalog("", language, fingerprint)
{
SetSignature(catalogOwner);
status_t status;
node_ref nref;
nref.device = catalogOwner.device;
nref.node = catalogOwner.directory;
BDirectory appDir(&nref);
BString catalogName("locale/");
catalogName << kCatFolder
<< "/" << fSignature
<< "/" << fLanguageName
<< kCatExtension;
BPath catalogPath(&appDir, catalogName.String());
status = ReadFromFile(catalogPath.Path());
if (status != B_OK)
status = ReadFromStandardLocations();
if (status != B_OK) {
status = ReadFromResource(catalogOwner);
}
fInitCheck = status;
}
given entry-ref (which usually is an app- or add-on-file).
InitCheck() will be B_OK if catalog could be loaded successfully, it will
give an appropriate error-code otherwise.
*/
DefaultCatalog::DefaultCatalog(entry_ref *appOrAddOnRef)
:
HashMapCatalog("", "", 0)
{
fInitCheck = ReadFromResource(*appOrAddOnRef);
}
This is used for editing/testing purposes.
InitCheck() will always be B_OK.
*/
DefaultCatalog::DefaultCatalog(const char *path, const char *signature,
const char *language)
:
HashMapCatalog(signature, language, 0),
fPath(path)
{
fInitCheck = B_OK;
}
DefaultCatalog::~DefaultCatalog()
{
}
void
DefaultCatalog::SetSignature(const entry_ref &catalogOwner)
{
BFile objectFile(&catalogOwner, B_READ_ONLY);
BAppFileInfo objectInfo(&objectFile);
char objectSignature[B_MIME_TYPE_LENGTH];
if (objectInfo.GetSignature(objectSignature) != B_OK) {
fSignature = "";
return;
}
char* stripSignature = objectSignature;
while (*stripSignature != '/' && *stripSignature != '\0')
stripSignature ++;
if (*stripSignature == '\0')
stripSignature = objectSignature;
else
stripSignature ++;
fSignature = stripSignature;
}
status_t
DefaultCatalog::SetRawString(const CatKey& key, const char *translated)
{
return fCatMap.Put(key, translated);
}
status_t
DefaultCatalog::ReadFromStandardLocations()
{
directory_which which[] = {
B_USER_NONPACKAGED_DATA_DIRECTORY,
B_USER_DATA_DIRECTORY,
B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
B_SYSTEM_DATA_DIRECTORY
};
status_t status = B_ENTRY_NOT_FOUND;
for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
BPath path;
if (find_directory(which[i], &path) == B_OK) {
BString catalogName(path.Path());
catalogName << "/locale/" << kCatFolder
<< "/" << fSignature
<< "/" << fLanguageName
<< kCatExtension;
status = ReadFromFile(catalogName.String());
if (status == B_OK)
break;
}
}
return status;
}
status_t
DefaultCatalog::ReadFromFile(const char *path)
{
if (!path)
path = fPath.String();
BFile catalogFile;
status_t res = catalogFile.SetTo(path, B_READ_ONLY);
if (res != B_OK)
return B_ENTRY_NOT_FOUND;
fPath = path;
off_t sz = 0;
res = catalogFile.GetSize(&sz);
if (res != B_OK) {
return res;
}
BStackOrHeapArray<char, 0> buf(sz);
if (!buf.IsValid())
return B_NO_MEMORY;
res = catalogFile.Read(buf, sz);
if (res < B_OK)
return res;
if (res < sz)
return res;
BMemoryIO memIO(buf, sz);
res = Unflatten(&memIO);
if (res == B_OK) {
UpdateAttributes(catalogFile);
}
return res;
}
status_t
DefaultCatalog::ReadFromResource(const entry_ref &appOrAddOnRef)
{
BFile file;
status_t res = file.SetTo(&appOrAddOnRef, B_READ_ONLY);
if (res != B_OK)
return B_ENTRY_NOT_FOUND;
BResources rsrc;
res = rsrc.SetTo(&file);
if (res != B_OK)
return res;
size_t sz;
const void *buf = rsrc.LoadResource('CADA', fLanguageName, &sz);
if (!buf)
return B_NAME_NOT_FOUND;
BMemoryIO memIO(buf, sz);
res = Unflatten(&memIO);
return res;
}
status_t
DefaultCatalog::WriteToFile(const char *path)
{
BFile catalogFile;
if (path)
fPath = path;
status_t res = catalogFile.SetTo(fPath.String(),
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
if (res != B_OK)
return res;
BMallocIO mallocIO;
mallocIO.SetBlockSize(max(fCatMap.Size() * 20, (int32)256));
res = Flatten(&mallocIO);
if (res == B_OK) {
ssize_t wsz;
wsz = catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength());
if (wsz != (ssize_t)mallocIO.BufferLength())
return B_FILE_ERROR;
UpdateAttributes(catalogFile);
}
if (res == B_OK)
UpdateAttributes(catalogFile);
return res;
}
status_t
DefaultCatalog::WriteToResource(const entry_ref &appOrAddOnRef)
{
BFile file;
status_t res = file.SetTo(&appOrAddOnRef, B_READ_WRITE);
if (res != B_OK)
return res;
BResources rsrc;
res = rsrc.SetTo(&file);
if (res != B_OK)
return res;
BMallocIO mallocIO;
mallocIO.SetBlockSize(max(fCatMap.Size() * 20, (int32)256));
res = Flatten(&mallocIO);
int mangledLanguage = CatKey::HashFun(fLanguageName.String(), 0);
if (res == B_OK) {
res = rsrc.AddResource('CADA', mangledLanguage,
mallocIO.Buffer(), mallocIO.BufferLength(),
BString(fLanguageName));
}
return res;
}
catalog-file.
*/
void
DefaultCatalog::UpdateAttributes(BFile& catalogFile)
{
static const int bufSize = 256;
char buf[bufSize];
uint32 temp;
if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf,
bufSize) <= 0
|| strcmp(kCatMimeType, buf) != 0) {
catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
kCatMimeType, strlen(kCatMimeType)+1);
}
if (catalogFile.ReadAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
&buf, bufSize) <= 0
|| fLanguageName != buf) {
catalogFile.WriteAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
fLanguageName.String(), fLanguageName.Length()+1);
}
if (catalogFile.ReadAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
&buf, bufSize) <= 0
|| fSignature != buf) {
catalogFile.WriteAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
fSignature.String(), fSignature.Length()+1);
}
if (catalogFile.ReadAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
0, &temp, sizeof(uint32)) <= 0) {
catalogFile.WriteAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
0, &fFingerprint, sizeof(uint32));
}
}
status_t
DefaultCatalog::Flatten(BDataIO *dataIO)
{
UpdateFingerprint();
status_t res;
BMessage archive;
int32 count = fCatMap.Size();
res = archive.AddString("class", "DefaultCatalog");
if (res == B_OK)
res = archive.AddInt32("c:sz", count);
if (res == B_OK)
res = archive.AddInt16("c:ver", kCatArchiveVersion);
if (res == B_OK)
res = archive.AddString("c:lang", fLanguageName.String());
if (res == B_OK)
res = archive.AddString("c:sig", fSignature.String());
if (res == B_OK)
res = archive.AddInt32("c:fpr", fFingerprint);
if (res == B_OK)
res = archive.Flatten(dataIO);
CatMap::Iterator iter = fCatMap.GetIterator();
CatMap::Entry entry;
while (res == B_OK && iter.HasNext()) {
entry = iter.Next();
archive.MakeEmpty();
res = archive.AddString("c:ostr", entry.key.fString.String());
if (res == B_OK)
res = archive.AddString("c:ctxt", entry.key.fContext.String());
if (res == B_OK)
res = archive.AddString("c:comt", entry.key.fComment.String());
if (res == B_OK)
res = archive.AddInt32("c:hash", entry.key.fHashVal);
if (res == B_OK)
res = archive.AddString("c:tstr", entry.value.String());
if (res == B_OK)
res = archive.Flatten(dataIO);
}
return res;
}
status_t
DefaultCatalog::Unflatten(BDataIO *dataIO)
{
fCatMap.Clear();
int32 count = 0;
int16 version;
BMessage archiveMsg;
status_t res = archiveMsg.Unflatten(dataIO);
if (res == B_OK) {
res = archiveMsg.FindInt16("c:ver", &version)
|| archiveMsg.FindInt32("c:sz", &count);
}
if (res == B_OK) {
fLanguageName = archiveMsg.FindString("c:lang");
fSignature = archiveMsg.FindString("c:sig");
uint32 foundFingerprint = archiveMsg.FindInt32("c:fpr");
if (foundFingerprint != 0 && fFingerprint != 0
&& foundFingerprint != fFingerprint) {
res = B_MISMATCHED_VALUES;
} else
fFingerprint = foundFingerprint;
}
if (res == B_OK && count > 0) {
CatKey key;
const char *keyStr;
const char *keyCtx;
const char *keyCmt;
const char *translated;
for (int i=0; res == B_OK && i < count; ++i) {
res = archiveMsg.Unflatten(dataIO);
if (res == B_OK)
res = archiveMsg.FindString("c:ostr", &keyStr);
if (res == B_OK)
res = archiveMsg.FindString("c:ctxt", &keyCtx);
if (res == B_OK)
res = archiveMsg.FindString("c:comt", &keyCmt);
if (res == B_OK)
res = archiveMsg.FindInt32("c:hash", (int32*)&key.fHashVal);
if (res == B_OK)
res = archiveMsg.FindString("c:tstr", &translated);
if (res == B_OK) {
key.fString = keyStr;
key.fContext = keyCtx;
key.fComment = keyCmt;
fCatMap.Put(key, translated);
}
}
uint32 checkFP = ComputeFingerprint();
if (fFingerprint != checkFP)
return B_BAD_DATA;
}
return res;
}
BCatalogData *
DefaultCatalog::Instantiate(const entry_ref &catalogOwner, const char *language,
uint32 fingerprint)
{
DefaultCatalog *catalog
= new(std::nothrow) DefaultCatalog(catalogOwner, language, fingerprint);
if (catalog && catalog->InitCheck() != B_OK) {
delete catalog;
return NULL;
}
return catalog;
}
BCatalogData *
DefaultCatalog::Create(const char *signature, const char *language)
{
DefaultCatalog *catalog
= new(std::nothrow) DefaultCatalog("", signature, language);
if (catalog && catalog->InitCheck() != B_OK) {
delete catalog;
return NULL;
}
return catalog;
}
}
extern "C" status_t
default_catalog_get_available_languages(BMessage* availableLanguages,
const char* sigPattern, const char* langPattern, int32 fingerprint)
{
if (availableLanguages == NULL || sigPattern == NULL)
return B_BAD_DATA;
app_info appInfo;
be_app->GetAppInfo(&appInfo);
node_ref nref;
nref.device = appInfo.ref.device;
nref.node = appInfo.ref.directory;
BDirectory appDir(&nref);
BString catalogName("locale/");
catalogName << kCatFolder
<< "/" << sigPattern ;
BPath catalogPath(&appDir, catalogName.String());
BEntry file(catalogPath.Path());
BDirectory dir(&file);
char fileName[B_FILE_NAME_LENGTH];
while(dir.GetNextEntry(&file) == B_OK) {
file.GetName(fileName);
BString langName(fileName);
langName.Replace(kCatExtension, "", 1);
availableLanguages->AddString("language", langName);
}
directory_which which[] = {
B_USER_NONPACKAGED_DATA_DIRECTORY,
B_USER_DATA_DIRECTORY,
B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
B_SYSTEM_DATA_DIRECTORY
};
for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
BPath path;
if (find_directory(which[i], &path) == B_OK) {
catalogName = BString("locale/")
<< kCatFolder
<< "/" << sigPattern;
BPath catalogPath(path.Path(), catalogName.String());
BEntry file(catalogPath.Path());
BDirectory dir(&file);
char fileName[B_FILE_NAME_LENGTH];
while(dir.GetNextEntry(&file) == B_OK) {
file.GetName(fileName);
BString langName(fileName);
langName.Replace(kCatExtension, "", 1);
availableLanguages->AddString("language", langName);
}
}
}
return B_OK;
}