| 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 | 
|---|