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