Back to index

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