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

Contents of /trunk/Src/wptNLS.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 349 - (show annotations)
Thu Dec 1 19:52:31 2011 UTC (13 years, 2 months ago) by twoaday
File size: 12752 byte(s)
2011-12-01  Timo Schulz  <twoaday@gmx.net>

       * wptGroupsDlg.cpp: Removed.
       * wptGroupManager.cpp: Likewise.
	      

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26