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

Annotation of /trunk/Src/wptNLS.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 366 - (hide annotations)
Tue Dec 6 12:32:08 2011 UTC (13 years, 2 months ago) by twoaday
File size: 12708 byte(s)
2011-12-06  Timo Schulz  <twoaday@gmx.net>

        * wptKeyPropsDlg.cpp (on_init_dialog): Extra hint in
        the title that the key has been revoked.
	* wptKeyManagerDlg.cpp (keymanger_dlg_proc): Redraw
        list view in case a modal client dialog has been closed.


1 twoaday 273 /* wptNLS.cpp - W32 Native Language Support
2 twoaday 328 * Copyright (C) 2001-2003, 2006, 2008, 2009 Timo Schulz
3 twoaday 273 * Copyright (C) 1995-1999 Free Software Foundation, Inc.
4     *
5 twoaday 278 * This code is a stripped down version of simple-gettext.c
6     * written by by Ulrich Drepper from the GPG project.
7 twoaday 273 *
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 twoaday 328 #include <time.h>
26 twoaday 273 #include <windows.h>
27    
28     #include "wptNLS.h"
29 twoaday 278 #include "wptTypes.h"
30 twoaday 328 #include "wptUtil.h"
31     #include "wptW32API.h"
32     #include "wptVersion.h"
33     #include "StringBuffer.h"
34 twoaday 273
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 twoaday 328 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 twoaday 273 };
78 twoaday 328 typedef struct loaded_domain_s *gettext_domain_t;
79 twoaday 273
80     /* List of all available languages. */
81     struct lang_table_s lang_list[] = {
82     {"en", "English", LANG_ENGLISH},
83     {"de", "German", LANG_GERMAN},
84 twoaday 328 {"ru", "Russian", LANG_RUSSIAN},
85     {NULL, NULL, 0}
86 twoaday 273 };
87    
88 twoaday 278
89 twoaday 328 /* The current gettext domain. */
90     static gettext_domain_t the_domain;
91 twoaday 273
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 twoaday 278
123 twoaday 273 /* Missing W32 functions. */
124     static char*
125 twoaday 328 w32_stpcpy (char *a, const char *b)
126 twoaday 273 {
127     while (*b)
128     *a++ = *b++;
129     *a = 0;
130     return (char*)a;
131     }
132    
133    
134 twoaday 328 static gettext_domain_t
135 twoaday 278 load_domain (const char *filename)
136 twoaday 273 {
137     FILE *fp;
138     size_t size;
139     struct stat st;
140     struct mo_file_header *data = NULL;
141 twoaday 328 gettext_domain_t domain = NULL;
142 twoaday 273 size_t to_read;
143     char *read_ptr;
144    
145 twoaday 278 fp = fopen (filename, "rb");
146     if (!fp)
147     return NULL;
148 twoaday 273 /* we must know about the size of the file */
149 twoaday 328 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 twoaday 273 return NULL;
154     }
155    
156 twoaday 278 data = (struct mo_file_header *) malloc (size);
157     if (!data)
158     BUG (0);
159 twoaday 273
160     to_read = size;
161     read_ptr = (char *) data;
162     do {
163 twoaday 328 size_t nb = fread (read_ptr, 1, to_read, fp);
164     if (nb < to_read) {
165 twoaday 273 fclose (fp);
166     free(data);
167 twoaday 328 return NULL; /* read error */
168 twoaday 273 }
169     read_ptr += nb;
170     to_read -= nb;
171 twoaday 328 } while (to_read > 0);
172 twoaday 273 fclose (fp);
173    
174     /* Using the magic number we can test whether it really is a message
175     * catalog file. */
176 twoaday 328 if (data->magic != MAGIC && data->magic != MAGIC_SWAPPED) {
177 twoaday 273 /* The magic number is wrong: not a message catalog file. */
178 twoaday 328 free (data);
179 twoaday 273 return NULL;
180     }
181    
182 twoaday 328 domain = (struct loaded_domain_s *)calloc (1, sizeof *domain);
183 twoaday 278 if (!domain)
184     BUG (0);
185 twoaday 273 domain->data = (char *) data;
186     domain->must_swap = data->magic != MAGIC;
187    
188     /* Fill in the information about the available tables. */
189 twoaday 328 switch (SWAPIT(domain->must_swap, data->revision)) {
190 twoaday 273 case 0:
191 twoaday 328 domain->nstrings = SWAPIT (domain->must_swap, data->nstrings);
192 twoaday 273 domain->orig_tab = (struct string_desc *)
193 twoaday 328 ((char *) data + SWAPIT (domain->must_swap, data->orig_tab_offset));
194 twoaday 273 domain->trans_tab = (struct string_desc *)
195 twoaday 328 ((char *) data + SWAPIT (domain->must_swap, data->trans_tab_offset));
196     domain->hash_size = SWAPIT (domain->must_swap, data->hash_tab_size);
197 twoaday 273 domain->hash_tab = (DWORD *)
198 twoaday 328 ((char *) data + SWAPIT (domain->must_swap, data->hash_tab_offset));
199 twoaday 273 break;
200    
201     default: /* This is an invalid revision. */
202 twoaday 328 free (data);
203     free (domain);
204 twoaday 273 return NULL;
205     }
206    
207     /* allocate an array to keep track of code page mappings */
208     domain->mapped = (char *)calloc( 1, domain->nstrings );
209 twoaday 278 if (!domain->mapped)
210     BUG (0);
211 twoaday 328
212     domain->file_name = strdup (filename);
213     domain->modify_time = st.st_mtime;
214 twoaday 273 return domain;
215 twoaday 278 }
216 twoaday 273
217    
218 twoaday 328 /* 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 twoaday 349 name = substr (file, 0, p-file);
272     ext = substr (file, p-file+1, strlen(file));
273 twoaday 328 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 twoaday 278 /* Deallocate static resources. */
286     void
287     gettext_free_current_domain (void)
288     {
289 twoaday 328 if (the_domain == NULL)
290 twoaday 278 return;
291     free (the_domain->data);
292     free (the_domain->mapped);
293 twoaday 328 free (the_domain->file_name);
294 twoaday 278 free (the_domain);
295     the_domain = NULL;
296     }
297    
298    
299 twoaday 328 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    
311 twoaday 366 char *modir = get_reg_entry_mo ();
312 twoaday 328 if (!modir)
313     return NULL;
314     if (dir_exist_check (modir)) {
315     free_if_alloc (modir);
316     return NULL;
317     }
318 twoaday 366 StringBuffer s = modir;
319 twoaday 328 if (modir[strlen (modir)-1] != '\\')
320     s += "\\";
321     s += "winpt.mo";
322 twoaday 366
323 twoaday 328 const char *fname = s.getBuffer();
324     if (!file_exist_check (fname))
325     domain = load_domain (fname);
326     free_if_alloc (modir);
327     return domain;
328     }
329    
330    
331     int
332     gettext_set_user_domain (void)
333     {
334     gettext_domain_t domain = NULL;
335     char *file = get_locale_name ("winpt.mo");
336     if (file && !file_exist_check (file))
337     domain = load_domain (file);
338     else
339     domain = load_modir_domain ();
340     free_if_alloc (file);
341     gettext_free_current_domain ();
342     the_domain = domain;
343     return 0;
344     }
345    
346    
347     int
348     gettext_domain_needs_refresh (void)
349     {
350     struct stat st;
351    
352     if (the_domain == NULL || stat (the_domain->file_name, &st))
353     return 0;
354     /* '1' means that the MO file were modified in the mean time
355     and the application should reload the domain. */
356     if (st.st_mtime != the_domain->modify_time)
357     return 1;
358    
359     return 0;
360     }
361    
362 twoaday 273 /* Set the file used for translations. Pass a NULL to disable translation.
363     A new filename may be set at anytime. */
364     int
365 twoaday 278 gettext_set_file (const char *filename, const char *nls_dir)
366 twoaday 273 {
367 twoaday 328 gettext_domain_t domain = NULL;
368 twoaday 273
369 twoaday 278 if (filename && *filename) {
370     if (filename[1] == ':' && filename[2] == '\\')
371     domain = load_domain (filename); /* absolute path - use it as is */
372 twoaday 273 else { /* relative path - append ".mo" and get dir from the environment */
373 twoaday 278 char *buf, *dir;
374 twoaday 273
375 twoaday 278 dir = strdup (nls_dir);
376     if (!dir)
377     BUG (0);
378     buf= (char *)malloc (strlen (dir) + strlen (filename)+1+3+1);
379     strcpy (w32_stpcpy (w32_stpcpy (
380 twoaday 328 w32_stpcpy (buf, dir), "\\"), filename),".mo");
381 twoaday 278 domain = load_domain(buf);
382     free (buf);
383     free (dir);
384 twoaday 273 }
385 twoaday 278 if (!domain)
386 twoaday 273 return -1;
387     }
388    
389 twoaday 328 gettext_free_current_domain ();
390 twoaday 273 the_domain = domain;
391     return 0;
392     }
393    
394    
395     static const char*
396 twoaday 328 get_string (gettext_domain_t domain, DWORD idx)
397 twoaday 273 {
398     char *p = domain->data + SWAPIT(domain->must_swap,
399     domain->trans_tab[idx].offset);
400 twoaday 328 if (!domain->mapped[idx])
401     domain->mapped[idx] = 1;
402 twoaday 366 return (const char *)p;
403 twoaday 273 }
404    
405    
406     const char *
407 twoaday 328 gettext (const char *msgid)
408 twoaday 273 {
409     size_t act = 0;
410     size_t top, bottom;
411 twoaday 328
412 twoaday 366 gettext_domain_t domain = the_domain;
413 twoaday 328 if (domain == NULL)
414 twoaday 273 goto not_found;
415    
416     /* Locate the MSGID and its translation. */
417 twoaday 328 if (domain->hash_size > 2 && domain->hash_tab) {
418 twoaday 273 /* Use the hashing table. */
419     DWORD len = strlen (msgid);
420     DWORD hash_val = hash_string (msgid);
421     DWORD idx = hash_val % domain->hash_size;
422     DWORD incr = 1 + (hash_val % (domain->hash_size - 2));
423     DWORD nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
424    
425 twoaday 328 if (!nstr) /* Hash table entry is empty. */
426 twoaday 273 goto not_found;
427 twoaday 328
428     if (SWAPIT(domain->must_swap,
429 twoaday 273 domain->orig_tab[nstr - 1].length) == len
430 twoaday 328 && !strcmp (msgid,
431     domain->data + SWAPIT (domain->must_swap,
432     domain->orig_tab[nstr - 1].offset)))
433     return get_string (domain, nstr - 1);
434 twoaday 273 for(;;) {
435     if (idx >= domain->hash_size - incr)
436     idx -= domain->hash_size - incr;
437     else
438     idx += incr;
439 twoaday 366 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
440 twoaday 328 if (!nstr)
441 twoaday 273 goto not_found; /* Hash table entry is empty. */
442    
443 twoaday 328 if (SWAPIT (domain->must_swap,
444     domain->orig_tab[nstr - 1].length) == len
445     && !strcmp (msgid,
446     domain->data + SWAPIT (domain->must_swap,
447     domain->orig_tab[nstr - 1].offset)))
448    
449     return get_string (domain, nstr-1);
450 twoaday 273 } /* NOTREACHED */
451     }
452    
453     /* Now we try the default method: binary search in the sorted
454     array of messages. */
455     bottom = 0;
456     top = domain->nstrings;
457 twoaday 366 while (bottom < top) {
458 twoaday 273 act = (bottom + top) / 2;
459 twoaday 366 int cmp_val = strcmp (msgid, domain->data + SWAPIT(domain->must_swap,
460     domain->orig_tab[act].offset));
461 twoaday 273 if (cmp_val < 0)
462     top = act;
463     else if (cmp_val > 0)
464     bottom = act + 1;
465 twoaday 328 else
466     return get_string (domain, act);
467 twoaday 273 }
468    
469     not_found:
470     return msgid;
471     }
472    
473    
474     /* Map the user specific language ID to a gettext conform language string.
475     Example: LANG_GERMAN -> "de" */
476     const char*
477 twoaday 278 gettext_get_langid (void)
478 twoaday 273 {
479 twoaday 366 LANGID lang = GetUserDefaultLangID ();
480 twoaday 273 if (PRIMARYLANGID (lang) == LANG_ENGLISH)
481     return NULL;
482    
483 twoaday 328 for (int i=0; lang_list[i].id; i++) {
484 twoaday 273 if (PRIMARYLANGID (lang) == lang_list[i].langid)
485     return lang_list[i].id;
486     }
487     return NULL;
488     }
489    
490    
491     /* Take a table with control item IDs and their translation
492     and set each text to the translated value. */
493     void
494 twoaday 366 gettext_localize_dialog (HWND dlg, struct gettext_tab_s *tab, const char *title)
495 twoaday 273 {
496 twoaday 328 for (int i=0; tab[i].trans != NULL; i++)
497 twoaday 273 SetDlgItemText (dlg, tab[i].ctlid, tab[i].trans);
498 twoaday 328 if (title != NULL)
499 twoaday 273 SetWindowText (dlg, title);
500     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26