Statistics
| Revision:

root / src / simple-gettext.c @ 1

History | View | Annotate | Download (9.61 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 */