No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / grep / intl / loadmsgcat.c
blobfde9e44757c53c799439d31da2f30c2644ced199
1 /* $NetBSD$ */
3 /* Load needed message catalogs.
4 Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
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.
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.
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. */
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
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
32 #include <ctype.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
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
55 #include <stdlib.h>
56 #include <string.h>
58 #if defined HAVE_UNISTD_H || defined _LIBC
59 # include <unistd.h>
60 #endif
62 #ifdef _LIBC
63 # include <langinfo.h>
64 # include <locale.h>
65 #endif
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
76 #include "gmo.h"
77 #include "gettextP.h"
78 #include "plural-exp.h"
80 #ifdef _LIBC
81 # include "../locale/localeinfo.h"
82 #endif
84 /* @@ end of prolog @@ */
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
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
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
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;
129 /* Initialize the codeset dependent parts of an opened message catalog.
130 Return the header entry. */
131 const char *
132 internal_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;
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;
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;
158 /* Get the header entry. */
159 nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen);
161 if (nullentry != NULL)
163 #if defined _LIBC || HAVE_ICONV
164 const char *charsetstr;
166 charsetstr = strstr (nullentry, "charset=");
167 if (charsetstr != NULL)
169 size_t len;
170 char *charset;
171 const char *outcharset;
173 charsetstr += strlen ("charset=");
174 len = strcspn (charsetstr, " \t\n");
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
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
193 outcharset = getenv ("OUTPUT_CHARSET");
194 if (outcharset == NULL || outcharset[0] == '\0')
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
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);
223 char *tmp = (char *) alloca (len + 10 + 1);
224 memcpy (tmp, outcharset, len);
225 memcpy (tmp + len, "//TRANSLIT", 10 + 1);
226 outcharset = tmp;
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
237 freea (charset);
239 #endif /* _LIBC || HAVE_ICONV */
242 return nullentry;
245 /* Frees the codeset dependent parts of an opened message catalog. */
246 void
247 internal_function
248 _nl_free_domain_conv (domain)
249 struct loaded_domain *domain;
251 if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1)
252 free (domain->conv_tab);
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
265 /* Load the message catalogs specified by FILENAME. If it is no valid
266 message catalog do nothing. */
267 void
268 internal_function
269 _nl_load_domain (domain_file, domainbinding)
270 struct loaded_l10nfile *domain_file;
271 struct binding *domainbinding;
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;
285 domain_file->decided = 1;
286 domain_file->data = NULL;
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). */
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;
299 /* Try to open the addressed file. */
300 fd = open (domain_file->filename, O_RDONLY | O_BINARY);
301 if (fd == -1)
302 return;
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))
314 /* Something went wrong. */
315 close (fd);
316 return;
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);
325 if (__builtin_expect (data != (struct mo_file_header *) -1, 1))
327 /* mmap() call was successful. */
328 close (fd);
329 use_mmap = 1;
331 #endif
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)
337 size_t to_read;
338 char *read_ptr;
340 data = (struct mo_file_header *) malloc (size);
341 if (data == NULL)
342 return;
344 to_read = size;
345 read_ptr = (char *) data;
348 long int nb = (long int) read (fd, read_ptr, to_read);
349 if (nb <= 0)
351 #ifdef EINTR
352 if (nb == -1 && errno == EINTR)
353 continue;
354 #endif
355 close (fd);
356 return;
358 read_ptr += nb;
359 to_read -= nb;
361 while (to_read > 0);
363 close (fd);
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,
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;
381 domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain));
382 if (domain == NULL)
383 return;
384 domain_file->data = domain;
386 domain->data = (char *) data;
387 domain->use_mmap = use_mmap;
388 domain->mmap_size = size;
389 domain->must_swap = data->magic != _MAGIC;
391 /* Fill in the information about the available tables. */
392 switch (W (domain->must_swap, data->revision))
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;
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);
422 /* Also look for a plural specification. */
423 EXTRACT_PLURAL_EXPRESSION (nullentry, &domain->plural, &domain->nplurals);
427 #ifdef _LIBC
428 void
429 internal_function
430 _nl_unload_domain (domain)
431 struct loaded_domain *domain;
433 if (domain->plural != &__gettext_germanic_plural)
434 __gettext_free_exp (domain->plural);
436 _nl_free_domain_conv (domain);
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);
445 free (domain);
447 #endif