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