db6e7ea6d61dad8341166ffcb2a4797d497e254b
[kopensolaris-gnu/glibc.git] / intl / loadmsgcat.c
1 /* Load needed message catalogs.
2    Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
21    This must come before <config.h> because <config.h> may include
22    <features.h>, and once <features.h> has been included, it's too late.  */
23 #ifndef _GNU_SOURCE
24 # define _GNU_SOURCE    1
25 #endif
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <ctype.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #ifdef __GNUC__
38 # define alloca __builtin_alloca
39 # define HAVE_ALLOCA 1
40 #else
41 # if defined HAVE_ALLOCA_H || defined _LIBC
42 #  include <alloca.h>
43 # else
44 #  ifdef _AIX
45  #pragma alloca
46 #  else
47 #   ifndef alloca
48 char *alloca ();
49 #   endif
50 #  endif
51 # endif
52 #endif
53
54 #include <stdlib.h>
55 #include <string.h>
56
57 #if defined HAVE_UNISTD_H || defined _LIBC
58 # include <unistd.h>
59 #endif
60
61 #ifdef _LIBC
62 # include <langinfo.h>
63 # include <locale.h>
64 #endif
65
66 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) \
67     || (defined _LIBC && defined _POSIX_MAPPED_FILES)
68 # include <sys/mman.h>
69 # undef HAVE_MMAP
70 # define HAVE_MMAP      1
71 #else
72 # undef HAVE_MMAP
73 #endif
74
75 #include "gettext.h"
76 #include "gettextP.h"
77 #include "plural-exp.h"
78
79 #ifdef _LIBC
80 # include "../locale/localeinfo.h"
81 #endif
82
83 /* @@ end of prolog @@ */
84
85 #ifdef _LIBC
86 /* Rename the non ISO C functions.  This is required by the standard
87    because some ISO C functions will require linking with this object
88    file and the name space must not be polluted.  */
89 # define open   __open
90 # define close  __close
91 # define read   __read
92 # define mmap   __mmap
93 # define munmap __munmap
94 #endif
95
96 /* For those losing systems which don't have `alloca' we have to add
97    some additional code emulating it.  */
98 #ifdef HAVE_ALLOCA
99 # define freea(p) /* nothing */
100 #else
101 # define alloca(n) malloc (n)
102 # define freea(p) free (p)
103 #endif
104
105 /* We need a sign, whether a new catalog was loaded, which can be associated
106    with all translations.  This is important if the translations are
107    cached by one of GCC's features.  */
108 int _nl_msg_cat_cntr;
109
110
111 /* Initialize the codeset dependent parts of an opened message catalog.
112    Return the header entry.  */
113 const char *
114 internal_function
115 _nl_init_domain_conv (domain_file, domain, domainbinding)
116      struct loaded_l10nfile *domain_file;
117      struct loaded_domain *domain;
118      struct binding *domainbinding;
119 {
120   /* Find out about the character set the file is encoded with.
121      This can be found (in textual form) in the entry "".  If this
122      entry does not exist or if this does not contain the `charset='
123      information, we will assume the charset matches the one the
124      current locale and we don't have to perform any conversion.  */
125   char *nullentry;
126   size_t nullentrylen;
127
128   /* Preinitialize fields, to avoid recursion during _nl_find_msg.  */
129   domain->codeset_cntr =
130     (domainbinding != NULL ? domainbinding->codeset_cntr : 0);
131 #ifdef _LIBC
132   domain->conv = (__gconv_t) -1;
133 #else
134 # if HAVE_ICONV
135   domain->conv = (iconv_t) -1;
136 # endif
137 #endif
138   domain->conv_tab = NULL;
139
140   /* Get the header entry.  */
141   nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen);
142
143   if (nullentry != NULL)
144     {
145 #if defined _LIBC || HAVE_ICONV
146       const char *charsetstr;
147
148       charsetstr = strstr (nullentry, "charset=");
149       if (charsetstr != NULL)
150         {
151           size_t len;
152           char *charset;
153           const char *outcharset;
154
155           charsetstr += strlen ("charset=");
156           len = strcspn (charsetstr, " \t\n");
157
158           charset = (char *) alloca (len + 1);
159 # if defined _LIBC || HAVE_MEMPCPY
160           *((char *) mempcpy (charset, charsetstr, len)) = '\0';
161 # else
162           memcpy (charset, charsetstr, len);
163           charset[len] = '\0';
164 # endif
165
166           /* The output charset should normally be determined by the
167              locale.  But sometimes the locale is not used or not correctly
168              set up, so we provide a possibility for the user to override
169              this.  Moreover, the value specified through
170              bind_textdomain_codeset overrides both.  */
171           if (domainbinding != NULL && domainbinding->codeset != NULL)
172             outcharset = domainbinding->codeset;
173           else
174             {
175               outcharset = getenv ("OUTPUT_CHARSET");
176               if (outcharset == NULL || outcharset[0] == '\0')
177                 {
178 # ifdef _LIBC
179                   outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string;
180 # else
181 #  if HAVE_ICONV
182                   extern const char *locale_charset (void);
183                   outcharset = locale_charset ();
184 #  endif
185 # endif
186                 }
187             }
188
189 # ifdef _LIBC
190           /* We always want to use transliteration.  */
191           outcharset = norm_add_slashes (outcharset, "TRANSLIT");
192           charset = norm_add_slashes (charset, NULL);
193           if (__gconv_open (outcharset, charset, &domain->conv,
194                             GCONV_AVOID_NOCONV)
195               != __GCONV_OK)
196             domain->conv = (__gconv_t) -1;
197 # else
198 #  if HAVE_ICONV
199           /* When using GNU libc >= 2.2 or GNU libiconv >= 1.5,
200              we want to use transliteration.  */
201 #   if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 \
202        || _LIBICONV_VERSION >= 0x0105
203           len = strlen (outcharset);
204           if (len < 10 || strcmp (outcharset + len - 9, "/TRANSLIT") != 0)
205             {
206               char *tmp = (char *) alloca (len + 10 + 1);
207               memcpy (tmp, outcharset, len);
208               memcpy (tmp + len, "//TRANSLIT", 10 + 1);
209               outcharset = tmp;
210             }
211 #   endif
212           domain->conv = iconv_open (outcharset, charset);
213 #   if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 \
214        || _LIBICONV_VERSION >= 0x0105
215           freea (outcharset);
216 #   endif
217 #  endif
218 # endif
219
220           freea (charset);
221         }
222 #endif /* _LIBC || HAVE_ICONV */
223     }
224
225   return nullentry;
226 }
227
228 /* Frees the codeset dependent parts of an opened message catalog.  */
229 void
230 internal_function
231 _nl_free_domain_conv (domain)
232      struct loaded_domain *domain;
233 {
234   if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1)
235     free (domain->conv_tab);
236
237 #ifdef _LIBC
238   if (domain->conv != (__gconv_t) -1)
239     __gconv_close (domain->conv);
240 #else
241 # if HAVE_ICONV
242   if (domain->conv != (iconv_t) -1)
243     iconv_close (domain->conv);
244 # endif
245 #endif
246 }
247
248 /* Load the message catalogs specified by FILENAME.  If it is no valid
249    message catalog do nothing.  */
250 void
251 internal_function
252 _nl_load_domain (domain_file, domainbinding)
253      struct loaded_l10nfile *domain_file;
254      struct binding *domainbinding;
255 {
256   int fd;
257   size_t size;
258 #ifdef _LIBC
259   struct stat64 st;
260 #else
261   struct stat st;
262 #endif
263   struct mo_file_header *data = (struct mo_file_header *) -1;
264   int use_mmap = 0;
265   struct loaded_domain *domain;
266   const char *nullentry;
267
268   domain_file->decided = 1;
269   domain_file->data = NULL;
270
271   /* Note that it would be useless to store domainbinding in domain_file
272      because domainbinding might be == NULL now but != NULL later (after
273      a call to bind_textdomain_codeset).  */
274
275   /* If the record does not represent a valid locale the FILENAME
276      might be NULL.  This can happen when according to the given
277      specification the locale file name is different for XPG and CEN
278      syntax.  */
279   if (domain_file->filename == NULL)
280     return;
281
282   /* Try to open the addressed file.  */
283   fd = open (domain_file->filename, O_RDONLY);
284   if (fd == -1)
285     return;
286
287   /* We must know about the size of the file.  */
288   if (
289 #ifdef _LIBC
290       __builtin_expect (fstat64 (fd, &st) != 0, 0)
291 #else
292       __builtin_expect (fstat (fd, &st) != 0, 0)
293 #endif
294       || __builtin_expect ((size = (size_t) st.st_size) != st.st_size, 0)
295       || __builtin_expect (size < sizeof (struct mo_file_header), 0))
296     {
297       /* Something went wrong.  */
298       close (fd);
299       return;
300     }
301
302 #ifdef HAVE_MMAP
303   /* Now we are ready to load the file.  If mmap() is available we try
304      this first.  If not available or it failed we try to load it.  */
305   data = (struct mo_file_header *) mmap (NULL, size, PROT_READ,
306                                          MAP_PRIVATE, fd, 0);
307
308   if (__builtin_expect (data != (struct mo_file_header *) -1, 1))
309     {
310       /* mmap() call was successful.  */
311       close (fd);
312       use_mmap = 1;
313     }
314 #endif
315
316   /* If the data is not yet available (i.e. mmap'ed) we try to load
317      it manually.  */
318   if (data == (struct mo_file_header *) -1)
319     {
320       size_t to_read;
321       char *read_ptr;
322
323       data = (struct mo_file_header *) malloc (size);
324       if (data == NULL)
325         return;
326
327       to_read = size;
328       read_ptr = (char *) data;
329       do
330         {
331           long int nb = (long int) read (fd, read_ptr, to_read);
332           if (nb <= 0)
333             {
334 #ifdef EINTR
335               if (nb == -1 && errno == EINTR)
336                 continue;
337 #endif
338               close (fd);
339               return;
340             }
341           read_ptr += nb;
342           to_read -= nb;
343         }
344       while (to_read > 0);
345
346       close (fd);
347     }
348
349   /* Using the magic number we can test whether it really is a message
350      catalog file.  */
351   if (__builtin_expect (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED,
352                         0))
353     {
354       /* The magic number is wrong: not a message catalog file.  */
355 #ifdef HAVE_MMAP
356       if (use_mmap)
357         munmap ((caddr_t) data, size);
358       else
359 #endif
360         free (data);
361       return;
362     }
363
364   domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain));
365   if (domain == NULL)
366     return;
367   domain_file->data = domain;
368
369   domain->data = (char *) data;
370   domain->use_mmap = use_mmap;
371   domain->mmap_size = size;
372   domain->must_swap = data->magic != _MAGIC;
373
374   /* Fill in the information about the available tables.  */
375   switch (W (domain->must_swap, data->revision))
376     {
377     case 0:
378       domain->nstrings = W (domain->must_swap, data->nstrings);
379       domain->orig_tab = (struct string_desc *)
380         ((char *) data + W (domain->must_swap, data->orig_tab_offset));
381       domain->trans_tab = (struct string_desc *)
382         ((char *) data + W (domain->must_swap, data->trans_tab_offset));
383       domain->hash_size = W (domain->must_swap, data->hash_tab_size);
384       domain->hash_tab = (nls_uint32 *)
385         ((char *) data + W (domain->must_swap, data->hash_tab_offset));
386       break;
387     default:
388       /* This is an invalid revision.  */
389 #ifdef HAVE_MMAP
390       if (use_mmap)
391         munmap ((caddr_t) data, size);
392       else
393 #endif
394         free (data);
395       free (domain);
396       domain_file->data = NULL;
397       return;
398     }
399
400   /* Now initialize the character set converter from the character set
401      the file is encoded with (found in the header entry) to the domain's
402      specified character set or the locale's character set.  */
403   nullentry = _nl_init_domain_conv (domain_file, domain, domainbinding);
404
405   /* Also look for a plural specification.  */
406   EXTRACT_PLURAL_EXPRESSION (nullentry, &domain->plural, &domain->nplurals);
407 }
408
409
410 #ifdef _LIBC
411 void
412 internal_function
413 _nl_unload_domain (domain)
414      struct loaded_domain *domain;
415 {
416   if (domain->plural != &__gettext_germanic_plural)
417     __gettext_free_exp (domain->plural);
418
419   _nl_free_domain_conv (domain);
420
421 # ifdef _POSIX_MAPPED_FILES
422   if (domain->use_mmap)
423     munmap ((caddr_t) domain->data, domain->mmap_size);
424   else
425 # endif /* _POSIX_MAPPED_FILES */
426     free ((void *) domain->data);
427
428   free (domain);
429 }
430 #endif