/*
 * Copyright 2003-2009, Haiku.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Oliver Tappe, zooey@hirschkaefer.de
 *		Adrien Destugues, pulkomandy@gmail.com
 */
 
 
#include <new>
#include <syslog.h>
 
#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 <LocaleRoster.h>
 
#include <cstdio>
 
 
using BPrivate::DefaultCatalog;
 
 
/*!	This file implements the default catalog-type for the opentracker locale
	kit. Alternatively, this could be used as a full add-on, but currently this
	is provided as part of liblocale.so.
*/
 
 
namespace BPrivate {
 
 
// several attributes/resource-IDs used within the Locale Kit:
 
const char *kCatLangAttr = "BEOS:LOCALE_LANGUAGE";
	// name of catalog language, lives in every catalog file
const char *kCatSigAttr = "BEOS:LOCALE_SIGNATURE";
	// catalog signature, lives in every catalog file
const char *kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT";
	// catalog fingerprint, may live in catalog file
 
const char *DefaultCatalog::kCatMimeType
	= "locale/x-vnd.Be.locale-catalog.default";
 
static int16 kCatArchiveVersion = 1;
	// version of the catalog archive structure, bump this if you change it!
 
 
/*!	Constructs a DefaultCatalog 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.
*/
DefaultCatalog::DefaultCatalog(const entry_ref &catalogOwner,
	const char *language, uint32 fingerprint)
	:
	HashMapCatalog("", language, fingerprint)
{
	fInitCheck = B_NOT_SUPPORTED;
	fprintf(stderr,
		"trying to load default-catalog(lang=%s) results in %s",
		language, strerror(fInitCheck));
}
 
 
/*!	Constructs a DefaultCatalog and reads it from the resources of the
	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);
	// fprintf(stderr,
	//	"trying to load embedded catalog from resources results in %s",
	//	strerror(fInitCheck));
}
 
 
/*!	Constructs an empty DefaultCatalog with given sig and language.
	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)
{
	// Not allowed for the build-tool version.
	return;
}
 
 
status_t
DefaultCatalog::SetRawString(const CatKey& key, const char *translated)
{
	return fCatMap.Put(key, translated);
}
 
 
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) {
		fprintf(stderr, "no catalog at %s\n", path);
		return B_ENTRY_NOT_FOUND;
	}
 
	fPath = path;
	fprintf(stderr, "found catalog at %s\n", path);
 
	off_t sz = 0;
	res = catalogFile.GetSize(&sz);
	if (res != B_OK) {
		fprintf(stderr, "couldn't get size for catalog-file %s\n", path);
		return res;
	}
 
	BStackOrHeapArray<char, 0> buf(sz);
	if (!buf.IsValid()) {
		fprintf(stderr, "couldn't allocate array of %Ld chars\n", sz);
		return B_NO_MEMORY;
	}
	res = catalogFile.Read(buf, sz);
	if (res < B_OK) {
		fprintf(stderr, "couldn't read from catalog-file %s\n", path);
		return res;
	}
	if (res < sz) {
		fprintf(stderr,
			"only got %u instead of %Lu bytes from catalog-file %s\n", res, sz,
			path);
		return res;
	}
	BMemoryIO memIO(buf, sz);
	res = Unflatten(&memIO);
 
	if (res == B_OK) {
		// some information living in member variables needs to be copied
		// to attributes. Although these attributes should have been written
		// when creating the catalog, we make sure that they exist there:
		UpdateAttributes(catalogFile);
	}
 
	return res;
}
 
 
/*!	This method is not currently being used, but it may be useful in the
	future...
*/
status_t
DefaultCatalog::ReadFromAttribute(const entry_ref &appOrAddOnRef)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
DefaultCatalog::ReadFromResource(const entry_ref &appOrAddOnRef)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
DefaultCatalog::WriteToFile(const char *path)
{
	BFile catalogFile;
	if (path)
		fPath = path;
	status_t status = catalogFile.SetTo(fPath.String(),
		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
	if (status != B_OK)
		return status;
 
	BMallocIO mallocIO;
	mallocIO.SetBlockSize(max_c(fCatMap.Size() * 20, 256));
		// set a largish block-size in order to avoid reallocs
	status = Flatten(&mallocIO);
	if (status != B_OK)
		return status;
 
	ssize_t bytesWritten
		= catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength());
	if (bytesWritten < 0)
		return bytesWritten;
	if (bytesWritten != (ssize_t)mallocIO.BufferLength())
		return B_IO_ERROR;
 
	// set mimetype-, language- and signature-attributes:
	UpdateAttributes(catalogFile);
 
	return B_OK;
}
 
 
/*!	This method is not currently being used, but it may be useful in the
	future...
*/
status_t
DefaultCatalog::WriteToAttribute(const entry_ref &appOrAddOnRef)
{
	return B_NOT_SUPPORTED;
}
 
 
status_t
DefaultCatalog::WriteToResource(const entry_ref &appOrAddOnRef)
{
	return B_NOT_SUPPORTED;
}
 
 
/*!	Writes mimetype, language-name and signature of catalog into the
	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(kCatLangAttr, B_STRING_TYPE, 0,
			&buf, bufSize) <= 0
		|| fLanguageName != buf) {
		catalogFile.WriteAttr(kCatLangAttr, B_STRING_TYPE, 0,
			fLanguageName.String(), fLanguageName.Length()+1);
	}
	if (catalogFile.ReadAttr(kCatSigAttr, B_STRING_TYPE, 0,
			&buf, bufSize) <= 0
		|| fSignature != buf) {
		catalogFile.WriteAttr(kCatSigAttr, B_STRING_TYPE, 0,
			fSignature.String(), fSignature.Length()+1);
	}
	if (catalogFile.ReadAttr(kCatFingerprintAttr, B_UINT32_TYPE,
		0, &temp, sizeof(uint32)) <= 0) {
		catalogFile.WriteAttr(kCatFingerprintAttr, B_UINT32_TYPE,
			0, &fFingerprint, sizeof(uint32));
	}
}
 
 
status_t
DefaultCatalog::Flatten(BDataIO *dataIO)
{
	UpdateFingerprint();
		// make sure we have the correct fingerprint before we flatten it
 
	status_t res;
	BMessage archive;
	uint32 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 a specific fingerprint has been requested and the catalog does in
		// fact have a fingerprint, both are compared. If they mismatch, we do
		// not accept this catalog:
		if (foundFingerprint != 0 && fFingerprint != 0
			&& foundFingerprint != fFingerprint) {
			fprintf(stderr, "default-catalog(sig=%s, lang=%s) "
				"has mismatching fingerprint (%d instead of the requested %d)"
				", so this catalog is skipped.\n",
				fSignature.String(), fLanguageName.String(), 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;
 
		// fCatMap.resize(count);
			// There is no resize method in Haiku Hash Map to prealloc space
		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) {
			fprintf(stderr, "default-catalog(sig=%s, lang=%s) "
				"has wrong fingerprint after load (%d instead of %d). "
				"The catalog data may be corrupted, so this catalog is "
				"skipped.\n",
				fSignature.String(), fLanguageName.String(), checkFP,
				fFingerprint);
			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;
}
 
 
const uint8 DefaultCatalog::kDefaultCatalogAddOnPriority = 1;
	// give highest priority to our embedded catalog-add-on
 
 
} // namespace BPrivate
↑ V576 Potentially incorrect format string is passed to the 'fprintf' function. Prefix 'L' is not applicable to conversion specifier 'u'.
↑ V576 Potentially incorrect format string is passed to the 'fprintf' function. Prefix 'L' is not applicable to conversion specifier 'd'.