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