root / src / simple-gettext.c @ 1
History | View | Annotate | Download (9.6 kB)
| 1 | /* simple-gettext.c - a simplified version of gettext.
|
|---|---|
| 2 | * Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. |
| 3 | * |
| 4 | * This file is part of GnuPG. |
| 5 | * |
| 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. |
| 10 | * |
| 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. |
| 15 | * |
| 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 |
| 19 | */ |
| 20 | |
| 21 | /* This is a simplified version of gettext written by Ulrich Drepper.
|
| 22 | * It is used for the Win32 version of GnuPG becuase 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 |
| 26 | */ |
| 27 | |
| 28 | #include <config.h> |
| 29 | #ifdef USE_SIMPLE_GETTEXT
|
| 30 | #ifndef __MINGW32__
|
| 31 | #error This file can only be used with MinGW32
|
| 32 | #endif
|
| 33 | |
| 34 | #include <stdio.h> |
| 35 | #include <stdlib.h> |
| 36 | #include <string.h> |
| 37 | #include <ctype.h> |
| 38 | #include <errno.h> |
| 39 | #include <sys/types.h> |
| 40 | #include <sys/stat.h> |
| 41 | #include <windows.h> |
| 42 | #include "w32reg.h" |
| 43 | |
| 44 | typedef unsigned int u32; /* That is fine with MingW32 */ |
| 45 | |
| 46 | typedef unsigned long ulong; |
| 47 | |
| 48 | /* The magic number of the GNU message catalog format. */
|
| 49 | #define MAGIC 0x950412de |
| 50 | #define MAGIC_SWAPPED 0xde120495 |
| 51 | |
| 52 | /* Revision number of the currently used .mo (binary) file format. */
|
| 53 | #define MO_REVISION_NUMBER 0 |
| 54 | |
| 55 | |
| 56 | /* Header for binary .mo file format. */
|
| 57 | struct mo_file_header
|
| 58 | {
|
| 59 | /* The magic number. */
|
| 60 | u32 magic; |
| 61 | /* The revision number of the file format. */
|
| 62 | u32 revision; |
| 63 | /* The number of strings pairs. */
|
| 64 | u32 nstrings; |
| 65 | /* Offset of table with start offsets of original strings. */
|
| 66 | u32 orig_tab_offset; |
| 67 | /* Offset of table with start offsets of translation strings. */
|
| 68 | u32 trans_tab_offset; |
| 69 | /* Size of hashing table. */
|
| 70 | u32 hash_tab_size; |
| 71 | /* Offset of first hashing entry. */
|
| 72 | u32 hash_tab_offset; |
| 73 | }; |
| 74 | |
| 75 | struct string_desc
|
| 76 | {
|
| 77 | /* Length of addressed string. */
|
| 78 | u32 length; |
| 79 | /* Offset of string in file. */
|
| 80 | u32 offset; |
| 81 | }; |
| 82 | |
| 83 | |
| 84 | |
| 85 | struct loaded_domain
|
| 86 | {
|
| 87 | char *data;
|
| 88 | int must_swap;
|
| 89 | u32 nstrings; |
| 90 | char *mapped;
|
| 91 | struct string_desc *orig_tab;
|
| 92 | struct string_desc *trans_tab;
|
| 93 | u32 hash_size; |
| 94 | u32 *hash_tab; |
| 95 | }; |
| 96 | |
| 97 | |
| 98 | static struct loaded_domain *the_domain; |
| 99 | |
| 100 | static __inline__ u32
|
| 101 | do_swap_u32( u32 i ) |
| 102 | {
|
| 103 | return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24); |
| 104 | } |
| 105 | |
| 106 | #define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
|
| 107 | |
| 108 | |
| 109 | /* We assume to have `unsigned long int' value with at least 32 bits. */
|
| 110 | #define HASHWORDBITS 32 |
| 111 | |
| 112 | /* The so called `hashpjw' function by P.J. Weinberger
|
| 113 | [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, |
| 114 | 1986, 1987 Bell Telephone Laboratories, Inc.] */ |
| 115 | |
| 116 | static __inline__ ulong
|
| 117 | hash_string( const char *str_param ) |
| 118 | {
|
| 119 | unsigned long int hval, g; |
| 120 | const char *str = str_param; |
| 121 | |
| 122 | hval = 0;
|
| 123 | while (*str != '\0') |
| 124 | {
|
| 125 | hval <<= 4;
|
| 126 | hval += (unsigned long int) *str++; |
| 127 | g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4)); |
| 128 | if (g != 0) |
| 129 | {
|
| 130 | hval ^= g >> (HASHWORDBITS - 8);
|
| 131 | hval ^= g; |
| 132 | } |
| 133 | } |
| 134 | return hval;
|
| 135 | } |
| 136 | |
| 137 | |
| 138 | static struct loaded_domain * |
| 139 | load_domain( const char *filename ) |
| 140 | {
|
| 141 | FILE *fp; |
| 142 | size_t size; |
| 143 | struct stat st;
|
| 144 | struct mo_file_header *data = NULL; |
| 145 | struct loaded_domain *domain = NULL; |
| 146 | size_t to_read; |
| 147 | char *read_ptr;
|
| 148 | |
| 149 | fp = fopen( filename, "rb" );
|
| 150 | if( !fp )
|
| 151 | return NULL; /* can't open the file */ |
| 152 | /* we must know about the size of the file */
|
| 153 | if( fstat( fileno(fp ), &st )
|
| 154 | || (size = (size_t)st.st_size) != st.st_size |
| 155 | || size < sizeof (struct mo_file_header) ) { |
| 156 | fclose( fp ); |
| 157 | return NULL; |
| 158 | } |
| 159 | |
| 160 | data = malloc( size ); |
| 161 | if( !data ) {
|
| 162 | fclose( fp ); |
| 163 | return NULL; /* out of memory */ |
| 164 | } |
| 165 | |
| 166 | to_read = size; |
| 167 | read_ptr = (char *) data;
|
| 168 | do {
|
| 169 | long int nb = fread( read_ptr, 1, to_read, fp ); |
| 170 | if( nb < to_read ) {
|
| 171 | fclose (fp); |
| 172 | free(data); |
| 173 | return NULL; /* read error */ |
| 174 | } |
| 175 | read_ptr += nb; |
| 176 | to_read -= nb; |
| 177 | } while( to_read > 0 ); |
| 178 | fclose (fp); |
| 179 | |
| 180 | /* Using the magic number we can test whether it really is a message
|
| 181 | * catalog file. */ |
| 182 | if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) {
|
| 183 | /* The magic number is wrong: not a message catalog file. */
|
| 184 | free( data ); |
| 185 | return NULL; |
| 186 | } |
| 187 | |
| 188 | domain = calloc( 1, sizeof *domain ); |
| 189 | if( !domain ) {
|
| 190 | free( data ); |
| 191 | return NULL; |
| 192 | } |
| 193 | domain->data = (char *) data;
|
| 194 | domain->must_swap = data->magic != MAGIC; |
| 195 | |
| 196 | /* Fill in the information about the available tables. */
|
| 197 | switch( SWAPIT(domain->must_swap, data->revision) ) {
|
| 198 | case 0: |
| 199 | domain->nstrings = SWAPIT(domain->must_swap, data->nstrings); |
| 200 | domain->orig_tab = (struct string_desc *)
|
| 201 | ((char *) data + SWAPIT(domain->must_swap, data->orig_tab_offset));
|
| 202 | domain->trans_tab = (struct string_desc *)
|
| 203 | ((char *) data + SWAPIT(domain->must_swap, data->trans_tab_offset));
|
| 204 | domain->hash_size = SWAPIT(domain->must_swap, data->hash_tab_size); |
| 205 | domain->hash_tab = (u32 *) |
| 206 | ((char *) data + SWAPIT(domain->must_swap, data->hash_tab_offset));
|
| 207 | break;
|
| 208 | |
| 209 | default: /* This is an invalid revision. */ |
| 210 | free( data ); |
| 211 | free( domain ); |
| 212 | return NULL; |
| 213 | } |
| 214 | |
| 215 | /* allocate an array to keep track of code page mappings */
|
| 216 | domain->mapped = calloc( 1, domain->nstrings );
|
| 217 | if( !domain->mapped ) {
|
| 218 | free( data ); |
| 219 | free( domain ); |
| 220 | return NULL; |
| 221 | } |
| 222 | |
| 223 | return domain;
|
| 224 | } |
| 225 | |
| 226 | |
| 227 | /****************
|
| 228 | * Set the file used for translations. Pass a NULL to disable |
| 229 | * translation. A new filename may be set at anytime. |
| 230 | * WARNING: After changing the filename you shoudl not access any data |
| 231 | * retrieved by gettext(). |
| 232 | */ |
| 233 | int
|
| 234 | set_gettext_file( const char *filename ) |
| 235 | {
|
| 236 | struct loaded_domain *domain = NULL; |
| 237 | |
| 238 | if( filename && *filename ) {
|
| 239 | if( filename[0] == '/' |
| 240 | #ifdef HAVE_DRIVE_LETTERS
|
| 241 | || ( isalpha(filename[0])
|
| 242 | && filename[1] == ':' |
| 243 | && (filename[2] == '/' || filename[2] == '\\') ) |
| 244 | #endif
|
| 245 | ) {
|
| 246 | /* absolute path - use it as is */
|
| 247 | domain = load_domain( filename ); |
| 248 | } |
| 249 | else { /* relative path - append ".mo" and get dir from the environment */ |
| 250 | char *buf = NULL; |
| 251 | char *dir;
|
| 252 | |
| 253 | dir = read_w32_registry_string( NULL,
|
| 254 | "Control Panel\\Mingw32\\NLS",
|
| 255 | "MODir" );
|
| 256 | if( dir && (buf=malloc(strlen(dir)+strlen(filename)+1+3+1)) ) { |
| 257 | strcpy(stpcpy(stpcpy(stpcpy( buf, dir),"\\"), filename),".mo"); |
| 258 | domain = load_domain( buf ); |
| 259 | free(buf); |
| 260 | } |
| 261 | free(dir); |
| 262 | } |
| 263 | if( !domain ) {
|
| 264 | return -1; } |
| 265 | } |
| 266 | if( the_domain ) {
|
| 267 | free( the_domain->data ); |
| 268 | free( the_domain->mapped ); |
| 269 | free( the_domain ); |
| 270 | the_domain = NULL;
|
| 271 | } |
| 272 | the_domain = domain; |
| 273 | return NULL; |
| 274 | } |
| 275 | |
| 276 | |
| 277 | static const char* |
| 278 | get_string( struct loaded_domain *domain, u32 idx )
|
| 279 | {
|
| 280 | char *p = domain->data + SWAPIT(domain->must_swap,
|
| 281 | domain->trans_tab[idx].offset); |
| 282 | |
| 283 | /* status of domain->mapped[idx] is ignored.
|
| 284 | * not sure about the consequences. |
| 285 | * perhaps mapped can entirely be removed? |
| 286 | */ |
| 287 | |
| 288 | /* we assume, strings are already correctly
|
| 289 | * encoded. |
| 290 | */ |
| 291 | |
| 292 | return (const char*)p; |
| 293 | } |
| 294 | |
| 295 | |
| 296 | |
| 297 | const char * |
| 298 | gettext( const char *msgid ) |
| 299 | {
|
| 300 | struct loaded_domain *domain;
|
| 301 | size_t act = 0;
|
| 302 | size_t top, bottom; |
| 303 | |
| 304 | if( !(domain = the_domain) ) {
|
| 305 | goto not_found;
|
| 306 | } |
| 307 | |
| 308 | /* Locate the MSGID and its translation. */
|
| 309 | if( domain->hash_size > 2 && domain->hash_tab ) { |
| 310 | /* Use the hashing table. */
|
| 311 | u32 len = strlen (msgid); |
| 312 | u32 hash_val = hash_string (msgid); |
| 313 | u32 idx = hash_val % domain->hash_size; |
| 314 | u32 incr = 1 + (hash_val % (domain->hash_size - 2)); |
| 315 | u32 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]); |
| 316 | |
| 317 | if ( !nstr ) /* Hash table entry is empty. */ |
| 318 | goto not_found;
|
| 319 | |
| 320 | if( SWAPIT(domain->must_swap,
|
| 321 | domain->orig_tab[nstr - 1].length) == len
|
| 322 | && !strcmp( msgid, |
| 323 | domain->data + SWAPIT(domain->must_swap, |
| 324 | domain->orig_tab[nstr - 1].offset)) )
|
| 325 | return get_string( domain, nstr - 1 ); |
| 326 | |
| 327 | for(;;) {
|
| 328 | if (idx >= domain->hash_size - incr)
|
| 329 | idx -= domain->hash_size - incr; |
| 330 | else
|
| 331 | idx += incr; |
| 332 | |
| 333 | nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]); |
| 334 | if( !nstr )
|
| 335 | goto not_found; /* Hash table entry is empty. */ |
| 336 | |
| 337 | if ( SWAPIT(domain->must_swap,
|
| 338 | domain->orig_tab[nstr - 1].length) == len
|
| 339 | && !strcmp (msgid, |
| 340 | domain->data + SWAPIT(domain->must_swap, |
| 341 | domain->orig_tab[nstr - 1].offset)))
|
| 342 | return get_string( domain, nstr-1 ); |
| 343 | } |
| 344 | /* NOTREACHED */
|
| 345 | } |
| 346 | |
| 347 | /* Now we try the default method: binary search in the sorted
|
| 348 | array of messages. */ |
| 349 | bottom = 0;
|
| 350 | top = domain->nstrings; |
| 351 | while( bottom < top ) {
|
| 352 | int cmp_val;
|
| 353 | |
| 354 | act = (bottom + top) / 2;
|
| 355 | cmp_val = strcmp(msgid, domain->data |
| 356 | + SWAPIT(domain->must_swap, |
| 357 | domain->orig_tab[act].offset)); |
| 358 | if (cmp_val < 0) |
| 359 | top = act; |
| 360 | else if (cmp_val > 0) |
| 361 | bottom = act + 1;
|
| 362 | else
|
| 363 | return get_string( domain, act );
|
| 364 | } |
| 365 | |
| 366 | not_found:
|
| 367 | return msgid;
|
| 368 | } |
| 369 | |
| 370 | #if 0
|
| 371 | unsigned int cp1, cp2; |
| 372 | |
| 373 | cp1 = GetConsoleCP(); |
| 374 | cp2 = GetConsoleOutputCP(); |
| 375 | |
| 376 | log_info("InputCP=%u OutputCP=%u\n", cp1, cp2 );
|
| 377 | |
| 378 | if( !SetConsoleOutputCP( 1252 ) ) |
| 379 | log_info("SetConsoleOutputCP failed: %d\n", (int)GetLastError() );
|
| 380 | |
| 381 | cp1 = GetConsoleCP(); |
| 382 | cp2 = GetConsoleOutputCP(); |
| 383 | log_info("InputCP=%u OutputCP=%u after switch1\n", cp1, cp2 );
|
| 384 | #endif |
| 385 | |
| 386 | #endif /* USE_SIMPLE_GETTEXT */ |