sync with sylpheed 0.5.0pre4
[claws.git] / intl / loadmsgcat.c
1 /* Load needed message catalogs.
2    Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
19    This must come before <config.h> because <config.h> may include
20    <features.h>, and once <features.h> has been included, it's too late.  */
21 #ifndef _GNU_SOURCE
22 # define _GNU_SOURCE    1
23 #endif
24
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #ifdef __GNUC__
36 # define alloca __builtin_alloca
37 # define HAVE_ALLOCA 1
38 #else
39 # if defined HAVE_ALLOCA_H || defined _LIBC
40 #  include <alloca.h>
41 # else
42 #  ifdef _AIX
43  #pragma alloca
44 #  else
45 #   ifndef alloca
46 char *alloca ();
47 #   endif
48 #  endif
49 # endif
50 #endif
51
52 #include <stdlib.h>
53 #include <string.h>
54
55 #if defined HAVE_UNISTD_H || defined _LIBC
56 # include <unistd.h>
57 #endif
58
59 #ifdef _LIBC
60 # include <langinfo.h>
61 # include <locale.h>
62 #endif
63
64 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) \
65     || (defined _LIBC && defined _POSIX_MAPPED_FILES)
66 # include <sys/mman.h>
67 # undef HAVE_MMAP
68 # define HAVE_MMAP      1
69 #else
70 # undef HAVE_MMAP
71 #endif
72
73 #include "gettext.h"
74 #include "gettextP.h"
75
76 #ifdef _LIBC
77 # include "../locale/localeinfo.h"
78 #endif
79
80 /* @@ end of prolog @@ */
81
82 #ifdef _LIBC
83 /* Rename the non ISO C functions.  This is required by the standard
84    because some ISO C functions will require linking with this object
85    file and the name space must not be polluted.  */
86 # define open   __open
87 # define close  __close
88 # define read   __read
89 # define mmap   __mmap
90 # define munmap __munmap
91 #endif
92
93 /* Names for the libintl functions are a problem.  They must not clash
94    with existing names and they should follow ANSI C.  But this source
95    code is also used in GNU C Library where the names have a __
96    prefix.  So we have to make a difference here.  */
97 #ifdef _LIBC
98 # define PLURAL_PARSE __gettextparse
99 #else
100 # define PLURAL_PARSE gettextparse__
101 #endif
102
103 /* For those losing systems which don't have `alloca' we have to add
104    some additional code emulating it.  */
105 #ifdef HAVE_ALLOCA
106 # define freea(p) /* nothing */
107 #else
108 # define alloca(n) malloc (n)
109 # define freea(p) free (p)
110 #endif
111
112 /* For systems that distinguish between text and binary I/O.
113    O_BINARY is usually declared in <fcntl.h>. */
114 #if !defined O_BINARY && defined _O_BINARY
115   /* For MSC-compatible compilers.  */
116 # define O_BINARY _O_BINARY
117 # define O_TEXT _O_TEXT
118 #endif
119 #ifdef __BEOS__
120   /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect.  */
121 # undef O_BINARY
122 # undef O_TEXT
123 #endif
124 /* On reasonable systems, binary I/O is the default.  */
125 #ifndef O_BINARY
126 # define O_BINARY 0
127 #endif
128
129 /* We need a sign, whether a new catalog was loaded, which can be associated
130    with all translations.  This is important if the translations are
131    cached by one of GCC's features.  */
132 int _nl_msg_cat_cntr;
133
134 #if (defined __GNUC__ && !defined __APPLE_CC__) \
135     || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
136
137 /* These structs are the constant expression for the germanic plural
138    form determination.  It represents the expression  "n != 1".  */
139 static const struct expression plvar =
140 {
141   .nargs = 0,
142   .operation = var,
143 };
144 static const struct expression plone =
145 {
146   .nargs = 0,
147   .operation = num,
148   .val =
149   {
150     .num = 1
151   }
152 };
153 static struct expression germanic_plural =
154 {
155   .nargs = 2,
156   .operation = not_equal,
157   .val =
158   {
159     .args =
160     {
161       [0] = (struct expression *) &plvar,
162       [1] = (struct expression *) &plone
163     }
164   }
165 };
166
167 # define INIT_GERMANIC_PLURAL()
168
169 #else
170
171 /* For compilers without support for ISO C 99 struct/union initializers:
172    Initialization at run-time.  */
173
174 static struct expression plvar;
175 static struct expression plone;
176 static struct expression germanic_plural;
177
178 static void
179 init_germanic_plural ()
180 {
181   if (plone.val.num == 0)
182     {
183       plvar.nargs = 0;
184       plvar.operation = var;
185
186       plone.nargs = 0;
187       plone.operation = num;
188       plone.val.num = 1;
189
190       germanic_plural.nargs = 2;
191       germanic_plural.operation = not_equal;
192       germanic_plural.val.args[0] = &plvar;
193       germanic_plural.val.args[1] = &plone;
194     }
195 }
196
197 # define INIT_GERMANIC_PLURAL() init_germanic_plural ()
198
199 #endif
200
201
202 /* Initialize the codeset dependent parts of an opened message catalog.
203    Return the header entry.  */
204 const char *
205 internal_function
206 _nl_init_domain_conv (domain_file, domain, domainbinding)
207      struct loaded_l10nfile *domain_file;
208      struct loaded_domain *domain;
209      struct binding *domainbinding;
210 {
211   /* Find out about the character set the file is encoded with.
212      This can be found (in textual form) in the entry "".  If this
213      entry does not exist or if this does not contain the `charset='
214      information, we will assume the charset matches the one the
215      current locale and we don't have to perform any conversion.  */
216   char *nullentry;
217   size_t nullentrylen;
218
219   /* Preinitialize fields, to avoid recursion during _nl_find_msg.  */
220   domain->codeset_cntr =
221     (domainbinding != NULL ? domainbinding->codeset_cntr : 0);
222 #ifdef _LIBC
223   domain->conv = (__gconv_t) -1;
224 #else
225 # if HAVE_ICONV
226   domain->conv = (iconv_t) -1;
227 # endif
228 #endif
229   domain->conv_tab = NULL;
230
231   /* Get the header entry.  */
232   nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen);
233
234   if (nullentry != NULL)
235     {
236 #if defined _LIBC || HAVE_ICONV
237       const char *charsetstr;
238
239       charsetstr = strstr (nullentry, "charset=");
240       if (charsetstr != NULL)
241         {
242           size_t len;
243           char *charset;
244           const char *outcharset;
245
246           charsetstr += strlen ("charset=");
247           len = strcspn (charsetstr, " \t\n");
248
249           charset = (char *) alloca (len + 1);
250 # if defined _LIBC || HAVE_MEMPCPY
251           *((char *) mempcpy (charset, charsetstr, len)) = '\0';
252 # else
253           memcpy (charset, charsetstr, len);
254           charset[len] = '\0';
255 # endif
256
257           /* The output charset should normally be determined by the
258              locale.  But sometimes the locale is not used or not correctly
259              set up, so we provide a possibility for the user to override
260              this.  Moreover, the value specified through
261              bind_textdomain_codeset overrides both.  */
262           if (domainbinding != NULL && domainbinding->codeset != NULL)
263             outcharset = domainbinding->codeset;
264           else
265             {
266               outcharset = getenv ("OUTPUT_CHARSET");
267               if (outcharset == NULL || outcharset[0] == '\0')
268                 {
269 # ifdef _LIBC
270                   outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string;
271 # else
272 #  if HAVE_ICONV
273                   extern const char *locale_charset (void);
274                   outcharset = locale_charset ();
275 #  endif
276 # endif
277                 }
278             }
279
280 # ifdef _LIBC
281           /* We always want to use transliteration.  */
282           outcharset = norm_add_slashes (outcharset, "TRANSLIT");
283           charset = norm_add_slashes (charset, NULL);
284           if (__gconv_open (outcharset, charset, &domain->conv,
285                             GCONV_AVOID_NOCONV)
286               != __GCONV_OK)
287             domain->conv = (__gconv_t) -1;
288 # else
289 #  if HAVE_ICONV
290           /* When using GNU libiconv, we want to use transliteration.  */
291 #   if _LIBICONV_VERSION >= 0x0105
292           len = strlen (outcharset);
293           {
294             char *tmp = (char *) alloca (len + 10 + 1);
295             memcpy (tmp, outcharset, len);
296             memcpy (tmp + len, "//TRANSLIT", 10 + 1);
297             outcharset = tmp;
298           }
299 #   endif
300           domain->conv = iconv_open (outcharset, charset);
301 #   if _LIBICONV_VERSION >= 0x0105
302           freea (outcharset);
303 #   endif
304 #  endif
305 # endif
306
307           freea (charset);
308         }
309 #endif /* _LIBC || HAVE_ICONV */
310     }
311
312   return nullentry;
313 }
314
315 /* Frees the codeset dependent parts of an opened message catalog.  */
316 void
317 internal_function
318 _nl_free_domain_conv (domain)
319      struct loaded_domain *domain;
320 {
321   if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1)
322     free (domain->conv_tab);
323
324 #ifdef _LIBC
325   if (domain->conv != (__gconv_t) -1)
326     __gconv_close (domain->conv);
327 #else
328 # if HAVE_ICONV
329   if (domain->conv != (iconv_t) -1)
330     iconv_close (domain->conv);
331 # endif
332 #endif
333 }
334
335 /* Load the message catalogs specified by FILENAME.  If it is no valid
336    message catalog do nothing.  */
337 void
338 internal_function
339 _nl_load_domain (domain_file, domainbinding)
340      struct loaded_l10nfile *domain_file;
341      struct binding *domainbinding;
342 {
343   int fd;
344   size_t size;
345 #ifdef _LIBC
346   struct stat64 st;
347 #else
348   struct stat st;
349 #endif
350   struct mo_file_header *data = (struct mo_file_header *) -1;
351   int use_mmap = 0;
352   struct loaded_domain *domain;
353   const char *nullentry;
354
355   domain_file->decided = 1;
356   domain_file->data = NULL;
357
358   /* Note that it would be useless to store domainbinding in domain_file
359      because domainbinding might be == NULL now but != NULL later (after
360      a call to bind_textdomain_codeset).  */
361
362   /* If the record does not represent a valid locale the FILENAME
363      might be NULL.  This can happen when according to the given
364      specification the locale file name is different for XPG and CEN
365      syntax.  */
366   if (domain_file->filename == NULL)
367     return;
368
369   /* Try to open the addressed file.  */
370   fd = open (domain_file->filename, O_RDONLY | O_BINARY);
371   if (fd == -1)
372     return;
373
374   /* We must know about the size of the file.  */
375   if (
376 #ifdef _LIBC
377       __builtin_expect (fstat64 (fd, &st) != 0, 0)
378 #else
379       __builtin_expect (fstat (fd, &st) != 0, 0)
380 #endif
381       || __builtin_expect ((size = (size_t) st.st_size) != st.st_size, 0)
382       || __builtin_expect (size < sizeof (struct mo_file_header), 0))
383     {
384       /* Something went wrong.  */
385       close (fd);
386       return;
387     }
388
389 #ifdef HAVE_MMAP
390   /* Now we are ready to load the file.  If mmap() is available we try
391      this first.  If not available or it failed we try to load it.  */
392   data = (struct mo_file_header *) mmap (NULL, size, PROT_READ,
393                                          MAP_PRIVATE, fd, 0);
394
395   if (__builtin_expect (data != (struct mo_file_header *) -1, 1))
396     {
397       /* mmap() call was successful.  */
398       close (fd);
399       use_mmap = 1;
400     }
401 #endif
402
403   /* If the data is not yet available (i.e. mmap'ed) we try to load
404      it manually.  */
405   if (data == (struct mo_file_header *) -1)
406     {
407       size_t to_read;
408       char *read_ptr;
409
410       data = (struct mo_file_header *) malloc (size);
411       if (data == NULL)
412         return;
413
414       to_read = size;
415       read_ptr = (char *) data;
416       do
417         {
418           long int nb = (long int) read (fd, read_ptr, to_read);
419           if (nb <= 0)
420             {
421 #ifdef EINTR
422               if (nb == -1 && errno == EINTR)
423                 continue;
424 #endif
425               close (fd);
426               return;
427             }
428           read_ptr += nb;
429           to_read -= nb;
430         }
431       while (to_read > 0);
432
433       close (fd);
434     }
435
436   /* Using the magic number we can test whether it really is a message
437      catalog file.  */
438   if (__builtin_expect (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED,
439                         0))
440     {
441       /* The magic number is wrong: not a message catalog file.  */
442 #ifdef HAVE_MMAP
443       if (use_mmap)
444         munmap ((caddr_t) data, size);
445       else
446 #endif
447         free (data);
448       return;
449     }
450
451   domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain));
452   if (domain == NULL)
453     return;
454   domain_file->data = domain;
455
456   domain->data = (char *) data;
457   domain->use_mmap = use_mmap;
458   domain->mmap_size = size;
459   domain->must_swap = data->magic != _MAGIC;
460
461   /* Fill in the information about the available tables.  */
462   switch (W (domain->must_swap, data->revision))
463     {
464     case 0:
465       domain->nstrings = W (domain->must_swap, data->nstrings);
466       domain->orig_tab = (struct string_desc *)
467         ((char *) data + W (domain->must_swap, data->orig_tab_offset));
468       domain->trans_tab = (struct string_desc *)
469         ((char *) data + W (domain->must_swap, data->trans_tab_offset));
470       domain->hash_size = W (domain->must_swap, data->hash_tab_size);
471       domain->hash_tab = (nls_uint32 *)
472         ((char *) data + W (domain->must_swap, data->hash_tab_offset));
473       break;
474     default:
475       /* This is an invalid revision.  */
476 #ifdef HAVE_MMAP
477       if (use_mmap)
478         munmap ((caddr_t) data, size);
479       else
480 #endif
481         free (data);
482       free (domain);
483       domain_file->data = NULL;
484       return;
485     }
486
487   /* Now initialize the character set converter from the character set
488      the file is encoded with (found in the header entry) to the domain's
489      specified character set or the locale's character set.  */
490   nullentry = _nl_init_domain_conv (domain_file, domain, domainbinding);
491
492   /* Also look for a plural specification.  */
493   if (nullentry != NULL)
494     {
495       const char *plural;
496       const char *nplurals;
497
498       plural = strstr (nullentry, "plural=");
499       nplurals = strstr (nullentry, "nplurals=");
500       if (plural == NULL || nplurals == NULL)
501         goto no_plural;
502       else
503         {
504           /* First get the number.  */
505           char *endp;
506           unsigned long int n;
507           struct parse_args args;
508
509           nplurals += 9;
510           while (*nplurals != '\0' && isspace (*nplurals))
511             ++nplurals;
512 #if defined HAVE_STRTOUL || defined _LIBC
513           n = strtoul (nplurals, &endp, 10);
514 #else
515           for (endp = nplurals, n = 0; *endp >= '0' && *endp <= '9'; endp++)
516             n = n * 10 + (*endp - '0');
517 #endif
518           domain->nplurals = n;
519           if (nplurals == endp)
520             goto no_plural;
521
522           /* Due to the restrictions bison imposes onto the interface of the
523              scanner function we have to put the input string and the result
524              passed up from the parser into the same structure which address
525              is passed down to the parser.  */
526           plural += 7;
527           args.cp = plural;
528           if (PLURAL_PARSE (&args) != 0)
529             goto no_plural;
530           domain->plural = args.res;
531         }
532     }
533   else
534     {
535       /* By default we are using the Germanic form: singular form only
536          for `one', the plural form otherwise.  Yes, this is also what
537          English is using since English is a Germanic language.  */
538     no_plural:
539       INIT_GERMANIC_PLURAL ();
540       domain->plural = &germanic_plural;
541       domain->nplurals = 2;
542     }
543 }
544
545
546 #ifdef _LIBC
547 void
548 internal_function
549 _nl_unload_domain (domain)
550      struct loaded_domain *domain;
551 {
552   if (domain->plural != &germanic_plural)
553     __gettext_free_exp (domain->plural);
554
555   _nl_free_domain_conv (domain);
556
557 # ifdef _POSIX_MAPPED_FILES
558   if (domain->use_mmap)
559     munmap ((caddr_t) domain->data, domain->mmap_size);
560   else
561 # endif /* _POSIX_MAPPED_FILES */
562     free ((void *) domain->data);
563
564   free (domain);
565 }
566 #endif