1 /* Implementation of the dcgettext(3) function. 2 Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 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 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software Foundation, 16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 17 18 #ifdef HAVE_CONFIG_H 19 # include <config.h> 20 #endif 21 22 #include <sys/types.h> 23 24 #ifdef __GNUC__ 25 # define alloca __builtin_alloca 26 # define HAVE_ALLOCA 1 27 #else 28 # if defined HAVE_ALLOCA_H || defined _LIBC 29 # include <alloca.h> 30 # else 31 # ifdef _AIX 32 #pragma alloca 33 # else 34 # ifndef alloca 35 char *alloca (); 36 # endif 37 # endif 38 # endif 39 #endif 40 41 #include <errno.h> 42 #ifndef errno 43 extern int errno; 44 #endif 45 #ifndef __set_errno 46 # define __set_errno(val) errno = (val) 47 #endif 48 49 #if defined STDC_HEADERS || defined _LIBC 50 # include <stdlib.h> 51 #else 52 char *getenv (); 53 # ifdef HAVE_MALLOC_H 54 # include <malloc.h> 55 # else 56 void free (); 57 # endif 58 #endif 59 60 #if defined HAVE_STRING_H || defined _LIBC 61 # ifndef _GNU_SOURCE 62 # define _GNU_SOURCE 1 63 # endif 64 # include <string.h> 65 #else 66 # include <strings.h> 67 #endif 68 #if !HAVE_STRCHR && !defined _LIBC 69 # ifndef strchr 70 # define strchr index 71 # endif 72 #endif 73 74 #if defined HAVE_UNISTD_H || defined _LIBC 75 # include <unistd.h> 76 #endif 77 78 #include "gettext.h" 79 #include "gettextP.h" 80 #ifdef _LIBC 81 # include <libintl.h> 82 #else 83 # include "libgettext.h" 84 #endif 85 #include "hash-string.h" 86 87 /* @@ end of prolog @@ */ 88 89 #ifdef _LIBC 90 /* Rename the non ANSI C functions. This is required by the standard 91 because some ANSI C functions will require linking with this object 92 file and the name space must not be polluted. */ 93 # define getcwd __getcwd 94 # ifndef stpcpy 95 # define stpcpy __stpcpy 96 # endif 97 #else 98 # if !defined HAVE_GETCWD 99 char *getwd (); 100 # define getcwd(buf, max) getwd (buf) 101 # else 102 char *getcwd (); 103 # endif 104 # ifndef HAVE_STPCPY 105 static char *stpcpy PARAMS ((char *dest, const char *src)); 106 # endif 107 #endif 108 109 /* Amount to increase buffer size by in each try. */ 110 #define PATH_INCR 32 111 112 /* The following is from pathmax.h. */ 113 /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define 114 PATH_MAX but might cause redefinition warnings when sys/param.h is 115 later included (as on MORE/BSD 4.3). */ 116 #if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__)) 117 # include <limits.h> 118 #endif 119 120 #ifndef _POSIX_PATH_MAX 121 # define _POSIX_PATH_MAX 255 122 #endif 123 124 #if !defined(PATH_MAX) && defined(_PC_PATH_MAX) 125 # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX)) 126 #endif 127 128 /* Don't include sys/param.h if it already has been. */ 129 #if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN) 130 # include <sys/param.h> 131 #endif 132 133 #if !defined(PATH_MAX) && defined(MAXPATHLEN) 134 # define PATH_MAX MAXPATHLEN 135 #endif 136 137 #ifndef PATH_MAX 138 # define PATH_MAX _POSIX_PATH_MAX 139 #endif 140 141 /* XPG3 defines the result of `setlocale (category, NULL)' as: 142 ``Directs `setlocale()' to query `category' and return the current 143 setting of `local'.'' 144 However it does not specify the exact format. And even worse: POSIX 145 defines this not at all. So we can use this feature only on selected 146 system (e.g. those using GNU C Library). */ 147 #ifdef _LIBC 148 # define HAVE_LOCALE_NULL 149 #endif 150 151 /* Name of the default domain used for gettext(3) prior any call to 152 textdomain(3). The default value for this is "messages". */ 153 const char _nl_default_default_domain[] = "messages"; 154 155 /* Value used as the default domain for gettext(3). */ 156 const char *_nl_current_default_domain = _nl_default_default_domain; 157 158 /* Contains the default location of the message catalogs. */ 159 const char _nl_default_dirname[] = GNULOCALEDIR; 160 161 /* List with bindings of specific domains created by bindtextdomain() 162 calls. */ 163 struct binding *_nl_domain_bindings; 164 165 /* Prototypes for local functions. */ 166 static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file, 167 const char *msgid)) internal_function; 168 static const char *category_to_name PARAMS ((int category)) internal_function; 169 static const char *guess_category_value PARAMS ((int category, 170 const char *categoryname)) 171 internal_function; 172 173 174 /* For those loosing systems which don't have `alloca' we have to add 175 some additional code emulating it. */ 176 #ifdef HAVE_ALLOCA 177 /* Nothing has to be done. */ 178 # define ADD_BLOCK(list, address) /* nothing */ 179 # define FREE_BLOCKS(list) /* nothing */ 180 #else 181 struct block_list 182 { 183 void *address; 184 struct block_list *next; 185 }; 186 # define ADD_BLOCK(list, addr) \ 187 do { \ 188 struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \ 189 /* If we cannot get a free block we cannot add the new element to \ 190 the list. */ \ 191 if (newp != NULL) { \ 192 newp->address = (addr); \ 193 newp->next = (list); \ 194 (list) = newp; \ 195 } \ 196 } while (0) 197 # define FREE_BLOCKS(list) \ 198 do { \ 199 while (list != NULL) { \ 200 struct block_list *old = list; \ 201 list = list->next; \ 202 free (old); \ 203 } \ 204 } while (0) 205 # undef alloca 206 # define alloca(size) (malloc (size)) 207 #endif /* have alloca */ 208 209 210 /* Names for the libintl functions are a problem. They must not clash 211 with existing names and they should follow ANSI C. But this source 212 code is also used in GNU C Library where the names have a __ 213 prefix. So we have to make a difference here. */ 214 #ifdef _LIBC 215 # define DCGETTEXT __dcgettext 216 #else 217 # define DCGETTEXT dcgettext__ 218 #endif 219 220 /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY 221 locale. */ 222 char * 223 DCGETTEXT (domainname, msgid, category) 224 const char *domainname; 225 const char *msgid; 226 int category; 227 { 228 #ifndef HAVE_ALLOCA 229 struct block_list *block_list = NULL; 230 #endif 231 struct loaded_l10nfile *domain; 232 struct binding *binding; 233 const char *categoryname; 234 const char *categoryvalue; 235 char *dirname, *xdomainname; 236 char *single_locale; 237 char *retval; 238 int saved_errno = errno; 239 240 /* If no real MSGID is given return NULL. */ 241 if (msgid == NULL) 242 return NULL; 243 244 /* If DOMAINNAME is NULL, we are interested in the default domain. If 245 CATEGORY is not LC_MESSAGES this might not make much sense but the 246 defintion left this undefined. */ 247 if (domainname == NULL) 248 domainname = _nl_current_default_domain; 249 250 /* First find matching binding. */ 251 for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) 252 { 253 int compare = strcmp (domainname, binding->domainname); 254 if (compare == 0) 255 /* We found it! */ 256 break; 257 if (compare < 0) 258 { 259 /* It is not in the list. */ 260 binding = NULL; 261 break; 262 } 263 } 264 265 if (binding == NULL) 266 dirname = (char *) _nl_default_dirname; 267 else if (binding->dirname[0] == '/') 268 dirname = binding->dirname; 269 else 270 { 271 /* We have a relative path. Make it absolute now. */ 272 size_t dirname_len = strlen (binding->dirname) + 1; 273 size_t path_max; 274 char *ret; 275 276 path_max = (unsigned) PATH_MAX; 277 path_max += 2; /* The getcwd docs say to do this. */ 278 279 dirname = (char *) alloca (path_max + dirname_len); 280 ADD_BLOCK (block_list, dirname); 281 282 __set_errno (0); 283 while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE) 284 { 285 path_max += PATH_INCR; 286 dirname = (char *) alloca (path_max + dirname_len); 287 ADD_BLOCK (block_list, dirname); 288 __set_errno (0); 289 } 290 291 if (ret == NULL) 292 { 293 /* We cannot get the current working directory. Don't signal an 294 error but simply return the default string. */ 295 FREE_BLOCKS (block_list); 296 __set_errno (saved_errno); 297 return (char *) msgid; 298 } 299 300 stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname); 301 } 302 303 /* Now determine the symbolic name of CATEGORY and its value. */ 304 categoryname = category_to_name (category); 305 categoryvalue = guess_category_value (category, categoryname); 306 307 xdomainname = (char *) alloca (strlen (categoryname) 308 + strlen (domainname) + 5); 309 ADD_BLOCK (block_list, xdomainname); 310 311 stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"), 312 domainname), 313 ".mo"); 314 315 /* Creating working area. */ 316 single_locale = (char *) alloca (strlen (categoryvalue) + 1); 317 ADD_BLOCK (block_list, single_locale); 318 319 320 /* Search for the given string. This is a loop because we perhaps 321 got an ordered list of languages to consider for th translation. */ 322 while (1) 323 { 324 /* Make CATEGORYVALUE point to the next element of the list. */ 325 while (categoryvalue[0] != '\0' && categoryvalue[0] == ':') 326 ++categoryvalue; 327 if (categoryvalue[0] == '\0') 328 { 329 /* The whole contents of CATEGORYVALUE has been searched but 330 no valid entry has been found. We solve this situation 331 by implicitly appending a "C" entry, i.e. no translation 332 will take place. */ 333 single_locale[0] = 'C'; 334 single_locale[1] = '\0'; 335 } 336 else 337 { 338 char *cp = single_locale; 339 while (categoryvalue[0] != '\0' && categoryvalue[0] != ':') 340 *cp++ = *categoryvalue++; 341 *cp = '\0'; 342 } 343 344 /* If the current locale value is C (or POSIX) we don't load a 345 domain. Return the MSGID. */ 346 if (strcmp (single_locale, "C") == 0 347 || strcmp (single_locale, "POSIX") == 0) 348 { 349 FREE_BLOCKS (block_list); 350 __set_errno (saved_errno); 351 return (char *) msgid; 352 } 353 354 355 /* Find structure describing the message catalog matching the 356 DOMAINNAME and CATEGORY. */ 357 domain = _nl_find_domain (dirname, single_locale, xdomainname); 358 359 if (domain != NULL) 360 { 361 retval = find_msg (domain, msgid); 362 363 if (retval == NULL) 364 { 365 int cnt; 366 367 for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) 368 { 369 retval = find_msg (domain->successor[cnt], msgid); 370 371 if (retval != NULL) 372 break; 373 } 374 } 375 376 if (retval != NULL) 377 { 378 FREE_BLOCKS (block_list); 379 __set_errno (saved_errno); 380 return retval; 381 } 382 } 383 } 384 /* NOTREACHED */ 385 } 386 387 #ifdef _LIBC 388 /* Alias for function name in GNU C Library. */ 389 weak_alias (__dcgettext, dcgettext); 390 #endif 391 392 393 static char * 394 internal_function 395 find_msg (domain_file, msgid) 396 struct loaded_l10nfile *domain_file; 397 const char *msgid; 398 { 399 size_t top, act, bottom; 400 struct loaded_domain *domain; 401 402 if (domain_file->decided == 0) 403 _nl_load_domain (domain_file); 404 405 if (domain_file->data == NULL) 406 return NULL; 407 408 domain = (struct loaded_domain *) domain_file->data; 409 410 /* Locate the MSGID and its translation. */ 411 if (domain->hash_size > 2 && domain->hash_tab != NULL) 412 { 413 /* Use the hashing table. */ 414 nls_uint32 len = strlen (msgid); 415 nls_uint32 hash_val = hash_string (msgid); 416 nls_uint32 idx = hash_val % domain->hash_size; 417 nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); 418 nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]); 419 420 if (nstr == 0) 421 /* Hash table entry is empty. */ 422 return NULL; 423 424 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 425 && strcmp (msgid, 426 domain->data + W (domain->must_swap, 427 domain->orig_tab[nstr - 1].offset)) == 0) 428 return (char *) domain->data + W (domain->must_swap, 429 domain->trans_tab[nstr - 1].offset); 430 431 while (1) 432 { 433 if (idx >= domain->hash_size - incr) 434 idx -= domain->hash_size - incr; 435 else 436 idx += incr; 437 438 nstr = W (domain->must_swap, domain->hash_tab[idx]); 439 if (nstr == 0) 440 /* Hash table entry is empty. */ 441 return NULL; 442 443 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 444 && strcmp (msgid, 445 domain->data + W (domain->must_swap, 446 domain->orig_tab[nstr - 1].offset)) 447 == 0) 448 return (char *) domain->data 449 + W (domain->must_swap, domain->trans_tab[nstr - 1].offset); 450 } 451 /* NOTREACHED */ 452 } 453 454 /* Now we try the default method: binary search in the sorted 455 array of messages. */ 456 bottom = 0; 457 top = domain->nstrings; 458 while (bottom < top) 459 { 460 int cmp_val; 461 462 act = (bottom + top) / 2; 463 cmp_val = strcmp (msgid, domain->data 464 + W (domain->must_swap, 465 domain->orig_tab[act].offset)); 466 if (cmp_val < 0) 467 top = act; 468 else if (cmp_val > 0) 469 bottom = act + 1; 470 else 471 break; 472 } 473 474 /* If an translation is found return this. */ 475 return bottom >= top ? NULL : (char *) domain->data 476 + W (domain->must_swap, 477 domain->trans_tab[act].offset); 478 } 479 480 481 /* Return string representation of locale CATEGORY. */ 482 static const char * 483 internal_function 484 category_to_name (category) 485 int category; 486 { 487 const char *retval; 488 489 switch (category) 490 { 491 #ifdef LC_COLLATE 492 case LC_COLLATE: 493 retval = "LC_COLLATE"; 494 break; 495 #endif 496 #ifdef LC_CTYPE 497 case LC_CTYPE: 498 retval = "LC_CTYPE"; 499 break; 500 #endif 501 #ifdef LC_MONETARY 502 case LC_MONETARY: 503 retval = "LC_MONETARY"; 504 break; 505 #endif 506 #ifdef LC_NUMERIC 507 case LC_NUMERIC: 508 retval = "LC_NUMERIC"; 509 break; 510 #endif 511 #ifdef LC_TIME 512 case LC_TIME: 513 retval = "LC_TIME"; 514 break; 515 #endif 516 #ifdef LC_MESSAGES 517 case LC_MESSAGES: 518 retval = "LC_MESSAGES"; 519 break; 520 #endif 521 #ifdef LC_RESPONSE 522 case LC_RESPONSE: 523 retval = "LC_RESPONSE"; 524 break; 525 #endif 526 #ifdef LC_ALL 527 case LC_ALL: 528 /* This might not make sense but is perhaps better than any other 529 value. */ 530 retval = "LC_ALL"; 531 break; 532 #endif 533 default: 534 /* If you have a better idea for a default value let me know. */ 535 retval = "LC_XXX"; 536 } 537 538 return retval; 539 } 540 541 /* Guess value of current locale from value of the environment variables. */ 542 static const char * 543 internal_function 544 guess_category_value (category, categoryname) 545 int category; 546 const char *categoryname; 547 { 548 const char *retval; 549 550 /* The highest priority value is the `LANGUAGE' environment 551 variable. This is a GNU extension. */ 552 retval = getenv ("LANGUAGE"); 553 if (retval != NULL && retval[0] != '\0') 554 return retval; 555 556 /* `LANGUAGE' is not set. So we have to proceed with the POSIX 557 methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some 558 systems this can be done by the `setlocale' function itself. */ 559 #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL 560 return setlocale (category, NULL); 561 #else 562 /* Setting of LC_ALL overwrites all other. */ 563 retval = getenv ("LC_ALL"); 564 if (retval != NULL && retval[0] != '\0') 565 return retval; 566 567 /* Next comes the name of the desired category. */ 568 retval = getenv (categoryname); 569 if (retval != NULL && retval[0] != '\0') 570 return retval; 571 572 /* Last possibility is the LANG environment variable. */ 573 retval = getenv ("LANG"); 574 if (retval != NULL && retval[0] != '\0') 575 return retval; 576 577 /* We use C as the default domain. POSIX says this is implementation 578 defined. */ 579 return "C"; 580 #endif 581 } 582 583 /* @@ begin of epilog @@ */ 584 585 /* We don't want libintl.a to depend on any other library. So we 586 avoid the non-standard function stpcpy. In GNU C Library this 587 function is available, though. Also allow the symbol HAVE_STPCPY 588 to be defined. */ 589 #if !_LIBC && !HAVE_STPCPY 590 static char * 591 stpcpy (dest, src) 592 char *dest; 593 const char *src; 594 { 595 while ((*dest++ = *src++) != '\0') 596 /* Do nothing. */ ; 597 return dest - 1; 598 } 599 #endif 600 601 602 #ifdef _LIBC 603 /* If we want to free all resources we have to do some work at 604 program's end. */ 605 static void __attribute__ ((unused)) 606 free_mem (void) 607 { 608 struct binding *runp; 609 610 for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next) 611 { 612 free (runp->domainname); 613 if (runp->dirname != _nl_default_dirname) 614 /* Yes, this is a pointer comparison. */ 615 free (runp->dirname); 616 } 617 618 if (_nl_current_default_domain != _nl_default_default_domain) 619 /* Yes, again a pointer comparison. */ 620 free ((char *) _nl_current_default_domain); 621 } 622 623 text_set_element (__libc_subfreeres, free_mem); 624 #endif 625