* Copyright 2009 Adrien Destugues, pulkomandy@gmail.com. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <PlainTextCatalog.h>
#include <assert.h>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <new>
#include <sstream>
#include <string>
#include <AppFileInfo.h>
#include <Application.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 <String.h>
#include <LocaleRoster.h>
#include <Catalog.h>
using BPrivate::HashMapCatalog;
using BPrivate::PlainTextCatalog;
using std::min;
using std::max;
using std::pair;
* This file implements the plain text catalog-type for the Haiku
* locale kit. It is not meant to be used in applications like other add ons,
* but only as a way to get an easy to translate file for developers.
*/
extern "C" uint32 adler32(uint32 adler, const uint8 *buf, uint32 len);
static const char *kCatFolder = "catalogs";
static const char *kCatExtension = ".catkeys";
const char *PlainTextCatalog::kCatMimeType
= "locale/x-vnd.Be.locale-catalog.plaintext";
static int16 kCatArchiveVersion = 1;
void
escapeQuotedChars(BString& stringToEscape)
{
stringToEscape.ReplaceAll("\\","\\\\");
stringToEscape.ReplaceAll("\n","\\n");
stringToEscape.ReplaceAll("\t","\\t");
stringToEscape.ReplaceAll("\"","\\\"");
}
* constructs a PlainTextCatalog with given signature and language and reads
* the catalog from disk.
* InitCheck() will be B_OK if catalog could be loaded successfully, it will
* give an appropriate error-code otherwise.
*/
PlainTextCatalog::PlainTextCatalog(const entry_ref &owner, const char *language,
uint32 fingerprint)
:
HashMapCatalog("", language, fingerprint)
{
SetSignature(owner);
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
<< "/" << fSignature
<< "/" << fLanguageName
<< kCatExtension;
BPath catalogPath(&appDir, catalogName.String());
status_t status = ReadFromFile(catalogPath.Path());
if (status != B_OK) {
BPath commonEtcPath;
find_directory(B_SYSTEM_ETC_DIRECTORY, &commonEtcPath);
if (commonEtcPath.InitCheck() == B_OK) {
catalogName = BString(commonEtcPath.Path())
<< "/locale/" << kCatFolder
<< "/" << fSignature
<< "/" << fLanguageName
<< kCatExtension;
status = ReadFromFile(catalogName.String());
}
}
if (status != B_OK) {
BPath systemEtcPath;
find_directory(B_BEOS_ETC_DIRECTORY, &systemEtcPath);
if (systemEtcPath.InitCheck() == B_OK) {
catalogName = BString(systemEtcPath.Path())
<< "/locale/" << kCatFolder
<< "/" << fSignature
<< "/" << fLanguageName
<< kCatExtension;
status = ReadFromFile(catalogName.String());
}
}
fInitCheck = status;
}
* constructs an empty PlainTextCatalog with given sig and language.
* This is used for editing/testing purposes.
* InitCheck() will always be B_OK.
*/
PlainTextCatalog::PlainTextCatalog(const char *path, const char *signature,
const char *language)
:
HashMapCatalog(signature, language, 0),
fPath(path)
{
fInitCheck = B_OK;
}
PlainTextCatalog::~PlainTextCatalog()
{
}
void
PlainTextCatalog::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
PlainTextCatalog::ReadFromFile(const char *path)
{
std::fstream catalogFile;
std::string currentItem;
if (!path)
path = fPath.String();
catalogFile.open(path, std::fstream::in);
if (!catalogFile.is_open())
return B_ENTRY_NOT_FOUND;
if (std::getline(catalogFile, currentItem, '\t').good()) {
int arcver= -1;
std::istringstream ss(currentItem);
ss >> arcver;
if (ss.fail()) {
return B_ERROR;
}
if (arcver != kCatArchiveVersion) {
return B_ERROR;
}
} else
return B_ERROR;
if (std::getline(catalogFile, currentItem, '\t').good()) {
fLanguageName = currentItem.c_str() ;
} else
return B_ERROR;
if (std::getline(catalogFile, currentItem, '\t').good()) {
fSignature = currentItem.c_str() ;
} else
return B_ERROR;
if (std::getline(catalogFile, currentItem).good()) {
std::istringstream ss(currentItem);
uint32 foundFingerprint;
ss >> foundFingerprint;
if (ss.fail())
return B_ERROR;
if (fFingerprint == 0)
fFingerprint = foundFingerprint;
if (fFingerprint != foundFingerprint) {
return B_MISMATCHED_VALUES;
}
} else
return B_ERROR;
fPath = path;
std::string originalString;
std::string context;
std::string comment;
std::string translated;
while (std::getline(catalogFile, originalString,'\t').good()) {
if (!std::getline(catalogFile, context,'\t').good())
return B_ERROR;
if (!std::getline(catalogFile, comment,'\t').good())
return B_ERROR;
if (!std::getline(catalogFile, translated).good())
return B_ERROR;
SetString(originalString.c_str(), translated.c_str(), context.c_str(),
comment.c_str());
}
catalogFile.close();
uint32 checkFP = ComputeFingerprint();
if (fFingerprint != checkFP)
return B_BAD_DATA;
UpdateAttributes(path);
return B_OK;
}
status_t
PlainTextCatalog::WriteToFile(const char *path)
{
BString textContent;
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;
UpdateFingerprint();
textContent << kCatArchiveVersion << "\t" << fLanguageName.String() << "\t"
<< fSignature.String() << "\t" << fFingerprint << "\n";
res = catalogFile.Write(textContent.String(),textContent.Length());
if (res != textContent.Length())
return res;
CatMap::Iterator iter = fCatMap.GetIterator();
CatMap::Entry entry;
BString original;
BString comment;
BString translated;
while (iter.HasNext()) {
entry = iter.Next();
original = entry.key.fString;
comment = entry.key.fComment;
translated = entry.value;
escapeQuotedChars(original);
escapeQuotedChars(comment);
escapeQuotedChars(translated);
textContent.Truncate(0);
textContent << original.String() << "\t"
<< entry.key.fContext.String() << "\t"
<< comment << "\t"
<< translated.String() << "\n";
res = catalogFile.Write(textContent.String(),textContent.Length());
if (res != textContent.Length())
return res;
}
UpdateAttributes(catalogFile);
return B_OK;
}
* writes mimetype, language-name and signature of catalog into the
* catalog-file.
*/
void
PlainTextCatalog::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.WriteAttrString(BLocaleRoster::kCatLangAttr, &fLanguageName);
}
if (catalogFile.ReadAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
&buf, bufSize) <= 0 || fSignature != buf) {
catalogFile.WriteAttrString(BLocaleRoster::kCatSigAttr, &fSignature);
}
if (catalogFile.ReadAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
0, &temp, sizeof(uint32)) <= 0) {
catalogFile.WriteAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
0, &fFingerprint, sizeof(uint32));
}
}
void
PlainTextCatalog::UpdateAttributes(const char* path)
{
BEntry entry(path);
BFile node(&entry, B_READ_WRITE);
UpdateAttributes(node);
}
BCatalogData *
PlainTextCatalog::Instantiate(const entry_ref& owner, const char *language,
uint32 fingerprint)
{
PlainTextCatalog *catalog
= new(std::nothrow) PlainTextCatalog(owner, language, fingerprint);
if (catalog && catalog->InitCheck() != B_OK) {
delete catalog;
return NULL;
}
return catalog;
}
extern "C" BCatalogData *
instantiate_catalog(const entry_ref& owner, const char *language,
uint32 fingerprint)
{
PlainTextCatalog *catalog
= new(std::nothrow) PlainTextCatalog(owner, language, fingerprint);
if (catalog && catalog->InitCheck() != B_OK) {
delete catalog;
return NULL;
}
return catalog;
}
extern "C" BCatalogData *
create_catalog(const char *signature, const char *language)
{
PlainTextCatalog *catalog
= new(std::nothrow) PlainTextCatalog("emptycat", signature, language);
return catalog;
}
extern "C" status_t
get_available_languages(BMessage* availableLanguages,
const char* sigPattern = NULL, const char* langPattern = NULL,
int32 fingerprint = 0)
{
return B_ERROR;
}
uint8 gCatalogAddOnPriority = 99;