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