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