1 /* Load needed message catalogs.
2 Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
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)
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.
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. */
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. */
22 # define _GNU_SOURCE 1
26 # include <gnulib/config.h>
28 # undef PACKAGE_VERSION
29 # undef PACKAGE_TARNAME
30 # undef PACKAGE_STRING
37 #include <sys/types.h>
41 # define alloca __builtin_alloca
42 # define HAVE_ALLOCA 1
44 # if defined HAVE_ALLOCA_H || defined _LIBC
60 #if defined HAVE_UNISTD_H || defined _LIBC
65 # include <langinfo.h>
69 #if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) \
70 || (defined _LIBC && defined _POSIX_MAPPED_FILES)
71 # include <sys/mman.h>
82 # include "../locale/localeinfo.h"
85 /* @@ end of prolog @@ */
88 /* Rename the non ISO C functions. This is required by the standard
89 because some ISO C functions will require linking with this object
90 file and the name space must not be polluted. */
92 # define close __close
95 # define munmap __munmap
98 /* Names for the libintl functions are a problem. They must not clash
99 with existing names and they should follow ANSI C. But this source
100 code is also used in GNU C Library where the names have a __
101 prefix. So we have to make a difference here. */
103 # define PLURAL_PARSE __gettextparse
105 # define PLURAL_PARSE gettextparse__
108 /* For those losing systems which don't have `alloca' we have to add
109 some additional code emulating it. */
111 # define freea(p) /* nothing */
113 # define alloca(n) malloc (n)
114 # define freea(p) free (p)
117 /* For systems that distinguish between text and binary I/O.
118 O_BINARY is usually declared in <fcntl.h>. */
119 #if !defined O_BINARY && defined _O_BINARY
120 /* For MSC-compatible compilers. */
121 # define O_BINARY _O_BINARY
122 # define O_TEXT _O_TEXT
125 /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect. */
129 /* On reasonable systems, binary I/O is the default. */
134 /* We need a sign, whether a new catalog was loaded, which can be associated
135 with all translations. This is important if the translations are
136 cached by one of GCC's features. */
137 int _nl_msg_cat_cntr
;
139 #if (defined __GNUC__ && !defined __APPLE_CC__) \
140 || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
142 /* These structs are the constant expression for the germanic plural
143 form determination. It represents the expression "n != 1". */
144 static const struct expression plvar
=
149 static const struct expression plone
=
158 static struct expression germanic_plural
=
161 .operation
= not_equal
,
166 [0] = (struct expression
*) &plvar
,
167 [1] = (struct expression
*) &plone
172 # define INIT_GERMANIC_PLURAL()
176 /* For compilers without support for ISO C 99 struct/union initializers:
177 Initialization at run-time. */
179 static struct expression plvar
;
180 static struct expression plone
;
181 static struct expression germanic_plural
;
184 init_germanic_plural ()
186 if (plone
.val
.num
== 0)
189 plvar
.operation
= var
;
192 plone
.operation
= num
;
195 germanic_plural
.nargs
= 2;
196 germanic_plural
.operation
= not_equal
;
197 germanic_plural
.val
.args
[0] = &plvar
;
198 germanic_plural
.val
.args
[1] = &plone
;
202 # define INIT_GERMANIC_PLURAL() init_germanic_plural ()
207 /* Initialize the codeset dependent parts of an opened message catalog.
208 Return the header entry. */
211 _nl_init_domain_conv (domain_file
, domain
, domainbinding
)
212 struct loaded_l10nfile
*domain_file
;
213 struct loaded_domain
*domain
;
214 struct binding
*domainbinding
;
216 /* Find out about the character set the file is encoded with.
217 This can be found (in textual form) in the entry "". If this
218 entry does not exist or if this does not contain the `charset='
219 information, we will assume the charset matches the one the
220 current locale and we don't have to perform any conversion. */
224 /* Preinitialize fields, to avoid recursion during _nl_find_msg. */
225 domain
->codeset_cntr
=
226 (domainbinding
!= NULL
? domainbinding
->codeset_cntr
: 0);
228 domain
->conv
= (__gconv_t
) -1;
231 domain
->conv
= (iconv_t
) -1;
234 domain
->conv_tab
= NULL
;
236 /* Get the header entry. */
237 nullentry
= _nl_find_msg (domain_file
, domainbinding
, "", &nullentrylen
);
239 if (nullentry
!= NULL
)
241 #if defined _LIBC || HAVE_ICONV
242 const char *charsetstr
;
244 charsetstr
= strstr (nullentry
, "charset=");
245 if (charsetstr
!= NULL
)
249 const char *outcharset
;
251 charsetstr
+= strlen ("charset=");
252 len
= strcspn (charsetstr
, " \t\n");
254 charset
= (char *) alloca (len
+ 1);
255 # if defined _LIBC || HAVE_MEMPCPY
256 *((char *) mempcpy (charset
, charsetstr
, len
)) = '\0';
258 memcpy (charset
, charsetstr
, len
);
262 /* The output charset should normally be determined by the
263 locale. But sometimes the locale is not used or not correctly
264 set up, so we provide a possibility for the user to override
265 this. Moreover, the value specified through
266 bind_textdomain_codeset overrides both. */
267 if (domainbinding
!= NULL
&& domainbinding
->codeset
!= NULL
)
268 outcharset
= domainbinding
->codeset
;
271 outcharset
= getenv ("OUTPUT_CHARSET");
272 if (outcharset
== NULL
|| outcharset
[0] == '\0')
275 outcharset
= (*_nl_current
[LC_CTYPE
])->values
[_NL_ITEM_INDEX (CODESET
)].string
;
278 extern const char *locale_charset (void);
279 outcharset
= locale_charset ();
286 /* We always want to use transliteration. */
287 outcharset
= norm_add_slashes (outcharset
, "TRANSLIT");
288 charset
= norm_add_slashes (charset
, NULL
);
289 if (__gconv_open (outcharset
, charset
, &domain
->conv
,
292 domain
->conv
= (__gconv_t
) -1;
295 /* When using GNU libiconv, we want to use transliteration. */
296 # if _LIBICONV_VERSION >= 0x0105
297 len
= strlen (outcharset
);
299 char *tmp
= (char *) alloca (len
+ 10 + 1);
300 memcpy (tmp
, outcharset
, len
);
301 memcpy (tmp
+ len
, "//TRANSLIT", 10 + 1);
305 domain
->conv
= iconv_open (outcharset
, charset
);
306 # if _LIBICONV_VERSION >= 0x0105
314 #endif /* _LIBC || HAVE_ICONV */
320 /* Frees the codeset dependent parts of an opened message catalog. */
323 _nl_free_domain_conv (domain
)
324 struct loaded_domain
*domain
;
326 if (domain
->conv_tab
!= NULL
&& domain
->conv_tab
!= (char **) -1)
327 free (domain
->conv_tab
);
330 if (domain
->conv
!= (__gconv_t
) -1)
331 __gconv_close (domain
->conv
);
334 if (domain
->conv
!= (iconv_t
) -1)
335 iconv_close (domain
->conv
);
340 /* Load the message catalogs specified by FILENAME. If it is no valid
341 message catalog do nothing. */
344 _nl_load_domain (domain_file
, domainbinding
)
345 struct loaded_l10nfile
*domain_file
;
346 struct binding
*domainbinding
;
355 struct mo_file_header
*data
= (struct mo_file_header
*) -1;
357 struct loaded_domain
*domain
;
358 const char *nullentry
;
360 domain_file
->decided
= 1;
361 domain_file
->data
= NULL
;
363 /* Note that it would be useless to store domainbinding in domain_file
364 because domainbinding might be == NULL now but != NULL later (after
365 a call to bind_textdomain_codeset). */
367 /* If the record does not represent a valid locale the FILENAME
368 might be NULL. This can happen when according to the given
369 specification the locale file name is different for XPG and CEN
371 if (domain_file
->filename
== NULL
)
374 /* Try to open the addressed file. */
375 fd
= open (domain_file
->filename
, O_RDONLY
| O_BINARY
);
379 /* We must know about the size of the file. */
382 __builtin_expect (fstat64 (fd
, &st
) != 0, 0)
384 __builtin_expect (fstat (fd
, &st
) != 0, 0)
386 || __builtin_expect ((size
= (size_t) st
.st_size
) != st
.st_size
, 0)
387 || __builtin_expect (size
< sizeof (struct mo_file_header
), 0))
389 /* Something went wrong. */
395 /* Now we are ready to load the file. If mmap() is available we try
396 this first. If not available or it failed we try to load it. */
397 data
= (struct mo_file_header
*) mmap (NULL
, size
, PROT_READ
,
400 if (__builtin_expect (data
!= (struct mo_file_header
*) -1, 1))
402 /* mmap() call was successful. */
408 /* If the data is not yet available (i.e. mmap'ed) we try to load
410 if (data
== (struct mo_file_header
*) -1)
415 data
= (struct mo_file_header
*) malloc (size
);
420 read_ptr
= (char *) data
;
423 long int nb
= (long int) read (fd
, read_ptr
, to_read
);
427 if (nb
== -1 && errno
== EINTR
)
441 /* Using the magic number we can test whether it really is a message
443 if (__builtin_expect (data
->magic
!= _MAGIC
&& data
->magic
!= _MAGIC_SWAPPED
,
446 /* The magic number is wrong: not a message catalog file. */
449 munmap ((caddr_t
) data
, size
);
456 domain
= (struct loaded_domain
*) malloc (sizeof (struct loaded_domain
));
459 domain_file
->data
= domain
;
461 domain
->data
= (char *) data
;
462 domain
->use_mmap
= use_mmap
;
463 domain
->mmap_size
= size
;
464 domain
->must_swap
= data
->magic
!= _MAGIC
;
466 /* Fill in the information about the available tables. */
467 switch (W (domain
->must_swap
, data
->revision
))
470 domain
->nstrings
= W (domain
->must_swap
, data
->nstrings
);
471 domain
->orig_tab
= (struct string_desc
*)
472 ((char *) data
+ W (domain
->must_swap
, data
->orig_tab_offset
));
473 domain
->trans_tab
= (struct string_desc
*)
474 ((char *) data
+ W (domain
->must_swap
, data
->trans_tab_offset
));
475 domain
->hash_size
= W (domain
->must_swap
, data
->hash_tab_size
);
476 domain
->hash_tab
= (nls_uint32
*)
477 ((char *) data
+ W (domain
->must_swap
, data
->hash_tab_offset
));
480 /* This is an invalid revision. */
483 munmap ((caddr_t
) data
, size
);
488 domain_file
->data
= NULL
;
492 /* Now initialize the character set converter from the character set
493 the file is encoded with (found in the header entry) to the domain's
494 specified character set or the locale's character set. */
495 nullentry
= _nl_init_domain_conv (domain_file
, domain
, domainbinding
);
497 /* Also look for a plural specification. */
498 if (nullentry
!= NULL
)
501 const char *nplurals
;
503 plural
= strstr (nullentry
, "plural=");
504 nplurals
= strstr (nullentry
, "nplurals=");
505 if (plural
== NULL
|| nplurals
== NULL
)
509 /* First get the number. */
512 struct parse_args args
;
515 while (*nplurals
!= '\0' && isspace (*nplurals
))
517 #if defined HAVE_STRTOUL || defined _LIBC
518 n
= strtoul (nplurals
, &endp
, 10);
520 for (endp
= nplurals
, n
= 0; *endp
>= '0' && *endp
<= '9'; endp
++)
521 n
= n
* 10 + (*endp
- '0');
523 domain
->nplurals
= n
;
524 if (nplurals
== endp
)
527 /* Due to the restrictions bison imposes onto the interface of the
528 scanner function we have to put the input string and the result
529 passed up from the parser into the same structure which address
530 is passed down to the parser. */
533 if (PLURAL_PARSE (&args
) != 0)
535 domain
->plural
= args
.res
;
540 /* By default we are using the Germanic form: singular form only
541 for `one', the plural form otherwise. Yes, this is also what
542 English is using since English is a Germanic language. */
544 INIT_GERMANIC_PLURAL ();
545 domain
->plural
= &germanic_plural
;
546 domain
->nplurals
= 2;
554 _nl_unload_domain (domain
)
555 struct loaded_domain
*domain
;
557 if (domain
->plural
!= &germanic_plural
)
558 __gettext_free_exp (domain
->plural
);
560 _nl_free_domain_conv (domain
);
562 # ifdef _POSIX_MAPPED_FILES
563 if (domain
->use_mmap
)
564 munmap ((caddr_t
) domain
->data
, domain
->mmap_size
);
566 # endif /* _POSIX_MAPPED_FILES */
567 free ((void *) domain
->data
);