1/* $NetBSD: loadmsgcat.c,v 1.1.1.1 2016/01/10 21:36:18 christos Exp $ */ 2 3/* Load needed message catalogs. 4 Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc. 5 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. 10 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. 15 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. */ 20 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 27 28#ifdef HAVE_CONFIG_H 29# include <config.h> 30#endif 31 32#include <ctype.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <sys/types.h> 36#include <sys/stat.h> 37 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 49char *alloca (); 50# endif 51# endif 52# endif 53#endif 54 55#include <stdlib.h> 56#include <string.h> 57 58#if defined HAVE_UNISTD_H || defined _LIBC 59# include <unistd.h> 60#endif 61 62#ifdef _LIBC 63# include <langinfo.h> 64# include <locale.h> 65#endif 66 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 75 76#include "gmo.h" 77#include "gettextP.h" 78#include "plural-exp.h" 79 80#ifdef _LIBC 81# include "../locale/localeinfo.h" 82#endif 83 84/* @@ end of prolog @@ */ 85 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 96 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 105 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 122 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. */ 126int _nl_msg_cat_cntr; 127 128 129/* Initialize the codeset dependent parts of an opened message catalog. 130 Return the header entry. */ 131const char * 132internal_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; 137{ 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; 145 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; 157 158 /* Get the header entry. */ 159 nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen); 160 161 if (nullentry != NULL) 162 { 163#if defined _LIBC || HAVE_ICONV 164 const char *charsetstr; 165 166 charsetstr = strstr (nullentry, "charset="); 167 if (charsetstr != NULL) 168 { 169 size_t len; 170 char *charset; 171 const char *outcharset; 172 173 charsetstr += strlen ("charset="); 174 len = strcspn (charsetstr, " \t\n"); 175 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 183 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 192 { 193 outcharset = getenv ("OUTPUT_CHARSET"); 194 if (outcharset == NULL || outcharset[0] == '\0') 195 { 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 204 } 205 } 206 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); 222 { 223 char *tmp = (char *) alloca (len + 10 + 1); 224 memcpy (tmp, outcharset, len); 225 memcpy (tmp + len, "//TRANSLIT", 10 + 1); 226 outcharset = tmp; 227 } 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 236 237 freea (charset); 238 } 239#endif /* _LIBC || HAVE_ICONV */ 240 } 241 242 return nullentry; 243} 244 245/* Frees the codeset dependent parts of an opened message catalog. */ 246void 247internal_function 248_nl_free_domain_conv (domain) 249 struct loaded_domain *domain; 250{ 251 if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1) 252 free (domain->conv_tab); 253 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 263} 264 265/* Load the message catalogs specified by FILENAME. If it is no valid 266 message catalog do nothing. */ 267void 268internal_function 269_nl_load_domain (domain_file, domainbinding) 270 struct loaded_l10nfile *domain_file; 271 struct binding *domainbinding; 272{ 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; 284 285 domain_file->decided = 1; 286 domain_file->data = NULL; 287 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). */ 291 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; 298 299 /* Try to open the addressed file. */ 300 fd = open (domain_file->filename, O_RDONLY | O_BINARY); 301 if (fd == -1) 302 return; 303 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)) 313 { 314 /* Something went wrong. */ 315 close (fd); 316 return; 317 } 318 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); 324 325 if (__builtin_expect (data != (struct mo_file_header *) -1, 1)) 326 { 327 /* mmap() call was successful. */ 328 close (fd); 329 use_mmap = 1; 330 } 331#endif 332 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) 336 { 337 size_t to_read; 338 char *read_ptr; 339 340 data = (struct mo_file_header *) malloc (size); 341 if (data == NULL) 342 return; 343 344 to_read = size; 345 read_ptr = (char *) data; 346 do 347 { 348 long int nb = (long int) read (fd, read_ptr, to_read); 349 if (nb <= 0) 350 { 351#ifdef EINTR 352 if (nb == -1 && errno == EINTR) 353 continue; 354#endif 355 close (fd); 356 return; 357 } 358 read_ptr += nb; 359 to_read -= nb; 360 } 361 while (to_read > 0); 362 363 close (fd); 364 } 365 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, 369 0)) 370 { 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; 379 } 380 381 domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain)); 382 if (domain == NULL) 383 return; 384 domain_file->data = domain; 385 386 domain->data = (char *) data; 387 domain->use_mmap = use_mmap; 388 domain->mmap_size = size; 389 domain->must_swap = data->magic != _MAGIC; 390 391 /* Fill in the information about the available tables. */ 392 switch (W (domain->must_swap, data->revision)) 393 { 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; 415 } 416 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); 421 422 /* Also look for a plural specification. */ 423 EXTRACT_PLURAL_EXPRESSION (nullentry, &domain->plural, &domain->nplurals); 424} 425 426 427#ifdef _LIBC 428void 429internal_function 430_nl_unload_domain (domain) 431 struct loaded_domain *domain; 432{ 433 if (domain->plural != &__gettext_germanic_plural) 434 __gettext_free_exp (domain->plural); 435 436 _nl_free_domain_conv (domain); 437 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); 444 445 free (domain); 446} 447#endif 448