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