1 /* simple-gettext.c - a simplified version of gettext.
2 * Copyright (C) 1995, 1996, 1997, 1999,
3 * 2005 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
7 * GnuPG is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * GnuPG is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
22 /* This is a simplified version of gettext written by Ulrich Drepper.
23 * It is used for the Win32 version of GnuPG beucase all the overhead
24 * of gettext is not needed and we have to do some special Win32 stuff.
25 * I decided that this is far easier than to tweak gettext for the special
26 * cases (I tried it but it is a lot of code). wk 15.09.99
30 #ifdef USE_SIMPLE_GETTEXT
31 #if !defined (_WIN32) && !defined (__CYGWIN32__)
32 #error This file can only be used under Windows or Cygwin32
40 #include <sys/types.h>
46 /* The magic number of the GNU message catalog format. */
47 #define MAGIC 0x950412de
48 #define MAGIC_SWAPPED 0xde120495
50 /* Revision number of the currently used .mo (binary) file format. */
51 #define MO_REVISION_NUMBER 0
54 /* Header for binary .mo file format. */
57 /* The magic number. */
59 /* The revision number of the file format. */
61 /* The number of strings pairs. */
63 /* Offset of table with start offsets of original strings. */
65 /* Offset of table with start offsets of translation strings. */
67 /* Size of hashing table. */
69 /* Offset of first hashing entry. */
75 /* Length of addressed string. */
77 /* Offset of string in file. */
82 struct overflow_space_s
84 struct overflow_space_s *next;
94 char *mapped; /* 0 = not yet mapped, 1 = mapped,
97 struct overflow_space_s *overflow_space;
98 struct string_desc *orig_tab;
99 struct string_desc *trans_tab;
105 static struct loaded_domain *the_domain;
107 static __inline__ u32
110 return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24);
113 #define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
116 /* We assume to have `unsigned long int' value with at least 32 bits. */
117 #define HASHWORDBITS 32
119 /* The so called `hashpjw' function by P.J. Weinberger
120 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
121 1986, 1987 Bell Telephone Laboratories, Inc.] */
123 static __inline__ ulong
124 hash_string( const char *str_param )
126 unsigned long int hval, g;
127 const char *str = str_param;
133 hval += (unsigned long int) *str++;
134 g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
137 hval ^= g >> (HASHWORDBITS - 8);
145 static struct loaded_domain *
146 load_domain( const char *filename )
151 struct mo_file_header *data = NULL;
152 struct loaded_domain *domain = NULL;
156 fp = fopen( filename, "rb" );
158 return NULL; /* can't open the file */
159 /* we must know about the size of the file */
160 if( fstat( fileno(fp ), &st )
161 || (size = (size_t)st.st_size) != st.st_size
162 || size < sizeof (struct mo_file_header) ) {
167 data = malloc( size );
170 return NULL; /* out of memory */
174 read_ptr = (char *) data;
176 long int nb = fread( read_ptr, 1, to_read, fp );
180 return NULL; /* read error */
184 } while( to_read > 0 );
187 /* Using the magic number we can test whether it really is a message
189 if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) {
190 /* The magic number is wrong: not a message catalog file. */
195 domain = calloc( 1, sizeof *domain );
200 domain->data = (char *) data;
201 domain->must_swap = data->magic != MAGIC;
203 /* Fill in the information about the available tables. */
204 switch( SWAPIT(domain->must_swap, data->revision) ) {
206 domain->nstrings = SWAPIT(domain->must_swap, data->nstrings);
207 domain->orig_tab = (struct string_desc *)
208 ((char *) data + SWAPIT(domain->must_swap, data->orig_tab_offset));
209 domain->trans_tab = (struct string_desc *)
210 ((char *) data + SWAPIT(domain->must_swap, data->trans_tab_offset));
211 domain->hash_size = SWAPIT(domain->must_swap, data->hash_tab_size);
212 domain->hash_tab = (u32 *)
213 ((char *) data + SWAPIT(domain->must_swap, data->hash_tab_offset));
216 default: /* This is an invalid revision. */
222 /* Allocate an array to keep track of code page mappings. */
223 domain->mapped = calloc( 1, domain->nstrings );
224 if( !domain->mapped ) {
235 * Set the file used for translations. Pass a NULL to disable
236 * translation. A new filename may be set at anytime. If REGKEY is
237 * not NULL, the function tries to selected the language the registry
238 * key "Lang" below that key. WARNING: After changing the filename you
239 * should not access any data retrieved by gettext().
242 set_gettext_file ( const char *filename, const char *regkey )
244 struct loaded_domain *domain = NULL;
246 if( filename && *filename ) {
247 if( filename[0] == '/'
248 #ifdef HAVE_DRIVE_LETTERS
249 || ( isalpha(filename[0])
250 && filename[1] == ':'
251 && (filename[2] == '/' || filename[2] == '\\') )
254 /* absolute path - use it as is */
255 domain = load_domain( filename );
257 else if (regkey) { /* Standard. */
258 char *instdir, *langid, *fname;
261 instdir = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
263 "Install Directory");
266 langid = read_w32_registry_string (NULL, /* HKCU then HKLM */
273 /* Strip stuff after a dot in case the user tried to enter
274 * the entire locale synatcs as usual for POSIX. */
275 p = strchr (langid, '.');
279 /* Build the key: "<instdir>/<domain>.nls/<langid>.mo" We
280 use a directory below the installation directory with
281 the domain included in case the software has been
282 insalled with other software altogether at the same
284 fname = malloc (strlen (instdir) + 1 + strlen (filename) + 5
285 + strlen (langid) + 3 + 1);
291 strcpy (stpcpy (stpcpy (stpcpy (stpcpy ( stpcpy (fname,
292 instdir),"\\"), filename), ".nls\\"), langid), ".mo");
296 /* Better make sure that we don't mix forward and
297 backward slashes. It seems that some Windoze
298 versions don't accept this. */
299 for (p=fname; *p; p++) {
303 domain = load_domain (fname);
312 struct overflow_space_s *os, *os2;
313 free( the_domain->data );
314 free( the_domain->mapped );
315 for (os=the_domain->overflow_space; os; os = os2) {
328 get_string( struct loaded_domain *domain, u32 idx )
330 struct overflow_space_s *os;
333 p = domain->data + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset);
334 if (!domain->mapped[idx])
339 domain->mapped[idx] = 1;
342 buf = utf8_to_native (p, plen, -1);
343 buflen = strlen (buf);
348 /* There is not enough space for the translation - store it
349 in the overflow_space else and mark that in the mapped
350 array. Because we expect that this won't happen too
351 often, we use a simple linked list. */
352 os = malloc (sizeof *os + buflen);
357 os->next = domain->overflow_space;
358 domain->overflow_space = os;
362 p = "ERROR in GETTEXT MALLOC";
366 else if (domain->mapped[idx] == 2)
367 { /* We need to get the string from the overflow_space. */
368 for (os=domain->overflow_space; os; os = os->next)
370 return (const char*)os->d;
371 p = "ERROR in GETTEXT\n";
373 return (const char*)p;
379 gettext( const char *msgid )
381 struct loaded_domain *domain;
385 if( !(domain = the_domain) )
388 /* Locate the MSGID and its translation. */
389 if( domain->hash_size > 2 && domain->hash_tab ) {
390 /* Use the hashing table. */
391 u32 len = strlen (msgid);
392 u32 hash_val = hash_string (msgid);
393 u32 idx = hash_val % domain->hash_size;
394 u32 incr = 1 + (hash_val % (domain->hash_size - 2));
395 u32 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
397 if ( !nstr ) /* Hash table entry is empty. */
400 if( SWAPIT(domain->must_swap,
401 domain->orig_tab[nstr - 1].length) == len
403 domain->data + SWAPIT(domain->must_swap,
404 domain->orig_tab[nstr - 1].offset)) )
405 return get_string( domain, nstr - 1 );
408 if (idx >= domain->hash_size - incr)
409 idx -= domain->hash_size - incr;
413 nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]);
415 goto not_found; /* Hash table entry is empty. */
417 if ( SWAPIT(domain->must_swap,
418 domain->orig_tab[nstr - 1].length) == len
420 domain->data + SWAPIT(domain->must_swap,
421 domain->orig_tab[nstr - 1].offset)))
422 return get_string( domain, nstr-1 );
427 /* Now we try the default method: binary search in the sorted
428 array of messages. */
430 top = domain->nstrings;
431 while( bottom < top ) {
434 act = (bottom + top) / 2;
435 cmp_val = strcmp(msgid, domain->data
436 + SWAPIT(domain->must_swap,
437 domain->orig_tab[act].offset));
440 else if (cmp_val > 0)
443 return get_string( domain, act );
451 unsigned int cp1, cp2;
453 cp1 = GetConsoleCP();
454 cp2 = GetConsoleOutputCP();
456 log_info("InputCP=%u OutputCP=%u\n", cp1, cp2 );
458 if( !SetConsoleOutputCP( 1252 ) )
459 log_info("SetConsoleOutputCP failed: %s\n", w32_strerror (0));
461 cp1 = GetConsoleCP();
462 cp2 = GetConsoleOutputCP();
463 log_info("InputCP=%u OutputCP=%u after switch1\n", cp1, cp2 );
466 #endif /* USE_SIMPLE_GETTEXT */