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