1 /* w32-gettext.c - A simplified version of gettext for use under W32.
2 * Copyright (C) 1995, 1996, 1997, 1999,
3 * 2005, 2007 Free Software Foundation, Inc.
5 * This file is part of JNLIB.
7 * JNLIB is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 3 of
10 * the License, or (at your option) any later version.
12 * JNLIB is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
22 This is a simplified version of gettext written by Ulrich Drepper.
23 It is used for the Win32 version of GnuPG becaise all the overhead
24 of gettext is not needed and we have to do some special Win32
25 stuff. I decided that this is far easier than to tweak gettext for
26 the special 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 module may only be build for Windows or Cygwin32
40 #include <sys/types.h>
43 #include "libjnlib-config.h"
45 #include "stringhelp.h"
49 #include "windows.h" /* For GetModuleFileName. */
51 /* The magic number of the GNU message catalog format. */
52 #define MAGIC 0x950412de
53 #define MAGIC_SWAPPED 0xde120495
55 /* Revision number of the currently used .mo (binary) file format. */
56 #define MO_REVISION_NUMBER 0
59 /* Header for binary .mo file format. */
62 /* The magic number. */
64 /* The revision number of the file format. */
66 /* The number of strings pairs. */
68 /* Offset of table with start offsets of original strings. */
70 /* Offset of table with start offsets of translation strings. */
72 /* Size of hashing table. */
74 /* Offset of first hashing entry. */
80 /* Length of addressed string. */
82 /* Offset of string in file. */
87 struct overflow_space_s
89 struct overflow_space_s
*next
;
99 char *mapped
; /* 0 = not yet mapped, 1 = mapped,
102 struct overflow_space_s
*overflow_space
;
103 struct string_desc
*orig_tab
;
104 struct string_desc
*trans_tab
;
110 static struct loaded_domain
*the_domain
;
112 static __inline__ u32
115 return (i
<< 24) | ((i
& 0xff00) << 8) | ((i
>> 8) & 0xff00) | (i
>> 24);
118 #define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
121 /* We assume to have `unsigned long int' value with at least 32 bits. */
122 #define HASHWORDBITS 32
124 /* The so called `hashpjw' function by P.J. Weinberger
125 [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
126 1986, 1987 Bell Telephone Laboratories, Inc.] */
128 static __inline__ ulong
129 hash_string (const char *str_param
)
131 unsigned long int hval
, g
;
132 const char *str
= str_param
;
138 hval
+= (unsigned long int) *str
++;
139 g
= hval
& ((unsigned long int) 0xf << (HASHWORDBITS
- 4));
142 hval
^= g
>> (HASHWORDBITS
- 8);
150 static struct loaded_domain
*
151 load_domain (const char *filename
)
156 struct mo_file_header
*data
= NULL
;
157 struct loaded_domain
*domain
= NULL
;
161 fp
= fopen( filename
, "rb" );
163 return NULL
; /* Can't open the file. */
164 /* We need to know the size of the file. */
165 if (fstat( fileno(fp
), &st
)
166 || (size
= (size_t)st
.st_size
) != st
.st_size
167 || size
< sizeof (struct mo_file_header
) )
173 data
= jnlib_malloc (size
);
177 return NULL
; /* Out of memory. */
181 read_ptr
= (char *) data
;
186 nb
= fread (read_ptr
, 1, to_read
, fp
);
191 return NULL
; /* Read error. */
199 /* Using the magic number we test whether it is really a message
201 if (data
->magic
!= MAGIC
&& data
->magic
!= MAGIC_SWAPPED
)
203 /* The magic number is wrong: not a message catalog file. */
208 domain
= jnlib_calloc (1, sizeof *domain
);
214 domain
->data
= (char *) data
;
215 domain
->must_swap
= data
->magic
!= MAGIC
;
217 /* Fill in the information about the available tables. */
218 switch (SWAPIT(domain
->must_swap
, data
->revision
))
221 domain
->nstrings
= SWAPIT(domain
->must_swap
, data
->nstrings
);
222 domain
->orig_tab
= (struct string_desc
*)
223 ((char *) data
+ SWAPIT(domain
->must_swap
, data
->orig_tab_offset
));
224 domain
->trans_tab
= (struct string_desc
*)
225 ((char *) data
+ SWAPIT(domain
->must_swap
, data
->trans_tab_offset
));
226 domain
->hash_size
= SWAPIT(domain
->must_swap
, data
->hash_tab_size
);
227 domain
->hash_tab
= (u32
*)
228 ((char *) data
+ SWAPIT(domain
->must_swap
, data
->hash_tab_offset
));
231 default: /* This is an invalid revision. */
233 jnlib_free( domain
);
237 /* Allocate an array to keep track of code page mappings. */
238 domain
->mapped
= jnlib_calloc (1, domain
->nstrings
);
250 /* Set the file used for translations. Pass a NULL to disable
251 translation. A new filename may be set at anytime. WARNING: After
252 changing the filename you should not access any data retrieved by
255 If REGKEY is not NULL, the function tries to selected the language
256 the registry key "Lang" below that key. If in addition the
257 environment variable LANGUAGE has been set, that value will
258 override a value set by the registry key.
261 set_gettext_file ( const char *filename
, const char *regkey
)
263 struct loaded_domain
*domain
= NULL
;
265 if ( filename
&& *filename
)
267 if ( filename
[0] == '/'
268 #ifdef HAVE_DRIVE_LETTERS
269 || ( isalpha(filename
[0])
270 && filename
[1] == ':'
271 && (filename
[2] == '/' || filename
[2] == '\\') )
275 /* absolute path - use it as is */
276 domain
= load_domain( filename
);
278 else if (regkey
) /* Standard. */
280 char *instdir
, *langid
, *fname
;
285 if (!envvar_mode
&& (p
= getenv ("LANGUAGE")) && *p
)
288 langid
= jnlib_malloc (strlen (p
)+1);
292 /* We only make use of the first language given. Strip
294 p
= strchr (langid
, ':');
298 /* In the $LANGUAGE case we do not use the registered
299 installation directory but the one where the gpg
300 binary has been found. */
301 instdir
= jnlib_malloc (MAX_PATH
+5);
302 if ( !instdir
|| !GetModuleFileName (NULL
, instdir
, MAX_PATH
) )
305 jnlib_free (instdir
);
306 return -1; /* Error getting the process' file name. */
308 p
= strrchr (instdir
, DIRSEP_C
);
312 jnlib_free (instdir
);
313 return -1; /* Invalid file name returned. */
319 instdir
= read_w32_registry_string ("HKEY_LOCAL_MACHINE",
321 "Install Directory");
324 langid
= read_w32_registry_string (NULL
, /* HKCU then HKLM */
329 jnlib_free (instdir
);
334 /* Strip stuff after a dot in case the user tried to enter
335 the entire locale syntacs as usual for POSIX. */
336 p
= strchr (langid
, '.');
340 /* Build the key: "<instdir>/<domain>.nls/<langid>.mo" We
341 use a directory below the installation directory with the
342 domain included in case the software has been insalled
343 with other software altogether at the same place. */
344 fname
= jnlib_malloc (strlen (instdir
) + 1 + strlen (filename
) + 5
345 + strlen (langid
) + 3 + 1);
348 jnlib_free (instdir
);
352 strcpy (stpcpy (stpcpy (stpcpy (stpcpy ( stpcpy (fname
,
353 instdir
),"\\"), filename
), ".nls\\"), langid
), ".mo");
354 jnlib_free (instdir
);
357 /* Better make sure that we don't mix forward and backward
358 slashes. It seems that some Windoze versions don't
360 for (p
=fname
; *p
; p
++)
365 domain
= load_domain (fname
);
368 if (!domain
&& envvar_mode
== 1)
370 /* In case it failed, we try again using the registry
384 struct overflow_space_s
*os
, *os2
;
386 jnlib_free ( the_domain
->data
);
387 jnlib_free ( the_domain
->mapped
);
388 for (os
=the_domain
->overflow_space
; os
; os
= os2
)
393 jnlib_free ( the_domain
);
402 get_string( struct loaded_domain
*domain
, u32 idx
)
404 struct overflow_space_s
*os
;
407 p
= domain
->data
+ SWAPIT(domain
->must_swap
, domain
->trans_tab
[idx
].offset
);
408 if (!domain
->mapped
[idx
])
413 domain
->mapped
[idx
] = 1;
416 buf
= utf8_to_native (p
, plen
, -1);
417 buflen
= strlen (buf
);
422 /* There is not enough space for the translation - store it
423 in the overflow_space else and mark that in the mapped
424 array. Because we expect that this won't happen too
425 often, we use a simple linked list. */
426 os
= jnlib_malloc (sizeof *os
+ buflen
);
431 os
->next
= domain
->overflow_space
;
432 domain
->overflow_space
= os
;
436 p
= "ERROR in GETTEXT MALLOC";
440 else if (domain
->mapped
[idx
] == 2)
441 { /* We need to get the string from the overflow_space. */
442 for (os
=domain
->overflow_space
; os
; os
= os
->next
)
444 return (const char*)os
->d
;
445 p
= "ERROR in GETTEXT\n";
447 return (const char*)p
;
453 gettext( const char *msgid
)
455 struct loaded_domain
*domain
;
459 if (!(domain
= the_domain
))
462 /* Locate the MSGID and its translation. */
463 if (domain
->hash_size
> 2 && domain
->hash_tab
)
465 /* Use the hashing table. */
466 u32 len
= strlen (msgid
);
467 u32 hash_val
= hash_string (msgid
);
468 u32 idx
= hash_val
% domain
->hash_size
;
469 u32 incr
= 1 + (hash_val
% (domain
->hash_size
- 2));
470 u32 nstr
= SWAPIT (domain
->must_swap
, domain
->hash_tab
[idx
]);
472 if ( !nstr
) /* Hash table entry is empty. */
475 if (SWAPIT(domain
->must_swap
,
476 domain
->orig_tab
[nstr
- 1].length
) == len
478 domain
->data
+ SWAPIT(domain
->must_swap
,
479 domain
->orig_tab
[nstr
-1].offset
)))
480 return get_string( domain
, nstr
- 1 );
484 if (idx
>= domain
->hash_size
- incr
)
485 idx
-= domain
->hash_size
- incr
;
489 nstr
= SWAPIT (domain
->must_swap
, domain
->hash_tab
[idx
]);
491 goto not_found
; /* Hash table entry is empty. */
493 if ( SWAPIT(domain
->must_swap
,
494 domain
->orig_tab
[nstr
- 1].length
) == len
497 + SWAPIT(domain
->must_swap
,
498 domain
->orig_tab
[nstr
-1].offset
)))
499 return get_string( domain
, nstr
-1 );
504 /* Now we try the default method: binary search in the sorted array
507 top
= domain
->nstrings
;
512 act
= (bottom
+ top
) / 2;
513 cmp_val
= strcmp(msgid
, domain
->data
514 + SWAPIT(domain
->must_swap
,
515 domain
->orig_tab
[act
].offset
));
518 else if (cmp_val
> 0)
521 return get_string (domain
, act
);
530 ngettext (const char *msgid1
, const char *msgid2
, unsigned long int n
)
532 /* We use the simple Germanic plural rule. */
533 return gettext (n
==1? msgid1
: msgid2
);
539 unsigned int cp1
, cp2
;
541 cp1
= GetConsoleCP();
542 cp2
= GetConsoleOutputCP();
544 log_info("InputCP=%u OutputCP=%u\n", cp1
, cp2
);
546 if( !SetConsoleOutputCP( 1252 ) )
547 log_info("SetConsoleOutputCP failed: %s\n", w32_strerror (0));
549 cp1
= GetConsoleCP();
550 cp2
= GetConsoleOutputCP();
551 log_info("InputCP=%u OutputCP=%u after switch1\n", cp1
, cp2
);
554 #endif /* USE_SIMPLE_GETTEXT */