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