xref: /netbsd-src/external/gpl2/grep/dist/intl/loadmsgcat.c (revision a8fa202a6440953be7b92a8960a811bff58203f4)
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
49 char *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.  */
126 int _nl_msg_cat_cntr;
127 
128 
129 /* Initialize the codeset dependent parts of an opened message catalog.
130    Return the header entry.  */
131 const char *
132 internal_function
_nl_init_domain_conv(domain_file,domain,domainbinding)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.  */
246 void
247 internal_function
_nl_free_domain_conv(domain)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.  */
267 void
268 internal_function
_nl_load_domain(domain_file,domainbinding)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
428 void
429 internal_function
_nl_unload_domain(domain)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