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 */