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 446