]> git.decadent.org.uk Git - ap-utils.git/blob - intl/loadmsgcat.c
Update config.{sub,guess} in the right place at build time - closes: #534825
[ap-utils.git] / intl / loadmsgcat.c
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
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
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
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
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
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