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