1
/* simple-gettext.c - a simplified version of gettext.
2
* Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
6
* GnuPG is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* GnuPG is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21
/* This is a simplified version of gettext written by Ulrich Drepper.
22
* It is used for the Win32 version of GnuPG beucase all the overhead
23
* of gettext is not needed and we have to do some special Win32 stuff.
24
* I decided that this is far easier than to tweak gettext for the special
25
* cases (I tried it but it is a lot of code). wk 15.09.99
29
#ifdef USE_SIMPLE_GETTEXT
30
#if !defined (_WIN32) && !defined (__CYGWIN32__)
31
#error This file can only be used under Windows or Cygwin32
39
#include <sys/types.h>
45
/* The magic number of the GNU message catalog format. */
46
#define MAGIC 0x950412de
47
#define MAGIC_SWAPPED 0xde120495
49
/* Revision number of the currently used .mo (binary) file format. */
50
#define MO_REVISION_NUMBER 0
53
/* Header for binary .mo file format. */
56
/* The magic number. */
58
/* The revision number of the file format. */
60
/* The number of strings pairs. */
62
/* Offset of table with start offsets of original strings. */
64
/* Offset of table with start offsets of translation strings. */
66
/* Size of hashing table. */
68
/* Offset of first hashing entry. */
74
/* Length of addressed string. */
76
/* Offset of string in file. */
81
struct overflow_space_s
83
struct overflow_space_s *next;
93
char *mapped; /* 0 = not yet mapped, 1 = mapped,
96
struct overflow_space_s *overflow_space;
97
struct string_desc *orig_tab;
98
struct string_desc *trans_tab;
104
static struct loaded_domain *the_domain;
106
static __inline__ u32
109
return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24);
112
#define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
115
/* We assume to have `unsigned long int' value with at least 32 bits. */
116
#define HASHWORDBITS 32
118
/* The so called `hashpjw' function by P.J. Weinberger
119
[see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
120
1986, 1987 Bell Telephone Laboratories, Inc.] */
122
static __inline__ ulong
123
hash_string( const char *str_param )
125
unsigned long int hval, g;
126
const char *str = str_param;
132
hval += (unsigned long int) *str++;
133
g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
136
hval ^= g >> (HASHWORDBITS - 8);
144
static struct loaded_domain *
145
load_domain( const char *filename )
150
struct mo_file_header *data = NULL;
151
struct loaded_domain *domain = NULL;
155
fp = fopen( filename, "rb" );
157
return NULL; /* can't open the file */
158
/* we must know about the size of the file */
159
if( fstat( fileno(fp ), &st )
160
|| (size = (size_t)st.st_size) != st.st_size
161
|| size < sizeof (struct mo_file_header) ) {
166
data = malloc( size );
169
return NULL; /* out of memory */
173
read_ptr = (char *) data;
175
long int nb = fread( read_ptr, 1, to_read, fp );
179
return NULL; /* read error */
183
} while( to_read > 0 );
186
/* Using the magic number we can test whether it really is a message
188
if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) {
189
/* The magic number is wrong: not a message catalog file. */
194
domain = calloc( 1, sizeof *domain );
199
domain->data = (char *) data;
200
domain->must_swap = data->magic != MAGIC;
202
/* Fill in the information about the available tables. */
203
switch( SWAPIT(domain->must_swap, data->revision) ) {
205
domain->nstrings = SWAPIT(domain->must_swap, data->nstrings);
206
domain->orig_tab = (struct string_desc *)
207
((char *) data + SWAPIT(domain->must_swap, data->orig_tab_offset));
208
domain->trans_tab = (struct string_desc *)
209
((char *) data + SWAPIT(domain->must_swap, data->trans_tab_offset));
210
domain->hash_size = SWAPIT(domain->must_swap, data->hash_tab_size);
211
domain->hash_tab = (u32 *)
212
((char *) data + SWAPIT(domain->must_swap, data->hash_tab_offset));
215
default: /* This is an invalid revision. */
221
/* Allocate an array to keep track of code page mappings. */
222
domain->mapped = calloc( 1, domain->nstrings );
223
if( !domain->mapped ) {
234
* Set the file used for translations. Pass a NULL to disable
235
* translation. A new filename may be set at anytime.
236
* WARNING: After changing the filename you should not access any data
237
* retrieved by gettext().
240
set_gettext_file( const char *filename )
242
struct loaded_domain *domain = NULL;
244
if( filename && *filename ) {
245
if( filename[0] == '/'
246
#ifdef HAVE_DRIVE_LETTERS
247
|| ( isalpha(filename[0])
248
&& filename[1] == ':'
249
&& (filename[2] == '/' || filename[2] == '\\') )
252
/* absolute path - use it as is */
253
domain = load_domain( filename );
255
else { /* relative path - append ".mo" and get dir from the environment */
260
dir = read_w32_registry_string( NULL,
261
"Control Panel\\Mingw32\\NLS",
263
if( dir && (buf=malloc(strlen(dir)+strlen(filename)+1+3+1)) ) {
264
strcpy(stpcpy(stpcpy(stpcpy( buf, dir),"\\"), filename),".mo");
265
/* Better make sure that we don't mix forward and
266
backward slashes. It seems that some Windoze
267
versions don't accept this. */
273
domain = load_domain( buf );
283
struct overflow_space_s *os, *os2;
284
free( the_domain->data );
285
free( the_domain->mapped );
286
for (os=the_domain->overflow_space; os; os = os2) {
299
get_string( struct loaded_domain *domain, u32 idx )
301
struct overflow_space_s *os;
304
p = domain->data + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset);
305
if (!domain->mapped[idx])
310
domain->mapped[idx] = 1;
313
buf = utf8_to_native (p, plen, -1);
314
buflen = strlen (buf);
319
/* There is not enough space for the translation - store it
320
in the overflow_space else and mark that in the mapped
321
array. Because we expect that this won't happen too
322
often, we use a simple linked list. */
323
os = malloc (sizeof *os + buflen);
328
os->next = domain->overflow_space;
329
domain->overflow_space = os;
333
p = "ERROR in GETTEXT MALLOC";
337
else if (domain->mapped[idx] == 2)
338
{ /* We need to get the string from the overflow_space. */
339
for (os=domain->overflow_space; os; os = os->next)
341
return (const char*)os->d;
342
p = "ERROR in GETTEXT\n";
344
return (const char*)p;
350
gettext( const char *msgid )
352
struct loaded_domain *domain;
356
if( !(domain = the_domain) )
359
/* Locate the MSGID and its translation. */
360
if( domain->hash_size > 2 && domain->hash_tab ) {
361
/* Use the hashing table. */
362
u32 len = strlen (msgid);
363
u32 hash_val = hash_string (msgid);
364
u32 idx = hash_val % domain->hash_size;
365
u32 incr = 1 + (hash_val % (domain->hash_size - 2));
366
u32 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
368
if ( !nstr ) /* Hash table entry is empty. */
371
if( SWAPIT(domain->must_swap,
372
domain->orig_tab[nstr - 1].length) == len
374
domain->data + SWAPIT(domain->must_swap,
375
domain->orig_tab[nstr - 1].offset)) )
376
return get_string( domain, nstr - 1 );
379
if (idx >= domain->hash_size - incr)
380
idx -= domain->hash_size - incr;
384
nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]);
386
goto not_found; /* Hash table entry is empty. */
388
if ( SWAPIT(domain->must_swap,
389
domain->orig_tab[nstr - 1].length) == len
391
domain->data + SWAPIT(domain->must_swap,
392
domain->orig_tab[nstr - 1].offset)))
393
return get_string( domain, nstr-1 );
398
/* Now we try the default method: binary search in the sorted
399
array of messages. */
401
top = domain->nstrings;
402
while( bottom < top ) {
405
act = (bottom + top) / 2;
406
cmp_val = strcmp(msgid, domain->data
407
+ SWAPIT(domain->must_swap,
408
domain->orig_tab[act].offset));
411
else if (cmp_val > 0)
414
return get_string( domain, act );
422
unsigned int cp1, cp2;
424
cp1 = GetConsoleCP();
425
cp2 = GetConsoleOutputCP();
427
log_info("InputCP=%u OutputCP=%u\n", cp1, cp2 );
429
if( !SetConsoleOutputCP( 1252 ) )
430
log_info("SetConsoleOutputCP failed: %s\n", w32_strerror (0));
432
cp1 = GetConsoleCP();
433
cp2 = GetConsoleOutputCP();
434
log_info("InputCP=%u OutputCP=%u after switch1\n", cp1, cp2 );
437
#endif /* USE_SIMPLE_GETTEXT */