xref: /haiku/src/kits/locale/DefaultCatalog.cpp (revision 802547718913d68643d59533536ecb9ba4691dc4)
1c3ac87e8SOliver Tappe /*
275f15221SOliver Tappe  * Copyright 2003-2009, Haiku.
3c3ac87e8SOliver Tappe  * Distributed under the terms of the MIT License.
475f15221SOliver Tappe  *
575f15221SOliver Tappe  * Authors:
675f15221SOliver Tappe  *		Oliver Tappe, zooey@hirschkaefer.de
775f15221SOliver Tappe  *		Adrien Destugues, pulkomandy@gmail.com
8c3ac87e8SOliver Tappe  */
9c3ac87e8SOliver Tappe 
10c3ac87e8SOliver Tappe 
11c3ac87e8SOliver Tappe #include <memory>
1275f15221SOliver Tappe #include <new>
13c3ac87e8SOliver Tappe #include <syslog.h>
14c3ac87e8SOliver Tappe 
15c3ac87e8SOliver Tappe #include <Application.h>
16c3ac87e8SOliver Tappe #include <DataIO.h>
17c3ac87e8SOliver Tappe #include <Directory.h>
18c3ac87e8SOliver Tappe #include <File.h>
19c3ac87e8SOliver Tappe #include <FindDirectory.h>
20c3ac87e8SOliver Tappe #include <fs_attr.h>
21c3ac87e8SOliver Tappe #include <Message.h>
22c3ac87e8SOliver Tappe #include <Mime.h>
23c3ac87e8SOliver Tappe #include <Path.h>
24c3ac87e8SOliver Tappe #include <Resources.h>
25c3ac87e8SOliver Tappe #include <Roster.h>
26c3ac87e8SOliver Tappe 
27c3ac87e8SOliver Tappe #include <DefaultCatalog.h>
28c3ac87e8SOliver Tappe #include <LocaleRoster.h>
29c3ac87e8SOliver Tappe 
30c3ac87e8SOliver Tappe #include <cstdio>
31c3ac87e8SOliver Tappe 
326449afb1SOliver Tappe 
33a0352959SOliver Tappe using std::auto_ptr;
34a0352959SOliver Tappe using std::min;
35a0352959SOliver Tappe using std::max;
36a0352959SOliver Tappe using std::pair;
37a0352959SOliver Tappe 
38a0352959SOliver Tappe 
39f9a80fecSAxel Dörfler /*!	This file implements the default catalog-type for the opentracker locale
40f9a80fecSAxel Dörfler 	kit. Alternatively, this could be used as a full add-on, but currently this
41f9a80fecSAxel Dörfler 	is provided as part of liblocale.so.
42c3ac87e8SOliver Tappe */
43c3ac87e8SOliver Tappe 
44c3ac87e8SOliver Tappe 
45c3ac87e8SOliver Tappe static const char *kCatFolder = "catalogs";
46c3ac87e8SOliver Tappe static const char *kCatExtension = ".catalog";
47c3ac87e8SOliver Tappe 
48c3ac87e8SOliver Tappe const char *DefaultCatalog::kCatMimeType
49c3ac87e8SOliver Tappe 	= "locale/x-vnd.Be.locale-catalog.default";
50c3ac87e8SOliver Tappe 
51c3ac87e8SOliver Tappe static int16 kCatArchiveVersion = 1;
52c3ac87e8SOliver Tappe 	// version of the catalog archive structure, bump this if you change it!
53c3ac87e8SOliver Tappe 
54f9a80fecSAxel Dörfler const uint8 DefaultCatalog::kDefaultCatalogAddOnPriority = 1;
55f9a80fecSAxel Dörfler 	// give highest priority to our embedded catalog-add-on
56c3ac87e8SOliver Tappe 
57f9a80fecSAxel Dörfler 
58f9a80fecSAxel Dörfler /*!	Constructs a DefaultCatalog with given signature and language and reads
59f9a80fecSAxel Dörfler 	the catalog from disk.
60f9a80fecSAxel Dörfler 	InitCheck() will be B_OK if catalog could be loaded successfully, it will
61f9a80fecSAxel Dörfler 	give an appropriate error-code otherwise.
62c3ac87e8SOliver Tappe */
63c3ac87e8SOliver Tappe DefaultCatalog::DefaultCatalog(const char *signature, const char *language,
6475f15221SOliver Tappe 	uint32 fingerprint)
65c3ac87e8SOliver Tappe 	:
6675f15221SOliver Tappe 	BHashMapCatalog(signature, language, fingerprint)
67c3ac87e8SOliver Tappe {
68c3ac87e8SOliver Tappe 	// give highest priority to catalog living in sub-folder of app's folder:
69c3ac87e8SOliver Tappe 	app_info appInfo;
70c3ac87e8SOliver Tappe 	be_app->GetAppInfo(&appInfo);
71c3ac87e8SOliver Tappe 	node_ref nref;
72c3ac87e8SOliver Tappe 	nref.device = appInfo.ref.device;
73c3ac87e8SOliver Tappe 	nref.node = appInfo.ref.directory;
74c3ac87e8SOliver Tappe 	BDirectory appDir(&nref);
75c3ac87e8SOliver Tappe 	BString catalogName("locale/");
76c3ac87e8SOliver Tappe 	catalogName << kCatFolder
77c3ac87e8SOliver Tappe 		<< "/" << fSignature
78c3ac87e8SOliver Tappe 		<< "/" << fLanguageName
79c3ac87e8SOliver Tappe 		<< kCatExtension;
80c3ac87e8SOliver Tappe 	BPath catalogPath(&appDir, catalogName.String());
81c3ac87e8SOliver Tappe 	status_t status = ReadFromFile(catalogPath.Path());
82c3ac87e8SOliver Tappe 
83c3ac87e8SOliver Tappe 	if (status != B_OK) {
84f9a80fecSAxel Dörfler 		// search in data folders
85c3ac87e8SOliver Tappe 
86f9a80fecSAxel Dörfler 		directory_which which[] = {
87f9a80fecSAxel Dörfler 			B_USER_DATA_DIRECTORY,
88f9a80fecSAxel Dörfler 			B_COMMON_DATA_DIRECTORY,
89f9a80fecSAxel Dörfler 			B_SYSTEM_DATA_DIRECTORY
90f9a80fecSAxel Dörfler 		};
91f9a80fecSAxel Dörfler 
92f9a80fecSAxel Dörfler 		for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
93f9a80fecSAxel Dörfler 			BPath path;
94f9a80fecSAxel Dörfler 			if (find_directory(which[i], &path) == B_OK) {
95f9a80fecSAxel Dörfler 				catalogName = BString(path.Path())
96c3ac87e8SOliver Tappe 					<< "/locale/" << kCatFolder
97c3ac87e8SOliver Tappe 					<< "/" << fSignature
98c3ac87e8SOliver Tappe 					<< "/" << fLanguageName
99c3ac87e8SOliver Tappe 					<< kCatExtension;
100c3ac87e8SOliver Tappe 				status = ReadFromFile(catalogName.String());
101f9a80fecSAxel Dörfler 				if (status == B_OK)
102f9a80fecSAxel Dörfler 					break;
103f9a80fecSAxel Dörfler 			}
104c3ac87e8SOliver Tappe 		}
105c3ac87e8SOliver Tappe 	}
106c3ac87e8SOliver Tappe 
107c3ac87e8SOliver Tappe 	fInitCheck = status;
108c3ac87e8SOliver Tappe 	log_team(LOG_DEBUG,
109c3ac87e8SOliver Tappe 		"trying to load default-catalog(sig=%s, lang=%s) results in %s",
110c3ac87e8SOliver Tappe 		signature, language, strerror(fInitCheck));
111c3ac87e8SOliver Tappe }
112c3ac87e8SOliver Tappe 
113c3ac87e8SOliver Tappe 
114f9a80fecSAxel Dörfler /*!	Constructs a DefaultCatalog and reads it from the resources of the
115f9a80fecSAxel Dörfler 	given entry-ref (which usually is an app- or add-on-file).
116f9a80fecSAxel Dörfler 	InitCheck() will be B_OK if catalog could be loaded successfully, it will
117f9a80fecSAxel Dörfler 	give an appropriate error-code otherwise.
118c3ac87e8SOliver Tappe */
119c3ac87e8SOliver Tappe DefaultCatalog::DefaultCatalog(entry_ref *appOrAddOnRef)
120c3ac87e8SOliver Tappe 	:
12175f15221SOliver Tappe 	BHashMapCatalog("", "", 0)
122c3ac87e8SOliver Tappe {
123c3ac87e8SOliver Tappe 	fInitCheck = ReadFromResource(appOrAddOnRef);
124c3ac87e8SOliver Tappe 	log_team(LOG_DEBUG,
125c3ac87e8SOliver Tappe 		"trying to load embedded catalog from resources results in %s",
126c3ac87e8SOliver Tappe 		strerror(fInitCheck));
127c3ac87e8SOliver Tappe }
128c3ac87e8SOliver Tappe 
129c3ac87e8SOliver Tappe 
130f9a80fecSAxel Dörfler /*!	Constructs an empty DefaultCatalog with given sig and language.
131f9a80fecSAxel Dörfler 	This is used for editing/testing purposes.
132f9a80fecSAxel Dörfler 	InitCheck() will always be B_OK.
133c3ac87e8SOliver Tappe */
134c3ac87e8SOliver Tappe DefaultCatalog::DefaultCatalog(const char *path, const char *signature,
135c3ac87e8SOliver Tappe 	const char *language)
136c3ac87e8SOliver Tappe 	:
13775f15221SOliver Tappe 	BHashMapCatalog(signature, language, 0),
138c3ac87e8SOliver Tappe 	fPath(path)
139c3ac87e8SOliver Tappe {
140c3ac87e8SOliver Tappe 	fInitCheck = B_OK;
141c3ac87e8SOliver Tappe }
142c3ac87e8SOliver Tappe 
143c3ac87e8SOliver Tappe 
144c3ac87e8SOliver Tappe DefaultCatalog::~DefaultCatalog()
145c3ac87e8SOliver Tappe {
146c3ac87e8SOliver Tappe }
147c3ac87e8SOliver Tappe 
148c3ac87e8SOliver Tappe 
149c3ac87e8SOliver Tappe status_t
150c3ac87e8SOliver Tappe DefaultCatalog::ReadFromFile(const char *path)
151c3ac87e8SOliver Tappe {
152c3ac87e8SOliver Tappe 	if (!path)
153c3ac87e8SOliver Tappe 		path = fPath.String();
154c3ac87e8SOliver Tappe 
155c3ac87e8SOliver Tappe 	BFile catalogFile;
156c3ac87e8SOliver Tappe 	status_t res = catalogFile.SetTo(path, B_READ_ONLY);
157c3ac87e8SOliver Tappe 	if (res != B_OK) {
1580a255c0cSAdrien Destugues 		log_team(LOG_DEBUG, "LocaleKit DefaultCatalog: no catalog at %s", path);
159c3ac87e8SOliver Tappe 		return B_ENTRY_NOT_FOUND;
160c3ac87e8SOliver Tappe 	}
161c3ac87e8SOliver Tappe 
162c3ac87e8SOliver Tappe 	fPath = path;
1630a255c0cSAdrien Destugues 	log_team(LOG_DEBUG, "LocaleKit DefaultCatalog: found catalog at %s", path);
164c3ac87e8SOliver Tappe 
165c3ac87e8SOliver Tappe 	off_t sz = 0;
166c3ac87e8SOliver Tappe 	res = catalogFile.GetSize(&sz);
167c3ac87e8SOliver Tappe 	if (res != B_OK) {
1680a255c0cSAdrien Destugues 		log_team(LOG_ERR, "LocaleKit DefaultCatalog: couldn't get size for "
1690a255c0cSAdrien Destugues 			"catalog-file %s", path);
170c3ac87e8SOliver Tappe 		return res;
171c3ac87e8SOliver Tappe 	}
172c3ac87e8SOliver Tappe 
17375f15221SOliver Tappe 	auto_ptr<char> buf(new(std::nothrow) char [sz]);
17475f15221SOliver Tappe 	if (buf.get() == NULL) {
1750a255c0cSAdrien Destugues 		log_team(LOG_ERR, "LocaleKit DefaultCatalog: couldn't allocate array "
1760a255c0cSAdrien Destugues 			"of %d chars", sz);
17775f15221SOliver Tappe 		return B_NO_MEMORY;
17875f15221SOliver Tappe 	}
179c3ac87e8SOliver Tappe 	res = catalogFile.Read(buf.get(), sz);
180c3ac87e8SOliver Tappe 	if (res < B_OK) {
1810a255c0cSAdrien Destugues 		log_team(LOG_ERR, "LocaleKit DefaultCatalog: couldn't read from "
1820a255c0cSAdrien Destugues 			"catalog-file %s", path);
183c3ac87e8SOliver Tappe 		return res;
184c3ac87e8SOliver Tappe 	}
185c3ac87e8SOliver Tappe 	if (res < sz) {
18675f15221SOliver Tappe 		log_team(LOG_ERR,
1870a255c0cSAdrien Destugues 			"LocaleKit DefaultCatalog: only got %lu instead of %Lu bytes from "
1880a255c0cSAdrien Destugues 			"catalog-file %s", res, sz, path);
189c3ac87e8SOliver Tappe 		return res;
190c3ac87e8SOliver Tappe 	}
191c3ac87e8SOliver Tappe 	BMemoryIO memIO(buf.get(), sz);
192c3ac87e8SOliver Tappe 	res = Unflatten(&memIO);
193c3ac87e8SOliver Tappe 
194c3ac87e8SOliver Tappe 	if (res == B_OK) {
195c3ac87e8SOliver Tappe 		// some information living in member variables needs to be copied
196c3ac87e8SOliver Tappe 		// to attributes. Although these attributes should have been written
197c3ac87e8SOliver Tappe 		// when creating the catalog, we make sure that they exist there:
198c3ac87e8SOliver Tappe 		UpdateAttributes(catalogFile);
199c3ac87e8SOliver Tappe 	}
200c3ac87e8SOliver Tappe 
201c3ac87e8SOliver Tappe 	return res;
202c3ac87e8SOliver Tappe }
203c3ac87e8SOliver Tappe 
204c3ac87e8SOliver Tappe 
205c3ac87e8SOliver Tappe /*
206c3ac87e8SOliver Tappe  * this method is not currently being used, but it may be useful in the future...
207c3ac87e8SOliver Tappe  */
208c3ac87e8SOliver Tappe status_t
209c3ac87e8SOliver Tappe DefaultCatalog::ReadFromAttribute(entry_ref *appOrAddOnRef)
210c3ac87e8SOliver Tappe {
211c3ac87e8SOliver Tappe 	BNode node;
212c3ac87e8SOliver Tappe 	status_t res = node.SetTo(appOrAddOnRef);
213c3ac87e8SOliver Tappe 	if (res != B_OK) {
21475f15221SOliver Tappe 		log_team(LOG_ERR,
21575f15221SOliver Tappe 			"couldn't find app or add-on (dev=%lu, dir=%Lu, name=%s)",
216c3ac87e8SOliver Tappe 			appOrAddOnRef->device, appOrAddOnRef->directory,
217c3ac87e8SOliver Tappe 			appOrAddOnRef->name);
218c3ac87e8SOliver Tappe 		return B_ENTRY_NOT_FOUND;
219c3ac87e8SOliver Tappe 	}
220c3ac87e8SOliver Tappe 
221c3ac87e8SOliver Tappe 	log_team(LOG_DEBUG,
222c3ac87e8SOliver Tappe 		"looking for embedded catalog-attribute in app/add-on"
223c3ac87e8SOliver Tappe 		"(dev=%lu, dir=%Lu, name=%s)", appOrAddOnRef->device,
224c3ac87e8SOliver Tappe 		appOrAddOnRef->directory, appOrAddOnRef->name);
225c3ac87e8SOliver Tappe 
226c3ac87e8SOliver Tappe 	attr_info attrInfo;
227c3ac87e8SOliver Tappe 	res = node.GetAttrInfo(BLocaleRoster::kEmbeddedCatAttr, &attrInfo);
228c3ac87e8SOliver Tappe 	if (res != B_OK) {
229c3ac87e8SOliver Tappe 		log_team(LOG_DEBUG, "no embedded catalog found");
230c3ac87e8SOliver Tappe 		return B_NAME_NOT_FOUND;
231c3ac87e8SOliver Tappe 	}
232c3ac87e8SOliver Tappe 	if (attrInfo.type != B_MESSAGE_TYPE) {
233c3ac87e8SOliver Tappe 		log_team(LOG_ERR, "attribute %s has incorrect type and is ignored!",
234c3ac87e8SOliver Tappe 			BLocaleRoster::kEmbeddedCatAttr);
235c3ac87e8SOliver Tappe 		return B_BAD_TYPE;
236c3ac87e8SOliver Tappe 	}
237c3ac87e8SOliver Tappe 
238c3ac87e8SOliver Tappe 	size_t size = attrInfo.size;
23975f15221SOliver Tappe 	auto_ptr<char> buf(new(std::nothrow) char [size]);
24075f15221SOliver Tappe 	if (buf.get() == NULL) {
24175f15221SOliver Tappe 		log_team(LOG_ERR, "couldn't allocate array of %d chars", size);
24275f15221SOliver Tappe 		return B_NO_MEMORY;
24375f15221SOliver Tappe 	}
244c3ac87e8SOliver Tappe 	res = node.ReadAttr(BLocaleRoster::kEmbeddedCatAttr, B_MESSAGE_TYPE, 0,
245c3ac87e8SOliver Tappe 		buf.get(), size);
246c3ac87e8SOliver Tappe 	if (res < (ssize_t)size) {
247c3ac87e8SOliver Tappe 		log_team(LOG_ERR, "unable to read embedded catalog from attribute");
248c3ac87e8SOliver Tappe 		return res < B_OK ? res : B_BAD_DATA;
249c3ac87e8SOliver Tappe 	}
250c3ac87e8SOliver Tappe 
251c3ac87e8SOliver Tappe 	BMemoryIO memIO(buf.get(), size);
252c3ac87e8SOliver Tappe 	res = Unflatten(&memIO);
253c3ac87e8SOliver Tappe 
254c3ac87e8SOliver Tappe 	return res;
255c3ac87e8SOliver Tappe }
256c3ac87e8SOliver Tappe 
257c3ac87e8SOliver Tappe 
258c3ac87e8SOliver Tappe status_t
259c3ac87e8SOliver Tappe DefaultCatalog::ReadFromResource(entry_ref *appOrAddOnRef)
260c3ac87e8SOliver Tappe {
261c3ac87e8SOliver Tappe 	BFile file;
262c3ac87e8SOliver Tappe 	status_t res = file.SetTo(appOrAddOnRef, B_READ_ONLY);
263c3ac87e8SOliver Tappe 	if (res != B_OK) {
26475f15221SOliver Tappe 		log_team(LOG_ERR,
26575f15221SOliver Tappe 			"couldn't find app or add-on (dev=%lu, dir=%Lu, name=%s)",
266c3ac87e8SOliver Tappe 			appOrAddOnRef->device, appOrAddOnRef->directory,
267c3ac87e8SOliver Tappe 			appOrAddOnRef->name);
268c3ac87e8SOliver Tappe 		return B_ENTRY_NOT_FOUND;
269c3ac87e8SOliver Tappe 	}
270c3ac87e8SOliver Tappe 
271c3ac87e8SOliver Tappe 	log_team(LOG_DEBUG,
272c3ac87e8SOliver Tappe 		"looking for embedded catalog-resource in app/add-on"
273c3ac87e8SOliver Tappe 		"(dev=%lu, dir=%Lu, name=%s)", appOrAddOnRef->device,
274c3ac87e8SOliver Tappe 		appOrAddOnRef->directory, appOrAddOnRef->name);
275c3ac87e8SOliver Tappe 
276c3ac87e8SOliver Tappe 	BResources rsrc;
277c3ac87e8SOliver Tappe 	res = rsrc.SetTo(&file);
278c3ac87e8SOliver Tappe 	if (res != B_OK) {
279c3ac87e8SOliver Tappe 		log_team(LOG_DEBUG, "file has no resources");
280c3ac87e8SOliver Tappe 		return res;
281c3ac87e8SOliver Tappe 	}
282c3ac87e8SOliver Tappe 
283c3ac87e8SOliver Tappe 	size_t sz;
284c3ac87e8SOliver Tappe 	const void *buf = rsrc.LoadResource(B_MESSAGE_TYPE,
285c3ac87e8SOliver Tappe 		BLocaleRoster::kEmbeddedCatResId, &sz);
286c3ac87e8SOliver Tappe 	if (!buf) {
287c3ac87e8SOliver Tappe 		log_team(LOG_DEBUG, "file has no catalog-resource");
288c3ac87e8SOliver Tappe 		return B_NAME_NOT_FOUND;
289c3ac87e8SOliver Tappe 	}
290c3ac87e8SOliver Tappe 
291c3ac87e8SOliver Tappe 	BMemoryIO memIO(buf, sz);
292c3ac87e8SOliver Tappe 	res = Unflatten(&memIO);
293c3ac87e8SOliver Tappe 
294c3ac87e8SOliver Tappe 	return res;
295c3ac87e8SOliver Tappe }
296c3ac87e8SOliver Tappe 
297c3ac87e8SOliver Tappe 
298c3ac87e8SOliver Tappe status_t
299c3ac87e8SOliver Tappe DefaultCatalog::WriteToFile(const char *path)
300c3ac87e8SOliver Tappe {
301c3ac87e8SOliver Tappe 	BFile catalogFile;
302c3ac87e8SOliver Tappe 	if (path)
303c3ac87e8SOliver Tappe 		fPath = path;
304c3ac87e8SOliver Tappe 	status_t res = catalogFile.SetTo(fPath.String(),
305c3ac87e8SOliver Tappe 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
306c3ac87e8SOliver Tappe 	if (res != B_OK)
307c3ac87e8SOliver Tappe 		return res;
308c3ac87e8SOliver Tappe 
309c3ac87e8SOliver Tappe 	BMallocIO mallocIO;
31075f15221SOliver Tappe 	mallocIO.SetBlockSize(max(fCatMap.Size() * 20, 256L));
311c3ac87e8SOliver Tappe 		// set a largish block-size in order to avoid reallocs
312c3ac87e8SOliver Tappe 	res = Flatten(&mallocIO);
313c3ac87e8SOliver Tappe 	if (res == B_OK) {
314c3ac87e8SOliver Tappe 		ssize_t wsz;
315c3ac87e8SOliver Tappe 		wsz = catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength());
316c3ac87e8SOliver Tappe 		if (wsz != (ssize_t)mallocIO.BufferLength())
317c3ac87e8SOliver Tappe 			return B_FILE_ERROR;
318c3ac87e8SOliver Tappe 
319c3ac87e8SOliver Tappe 		// set mimetype-, language- and signature-attributes:
320c3ac87e8SOliver Tappe 		UpdateAttributes(catalogFile);
321c3ac87e8SOliver Tappe 	}
322c3ac87e8SOliver Tappe 	if (res == B_OK)
323c3ac87e8SOliver Tappe 		UpdateAttributes(catalogFile);
324c3ac87e8SOliver Tappe 	return res;
325c3ac87e8SOliver Tappe }
326c3ac87e8SOliver Tappe 
327c3ac87e8SOliver Tappe 
328c3ac87e8SOliver Tappe /*
32975f15221SOliver Tappe  * this method is not currently being used, but it may be useful in the
33075f15221SOliver Tappe  * future...
331c3ac87e8SOliver Tappe  */
332c3ac87e8SOliver Tappe status_t
333c3ac87e8SOliver Tappe DefaultCatalog::WriteToAttribute(entry_ref *appOrAddOnRef)
334c3ac87e8SOliver Tappe {
335c3ac87e8SOliver Tappe 	BNode node;
336c3ac87e8SOliver Tappe 	status_t res = node.SetTo(appOrAddOnRef);
337c3ac87e8SOliver Tappe 	if (res != B_OK)
338c3ac87e8SOliver Tappe 		return res;
339c3ac87e8SOliver Tappe 
340c3ac87e8SOliver Tappe 	BMallocIO mallocIO;
34175f15221SOliver Tappe 	mallocIO.SetBlockSize(max(fCatMap.Size() * 20, 256L));
342c3ac87e8SOliver Tappe 		// set a largish block-size in order to avoid reallocs
343c3ac87e8SOliver Tappe 	res = Flatten(&mallocIO);
344c3ac87e8SOliver Tappe 
345c3ac87e8SOliver Tappe 	if (res == B_OK) {
346c3ac87e8SOliver Tappe 		ssize_t wsz;
347c3ac87e8SOliver Tappe 		wsz = node.WriteAttr(BLocaleRoster::kEmbeddedCatAttr, B_MESSAGE_TYPE, 0,
348c3ac87e8SOliver Tappe 			mallocIO.Buffer(), mallocIO.BufferLength());
349c3ac87e8SOliver Tappe 		if (wsz < B_OK)
350c3ac87e8SOliver Tappe 			res = wsz;
351c3ac87e8SOliver Tappe 		else if (wsz != (ssize_t)mallocIO.BufferLength())
352c3ac87e8SOliver Tappe 			res = B_ERROR;
353c3ac87e8SOliver Tappe 	}
354c3ac87e8SOliver Tappe 	return res;
355c3ac87e8SOliver Tappe }
356c3ac87e8SOliver Tappe 
357c3ac87e8SOliver Tappe 
358c3ac87e8SOliver Tappe status_t
359c3ac87e8SOliver Tappe DefaultCatalog::WriteToResource(entry_ref *appOrAddOnRef)
360c3ac87e8SOliver Tappe {
361c3ac87e8SOliver Tappe 	BFile file;
362c3ac87e8SOliver Tappe 	status_t res = file.SetTo(appOrAddOnRef, B_READ_WRITE);
363c3ac87e8SOliver Tappe 	if (res != B_OK)
364c3ac87e8SOliver Tappe 		return res;
365c3ac87e8SOliver Tappe 
366c3ac87e8SOliver Tappe 	BResources rsrc;
367c3ac87e8SOliver Tappe 	res = rsrc.SetTo(&file);
368c3ac87e8SOliver Tappe 	if (res != B_OK)
369c3ac87e8SOliver Tappe 		return res;
370c3ac87e8SOliver Tappe 
371c3ac87e8SOliver Tappe 	BMallocIO mallocIO;
37275f15221SOliver Tappe 	mallocIO.SetBlockSize(max(fCatMap.Size() * 20, 256L));
373c3ac87e8SOliver Tappe 		// set a largish block-size in order to avoid reallocs
374c3ac87e8SOliver Tappe 	res = Flatten(&mallocIO);
375c3ac87e8SOliver Tappe 
37675f15221SOliver Tappe 	if (res == B_OK) {
377c3ac87e8SOliver Tappe 		res = rsrc.AddResource(B_MESSAGE_TYPE, BLocaleRoster::kEmbeddedCatResId,
378c3ac87e8SOliver Tappe 			mallocIO.Buffer(), mallocIO.BufferLength(), "embedded catalog");
37975f15221SOliver Tappe 	}
380c3ac87e8SOliver Tappe 
381c3ac87e8SOliver Tappe 	return res;
382c3ac87e8SOliver Tappe }
383c3ac87e8SOliver Tappe 
384c3ac87e8SOliver Tappe 
385f9a80fecSAxel Dörfler /*!	Writes mimetype, language-name and signature of catalog into the
386f9a80fecSAxel Dörfler 	catalog-file.
387c3ac87e8SOliver Tappe */
388c3ac87e8SOliver Tappe void
389c3ac87e8SOliver Tappe DefaultCatalog::UpdateAttributes(BFile& catalogFile)
390c3ac87e8SOliver Tappe {
391c3ac87e8SOliver Tappe 	static const int bufSize = 256;
392c3ac87e8SOliver Tappe 	char buf[bufSize];
39375f15221SOliver Tappe 	uint32 temp;
39475f15221SOliver Tappe 	if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf,
39575f15221SOliver Tappe 			bufSize) <= 0
396c3ac87e8SOliver Tappe 		|| strcmp(kCatMimeType, buf) != 0) {
397c3ac87e8SOliver Tappe 		catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
398c3ac87e8SOliver Tappe 			kCatMimeType, strlen(kCatMimeType)+1);
399c3ac87e8SOliver Tappe 	}
400c3ac87e8SOliver Tappe 	if (catalogFile.ReadAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
401c3ac87e8SOliver Tappe 			&buf, bufSize) <= 0
402c3ac87e8SOliver Tappe 		|| fLanguageName != buf) {
403c3ac87e8SOliver Tappe 		catalogFile.WriteAttr(BLocaleRoster::kCatLangAttr, B_STRING_TYPE, 0,
404c3ac87e8SOliver Tappe 			fLanguageName.String(), fLanguageName.Length()+1);
405c3ac87e8SOliver Tappe 	}
406c3ac87e8SOliver Tappe 	if (catalogFile.ReadAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
407c3ac87e8SOliver Tappe 			&buf, bufSize) <= 0
408c3ac87e8SOliver Tappe 		|| fSignature != buf) {
409c3ac87e8SOliver Tappe 		catalogFile.WriteAttr(BLocaleRoster::kCatSigAttr, B_STRING_TYPE, 0,
410c3ac87e8SOliver Tappe 			fSignature.String(), fSignature.Length()+1);
411c3ac87e8SOliver Tappe 	}
41275f15221SOliver Tappe 	if (catalogFile.ReadAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
41375f15221SOliver Tappe 		0, &temp, sizeof(uint32)) <= 0) {
41475f15221SOliver Tappe 		catalogFile.WriteAttr(BLocaleRoster::kCatFingerprintAttr, B_UINT32_TYPE,
41575f15221SOliver Tappe 			0, &fFingerprint, sizeof(uint32));
41675f15221SOliver Tappe 	}
417c3ac87e8SOliver Tappe }
418c3ac87e8SOliver Tappe 
419c3ac87e8SOliver Tappe 
420c3ac87e8SOliver Tappe status_t
421c3ac87e8SOliver Tappe DefaultCatalog::Flatten(BDataIO *dataIO)
422c3ac87e8SOliver Tappe {
423c3ac87e8SOliver Tappe 	UpdateFingerprint();
424c3ac87e8SOliver Tappe 		// make sure we have the correct fingerprint before we flatten it
425c3ac87e8SOliver Tappe 
426c3ac87e8SOliver Tappe 	status_t res;
427c3ac87e8SOliver Tappe 	BMessage archive;
42875f15221SOliver Tappe 	int32 count = fCatMap.Size();
42975f15221SOliver Tappe 	res = archive.AddString("class", "DefaultCatalog");
43075f15221SOliver Tappe 	if (res == B_OK)
43175f15221SOliver Tappe 		res = archive.AddInt32("c:sz", count);
43275f15221SOliver Tappe 	if (res == B_OK)
43375f15221SOliver Tappe 		res = archive.AddInt16("c:ver", kCatArchiveVersion);
43475f15221SOliver Tappe 	if (res == B_OK)
43575f15221SOliver Tappe 		res = archive.AddString("c:lang", fLanguageName.String());
43675f15221SOliver Tappe 	if (res == B_OK)
43775f15221SOliver Tappe 		res = archive.AddString("c:sig", fSignature.String());
43875f15221SOliver Tappe 	if (res == B_OK)
43975f15221SOliver Tappe 		res = archive.AddInt32("c:fpr", fFingerprint);
440c3ac87e8SOliver Tappe 	if (res == B_OK)
441c3ac87e8SOliver Tappe 		res = archive.Flatten(dataIO);
442c3ac87e8SOliver Tappe 
44375f15221SOliver Tappe 	CatMap::Iterator iter = fCatMap.GetIterator();
44475f15221SOliver Tappe 	CatMap::Entry entry;
44575f15221SOliver Tappe 	while (res == B_OK && iter.HasNext()) {
44675f15221SOliver Tappe 		entry = iter.Next();
447c3ac87e8SOliver Tappe 		archive.MakeEmpty();
44875f15221SOliver Tappe 		res = archive.AddString("c:ostr", entry.key.fString.String());
44975f15221SOliver Tappe 		if (res == B_OK)
45075f15221SOliver Tappe 			res = archive.AddString("c:ctxt", entry.key.fContext.String());
45175f15221SOliver Tappe 		if (res == B_OK)
45275f15221SOliver Tappe 			res = archive.AddString("c:comt", entry.key.fComment.String());
45375f15221SOliver Tappe 		if (res == B_OK)
45475f15221SOliver Tappe 			res = archive.AddInt32("c:hash", entry.key.fHashVal);
45575f15221SOliver Tappe 		if (res == B_OK)
45675f15221SOliver Tappe 			res = archive.AddString("c:tstr", entry.value.String());
457c3ac87e8SOliver Tappe 		if (res == B_OK)
458c3ac87e8SOliver Tappe 			res = archive.Flatten(dataIO);
459c3ac87e8SOliver Tappe 	}
46075f15221SOliver Tappe 
461c3ac87e8SOliver Tappe 	return res;
462c3ac87e8SOliver Tappe }
463c3ac87e8SOliver Tappe 
464c3ac87e8SOliver Tappe 
465c3ac87e8SOliver Tappe status_t
466c3ac87e8SOliver Tappe DefaultCatalog::Unflatten(BDataIO *dataIO)
467c3ac87e8SOliver Tappe {
46875f15221SOliver Tappe 	fCatMap.Clear();
469c3ac87e8SOliver Tappe 	int32 count = 0;
470c3ac87e8SOliver Tappe 	int16 version;
471c3ac87e8SOliver Tappe 	BMessage archiveMsg;
472c3ac87e8SOliver Tappe 	status_t res = archiveMsg.Unflatten(dataIO);
473c3ac87e8SOliver Tappe 
474c3ac87e8SOliver Tappe 	if (res == B_OK) {
475c3ac87e8SOliver Tappe 		res = archiveMsg.FindInt16("c:ver", &version)
476c3ac87e8SOliver Tappe 			|| archiveMsg.FindInt32("c:sz", &count);
477c3ac87e8SOliver Tappe 	}
478c3ac87e8SOliver Tappe 	if (res == B_OK) {
479c3ac87e8SOliver Tappe 		fLanguageName = archiveMsg.FindString("c:lang");
480c3ac87e8SOliver Tappe 		fSignature = archiveMsg.FindString("c:sig");
48175f15221SOliver Tappe 		uint32 foundFingerprint = archiveMsg.FindInt32("c:fpr");
482c3ac87e8SOliver Tappe 
48375f15221SOliver Tappe 		// if a specific fingerprint has been requested and the catalog does in
48475f15221SOliver Tappe 		// fact have a fingerprint, both are compared. If they mismatch, we do
48575f15221SOliver Tappe 		// not accept this catalog:
486c3ac87e8SOliver Tappe 		if (foundFingerprint != 0 && fFingerprint != 0
487c3ac87e8SOliver Tappe 			&& foundFingerprint != fFingerprint) {
488c3ac87e8SOliver Tappe 			log_team(LOG_INFO, "default-catalog(sig=%s, lang=%s) "
489c3ac87e8SOliver Tappe 				"has mismatching fingerprint (%ld instead of the requested %ld), "
490c3ac87e8SOliver Tappe 				"so this catalog is skipped.",
491c3ac87e8SOliver Tappe 				fSignature.String(), fLanguageName.String(), foundFingerprint,
492c3ac87e8SOliver Tappe 				fFingerprint);
493c3ac87e8SOliver Tappe 			res = B_MISMATCHED_VALUES;
494c3ac87e8SOliver Tappe 		} else
495c3ac87e8SOliver Tappe 			fFingerprint = foundFingerprint;
496c3ac87e8SOliver Tappe 	}
497c3ac87e8SOliver Tappe 
498c3ac87e8SOliver Tappe 	if (res == B_OK && count > 0) {
499c3ac87e8SOliver Tappe 		CatKey key;
500c3ac87e8SOliver Tappe 		const char *keyStr;
50175f15221SOliver Tappe 		const char *keyCtx;
50275f15221SOliver Tappe 		const char *keyCmt;
503c3ac87e8SOliver Tappe 		const char *translated;
50475f15221SOliver Tappe 
50575f15221SOliver Tappe 		// fCatMap.resize(count);
50675f15221SOliver Tappe 			// There is no resize method in Haiku's HashMap to preallocate
50775f15221SOliver Tappe 			// memory.
508c3ac87e8SOliver Tappe 		for (int i=0; res == B_OK && i < count; ++i) {
509c3ac87e8SOliver Tappe 			res = archiveMsg.Unflatten(dataIO);
51075f15221SOliver Tappe 			if (res == B_OK)
51175f15221SOliver Tappe 				res = archiveMsg.FindString("c:ostr", &keyStr);
51275f15221SOliver Tappe 			if (res == B_OK)
51375f15221SOliver Tappe 				res = archiveMsg.FindString("c:ctxt", &keyCtx);
51475f15221SOliver Tappe 			if (res == B_OK)
51575f15221SOliver Tappe 				res = archiveMsg.FindString("c:comt", &keyCmt);
51675f15221SOliver Tappe 			if (res == B_OK)
51775f15221SOliver Tappe 				res = archiveMsg.FindInt32("c:hash", (int32*)&key.fHashVal);
51875f15221SOliver Tappe 			if (res == B_OK)
51975f15221SOliver Tappe 				res = archiveMsg.FindString("c:tstr", &translated);
520c3ac87e8SOliver Tappe 			if (res == B_OK) {
52175f15221SOliver Tappe 				key.fString = keyStr;
52275f15221SOliver Tappe 				key.fContext = keyCtx;
52375f15221SOliver Tappe 				key.fComment = keyCmt;
52475f15221SOliver Tappe 				fCatMap.Put(key, translated);
525c3ac87e8SOliver Tappe 			}
526c3ac87e8SOliver Tappe 		}
52775f15221SOliver Tappe 		uint32 checkFP = ComputeFingerprint();
528c3ac87e8SOliver Tappe 		if (fFingerprint != checkFP) {
529c3ac87e8SOliver Tappe 			log_team(LOG_WARNING, "default-catalog(sig=%s, lang=%s) "
530c3ac87e8SOliver Tappe 				"has wrong fingerprint after load (%ld instead of the %ld). "
531c3ac87e8SOliver Tappe 				"The catalog data may be corrupted, so this catalog is skipped.",
532c3ac87e8SOliver Tappe 				fSignature.String(), fLanguageName.String(), checkFP,
533c3ac87e8SOliver Tappe 				fFingerprint);
534c3ac87e8SOliver Tappe 			return B_BAD_DATA;
535c3ac87e8SOliver Tappe 		}
536c3ac87e8SOliver Tappe 	}
537c3ac87e8SOliver Tappe 	return res;
538c3ac87e8SOliver Tappe }
539c3ac87e8SOliver Tappe 
540c3ac87e8SOliver Tappe 
541c3ac87e8SOliver Tappe BCatalogAddOn *
542c3ac87e8SOliver Tappe DefaultCatalog::Instantiate(const char *signature, const char *language,
54375f15221SOliver Tappe 	uint32 fingerprint)
544c3ac87e8SOliver Tappe {
54575f15221SOliver Tappe 	DefaultCatalog *catalog
54675f15221SOliver Tappe 		= new(std::nothrow) DefaultCatalog(signature, language, fingerprint);
547c3ac87e8SOliver Tappe 	if (catalog && catalog->InitCheck() != B_OK) {
548c3ac87e8SOliver Tappe 		delete catalog;
549c3ac87e8SOliver Tappe 		return NULL;
550c3ac87e8SOliver Tappe 	}
551c3ac87e8SOliver Tappe 	return catalog;
552c3ac87e8SOliver Tappe }
553c3ac87e8SOliver Tappe 
554c3ac87e8SOliver Tappe 
555c3ac87e8SOliver Tappe BCatalogAddOn *
556c3ac87e8SOliver Tappe DefaultCatalog::InstantiateEmbedded(entry_ref *appOrAddOnRef)
557c3ac87e8SOliver Tappe {
55875f15221SOliver Tappe 	DefaultCatalog *catalog = new(std::nothrow) DefaultCatalog(appOrAddOnRef);
559c3ac87e8SOliver Tappe 	if (catalog && catalog->InitCheck() != B_OK) {
560c3ac87e8SOliver Tappe 		delete catalog;
561c3ac87e8SOliver Tappe 		return NULL;
562c3ac87e8SOliver Tappe 	}
563c3ac87e8SOliver Tappe 	return catalog;
564c3ac87e8SOliver Tappe }
565c3ac87e8SOliver Tappe 
566c3ac87e8SOliver Tappe 
567c3ac87e8SOliver Tappe BCatalogAddOn *
568c3ac87e8SOliver Tappe DefaultCatalog::Create(const char *signature, const char *language)
569c3ac87e8SOliver Tappe {
57075f15221SOliver Tappe 	DefaultCatalog *catalog
57175f15221SOliver Tappe 		= new(std::nothrow) DefaultCatalog("", signature, language);
572c3ac87e8SOliver Tappe 	if (catalog && catalog->InitCheck() != B_OK) {
573c3ac87e8SOliver Tappe 		delete catalog;
574c3ac87e8SOliver Tappe 		return NULL;
575c3ac87e8SOliver Tappe 	}
576c3ac87e8SOliver Tappe 	return catalog;
577c3ac87e8SOliver Tappe }
578*80254771SAdrien Destugues 
579*80254771SAdrien Destugues extern "C" status_t
580*80254771SAdrien Destugues default_catalog_get_available_languages(BMessage* availableLanguages,
581*80254771SAdrien Destugues 	const char* sigPattern, const char* langPattern = NULL,
582*80254771SAdrien Destugues 	int32 fingerprint = 0)
583*80254771SAdrien Destugues {
584*80254771SAdrien Destugues 	if (availableLanguages == NULL || sigPattern == NULL)
585*80254771SAdrien Destugues 		return B_BAD_DATA;
586*80254771SAdrien Destugues 
587*80254771SAdrien Destugues 	app_info appInfo;
588*80254771SAdrien Destugues 	be_app->GetAppInfo(&appInfo);
589*80254771SAdrien Destugues 	node_ref nref;
590*80254771SAdrien Destugues 	nref.device = appInfo.ref.device;
591*80254771SAdrien Destugues 	nref.node = appInfo.ref.directory;
592*80254771SAdrien Destugues 	BDirectory appDir(&nref);
593*80254771SAdrien Destugues 	BString catalogName("locale/");
594*80254771SAdrien Destugues 	catalogName << kCatFolder
595*80254771SAdrien Destugues 		<< "/" << sigPattern ;
596*80254771SAdrien Destugues 	BPath catalogPath(&appDir, catalogName.String());
597*80254771SAdrien Destugues 	BEntry file(catalogPath.Path());
598*80254771SAdrien Destugues 	BDirectory dir(&file);
599*80254771SAdrien Destugues 
600*80254771SAdrien Destugues 	char fileName[B_FILE_NAME_LENGTH];
601*80254771SAdrien Destugues 	while(dir.GetNextEntry(&file) == B_OK) {
602*80254771SAdrien Destugues 		file.GetName(fileName);
603*80254771SAdrien Destugues 		BString langName(fileName);
604*80254771SAdrien Destugues 		langName.Replace(kCatExtension,"",1);
605*80254771SAdrien Destugues 		availableLanguages->AddString("langs",langName);
606*80254771SAdrien Destugues 	}
607*80254771SAdrien Destugues 
608*80254771SAdrien Destugues 	// search in data folders
609*80254771SAdrien Destugues 
610*80254771SAdrien Destugues 	directory_which which[] = {
611*80254771SAdrien Destugues 		B_USER_DATA_DIRECTORY,
612*80254771SAdrien Destugues 		B_COMMON_DATA_DIRECTORY,
613*80254771SAdrien Destugues 		B_SYSTEM_DATA_DIRECTORY
614*80254771SAdrien Destugues 	};
615*80254771SAdrien Destugues 
616*80254771SAdrien Destugues 	for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) {
617*80254771SAdrien Destugues 		BPath path;
618*80254771SAdrien Destugues 		if (find_directory(which[i], &path) == B_OK) {
619*80254771SAdrien Destugues 			catalogName = BString("locale/")
620*80254771SAdrien Destugues 				<< kCatFolder
621*80254771SAdrien Destugues 				<< "/" << sigPattern;
622*80254771SAdrien Destugues 
623*80254771SAdrien Destugues 			BPath catalogPath(path.Path(), catalogName.String());
624*80254771SAdrien Destugues 			BEntry file(catalogPath.Path());
625*80254771SAdrien Destugues 			BDirectory dir(&file);
626*80254771SAdrien Destugues 
627*80254771SAdrien Destugues 			char fileName[B_FILE_NAME_LENGTH];
628*80254771SAdrien Destugues 			while(dir.GetNextEntry(&file) == B_OK) {
629*80254771SAdrien Destugues 				file.GetName(fileName);
630*80254771SAdrien Destugues 				BString langName(fileName);
631*80254771SAdrien Destugues 				langName.Replace(kCatExtension,"",1);
632*80254771SAdrien Destugues 				availableLanguages->AddString("langs",langName);
633*80254771SAdrien Destugues 			}
634*80254771SAdrien Destugues 		}
635*80254771SAdrien Destugues 	}
636*80254771SAdrien Destugues 
637*80254771SAdrien Destugues 	return B_OK;
638*80254771SAdrien Destugues }
639