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