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