~ubuntu-branches/ubuntu/precise/gnupg2/precise-updates

« back to all changes in this revision

Viewing changes to common/simple-gettext.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Mueller
  • Date: 2005-03-29 10:30:32 UTC
  • Revision ID: james.westby@ubuntu.com-20050329103032-sj42n2ain3ipx310
Tags: upstream-1.9.15
ImportĀ upstreamĀ versionĀ 1.9.15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* simple-gettext.c  - a simplified version of gettext.
 
2
 * Copyright (C) 1995, 1996, 1997, 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 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
 
26
 */
 
27
 
 
28
#include <config.h>
 
29
#ifdef USE_SIMPLE_GETTEXT
 
30
#if !defined (_WIN32) && !defined (__CYGWIN32__)
 
31
#error This file can only be used under Windows or Cygwin32
 
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
 
 
42
#include "util.h"
 
43
#include "sysutils.h"
 
44
 
 
45
/* The magic number of the GNU message catalog format.  */
 
46
#define MAGIC         0x950412de
 
47
#define MAGIC_SWAPPED 0xde120495
 
48
 
 
49
/* Revision number of the currently used .mo (binary) file format.  */
 
50
#define MO_REVISION_NUMBER 0
 
51
 
 
52
 
 
53
/* Header for binary .mo file format.  */
 
54
struct mo_file_header
 
55
{
 
56
  /* The magic number.  */
 
57
  u32 magic;
 
58
  /* The revision number of the file format.  */
 
59
  u32 revision;
 
60
  /* The number of strings pairs.  */
 
61
  u32 nstrings;
 
62
  /* Offset of table with start offsets of original strings.  */
 
63
  u32 orig_tab_offset;
 
64
  /* Offset of table with start offsets of translation strings.  */
 
65
  u32 trans_tab_offset;
 
66
  /* Size of hashing table.  */
 
67
  u32 hash_tab_size;
 
68
  /* Offset of first hashing entry.  */
 
69
  u32 hash_tab_offset;
 
70
};
 
71
 
 
72
struct string_desc
 
73
{
 
74
  /* Length of addressed string.  */
 
75
  u32 length;
 
76
  /* Offset of string in file.  */
 
77
  u32 offset;
 
78
};
 
79
 
 
80
 
 
81
struct overflow_space_s
 
82
{
 
83
  struct overflow_space_s *next;
 
84
  u32 idx;
 
85
  char d[1];
 
86
};
 
87
 
 
88
struct loaded_domain
 
89
{
 
90
  char *data;
 
91
  int must_swap;
 
92
  u32 nstrings;
 
93
  char *mapped;  /* 0 = not yet mapped, 1 = mapped,
 
94
                    2 = mapped to
 
95
                    overflow space */
 
96
  struct overflow_space_s *overflow_space;
 
97
  struct string_desc *orig_tab;
 
98
  struct string_desc *trans_tab;
 
99
  u32 hash_size;
 
100
  u32 *hash_tab;
 
101
};
 
102
 
 
103
 
 
104
static struct loaded_domain *the_domain;
 
105
 
 
106
static __inline__ u32
 
107
do_swap_u32( u32 i )
 
108
{
 
109
  return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24);
 
110
}
 
111
 
 
112
#define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
 
113
 
 
114
 
 
115
/* We assume to have `unsigned long int' value with at least 32 bits.  */
 
116
#define HASHWORDBITS 32
 
117
 
 
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.]  */
 
121
 
 
122
static __inline__ ulong
 
123
hash_string( const char *str_param )
 
124
{
 
125
    unsigned long int hval, g;
 
126
    const char *str = str_param;
 
127
 
 
128
    hval = 0;
 
129
    while (*str != '\0')
 
130
    {
 
131
        hval <<= 4;
 
132
        hval += (unsigned long int) *str++;
 
133
        g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
 
134
        if (g != 0)
 
135
        {
 
136
          hval ^= g >> (HASHWORDBITS - 8);
 
137
          hval ^= g;
 
138
        }
 
139
    }
 
140
    return hval;
 
141
}
 
142
 
 
143
 
 
144
static struct loaded_domain *
 
145
load_domain( const char *filename )
 
146
{
 
147
    FILE *fp;
 
148
    size_t size;
 
149
    struct stat st;
 
150
    struct mo_file_header *data = NULL;
 
151
    struct loaded_domain *domain = NULL;
 
152
    size_t to_read;
 
153
    char *read_ptr;
 
154
 
 
155
    fp = fopen( filename, "rb" );
 
156
    if( !fp )
 
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) ) {
 
162
        fclose( fp );
 
163
        return NULL;
 
164
    }
 
165
 
 
166
    data = malloc( size );
 
167
    if( !data ) {
 
168
        fclose( fp );
 
169
        return NULL; /* out of memory */
 
170
    }
 
171
 
 
172
    to_read = size;
 
173
    read_ptr = (char *) data;
 
174
    do {
 
175
        long int nb = fread( read_ptr, 1, to_read, fp );
 
176
        if( nb < to_read ) {
 
177
            fclose (fp);
 
178
            free(data);
 
179
            return NULL; /* read error */
 
180
        }
 
181
        read_ptr += nb;
 
182
        to_read -= nb;
 
183
    } while( to_read > 0 );
 
184
    fclose (fp);
 
185
 
 
186
    /* Using the magic number we can test whether it really is a message
 
187
     * catalog file.  */
 
188
    if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) {
 
189
        /* The magic number is wrong: not a message catalog file.  */
 
190
        free( data );
 
191
        return NULL;
 
192
    }
 
193
 
 
194
    domain = calloc( 1, sizeof *domain );
 
195
    if( !domain )  {
 
196
        free( data );
 
197
        return NULL;
 
198
    }
 
199
    domain->data = (char *) data;
 
200
    domain->must_swap = data->magic != MAGIC;
 
201
 
 
202
    /* Fill in the information about the available tables.  */
 
203
    switch( SWAPIT(domain->must_swap, data->revision) ) {
 
204
      case 0:
 
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));
 
213
      break;
 
214
 
 
215
      default: /* This is an invalid revision.  */
 
216
        free( data );
 
217
        free( domain );
 
218
        return NULL;
 
219
    }
 
220
 
 
221
    /* Allocate an array to keep track of code page mappings. */
 
222
    domain->mapped = calloc( 1, domain->nstrings );
 
223
    if( !domain->mapped ) {
 
224
        free( data );
 
225
        free( domain );
 
226
        return NULL;
 
227
    }
 
228
 
 
229
    return domain;
 
230
}
 
231
 
 
232
 
 
233
/****************
 
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().
 
238
 */
 
239
int
 
240
set_gettext_file( const char *filename )
 
241
{
 
242
    struct loaded_domain *domain = NULL;
 
243
 
 
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] == '\\') )
 
250
#endif
 
251
           ) {
 
252
            /* absolute path - use it as is */
 
253
            domain = load_domain( filename );
 
254
        }
 
255
        else { /* relative path - append ".mo" and get dir from the environment */
 
256
            char *buf = NULL;
 
257
            char *dir;
 
258
            char *p;
 
259
 
 
260
            dir = read_w32_registry_string( NULL,
 
261
                                            "Control Panel\\Mingw32\\NLS",
 
262
                                            "MODir" );
 
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. */
 
268
                for (p=buf; *p; p++)
 
269
                  {
 
270
                    if (*p == '/')
 
271
                      *p = '\\';
 
272
                  }
 
273
                domain = load_domain( buf );
 
274
                free(buf);
 
275
            }
 
276
            free(dir);
 
277
        }
 
278
        if( !domain )
 
279
            return -1;
 
280
    }
 
281
 
 
282
    if( the_domain ) {
 
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) {
 
287
            os2 = os->next;
 
288
            free (os);
 
289
        }
 
290
        free( the_domain );
 
291
        the_domain = NULL;
 
292
    }
 
293
    the_domain = domain;
 
294
    return 0;
 
295
}
 
296
 
 
297
 
 
298
static const char*
 
299
get_string( struct loaded_domain *domain, u32 idx )
 
300
{
 
301
  struct overflow_space_s *os;
 
302
  char *p;
 
303
 
 
304
  p = domain->data + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset);
 
305
  if (!domain->mapped[idx]) 
 
306
    {
 
307
      size_t plen, buflen;
 
308
      char *buf;
 
309
 
 
310
      domain->mapped[idx] = 1;
 
311
 
 
312
      plen = strlen (p);
 
313
      buf = utf8_to_native (p, plen, -1);
 
314
      buflen = strlen (buf);
 
315
      if (buflen <= plen)
 
316
        strcpy (p, buf);
 
317
      else
 
318
        {
 
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);
 
324
          if (os)
 
325
            {
 
326
              os->idx = idx;
 
327
              strcpy (os->d, buf);
 
328
              os->next = domain->overflow_space;
 
329
              domain->overflow_space = os;
 
330
              p = os->d;
 
331
            }
 
332
          else
 
333
            p = "ERROR in GETTEXT MALLOC";
 
334
        }
 
335
      xfree (buf);
 
336
    }
 
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)
 
340
        if (os->idx == idx)
 
341
          return (const char*)os->d;
 
342
      p = "ERROR in GETTEXT\n";
 
343
    }
 
344
  return (const char*)p;
 
345
}
 
346
 
 
347
 
 
348
 
 
349
const char *
 
350
gettext( const char *msgid )
 
351
{
 
352
    struct loaded_domain *domain;
 
353
    size_t act = 0;
 
354
    size_t top, bottom;
 
355
 
 
356
    if( !(domain = the_domain) )
 
357
        goto not_found;
 
358
 
 
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]);
 
367
 
 
368
        if ( !nstr ) /* Hash table entry is empty.  */
 
369
            goto not_found;
 
370
 
 
371
        if( SWAPIT(domain->must_swap,
 
372
                    domain->orig_tab[nstr - 1].length) == len
 
373
            && !strcmp( msgid,
 
374
                       domain->data + SWAPIT(domain->must_swap,
 
375
                                    domain->orig_tab[nstr - 1].offset)) )
 
376
            return get_string( domain, nstr - 1 );
 
377
 
 
378
        for(;;) {
 
379
            if (idx >= domain->hash_size - incr)
 
380
                idx -= domain->hash_size - incr;
 
381
            else
 
382
                idx += incr;
 
383
 
 
384
            nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]);
 
385
            if( !nstr )
 
386
                goto not_found; /* Hash table entry is empty.  */
 
387
 
 
388
            if ( SWAPIT(domain->must_swap,
 
389
                                domain->orig_tab[nstr - 1].length) == len
 
390
                 && !strcmp (msgid,
 
391
                         domain->data + SWAPIT(domain->must_swap,
 
392
                                           domain->orig_tab[nstr - 1].offset)))
 
393
                return get_string( domain, nstr-1 );
 
394
        }
 
395
        /* NOTREACHED */
 
396
    }
 
397
 
 
398
    /* Now we try the default method:  binary search in the sorted
 
399
       array of messages.  */
 
400
    bottom = 0;
 
401
    top = domain->nstrings;
 
402
    while( bottom < top ) {
 
403
        int cmp_val;
 
404
 
 
405
        act = (bottom + top) / 2;
 
406
        cmp_val = strcmp(msgid, domain->data
 
407
                               + SWAPIT(domain->must_swap,
 
408
                                        domain->orig_tab[act].offset));
 
409
        if (cmp_val < 0)
 
410
            top = act;
 
411
        else if (cmp_val > 0)
 
412
            bottom = act + 1;
 
413
        else
 
414
            return get_string( domain, act );
 
415
    }
 
416
 
 
417
  not_found:
 
418
    return msgid;
 
419
}
 
420
 
 
421
#if 0
 
422
       unsigned int cp1, cp2;
 
423
 
 
424
       cp1 = GetConsoleCP();
 
425
       cp2 = GetConsoleOutputCP();
 
426
 
 
427
       log_info("InputCP=%u  OutputCP=%u\n", cp1, cp2 );
 
428
 
 
429
       if( !SetConsoleOutputCP( 1252 ) )
 
430
            log_info("SetConsoleOutputCP failed: %s\n", w32_strerror (0));
 
431
 
 
432
       cp1 = GetConsoleCP();
 
433
       cp2 = GetConsoleOutputCP();
 
434
       log_info("InputCP=%u  OutputCP=%u after switch1\n", cp1, cp2 );
 
435
#endif
 
436
 
 
437
#endif /* USE_SIMPLE_GETTEXT */