dcgettext.c
上传用户:xxcykj
上传日期:2007-01-04
资源大小:727k
文件大小:16k
源码类别:

Email客户端

开发平台:

Unix_Linux

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