File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lrzsz / intl / dcgettext.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Thu Oct 24 15:49:50 2019 UTC (5 years, 5 months ago) by misho
Branches: lrzsz, MAIN
CVS tags: v0_12_20p5, HEAD
lrzsz ver 0.12.20

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>