home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / lyx-0.13.2.tar.gz / lyx-0.13.2.tar / lyx-0.13.2 / intl / dcgettext.c < prev    next >
C/C++ Source or Header  |  1998-04-23  |  16KB  |  601 lines

  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.       /* We don't want libintl.a to depend on any other library.  So
  298.      we avoid the non-standard function stpcpy.  In GNU C Library
  299.      this function is available, though.  Also allow the symbol
  300.      HAVE_STPCPY to be defined.  */
  301.       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
  302.     }
  303.  
  304.   /* Now determine the symbolic name of CATEGORY and its value.  */
  305.   categoryname = category_to_name (category);
  306.   categoryvalue = guess_category_value (category, categoryname);
  307.  
  308.   xdomainname = (char *) alloca (strlen (categoryname)
  309.                  + strlen (domainname) + 5);
  310.   ADD_BLOCK (block_list, xdomainname);
  311.   /* We don't want libintl.a to depend on any other library.  So we
  312.      avoid the non-standard function stpcpy.  In GNU C Library this
  313.      function is available, though.  Also allow the symbol HAVE_STPCPY
  314.      to be defined.  */
  315.   stpcpy (stpcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
  316.           domainname),
  317.       ".mo");
  318.  
  319.   /* Creating working area.  */
  320.   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
  321.   ADD_BLOCK (block_list, single_locale);
  322.  
  323.  
  324.   /* Search for the given string.  This is a loop because we perhaps
  325.      got an ordered list of languages to consider for th translation.  */
  326.   while (1)
  327.     {
  328.       /* Make CATEGORYVALUE point to the next element of the list.  */
  329.       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
  330.     ++categoryvalue;
  331.       if (categoryvalue[0] == '\0')
  332.     {
  333.       /* The whole contents of CATEGORYVALUE has been searched but
  334.          no valid entry has been found.  We solve this situation
  335.          by implicitly appending a "C" entry, i.e. no translation
  336.          will take place.  */
  337.       single_locale[0] = 'C';
  338.       single_locale[1] = '\0';
  339.     }
  340.       else
  341.     {
  342.       char *cp = single_locale;
  343.       while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
  344.         *cp++ = *categoryvalue++;
  345.       *cp = '\0';
  346.     }
  347.  
  348.       /* If the current locale value is C (or POSIX) we don't load a
  349.      domain.  Return the MSGID.  */
  350.       if (strcmp (single_locale, "C") == 0
  351.       || strcmp (single_locale, "POSIX") == 0)
  352.     {
  353.       FREE_BLOCKS (block_list);
  354.       __set_errno (saved_errno);
  355.       return (char *) msgid;
  356.     }
  357.  
  358.  
  359.       /* Find structure describing the message catalog matching the
  360.      DOMAINNAME and CATEGORY.  */
  361.       domain = _nl_find_domain (dirname, single_locale, xdomainname);
  362.  
  363.       if (domain != NULL)
  364.     {
  365.       retval = find_msg (domain, msgid);
  366.  
  367.       if (retval == NULL)
  368.         {
  369.           int cnt;
  370.  
  371.           for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
  372.         {
  373.           retval = find_msg (domain->successor[cnt], msgid);
  374.  
  375.           if (retval != NULL)
  376.             break;
  377.         }
  378.         }
  379.  
  380.       if (retval != NULL)
  381.         {
  382.           FREE_BLOCKS (block_list);
  383.           __set_errno (saved_errno);
  384.           return retval;
  385.         }
  386.     }
  387.     }
  388.   /* NOTREACHED */
  389. }
  390.  
  391. #ifdef _LIBC
  392. /* Alias for function name in GNU C Library.  */
  393. weak_alias (__dcgettext, dcgettext);
  394. #endif
  395.  
  396.  
  397. static char *
  398. find_msg (domain_file, msgid)
  399.      struct loaded_l10nfile *domain_file;
  400.      const char *msgid;
  401. {
  402.   size_t top, act, bottom;
  403.   struct loaded_domain *domain;
  404.  
  405.   if (domain_file->decided == 0)
  406.     _nl_load_domain (domain_file);
  407.  
  408.   if (domain_file->data == NULL)
  409.     return NULL;
  410.  
  411.   domain = (struct loaded_domain *) domain_file->data;
  412.  
  413.   /* Locate the MSGID and its translation.  */
  414.   if (domain->hash_size > 2 && domain->hash_tab != NULL)
  415.     {
  416.       /* Use the hashing table.  */
  417.       nls_uint32 len = strlen (msgid);
  418.       nls_uint32 hash_val = hash_string (msgid);
  419.       nls_uint32 idx = hash_val % domain->hash_size;
  420.       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
  421.       nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
  422.  
  423.       if (nstr == 0)
  424.     /* Hash table entry is empty.  */
  425.     return NULL;
  426.  
  427.       if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
  428.       && strcmp (msgid,
  429.              domain->data + W (domain->must_swap,
  430.                        domain->orig_tab[nstr - 1].offset)) == 0)
  431.     return (char *) domain->data + W (domain->must_swap,
  432.                       domain->trans_tab[nstr - 1].offset);
  433.  
  434.       while (1)
  435.     {
  436.       if (idx >= domain->hash_size - incr)
  437.         idx -= domain->hash_size - incr;
  438.       else
  439.         idx += incr;
  440.  
  441.       nstr = W (domain->must_swap, domain->hash_tab[idx]);
  442.       if (nstr == 0)
  443.         /* Hash table entry is empty.  */
  444.         return NULL;
  445.  
  446.       if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
  447.           && strcmp (msgid,
  448.              domain->data + W (domain->must_swap,
  449.                        domain->orig_tab[nstr - 1].offset))
  450.              == 0)
  451.         return (char *) domain->data
  452.           + W (domain->must_swap, domain->trans_tab[nstr - 1].offset);
  453.     }
  454.       /* NOTREACHED */
  455.     }
  456.  
  457.   /* Now we try the default method:  binary search in the sorted
  458.      array of messages.  */
  459.   bottom = 0;
  460.   top = domain->nstrings;
  461.   while (bottom < top)
  462.     {
  463.       int cmp_val;
  464.  
  465.       act = (bottom + top) / 2;
  466.       cmp_val = strcmp (msgid, domain->data
  467.                    + W (domain->must_swap,
  468.                     domain->orig_tab[act].offset));
  469.       if (cmp_val < 0)
  470.     top = act;
  471.       else if (cmp_val > 0)
  472.     bottom = act + 1;
  473.       else
  474.     break;
  475.     }
  476.  
  477.   /* If an translation is found return this.  */
  478.   return bottom >= top ? NULL : (char *) domain->data
  479.                                 + W (domain->must_swap,
  480.                      domain->trans_tab[act].offset);
  481. }
  482.  
  483.  
  484. /* Return string representation of locale CATEGORY.  */
  485. static const char *
  486. category_to_name (category)
  487.      int category;
  488. {
  489.   const char *retval;
  490.  
  491.   switch (category)
  492.   {
  493. #ifdef LC_COLLATE
  494.   case LC_COLLATE:
  495.     retval = "LC_COLLATE";
  496.     break;
  497. #endif
  498. #ifdef LC_CTYPE
  499.   case LC_CTYPE:
  500.     retval = "LC_CTYPE";
  501.     break;
  502. #endif
  503. #ifdef LC_MONETARY
  504.   case LC_MONETARY:
  505.     retval = "LC_MONETARY";
  506.     break;
  507. #endif
  508. #ifdef LC_NUMERIC
  509.   case LC_NUMERIC:
  510.     retval = "LC_NUMERIC";
  511.     break;
  512. #endif
  513. #ifdef LC_TIME
  514.   case LC_TIME:
  515.     retval = "LC_TIME";
  516.     break;
  517. #endif
  518. #ifdef LC_MESSAGES
  519.   case LC_MESSAGES:
  520.     retval = "LC_MESSAGES";
  521.     break;
  522. #endif
  523. #ifdef LC_RESPONSE
  524.   case LC_RESPONSE:
  525.     retval = "LC_RESPONSE";
  526.     break;
  527. #endif
  528. #ifdef LC_ALL
  529.   case LC_ALL:
  530.     /* This might not make sense but is perhaps better than any other
  531.        value.  */
  532.     retval = "LC_ALL";
  533.     break;
  534. #endif
  535.   default:
  536.     /* If you have a better idea for a default value let me know.  */
  537.     retval = "LC_XXX";
  538.   }
  539.  
  540.   return retval;
  541. }
  542.  
  543. /* Guess value of current locale from value of the environment variables.  */
  544. static const char *
  545. guess_category_value (category, categoryname)
  546.      int category;
  547.      const char *categoryname;
  548. {
  549.   const char *retval;
  550.  
  551.   /* The highest priority value is the `LANGUAGE' environment
  552.      variable.  This is a GNU extension.  */
  553.   retval = getenv ("LANGUAGE");
  554.   if (retval != NULL && retval[0] != '\0')
  555.     return retval;
  556.  
  557.   /* `LANGUAGE' is not set.  So we have to proceed with the POSIX
  558.      methods of looking to `LC_ALL', `LC_xxx', and `LANG'.  On some
  559.      systems this can be done by the `setlocale' function itself.  */
  560. #if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
  561.   return setlocale (category, NULL);
  562. #else
  563.   /* Setting of LC_ALL overwrites all other.  */
  564.   retval = getenv ("LC_ALL");
  565.   if (retval != NULL && retval[0] != '\0')
  566.     return retval;
  567.  
  568.   /* Next comes the name of the desired category.  */
  569.   retval = getenv (categoryname);
  570.   if (retval != NULL && retval[0] != '\0')
  571.     return retval;
  572.  
  573.   /* Last possibility is the LANG environment variable.  */
  574.   retval = getenv ("LANG");
  575.   if (retval != NULL && retval[0] != '\0')
  576.     return retval;
  577.  
  578.   /* We use C as the default domain.  POSIX says this is implementation
  579.      defined.  */
  580.   return "C";
  581. #endif
  582. }
  583.  
  584. /* @@ begin of epilog @@ */
  585.  
  586. /* We don't want libintl.a to depend on any other library.  So we
  587.    avoid the non-standard function stpcpy.  In GNU C Library this
  588.    function is available, though.  Also allow the symbol HAVE_STPCPY
  589.    to be defined.  */
  590. #if !_LIBC && !HAVE_STPCPY
  591. static char *
  592. stpcpy (dest, src)
  593.      char *dest;
  594.      const char *src;
  595. {
  596.   while ((*dest++ = *src++) != '\0')
  597.     /* Do nothing. */ ;
  598.   return dest - 1;
  599. }
  600. #endif
  601.