Back to Evolution Project page
00001 /* Implementation of the dcgettext(3) function. 00002 Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. 00003 00004 This program is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU General Public License as published by 00006 the Free Software Foundation; either version 2, or (at your option) 00007 any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software Foundation, 00016 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 00017 00018 #ifdef HAVE_CONFIG_H 00019 # include <config.h> 00020 #endif 00021 00022 #include <sys/types.h> 00023 00024 #if defined __GNUC__ && !defined C_ALLOCA 00025 # define alloca __builtin_alloca 00026 # define HAVE_ALLOCA 1 00027 #else 00028 # if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA 00029 # include <alloca.h> 00030 # else 00031 # ifdef _AIX 00032 #pragma alloca 00033 # else 00034 # ifndef alloca 00035 char *alloca (); 00036 # endif 00037 # endif 00038 # endif 00039 #endif 00040 00041 #include <errno.h> 00042 #ifndef errno 00043 extern int errno; 00044 #endif 00045 #ifndef __set_errno 00046 # define __set_errno(val) errno = (val) 00047 #endif 00048 00049 #if defined STDC_HEADERS || defined _LIBC 00050 # include <stdlib.h> 00051 #else 00052 char *getenv (); 00053 # ifdef HAVE_MALLOC_H 00054 # include <malloc.h> 00055 # else 00056 void free (); 00057 # endif 00058 #endif 00059 00060 #if defined HAVE_STRING_H || defined _LIBC 00061 # ifndef _GNU_SOURCE 00062 # define _GNU_SOURCE 1 00063 # endif 00064 # include <string.h> 00065 #else 00066 # include <strings.h> 00067 #endif 00068 #if !HAVE_STRCHR && !defined _LIBC 00069 # ifndef strchr 00070 # define strchr index 00071 # endif 00072 #endif 00073 00074 #if defined HAVE_UNISTD_H || defined _LIBC 00075 # include <unistd.h> 00076 #endif 00077 00078 #include "gettext.h" 00079 #include "gettextP.h" 00080 #ifdef _LIBC 00081 # include <libintl.h> 00082 #else 00083 # include "libgettext.h" 00084 #endif 00085 #include "hash-string.h" 00086 00087 /* @@ end of prolog @@ */ 00088 00089 #ifdef _LIBC 00090 /* Rename the non ANSI C functions. This is required by the standard 00091 because some ANSI C functions will require linking with this object 00092 file and the name space must not be polluted. */ 00093 # define getcwd __getcwd 00094 # ifndef stpcpy 00095 # define stpcpy __stpcpy 00096 # endif 00097 #else 00098 # if !defined HAVE_GETCWD 00099 char *getwd (); 00100 # define getcwd(buf, max) getwd (buf) 00101 # else 00102 char *getcwd (); 00103 # endif 00104 # ifndef HAVE_STPCPY 00105 static char *stpcpy PARAMS ((char *dest, const char *src)); 00106 # endif 00107 #endif 00108 00109 /* Amount to increase buffer size by in each try. */ 00110 #define PATH_INCR 32 00111 00112 /* The following is from pathmax.h. */ 00113 /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define 00114 PATH_MAX but might cause redefinition warnings when sys/param.h is 00115 later included (as on MORE/BSD 4.3). */ 00116 #if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__)) 00117 # include <limits.h> 00118 #endif 00119 00120 #ifndef _POSIX_PATH_MAX 00121 # define _POSIX_PATH_MAX 255 00122 #endif 00123 00124 #if !defined(PATH_MAX) && defined(_PC_PATH_MAX) 00125 # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX)) 00126 #endif 00127 00128 /* Don't include sys/param.h if it already has been. */ 00129 #if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN) 00130 # include <sys/param.h> 00131 #endif 00132 00133 #if !defined(PATH_MAX) && defined(MAXPATHLEN) 00134 # define PATH_MAX MAXPATHLEN 00135 #endif 00136 00137 #ifndef PATH_MAX 00138 # define PATH_MAX _POSIX_PATH_MAX 00139 #endif 00140 00141 /* XPG3 defines the result of `setlocale (category, NULL)' as: 00142 ``Directs `setlocale()' to query `category' and return the current 00143 setting of `local'.'' 00144 However it does not specify the exact format. And even worse: POSIX 00145 defines this not at all. So we can use this feature only on selected 00146 system (e.g. those using GNU C Library). */ 00147 #ifdef _LIBC 00148 # define HAVE_LOCALE_NULL 00149 #endif 00150 00151 /* Name of the default domain used for gettext(3) prior any call to 00152 textdomain(3). The default value for this is "messages". */ 00153 const char _nl_default_default_domain[] = "messages"; 00154 00155 /* Value used as the default domain for gettext(3). */ 00156 const char *_nl_current_default_domain = _nl_default_default_domain; 00157 00158 /* Contains the default location of the message catalogs. */ 00159 const char _nl_default_dirname[] = GNULOCALEDIR; 00160 00161 /* List with bindings of specific domains created by bindtextdomain() 00162 calls. */ 00163 struct binding *_nl_domain_bindings; 00164 00165 /* Prototypes for local functions. */ 00166 static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file, 00167 const char *msgid)) internal_function; 00168 static const char *category_to_name PARAMS ((int category)) internal_function; 00169 static const char *guess_category_value PARAMS ((int category, 00170 const char *categoryname)) 00171 internal_function; 00172 00173 00174 /* For those loosing systems which don't have `alloca' we have to add 00175 some additional code emulating it. */ 00176 #ifdef HAVE_ALLOCA 00177 /* Nothing has to be done. */ 00178 # define ADD_BLOCK(list, address) /* nothing */ 00179 # define FREE_BLOCKS(list) /* nothing */ 00180 #else 00181 struct block_list 00182 { 00183 void *address; 00184 struct block_list *next; 00185 }; 00186 # define ADD_BLOCK(list, addr) \ 00187 do { \ 00188 struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \ 00189 /* If we cannot get a free block we cannot add the new element to \ 00190 the list. */ \ 00191 if (newp != NULL) { \ 00192 newp->address = (addr); \ 00193 newp->next = (list); \ 00194 (list) = newp; \ 00195 } \ 00196 } while (0) 00197 # define FREE_BLOCKS(list) \ 00198 do { \ 00199 while (list != NULL) { \ 00200 struct block_list *old = list; \ 00201 list = list->next; \ 00202 free (old); \ 00203 } \ 00204 } while (0) 00205 # undef alloca 00206 # define alloca(size) (malloc (size)) 00207 #endif /* have alloca */ 00208 00209 00210 /* Names for the libintl functions are a problem. They must not clash 00211 with existing names and they should follow ANSI C. But this source 00212 code is also used in GNU C Library where the names have a __ 00213 prefix. So we have to make a difference here. */ 00214 #ifdef _LIBC 00215 # define DCGETTEXT __dcgettext 00216 #else 00217 # define DCGETTEXT dcgettext__ 00218 #endif 00219 00220 /* Checking whether the binaries runs SUID must be done and glibc provides 00221 easier methods therefore we make a difference here. */ 00222 #ifdef _LIBC 00223 # define ENABLE_SECURE __libc_enable_secure 00224 # define DETERMINE_SECURE 00225 #else 00226 static int enable_secure; 00227 # define ENABLE_SECURE (enable_secure == 1) 00228 # define DETERMINE_SECURE \ 00229 if (enable_secure == 0) \ 00230 { \ 00231 if (getuid () != geteuid () || getgid () != getegid ()) \ 00232 enable_secure = 1; \ 00233 else \ 00234 enable_secure = -1; \ 00235 } 00236 #endif 00237 00238 /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY 00239 locale. */ 00240 char * 00241 DCGETTEXT (domainname, msgid, category) 00242 const char *domainname; 00243 const char *msgid; 00244 int category; 00245 { 00246 #ifndef HAVE_ALLOCA 00247 struct block_list *block_list = NULL; 00248 #endif 00249 struct loaded_l10nfile *domain; 00250 struct binding *binding; 00251 const char *categoryname; 00252 const char *categoryvalue; 00253 char *dirname, *xdomainname; 00254 char *single_locale; 00255 char *retval; 00256 int saved_errno = errno; 00257 00258 /* If no real MSGID is given return NULL. */ 00259 if (msgid == NULL) 00260 return NULL; 00261 00262 /* See whether this is a SUID binary or not. */ 00263 DETERMINE_SECURE; 00264 00265 /* If DOMAINNAME is NULL, we are interested in the default domain. If 00266 CATEGORY is not LC_MESSAGES this might not make much sense but the 00267 definition left this undefined. */ 00268 if (domainname == NULL) 00269 domainname = _nl_current_default_domain; 00270 00271 /* First find matching binding. */ 00272 for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) 00273 { 00274 int compare = strcmp (domainname, binding->domainname); 00275 if (compare == 0) 00276 /* We found it! */ 00277 break; 00278 if (compare < 0) 00279 { 00280 /* It is not in the list. */ 00281 binding = NULL; 00282 break; 00283 } 00284 } 00285 00286 if (binding == NULL) 00287 dirname = (char *) _nl_default_dirname; 00288 else if (binding->dirname[0] == '/') 00289 dirname = binding->dirname; 00290 else 00291 { 00292 /* We have a relative path. Make it absolute now. */ 00293 size_t dirname_len = strlen (binding->dirname) + 1; 00294 size_t path_max; 00295 char *ret; 00296 00297 path_max = (unsigned int) PATH_MAX; 00298 path_max += 2; /* The getcwd docs say to do this. */ 00299 00300 dirname = (char *) alloca (path_max + dirname_len); 00301 ADD_BLOCK (block_list, dirname); 00302 00303 __set_errno (0); 00304 while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE) 00305 { 00306 path_max += PATH_INCR; 00307 dirname = (char *) alloca (path_max + dirname_len); 00308 ADD_BLOCK (block_list, dirname); 00309 __set_errno (0); 00310 } 00311 00312 if (ret == NULL) 00313 { 00314 /* We cannot get the current working directory. Don't signal an 00315 error but simply return the default string. */ 00316 FREE_BLOCKS (block_list); 00317 __set_errno (saved_errno); 00318 return (char *) msgid; 00319 } 00320 00321 stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname); 00322 } 00323 00324 /* Now determine the symbolic name of CATEGORY and its value. */ 00325 categoryname = category_to_name (category); 00326 categoryvalue = guess_category_value (category, categoryname); 00327 00328 xdomainname = (char *) alloca (strlen (categoryname) 00329 + strlen (domainname) + 5); 00330 ADD_BLOCK (block_list, xdomainname); 00331 00332 stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"), 00333 domainname), 00334 ".mo"); 00335 00336 /* Creating working area. */ 00337 single_locale = (char *) alloca (strlen (categoryvalue) + 1); 00338 ADD_BLOCK (block_list, single_locale); 00339 00340 00341 /* Search for the given string. This is a loop because we perhaps 00342 got an ordered list of languages to consider for the translation. */ 00343 while (1) 00344 { 00345 /* Make CATEGORYVALUE point to the next element of the list. */ 00346 while (categoryvalue[0] != '\0' && categoryvalue[0] == ':') 00347 ++categoryvalue; 00348 if (categoryvalue[0] == '\0') 00349 { 00350 /* The whole contents of CATEGORYVALUE has been searched but 00351 no valid entry has been found. We solve this situation 00352 by implicitly appending a "C" entry, i.e. no translation 00353 will take place. */ 00354 single_locale[0] = 'C'; 00355 single_locale[1] = '\0'; 00356 } 00357 else 00358 { 00359 char *cp = single_locale; 00360 while (categoryvalue[0] != '\0' && categoryvalue[0] != ':') 00361 *cp++ = *categoryvalue++; 00362 *cp = '\0'; 00363 00364 /* When this is a SUID binary we must not allow accessing files 00365 outside the dedicated directories. */ 00366 if (ENABLE_SECURE 00367 && (memchr (single_locale, '/', 00368 _nl_find_language (single_locale) - single_locale) 00369 != NULL)) 00370 /* Ingore this entry. */ 00371 continue; 00372 } 00373 00374 /* If the current locale value is C (or POSIX) we don't load a 00375 domain. Return the MSGID. */ 00376 if (strcmp (single_locale, "C") == 0 00377 || strcmp (single_locale, "POSIX") == 0) 00378 { 00379 FREE_BLOCKS (block_list); 00380 __set_errno (saved_errno); 00381 return (char *) msgid; 00382 } 00383 00384 00385 /* Find structure describing the message catalog matching the 00386 DOMAINNAME and CATEGORY. */ 00387 domain = _nl_find_domain (dirname, single_locale, xdomainname); 00388 00389 if (domain != NULL) 00390 { 00391 retval = find_msg (domain, msgid); 00392 00393 if (retval == NULL) 00394 { 00395 int cnt; 00396 00397 for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) 00398 { 00399 retval = find_msg (domain->successor[cnt], msgid); 00400 00401 if (retval != NULL) 00402 break; 00403 } 00404 } 00405 00406 if (retval != NULL) 00407 { 00408 FREE_BLOCKS (block_list); 00409 __set_errno (saved_errno); 00410 return retval; 00411 } 00412 } 00413 } 00414 /* NOTREACHED */ 00415 } 00416 00417 #ifdef _LIBC 00418 /* Alias for function name in GNU C Library. */ 00419 weak_alias (__dcgettext, dcgettext); 00420 #endif 00421 00422 00423 static char * 00424 internal_function 00425 find_msg (domain_file, msgid) 00426 struct loaded_l10nfile *domain_file; 00427 const char *msgid; 00428 { 00429 size_t act = 0; 00430 size_t top, bottom; 00431 struct loaded_domain *domain; 00432 00433 if (domain_file->decided == 0) 00434 _nl_load_domain (domain_file); 00435 00436 if (domain_file->data == NULL) 00437 return NULL; 00438 00439 domain = (struct loaded_domain *) domain_file->data; 00440 00441 /* Locate the MSGID and its translation. */ 00442 if (domain->hash_size > 2 && domain->hash_tab != NULL) 00443 { 00444 /* Use the hashing table. */ 00445 nls_uint32 len = strlen (msgid); 00446 nls_uint32 hash_val = hash_string (msgid); 00447 nls_uint32 idx = hash_val % domain->hash_size; 00448 nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); 00449 nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]); 00450 00451 if (nstr == 0) 00452 /* Hash table entry is empty. */ 00453 return NULL; 00454 00455 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 00456 && strcmp (msgid, 00457 domain->data + W (domain->must_swap, 00458 domain->orig_tab[nstr - 1].offset)) == 0) 00459 return (char *) domain->data + W (domain->must_swap, 00460 domain->trans_tab[nstr - 1].offset); 00461 00462 while (1) 00463 { 00464 if (idx >= domain->hash_size - incr) 00465 idx -= domain->hash_size - incr; 00466 else 00467 idx += incr; 00468 00469 nstr = W (domain->must_swap, domain->hash_tab[idx]); 00470 if (nstr == 0) 00471 /* Hash table entry is empty. */ 00472 return NULL; 00473 00474 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len 00475 && strcmp (msgid, 00476 domain->data + W (domain->must_swap, 00477 domain->orig_tab[nstr - 1].offset)) 00478 == 0) 00479 return (char *) domain->data 00480 + W (domain->must_swap, domain->trans_tab[nstr - 1].offset); 00481 } 00482 /* NOTREACHED */ 00483 } 00484 00485 /* Now we try the default method: binary search in the sorted 00486 array of messages. */ 00487 bottom = 0; 00488 top = domain->nstrings; 00489 while (bottom < top) 00490 { 00491 int cmp_val; 00492 00493 act = (bottom + top) / 2; 00494 cmp_val = strcmp (msgid, domain->data 00495 + W (domain->must_swap, 00496 domain->orig_tab[act].offset)); 00497 if (cmp_val < 0) 00498 top = act; 00499 else if (cmp_val > 0) 00500 bottom = act + 1; 00501 else 00502 break; 00503 } 00504 00505 /* If an translation is found return this. */ 00506 return bottom >= top ? NULL : (char *) domain->data 00507 + W (domain->must_swap, 00508 domain->trans_tab[act].offset); 00509 } 00510 00511 00512 /* Return string representation of locale CATEGORY. */ 00513 static const char * 00514 internal_function 00515 category_to_name (category) 00516 int category; 00517 { 00518 const char *retval; 00519 00520 switch (category) 00521 { 00522 #ifdef LC_COLLATE 00523 case LC_COLLATE: 00524 retval = "LC_COLLATE"; 00525 break; 00526 #endif 00527 #ifdef LC_CTYPE 00528 case LC_CTYPE: 00529 retval = "LC_CTYPE"; 00530 break; 00531 #endif 00532 #ifdef LC_MONETARY 00533 case LC_MONETARY: 00534 retval = "LC_MONETARY"; 00535 break; 00536 #endif 00537 #ifdef LC_NUMERIC 00538 case LC_NUMERIC: 00539 retval = "LC_NUMERIC"; 00540 break; 00541 #endif 00542 #ifdef LC_TIME 00543 case LC_TIME: 00544 retval = "LC_TIME"; 00545 break; 00546 #endif 00547 #ifdef LC_MESSAGES 00548 case LC_MESSAGES: 00549 retval = "LC_MESSAGES"; 00550 break; 00551 #endif 00552 #ifdef LC_RESPONSE 00553 case LC_RESPONSE: 00554 retval = "LC_RESPONSE"; 00555 break; 00556 #endif 00557 #ifdef LC_ALL 00558 case LC_ALL: 00559 /* This might not make sense but is perhaps better than any other 00560 value. */ 00561 retval = "LC_ALL"; 00562 break; 00563 #endif 00564 default: 00565 /* If you have a better idea for a default value let me know. */ 00566 retval = "LC_XXX"; 00567 } 00568 00569 return retval; 00570 } 00571 00572 /* Guess value of current locale from value of the environment variables. */ 00573 static const char * 00574 internal_function 00575 guess_category_value (category, categoryname) 00576 int category; 00577 const char *categoryname; 00578 { 00579 const char *retval; 00580 00581 /* The highest priority value is the `LANGUAGE' environment 00582 variable. This is a GNU extension. */ 00583 retval = getenv ("LANGUAGE"); 00584 if (retval != NULL && retval[0] != '\0') 00585 return retval; 00586 00587 /* `LANGUAGE' is not set. So we have to proceed with the POSIX 00588 methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some 00589 systems this can be done by the `setlocale' function itself. */ 00590 #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL 00591 return setlocale (category, NULL); 00592 #else 00593 /* Setting of LC_ALL overwrites all other. */ 00594 retval = getenv ("LC_ALL"); 00595 if (retval != NULL && retval[0] != '\0') 00596 return retval; 00597 00598 /* Next comes the name of the desired category. */ 00599 retval = getenv (categoryname); 00600 if (retval != NULL && retval[0] != '\0') 00601 return retval; 00602 00603 /* Last possibility is the LANG environment variable. */ 00604 retval = getenv ("LANG"); 00605 if (retval != NULL && retval[0] != '\0') 00606 return retval; 00607 00608 /* We use C as the default domain. POSIX says this is implementation 00609 defined. */ 00610 return "C"; 00611 #endif 00612 } 00613 00614 /* @@ begin of epilog @@ */ 00615 00616 /* We don't want libintl.a to depend on any other library. So we 00617 avoid the non-standard function stpcpy. In GNU C Library this 00618 function is available, though. Also allow the symbol HAVE_STPCPY 00619 to be defined. */ 00620 #if !_LIBC && !HAVE_STPCPY 00621 static char * 00622 stpcpy (dest, src) 00623 char *dest; 00624 const char *src; 00625 { 00626 while ((*dest++ = *src++) != '\0') 00627 /* Do nothing. */ ; 00628 return dest - 1; 00629 } 00630 #endif 00631 00632 00633 #ifdef _LIBC 00634 /* If we want to free all resources we have to do some work at 00635 program's end. */ 00636 static void __attribute__ ((unused)) 00637 free_mem (void) 00638 { 00639 struct binding *runp; 00640 00641 for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next) 00642 { 00643 free (runp->domainname); 00644 if (runp->dirname != _nl_default_dirname) 00645 /* Yes, this is a pointer comparison. */ 00646 free (runp->dirname); 00647 } 00648 00649 if (_nl_current_default_domain != _nl_default_default_domain) 00650 /* Yes, again a pointer comparison. */ 00651 free ((char *) _nl_current_default_domain); 00652 } 00653 00654 text_set_element (__libc_subfreeres, free_mem); 00655 #endif