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

Contents of /trunk/Src/wptNLS.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 368 - (show annotations)
Tue Dec 6 13:40:04 2011 UTC (13 years, 2 months ago) by twoaday
File size: 12085 byte(s)
2011-12-06  Timo Schulz  <twoaday@gmx.net>

        * wptNLS.cpp (get_user_langid): Removed.
        Use gettext_get_langid() instead. Changed all callers.
        (gettext_set_file): Removed.
        (w32_stpcpy): Likewise.
        * StringBuffer.cpp: Overload '=' operator for 'char*'.


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26