xref: /netbsd-src/external/gpl2/grep/dist/intl/dcigettext.c (revision a8fa202a6440953be7b92a8960a811bff58203f4)
1 /*	$NetBSD: dcigettext.c,v 1.1.1.1 2016/01/10 21:36:18 christos Exp $	*/
2 
3 /* Implementation of the internal dcigettext function.
4    Copyright (C) 1995-1999, 2000-2002 Free Software Foundation, Inc.
5 
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU Library General Public License as published
8    by the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public
17    License along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19    USA.  */
20 
21 /* Tell glibc's <string.h> to provide a prototype for mempcpy().
22    This must come before <config.h> because <config.h> may include
23    <features.h>, and once <features.h> has been included, it's too late.  */
24 #ifndef _GNU_SOURCE
25 # define _GNU_SOURCE	1
26 #endif
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #include <sys/types.h>
33 
34 #ifdef __GNUC__
35 # define alloca __builtin_alloca
36 # define HAVE_ALLOCA 1
37 #else
38 # if defined HAVE_ALLOCA_H || defined _LIBC
39 #  include <alloca.h>
40 # else
41 #  ifdef _AIX
42  #pragma alloca
43 #  else
44 #   ifndef alloca
45 char *alloca ();
46 #   endif
47 #  endif
48 # endif
49 #endif
50 
51 #include <errno.h>
52 #ifndef errno
53 extern int errno;
54 #endif
55 #ifndef __set_errno
56 # define __set_errno(val) errno = (val)
57 #endif
58 
59 #include <stddef.h>
60 #include <stdlib.h>
61 #include <string.h>
62 
63 #if defined HAVE_UNISTD_H || defined _LIBC
64 # include <unistd.h>
65 #endif
66 
67 #include <locale.h>
68 
69 #if defined HAVE_SYS_PARAM_H || defined _LIBC
70 # include <sys/param.h>
71 #endif
72 
73 #include "gettextP.h"
74 #include "plural-exp.h"
75 #ifdef _LIBC
76 # include <libintl.h>
77 #else
78 # include "libgnuintl.h"
79 #endif
80 #include "hash-string.h"
81 
82 /* Thread safetyness.  */
83 #ifdef _LIBC
84 # include <bits/libc-lock.h>
85 #else
86 /* Provide dummy implementation if this is outside glibc.  */
87 # define __libc_lock_define_initialized(CLASS, NAME)
88 # define __libc_lock_lock(NAME)
89 # define __libc_lock_unlock(NAME)
90 # define __libc_rwlock_define_initialized(CLASS, NAME)
91 # define __libc_rwlock_rdlock(NAME)
92 # define __libc_rwlock_unlock(NAME)
93 #endif
94 
95 /* Alignment of types.  */
96 #if defined __GNUC__ && __GNUC__ >= 2
97 # define alignof(TYPE) __alignof__ (TYPE)
98 #else
99 # define alignof(TYPE) \
100     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
101 #endif
102 
103 /* The internal variables in the standalone libintl.a must have different
104    names than the internal variables in GNU libc, otherwise programs
105    using libintl.a cannot be linked statically.  */
106 #if !defined _LIBC
107 # define _nl_default_default_domain _nl_default_default_domain__
108 # define _nl_current_default_domain _nl_current_default_domain__
109 # define _nl_default_dirname _nl_default_dirname__
110 # define _nl_domain_bindings _nl_domain_bindings__
111 #endif
112 
113 /* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
114 #ifndef offsetof
115 # define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
116 #endif
117 
118 /* @@ end of prolog @@ */
119 
120 #ifdef _LIBC
121 /* Rename the non ANSI C functions.  This is required by the standard
122    because some ANSI C functions will require linking with this object
123    file and the name space must not be polluted.  */
124 # define getcwd __getcwd
125 # ifndef stpcpy
126 #  define stpcpy __stpcpy
127 # endif
128 # define tfind __tfind
129 #else
130 # if !defined HAVE_GETCWD
131 char *getwd ();
132 #  define getcwd(buf, max) getwd (buf)
133 # else
134 char *getcwd ();
135 # endif
136 # ifndef HAVE_STPCPY
137 static char *stpcpy PARAMS ((char *dest, const char *src));
138 # endif
139 # ifndef HAVE_MEMPCPY
140 static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
141 # endif
142 #endif
143 
144 /* Amount to increase buffer size by in each try.  */
145 #define PATH_INCR 32
146 
147 /* The following is from pathmax.h.  */
148 /* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
149    PATH_MAX but might cause redefinition warnings when sys/param.h is
150    later included (as on MORE/BSD 4.3).  */
151 #if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
152 # include <limits.h>
153 #endif
154 
155 #ifndef _POSIX_PATH_MAX
156 # define _POSIX_PATH_MAX 255
157 #endif
158 
159 #if !defined PATH_MAX && defined _PC_PATH_MAX
160 # define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
161 #endif
162 
163 /* Don't include sys/param.h if it already has been.  */
164 #if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
165 # include <sys/param.h>
166 #endif
167 
168 #if !defined PATH_MAX && defined MAXPATHLEN
169 # define PATH_MAX MAXPATHLEN
170 #endif
171 
172 #ifndef PATH_MAX
173 # define PATH_MAX _POSIX_PATH_MAX
174 #endif
175 
176 /* Pathname support.
177    ISSLASH(C)           tests whether C is a directory separator character.
178    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
179                         it may be concatenated to a directory pathname.
180    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
181  */
182 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
183   /* Win32, OS/2, DOS */
184 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
185 # define HAS_DEVICE(P) \
186     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
187      && (P)[1] == ':')
188 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
189 # define IS_PATH_WITH_DIR(P) \
190     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
191 #else
192   /* Unix */
193 # define ISSLASH(C) ((C) == '/')
194 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
195 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
196 #endif
197 
198 /* This is the type used for the search tree where known translations
199    are stored.  */
200 struct known_translation_t
201 {
202   /* Domain in which to search.  */
203   char *domainname;
204 
205   /* The category.  */
206   int category;
207 
208   /* State of the catalog counter at the point the string was found.  */
209   int counter;
210 
211   /* Catalog where the string was found.  */
212   struct loaded_l10nfile *domain;
213 
214   /* And finally the translation.  */
215   const char *translation;
216   size_t translation_length;
217 
218   /* Pointer to the string in question.  */
219   char msgid[ZERO];
220 };
221 
222 /* Root of the search tree with known translations.  We can use this
223    only if the system provides the `tsearch' function family.  */
224 #if defined HAVE_TSEARCH || defined _LIBC
225 # include <search.h>
226 
227 static void *root;
228 
229 # ifdef _LIBC
230 #  define tsearch __tsearch
231 # endif
232 
233 /* Function to compare two entries in the table of known translations.  */
234 static int transcmp PARAMS ((const void *p1, const void *p2));
235 static int
transcmp(p1,p2)236 transcmp (p1, p2)
237      const void *p1;
238      const void *p2;
239 {
240   const struct known_translation_t *s1;
241   const struct known_translation_t *s2;
242   int result;
243 
244   s1 = (const struct known_translation_t *) p1;
245   s2 = (const struct known_translation_t *) p2;
246 
247   result = strcmp (s1->msgid, s2->msgid);
248   if (result == 0)
249     {
250       result = strcmp (s1->domainname, s2->domainname);
251       if (result == 0)
252 	/* We compare the category last (though this is the cheapest
253 	   operation) since it is hopefully always the same (namely
254 	   LC_MESSAGES).  */
255 	result = s1->category - s2->category;
256     }
257 
258   return result;
259 }
260 #endif
261 
262 /* Name of the default domain used for gettext(3) prior any call to
263    textdomain(3).  The default value for this is "messages".  */
264 const char _nl_default_default_domain[] = "messages";
265 
266 /* Value used as the default domain for gettext(3).  */
267 const char *_nl_current_default_domain = _nl_default_default_domain;
268 
269 /* Contains the default location of the message catalogs.  */
270 #if defined __EMX__
271 extern const char _nl_default_dirname[];
272 #else
273 const char _nl_default_dirname[] = LOCALEDIR;
274 #endif
275 
276 /* List with bindings of specific domains created by bindtextdomain()
277    calls.  */
278 struct binding *_nl_domain_bindings;
279 
280 /* Prototypes for local functions.  */
281 static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain,
282 				    unsigned long int n,
283 				    const char *translation,
284 				    size_t translation_len))
285      internal_function;
286 static const char *category_to_name PARAMS ((int category)) internal_function;
287 static const char *guess_category_value PARAMS ((int category,
288 						 const char *categoryname))
289      internal_function;
290 
291 
292 /* For those loosing systems which don't have `alloca' we have to add
293    some additional code emulating it.  */
294 #ifdef HAVE_ALLOCA
295 /* Nothing has to be done.  */
296 # define ADD_BLOCK(list, address) /* nothing */
297 # define FREE_BLOCKS(list) /* nothing */
298 #else
299 struct block_list
300 {
301   void *address;
302   struct block_list *next;
303 };
304 # define ADD_BLOCK(list, addr)						      \
305   do {									      \
306     struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
307     /* If we cannot get a free block we cannot add the new element to	      \
308        the list.  */							      \
309     if (newp != NULL) {							      \
310       newp->address = (addr);						      \
311       newp->next = (list);						      \
312       (list) = newp;							      \
313     }									      \
314   } while (0)
315 # define FREE_BLOCKS(list)						      \
316   do {									      \
317     while (list != NULL) {						      \
318       struct block_list *old = list;					      \
319       list = list->next;						      \
320       free (old);							      \
321     }									      \
322   } while (0)
323 # undef alloca
324 # define alloca(size) (malloc (size))
325 #endif	/* have alloca */
326 
327 
328 #ifdef _LIBC
329 /* List of blocks allocated for translations.  */
330 typedef struct transmem_list
331 {
332   struct transmem_list *next;
333   char data[ZERO];
334 } transmem_block_t;
335 static struct transmem_list *transmem_list;
336 #else
337 typedef unsigned char transmem_block_t;
338 #endif
339 
340 
341 /* Names for the libintl functions are a problem.  They must not clash
342    with existing names and they should follow ANSI C.  But this source
343    code is also used in GNU C Library where the names have a __
344    prefix.  So we have to make a difference here.  */
345 #ifdef _LIBC
346 # define DCIGETTEXT __dcigettext
347 #else
348 # define DCIGETTEXT dcigettext__
349 #endif
350 
351 /* Lock variable to protect the global data in the gettext implementation.  */
352 #ifdef _LIBC
353 __libc_rwlock_define_initialized (, _nl_state_lock)
354 #endif
355 
356 /* Checking whether the binaries runs SUID must be done and glibc provides
357    easier methods therefore we make a difference here.  */
358 #ifdef _LIBC
359 # define ENABLE_SECURE __libc_enable_secure
360 # define DETERMINE_SECURE
361 #else
362 # ifndef HAVE_GETUID
363 #  define getuid() 0
364 # endif
365 # ifndef HAVE_GETGID
366 #  define getgid() 0
367 # endif
368 # ifndef HAVE_GETEUID
369 #  define geteuid() getuid()
370 # endif
371 # ifndef HAVE_GETEGID
372 #  define getegid() getgid()
373 # endif
374 static int enable_secure;
375 # define ENABLE_SECURE (enable_secure == 1)
376 # define DETERMINE_SECURE \
377   if (enable_secure == 0)						      \
378     {									      \
379       if (getuid () != geteuid () || getgid () != getegid ())		      \
380 	enable_secure = 1;						      \
381       else								      \
382 	enable_secure = -1;						      \
383     }
384 #endif
385 
386 /* Get the function to evaluate the plural expression.  */
387 #include "eval-plural.h"
388 
389 /* Look up MSGID in the DOMAINNAME message catalog for the current
390    CATEGORY locale and, if PLURAL is nonzero, search over string
391    depending on the plural form determined by N.  */
392 char *
393 DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
394      const char *domainname;
395      const char *msgid1;
396      const char *msgid2;
397      int plural;
398      unsigned long int n;
399      int category;
400 {
401 #ifndef HAVE_ALLOCA
402   struct block_list *block_list = NULL;
403 #endif
404   struct loaded_l10nfile *domain;
405   struct binding *binding;
406   const char *categoryname;
407   const char *categoryvalue;
408   char *dirname, *xdomainname;
409   char *single_locale;
410   char *retval;
411   size_t retlen;
412   int saved_errno;
413 #if defined HAVE_TSEARCH || defined _LIBC
414   struct known_translation_t *search;
415   struct known_translation_t **foundp = NULL;
416   size_t msgid_len;
417 #endif
418   size_t domainname_len;
419 
420   /* If no real MSGID is given return NULL.  */
421   if (msgid1 == NULL)
422     return NULL;
423 
424   __libc_rwlock_rdlock (_nl_state_lock);
425 
426   /* If DOMAINNAME is NULL, we are interested in the default domain.  If
427      CATEGORY is not LC_MESSAGES this might not make much sense but the
428      definition left this undefined.  */
429   if (domainname == NULL)
430     domainname = _nl_current_default_domain;
431 
432   /* OS/2 specific: backward compatibility with older libintl versions  */
433 #ifdef LC_MESSAGES_COMPAT
434   if (category == LC_MESSAGES_COMPAT)
435     category = LC_MESSAGES;
436 #endif
437 
438 #if defined HAVE_TSEARCH || defined _LIBC
439   msgid_len = strlen (msgid1) + 1;
440 
441   /* Try to find the translation among those which we found at
442      some time.  */
443   search = (struct known_translation_t *)
444 	   alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
445   memcpy (search->msgid, msgid1, msgid_len);
446   search->domainname = (char *) domainname;
447   search->category = category;
448 
449   foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
450   if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
451     {
452       /* Now deal with plural.  */
453       if (plural)
454 	retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
455 				(*foundp)->translation_length);
456       else
457 	retval = (char *) (*foundp)->translation;
458 
459       __libc_rwlock_unlock (_nl_state_lock);
460       return retval;
461     }
462 #endif
463 
464   /* Preserve the `errno' value.  */
465   saved_errno = errno;
466 
467   /* See whether this is a SUID binary or not.  */
468   DETERMINE_SECURE;
469 
470   /* First find matching binding.  */
471   for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
472     {
473       int compare = strcmp (domainname, binding->domainname);
474       if (compare == 0)
475 	/* We found it!  */
476 	break;
477       if (compare < 0)
478 	{
479 	  /* It is not in the list.  */
480 	  binding = NULL;
481 	  break;
482 	}
483     }
484 
485   if (binding == NULL)
486     dirname = (char *) _nl_default_dirname;
487   else if (IS_ABSOLUTE_PATH (binding->dirname))
488     dirname = binding->dirname;
489   else
490     {
491       /* We have a relative path.  Make it absolute now.  */
492       size_t dirname_len = strlen (binding->dirname) + 1;
493       size_t path_max;
494       char *ret;
495 
496       path_max = (unsigned int) PATH_MAX;
497       path_max += 2;		/* The getcwd docs say to do this.  */
498 
499       for (;;)
500 	{
501 	  dirname = (char *) alloca (path_max + dirname_len);
502 	  ADD_BLOCK (block_list, dirname);
503 
504 	  __set_errno (0);
505 	  ret = getcwd (dirname, path_max);
506 	  if (ret != NULL || errno != ERANGE)
507 	    break;
508 
509 	  path_max += path_max / 2;
510 	  path_max += PATH_INCR;
511 	}
512 
513       if (ret == NULL)
514 	{
515 	  /* We cannot get the current working directory.  Don't signal an
516 	     error but simply return the default string.  */
517 	  FREE_BLOCKS (block_list);
518 	  __libc_rwlock_unlock (_nl_state_lock);
519 	  __set_errno (saved_errno);
520 	  return (plural == 0
521 		  ? (char *) msgid1
522 		  /* Use the Germanic plural rule.  */
523 		  : n == 1 ? (char *) msgid1 : (char *) msgid2);
524 	}
525 
526       stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
527     }
528 
529   /* Now determine the symbolic name of CATEGORY and its value.  */
530   categoryname = category_to_name (category);
531   categoryvalue = guess_category_value (category, categoryname);
532 
533   domainname_len = strlen (domainname);
534   xdomainname = (char *) alloca (strlen (categoryname)
535 				 + domainname_len + 5);
536   ADD_BLOCK (block_list, xdomainname);
537 
538   stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
539 		  domainname, domainname_len),
540 	  ".mo");
541 
542   /* Creating working area.  */
543   single_locale = (char *) alloca (strlen (categoryvalue) + 1);
544   ADD_BLOCK (block_list, single_locale);
545 
546 
547   /* Search for the given string.  This is a loop because we perhaps
548      got an ordered list of languages to consider for the translation.  */
549   while (1)
550     {
551       /* Make CATEGORYVALUE point to the next element of the list.  */
552       while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
553 	++categoryvalue;
554       if (categoryvalue[0] == '\0')
555 	{
556 	  /* The whole contents of CATEGORYVALUE has been searched but
557 	     no valid entry has been found.  We solve this situation
558 	     by implicitly appending a "C" entry, i.e. no translation
559 	     will take place.  */
560 	  single_locale[0] = 'C';
561 	  single_locale[1] = '\0';
562 	}
563       else
564 	{
565 	  char *cp = single_locale;
566 	  while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
567 	    *cp++ = *categoryvalue++;
568 	  *cp = '\0';
569 
570 	  /* When this is a SUID binary we must not allow accessing files
571 	     outside the dedicated directories.  */
572 	  if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
573 	    /* Ingore this entry.  */
574 	    continue;
575 	}
576 
577       /* If the current locale value is C (or POSIX) we don't load a
578 	 domain.  Return the MSGID.  */
579       if (strcmp (single_locale, "C") == 0
580 	  || strcmp (single_locale, "POSIX") == 0)
581 	{
582 	  FREE_BLOCKS (block_list);
583 	  __libc_rwlock_unlock (_nl_state_lock);
584 	  __set_errno (saved_errno);
585 	  return (plural == 0
586 		  ? (char *) msgid1
587 		  /* Use the Germanic plural rule.  */
588 		  : n == 1 ? (char *) msgid1 : (char *) msgid2);
589 	}
590 
591 
592       /* Find structure describing the message catalog matching the
593 	 DOMAINNAME and CATEGORY.  */
594       domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
595 
596       if (domain != NULL)
597 	{
598 	  retval = _nl_find_msg (domain, binding, msgid1, &retlen);
599 
600 	  if (retval == NULL)
601 	    {
602 	      int cnt;
603 
604 	      for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
605 		{
606 		  retval = _nl_find_msg (domain->successor[cnt], binding,
607 					 msgid1, &retlen);
608 
609 		  if (retval != NULL)
610 		    {
611 		      domain = domain->successor[cnt];
612 		      break;
613 		    }
614 		}
615 	    }
616 
617 	  if (retval != NULL)
618 	    {
619 	      /* Found the translation of MSGID1 in domain DOMAIN:
620 		 starting at RETVAL, RETLEN bytes.  */
621 	      FREE_BLOCKS (block_list);
622 	      __set_errno (saved_errno);
623 #if defined HAVE_TSEARCH || defined _LIBC
624 	      if (foundp == NULL)
625 		{
626 		  /* Create a new entry and add it to the search tree.  */
627 		  struct known_translation_t *newp;
628 
629 		  newp = (struct known_translation_t *)
630 		    malloc (offsetof (struct known_translation_t, msgid)
631 			    + msgid_len + domainname_len + 1);
632 		  if (newp != NULL)
633 		    {
634 		      newp->domainname =
635 			mempcpy (newp->msgid, msgid1, msgid_len);
636 		      memcpy (newp->domainname, domainname, domainname_len + 1);
637 		      newp->category = category;
638 		      newp->counter = _nl_msg_cat_cntr;
639 		      newp->domain = domain;
640 		      newp->translation = retval;
641 		      newp->translation_length = retlen;
642 
643 		      /* Insert the entry in the search tree.  */
644 		      foundp = (struct known_translation_t **)
645 			tsearch (newp, &root, transcmp);
646 		      if (foundp == NULL
647 			  || __builtin_expect (*foundp != newp, 0))
648 			/* The insert failed.  */
649 			free (newp);
650 		    }
651 		}
652 	      else
653 		{
654 		  /* We can update the existing entry.  */
655 		  (*foundp)->counter = _nl_msg_cat_cntr;
656 		  (*foundp)->domain = domain;
657 		  (*foundp)->translation = retval;
658 		  (*foundp)->translation_length = retlen;
659 		}
660 #endif
661 	      /* Now deal with plural.  */
662 	      if (plural)
663 		retval = plural_lookup (domain, n, retval, retlen);
664 
665 	      __libc_rwlock_unlock (_nl_state_lock);
666 	      return retval;
667 	    }
668 	}
669     }
670   /* NOTREACHED */
671 }
672 
673 
674 char *
675 internal_function
_nl_find_msg(domain_file,domainbinding,msgid,lengthp)676 _nl_find_msg (domain_file, domainbinding, msgid, lengthp)
677      struct loaded_l10nfile *domain_file;
678      struct binding *domainbinding;
679      const char *msgid;
680      size_t *lengthp;
681 {
682   struct loaded_domain *domain;
683   size_t act;
684   char *result;
685   size_t resultlen;
686 
687   if (domain_file->decided == 0)
688     _nl_load_domain (domain_file, domainbinding);
689 
690   if (domain_file->data == NULL)
691     return NULL;
692 
693   domain = (struct loaded_domain *) domain_file->data;
694 
695   /* Locate the MSGID and its translation.  */
696   if (domain->hash_size > 2 && domain->hash_tab != NULL)
697     {
698       /* Use the hashing table.  */
699       nls_uint32 len = strlen (msgid);
700       nls_uint32 hash_val = hash_string (msgid);
701       nls_uint32 idx = hash_val % domain->hash_size;
702       nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
703 
704       while (1)
705 	{
706 	  nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
707 
708 	  if (nstr == 0)
709 	    /* Hash table entry is empty.  */
710 	    return NULL;
711 
712 	  /* Compare msgid with the original string at index nstr-1.
713 	     We compare the lengths with >=, not ==, because plural entries
714 	     are represented by strings with an embedded NUL.  */
715 	  if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) >= len
716 	      && (strcmp (msgid,
717 			  domain->data + W (domain->must_swap,
718 					    domain->orig_tab[nstr - 1].offset))
719 		  == 0))
720 	    {
721 	      act = nstr - 1;
722 	      goto found;
723 	    }
724 
725 	  if (idx >= domain->hash_size - incr)
726 	    idx -= domain->hash_size - incr;
727 	  else
728 	    idx += incr;
729 	}
730       /* NOTREACHED */
731     }
732   else
733     {
734       /* Try the default method:  binary search in the sorted array of
735 	 messages.  */
736       size_t top, bottom;
737 
738       bottom = 0;
739       top = domain->nstrings;
740       while (bottom < top)
741 	{
742 	  int cmp_val;
743 
744 	  act = (bottom + top) / 2;
745 	  cmp_val = strcmp (msgid, (domain->data
746 				    + W (domain->must_swap,
747 					 domain->orig_tab[act].offset)));
748 	  if (cmp_val < 0)
749 	    top = act;
750 	  else if (cmp_val > 0)
751 	    bottom = act + 1;
752 	  else
753 	    goto found;
754 	}
755       /* No translation was found.  */
756       return NULL;
757     }
758 
759  found:
760   /* The translation was found at index ACT.  If we have to convert the
761      string to use a different character set, this is the time.  */
762   result = ((char *) domain->data
763 	    + W (domain->must_swap, domain->trans_tab[act].offset));
764   resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
765 
766 #if defined _LIBC || HAVE_ICONV
767   if (domain->codeset_cntr
768       != (domainbinding != NULL ? domainbinding->codeset_cntr : 0))
769     {
770       /* The domain's codeset has changed through bind_textdomain_codeset()
771 	 since the message catalog was initialized or last accessed.  We
772 	 have to reinitialize the converter.  */
773       _nl_free_domain_conv (domain);
774       _nl_init_domain_conv (domain_file, domain, domainbinding);
775     }
776 
777   if (
778 # ifdef _LIBC
779       domain->conv != (__gconv_t) -1
780 # else
781 #  if HAVE_ICONV
782       domain->conv != (iconv_t) -1
783 #  endif
784 # endif
785       )
786     {
787       /* We are supposed to do a conversion.  First allocate an
788 	 appropriate table with the same structure as the table
789 	 of translations in the file, where we can put the pointers
790 	 to the converted strings in.
791 	 There is a slight complication with plural entries.  They
792 	 are represented by consecutive NUL terminated strings.  We
793 	 handle this case by converting RESULTLEN bytes, including
794 	 NULs.  */
795 
796       if (domain->conv_tab == NULL
797 	  && ((domain->conv_tab = (char **) calloc (domain->nstrings,
798 						    sizeof (char *)))
799 	      == NULL))
800 	/* Mark that we didn't succeed allocating a table.  */
801 	domain->conv_tab = (char **) -1;
802 
803       if (__builtin_expect (domain->conv_tab == (char **) -1, 0))
804 	/* Nothing we can do, no more memory.  */
805 	goto converted;
806 
807       if (domain->conv_tab[act] == NULL)
808 	{
809 	  /* We haven't used this string so far, so it is not
810 	     translated yet.  Do this now.  */
811 	  /* We use a bit more efficient memory handling.
812 	     We allocate always larger blocks which get used over
813 	     time.  This is faster than many small allocations.   */
814 	  __libc_lock_define_initialized (static, lock)
815 # define INITIAL_BLOCK_SIZE	4080
816 	  static unsigned char *freemem;
817 	  static size_t freemem_size;
818 
819 	  const unsigned char *inbuf;
820 	  unsigned char *outbuf;
821 	  int malloc_count;
822 # ifndef _LIBC
823 	  transmem_block_t *transmem_list = NULL;
824 # endif
825 
826 	  __libc_lock_lock (lock);
827 
828 	  inbuf = (const unsigned char *) result;
829 	  outbuf = freemem + sizeof (size_t);
830 
831 	  malloc_count = 0;
832 	  while (1)
833 	    {
834 	      transmem_block_t *newmem;
835 # ifdef _LIBC
836 	      size_t non_reversible;
837 	      int res;
838 
839 	      if (freemem_size < sizeof (size_t))
840 		goto resize_freemem;
841 
842 	      res = __gconv (domain->conv,
843 			     &inbuf, inbuf + resultlen,
844 			     &outbuf,
845 			     outbuf + freemem_size - sizeof (size_t),
846 			     &non_reversible);
847 
848 	      if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
849 		break;
850 
851 	      if (res != __GCONV_FULL_OUTPUT)
852 		{
853 		  __libc_lock_unlock (lock);
854 		  goto converted;
855 		}
856 
857 	      inbuf = result;
858 # else
859 #  if HAVE_ICONV
860 	      const char *inptr = (const char *) inbuf;
861 	      size_t inleft = resultlen;
862 	      char *outptr = (char *) outbuf;
863 	      size_t outleft;
864 
865 	      if (freemem_size < sizeof (size_t))
866 		goto resize_freemem;
867 
868 	      outleft = freemem_size - sizeof (size_t);
869 	      if (iconv (domain->conv,
870 			 (ICONV_CONST char **) &inptr, &inleft,
871 			 &outptr, &outleft)
872 		  != (size_t) (-1))
873 		{
874 		  outbuf = (unsigned char *) outptr;
875 		  break;
876 		}
877 	      if (errno != E2BIG)
878 		{
879 		  __libc_lock_unlock (lock);
880 		  goto converted;
881 		}
882 #  endif
883 # endif
884 
885 	    resize_freemem:
886 	      /* We must allocate a new buffer or resize the old one.  */
887 	      if (malloc_count > 0)
888 		{
889 		  ++malloc_count;
890 		  freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
891 		  newmem = (transmem_block_t *) realloc (transmem_list,
892 							 freemem_size);
893 # ifdef _LIBC
894 		  if (newmem != NULL)
895 		    transmem_list = transmem_list->next;
896 		  else
897 		    {
898 		      struct transmem_list *old = transmem_list;
899 
900 		      transmem_list = transmem_list->next;
901 		      free (old);
902 		    }
903 # endif
904 		}
905 	      else
906 		{
907 		  malloc_count = 1;
908 		  freemem_size = INITIAL_BLOCK_SIZE;
909 		  newmem = (transmem_block_t *) malloc (freemem_size);
910 		}
911 	      if (__builtin_expect (newmem == NULL, 0))
912 		{
913 		  freemem = NULL;
914 		  freemem_size = 0;
915 		  __libc_lock_unlock (lock);
916 		  goto converted;
917 		}
918 
919 # ifdef _LIBC
920 	      /* Add the block to the list of blocks we have to free
921                  at some point.  */
922 	      newmem->next = transmem_list;
923 	      transmem_list = newmem;
924 
925 	      freemem = newmem->data;
926 	      freemem_size -= offsetof (struct transmem_list, data);
927 # else
928 	      transmem_list = newmem;
929 	      freemem = newmem;
930 # endif
931 
932 	      outbuf = freemem + sizeof (size_t);
933 	    }
934 
935 	  /* We have now in our buffer a converted string.  Put this
936 	     into the table of conversions.  */
937 	  *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
938 	  domain->conv_tab[act] = (char *) freemem;
939 	  /* Shrink freemem, but keep it aligned.  */
940 	  freemem_size -= outbuf - freemem;
941 	  freemem = outbuf;
942 	  freemem += freemem_size & (alignof (size_t) - 1);
943 	  freemem_size = freemem_size & ~ (alignof (size_t) - 1);
944 
945 	  __libc_lock_unlock (lock);
946 	}
947 
948       /* Now domain->conv_tab[act] contains the translation of all
949 	 the plural variants.  */
950       result = domain->conv_tab[act] + sizeof (size_t);
951       resultlen = *(size_t *) domain->conv_tab[act];
952     }
953 
954  converted:
955   /* The result string is converted.  */
956 
957 #endif /* _LIBC || HAVE_ICONV */
958 
959   *lengthp = resultlen;
960   return result;
961 }
962 
963 
964 /* Look up a plural variant.  */
965 static char *
966 internal_function
plural_lookup(domain,n,translation,translation_len)967 plural_lookup (domain, n, translation, translation_len)
968      struct loaded_l10nfile *domain;
969      unsigned long int n;
970      const char *translation;
971      size_t translation_len;
972 {
973   struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
974   unsigned long int index;
975   const char *p;
976 
977   index = plural_eval (domaindata->plural, n);
978   if (index >= domaindata->nplurals)
979     /* This should never happen.  It means the plural expression and the
980        given maximum value do not match.  */
981     index = 0;
982 
983   /* Skip INDEX strings at TRANSLATION.  */
984   p = translation;
985   while (index-- > 0)
986     {
987 #ifdef _LIBC
988       p = __rawmemchr (p, '\0');
989 #else
990       p = strchr (p, '\0');
991 #endif
992       /* And skip over the NUL byte.  */
993       p++;
994 
995       if (p >= translation + translation_len)
996 	/* This should never happen.  It means the plural expression
997 	   evaluated to a value larger than the number of variants
998 	   available for MSGID1.  */
999 	return (char *) translation;
1000     }
1001   return (char *) p;
1002 }
1003 
1004 
1005 /* Return string representation of locale CATEGORY.  */
1006 static const char *
1007 internal_function
category_to_name(category)1008 category_to_name (category)
1009      int category;
1010 {
1011   const char *retval;
1012 
1013   switch (category)
1014   {
1015 #ifdef LC_COLLATE
1016   case LC_COLLATE:
1017     retval = "LC_COLLATE";
1018     break;
1019 #endif
1020 #ifdef LC_CTYPE
1021   case LC_CTYPE:
1022     retval = "LC_CTYPE";
1023     break;
1024 #endif
1025 #ifdef LC_MONETARY
1026   case LC_MONETARY:
1027     retval = "LC_MONETARY";
1028     break;
1029 #endif
1030 #ifdef LC_NUMERIC
1031   case LC_NUMERIC:
1032     retval = "LC_NUMERIC";
1033     break;
1034 #endif
1035 #ifdef LC_TIME
1036   case LC_TIME:
1037     retval = "LC_TIME";
1038     break;
1039 #endif
1040 #ifdef LC_MESSAGES
1041   case LC_MESSAGES:
1042     retval = "LC_MESSAGES";
1043     break;
1044 #endif
1045 #ifdef LC_RESPONSE
1046   case LC_RESPONSE:
1047     retval = "LC_RESPONSE";
1048     break;
1049 #endif
1050 #ifdef LC_ALL
1051   case LC_ALL:
1052     /* This might not make sense but is perhaps better than any other
1053        value.  */
1054     retval = "LC_ALL";
1055     break;
1056 #endif
1057   default:
1058     /* If you have a better idea for a default value let me know.  */
1059     retval = "LC_XXX";
1060   }
1061 
1062   return retval;
1063 }
1064 
1065 /* Guess value of current locale from value of the environment variables.  */
1066 static const char *
1067 internal_function
guess_category_value(category,categoryname)1068 guess_category_value (category, categoryname)
1069      int category;
1070      const char *categoryname;
1071 {
1072   const char *language;
1073   const char *retval;
1074 
1075   /* The highest priority value is the `LANGUAGE' environment
1076      variable.  But we don't use the value if the currently selected
1077      locale is the C locale.  This is a GNU extension.  */
1078   language = getenv ("LANGUAGE");
1079   if (language != NULL && language[0] == '\0')
1080     language = NULL;
1081 
1082   /* We have to proceed with the POSIX methods of looking to `LC_ALL',
1083      `LC_xxx', and `LANG'.  On some systems this can be done by the
1084      `setlocale' function itself.  */
1085 #ifdef _LIBC
1086   retval = setlocale (category, NULL);
1087 #else
1088   retval = _nl_locale_name (category, categoryname);
1089 #endif
1090 
1091   /* Ignore LANGUAGE if the locale is set to "C" because
1092      1. "C" locale usually uses the ASCII encoding, and most international
1093 	messages use non-ASCII characters. These characters get displayed
1094 	as question marks (if using glibc's iconv()) or as invalid 8-bit
1095 	characters (because other iconv()s refuse to convert most non-ASCII
1096 	characters to ASCII). In any case, the output is ugly.
1097      2. The precise output of some programs in the "C" locale is specified
1098 	by POSIX and should not depend on environment variables like
1099 	"LANGUAGE".  We allow such programs to use gettext().  */
1100   return language != NULL && strcmp (retval, "C") != 0 ? language : retval;
1101 }
1102 
1103 /* @@ begin of epilog @@ */
1104 
1105 /* We don't want libintl.a to depend on any other library.  So we
1106    avoid the non-standard function stpcpy.  In GNU C Library this
1107    function is available, though.  Also allow the symbol HAVE_STPCPY
1108    to be defined.  */
1109 #if !_LIBC && !HAVE_STPCPY
1110 static char *
stpcpy(dest,src)1111 stpcpy (dest, src)
1112      char *dest;
1113      const char *src;
1114 {
1115   while ((*dest++ = *src++) != '\0')
1116     /* Do nothing. */ ;
1117   return dest - 1;
1118 }
1119 #endif
1120 
1121 #if !_LIBC && !HAVE_MEMPCPY
1122 static void *
mempcpy(dest,src,n)1123 mempcpy (dest, src, n)
1124      void *dest;
1125      const void *src;
1126      size_t n;
1127 {
1128   return (void *) ((char *) memcpy (dest, src, n) + n);
1129 }
1130 #endif
1131 
1132 
1133 #ifdef _LIBC
1134 /* If we want to free all resources we have to do some work at
1135    program's end.  */
1136 static void __attribute__ ((unused))
free_mem(void)1137 free_mem (void)
1138 {
1139   void *old;
1140 
1141   while (_nl_domain_bindings != NULL)
1142     {
1143       struct binding *oldp = _nl_domain_bindings;
1144       _nl_domain_bindings = _nl_domain_bindings->next;
1145       if (oldp->dirname != _nl_default_dirname)
1146 	/* Yes, this is a pointer comparison.  */
1147 	free (oldp->dirname);
1148       free (oldp->codeset);
1149       free (oldp);
1150     }
1151 
1152   if (_nl_current_default_domain != _nl_default_default_domain)
1153     /* Yes, again a pointer comparison.  */
1154     free ((char *) _nl_current_default_domain);
1155 
1156   /* Remove the search tree with the known translations.  */
1157   __tdestroy (root, free);
1158   root = NULL;
1159 
1160   while (transmem_list != NULL)
1161     {
1162       old = transmem_list;
1163       transmem_list = transmem_list->next;
1164       free (old);
1165     }
1166 }
1167 
1168 text_set_element (__libc_subfreeres, free_mem);
1169 #endif
1170