xref: /haiku/src/tools/locale/DefaultCatalog.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
1 /*
2  * Copyright 2003-2009, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Oliver Tappe, zooey@hirschkaefer.de
7  *		Adrien Destugues, pulkomandy@gmail.com
8  */
9 
10 
11 #include <memory>
12 #include <new>
13 #include <syslog.h>
14 
15 #include <Application.h>
16 #include <DataIO.h>
17 #include <Directory.h>
18 #include <File.h>
19 #include <FindDirectory.h>
20 #include <fs_attr.h>
21 #include <Message.h>
22 #include <Mime.h>
23 #include <Path.h>
24 #include <Resources.h>
25 #include <Roster.h>
26 
27 #include <DefaultCatalog.h>
28 #include <LocaleRoster.h>
29 
30 #include <cstdio>
31 
32 
33 using std::auto_ptr;
34 
35 
36 /*!	This file implements the default catalog-type for the opentracker locale
37 	kit. Alternatively, this could be used as a full add-on, but currently this
38 	is provided as part of liblocale.so.
39 */
40 
41 
42 // several attributes/resource-IDs used within the Locale Kit:
43 
44 const char *kCatLangAttr = "BEOS:LOCALE_LANGUAGE";
45 	// name of catalog language, lives in every catalog file
46 const char *kCatSigAttr = "BEOS:LOCALE_SIGNATURE";
47 	// catalog signature, lives in every catalog file
48 const char *kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT";
49 	// catalog fingerprint, may live in catalog file
50 
51 const char *DefaultCatalog::kCatMimeType
52 	= "locale/x-vnd.Be.locale-catalog.default";
53 
54 static int16 kCatArchiveVersion = 1;
55 	// version of the catalog archive structure, bump this if you change it!
56 
57 
58 /*!	Constructs a DefaultCatalog with given signature and language and reads
59 	the catalog from disk.
60 	InitCheck() will be B_OK if catalog could be loaded successfully, it will
61 	give an appropriate error-code otherwise.
62 */
63 DefaultCatalog::DefaultCatalog(const char *signature, const char *language,
64 	uint32 fingerprint)
65 	:
66 	BHashMapCatalog(signature, language, fingerprint)
67 {
68 	fInitCheck = B_NOT_SUPPORTED;
69 	fprintf(stderr,
70 		"trying to load default-catalog(sig=%s, lang=%s) results in %s",
71 		signature, language, strerror(fInitCheck));
72 }
73 
74 
75 /*!	Constructs a DefaultCatalog and reads it from the resources of the
76 	given entry-ref (which usually is an app- or add-on-file).
77 	InitCheck() will be B_OK if catalog could be loaded successfully, it will
78 	give an appropriate error-code otherwise.
79 */
80 DefaultCatalog::DefaultCatalog(entry_ref *appOrAddOnRef)
81 	:
82 	BHashMapCatalog("", "", 0)
83 {
84 	fInitCheck = ReadFromResource(appOrAddOnRef);
85 	fprintf(stderr,
86 		"trying to load embedded catalog from resources results in %s",
87 		strerror(fInitCheck));
88 }
89 
90 
91 /*!	Constructs an empty DefaultCatalog with given sig and language.
92 	This is used for editing/testing purposes.
93 	InitCheck() will always be B_OK.
94 */
95 DefaultCatalog::DefaultCatalog(const char *path, const char *signature,
96 	const char *language)
97 	:
98 	BHashMapCatalog(signature, language, 0),
99 	fPath(path)
100 {
101 	fInitCheck = B_OK;
102 }
103 
104 
105 DefaultCatalog::~DefaultCatalog()
106 {
107 }
108 
109 
110 status_t
111 DefaultCatalog::ReadFromFile(const char *path)
112 {
113 	if (!path)
114 		path = fPath.String();
115 
116 	BFile catalogFile;
117 	status_t res = catalogFile.SetTo(path, B_READ_ONLY);
118 	if (res != B_OK) {
119 		fprintf(stderr, "no catalog at %s\n", path);
120 		return B_ENTRY_NOT_FOUND;
121 	}
122 
123 	fPath = path;
124 	fprintf(stderr, "found catalog at %s\n", path);
125 
126 	off_t sz = 0;
127 	res = catalogFile.GetSize(&sz);
128 	if (res != B_OK) {
129 		fprintf(stderr, "couldn't get size for catalog-file %s\n", path);
130 		return res;
131 	}
132 
133 	auto_ptr<char> buf(new(std::nothrow) char [sz]);
134 	if (buf.get() == NULL) {
135 		fprintf(stderr, "couldn't allocate array of %Ld chars\n", sz);
136 		return B_NO_MEMORY;
137 	}
138 	res = catalogFile.Read(buf.get(), sz);
139 	if (res < B_OK) {
140 		fprintf(stderr, "couldn't read from catalog-file %s\n", path);
141 		return res;
142 	}
143 	if (res < sz) {
144 		fprintf(stderr,
145 			"only got %lu instead of %Lu bytes from catalog-file %s\n", res, sz,
146 			path);
147 		return res;
148 	}
149 	BMemoryIO memIO(buf.get(), sz);
150 	res = Unflatten(&memIO);
151 
152 	if (res == B_OK) {
153 		// some information living in member variables needs to be copied
154 		// to attributes. Although these attributes should have been written
155 		// when creating the catalog, we make sure that they exist there:
156 		UpdateAttributes(catalogFile);
157 	}
158 
159 	return res;
160 }
161 
162 
163 /*!	This method is not currently being used, but it may be useful in the
164 	future...
165 */
166 status_t
167 DefaultCatalog::ReadFromAttribute(entry_ref *appOrAddOnRef)
168 {
169 	return B_NOT_SUPPORTED;
170 }
171 
172 
173 status_t
174 DefaultCatalog::ReadFromResource(entry_ref *appOrAddOnRef)
175 {
176 	return B_NOT_SUPPORTED;
177 }
178 
179 
180 status_t
181 DefaultCatalog::WriteToFile(const char *path)
182 {
183 	BFile catalogFile;
184 	if (path)
185 		fPath = path;
186 	status_t status = catalogFile.SetTo(fPath.String(),
187 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
188 	if (status != B_OK)
189 		return status;
190 
191 	BMallocIO mallocIO;
192 	mallocIO.SetBlockSize(max_c(fCatMap.Size() * 20, 256));
193 		// set a largish block-size in order to avoid reallocs
194 	status = Flatten(&mallocIO);
195 	if (status != B_OK)
196 		return status;
197 
198 	ssize_t bytesWritten
199 		= catalogFile.Write(mallocIO.Buffer(), mallocIO.BufferLength());
200 	if (bytesWritten < 0)
201 		return bytesWritten;
202 	if (bytesWritten != (ssize_t)mallocIO.BufferLength())
203 		return B_IO_ERROR;
204 
205 	// set mimetype-, language- and signature-attributes:
206 	UpdateAttributes(catalogFile);
207 
208 	return B_OK;
209 }
210 
211 
212 /*!	This method is not currently being used, but it may be useful in the
213 	future...
214 */
215 status_t
216 DefaultCatalog::WriteToAttribute(entry_ref *appOrAddOnRef)
217 {
218 	return B_NOT_SUPPORTED;
219 }
220 
221 
222 status_t
223 DefaultCatalog::WriteToResource(entry_ref *appOrAddOnRef)
224 {
225 	return B_NOT_SUPPORTED;
226 }
227 
228 
229 /*!	Writes mimetype, language-name and signature of catalog into the
230 	catalog-file.
231 */
232 void
233 DefaultCatalog::UpdateAttributes(BFile& catalogFile)
234 {
235 	static const int bufSize = 256;
236 	char buf[bufSize];
237 	uint32 temp;
238 	if (catalogFile.ReadAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, &buf,
239 			bufSize) <= 0
240 		|| strcmp(kCatMimeType, buf) != 0) {
241 		catalogFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0,
242 			kCatMimeType, strlen(kCatMimeType)+1);
243 	}
244 	if (catalogFile.ReadAttr(kCatLangAttr, B_STRING_TYPE, 0,
245 			&buf, bufSize) <= 0
246 		|| fLanguageName != buf) {
247 		catalogFile.WriteAttr(kCatLangAttr, B_STRING_TYPE, 0,
248 			fLanguageName.String(), fLanguageName.Length()+1);
249 	}
250 	if (catalogFile.ReadAttr(kCatSigAttr, B_STRING_TYPE, 0,
251 			&buf, bufSize) <= 0
252 		|| fSignature != buf) {
253 		catalogFile.WriteAttr(kCatSigAttr, B_STRING_TYPE, 0,
254 			fSignature.String(), fSignature.Length()+1);
255 	}
256 	if (catalogFile.ReadAttr(kCatFingerprintAttr, B_UINT32_TYPE,
257 		0, &temp, sizeof(uint32)) <= 0) {
258 		catalogFile.WriteAttr(kCatFingerprintAttr, B_UINT32_TYPE,
259 			0, &fFingerprint, sizeof(uint32));
260 	}
261 }
262 
263 
264 status_t
265 DefaultCatalog::Flatten(BDataIO *dataIO)
266 {
267 	UpdateFingerprint();
268 		// make sure we have the correct fingerprint before we flatten it
269 
270 	status_t res;
271 	BMessage archive;
272 	uint32 count = fCatMap.Size();
273 	res = archive.AddString("class", "DefaultCatalog");
274 	if (res == B_OK)
275 		res = archive.AddInt32("c:sz", count);
276 	if (res == B_OK)
277 		res = archive.AddInt16("c:ver", kCatArchiveVersion);
278 	if (res == B_OK)
279 		res = archive.AddString("c:lang", fLanguageName.String());
280 	if (res == B_OK)
281 		res = archive.AddString("c:sig", fSignature.String());
282 	if (res == B_OK)
283 		res = archive.AddInt32("c:fpr", fFingerprint);
284 	if (res == B_OK)
285 		res = archive.Flatten(dataIO);
286 
287 	CatMap::Iterator iter = fCatMap.GetIterator();
288 	CatMap::Entry entry;
289 	while (res == B_OK && iter.HasNext()) {
290 		entry = iter.Next();
291 		archive.MakeEmpty();
292 		res = archive.AddString("c:ostr", entry.key.fString.String());
293 		if (res == B_OK)
294 			res = archive.AddString("c:ctxt", entry.key.fContext.String());
295 		if (res == B_OK)
296 			res = archive.AddString("c:comt", entry.key.fComment.String());
297 		if (res == B_OK)
298 			res = archive.AddInt32("c:hash", entry.key.fHashVal);
299 		if (res == B_OK)
300 			res = archive.AddString("c:tstr", entry.value.String());
301 		if (res == B_OK)
302 			res = archive.Flatten(dataIO);
303 	}
304 	return res;
305 }
306 
307 
308 status_t
309 DefaultCatalog::Unflatten(BDataIO *dataIO)
310 {
311 	fCatMap.Clear();
312 	int32 count = 0;
313 	int16 version;
314 	BMessage archiveMsg;
315 	status_t res = archiveMsg.Unflatten(dataIO);
316 
317 	if (res == B_OK) {
318 		res = archiveMsg.FindInt16("c:ver", &version)
319 			|| archiveMsg.FindInt32("c:sz", &count);
320 	}
321 	if (res == B_OK) {
322 		fLanguageName = archiveMsg.FindString("c:lang");
323 		fSignature = archiveMsg.FindString("c:sig");
324 		uint32 foundFingerprint = archiveMsg.FindInt32("c:fpr");
325 
326 		// if a specific fingerprint has been requested and the catalog does in
327 		// fact have a fingerprint, both are compared. If they mismatch, we do
328 		// not accept this catalog:
329 		if (foundFingerprint != 0 && fFingerprint != 0
330 			&& foundFingerprint != fFingerprint) {
331 			fprintf(stderr, "default-catalog(sig=%s, lang=%s) "
332 				"has mismatching fingerprint (%ld instead of the requested %ld)"
333 				", so this catalog is skipped.\n",
334 				fSignature.String(), fLanguageName.String(), foundFingerprint,
335 				fFingerprint);
336 			res = B_MISMATCHED_VALUES;
337 		} else
338 			fFingerprint = foundFingerprint;
339 	}
340 
341 	if (res == B_OK && count > 0) {
342 		CatKey key;
343 		const char *keyStr;
344 		const char *keyCtx;
345 		const char *keyCmt;
346 		const char *translated;
347 
348 		// fCatMap.resize(count);
349 			// There is no resize method in Haiku Hash Map to prealloc space
350 		for (int i=0; res == B_OK && i < count; ++i) {
351 			res = archiveMsg.Unflatten(dataIO);
352 			if (res == B_OK)
353 				res = archiveMsg.FindString("c:ostr", &keyStr);
354 			if (res == B_OK)
355 				res = archiveMsg.FindString("c:ctxt", &keyCtx);
356 			if (res == B_OK)
357 				res = archiveMsg.FindString("c:comt", &keyCmt);
358 			if (res == B_OK)
359 				res = archiveMsg.FindInt32("c:hash", (int32*)&key.fHashVal);
360 			if (res == B_OK)
361 				res = archiveMsg.FindString("c:tstr", &translated);
362 			if (res == B_OK) {
363 				key.fString = keyStr;
364 				key.fContext = keyCtx;
365 				key.fComment = keyCmt;
366 				fCatMap.Put(key, translated);
367 			}
368 		}
369 		uint32 checkFP = ComputeFingerprint();
370 		if (fFingerprint != checkFP) {
371 			fprintf(stderr, "default-catalog(sig=%s, lang=%s) "
372 				"has wrong fingerprint after load (%ld instead of the %ld). "
373 				"The catalog data may be corrupted, so this catalog is "
374 				"skipped.\n",
375 				fSignature.String(), fLanguageName.String(), checkFP,
376 				fFingerprint);
377 			return B_BAD_DATA;
378 		}
379 	}
380 	return res;
381 }
382 
383 
384 BCatalogAddOn *
385 DefaultCatalog::Instantiate(const char *signature, const char *language,
386 	uint32 fingerprint)
387 {
388 	DefaultCatalog *catalog
389 		= new(std::nothrow) DefaultCatalog(signature, language, fingerprint);
390 	if (catalog && catalog->InitCheck() != B_OK) {
391 		delete catalog;
392 		return NULL;
393 	}
394 	return catalog;
395 }
396 
397 
398 BCatalogAddOn *
399 DefaultCatalog::InstantiateEmbedded(entry_ref *appOrAddOnRef)
400 {
401 	DefaultCatalog *catalog = new(std::nothrow) DefaultCatalog(appOrAddOnRef);
402 	if (catalog && catalog->InitCheck() != B_OK) {
403 		delete catalog;
404 		return NULL;
405 	}
406 	return catalog;
407 }
408 
409 
410 BCatalogAddOn *
411 DefaultCatalog::Create(const char *signature, const char *language)
412 {
413 	DefaultCatalog *catalog
414 		= new(std::nothrow) DefaultCatalog("", signature, language);
415 	if (catalog && catalog->InitCheck() != B_OK) {
416 		delete catalog;
417 		return NULL;
418 	}
419 	return catalog;
420 }
421 
422 
423 const uint8 DefaultCatalog::kDefaultCatalogAddOnPriority = 1;
424 	// give highest priority to our embedded catalog-add-on
425