]>
Commit | Line | Data |
---|---|---|
705db0b5 | 1 | /* Load needed message catalogs. |
1e24cc5b | 2 | Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc. |
705db0b5 AD |
3 | |
4 | This program is free software; you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | 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 | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program; if not, write to the Free Software Foundation, | |
16 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
17 | ||
1e24cc5b AD |
18 | /* Tell glibc's <string.h> to provide a prototype for mempcpy(). |
19 | This must come before <config.h> because <config.h> may include | |
20 | <features.h>, and once <features.h> has been included, it's too late. */ | |
21 | #ifndef _GNU_SOURCE | |
22 | # define _GNU_SOURCE 1 | |
23 | #endif | |
24 | ||
705db0b5 AD |
25 | #ifdef HAVE_CONFIG_H |
26 | # include <config.h> | |
27 | #endif | |
28 | ||
1e24cc5b AD |
29 | #include <ctype.h> |
30 | #include <errno.h> | |
705db0b5 AD |
31 | #include <fcntl.h> |
32 | #include <sys/types.h> | |
33 | #include <sys/stat.h> | |
34 | ||
1e24cc5b AD |
35 | #ifdef __GNUC__ |
36 | # define alloca __builtin_alloca | |
37 | # define HAVE_ALLOCA 1 | |
38 | #else | |
39 | # if defined HAVE_ALLOCA_H || defined _LIBC | |
40 | # include <alloca.h> | |
41 | # else | |
42 | # ifdef _AIX | |
43 | #pragma alloca | |
44 | # else | |
45 | # ifndef alloca | |
46 | char *alloca (); | |
47 | # endif | |
48 | # endif | |
49 | # endif | |
705db0b5 AD |
50 | #endif |
51 | ||
1e24cc5b AD |
52 | #include <stdlib.h> |
53 | #include <string.h> | |
54 | ||
705db0b5 AD |
55 | #if defined HAVE_UNISTD_H || defined _LIBC |
56 | # include <unistd.h> | |
57 | #endif | |
58 | ||
1e24cc5b AD |
59 | #ifdef _LIBC |
60 | # include <langinfo.h> | |
61 | # include <locale.h> | |
62 | #endif | |
63 | ||
64 | #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) \ | |
65 | || (defined _LIBC && defined _POSIX_MAPPED_FILES) | |
705db0b5 | 66 | # include <sys/mman.h> |
1e24cc5b AD |
67 | # undef HAVE_MMAP |
68 | # define HAVE_MMAP 1 | |
69 | #else | |
70 | # undef HAVE_MMAP | |
705db0b5 AD |
71 | #endif |
72 | ||
73 | #include "gettext.h" | |
74 | #include "gettextP.h" | |
75 | ||
1e24cc5b AD |
76 | #ifdef _LIBC |
77 | # include "../locale/localeinfo.h" | |
78 | #endif | |
79 | ||
705db0b5 AD |
80 | /* @@ end of prolog @@ */ |
81 | ||
82 | #ifdef _LIBC | |
83 | /* Rename the non ISO C functions. This is required by the standard | |
84 | because some ISO C functions will require linking with this object | |
85 | file and the name space must not be polluted. */ | |
86 | # define open __open | |
87 | # define close __close | |
88 | # define read __read | |
89 | # define mmap __mmap | |
90 | # define munmap __munmap | |
91 | #endif | |
92 | ||
1e24cc5b AD |
93 | /* Names for the libintl functions are a problem. They must not clash |
94 | with existing names and they should follow ANSI C. But this source | |
95 | code is also used in GNU C Library where the names have a __ | |
96 | prefix. So we have to make a difference here. */ | |
97 | #ifdef _LIBC | |
98 | # define PLURAL_PARSE __gettextparse | |
99 | #else | |
100 | # define PLURAL_PARSE gettextparse__ | |
101 | #endif | |
102 | ||
103 | /* For those losing systems which don't have `alloca' we have to add | |
104 | some additional code emulating it. */ | |
105 | #ifdef HAVE_ALLOCA | |
106 | # define freea(p) /* nothing */ | |
107 | #else | |
108 | # define alloca(n) malloc (n) | |
109 | # define freea(p) free (p) | |
110 | #endif | |
111 | ||
112 | /* For systems that distinguish between text and binary I/O. | |
113 | O_BINARY is usually declared in <fcntl.h>. */ | |
114 | #if !defined O_BINARY && defined _O_BINARY | |
115 | /* For MSC-compatible compilers. */ | |
116 | # define O_BINARY _O_BINARY | |
117 | # define O_TEXT _O_TEXT | |
118 | #endif | |
119 | #ifdef __BEOS__ | |
120 | /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect. */ | |
121 | # undef O_BINARY | |
122 | # undef O_TEXT | |
123 | #endif | |
124 | /* On reasonable systems, binary I/O is the default. */ | |
125 | #ifndef O_BINARY | |
126 | # define O_BINARY 0 | |
127 | #endif | |
128 | ||
705db0b5 AD |
129 | /* We need a sign, whether a new catalog was loaded, which can be associated |
130 | with all translations. This is important if the translations are | |
131 | cached by one of GCC's features. */ | |
1e24cc5b AD |
132 | int _nl_msg_cat_cntr; |
133 | ||
134 | #if (defined __GNUC__ && !defined __APPLE_CC__) \ | |
135 | || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) | |
136 | ||
137 | /* These structs are the constant expression for the germanic plural | |
138 | form determination. It represents the expression "n != 1". */ | |
139 | static const struct expression plvar = | |
140 | { | |
141 | .nargs = 0, | |
142 | .operation = var, | |
143 | }; | |
144 | static const struct expression plone = | |
145 | { | |
146 | .nargs = 0, | |
147 | .operation = num, | |
148 | .val = | |
149 | { | |
150 | .num = 1 | |
151 | } | |
152 | }; | |
153 | static struct expression germanic_plural = | |
154 | { | |
155 | .nargs = 2, | |
156 | .operation = not_equal, | |
157 | .val = | |
158 | { | |
159 | .args = | |
160 | { | |
161 | [0] = (struct expression *) &plvar, | |
162 | [1] = (struct expression *) &plone | |
163 | } | |
164 | } | |
165 | }; | |
166 | ||
167 | # define INIT_GERMANIC_PLURAL() | |
168 | ||
169 | #else | |
170 | ||
171 | /* For compilers without support for ISO C 99 struct/union initializers: | |
172 | Initialization at run-time. */ | |
173 | ||
174 | static struct expression plvar; | |
175 | static struct expression plone; | |
176 | static struct expression germanic_plural; | |
177 | ||
178 | static void | |
179 | init_germanic_plural () | |
180 | { | |
181 | if (plone.val.num == 0) | |
182 | { | |
183 | plvar.nargs = 0; | |
184 | plvar.operation = var; | |
185 | ||
186 | plone.nargs = 0; | |
187 | plone.operation = num; | |
188 | plone.val.num = 1; | |
189 | ||
190 | germanic_plural.nargs = 2; | |
191 | germanic_plural.operation = not_equal; | |
192 | germanic_plural.val.args[0] = &plvar; | |
193 | germanic_plural.val.args[1] = &plone; | |
194 | } | |
195 | } | |
196 | ||
197 | # define INIT_GERMANIC_PLURAL() init_germanic_plural () | |
198 | ||
199 | #endif | |
200 | ||
201 | ||
202 | /* Initialize the codeset dependent parts of an opened message catalog. | |
203 | Return the header entry. */ | |
204 | const char * | |
205 | internal_function | |
206 | _nl_init_domain_conv (domain_file, domain, domainbinding) | |
207 | struct loaded_l10nfile *domain_file; | |
208 | struct loaded_domain *domain; | |
209 | struct binding *domainbinding; | |
210 | { | |
211 | /* Find out about the character set the file is encoded with. | |
212 | This can be found (in textual form) in the entry "". If this | |
213 | entry does not exist or if this does not contain the `charset=' | |
214 | information, we will assume the charset matches the one the | |
215 | current locale and we don't have to perform any conversion. */ | |
216 | char *nullentry; | |
217 | size_t nullentrylen; | |
218 | ||
219 | /* Preinitialize fields, to avoid recursion during _nl_find_msg. */ | |
220 | domain->codeset_cntr = | |
221 | (domainbinding != NULL ? domainbinding->codeset_cntr : 0); | |
222 | #ifdef _LIBC | |
223 | domain->conv = (__gconv_t) -1; | |
224 | #else | |
225 | # if HAVE_ICONV | |
226 | domain->conv = (iconv_t) -1; | |
227 | # endif | |
228 | #endif | |
229 | domain->conv_tab = NULL; | |
705db0b5 | 230 | |
1e24cc5b AD |
231 | /* Get the header entry. */ |
232 | nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen); | |
233 | ||
234 | if (nullentry != NULL) | |
235 | { | |
236 | #if defined _LIBC || HAVE_ICONV | |
237 | const char *charsetstr; | |
238 | ||
239 | charsetstr = strstr (nullentry, "charset="); | |
240 | if (charsetstr != NULL) | |
241 | { | |
242 | size_t len; | |
243 | char *charset; | |
244 | const char *outcharset; | |
245 | ||
246 | charsetstr += strlen ("charset="); | |
247 | len = strcspn (charsetstr, " \t\n"); | |
248 | ||
249 | charset = (char *) alloca (len + 1); | |
250 | # if defined _LIBC || HAVE_MEMPCPY | |
251 | *((char *) mempcpy (charset, charsetstr, len)) = '\0'; | |
252 | # else | |
253 | memcpy (charset, charsetstr, len); | |
254 | charset[len] = '\0'; | |
255 | # endif | |
256 | ||
257 | /* The output charset should normally be determined by the | |
258 | locale. But sometimes the locale is not used or not correctly | |
259 | set up, so we provide a possibility for the user to override | |
260 | this. Moreover, the value specified through | |
261 | bind_textdomain_codeset overrides both. */ | |
262 | if (domainbinding != NULL && domainbinding->codeset != NULL) | |
263 | outcharset = domainbinding->codeset; | |
264 | else | |
265 | { | |
266 | outcharset = getenv ("OUTPUT_CHARSET"); | |
267 | if (outcharset == NULL || outcharset[0] == '\0') | |
268 | { | |
269 | # ifdef _LIBC | |
270 | outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string; | |
271 | # else | |
272 | # if HAVE_ICONV | |
273 | extern const char *locale_charset (void); | |
274 | outcharset = locale_charset (); | |
275 | # endif | |
276 | # endif | |
277 | } | |
278 | } | |
279 | ||
280 | # ifdef _LIBC | |
281 | /* We always want to use transliteration. */ | |
282 | outcharset = norm_add_slashes (outcharset, "TRANSLIT"); | |
283 | charset = norm_add_slashes (charset, NULL); | |
284 | if (__gconv_open (outcharset, charset, &domain->conv, | |
285 | GCONV_AVOID_NOCONV) | |
286 | != __GCONV_OK) | |
287 | domain->conv = (__gconv_t) -1; | |
288 | # else | |
289 | # if HAVE_ICONV | |
290 | /* When using GNU libiconv, we want to use transliteration. */ | |
291 | # if _LIBICONV_VERSION >= 0x0105 | |
292 | len = strlen (outcharset); | |
293 | { | |
294 | char *tmp = (char *) alloca (len + 10 + 1); | |
295 | memcpy (tmp, outcharset, len); | |
296 | memcpy (tmp + len, "//TRANSLIT", 10 + 1); | |
297 | outcharset = tmp; | |
298 | } | |
299 | # endif | |
300 | domain->conv = iconv_open (outcharset, charset); | |
301 | # if _LIBICONV_VERSION >= 0x0105 | |
302 | freea (outcharset); | |
303 | # endif | |
304 | # endif | |
305 | # endif | |
306 | ||
307 | freea (charset); | |
308 | } | |
309 | #endif /* _LIBC || HAVE_ICONV */ | |
310 | } | |
311 | ||
312 | return nullentry; | |
313 | } | |
314 | ||
315 | /* Frees the codeset dependent parts of an opened message catalog. */ | |
316 | void | |
317 | internal_function | |
318 | _nl_free_domain_conv (domain) | |
319 | struct loaded_domain *domain; | |
320 | { | |
321 | if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1) | |
322 | free (domain->conv_tab); | |
323 | ||
324 | #ifdef _LIBC | |
325 | if (domain->conv != (__gconv_t) -1) | |
326 | __gconv_close (domain->conv); | |
327 | #else | |
328 | # if HAVE_ICONV | |
329 | if (domain->conv != (iconv_t) -1) | |
330 | iconv_close (domain->conv); | |
331 | # endif | |
332 | #endif | |
333 | } | |
705db0b5 AD |
334 | |
335 | /* Load the message catalogs specified by FILENAME. If it is no valid | |
336 | message catalog do nothing. */ | |
337 | void | |
338 | internal_function | |
1e24cc5b | 339 | _nl_load_domain (domain_file, domainbinding) |
705db0b5 | 340 | struct loaded_l10nfile *domain_file; |
1e24cc5b | 341 | struct binding *domainbinding; |
705db0b5 AD |
342 | { |
343 | int fd; | |
344 | size_t size; | |
1e24cc5b AD |
345 | #ifdef _LIBC |
346 | struct stat64 st; | |
347 | #else | |
705db0b5 | 348 | struct stat st; |
1e24cc5b | 349 | #endif |
705db0b5 | 350 | struct mo_file_header *data = (struct mo_file_header *) -1; |
705db0b5 | 351 | int use_mmap = 0; |
705db0b5 | 352 | struct loaded_domain *domain; |
1e24cc5b | 353 | const char *nullentry; |
705db0b5 AD |
354 | |
355 | domain_file->decided = 1; | |
356 | domain_file->data = NULL; | |
357 | ||
1e24cc5b AD |
358 | /* Note that it would be useless to store domainbinding in domain_file |
359 | because domainbinding might be == NULL now but != NULL later (after | |
360 | a call to bind_textdomain_codeset). */ | |
361 | ||
705db0b5 AD |
362 | /* If the record does not represent a valid locale the FILENAME |
363 | might be NULL. This can happen when according to the given | |
364 | specification the locale file name is different for XPG and CEN | |
365 | syntax. */ | |
366 | if (domain_file->filename == NULL) | |
367 | return; | |
368 | ||
369 | /* Try to open the addressed file. */ | |
1e24cc5b | 370 | fd = open (domain_file->filename, O_RDONLY | O_BINARY); |
705db0b5 AD |
371 | if (fd == -1) |
372 | return; | |
373 | ||
374 | /* We must know about the size of the file. */ | |
1e24cc5b AD |
375 | if ( |
376 | #ifdef _LIBC | |
377 | __builtin_expect (fstat64 (fd, &st) != 0, 0) | |
378 | #else | |
379 | __builtin_expect (fstat (fd, &st) != 0, 0) | |
380 | #endif | |
381 | || __builtin_expect ((size = (size_t) st.st_size) != st.st_size, 0) | |
382 | || __builtin_expect (size < sizeof (struct mo_file_header), 0)) | |
705db0b5 AD |
383 | { |
384 | /* Something went wrong. */ | |
385 | close (fd); | |
386 | return; | |
387 | } | |
388 | ||
1e24cc5b | 389 | #ifdef HAVE_MMAP |
705db0b5 AD |
390 | /* Now we are ready to load the file. If mmap() is available we try |
391 | this first. If not available or it failed we try to load it. */ | |
392 | data = (struct mo_file_header *) mmap (NULL, size, PROT_READ, | |
393 | MAP_PRIVATE, fd, 0); | |
394 | ||
1e24cc5b | 395 | if (__builtin_expect (data != (struct mo_file_header *) -1, 1)) |
705db0b5 AD |
396 | { |
397 | /* mmap() call was successful. */ | |
398 | close (fd); | |
399 | use_mmap = 1; | |
400 | } | |
401 | #endif | |
402 | ||
403 | /* If the data is not yet available (i.e. mmap'ed) we try to load | |
404 | it manually. */ | |
405 | if (data == (struct mo_file_header *) -1) | |
406 | { | |
407 | size_t to_read; | |
408 | char *read_ptr; | |
409 | ||
410 | data = (struct mo_file_header *) malloc (size); | |
411 | if (data == NULL) | |
412 | return; | |
413 | ||
414 | to_read = size; | |
415 | read_ptr = (char *) data; | |
416 | do | |
417 | { | |
418 | long int nb = (long int) read (fd, read_ptr, to_read); | |
1e24cc5b | 419 | if (nb <= 0) |
705db0b5 | 420 | { |
1e24cc5b AD |
421 | #ifdef EINTR |
422 | if (nb == -1 && errno == EINTR) | |
423 | continue; | |
424 | #endif | |
705db0b5 AD |
425 | close (fd); |
426 | return; | |
427 | } | |
705db0b5 AD |
428 | read_ptr += nb; |
429 | to_read -= nb; | |
430 | } | |
431 | while (to_read > 0); | |
432 | ||
433 | close (fd); | |
434 | } | |
435 | ||
436 | /* Using the magic number we can test whether it really is a message | |
437 | catalog file. */ | |
1e24cc5b AD |
438 | if (__builtin_expect (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED, |
439 | 0)) | |
705db0b5 AD |
440 | { |
441 | /* The magic number is wrong: not a message catalog file. */ | |
1e24cc5b | 442 | #ifdef HAVE_MMAP |
705db0b5 AD |
443 | if (use_mmap) |
444 | munmap ((caddr_t) data, size); | |
445 | else | |
446 | #endif | |
447 | free (data); | |
448 | return; | |
449 | } | |
450 | ||
1e24cc5b AD |
451 | domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain)); |
452 | if (domain == NULL) | |
705db0b5 | 453 | return; |
1e24cc5b | 454 | domain_file->data = domain; |
705db0b5 | 455 | |
705db0b5 | 456 | domain->data = (char *) data; |
705db0b5 | 457 | domain->use_mmap = use_mmap; |
705db0b5 AD |
458 | domain->mmap_size = size; |
459 | domain->must_swap = data->magic != _MAGIC; | |
460 | ||
461 | /* Fill in the information about the available tables. */ | |
462 | switch (W (domain->must_swap, data->revision)) | |
463 | { | |
464 | case 0: | |
465 | domain->nstrings = W (domain->must_swap, data->nstrings); | |
466 | domain->orig_tab = (struct string_desc *) | |
467 | ((char *) data + W (domain->must_swap, data->orig_tab_offset)); | |
468 | domain->trans_tab = (struct string_desc *) | |
469 | ((char *) data + W (domain->must_swap, data->trans_tab_offset)); | |
470 | domain->hash_size = W (domain->must_swap, data->hash_tab_size); | |
471 | domain->hash_tab = (nls_uint32 *) | |
472 | ((char *) data + W (domain->must_swap, data->hash_tab_offset)); | |
473 | break; | |
474 | default: | |
1e24cc5b AD |
475 | /* This is an invalid revision. */ |
476 | #ifdef HAVE_MMAP | |
705db0b5 AD |
477 | if (use_mmap) |
478 | munmap ((caddr_t) data, size); | |
479 | else | |
480 | #endif | |
481 | free (data); | |
482 | free (domain); | |
483 | domain_file->data = NULL; | |
484 | return; | |
485 | } | |
486 | ||
1e24cc5b AD |
487 | /* Now initialize the character set converter from the character set |
488 | the file is encoded with (found in the header entry) to the domain's | |
489 | specified character set or the locale's character set. */ | |
490 | nullentry = _nl_init_domain_conv (domain_file, domain, domainbinding); | |
491 | ||
492 | /* Also look for a plural specification. */ | |
493 | if (nullentry != NULL) | |
494 | { | |
495 | const char *plural; | |
496 | const char *nplurals; | |
497 | ||
498 | plural = strstr (nullentry, "plural="); | |
499 | nplurals = strstr (nullentry, "nplurals="); | |
500 | if (plural == NULL || nplurals == NULL) | |
501 | goto no_plural; | |
502 | else | |
503 | { | |
504 | /* First get the number. */ | |
505 | char *endp; | |
506 | unsigned long int n; | |
507 | struct parse_args args; | |
508 | ||
509 | nplurals += 9; | |
510 | while (*nplurals != '\0' && isspace (*nplurals)) | |
511 | ++nplurals; | |
512 | #if defined HAVE_STRTOUL || defined _LIBC | |
513 | n = strtoul (nplurals, &endp, 10); | |
514 | #else | |
515 | for (endp = nplurals, n = 0; *endp >= '0' && *endp <= '9'; endp++) | |
516 | n = n * 10 + (*endp - '0'); | |
517 | #endif | |
518 | domain->nplurals = n; | |
519 | if (nplurals == endp) | |
520 | goto no_plural; | |
521 | ||
522 | /* Due to the restrictions bison imposes onto the interface of the | |
523 | scanner function we have to put the input string and the result | |
524 | passed up from the parser into the same structure which address | |
525 | is passed down to the parser. */ | |
526 | plural += 7; | |
527 | args.cp = plural; | |
528 | if (PLURAL_PARSE (&args) != 0) | |
529 | goto no_plural; | |
530 | domain->plural = args.res; | |
531 | } | |
532 | } | |
533 | else | |
534 | { | |
535 | /* By default we are using the Germanic form: singular form only | |
536 | for `one', the plural form otherwise. Yes, this is also what | |
537 | English is using since English is a Germanic language. */ | |
538 | no_plural: | |
539 | INIT_GERMANIC_PLURAL (); | |
540 | domain->plural = &germanic_plural; | |
541 | domain->nplurals = 2; | |
542 | } | |
705db0b5 AD |
543 | } |
544 | ||
545 | ||
546 | #ifdef _LIBC | |
547 | void | |
548 | internal_function | |
549 | _nl_unload_domain (domain) | |
550 | struct loaded_domain *domain; | |
551 | { | |
1e24cc5b AD |
552 | if (domain->plural != &germanic_plural) |
553 | __gettext_free_exp (domain->plural); | |
554 | ||
555 | _nl_free_domain_conv (domain); | |
556 | ||
557 | # ifdef _POSIX_MAPPED_FILES | |
705db0b5 AD |
558 | if (domain->use_mmap) |
559 | munmap ((caddr_t) domain->data, domain->mmap_size); | |
560 | else | |
1e24cc5b | 561 | # endif /* _POSIX_MAPPED_FILES */ |
705db0b5 AD |
562 | free ((void *) domain->data); |
563 | ||
564 | free (domain); | |
565 | } | |
566 | #endif |