Mercurial > pidgin
view intl/dcgettext.c @ 412:ad08e67ec874
[gaim-migrate @ 422]
A woman went to her doctor for advice. She told him that her husband had
developed a penchant for anal sex, and she was not sure that it was such
a good idea.
The doctor asked, "Do you enjoy it?"
She said that she did.
He asked, "Does it hurt you?"
She said that it didn't.
The doctor then told her, "Well, then, there's no reason that you
shouldn't practice anal sex, if that's what you like, so long as you
take care not to get pregnant."
The woman was mystified. She asked "You can get pregnant from anal sex?"
The doctor replied, "Of course. Where do you think attorneys come from?"
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Fri, 16 Jun 2000 09:29:36 +0000 |
parents | d64101dc5c7e |
children |
line wrap: on
line source
/* Implementation of the dcgettext(3) function. Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <sys/types.h> #if defined __GNUC__ && !defined C_ALLOCA # define alloca __builtin_alloca # define HAVE_ALLOCA 1 #else # if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA # include <alloca.h> # else # ifdef _AIX #pragma alloca # else # ifndef alloca char *alloca (); # endif # endif # endif #endif #include <errno.h> #ifndef errno extern int errno; #endif #ifndef __set_errno # define __set_errno(val) errno = (val) #endif #if defined STDC_HEADERS || defined _LIBC # include <stdlib.h> #else char *getenv (); # ifdef HAVE_MALLOC_H # include <malloc.h> # else void free (); # endif #endif #if defined HAVE_STRING_H || defined _LIBC # ifndef _GNU_SOURCE # define _GNU_SOURCE 1 # endif # include <string.h> #else # include <strings.h> #endif #if !HAVE_STRCHR && !defined _LIBC # ifndef strchr # define strchr index # endif #endif #if defined HAVE_UNISTD_H || defined _LIBC # include <unistd.h> #endif #include "gettext.h" #include "gettextP.h" #ifdef _LIBC # include <libintl.h> #else # include "libgettext.h" #endif #include "hash-string.h" /* @@ end of prolog @@ */ #ifdef _LIBC /* Rename the non ANSI C functions. This is required by the standard because some ANSI C functions will require linking with this object file and the name space must not be polluted. */ # define getcwd __getcwd # ifndef stpcpy # define stpcpy __stpcpy # endif #else # if !defined HAVE_GETCWD char *getwd (); # define getcwd(buf, max) getwd (buf) # else char *getcwd (); # endif # ifndef HAVE_STPCPY static char *stpcpy PARAMS ((char *dest, const char *src)); # endif #endif /* Amount to increase buffer size by in each try. */ #define PATH_INCR 32 /* The following is from pathmax.h. */ /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define PATH_MAX but might cause redefinition warnings when sys/param.h is later included (as on MORE/BSD 4.3). */ #if defined(_POSIX_VERSION) || (defined(HAVE_LIMITS_H) && !defined(__GNUC__)) # include <limits.h> #endif #ifndef _POSIX_PATH_MAX # define _POSIX_PATH_MAX 255 #endif #if !defined(PATH_MAX) && defined(_PC_PATH_MAX) # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX)) #endif /* Don't include sys/param.h if it already has been. */ #if defined(HAVE_SYS_PARAM_H) && !defined(PATH_MAX) && !defined(MAXPATHLEN) # include <sys/param.h> #endif #if !defined(PATH_MAX) && defined(MAXPATHLEN) # define PATH_MAX MAXPATHLEN #endif #ifndef PATH_MAX # define PATH_MAX _POSIX_PATH_MAX #endif /* XPG3 defines the result of `setlocale (category, NULL)' as: ``Directs `setlocale()' to query `category' and return the current setting of `local'.'' However it does not specify the exact format. And even worse: POSIX defines this not at all. So we can use this feature only on selected system (e.g. those using GNU C Library). */ #ifdef _LIBC # define HAVE_LOCALE_NULL #endif /* Name of the default domain used for gettext(3) prior any call to textdomain(3). The default value for this is "messages". */ const char _nl_default_default_domain[] = "messages"; /* Value used as the default domain for gettext(3). */ const char *_nl_current_default_domain = _nl_default_default_domain; /* Contains the default location of the message catalogs. */ const char _nl_default_dirname[] = GNULOCALEDIR; /* List with bindings of specific domains created by bindtextdomain() calls. */ struct binding *_nl_domain_bindings; /* Prototypes for local functions. */ static char *find_msg PARAMS ((struct loaded_l10nfile *domain_file, const char *msgid)) internal_function; static const char *category_to_name PARAMS ((int category)) internal_function; static const char *guess_category_value PARAMS ((int category, const char *categoryname)) internal_function; /* For those loosing systems which don't have `alloca' we have to add some additional code emulating it. */ #ifdef HAVE_ALLOCA /* Nothing has to be done. */ # define ADD_BLOCK(list, address) /* nothing */ # define FREE_BLOCKS(list) /* nothing */ #else struct block_list { void *address; struct block_list *next; }; # define ADD_BLOCK(list, addr) \ do { \ struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \ /* If we cannot get a free block we cannot add the new element to \ the list. */ \ if (newp != NULL) { \ newp->address = (addr); \ newp->next = (list); \ (list) = newp; \ } \ } while (0) # define FREE_BLOCKS(list) \ do { \ while (list != NULL) { \ struct block_list *old = list; \ list = list->next; \ free (old); \ } \ } while (0) # undef alloca # define alloca(size) (malloc (size)) #endif /* have alloca */ /* Names for the libintl functions are a problem. They must not clash with existing names and they should follow ANSI C. But this source code is also used in GNU C Library where the names have a __ prefix. So we have to make a difference here. */ #ifdef _LIBC # define DCGETTEXT __dcgettext #else # define DCGETTEXT dcgettext__ #endif /* Checking whether the binaries runs SUID must be done and glibc provides easier methods therefore we make a difference here. */ #ifdef _LIBC # define ENABLE_SECURE __libc_enable_secure # define DETERMINE_SECURE #else static int enable_secure; # define ENABLE_SECURE (enable_secure == 1) # define DETERMINE_SECURE \ if (enable_secure == 0) \ { \ if (getuid () != geteuid () || getgid () != getegid ()) \ enable_secure = 1; \ else \ enable_secure = -1; \ } #endif /* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY locale. */ char * DCGETTEXT (domainname, msgid, category) const char *domainname; const char *msgid; int category; { #ifndef HAVE_ALLOCA struct block_list *block_list = NULL; #endif struct loaded_l10nfile *domain; struct binding *binding; const char *categoryname; const char *categoryvalue; char *dirname, *xdomainname; char *single_locale; char *retval; int saved_errno = errno; /* If no real MSGID is given return NULL. */ if (msgid == NULL) return NULL; /* See whether this is a SUID binary or not. */ DETERMINE_SECURE; /* If DOMAINNAME is NULL, we are interested in the default domain. If CATEGORY is not LC_MESSAGES this might not make much sense but the definition left this undefined. */ if (domainname == NULL) domainname = _nl_current_default_domain; /* First find matching binding. */ for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) { int compare = strcmp (domainname, binding->domainname); if (compare == 0) /* We found it! */ break; if (compare < 0) { /* It is not in the list. */ binding = NULL; break; } } if (binding == NULL) dirname = (char *) _nl_default_dirname; else if (binding->dirname[0] == '/') dirname = binding->dirname; else { /* We have a relative path. Make it absolute now. */ size_t dirname_len = strlen (binding->dirname) + 1; size_t path_max; char *ret; path_max = (unsigned int) PATH_MAX; path_max += 2; /* The getcwd docs say to do this. */ dirname = (char *) alloca (path_max + dirname_len); ADD_BLOCK (block_list, dirname); __set_errno (0); while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE) { path_max += PATH_INCR; dirname = (char *) alloca (path_max + dirname_len); ADD_BLOCK (block_list, dirname); __set_errno (0); } if (ret == NULL) { /* We cannot get the current working directory. Don't signal an error but simply return the default string. */ FREE_BLOCKS (block_list); __set_errno (saved_errno); return (char *) msgid; } stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname); } /* Now determine the symbolic name of CATEGORY and its value. */ categoryname = category_to_name (category); categoryvalue = guess_category_value (category, categoryname); xdomainname = (char *) alloca (strlen (categoryname) + strlen (domainname) + 5); ADD_BLOCK (block_list, xdomainname); stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"), domainname), ".mo"); /* Creating working area. */ single_locale = (char *) alloca (strlen (categoryvalue) + 1); ADD_BLOCK (block_list, single_locale); /* Search for the given string. This is a loop because we perhaps got an ordered list of languages to consider for the translation. */ while (1) { /* Make CATEGORYVALUE point to the next element of the list. */ while (categoryvalue[0] != '\0' && categoryvalue[0] == ':') ++categoryvalue; if (categoryvalue[0] == '\0') { /* The whole contents of CATEGORYVALUE has been searched but no valid entry has been found. We solve this situation by implicitly appending a "C" entry, i.e. no translation will take place. */ single_locale[0] = 'C'; single_locale[1] = '\0'; } else { char *cp = single_locale; while (categoryvalue[0] != '\0' && categoryvalue[0] != ':') *cp++ = *categoryvalue++; *cp = '\0'; /* When this is a SUID binary we must not allow accessing files outside the dedicated directories. */ if (ENABLE_SECURE && (memchr (single_locale, '/', _nl_find_language (single_locale) - single_locale) != NULL)) /* Ingore this entry. */ continue; } /* If the current locale value is C (or POSIX) we don't load a domain. Return the MSGID. */ if (strcmp (single_locale, "C") == 0 || strcmp (single_locale, "POSIX") == 0) { FREE_BLOCKS (block_list); __set_errno (saved_errno); return (char *) msgid; } /* Find structure describing the message catalog matching the DOMAINNAME and CATEGORY. */ domain = _nl_find_domain (dirname, single_locale, xdomainname); if (domain != NULL) { retval = find_msg (domain, msgid); if (retval == NULL) { int cnt; for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) { retval = find_msg (domain->successor[cnt], msgid); if (retval != NULL) break; } } if (retval != NULL) { FREE_BLOCKS (block_list); __set_errno (saved_errno); return retval; } } } /* NOTREACHED */ } #ifdef _LIBC /* Alias for function name in GNU C Library. */ weak_alias (__dcgettext, dcgettext); #endif static char * internal_function find_msg (domain_file, msgid) struct loaded_l10nfile *domain_file; const char *msgid; { size_t act = 0; size_t top, bottom; struct loaded_domain *domain; if (domain_file->decided == 0) _nl_load_domain (domain_file); if (domain_file->data == NULL) return NULL; domain = (struct loaded_domain *) domain_file->data; /* Locate the MSGID and its translation. */ if (domain->hash_size > 2 && domain->hash_tab != NULL) { /* Use the hashing table. */ nls_uint32 len = strlen (msgid); nls_uint32 hash_val = hash_string (msgid); nls_uint32 idx = hash_val % domain->hash_size; nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2)); nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]); if (nstr == 0) /* Hash table entry is empty. */ return NULL; if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len && strcmp (msgid, domain->data + W (domain->must_swap, domain->orig_tab[nstr - 1].offset)) == 0) return (char *) domain->data + W (domain->must_swap, domain->trans_tab[nstr - 1].offset); while (1) { if (idx >= domain->hash_size - incr) idx -= domain->hash_size - incr; else idx += incr; nstr = W (domain->must_swap, domain->hash_tab[idx]); if (nstr == 0) /* Hash table entry is empty. */ return NULL; if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len && strcmp (msgid, domain->data + W (domain->must_swap, domain->orig_tab[nstr - 1].offset)) == 0) return (char *) domain->data + W (domain->must_swap, domain->trans_tab[nstr - 1].offset); } /* NOTREACHED */ } /* Now we try the default method: binary search in the sorted array of messages. */ bottom = 0; top = domain->nstrings; while (bottom < top) { int cmp_val; act = (bottom + top) / 2; cmp_val = strcmp (msgid, domain->data + W (domain->must_swap, domain->orig_tab[act].offset)); if (cmp_val < 0) top = act; else if (cmp_val > 0) bottom = act + 1; else break; } /* If an translation is found return this. */ return bottom >= top ? NULL : (char *) domain->data + W (domain->must_swap, domain->trans_tab[act].offset); } /* Return string representation of locale CATEGORY. */ static const char * internal_function category_to_name (category) int category; { const char *retval; switch (category) { #ifdef LC_COLLATE case LC_COLLATE: retval = "LC_COLLATE"; break; #endif #ifdef LC_CTYPE case LC_CTYPE: retval = "LC_CTYPE"; break; #endif #ifdef LC_MONETARY case LC_MONETARY: retval = "LC_MONETARY"; break; #endif #ifdef LC_NUMERIC case LC_NUMERIC: retval = "LC_NUMERIC"; break; #endif #ifdef LC_TIME case LC_TIME: retval = "LC_TIME"; break; #endif #ifdef LC_MESSAGES case LC_MESSAGES: retval = "LC_MESSAGES"; break; #endif #ifdef LC_RESPONSE case LC_RESPONSE: retval = "LC_RESPONSE"; break; #endif #ifdef LC_ALL case LC_ALL: /* This might not make sense but is perhaps better than any other value. */ retval = "LC_ALL"; break; #endif default: /* If you have a better idea for a default value let me know. */ retval = "LC_XXX"; } return retval; } /* Guess value of current locale from value of the environment variables. */ static const char * internal_function guess_category_value (category, categoryname) int category; const char *categoryname; { const char *retval; /* The highest priority value is the `LANGUAGE' environment variable. This is a GNU extension. */ retval = getenv ("LANGUAGE"); if (retval != NULL && retval[0] != '\0') return retval; /* `LANGUAGE' is not set. So we have to proceed with the POSIX methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some systems this can be done by the `setlocale' function itself. */ #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL return setlocale (category, NULL); #else /* Setting of LC_ALL overwrites all other. */ retval = getenv ("LC_ALL"); if (retval != NULL && retval[0] != '\0') return retval; /* Next comes the name of the desired category. */ retval = getenv (categoryname); if (retval != NULL && retval[0] != '\0') return retval; /* Last possibility is the LANG environment variable. */ retval = getenv ("LANG"); if (retval != NULL && retval[0] != '\0') return retval; /* We use C as the default domain. POSIX says this is implementation defined. */ return "C"; #endif } /* @@ begin of epilog @@ */ /* We don't want libintl.a to depend on any other library. So we avoid the non-standard function stpcpy. In GNU C Library this function is available, though. Also allow the symbol HAVE_STPCPY to be defined. */ #if !_LIBC && !HAVE_STPCPY static char * stpcpy (dest, src) char *dest; const char *src; { while ((*dest++ = *src++) != '\0') /* Do nothing. */ ; return dest - 1; } #endif #ifdef _LIBC /* If we want to free all resources we have to do some work at program's end. */ static void __attribute__ ((unused)) free_mem (void) { struct binding *runp; for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next) { free (runp->domainname); if (runp->dirname != _nl_default_dirname) /* Yes, this is a pointer comparison. */ free (runp->dirname); } if (_nl_current_default_domain != _nl_default_default_domain) /* Yes, again a pointer comparison. */ free ((char *) _nl_current_default_domain); } text_set_element (__libc_subfreeres, free_mem); #endif