/[winpt]/trunk/Src/wptNLS.cpp
ViewVC logotype

Contents of /trunk/Src/wptNLS.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 407 - (show annotations)
Mon Feb 6 19:26:03 2012 UTC (13 years ago) by twoaday
File size: 12210 byte(s)
2012-02-06  Timo Schulz  <twoaday@gmx.net>

        * wptNLS.cpp (gettext_free_current_domain): Replace free()
	        with safe_free():
        * WinPT.cpp: Use log_debug to improve bug tracking capabilities.


1 /* wptNLS.cpp - W32 Native Language Support
2 * Copyright (C) 2001-2003, 2006, 2008, 2009, 2012 Timo Schulz
3 * Copyright (C) 1995-1999 Free Software Foundation, Inc.
4 *
5 * This code is a stripped down version of simple-gettext.c
6 * written by by Ulrich Drepper from the GPG project.
7 *
8 * WinPT is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * WinPT is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <time.h>
26 #include <windows.h>
27
28 #include "wptNLS.h"
29 #include "wptTypes.h"
30 #include "wptUtil.h"
31 #include "wptW32API.h"
32 #include "wptVersion.h"
33 #include "StringBuffer.h"
34
35 /* The magic number of the GNU message catalog format. */
36 #define MAGIC 0x950412de
37 #define MAGIC_SWAPPED 0xde120495
38
39 /* Revision number of the currently used .mo (binary) file format. */
40 #define MO_REVISION_NUMBER 0
41
42 #define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
43
44
45 /* We assume to have `unsigned long int' value with at least 32 bits. */
46 #define HASHWORDBITS 32
47
48 /* Header for binary .mo file format. */
49 struct mo_file_header {
50 DWORD magic; /* The magic number. */
51 DWORD revision; /* The revision number of the file format. */
52 DWORD nstrings; /* The number of strings pairs. */
53 DWORD orig_tab_offset; /* Offset of table with start offsets of original
54 strings. */
55 DWORD trans_tab_offset; /* Offset of table with start offsets of translation
56 strings. */
57 DWORD hash_tab_size; /* Size of hashing table. */
58 DWORD hash_tab_offset; /* Offset of first hashing entry. */
59 };
60
61 struct string_desc {
62 DWORD length; /* Length of addressed string. */
63 DWORD offset; /* Offset of string in file. */
64 };
65
66 struct loaded_domain_s {
67 /* Full file name to the MO file */
68 char *file_name;
69 /* Last modification date of the MO file */
70 time_t modify_time;
71
72 char *data;
73 int must_swap;
74 DWORD nstrings;
75 char *mapped;
76 struct string_desc *orig_tab;
77 struct string_desc *trans_tab;
78 DWORD hash_size;
79 DWORD *hash_tab;
80 };
81 typedef struct loaded_domain_s *gettext_domain_t;
82
83 /* List of all available languages. */
84 struct lang_table_s lang_list[] = {
85 {"en", "English", LANG_ENGLISH},
86 {"de", "German", LANG_GERMAN},
87 {"ru", "Russian", LANG_RUSSIAN},
88 {"nl", "Dutch", LANG_DUTCH},
89 {NULL, NULL, 0}
90 };
91
92
93 /* The current gettext domain. */
94 static gettext_domain_t the_domain;
95
96
97 /* Return TRUE if the native user language is not English
98 and thus, gettext support is available */
99 static bool
100 gettext_is_required (void)
101 {
102 LANGID lang = GetUserDefaultLangID ();
103 if (PRIMARYLANGID (lang) == LANG_ENGLISH)
104 return false;
105 return true;
106 }
107
108
109 static DWORD
110 do_swap_u32 (DWORD i)
111 {
112 return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24);
113 }
114
115
116 /* The so called `hashpjw' function by P.J. Weinberger
117 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
118 1986, 1987 Bell Telephone Laboratories, Inc.] */
119 static DWORD
120 hash_string (const char *str_param)
121 {
122 DWORD hval, g;
123 const char *str = str_param;
124
125 hval = 0;
126 while (*str != '\0') {
127 hval <<= 4;
128 hval += (DWORD) *str++;
129 g = hval & ((DWORD) 0xf << (HASHWORDBITS - 4));
130 if (g != 0) {
131 hval ^= g >> (HASHWORDBITS - 8);
132 hval ^= g;
133 }
134 }
135 return hval;
136 }
137
138
139 static gettext_domain_t
140 load_domain (const char *filename)
141 {
142 FILE *fp;
143 size_t size;
144 struct stat st;
145 struct mo_file_header *data = NULL;
146 gettext_domain_t domain = NULL;
147 size_t to_read;
148 char *read_ptr;
149
150 fp = fopen (filename, "rb");
151 if (!fp)
152 return NULL;
153 /* we must know about the size of the file */
154 if (fstat (fileno(fp), &st) ||
155 (size = (size_t)st.st_size) != (size_t)st.st_size ||
156 size < sizeof (struct mo_file_header)) {
157 fclose (fp);
158 return NULL;
159 }
160
161 data = (struct mo_file_header *) malloc (size);
162 if (!data)
163 BUG (0);
164
165 to_read = size;
166 read_ptr = (char *) data;
167 do {
168 size_t nb = fread (read_ptr, 1, to_read, fp);
169 if (nb < to_read) {
170 fclose (fp);
171 free (data);
172 return NULL; /* read error */
173 }
174 read_ptr += nb;
175 to_read -= nb;
176 } while (to_read > 0);
177 fclose (fp);
178
179 /* Using the magic number we can test whether it really is a message catalog file. */
180 if (data->magic != MAGIC && data->magic != MAGIC_SWAPPED) {
181 /* The magic number is wrong: not a message catalog file. */
182 free (data);
183 return NULL;
184 }
185
186 domain = (struct loaded_domain_s *)calloc (1, sizeof *domain);
187 if (!domain)
188 BUG (0);
189 domain->data = (char *) data;
190 domain->must_swap = data->magic != MAGIC;
191
192 /* Fill in the information about the available tables. */
193 switch (SWAPIT(domain->must_swap, data->revision)) {
194 case 0:
195 domain->nstrings = SWAPIT (domain->must_swap, data->nstrings);
196 domain->orig_tab = (struct string_desc *)
197 ((char *) data + SWAPIT (domain->must_swap, data->orig_tab_offset));
198 domain->trans_tab = (struct string_desc *)
199 ((char *) data + SWAPIT (domain->must_swap, data->trans_tab_offset));
200 domain->hash_size = SWAPIT (domain->must_swap, data->hash_tab_size);
201 domain->hash_tab = (DWORD *)
202 ((char *) data + SWAPIT (domain->must_swap, data->hash_tab_offset));
203 break;
204
205 default: /* This is an invalid revision. */
206 free (data);
207 free (domain);
208 return NULL;
209 }
210
211 /* allocate an array to keep track of code page mappings */
212 domain->mapped = (char *)calloc (1, domain->nstrings);
213 if (!domain->mapped)
214 BUG (0);
215
216 domain->file_name = strdup (filename);
217 domain->modify_time = st.st_mtime;
218 return domain;
219 }
220
221
222 /* To avoid problems that the current directory is not the
223 * same folder the WinPT.exe is located in, we extract the
224 * module directory.
225 */
226 char*
227 get_module_dir (char *buf, DWORD buflen)
228 {
229 if (buflen < 1 || !GetModuleFileName (glob_hinst, buf, buflen-1))
230 return NULL;
231
232 size_t pos = strlen (buf);
233 while (pos > 0 && buf[--pos] != '\\')
234 ;
235 buf[pos + 1] = '\0';
236 return buf;
237 }
238
239
240 /* Returns a localisation name for a specific resource (file).
241 * For instance, lang=DE and file=winpt.chm will become winpt.DE.chm
242 */
243 char*
244 get_locale_name (const char *file)
245 {
246 const char *lang = gettext_get_langid ();
247 char buf[MAX_PATH+1];
248
249 char *p = strrchr (file, '.');
250 if (p == NULL) {
251 p = new char[strlen (file) + strlen (lang) + 8 + 1];
252 sprintf (p, "%s.%s", file, lang);
253 return p;
254 }
255 char *name = substr (file, 0, p - file);
256 char *ext = substr (file, p - file + 1, strlen (file));
257 p = get_module_dir (buf, MAX_PATH);
258 if (!p)
259 BUG (NULL);
260 StringBuffer s = p;
261 s = s + name + "." + lang + "." + ext;
262 p = s.getBufferCopy();
263 free_if_alloc (name);
264 free_if_alloc (ext);
265 return p;
266 }
267
268
269 /* Deallocate static resources. */
270 void
271 gettext_free_current_domain (void)
272 {
273 if (the_domain == NULL)
274 return;
275 safe_free(the_domain->data);
276 safe_free(the_domain->mapped);
277 safe_free(the_domain->file_name);
278 safe_free(the_domain);
279 the_domain = NULL;
280 }
281
282
283 /* Replace and release the old domain and assign a new domain then */
284 static void
285 gettext_update_domain (gettext_domain_t new_dom)
286 {
287 gettext_free_current_domain ();
288 the_domain = new_dom;
289 }
290
291
292 char* get_reg_entry_mo (void);
293
294 /* Fallback code which is used when no new NLS file were found.
295 Then we read the MODir registry key and check if a file named
296 'winpt.mo' exists in this directory. */
297 static gettext_domain_t
298 load_modir_domain (void)
299 {
300 char *modir = get_reg_entry_mo ();
301 if (!modir)
302 return NULL;
303 if (dir_exist_check (modir)) {
304 free_if_alloc (modir);
305 return NULL;
306 }
307 StringBuffer s = modir;
308 if (modir[strlen (modir)-1] != '\\')
309 s += "\\";
310 s += "winpt.mo";
311
312 gettext_domain_t domain = NULL;
313 const char *fname = s.getBuffer();
314 if (!file_exist_check (fname))
315 domain = load_domain (fname);
316 free_if_alloc (modir);
317 return domain;
318 }
319
320
321 /* Try to load the user domain, either from
322 the WinPT.exe directory or from the registry */
323 void
324 gettext_set_user_domain (void)
325 {
326 if (!gettext_is_required ())
327 return;
328
329 gettext_domain_t domain = NULL;
330 char *file = get_locale_name ("winpt.mo");
331 if (file && !file_exist_check (file))
332 domain = load_domain (file);
333 else
334 domain = load_modir_domain ();
335 free_if_alloc (file);
336 gettext_update_domain (domain);
337 }
338
339
340 int
341 gettext_domain_needs_refresh (void)
342 {
343 struct stat st;
344
345 if (the_domain == NULL || stat (the_domain->file_name, &st))
346 return 0;
347 /* '1' means that the MO file were modified in the mean time
348 and the application should reload the domain. */
349 if (st.st_mtime != the_domain->modify_time)
350 return 1;
351
352 return 0;
353 }
354
355 static const char*
356 get_string (gettext_domain_t domain, DWORD idx)
357 {
358 char *p = domain->data + SWAPIT(domain->must_swap,
359 domain->trans_tab[idx].offset);
360 if (!domain->mapped[idx])
361 domain->mapped[idx] = 1;
362 /* FIXME: all text is in UTF-8 and need to be converted to
363 the locale charset. But the caller does not free the data and
364 thus we will get a leak */
365 return (const char *)p;
366 }
367
368
369 const char *
370 gettext (const char *msgid)
371 {
372 size_t act = 0;
373 size_t top, bottom;
374
375 gettext_domain_t domain = the_domain;
376 if (domain == NULL)
377 goto not_found;
378
379 /* Locate the MSGID and its translation. */
380 if (domain->hash_size > 2 && domain->hash_tab) {
381 /* Use the hashing table. */
382 DWORD len = strlen (msgid);
383 DWORD hash_val = hash_string (msgid);
384 DWORD idx = hash_val % domain->hash_size;
385 DWORD incr = 1 + (hash_val % (domain->hash_size - 2));
386 DWORD nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
387
388 if (!nstr) /* Hash table entry is empty. */
389 goto not_found;
390
391 if (SWAPIT(domain->must_swap,
392 domain->orig_tab[nstr - 1].length) == len
393 && !strcmp (msgid,
394 domain->data + SWAPIT (domain->must_swap,
395 domain->orig_tab[nstr - 1].offset)))
396 return get_string (domain, nstr - 1);
397 for(;;) {
398 if (idx >= domain->hash_size - incr)
399 idx -= domain->hash_size - incr;
400 else
401 idx += incr;
402 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
403 if (!nstr)
404 goto not_found; /* Hash table entry is empty. */
405
406 if (SWAPIT (domain->must_swap,
407 domain->orig_tab[nstr - 1].length) == len
408 && !strcmp (msgid,
409 domain->data + SWAPIT (domain->must_swap,
410 domain->orig_tab[nstr - 1].offset)))
411
412 return get_string (domain, nstr-1);
413 } /* NOTREACHED */
414 }
415
416 /* Now we try the default method: binary search in the sorted
417 array of messages. */
418 bottom = 0;
419 top = domain->nstrings;
420 while (bottom < top) {
421 act = (bottom + top) / 2;
422 int cmp_val = strcmp (msgid, domain->data + SWAPIT(domain->must_swap,
423 domain->orig_tab[act].offset));
424 if (cmp_val < 0)
425 top = act;
426 else if (cmp_val > 0)
427 bottom = act + 1;
428 else
429 return get_string (domain, act);
430 }
431
432 not_found:
433 return msgid;
434 }
435
436
437
438 /* Map the user specific language ID to a gettext conform language string.
439 English "en" is defined as the fallback.
440 Example: LANG_GERMAN -> "de" */
441 const char*
442 gettext_get_langid (void)
443 {
444 LANGID lang = GetUserDefaultLangID ();
445
446 for (int i=0; lang_list[i].id; i++) {
447 if (PRIMARYLANGID (lang) == lang_list[i].langid)
448 return lang_list[i].id;
449 }
450
451 return "en";
452 }
453
454
455 /* Take a table with control item IDs and their translation
456 and set each text to the translated value. */
457 void
458 gettext_localize_dialog (HWND dlg, struct gettext_tab_s *tab, const char *title)
459 {
460 for (int i=0; tab[i].trans != NULL; i++)
461 SetDlgItemText (dlg, tab[i].ctlid, tab[i].trans);
462 if (title != NULL)
463 SetWindowText (dlg, title);
464 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26