]> git.ipfire.org Git - thirdparty/glibc.git/blame - intl/dcigettext.c
Update from translation team.
[thirdparty/glibc.git] / intl / dcigettext.c
CommitLineData
abbffdf9
UD
1/* Implementation of the internal dcigettext function.
2 Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
3
4 This file is part of the GNU C Library. Its master source is NOT part of
5 the C library, however.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with the GNU C Library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
25
26#include <sys/types.h>
27
28#if defined __GNUC__ && !defined C_ALLOCA
29# define alloca __builtin_alloca
30# define HAVE_ALLOCA 1
31#else
32# if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA
33# include <alloca.h>
34# else
35# ifdef _AIX
36 #pragma alloca
37# else
38# ifndef alloca
39char *alloca ();
40# endif
41# endif
42# endif
43#endif
44
45#include <errno.h>
46#ifndef errno
47extern int errno;
48#endif
49#ifndef __set_errno
50# define __set_errno(val) errno = (val)
51#endif
52
53#if defined STDC_HEADERS || defined _LIBC
54# include <stdlib.h>
55#else
56char *getenv ();
57# ifdef HAVE_MALLOC_H
58# include <malloc.h>
59# else
60void free ();
61# endif
62#endif
63
64#if defined HAVE_STRING_H || defined _LIBC
65# ifndef _GNU_SOURCE
66# define _GNU_SOURCE 1
67# endif
68# include <string.h>
69#else
70# include <strings.h>
71#endif
72#if !HAVE_STRCHR && !defined _LIBC
73# ifndef strchr
74# define strchr index
75# endif
76#endif
77
78#if defined HAVE_UNISTD_H || defined _LIBC
79# include <unistd.h>
80#endif
81
82#if defined HAVE_LOCALE_H || defined _LIBC
83# include <locale.h>
84#endif
85
86#if defined HAVE_SYS_PARAM_H || defined _LIBC
87# include <sys/param.h>
88#endif
89
90#include "gettext.h"
91#include "gettextP.h"
92#ifdef _LIBC
93# include <libintl.h>
94#else
95# include "libgettext.h"
96#endif
97#include "hash-string.h"
98
99/* Thread safetyness. */
100#ifdef _LIBC
101# include <bits/libc-lock.h>
102#endif
103
104/* @@ end of prolog @@ */
105
106#ifdef _LIBC
107/* Rename the non ANSI C functions. This is required by the standard
108 because some ANSI C functions will require linking with this object
109 file and the name space must not be polluted. */
110# define getcwd __getcwd
111# ifndef stpcpy
112# define stpcpy __stpcpy
113# endif
114#else
115# if !defined HAVE_GETCWD
116char *getwd ();
117# define getcwd(buf, max) getwd (buf)
118# else
119char *getcwd ();
120# endif
121# ifndef HAVE_STPCPY
122static char *stpcpy PARAMS ((char *dest, const char *src));
123# endif
124# ifndef HAVE_MEMPCPY
125static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
126# endif
127#endif
128
129/* Amount to increase buffer size by in each try. */
130#define PATH_INCR 32
131
132/* The following is from pathmax.h. */
133/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
134 PATH_MAX but might cause redefinition warnings when sys/param.h is
135 later included (as on MORE/BSD 4.3). */
136#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
137# include <limits.h>
138#endif
139
140#ifndef _POSIX_PATH_MAX
141# define _POSIX_PATH_MAX 255
142#endif
143
144#if !defined PATH_MAX && defined _PC_PATH_MAX
145# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
146#endif
147
148/* Don't include sys/param.h if it already has been. */
149#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
150# include <sys/param.h>
151#endif
152
153#if !defined PATH_MAX && defined MAXPATHLEN
154# define PATH_MAX MAXPATHLEN
155#endif
156
157#ifndef PATH_MAX
158# define PATH_MAX _POSIX_PATH_MAX
159#endif
160
161/* XPG3 defines the result of `setlocale (category, NULL)' as:
162 ``Directs `setlocale()' to query `category' and return the current
163 setting of `local'.''
164 However it does not specify the exact format. And even worse: POSIX
165 defines this not at all. So we can use this feature only on selected
166 system (e.g. those using GNU C Library). */
167#ifdef _LIBC
168# define HAVE_LOCALE_NULL
169#endif
170
171/* We want to allocate a string at the end of the struct. gcc makes
172 this easy. */
173#ifdef __GNUC__
174# define ZERO 0
175#else
176# define ZERO 1
177#endif
178
179/* This is the type used for the search tree where known translations
180 are stored. */
181struct known_translation_t
182{
183 /* Domain in which to search. */
184 char *domain;
185
186 /* Plural index. */
187 unsigned long int plindex;
188
189 /* The category. */
190 int category;
191
192 /* State of the catalog counter at the point the string was found. */
193 int counter;
194
195 /* And finally the translation. */
196 const char *translation;
197
198 /* Pointer to the string in question. */
199 char msgid[ZERO];
200};
201
202/* Root of the search tree with known translations. We can use this
203 only if the system provides the `tsearch' function family. */
204#if defined HAVE_TSEARCH || defined _LIBC
205# include <search.h>
206
207static void *root;
208
209# ifdef _LIBC
210# define tsearch __tsearch
211# endif
212
213/* Function to compare two entries in the table of known translations. */
214static int
215transcmp (const void *p1, const void *p2)
216{
217 struct known_translation_t *s1 = (struct known_translation_t *) p1;
218 struct known_translation_t *s2 = (struct known_translation_t *) p2;
219 int result;
220
221 result = strcmp (s1->msgid, s2->msgid);
222 if (result == 0)
223 {
224 result = strcmp (s1->msgid, s2->msgid);
225 if (result == 0)
226 {
227 result = s1->plindex - s2->plindex;
228 if (result == 0)
229 /* We compare the category last (though this is the cheapest
230 operation) since it is hopefully always the same (namely
231 LC_MESSAGES). */
232 result = s1->category - s2->category;
233 }
234 }
235
236 return result;
237}
238#endif
239
240/* Name of the default domain used for gettext(3) prior any call to
241 textdomain(3). The default value for this is "messages". */
242const char _nl_default_default_domain[] = "messages";
243
244/* Value used as the default domain for gettext(3). */
245const char *_nl_current_default_domain = _nl_default_default_domain;
246
247/* Contains the default location of the message catalogs. */
248const char _nl_default_dirname[] = GNULOCALEDIR;
249
250/* List with bindings of specific domains created by bindtextdomain()
251 calls. */
252struct binding *_nl_domain_bindings;
253
254/* Prototypes for local functions. */
255static unsigned long int plural_eval (struct expression *pexp,
256 unsigned long int n) internal_function;
257static const char *category_to_name PARAMS ((int category)) internal_function;
258static const char *guess_category_value PARAMS ((int category,
259 const char *categoryname))
260 internal_function;
261
262
263/* For those loosing systems which don't have `alloca' we have to add
264 some additional code emulating it. */
265#ifdef HAVE_ALLOCA
266/* Nothing has to be done. */
267# define ADD_BLOCK(list, address) /* nothing */
268# define FREE_BLOCKS(list) /* nothing */
269#else
270struct block_list
271{
272 void *address;
273 struct block_list *next;
274};
275# define ADD_BLOCK(list, addr) \
276 do { \
277 struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \
278 /* If we cannot get a free block we cannot add the new element to \
279 the list. */ \
280 if (newp != NULL) { \
281 newp->address = (addr); \
282 newp->next = (list); \
283 (list) = newp; \
284 } \
285 } while (0)
286# define FREE_BLOCKS(list) \
287 do { \
288 while (list != NULL) { \
289 struct block_list *old = list; \
290 list = list->next; \
291 free (old); \
292 } \
293 } while (0)
294# undef alloca
295# define alloca(size) (malloc (size))
296#endif /* have alloca */
297
298
299/* Names for the libintl functions are a problem. They must not clash
300 with existing names and they should follow ANSI C. But this source
301 code is also used in GNU C Library where the names have a __
302 prefix. So we have to make a difference here. */
303#ifdef _LIBC
304# define DCIGETTEXT __dcigettext
305#else
306# define DCIGETTEXT dcigettext__
307#endif
308
309/* Checking whether the binaries runs SUID must be done and glibc provides
310 easier methods therefore we make a difference here. */
311#ifdef _LIBC
312# define ENABLE_SECURE __libc_enable_secure
313# define DETERMINE_SECURE
314#else
315static int enable_secure;
316# define ENABLE_SECURE (enable_secure == 1)
317# define DETERMINE_SECURE \
318 if (enable_secure == 0) \
319 { \
320 if (getuid () != geteuid () || getgid () != getegid ()) \
321 enable_secure = 1; \
322 else \
323 enable_secure = -1; \
324 }
325#endif
326
327/* Look up MSGID in the DOMAINNAME message catalog for the current
328 CATEGORY locale and, if PLURAL is nonzero, search over string
329 depending on the plural form determined by N. */
330char *
331DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
332 const char *domainname;
333 const char *msgid1;
334 const char *msgid2;
335 int plural;
336 unsigned long int n;
337 int category;
338{
339#ifndef HAVE_ALLOCA
340 struct block_list *block_list = NULL;
341#endif
342 struct loaded_l10nfile *domain;
343 struct binding *binding;
344 const char *categoryname;
345 const char *categoryvalue;
346 char *dirname, *xdomainname;
347 char *single_locale;
348 char *retval;
349 int saved_errno;
350#if defined HAVE_TSEARCH || defined _LIBC
351 struct known_translation_t *search;
352 struct known_translation_t **foundp = NULL;
353 size_t msgid_len = strlen (msgid1) + 1;
354#endif
355 size_t domainname_len;
356
357 /* If no real MSGID is given return NULL. */
358 if (msgid1 == NULL)
359 return NULL;
360
361#if defined HAVE_TSEARCH || defined _LIBC
362 if (plural == 0)
363 {
364 /* Try to find the translation among those which we found at
365 some time. */
366 search = (struct known_translation_t *) alloca (sizeof (*search)
367 + msgid_len);
368 memcpy (search->msgid, msgid1, msgid_len);
369 search->domain = (char *) domainname;
370 search->plindex = 0;
371 search->category = category;
372
373 foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
374 if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
375 return (char *) (*foundp)->translation;
376 }
377#endif
378
379 /* Preserve the `errno' value. */
380 saved_errno = errno;
381
382 /* See whether this is a SUID binary or not. */
383 DETERMINE_SECURE;
384
385 /* If DOMAINNAME is NULL, we are interested in the default domain. If
386 CATEGORY is not LC_MESSAGES this might not make much sense but the
387 definition left this undefined. */
388 if (domainname == NULL)
389 domainname = _nl_current_default_domain;
390
391 /* First find matching binding. */
392 for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
393 {
394 int compare = strcmp (domainname, binding->domainname);
395 if (compare == 0)
396 /* We found it! */
397 break;
398 if (compare < 0)
399 {
400 /* It is not in the list. */
401 binding = NULL;
402 break;
403 }
404 }
405
406 if (binding == NULL)
407 dirname = (char *) _nl_default_dirname;
408 else if (binding->dirname[0] == '/')
409 dirname = binding->dirname;
410 else
411 {
412 /* We have a relative path. Make it absolute now. */
413 size_t dirname_len = strlen (binding->dirname) + 1;
414 size_t path_max;
415 char *ret;
416
417 path_max = (unsigned int) PATH_MAX;
418 path_max += 2; /* The getcwd docs say to do this. */
419
420 dirname = (char *) alloca (path_max + dirname_len);
421 ADD_BLOCK (block_list, dirname);
422
423 __set_errno (0);
424 while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
425 {
426 path_max += PATH_INCR;
427 dirname = (char *) alloca (path_max + dirname_len);
428 ADD_BLOCK (block_list, dirname);
429 __set_errno (0);
430 }
431
432 if (ret == NULL)
433 {
434 /* We cannot get the current working directory. Don't signal an
435 error but simply return the default string. */
436 FREE_BLOCKS (block_list);
437 __set_errno (saved_errno);
438 return (plural == 0
439 ? (char *) msgid1
440 /* Use the Germanic plural rule. */
441 : n == 1 ? (char *) msgid1 : (char *) msgid2);
442 }
443
444 stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
445 }
446
447 /* Now determine the symbolic name of CATEGORY and its value. */
448 categoryname = category_to_name (category);
449 categoryvalue = guess_category_value (category, categoryname);
450
451 domainname_len = strlen (domainname);
452 xdomainname = (char *) alloca (strlen (categoryname)
453 + domainname_len + 5);
454 ADD_BLOCK (block_list, xdomainname);
455
456 stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
457 domainname, domainname_len),
458 ".mo");
459
460 /* Creating working area. */
461 single_locale = (char *) alloca (strlen (categoryvalue) + 1);
462 ADD_BLOCK (block_list, single_locale);
463
464
465 /* Search for the given string. This is a loop because we perhaps
466 got an ordered list of languages to consider for the translation. */
467 while (1)
468 {
469 /* Make CATEGORYVALUE point to the next element of the list. */
470 while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
471 ++categoryvalue;
472 if (categoryvalue[0] == '\0')
473 {
474 /* The whole contents of CATEGORYVALUE has been searched but
475 no valid entry has been found. We solve this situation
476 by implicitly appending a "C" entry, i.e. no translation
477 will take place. */
478 single_locale[0] = 'C';
479 single_locale[1] = '\0';
480 }
481 else
482 {
483 char *cp = single_locale;
484 while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
485 *cp++ = *categoryvalue++;
486 *cp = '\0';
487
488 /* When this is a SUID binary we must not allow accessing files
489 outside the dedicated directories. */
490 if (ENABLE_SECURE
491 && (memchr (single_locale, '/',
492 _nl_find_language (single_locale) - single_locale)
493 != NULL))
494 /* Ingore this entry. */
495 continue;
496 }
497
498 /* If the current locale value is C (or POSIX) we don't load a
499 domain. Return the MSGID. */
500 if (strcmp (single_locale, "C") == 0
501 || strcmp (single_locale, "POSIX") == 0)
502 {
503 FREE_BLOCKS (block_list);
504 __set_errno (saved_errno);
505 return (plural == 0
506 ? (char *) msgid1
507 /* Use the Germanic plural rule. */
508 : n == 1 ? (char *) msgid1 : (char *) msgid2);
509 }
510
511
512 /* Find structure describing the message catalog matching the
513 DOMAINNAME and CATEGORY. */
514 domain = _nl_find_domain (dirname, single_locale, xdomainname);
515
516 if (domain != NULL)
517 {
518#if defined HAVE_TSEARCH || defined _LIBC
519 struct loaded_domain *domaindata =
520 (struct loaded_domain *) domain->data;
521 unsigned long int index = 0;
522
523 if (plural != 0)
524 {
525 /* Try to find the translation among those which we
526 found at some time. */
527 search = (struct known_translation_t *) alloca (sizeof (*search)
528 + msgid_len);
529 memcpy (search->msgid, msgid1, msgid_len);
530 search->domain = (char *) domainname;
531 search->plindex = plural_eval (domaindata->plural, n);
532 if (search->plindex >= domaindata->nplurals)
533 /* This should never happen. It means the plural expression
534 and the given maximum value do not match. */
535 search->plindex = 0;
536 index = search->plindex;
537 search->category = category;
538
539 foundp = (struct known_translation_t **) tfind (search, &root,
540 transcmp);
541 if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
542 return (char *) (*foundp)->translation;
543 }
544#endif
545
546 retval = _nl_find_msg (domain, msgid1, index);
547
548 if (retval == NULL)
549 {
550 int cnt;
551
552 for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
553 {
554 retval = _nl_find_msg (domain->successor[cnt], msgid1,
555 index);
556
557 if (retval != NULL)
558 break;
559 }
560 }
561
562 if (retval != NULL)
563 {
564 FREE_BLOCKS (block_list);
565 __set_errno (saved_errno);
566#if defined HAVE_TSEARCH || defined _LIBC
567 if (foundp == NULL)
568 {
569 /* Create a new entry and add it to the search tree. */
570 struct known_translation_t *newp;
571
572 newp = (struct known_translation_t *)
573 malloc (sizeof (*newp) + msgid_len
574 + domainname_len + 1 - ZERO);
575 if (newp != NULL)
576 {
577 newp->domain = mempcpy (newp->msgid, msgid1, msgid_len);
578 memcpy (newp->domain, domainname, domainname_len + 1);
579 newp->plindex = index;
580 newp->category = category;
581 newp->counter = _nl_msg_cat_cntr;
582 newp->translation = retval;
583
584 /* Insert the entry in the search tree. */
585 foundp = (struct known_translation_t **)
586 tsearch (newp, &root, transcmp);
587 if (&newp != foundp)
588 /* The insert failed. */
589 free (newp);
590 }
591 }
592 else
593 {
594 /* We can update the existing entry. */
595 (*foundp)->counter = _nl_msg_cat_cntr;
596 (*foundp)->translation = retval;
597 }
598#endif
599 return retval;
600 }
601 }
602 }
603 /* NOTREACHED */
604}
605
606
607char *
608internal_function
609_nl_find_msg (domain_file, msgid, index)
610 struct loaded_l10nfile *domain_file;
611 const char *msgid;
612 unsigned long int index;
613{
614 size_t act = 0;
615 size_t top, bottom;
616 struct loaded_domain *domain;
617
618 if (domain_file->decided == 0)
619 _nl_load_domain (domain_file);
620
621 if (domain_file->data == NULL)
622 return NULL;
623
624 domain = (struct loaded_domain *) domain_file->data;
625
626 /* Locate the MSGID and its translation. */
627 if (domain->hash_size > 2 && domain->hash_tab != NULL)
628 {
629 /* Use the hashing table. */
630 nls_uint32 len = strlen (msgid);
631 nls_uint32 hash_val = hash_string (msgid);
632 nls_uint32 idx = hash_val % domain->hash_size;
633 nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
634 nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
635
636 if (nstr == 0)
637 /* Hash table entry is empty. */
638 return NULL;
639
640 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
641 && strcmp (msgid,
642 domain->data + W (domain->must_swap,
643 domain->orig_tab[nstr - 1].offset)) == 0)
644 {
645 /* We found an entry. If we have to convert the string to use
646 a different character set this is the time. */
647 char *result =
648 (char *) domain->data + W (domain->must_swap,
649 domain->trans_tab[nstr - 1].offset);
650
651 /* Now skip some strings. How much depends on the index passed
652 in. */
653 while (index-- > 0)
654 {
655#ifdef _LIBC
656 result = __rawmemchr (result, '\0');
657#else
658 result = strchr (result, '\0');
659#endif
660 /* And skip over the NUL byte. */
661 ++result;
662 }
663
664 if (
665#ifdef _LIBC
666 domain->conv != (__gconv_t) -1
667#else
668# if HAVE_ICONV
669 domain->conv != (iconv_t) -1
670# endif
671#endif
672 )
673 {
674 /* We are supposed to do a conversion. First allocate an
675 appropriate table with the same structure as the hash
676 table in the file where we can put the pointers to the
677 converted strings in. */
678 if (domain->conv_tab == NULL
679 && ((domain->conv_tab = (char **) calloc (domain->hash_size,
680 sizeof (char *)))
681 == NULL))
682 /* Mark that we didn't succeed allocating a table. */
683 domain->conv_tab = (char **) -1;
684
685 if (domain->conv_tab == (char **) -1)
686 /* Nothing we can do, no more memory. */
687 return NULL;
688
689 if (domain->conv_tab[idx] == NULL)
690 {
691 /* We haven't used this string so far, so it is not
692 translated yet. Do this now. */
693#ifdef _LIBC
694 /* For glibc we use a bit more efficient memory handling.
695 We allocate always larger blocks which get used over
696 time. This is faster than many small allocations. */
697 __libc_lock_define_initialized (static, lock)
698 static unsigned char *freemem;
699 static size_t freemem_size;
700 /* Note that we include the NUL byte. */
701 size_t resultlen = strlen (result) + 1;
702 const unsigned char *inbuf = result;
703 unsigned char *outbuf = freemem;
704 size_t written;
705 int res;
706
707 __libc_lock_lock (lock);
708
709 while ((res = __gconv (domain->conv,
710 &inbuf, inbuf + resultlen,
711 &outbuf, outbuf + freemem_size,
712 &written)) == __GCONV_OK)
713 {
714 if (res != __GCONV_FULL_OUTPUT)
715 goto out;
716
717 /* We must resize the buffer. */
718 freemem_size = MAX (2 * freemem_size, 4064);
719 freemem = (char *) malloc (freemem_size);
720 if (freemem == NULL)
721 goto out;
722
723 inbuf = result;
724 outbuf = freemem;
725 }
726
727 /* We have now in our buffer a converted string. Put this
728 in the hash table */
729 domain->conv_tab[idx] = freemem;
730 freemem_size -= outbuf - freemem;
731 freemem = outbuf;
732
733 out:
734 __libc_lock_unlock (lock);
735#endif
736 }
737
738 result = domain->conv_tab[idx];
739 }
740
741 return result;
742 }
743
744 while (1)
745 {
746 if (idx >= domain->hash_size - incr)
747 idx -= domain->hash_size - incr;
748 else
749 idx += incr;
750
751 nstr = W (domain->must_swap, domain->hash_tab[idx]);
752 if (nstr == 0)
753 /* Hash table entry is empty. */
754 return NULL;
755
756 if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
757 && (strcmp (msgid,
758 domain->data + W (domain->must_swap,
759 domain->orig_tab[nstr - 1].offset))
760 == 0))
761 return ((char *) domain->data
762 + W (domain->must_swap,
763 domain->trans_tab[nstr - 1].offset));
764 }
765 /* NOTREACHED */
766 }
767
768 /* Now we try the default method: binary search in the sorted
769 array of messages. */
770 bottom = 0;
771 top = domain->nstrings;
772 while (bottom < top)
773 {
774 int cmp_val;
775
776 act = (bottom + top) / 2;
777 cmp_val = strcmp (msgid, (domain->data
778 + W (domain->must_swap,
779 domain->orig_tab[act].offset)));
780 if (cmp_val < 0)
781 top = act;
782 else if (cmp_val > 0)
783 bottom = act + 1;
784 else
785 break;
786 }
787
788 /* If an translation is found return this. */
789 return bottom >= top ? NULL : ((char *) domain->data
790 + W (domain->must_swap,
791 domain->trans_tab[act].offset));
792}
793
794
795/* Function to evaluate the plural expression and return an index value. */
796static unsigned long int
797internal_function
798plural_eval (struct expression *pexp, unsigned long int n)
799{
800 switch (pexp->operation)
801 {
802 case var:
803 return n;
804 case num:
805 return pexp->val.num;
806 case mult:
807 return (plural_eval (pexp->val.args2.left, n)
808 * plural_eval (pexp->val.args2.right, n));
809 case divide:
810 return (plural_eval (pexp->val.args2.left, n)
811 / plural_eval (pexp->val.args2.right, n));
812 case module:
813 return (plural_eval (pexp->val.args2.left, n)
814 % plural_eval (pexp->val.args2.right, n));
815 case plus:
816 return (plural_eval (pexp->val.args2.left, n)
817 + plural_eval (pexp->val.args2.right, n));
818 case minus:
819 return (plural_eval (pexp->val.args2.left, n)
820 - plural_eval (pexp->val.args2.right, n));
821 case equal:
822 return (plural_eval (pexp->val.args2.left, n)
823 == plural_eval (pexp->val.args2.right, n));
824 case not_equal:
825 return (plural_eval (pexp->val.args2.left, n)
826 != plural_eval (pexp->val.args2.right, n));
827 case land:
828 return (plural_eval (pexp->val.args2.left, n)
829 && plural_eval (pexp->val.args2.right, n));
830 case lor:
831 return (plural_eval (pexp->val.args2.left, n)
832 || plural_eval (pexp->val.args2.right, n));
833 case qmop:
834 return (plural_eval (pexp->val.args3.bexp, n)
835 ? plural_eval (pexp->val.args3.tbranch, n)
836 : plural_eval (pexp->val.args3.fbranch, n));
837 }
838 /* NOTREACHED */
839 return 0;
840}
841
842
843/* Return string representation of locale CATEGORY. */
844static const char *
845internal_function
846category_to_name (category)
847 int category;
848{
849 const char *retval;
850
851 switch (category)
852 {
853#ifdef LC_COLLATE
854 case LC_COLLATE:
855 retval = "LC_COLLATE";
856 break;
857#endif
858#ifdef LC_CTYPE
859 case LC_CTYPE:
860 retval = "LC_CTYPE";
861 break;
862#endif
863#ifdef LC_MONETARY
864 case LC_MONETARY:
865 retval = "LC_MONETARY";
866 break;
867#endif
868#ifdef LC_NUMERIC
869 case LC_NUMERIC:
870 retval = "LC_NUMERIC";
871 break;
872#endif
873#ifdef LC_TIME
874 case LC_TIME:
875 retval = "LC_TIME";
876 break;
877#endif
878#ifdef LC_MESSAGES
879 case LC_MESSAGES:
880 retval = "LC_MESSAGES";
881 break;
882#endif
883#ifdef LC_RESPONSE
884 case LC_RESPONSE:
885 retval = "LC_RESPONSE";
886 break;
887#endif
888#ifdef LC_ALL
889 case LC_ALL:
890 /* This might not make sense but is perhaps better than any other
891 value. */
892 retval = "LC_ALL";
893 break;
894#endif
895 default:
896 /* If you have a better idea for a default value let me know. */
897 retval = "LC_XXX";
898 }
899
900 return retval;
901}
902
903/* Guess value of current locale from value of the environment variables. */
904static const char *
905internal_function
906guess_category_value (category, categoryname)
907 int category;
908 const char *categoryname;
909{
910 const char *retval;
911
912 /* The highest priority value is the `LANGUAGE' environment
913 variable. This is a GNU extension. */
914 retval = getenv ("LANGUAGE");
915 if (retval != NULL && retval[0] != '\0')
916 return retval;
917
918 /* `LANGUAGE' is not set. So we have to proceed with the POSIX
919 methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
920 systems this can be done by the `setlocale' function itself. */
921#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
922 return setlocale (category, NULL);
923#else
924 /* Setting of LC_ALL overwrites all other. */
925 retval = getenv ("LC_ALL");
926 if (retval != NULL && retval[0] != '\0')
927 return retval;
928
929 /* Next comes the name of the desired category. */
930 retval = getenv (categoryname);
931 if (retval != NULL && retval[0] != '\0')
932 return retval;
933
934 /* Last possibility is the LANG environment variable. */
935 retval = getenv ("LANG");
936 if (retval != NULL && retval[0] != '\0')
937 return retval;
938
939 /* We use C as the default domain. POSIX says this is implementation
940 defined. */
941 return "C";
942#endif
943}
944
945/* @@ begin of epilog @@ */
946
947/* We don't want libintl.a to depend on any other library. So we
948 avoid the non-standard function stpcpy. In GNU C Library this
949 function is available, though. Also allow the symbol HAVE_STPCPY
950 to be defined. */
951#if !_LIBC && !HAVE_STPCPY
952static char *
953stpcpy (dest, src)
954 char *dest;
955 const char *src;
956{
957 while ((*dest++ = *src++) != '\0')
958 /* Do nothing. */ ;
959 return dest - 1;
960}
961#endif
962
963#if !_LIBC && !HAVE_MEMPCPY
964static void *
965mempcpy (dest, src, n)
966 void *dest;
967 const void *src;
968 size_t n;
969{
970 return (void *) ((char *) memcpy (dst, src, n) + n);
971}
972#endif
973
974
975#ifdef _LIBC
976/* If we want to free all resources we have to do some work at
977 program's end. */
978static void __attribute__ ((unused))
979free_mem (void)
980{
981 struct binding *runp;
982
983 for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
984 {
985 free (runp->domainname);
986 if (runp->dirname != _nl_default_dirname)
987 /* Yes, this is a pointer comparison. */
988 free (runp->dirname);
989 }
990
991 if (_nl_current_default_domain != _nl_default_default_domain)
992 /* Yes, again a pointer comparison. */
993 free ((char *) _nl_current_default_domain);
994
995 /* Remove the search tree with the know translations. */
996 __tdestroy (root, free);
997}
998
999text_set_element (__libc_subfreeres, free_mem);
1000#endif