aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog30
-rw-r--r--NEWS9
-rw-r--r--intl/Makefile30
-rw-r--r--intl/Versions10
-rw-r--r--intl/dcgettext.c835
-rw-r--r--intl/dcigettext.c1000
-rw-r--r--intl/dcngettext.c64
-rw-r--r--intl/dngettext.c67
-rw-r--r--intl/gettext.c8
-rw-r--r--intl/gettextP.h78
-rw-r--r--intl/libintl.h25
-rw-r--r--intl/loadinfo.h4
-rw-r--r--intl/loadmsgcat.c84
-rw-r--r--intl/ngettext.c78
-rw-r--r--intl/plural.c1218
-rw-r--r--intl/plural.y290
-rw-r--r--intl/po2test.sed70
-rw-r--r--intl/tst-gettext.c322
-rwxr-xr-xintl/tst-gettext.sh41
19 files changed, 3416 insertions, 847 deletions
diff --git a/ChangeLog b/ChangeLog
index 8f144c4bb4..861f5ba640 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+2000-01-21 Ulrich Drepper <drepper@cygnus.com>
+
+ * intl/Makefile (routines): Add dcigettext, dcngettext, dngettxt,
+ ngettext, and plural.
+ (distribute): Add plural.y, po2test.sed, and tst-gettext.sh.
+ (test-srcs): Add tst-gettext.
+ (before-compile): Add $(objpfx)msgs.h.
+ Add rules for plural.c and msgs.h generation and running tst-gettext.\
+ * intl/Versions [GLIBC_2.2]: Add __dcngettext, dcngettext, dngettext,
+ and ngettext.
+ * intl/dcgettext.c: Move most code into dcigettext.c. Add call
+ dcigettext with appropriate parameters.
+ * intl/dcigettext.c: New file.
+ * intl/dcngettext.c: New file.
+ * intl/dngettext.c: New file.
+ * intl/ngettext.c: New file.
+ * intl/gettextP.h (struct expression): Define.
+ (struct loaded_domain): Add plural and nplurals members.
+ Add prototypes for new internal functions.
+ * intl/libintl.h: Declare new functions. Add optimizations for them.
+ * intl/loadinfo.h: Add new parameter to _nl_find_msg declaration.
+ * intl/loadmsgcat.c (_nl_load_domain): Search for plural information in
+ header entry and parse and store the expression.
+ * intl/plural.y: New file.
+ * intl/po2test.sed: New file.
+ * intl/tst-gettext.c: New file.
+ * intl/tst-gettext.sh: New file.
+
+ * intl/gettext.c: Call __dcgettext directly.
+
2000-01-20 Ulrich Drepper <drepper@cygnus.com>
* manual/getopt.texi (Using the getopt function): Fix description of
diff --git a/NEWS b/NEWS
index 01e473ba65..90dc236734 100644
--- a/NEWS
+++ b/NEWS
@@ -15,7 +15,7 @@ Version 2.2
* Wide character I/O streams implemented by Ulrich Drepper.
-* functions from the extended socket API added by Ulrich Drepper.
+* Functions from the extended socket API added by Ulrich Drepper.
* Functions feenableexcept and fedisableexcept to control the
behaviour of individual exceptions have been added by Andreas Jaeger.
@@ -30,6 +30,13 @@ Version 2.2
header files. A ISO C compiler is needed to use the library
(conforming to either C89 or C99 standard).
+* Complete rewrite of the localedef program to support multibyte character
+ sets. Implement handling of ISO 14651 and ISO 14652. Rewrite strcoll,
+ strxfrm, wcscoll, and wcsxfrm functions. Make isw*() functions work.
+ Implemented by Ulrich Drepper.
+
+* Plural handling in gettext implemented by Ulrich Drepper.
+
Version 2.1.3
diff --git a/intl/Makefile b/intl/Makefile
index 4712179290..21c73e7566 100644
--- a/intl/Makefile
+++ b/intl/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+# Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
# The GNU C Library is free software; you can redistribute it and/or
@@ -21,16 +21,40 @@
subdir = intl
headers = libintl.h
routines = bindtextdom dcgettext dgettext gettext \
+ dcigettext dcngettext dngettext ngettext \
finddomain loadmsgcat localealias textdomain \
- l10nflist explodename
-distribute = gettext.h gettextP.h hash-string.h loadinfo.h locale.alias
+ l10nflist explodename plural
+distribute = gettext.h gettextP.h hash-string.h loadinfo.h locale.alias \
+ plural.y po2test.sed tst-gettext.sh
+
+test-srcs := tst-gettext
+
+before-compile = $(objpfx)msgs.h
install-others = $(inst_msgcatdir)/locale.alias
+plural.c: plural.y
+ $(YACC) $(YFLAGS) $@ $^
+ifeq ($(with-cvs),yes)
+ test ! -d CVS || cvs $(CVSOPTS) commit -m'$(YACC) $(YFLAGS) $@ $^' $@
+endif
+$(objpfx)plural.o: plural.c
+
include ../Rules
+.PHONY: do-gettext-test
+tests: do-gettext-test
+do-gettext-test: tst-gettext.sh $(objpfx)tst-gettext
+ $(SHELL) -e $< $(common-objpfx) $(objpfx)
+
+$(objpfx)msgs.h: po2test.sed ../po/de.po
+ sed -f $^ > $@
+
+CFLAGS-tst-gettext.c = -DTESTSTRS_H=\"$(objpfx)msgs.h\"
+
CPPFLAGS += -D'GNULOCALEDIR="$(msgcatdir)"' \
-D'LOCALE_ALIAS_PATH="$(msgcatdir):$(i18ndir)"'
+YFLAGS = --name-prefix=__gettext --output
$(inst_msgcatdir)/locale.alias: locale.alias $(+force)
$(do-install)
diff --git a/intl/Versions b/intl/Versions
index acf0ce0ed9..c784f67a66 100644
--- a/intl/Versions
+++ b/intl/Versions
@@ -18,4 +18,14 @@ libc {
# t*
textdomain;
}
+ GLIBC_2.2 {
+ # functions used in inline functions or macros
+ __dcngettext;
+
+ # d*
+ dcngettext; dngettext;
+
+ # n*
+ ngettext;
+ }
}
diff --git a/intl/dcgettext.c b/intl/dcgettext.c
index 8e51970495..5be8e4ff70 100644
--- a/intl/dcgettext.c
+++ b/intl/dcgettext.c
@@ -1,5 +1,5 @@
/* Implementation of the dcgettext(3) function.
- Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+ Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
@@ -23,70 +23,6 @@
# include <config.h>
#endif
-#include <sys/types.h>
-
-#if defined __GNUC__ && !defined C_ALLOCA
-# define alloca __builtin_alloca
-# define HAVE_ALLOCA 1
-#else
-# if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA
-# include <alloca.h>
-# else
-# ifdef _AIX
- #pragma alloca
-# else
-# ifndef alloca
-char *alloca ();
-# endif
-# endif
-# endif
-#endif
-
-#include <errno.h>
-#ifndef errno
-extern int errno;
-#endif
-#ifndef __set_errno
-# define __set_errno(val) errno = (val)
-#endif
-
-#if defined STDC_HEADERS || defined _LIBC
-# include <stdlib.h>
-#else
-char *getenv ();
-# ifdef HAVE_MALLOC_H
-# include <malloc.h>
-# else
-void free ();
-# endif
-#endif
-
-#if defined HAVE_STRING_H || defined _LIBC
-# ifndef _GNU_SOURCE
-# define _GNU_SOURCE 1
-# endif
-# include <string.h>
-#else
-# include <strings.h>
-#endif
-#if !HAVE_STRCHR && !defined _LIBC
-# ifndef strchr
-# define strchr index
-# endif
-#endif
-
-#if defined HAVE_UNISTD_H || defined _LIBC
-# include <unistd.h>
-#endif
-
-#if defined HAVE_LOCALE_H || defined _LIBC
-# include <locale.h>
-#endif
-
-#if defined HAVE_SYS_PARAM_H || defined _LIBC
-# include <sys/param.h>
-#endif
-
#include "gettext.h"
#include "gettextP.h"
#ifdef _LIBC
@@ -94,225 +30,19 @@ void free ();
#else
# include "libgettext.h"
#endif
-#include "hash-string.h"
-
-/* Thread safetyness. */
-#ifdef _LIBC
-# include <bits/libc-lock.h>
-#endif
/* @@ end of prolog @@ */
-#ifdef _LIBC
-/* Rename the non ANSI C functions. This is required by the standard
- because some ANSI C functions will require linking with this object
- file and the name space must not be polluted. */
-# define getcwd __getcwd
-# ifndef stpcpy
-# define stpcpy __stpcpy
-# endif
-#else
-# if !defined HAVE_GETCWD
-char *getwd ();
-# define getcwd(buf, max) getwd (buf)
-# else
-char *getcwd ();
-# endif
-# ifndef HAVE_STPCPY
-static char *stpcpy PARAMS ((char *dest, const char *src));
-# endif
-# ifndef HAVE_MEMPCPY
-static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
-# endif
-#endif
-
-/* Amount to increase buffer size by in each try. */
-#define PATH_INCR 32
-
-/* The following is from pathmax.h. */
-/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
- PATH_MAX but might cause redefinition warnings when sys/param.h is
- later included (as on MORE/BSD 4.3). */
-#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
-# include <limits.h>
-#endif
-
-#ifndef _POSIX_PATH_MAX
-# define _POSIX_PATH_MAX 255
-#endif
-
-#if !defined PATH_MAX && defined _PC_PATH_MAX
-# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
-#endif
-
-/* Don't include sys/param.h if it already has been. */
-#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
-# include <sys/param.h>
-#endif
-
-#if !defined PATH_MAX && defined MAXPATHLEN
-# define PATH_MAX MAXPATHLEN
-#endif
-
-#ifndef PATH_MAX
-# define PATH_MAX _POSIX_PATH_MAX
-#endif
-
-/* XPG3 defines the result of `setlocale (category, NULL)' as:
- ``Directs `setlocale()' to query `category' and return the current
- setting of `local'.''
- However it does not specify the exact format. And even worse: POSIX
- defines this not at all. So we can use this feature only on selected
- system (e.g. those using GNU C Library). */
-#ifdef _LIBC
-# define HAVE_LOCALE_NULL
-#endif
-
-/* We want to allocate a string at the end of the struct. gcc makes
- this easy. */
-#ifdef __GNUC__
-# define ZERO 0
-#else
-# define ZERO 1
-#endif
-
-/* This is the type used for the search tree where known translations
- are stored. */
-struct known_translation_t
-{
- /* Domain in which to search. */
- char *domain;
-
- /* The category. */
- int category;
-
- /* State of the catalog counter at the point the string was found. */
- int counter;
-
- /* And finally the translation. */
- const char *translation;
-
- /* Pointer to the string in question. */
- char msgid[ZERO];
-};
-
-/* Root of the search tree with known translations. We can use this
- only if the system provides the `tsearch' function family. */
-#if defined HAVE_TSEARCH || defined _LIBC
-# include <search.h>
-
-static void *root;
-
-# ifdef _LIBC
-# define tsearch __tsearch
-# endif
-
-/* Function to compare two entries in the table of known translations. */
-static int
-transcmp (const void *p1, const void *p2)
-{
- struct known_translation_t *s1 = (struct known_translation_t *) p1;
- struct known_translation_t *s2 = (struct known_translation_t *) p2;
- int result;
-
- result = strcmp (s1->msgid, s2->msgid);
- if (result == 0)
- {
- result = strcmp (s1->msgid, s2->msgid);
- if (result == 0)
- /* We compare the category last (though this is the cheapest
- operation) since it is hopefully always the same (namely
- LC_MESSAGES). */
- result = s1->category - s2->category;
- }
-
- return result;
-}
-#endif
-
-/* Name of the default domain used for gettext(3) prior any call to
- textdomain(3). The default value for this is "messages". */
-const char _nl_default_default_domain[] = "messages";
-
-/* Value used as the default domain for gettext(3). */
-const char *_nl_current_default_domain = _nl_default_default_domain;
-
-/* Contains the default location of the message catalogs. */
-const char _nl_default_dirname[] = GNULOCALEDIR;
-
-/* List with bindings of specific domains created by bindtextdomain()
- calls. */
-struct binding *_nl_domain_bindings;
-
-/* Prototypes for local functions. */
-static const char *category_to_name PARAMS ((int category)) internal_function;
-static const char *guess_category_value PARAMS ((int category,
- const char *categoryname))
- internal_function;
-
-
-/* For those loosing systems which don't have `alloca' we have to add
- some additional code emulating it. */
-#ifdef HAVE_ALLOCA
-/* Nothing has to be done. */
-# define ADD_BLOCK(list, address) /* nothing */
-# define FREE_BLOCKS(list) /* nothing */
-#else
-struct block_list
-{
- void *address;
- struct block_list *next;
-};
-# define ADD_BLOCK(list, addr) \
- do { \
- struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \
- /* If we cannot get a free block we cannot add the new element to \
- the list. */ \
- if (newp != NULL) { \
- newp->address = (addr); \
- newp->next = (list); \
- (list) = newp; \
- } \
- } while (0)
-# define FREE_BLOCKS(list) \
- do { \
- while (list != NULL) { \
- struct block_list *old = list; \
- list = list->next; \
- free (old); \
- } \
- } while (0)
-# undef alloca
-# define alloca(size) (malloc (size))
-#endif /* have alloca */
-
-
/* Names for the libintl functions are a problem. They must not clash
with existing names and they should follow ANSI C. But this source
code is also used in GNU C Library where the names have a __
prefix. So we have to make a difference here. */
#ifdef _LIBC
# define DCGETTEXT __dcgettext
+# define DCIGETTEXT __dcigettext
#else
# define DCGETTEXT dcgettext__
-#endif
-
-/* Checking whether the binaries runs SUID must be done and glibc provides
- easier methods therefore we make a difference here. */
-#ifdef _LIBC
-# define ENABLE_SECURE __libc_enable_secure
-# define DETERMINE_SECURE
-#else
-static int enable_secure;
-# define ENABLE_SECURE (enable_secure == 1)
-# define DETERMINE_SECURE \
- if (enable_secure == 0) \
- { \
- if (getuid () != geteuid () || getgid () != getegid ()) \
- enable_secure = 1; \
- else \
- enable_secure = -1; \
- }
+# define DCIGETTEXT dcigettext__
#endif
/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
@@ -323,567 +53,10 @@ DCGETTEXT (domainname, msgid, category)
const char *msgid;
int category;
{
-#ifndef HAVE_ALLOCA
- struct block_list *block_list = NULL;
-#endif
- struct loaded_l10nfile *domain;
- struct binding *binding;
- const char *categoryname;
- const char *categoryvalue;
- char *dirname, *xdomainname;
- char *single_locale;
- char *retval;
- int saved_errno;
-#if defined HAVE_TSEARCH || defined _LIBC
- struct known_translation_t *search;
- struct known_translation_t **foundp;
- size_t msgid_len = strlen (msgid) + 1;
-#endif
- size_t domainname_len;
-
- /* If no real MSGID is given return NULL. */
- if (msgid == NULL)
- return NULL;
-
-#if defined HAVE_TSEARCH || defined _LIBC
- /* Try to find the translation among those which we found at some time. */
- search = (struct known_translation_t *) alloca (sizeof (*search)
- + msgid_len);
- memcpy (search->msgid, msgid, msgid_len);
- search->domain = (char *) domainname;
- search->category = category;
-
- foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
- if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
- return (char *) (*foundp)->translation;
-#endif
-
- /* Preserve the `errno' value. */
- saved_errno = errno;
-
- /* See whether this is a SUID binary or not. */
- DETERMINE_SECURE;
-
- /* If DOMAINNAME is NULL, we are interested in the default domain. If
- CATEGORY is not LC_MESSAGES this might not make much sense but the
- definition left this undefined. */
- if (domainname == NULL)
- domainname = _nl_current_default_domain;
-
- /* First find matching binding. */
- for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
- {
- int compare = strcmp (domainname, binding->domainname);
- if (compare == 0)
- /* We found it! */
- break;
- if (compare < 0)
- {
- /* It is not in the list. */
- binding = NULL;
- break;
- }
- }
-
- if (binding == NULL)
- dirname = (char *) _nl_default_dirname;
- else if (binding->dirname[0] == '/')
- dirname = binding->dirname;
- else
- {
- /* We have a relative path. Make it absolute now. */
- size_t dirname_len = strlen (binding->dirname) + 1;
- size_t path_max;
- char *ret;
-
- path_max = (unsigned int) PATH_MAX;
- path_max += 2; /* The getcwd docs say to do this. */
-
- dirname = (char *) alloca (path_max + dirname_len);
- ADD_BLOCK (block_list, dirname);
-
- __set_errno (0);
- while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
- {
- path_max += PATH_INCR;
- dirname = (char *) alloca (path_max + dirname_len);
- ADD_BLOCK (block_list, dirname);
- __set_errno (0);
- }
-
- if (ret == NULL)
- {
- /* We cannot get the current working directory. Don't signal an
- error but simply return the default string. */
- FREE_BLOCKS (block_list);
- __set_errno (saved_errno);
- return (char *) msgid;
- }
-
- stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
- }
-
- /* Now determine the symbolic name of CATEGORY and its value. */
- categoryname = category_to_name (category);
- categoryvalue = guess_category_value (category, categoryname);
-
- domainname_len = strlen (domainname);
- xdomainname = (char *) alloca (strlen (categoryname)
- + domainname_len + 5);
- ADD_BLOCK (block_list, xdomainname);
-
- stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
- domainname, domainname_len),
- ".mo");
-
- /* Creating working area. */
- single_locale = (char *) alloca (strlen (categoryvalue) + 1);
- ADD_BLOCK (block_list, single_locale);
-
-
- /* Search for the given string. This is a loop because we perhaps
- got an ordered list of languages to consider for the translation. */
- while (1)
- {
- /* Make CATEGORYVALUE point to the next element of the list. */
- while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
- ++categoryvalue;
- if (categoryvalue[0] == '\0')
- {
- /* The whole contents of CATEGORYVALUE has been searched but
- no valid entry has been found. We solve this situation
- by implicitly appending a "C" entry, i.e. no translation
- will take place. */
- single_locale[0] = 'C';
- single_locale[1] = '\0';
- }
- else
- {
- char *cp = single_locale;
- while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
- *cp++ = *categoryvalue++;
- *cp = '\0';
-
- /* When this is a SUID binary we must not allow accessing files
- outside the dedicated directories. */
- if (ENABLE_SECURE
- && (memchr (single_locale, '/',
- _nl_find_language (single_locale) - single_locale)
- != NULL))
- /* Ingore this entry. */
- continue;
- }
-
- /* If the current locale value is C (or POSIX) we don't load a
- domain. Return the MSGID. */
- if (strcmp (single_locale, "C") == 0
- || strcmp (single_locale, "POSIX") == 0)
- {
- FREE_BLOCKS (block_list);
- __set_errno (saved_errno);
- return (char *) msgid;
- }
-
-
- /* Find structure describing the message catalog matching the
- DOMAINNAME and CATEGORY. */
- domain = _nl_find_domain (dirname, single_locale, xdomainname);
-
- if (domain != NULL)
- {
- retval = _nl_find_msg (domain, msgid);
-
- if (retval == NULL)
- {
- int cnt;
-
- for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
- {
- retval = _nl_find_msg (domain->successor[cnt], msgid);
-
- if (retval != NULL)
- break;
- }
- }
-
- if (retval != NULL)
- {
- FREE_BLOCKS (block_list);
- __set_errno (saved_errno);
-#if defined HAVE_TSEARCH || defined _LIBC
- if (foundp == NULL)
- {
- /* Create a new entry and add it to the search tree. */
- struct known_translation_t *newp;
-
- newp = (struct known_translation_t *)
- malloc (sizeof (*newp) + msgid_len
- + domainname_len + 1 - ZERO);
- if (newp != NULL)
- {
- newp->domain = mempcpy (newp->msgid, msgid, msgid_len);
- memcpy (newp->domain, domainname, domainname_len + 1);
- newp->category = category;
- newp->counter = _nl_msg_cat_cntr;
- newp->translation = retval;
-
- /* Insert the entry in the search tree. */
- foundp = (struct known_translation_t **)
- tsearch (newp, &root, transcmp);
- if (&newp != foundp)
- /* The insert failed. */
- free (newp);
- }
- }
- else
- {
- /* We can update the existing entry. */
- (*foundp)->counter = _nl_msg_cat_cntr;
- (*foundp)->translation = retval;
- }
-#endif
- return retval;
- }
- }
- }
- /* NOTREACHED */
+ return DCIGETTEXT (domainname, msgid, NULL, 0, 0, category);
}
#ifdef _LIBC
/* Alias for function name in GNU C Library. */
weak_alias (__dcgettext, dcgettext);
#endif
-
-
-char *
-internal_function
-_nl_find_msg (domain_file, msgid)
- struct loaded_l10nfile *domain_file;
- const char *msgid;
-{
- size_t act = 0;
- size_t top, bottom;
- struct loaded_domain *domain;
-
- if (domain_file->decided == 0)
- _nl_load_domain (domain_file);
-
- if (domain_file->data == NULL)
- return NULL;
-
- domain = (struct loaded_domain *) domain_file->data;
-
- /* Locate the MSGID and its translation. */
- if (domain->hash_size > 2 && domain->hash_tab != NULL)
- {
- /* Use the hashing table. */
- nls_uint32 len = strlen (msgid);
- nls_uint32 hash_val = hash_string (msgid);
- nls_uint32 idx = hash_val % domain->hash_size;
- nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
- nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
-
- if (nstr == 0)
- /* Hash table entry is empty. */
- return NULL;
-
- if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
- && strcmp (msgid,
- domain->data + W (domain->must_swap,
- domain->orig_tab[nstr - 1].offset)) == 0)
- {
- /* We found an entry. If we have to convert the string to use
- a different character set this is the time. */
- char *result =
- (char *) domain->data + W (domain->must_swap,
- domain->trans_tab[nstr - 1].offset);
-
- if (
-#ifdef _LIBC
- domain->conv != (__gconv_t) -1
-#else
-# if HAVE_ICONV
- domain->conv != (iconv_t) -1
-# endif
-#endif
- )
- {
- /* We are supposed to do a conversion. First allocate an
- appropriate table with the same structure as the hash
- table in the file where we can put the pointers to the
- converted strings in. */
- if (domain->conv_tab == NULL
- && ((domain->conv_tab = (char **) calloc (domain->hash_size,
- sizeof (char *)))
- == NULL))
- /* Mark that we didn't succeed allocating a table. */
- domain->conv_tab = (char **) -1;
-
- if (domain->conv_tab == (char **) -1)
- /* Nothing we can do, no more memory. */
- return NULL;
-
- if (domain->conv_tab[idx] == NULL)
- {
- /* We haven't used this string so far, so it is not
- translated yet. Do this now. */
-#ifdef _LIBC
- /* For glibc we use a bit more efficient memory handling.
- We allocate always larger blocks which get used over
- time. This is faster than many small allocations. */
- __libc_lock_define_initialized (static, lock)
- static unsigned char *freemem;
- static size_t freemem_size;
- /* Note that we include the NUL byte. */
- size_t resultlen = strlen (result) + 1;
- const unsigned char *inbuf = result;
- unsigned char *outbuf = freemem;
- size_t written;
- int res;
-
- __libc_lock_lock (lock);
-
- while ((res = __gconv (domain->conv,
- &inbuf, inbuf + resultlen,
- &outbuf, outbuf + freemem_size,
- &written)) == __GCONV_OK)
- {
- if (res != __GCONV_FULL_OUTPUT)
- goto out;
-
- /* We must resize the buffer. */
- freemem_size = MAX (2 * freemem_size, 4064);
- freemem = (char *) malloc (freemem_size);
- if (freemem == NULL)
- goto out;
-
- inbuf = result;
- outbuf = freemem;
- }
-
- /* We have now in our buffer a converted string. Put this
- in the hash table */
- domain->conv_tab[idx] = freemem;
- freemem_size -= outbuf - freemem;
- freemem = outbuf;
-
- out:
- __libc_lock_unlock (lock);
-#endif
- }
-
- result = domain->conv_tab[idx];
- }
-
- return result;
- }
-
- while (1)
- {
- if (idx >= domain->hash_size - incr)
- idx -= domain->hash_size - incr;
- else
- idx += incr;
-
- nstr = W (domain->must_swap, domain->hash_tab[idx]);
- if (nstr == 0)
- /* Hash table entry is empty. */
- return NULL;
-
- if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
- && (strcmp (msgid,
- domain->data + W (domain->must_swap,
- domain->orig_tab[nstr - 1].offset))
- == 0))
- return ((char *) domain->data
- + W (domain->must_swap,
- domain->trans_tab[nstr - 1].offset));
- }
- /* NOTREACHED */
- }
-
- /* Now we try the default method: binary search in the sorted
- array of messages. */
- bottom = 0;
- top = domain->nstrings;
- while (bottom < top)
- {
- int cmp_val;
-
- act = (bottom + top) / 2;
- cmp_val = strcmp (msgid, (domain->data
- + W (domain->must_swap,
- domain->orig_tab[act].offset)));
- if (cmp_val < 0)
- top = act;
- else if (cmp_val > 0)
- bottom = act + 1;
- else
- break;
- }
-
- /* If an translation is found return this. */
- return bottom >= top ? NULL : ((char *) domain->data
- + W (domain->must_swap,
- domain->trans_tab[act].offset));
-}
-
-
-/* Return string representation of locale CATEGORY. */
-static const char *
-internal_function
-category_to_name (category)
- int category;
-{
- const char *retval;
-
- switch (category)
- {
-#ifdef LC_COLLATE
- case LC_COLLATE:
- retval = "LC_COLLATE";
- break;
-#endif
-#ifdef LC_CTYPE
- case LC_CTYPE:
- retval = "LC_CTYPE";
- break;
-#endif
-#ifdef LC_MONETARY
- case LC_MONETARY:
- retval = "LC_MONETARY";
- break;
-#endif
-#ifdef LC_NUMERIC
- case LC_NUMERIC:
- retval = "LC_NUMERIC";
- break;
-#endif
-#ifdef LC_TIME
- case LC_TIME:
- retval = "LC_TIME";
- break;
-#endif
-#ifdef LC_MESSAGES
- case LC_MESSAGES:
- retval = "LC_MESSAGES";
- break;
-#endif
-#ifdef LC_RESPONSE
- case LC_RESPONSE:
- retval = "LC_RESPONSE";
- break;
-#endif
-#ifdef LC_ALL
- case LC_ALL:
- /* This might not make sense but is perhaps better than any other
- value. */
- retval = "LC_ALL";
- break;
-#endif
- default:
- /* If you have a better idea for a default value let me know. */
- retval = "LC_XXX";
- }
-
- return retval;
-}
-
-/* Guess value of current locale from value of the environment variables. */
-static const char *
-internal_function
-guess_category_value (category, categoryname)
- int category;
- const char *categoryname;
-{
- const char *retval;
-
- /* The highest priority value is the `LANGUAGE' environment
- variable. This is a GNU extension. */
- retval = getenv ("LANGUAGE");
- if (retval != NULL && retval[0] != '\0')
- return retval;
-
- /* `LANGUAGE' is not set. So we have to proceed with the POSIX
- methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
- systems this can be done by the `setlocale' function itself. */
-#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
- return setlocale (category, NULL);
-#else
- /* Setting of LC_ALL overwrites all other. */
- retval = getenv ("LC_ALL");
- if (retval != NULL && retval[0] != '\0')
- return retval;
-
- /* Next comes the name of the desired category. */
- retval = getenv (categoryname);
- if (retval != NULL && retval[0] != '\0')
- return retval;
-
- /* Last possibility is the LANG environment variable. */
- retval = getenv ("LANG");
- if (retval != NULL && retval[0] != '\0')
- return retval;
-
- /* We use C as the default domain. POSIX says this is implementation
- defined. */
- return "C";
-#endif
-}
-
-/* @@ begin of epilog @@ */
-
-/* We don't want libintl.a to depend on any other library. So we
- avoid the non-standard function stpcpy. In GNU C Library this
- function is available, though. Also allow the symbol HAVE_STPCPY
- to be defined. */
-#if !_LIBC && !HAVE_STPCPY
-static char *
-stpcpy (dest, src)
- char *dest;
- const char *src;
-{
- while ((*dest++ = *src++) != '\0')
- /* Do nothing. */ ;
- return dest - 1;
-}
-#endif
-
-#if !_LIBC && !HAVE_MEMPCPY
-static void *
-mempcpy (dest, src, n)
- void *dest;
- const void *src;
- size_t n;
-{
- return (void *) ((char *) memcpy (dst, src, n) + n);
-}
-#endif
-
-
-#ifdef _LIBC
-/* If we want to free all resources we have to do some work at
- program's end. */
-static void __attribute__ ((unused))
-free_mem (void)
-{
- struct binding *runp;
-
- for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
- {
- free (runp->domainname);
- if (runp->dirname != _nl_default_dirname)
- /* Yes, this is a pointer comparison. */
- free (runp->dirname);
- }
-
- if (_nl_current_default_domain != _nl_default_default_domain)
- /* Yes, again a pointer comparison. */
- free ((char *) _nl_current_default_domain);
-
- /* Remove the search tree with the know translations. */
- __tdestroy (root, free);
-}
-
-text_set_element (__libc_subfreeres, free_mem);
-#endif
diff --git a/intl/dcigettext.c b/intl/dcigettext.c
new file mode 100644
index 0000000000..e11fd06147
--- /dev/null
+++ b/intl/dcigettext.c
@@ -0,0 +1,1000 @@
+/* Implementation of the internal dcigettext function.
+ Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#if defined __GNUC__ && !defined C_ALLOCA
+# define alloca __builtin_alloca
+# define HAVE_ALLOCA 1
+#else
+# if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca
+char *alloca ();
+# endif
+# endif
+# endif
+#endif
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#if defined STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+#else
+char *getenv ();
+# ifdef HAVE_MALLOC_H
+# include <malloc.h>
+# else
+void free ();
+# endif
+#endif
+
+#if defined HAVE_STRING_H || defined _LIBC
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+# endif
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+#if !HAVE_STRCHR && !defined _LIBC
+# ifndef strchr
+# define strchr index
+# endif
+#endif
+
+#if defined HAVE_UNISTD_H || defined _LIBC
+# include <unistd.h>
+#endif
+
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+
+#if defined HAVE_SYS_PARAM_H || defined _LIBC
+# include <sys/param.h>
+#endif
+
+#include "gettext.h"
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgettext.h"
+#endif
+#include "hash-string.h"
+
+/* Thread safetyness. */
+#ifdef _LIBC
+# include <bits/libc-lock.h>
+#endif
+
+/* @@ end of prolog @@ */
+
+#ifdef _LIBC
+/* Rename the non ANSI C functions. This is required by the standard
+ because some ANSI C functions will require linking with this object
+ file and the name space must not be polluted. */
+# define getcwd __getcwd
+# ifndef stpcpy
+# define stpcpy __stpcpy
+# endif
+#else
+# if !defined HAVE_GETCWD
+char *getwd ();
+# define getcwd(buf, max) getwd (buf)
+# else
+char *getcwd ();
+# endif
+# ifndef HAVE_STPCPY
+static char *stpcpy PARAMS ((char *dest, const char *src));
+# endif
+# ifndef HAVE_MEMPCPY
+static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
+# endif
+#endif
+
+/* Amount to increase buffer size by in each try. */
+#define PATH_INCR 32
+
+/* The following is from pathmax.h. */
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+ PATH_MAX but might cause redefinition warnings when sys/param.h is
+ later included (as on MORE/BSD 4.3). */
+#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
+# include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+# define _POSIX_PATH_MAX 255
+#endif
+
+#if !defined PATH_MAX && defined _PC_PATH_MAX
+# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
+#endif
+
+/* Don't include sys/param.h if it already has been. */
+#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
+# include <sys/param.h>
+#endif
+
+#if !defined PATH_MAX && defined MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+/* XPG3 defines the result of `setlocale (category, NULL)' as:
+ ``Directs `setlocale()' to query `category' and return the current
+ setting of `local'.''
+ However it does not specify the exact format. And even worse: POSIX
+ defines this not at all. So we can use this feature only on selected
+ system (e.g. those using GNU C Library). */
+#ifdef _LIBC
+# define HAVE_LOCALE_NULL
+#endif
+
+/* We want to allocate a string at the end of the struct. gcc makes
+ this easy. */
+#ifdef __GNUC__
+# define ZERO 0
+#else
+# define ZERO 1
+#endif
+
+/* This is the type used for the search tree where known translations
+ are stored. */
+struct known_translation_t
+{
+ /* Domain in which to search. */
+ char *domain;
+
+ /* Plural index. */
+ unsigned long int plindex;
+
+ /* The category. */
+ int category;
+
+ /* State of the catalog counter at the point the string was found. */
+ int counter;
+
+ /* And finally the translation. */
+ const char *translation;
+
+ /* Pointer to the string in question. */
+ char msgid[ZERO];
+};
+
+/* Root of the search tree with known translations. We can use this
+ only if the system provides the `tsearch' function family. */
+#if defined HAVE_TSEARCH || defined _LIBC
+# include <search.h>
+
+static void *root;
+
+# ifdef _LIBC
+# define tsearch __tsearch
+# endif
+
+/* Function to compare two entries in the table of known translations. */
+static int
+transcmp (const void *p1, const void *p2)
+{
+ struct known_translation_t *s1 = (struct known_translation_t *) p1;
+ struct known_translation_t *s2 = (struct known_translation_t *) p2;
+ int result;
+
+ result = strcmp (s1->msgid, s2->msgid);
+ if (result == 0)
+ {
+ result = strcmp (s1->msgid, s2->msgid);
+ if (result == 0)
+ {
+ result = s1->plindex - s2->plindex;
+ if (result == 0)
+ /* We compare the category last (though this is the cheapest
+ operation) since it is hopefully always the same (namely
+ LC_MESSAGES). */
+ result = s1->category - s2->category;
+ }
+ }
+
+ return result;
+}
+#endif
+
+/* Name of the default domain used for gettext(3) prior any call to
+ textdomain(3). The default value for this is "messages". */
+const char _nl_default_default_domain[] = "messages";
+
+/* Value used as the default domain for gettext(3). */
+const char *_nl_current_default_domain = _nl_default_default_domain;
+
+/* Contains the default location of the message catalogs. */
+const char _nl_default_dirname[] = GNULOCALEDIR;
+
+/* List with bindings of specific domains created by bindtextdomain()
+ calls. */
+struct binding *_nl_domain_bindings;
+
+/* Prototypes for local functions. */
+static unsigned long int plural_eval (struct expression *pexp,
+ unsigned long int n) internal_function;
+static const char *category_to_name PARAMS ((int category)) internal_function;
+static const char *guess_category_value PARAMS ((int category,
+ const char *categoryname))
+ internal_function;
+
+
+/* For those loosing systems which don't have `alloca' we have to add
+ some additional code emulating it. */
+#ifdef HAVE_ALLOCA
+/* Nothing has to be done. */
+# define ADD_BLOCK(list, address) /* nothing */
+# define FREE_BLOCKS(list) /* nothing */
+#else
+struct block_list
+{
+ void *address;
+ struct block_list *next;
+};
+# define ADD_BLOCK(list, addr) \
+ do { \
+ struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \
+ /* If we cannot get a free block we cannot add the new element to \
+ the list. */ \
+ if (newp != NULL) { \
+ newp->address = (addr); \
+ newp->next = (list); \
+ (list) = newp; \
+ } \
+ } while (0)
+# define FREE_BLOCKS(list) \
+ do { \
+ while (list != NULL) { \
+ struct block_list *old = list; \
+ list = list->next; \
+ free (old); \
+ } \
+ } while (0)
+# undef alloca
+# define alloca(size) (malloc (size))
+#endif /* have alloca */
+
+
+/* Names for the libintl functions are a problem. They must not clash
+ with existing names and they should follow ANSI C. But this source
+ code is also used in GNU C Library where the names have a __
+ prefix. So we have to make a difference here. */
+#ifdef _LIBC
+# define DCIGETTEXT __dcigettext
+#else
+# define DCIGETTEXT dcigettext__
+#endif
+
+/* Checking whether the binaries runs SUID must be done and glibc provides
+ easier methods therefore we make a difference here. */
+#ifdef _LIBC
+# define ENABLE_SECURE __libc_enable_secure
+# define DETERMINE_SECURE
+#else
+static int enable_secure;
+# define ENABLE_SECURE (enable_secure == 1)
+# define DETERMINE_SECURE \
+ if (enable_secure == 0) \
+ { \
+ if (getuid () != geteuid () || getgid () != getegid ()) \
+ enable_secure = 1; \
+ else \
+ enable_secure = -1; \
+ }
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current
+ CATEGORY locale and, if PLURAL is nonzero, search over string
+ depending on the plural form determined by N. */
+char *
+DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
+ const char *domainname;
+ const char *msgid1;
+ const char *msgid2;
+ int plural;
+ unsigned long int n;
+ int category;
+{
+#ifndef HAVE_ALLOCA
+ struct block_list *block_list = NULL;
+#endif
+ struct loaded_l10nfile *domain;
+ struct binding *binding;
+ const char *categoryname;
+ const char *categoryvalue;
+ char *dirname, *xdomainname;
+ char *single_locale;
+ char *retval;
+ int saved_errno;
+#if defined HAVE_TSEARCH || defined _LIBC
+ struct known_translation_t *search;
+ struct known_translation_t **foundp = NULL;
+ size_t msgid_len = strlen (msgid1) + 1;
+#endif
+ size_t domainname_len;
+
+ /* If no real MSGID is given return NULL. */
+ if (msgid1 == NULL)
+ return NULL;
+
+#if defined HAVE_TSEARCH || defined _LIBC
+ if (plural == 0)
+ {
+ /* Try to find the translation among those which we found at
+ some time. */
+ search = (struct known_translation_t *) alloca (sizeof (*search)
+ + msgid_len);
+ memcpy (search->msgid, msgid1, msgid_len);
+ search->domain = (char *) domainname;
+ search->plindex = 0;
+ search->category = category;
+
+ foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
+ if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
+ return (char *) (*foundp)->translation;
+ }
+#endif
+
+ /* Preserve the `errno' value. */
+ saved_errno = errno;
+
+ /* See whether this is a SUID binary or not. */
+ DETERMINE_SECURE;
+
+ /* If DOMAINNAME is NULL, we are interested in the default domain. If
+ CATEGORY is not LC_MESSAGES this might not make much sense but the
+ definition left this undefined. */
+ if (domainname == NULL)
+ domainname = _nl_current_default_domain;
+
+ /* First find matching binding. */
+ for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
+ {
+ int compare = strcmp (domainname, binding->domainname);
+ if (compare == 0)
+ /* We found it! */
+ break;
+ if (compare < 0)
+ {
+ /* It is not in the list. */
+ binding = NULL;
+ break;
+ }
+ }
+
+ if (binding == NULL)
+ dirname = (char *) _nl_default_dirname;
+ else if (binding->dirname[0] == '/')
+ dirname = binding->dirname;
+ else
+ {
+ /* We have a relative path. Make it absolute now. */
+ size_t dirname_len = strlen (binding->dirname) + 1;
+ size_t path_max;
+ char *ret;
+
+ path_max = (unsigned int) PATH_MAX;
+ path_max += 2; /* The getcwd docs say to do this. */
+
+ dirname = (char *) alloca (path_max + dirname_len);
+ ADD_BLOCK (block_list, dirname);
+
+ __set_errno (0);
+ while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
+ {
+ path_max += PATH_INCR;
+ dirname = (char *) alloca (path_max + dirname_len);
+ ADD_BLOCK (block_list, dirname);
+ __set_errno (0);
+ }
+
+ if (ret == NULL)
+ {
+ /* We cannot get the current working directory. Don't signal an
+ error but simply return the default string. */
+ FREE_BLOCKS (block_list);
+ __set_errno (saved_errno);
+ return (plural == 0
+ ? (char *) msgid1
+ /* Use the Germanic plural rule. */
+ : n == 1 ? (char *) msgid1 : (char *) msgid2);
+ }
+
+ stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
+ }
+
+ /* Now determine the symbolic name of CATEGORY and its value. */
+ categoryname = category_to_name (category);
+ categoryvalue = guess_category_value (category, categoryname);
+
+ domainname_len = strlen (domainname);
+ xdomainname = (char *) alloca (strlen (categoryname)
+ + domainname_len + 5);
+ ADD_BLOCK (block_list, xdomainname);
+
+ stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
+ domainname, domainname_len),
+ ".mo");
+
+ /* Creating working area. */
+ single_locale = (char *) alloca (strlen (categoryvalue) + 1);
+ ADD_BLOCK (block_list, single_locale);
+
+
+ /* Search for the given string. This is a loop because we perhaps
+ got an ordered list of languages to consider for the translation. */
+ while (1)
+ {
+ /* Make CATEGORYVALUE point to the next element of the list. */
+ while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
+ ++categoryvalue;
+ if (categoryvalue[0] == '\0')
+ {
+ /* The whole contents of CATEGORYVALUE has been searched but
+ no valid entry has been found. We solve this situation
+ by implicitly appending a "C" entry, i.e. no translation
+ will take place. */
+ single_locale[0] = 'C';
+ single_locale[1] = '\0';
+ }
+ else
+ {
+ char *cp = single_locale;
+ while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
+ *cp++ = *categoryvalue++;
+ *cp = '\0';
+
+ /* When this is a SUID binary we must not allow accessing files
+ outside the dedicated directories. */
+ if (ENABLE_SECURE
+ && (memchr (single_locale, '/',
+ _nl_find_language (single_locale) - single_locale)
+ != NULL))
+ /* Ingore this entry. */
+ continue;
+ }
+
+ /* If the current locale value is C (or POSIX) we don't load a
+ domain. Return the MSGID. */
+ if (strcmp (single_locale, "C") == 0
+ || strcmp (single_locale, "POSIX") == 0)
+ {
+ FREE_BLOCKS (block_list);
+ __set_errno (saved_errno);
+ return (plural == 0
+ ? (char *) msgid1
+ /* Use the Germanic plural rule. */
+ : n == 1 ? (char *) msgid1 : (char *) msgid2);
+ }
+
+
+ /* Find structure describing the message catalog matching the
+ DOMAINNAME and CATEGORY. */
+ domain = _nl_find_domain (dirname, single_locale, xdomainname);
+
+ if (domain != NULL)
+ {
+#if defined HAVE_TSEARCH || defined _LIBC
+ struct loaded_domain *domaindata =
+ (struct loaded_domain *) domain->data;
+ unsigned long int index = 0;
+
+ if (plural != 0)
+ {
+ /* Try to find the translation among those which we
+ found at some time. */
+ search = (struct known_translation_t *) alloca (sizeof (*search)
+ + msgid_len);
+ memcpy (search->msgid, msgid1, msgid_len);
+ search->domain = (char *) domainname;
+ search->plindex = plural_eval (domaindata->plural, n);
+ if (search->plindex >= domaindata->nplurals)
+ /* This should never happen. It means the plural expression
+ and the given maximum value do not match. */
+ search->plindex = 0;
+ index = search->plindex;
+ search->category = category;
+
+ foundp = (struct known_translation_t **) tfind (search, &root,
+ transcmp);
+ if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
+ return (char *) (*foundp)->translation;
+ }
+#endif
+
+ retval = _nl_find_msg (domain, msgid1, index);
+
+ if (retval == NULL)
+ {
+ int cnt;
+
+ for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
+ {
+ retval = _nl_find_msg (domain->successor[cnt], msgid1,
+ index);
+
+ if (retval != NULL)
+ break;
+ }
+ }
+
+ if (retval != NULL)
+ {
+ FREE_BLOCKS (block_list);
+ __set_errno (saved_errno);
+#if defined HAVE_TSEARCH || defined _LIBC
+ if (foundp == NULL)
+ {
+ /* Create a new entry and add it to the search tree. */
+ struct known_translation_t *newp;
+
+ newp = (struct known_translation_t *)
+ malloc (sizeof (*newp) + msgid_len
+ + domainname_len + 1 - ZERO);
+ if (newp != NULL)
+ {
+ newp->domain = mempcpy (newp->msgid, msgid1, msgid_len);
+ memcpy (newp->domain, domainname, domainname_len + 1);
+ newp->plindex = index;
+ newp->category = category;
+ newp->counter = _nl_msg_cat_cntr;
+ newp->translation = retval;
+
+ /* Insert the entry in the search tree. */
+ foundp = (struct known_translation_t **)
+ tsearch (newp, &root, transcmp);
+ if (&newp != foundp)
+ /* The insert failed. */
+ free (newp);
+ }
+ }
+ else
+ {
+ /* We can update the existing entry. */
+ (*foundp)->counter = _nl_msg_cat_cntr;
+ (*foundp)->translation = retval;
+ }
+#endif
+ return retval;
+ }
+ }
+ }
+ /* NOTREACHED */
+}
+
+
+char *
+internal_function
+_nl_find_msg (domain_file, msgid, index)
+ struct loaded_l10nfile *domain_file;
+ const char *msgid;
+ unsigned long int index;
+{
+ size_t act = 0;
+ size_t top, bottom;
+ struct loaded_domain *domain;
+
+ if (domain_file->decided == 0)
+ _nl_load_domain (domain_file);
+
+ if (domain_file->data == NULL)
+ return NULL;
+
+ domain = (struct loaded_domain *) domain_file->data;
+
+ /* Locate the MSGID and its translation. */
+ if (domain->hash_size > 2 && domain->hash_tab != NULL)
+ {
+ /* Use the hashing table. */
+ nls_uint32 len = strlen (msgid);
+ nls_uint32 hash_val = hash_string (msgid);
+ nls_uint32 idx = hash_val % domain->hash_size;
+ nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
+ nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
+
+ if (nstr == 0)
+ /* Hash table entry is empty. */
+ return NULL;
+
+ if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
+ && strcmp (msgid,
+ domain->data + W (domain->must_swap,
+ domain->orig_tab[nstr - 1].offset)) == 0)
+ {
+ /* We found an entry. If we have to convert the string to use
+ a different character set this is the time. */
+ char *result =
+ (char *) domain->data + W (domain->must_swap,
+ domain->trans_tab[nstr - 1].offset);
+
+ /* Now skip some strings. How much depends on the index passed
+ in. */
+ while (index-- > 0)
+ {
+#ifdef _LIBC
+ result = __rawmemchr (result, '\0');
+#else
+ result = strchr (result, '\0');
+#endif
+ /* And skip over the NUL byte. */
+ ++result;
+ }
+
+ if (
+#ifdef _LIBC
+ domain->conv != (__gconv_t) -1
+#else
+# if HAVE_ICONV
+ domain->conv != (iconv_t) -1
+# endif
+#endif
+ )
+ {
+ /* We are supposed to do a conversion. First allocate an
+ appropriate table with the same structure as the hash
+ table in the file where we can put the pointers to the
+ converted strings in. */
+ if (domain->conv_tab == NULL
+ && ((domain->conv_tab = (char **) calloc (domain->hash_size,
+ sizeof (char *)))
+ == NULL))
+ /* Mark that we didn't succeed allocating a table. */
+ domain->conv_tab = (char **) -1;
+
+ if (domain->conv_tab == (char **) -1)
+ /* Nothing we can do, no more memory. */
+ return NULL;
+
+ if (domain->conv_tab[idx] == NULL)
+ {
+ /* We haven't used this string so far, so it is not
+ translated yet. Do this now. */
+#ifdef _LIBC
+ /* For glibc we use a bit more efficient memory handling.
+ We allocate always larger blocks which get used over
+ time. This is faster than many small allocations. */
+ __libc_lock_define_initialized (static, lock)
+ static unsigned char *freemem;
+ static size_t freemem_size;
+ /* Note that we include the NUL byte. */
+ size_t resultlen = strlen (result) + 1;
+ const unsigned char *inbuf = result;
+ unsigned char *outbuf = freemem;
+ size_t written;
+ int res;
+
+ __libc_lock_lock (lock);
+
+ while ((res = __gconv (domain->conv,
+ &inbuf, inbuf + resultlen,
+ &outbuf, outbuf + freemem_size,
+ &written)) == __GCONV_OK)
+ {
+ if (res != __GCONV_FULL_OUTPUT)
+ goto out;
+
+ /* We must resize the buffer. */
+ freemem_size = MAX (2 * freemem_size, 4064);
+ freemem = (char *) malloc (freemem_size);
+ if (freemem == NULL)
+ goto out;
+
+ inbuf = result;
+ outbuf = freemem;
+ }
+
+ /* We have now in our buffer a converted string. Put this
+ in the hash table */
+ domain->conv_tab[idx] = freemem;
+ freemem_size -= outbuf - freemem;
+ freemem = outbuf;
+
+ out:
+ __libc_lock_unlock (lock);
+#endif
+ }
+
+ result = domain->conv_tab[idx];
+ }
+
+ return result;
+ }
+
+ while (1)
+ {
+ if (idx >= domain->hash_size - incr)
+ idx -= domain->hash_size - incr;
+ else
+ idx += incr;
+
+ nstr = W (domain->must_swap, domain->hash_tab[idx]);
+ if (nstr == 0)
+ /* Hash table entry is empty. */
+ return NULL;
+
+ if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
+ && (strcmp (msgid,
+ domain->data + W (domain->must_swap,
+ domain->orig_tab[nstr - 1].offset))
+ == 0))
+ return ((char *) domain->data
+ + W (domain->must_swap,
+ domain->trans_tab[nstr - 1].offset));
+ }
+ /* NOTREACHED */
+ }
+
+ /* Now we try the default method: binary search in the sorted
+ array of messages. */
+ bottom = 0;
+ top = domain->nstrings;
+ while (bottom < top)
+ {
+ int cmp_val;
+
+ act = (bottom + top) / 2;
+ cmp_val = strcmp (msgid, (domain->data
+ + W (domain->must_swap,
+ domain->orig_tab[act].offset)));
+ if (cmp_val < 0)
+ top = act;
+ else if (cmp_val > 0)
+ bottom = act + 1;
+ else
+ break;
+ }
+
+ /* If an translation is found return this. */
+ return bottom >= top ? NULL : ((char *) domain->data
+ + W (domain->must_swap,
+ domain->trans_tab[act].offset));
+}
+
+
+/* Function to evaluate the plural expression and return an index value. */
+static unsigned long int
+internal_function
+plural_eval (struct expression *pexp, unsigned long int n)
+{
+ switch (pexp->operation)
+ {
+ case var:
+ return n;
+ case num:
+ return pexp->val.num;
+ case mult:
+ return (plural_eval (pexp->val.args2.left, n)
+ * plural_eval (pexp->val.args2.right, n));
+ case divide:
+ return (plural_eval (pexp->val.args2.left, n)
+ / plural_eval (pexp->val.args2.right, n));
+ case module:
+ return (plural_eval (pexp->val.args2.left, n)
+ % plural_eval (pexp->val.args2.right, n));
+ case plus:
+ return (plural_eval (pexp->val.args2.left, n)
+ + plural_eval (pexp->val.args2.right, n));
+ case minus:
+ return (plural_eval (pexp->val.args2.left, n)
+ - plural_eval (pexp->val.args2.right, n));
+ case equal:
+ return (plural_eval (pexp->val.args2.left, n)
+ == plural_eval (pexp->val.args2.right, n));
+ case not_equal:
+ return (plural_eval (pexp->val.args2.left, n)
+ != plural_eval (pexp->val.args2.right, n));
+ case land:
+ return (plural_eval (pexp->val.args2.left, n)
+ && plural_eval (pexp->val.args2.right, n));
+ case lor:
+ return (plural_eval (pexp->val.args2.left, n)
+ || plural_eval (pexp->val.args2.right, n));
+ case qmop:
+ return (plural_eval (pexp->val.args3.bexp, n)
+ ? plural_eval (pexp->val.args3.tbranch, n)
+ : plural_eval (pexp->val.args3.fbranch, n));
+ }
+ /* NOTREACHED */
+ return 0;
+}
+
+
+/* Return string representation of locale CATEGORY. */
+static const char *
+internal_function
+category_to_name (category)
+ int category;
+{
+ const char *retval;
+
+ switch (category)
+ {
+#ifdef LC_COLLATE
+ case LC_COLLATE:
+ retval = "LC_COLLATE";
+ break;
+#endif
+#ifdef LC_CTYPE
+ case LC_CTYPE:
+ retval = "LC_CTYPE";
+ break;
+#endif
+#ifdef LC_MONETARY
+ case LC_MONETARY:
+ retval = "LC_MONETARY";
+ break;
+#endif
+#ifdef LC_NUMERIC
+ case LC_NUMERIC:
+ retval = "LC_NUMERIC";
+ break;
+#endif
+#ifdef LC_TIME
+ case LC_TIME:
+ retval = "LC_TIME";
+ break;
+#endif
+#ifdef LC_MESSAGES
+ case LC_MESSAGES:
+ retval = "LC_MESSAGES";
+ break;
+#endif
+#ifdef LC_RESPONSE
+ case LC_RESPONSE:
+ retval = "LC_RESPONSE";
+ break;
+#endif
+#ifdef LC_ALL
+ case LC_ALL:
+ /* This might not make sense but is perhaps better than any other
+ value. */
+ retval = "LC_ALL";
+ break;
+#endif
+ default:
+ /* If you have a better idea for a default value let me know. */
+ retval = "LC_XXX";
+ }
+
+ return retval;
+}
+
+/* Guess value of current locale from value of the environment variables. */
+static const char *
+internal_function
+guess_category_value (category, categoryname)
+ int category;
+ const char *categoryname;
+{
+ const char *retval;
+
+ /* The highest priority value is the `LANGUAGE' environment
+ variable. This is a GNU extension. */
+ retval = getenv ("LANGUAGE");
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ /* `LANGUAGE' is not set. So we have to proceed with the POSIX
+ methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
+ systems this can be done by the `setlocale' function itself. */
+#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
+ return setlocale (category, NULL);
+#else
+ /* Setting of LC_ALL overwrites all other. */
+ retval = getenv ("LC_ALL");
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ /* Next comes the name of the desired category. */
+ retval = getenv (categoryname);
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ /* Last possibility is the LANG environment variable. */
+ retval = getenv ("LANG");
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ /* We use C as the default domain. POSIX says this is implementation
+ defined. */
+ return "C";
+#endif
+}
+
+/* @@ begin of epilog @@ */
+
+/* We don't want libintl.a to depend on any other library. So we
+ avoid the non-standard function stpcpy. In GNU C Library this
+ function is available, though. Also allow the symbol HAVE_STPCPY
+ to be defined. */
+#if !_LIBC && !HAVE_STPCPY
+static char *
+stpcpy (dest, src)
+ char *dest;
+ const char *src;
+{
+ while ((*dest++ = *src++) != '\0')
+ /* Do nothing. */ ;
+ return dest - 1;
+}
+#endif
+
+#if !_LIBC && !HAVE_MEMPCPY
+static void *
+mempcpy (dest, src, n)
+ void *dest;
+ const void *src;
+ size_t n;
+{
+ return (void *) ((char *) memcpy (dst, src, n) + n);
+}
+#endif
+
+
+#ifdef _LIBC
+/* If we want to free all resources we have to do some work at
+ program's end. */
+static void __attribute__ ((unused))
+free_mem (void)
+{
+ struct binding *runp;
+
+ for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
+ {
+ free (runp->domainname);
+ if (runp->dirname != _nl_default_dirname)
+ /* Yes, this is a pointer comparison. */
+ free (runp->dirname);
+ }
+
+ if (_nl_current_default_domain != _nl_default_default_domain)
+ /* Yes, again a pointer comparison. */
+ free ((char *) _nl_current_default_domain);
+
+ /* Remove the search tree with the know translations. */
+ __tdestroy (root, free);
+}
+
+text_set_element (__libc_subfreeres, free_mem);
+#endif
diff --git a/intl/dcngettext.c b/intl/dcngettext.c
new file mode 100644
index 0000000000..ef1dc349b6
--- /dev/null
+++ b/intl/dcngettext.c
@@ -0,0 +1,64 @@
+/* Implementation of the dcngettext(3) function.
+ Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "gettext.h"
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgettext.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem. They must not clash
+ with existing names and they should follow ANSI C. But this source
+ code is also used in GNU C Library where the names have a __
+ prefix. So we have to make a difference here. */
+#ifdef _LIBC
+# define DCNGETTEXT __dcngettext
+# define DCIGETTEXT __dcigettext
+#else
+# define DCNGETTEXT dcngettext__
+# define DCIGETTEXT dcigettext__
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
+ locale. */
+char *
+DCNGETTEXT (domainname, msgid1, msgid2, n, category)
+ const char *domainname;
+ const char *msgid1;
+ const char *msgid2;
+ unsigned long int n;
+ int category;
+{
+ return DCIGETTEXT (domainname, msgid1, msgid2, 1, n, category);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library. */
+weak_alias (__dcngettext, dcngettext);
+#endif
diff --git a/intl/dngettext.c b/intl/dngettext.c
new file mode 100644
index 0000000000..2fb861c6e4
--- /dev/null
+++ b/intl/dngettext.c
@@ -0,0 +1,67 @@
+/* Implementation of the dngettext(3) function.
+ Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+
+#include "gettext.h"
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgettext.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem. They must not clash
+ with existing names and they should follow ANSI C. But this source
+ code is also used in GNU C Library where the names have a __
+ prefix. So we have to make a difference here. */
+#ifdef _LIBC
+# define DNGETTEXT __dngettext
+# define DCNGETTEXT __dcngettext
+#else
+# define DNGETTEXT dngettext__
+# define DCNGETTEXT dcngettext__
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog of the current
+ LC_MESSAGES locale and skip message according to the plural form. */
+char *
+DNGETTEXT (domainname, msgid1, msgid2, n)
+ const char *domainname;
+ const char *msgid1;
+ const char *msgid2;
+ unsigned long int n;
+{
+ return DCNGETTEXT (domainname, msgid1, msgid2, n, LC_MESSAGES);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library. */
+weak_alias (__dngettext, dngettext);
+#endif
diff --git a/intl/gettext.c b/intl/gettext.c
index d4687ceb24..74eab14419 100644
--- a/intl/gettext.c
+++ b/intl/gettext.c
@@ -1,5 +1,5 @@
/* Implementation of gettext(3) function.
- Copyright (C) 1995, 1997 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1997, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
@@ -52,10 +52,10 @@
prefix. So we have to make a difference here. */
#ifdef _LIBC
# define GETTEXT __gettext
-# define DGETTEXT __dgettext
+# define DCGETTEXT __dcgettext
#else
# define GETTEXT gettext__
-# define DGETTEXT dgettext__
+# define DCGETTEXT dcgettext__
#endif
/* Look up MSGID in the current default message catalog for the current
@@ -65,7 +65,7 @@ char *
GETTEXT (msgid)
const char *msgid;
{
- return DGETTEXT (NULL, msgid);
+ return DCGETTEXT (NULL, msgid, LC_MESSAGES);
}
#ifdef _LIBC
diff --git a/intl/gettextP.h b/intl/gettextP.h
index b3c8c18598..d210dc6c4e 100644
--- a/intl/gettextP.h
+++ b/intl/gettextP.h
@@ -1,5 +1,5 @@
/* Header describing internals of gettext library
- Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+ Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
Written by Ulrich Drepper <drepper@cygnus.com>, 1995.
The GNU C Library is free software; you can redistribute it and/or
@@ -64,6 +64,51 @@ SWAP (i)
#endif
+/* This is the representation of the expressions to determine the
+ plural form. */
+struct expression
+{
+ enum operator
+ {
+ var, /* The variable "n". */
+ num, /* Decimal number. */
+ mult, /* Multiplication. */
+ divide, /* Division. */
+ module, /* Module operation. */
+ plus, /* Addition. */
+ minus, /* Subtraction. */
+ equal, /* Comparision for equality. */
+ not_equal, /* Comparision for inequality. */
+ land, /* Logical AND. */
+ lor, /* Logical OR. */
+ qmop /* Question mark operator. */
+ } operation;
+ union
+ {
+ unsigned long int num; /* Number value for `num'. */
+ struct
+ {
+ struct expression *left; /* Left expression in binary operation. */
+ struct expression *right; /* Right expression in binary operation. */
+ } args2;
+ struct
+ {
+ struct expression *bexp; /* Boolean expression in ?: operation. */
+ struct expression *tbranch; /* True-branch in ?: operation. */
+ struct expression *fbranch; /* False-branch in ?: operation. */
+ } args3;
+ } val;
+};
+
+/* This is the data structure to pass information to the parser and get
+ the result in a thread-safe way. */
+struct parse_args
+{
+ const char *cp;
+ struct expression *res;
+};
+
+
struct loaded_domain
{
const char *data;
@@ -83,6 +128,9 @@ struct loaded_domain
# endif
#endif
char **conv_tab;
+
+ struct expression *plural;
+ unsigned long int nplurals;
};
struct binding
@@ -103,6 +151,34 @@ void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain))
void _nl_unload_domain PARAMS ((struct loaded_domain *__domain))
internal_function;
+#ifdef _LIBC
+extern char *__ngettext PARAMS ((const char *msgid1, const char *msgid2,
+ unsigned long int n));
+extern char *__dngettext PARAMS ((const char *domainname, const char *msgid1,
+ const char *msgid2, unsigned long int n));
+extern char *__dcngettext PARAMS ((const char *domainname, const char *msgid1,
+ const char *msgid2, unsigned long int n,
+ int category));
+extern char *__dcigettext PARAMS ((const char *domainname, const char *msgid1,
+ const char *msgid2, int plural,
+ unsigned long int n, int category));
+#else
+extern char *ngettext__ PARAMS ((const char *msgid1, const char *msgid2,
+ unsigned long int n));
+extern char *dngettext__ PARAMS ((const char *domainname, const char *msgid1,
+ const char *msgid2, unsigned long int n));
+extern char *dcngettext__ PARAMS ((const char *domainname, const char *msgid1,
+ const char *msgid2, unsigned long int n,
+ int category));
+extern char *dcigettext__ PARAMS ((const char *domainname, const char *msgid1,
+ const char *msgid2, int plural,
+ unsigned long int n, int category));
+#endif
+
+extern int __gettextdebug;
+extern void __gettext_free_exp (struct expression *exp) internal_function;
+extern int __gettextparse (void *arg);
+
/* @@ begin of epilog @@ */
#endif /* gettextP.h */
diff --git a/intl/libintl.h b/intl/libintl.h
index 10c3726b7c..5ac716b75c 100644
--- a/intl/libintl.h
+++ b/intl/libintl.h
@@ -1,6 +1,5 @@
/* Message catalogs for internationalization.
- Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
- Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
+ Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
This file is derived from the file libgettext.h in the GNU gettext package.
This file is part of the GNU C Library. Its master source is NOT part of
@@ -52,6 +51,23 @@ extern char *__dcgettext (__const char *__domainname,
__const char *__msgid, int __category) __THROW;
+/* Similar to `gettext' but select the plural form corresponding to the
+ number N. */
+extern char *ngettext (__const char *__msgid1, __const char *__msgid2,
+ unsigned long int __n) __THROW;
+
+/* Similar to `dgettext' but select the plural form corresponding to the
+ number N. */
+extern char *dngettext (__const char *__domainname, __const char *__msgid1,
+ __const char *__msgid2, unsigned long int __n) __THROW;
+
+/* Similar to `dxgettext' but select the plural form corresponding to the
+ number N. */
+extern char *dcngettext (__const char *__domainname, __const char *__msgid1,
+ __const char *__msgid2, unsigned long int __n,
+ int __category) __THROW;
+
+
/* Set the current default message catalog to DOMAINNAME.
If DOMAINNAME is null, return the current default.
If DOMAINNAME is "", reset to the default of "messages". */
@@ -82,6 +98,11 @@ extern char *bindtextdomain (__const char *__domainname,
# define dgettext(domainname, msgid) \
dcgettext (domainname, msgid, LC_MESSAGES)
+# define ngettext(msgid, n) dngettext (NULL, msgid, n)
+
+# define dngettext(domainname, msgid, n) \
+ dcngettext (domainname, msgid, n, LC_MESSAGES)
+
#endif /* Optimizing. */
__END_DECLS
diff --git a/intl/loadinfo.h b/intl/loadinfo.h
index 09b2fdf6ac..009abcc620 100644
--- a/intl/loadinfo.h
+++ b/intl/loadinfo.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
@@ -89,7 +89,7 @@ extern char *_nl_find_language PARAMS ((const char *name));
extern char *_nl_find_msg PARAMS ((struct loaded_l10nfile *domain_file,
- const char *msgid))
+ const char *msgid, unsigned long int index))
internal_function;
#endif /* loadinfo.h */
diff --git a/intl/loadmsgcat.c b/intl/loadmsgcat.c
index ba818ce3b1..b017d710e8 100644
--- a/intl/loadmsgcat.c
+++ b/intl/loadmsgcat.c
@@ -1,5 +1,5 @@
/* Load needed message catalogs.
- Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+ Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
@@ -23,6 +23,7 @@
# include <config.h>
#endif
+#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -82,6 +83,32 @@
cached by one of GCC's features. */
int _nl_msg_cat_cntr;
+/* These structs are the constant expression for the germanic plural
+ form determination. */
+static const struct expression plvar =
+{
+ .operation = var,
+};
+static const struct expression plone =
+{
+ .operation = num,
+ .val =
+ {
+ .num = 1
+ }
+};
+static struct expression germanic_plural =
+{
+ .operation = not_equal,
+ .val =
+ {
+ .args2 = {
+ .left = (struct expression *) &plvar,
+ .right = (struct expression *) &plone
+ }
+ }
+};
+
/* Load the message catalogs specified by FILENAME. If it is no valid
message catalog do nothing. */
@@ -230,10 +257,12 @@ _nl_load_domain (domain_file)
domain->conv = (iconv_t) -1;
# endif
#endif
- nullentry = _nl_find_msg (domain_file, "");
+ nullentry = _nl_find_msg (domain_file, "", 0);
if (nullentry != NULL)
{
- char *charsetstr = strstr (nullentry, "charset=");
+ const char *charsetstr = strstr (nullentry, "charset=");
+ const char *plural;
+ const char *nplurals;
if (charsetstr != NULL)
{
@@ -270,6 +299,42 @@ _nl_load_domain (domain_file)
# endif
#endif
}
+
+ /* Also look for a plural specification. */
+ plural = strstr (nullentry, "plural=");
+ nplurals = strstr (nullentry, "nplurals=");
+ if (plural == NULL || nplurals == NULL)
+ {
+ /* By default we are using the Germanic form: singular form only
+ for `one', the plural form otherwise. Yes, this is also what
+ English is using since English is a Germanic language. */
+ no_plural:
+ domain->plural = &germanic_plural;
+ domain->nplurals = 2;
+ }
+ else
+ {
+ /* First get the number. */
+ char *endp;
+ struct parse_args args;
+
+ nplurals += 9;
+ while (*nplurals != '\0' && isspace (*nplurals))
+ ++nplurals;
+ domain->nplurals = strtoul (nplurals, &endp, 10);
+ if (nplurals == endp)
+ goto no_plural;
+
+ /* Due to the restrictions bison imposes onto the interface of the
+ scanner function we have to put the input string and the result
+ passed up from the parser into the same structure which address
+ is passed down to the parser. */
+ plural += 7;
+ args.cp = plural;
+ if (__gettextparse (&args) != 0)
+ goto no_plural;
+ domain->plural = args.res;
+ }
}
}
@@ -280,6 +345,19 @@ internal_function
_nl_unload_domain (domain)
struct loaded_domain *domain;
{
+ if (domain->plural != &germanic_plural)
+ __gettext_free_exp (domain->plural);
+
+#ifdef _LIBC
+ if (domain->conv != (__gconv_t) -1)
+ __gconv_close (domain->conv);
+#else
+# if HAVE_ICONV
+ if (domain->conv != (iconv_t) -1)
+ iconv_close (domain->conv);
+# endif
+#endif
+
#ifdef _POSIX_MAPPED_FILES
if (domain->use_mmap)
munmap ((caddr_t) domain->data, domain->mmap_size);
diff --git a/intl/ngettext.c b/intl/ngettext.c
new file mode 100644
index 0000000000..99e74d9938
--- /dev/null
+++ b/intl/ngettext.c
@@ -0,0 +1,78 @@
+/* Implementation of ngettext(3) function.
+ Copyright (C) 1995, 1997, 2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef _LIBC
+# define __need_NULL
+# include <stddef.h>
+#else
+# ifdef STDC_HEADERS
+# include <stdlib.h> /* Just for NULL. */
+# else
+# ifdef HAVE_STRING_H
+# include <string.h>
+# else
+# define NULL ((void *) 0)
+# endif
+# endif
+#endif
+
+#include "gettext.h"
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgettext.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem. They must not clash
+ with existing names and they should follow ANSI C. But this source
+ code is also used in GNU C Library where the names have a __
+ prefix. So we have to make a difference here. */
+#ifdef _LIBC
+# define NGETTEXT __ngettext
+# define DCNGETTEXT __dcngettext
+#else
+# define NGETTEXT gettext__
+# define DCNGETTEXT dcngettext__
+#endif
+
+/* Look up MSGID in the current default message catalog for the current
+ LC_MESSAGES locale. If not found, returns MSGID itself (the default
+ text). */
+char *
+NGETTEXT (msgid1, msgid2, n)
+ const char *msgid1;
+ const char *msgid2;
+ unsigned long int n;
+{
+ return DCNGETTEXT (NULL, msgid1, msgid2, n, LC_MESSAGES);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library. */
+weak_alias (__ngettext, ngettext);
+#endif
diff --git a/intl/plural.c b/intl/plural.c
new file mode 100644
index 0000000000..f9165b7868
--- /dev/null
+++ b/intl/plural.c
@@ -0,0 +1,1218 @@
+
+/* A Bison parser, made from plural.y
+ by GNU Bison version 1.28 */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define yyparse __gettextparse
+#define yylex __gettextlex
+#define yyerror __gettexterror
+#define yylval __gettextlval
+#define yychar __gettextchar
+#define yydebug __gettextdebug
+#define yynerrs __gettextnerrs
+#define NUMBER 257
+
+#line 1 "plural.y"
+
+/* Expression parsing for plural form selection.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "gettext.h"
+#include "gettextP.h"
+
+#define YYLEX_PARAM &((struct parse_args *) arg)->cp
+#define YYPARSE_PARAM arg
+
+#line 32 "plural.y"
+typedef union {
+ unsigned long int num;
+ struct expression *exp;
+} YYSTYPE;
+#line 37 "plural.y"
+
+/* Prototypes for local functions. */
+static struct expression *new_exp (enum operator op, ...);
+static int yylex (YYSTYPE *lval, const char **pexp);
+static void yyerror (const char *str);
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 31
+#define YYFLAG -32768
+#define YYNTBASE 18
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 257 ? yytranslate[x] : 20)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 7, 2, 2, 2, 12, 5, 2, 16,
+ 17, 10, 8, 2, 9, 2, 11, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 14, 2, 2,
+ 6, 2, 3, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 15,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 4, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 13
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 2, 8, 12, 16, 20, 24, 28, 32, 36,
+ 40, 44, 46, 48
+};
+
+static const short yyrhs[] = { 19,
+ 0, 19, 3, 19, 14, 19, 0, 19, 4, 19,
+ 0, 19, 5, 19, 0, 19, 6, 19, 0, 19,
+ 7, 19, 0, 19, 8, 19, 0, 19, 9, 19,
+ 0, 19, 10, 19, 0, 19, 11, 19, 0, 19,
+ 12, 19, 0, 15, 0, 13, 0, 16, 19, 17,
+ 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 55, 61, 66, 71, 76, 81, 86, 91, 96, 101,
+ 106, 111, 116, 122
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = { "$","error","$undefined.","'?'","'|'",
+"'&'","'='","'!'","'+'","'-'","'*'","'/'","'%'","NUMBER","':'","'n'","'('","')'",
+"start","exp", NULL
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 18, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19
+};
+
+static const short yyr2[] = { 0,
+ 1, 5, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 1, 1, 3
+};
+
+static const short yydefact[] = { 0,
+ 13, 12, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 14, 0, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 0, 2, 0, 0,
+ 0
+};
+
+static const short yydefgoto[] = { 29,
+ 4
+};
+
+static const short yypact[] = { 58,
+-32768,-32768, 58, 37, 10, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58,-32768, 25, 45, 52, 57,
+ 57, 65, 65,-32768,-32768,-32768, 58, 37, 1, 2,
+-32768
+};
+
+static const short yypgoto[] = {-32768,
+ -3
+};
+
+
+#define YYLAST 77
+
+
+static const short yytable[] = { 5,
+ 30, 31, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 0, 28, 0, 0, 16, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 0, 27, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 8,
+ 9, 10, 11, 12, 13, 14, 15, 9, 10, 11,
+ 12, 13, 14, 15, 11, 12, 13, 14, 15, 0,
+ 1, 0, 2, 3, 13, 14, 15
+};
+
+static const short yycheck[] = { 3,
+ 0, 0, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, -1, 27, -1, -1, 17, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, -1, 14, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 5,
+ 6, 7, 8, 9, 10, 11, 12, 6, 7, 8,
+ 9, 10, 11, 12, 8, 9, 10, 11, 12, -1,
+ 13, -1, 15, 16, 10, 11, 12
+};
+#define YYPURE 1
+
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "/usr/share/bison.simple"
+/* This file comes from bison-1.28. */
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C. */
+/* This used to test MSDOS, but that is a bad idea
+ since that symbol is in the user namespace. */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+ instead, just don't use alloca. */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+ So I turned it off. rms, 2 May 1997. */
+/* #include <malloc.h> */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up,
+ and on HPUX 10. Eventually we can turn this on. */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Define __yy_memcpy. Note that the size argument
+ should be passed with type unsigned int, because that is what the non-GCC
+ definitions require. With GCC, __builtin_memcpy takes an arg
+ of type size_t, but it can handle unsigned int. */
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (to, from, count)
+ char *to;
+ char *from;
+ unsigned int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+ register char *t = to;
+ register char *f = from;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 217 "/usr/share/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+ int yyfree_stacks = 0;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ if (yyfree_stacks)
+ {
+ free (yyss);
+ free (yyvs);
+#ifdef YYLSP_NEEDED
+ free (yyls);
+#endif
+ }
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+ yyfree_stacks = 1;
+#endif
+ yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+ __yy_memcpy ((char *)yyss, (char *)yyss1,
+ size * (unsigned int) sizeof (*yyssp));
+ yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+ __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+ size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+ __yy_memcpy ((char *)yyls, (char *)yyls1,
+ size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 1:
+#line 56 "plural.y"
+{
+ ((struct parse_args *) arg)->res = yyvsp[0].exp;
+ ;
+ break;}
+case 2:
+#line 62 "plural.y"
+{
+ if ((yyval.exp = new_exp (qmop, yyvsp[-4].exp, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 3:
+#line 67 "plural.y"
+{
+ if ((yyval.exp = new_exp (lor, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 4:
+#line 72 "plural.y"
+{
+ if ((yyval.exp = new_exp (land, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 5:
+#line 77 "plural.y"
+{
+ if ((yyval.exp = new_exp (equal, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 6:
+#line 82 "plural.y"
+{
+ if ((yyval.exp = new_exp (not_equal, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 7:
+#line 87 "plural.y"
+{
+ if ((yyval.exp = new_exp (plus, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 8:
+#line 92 "plural.y"
+{
+ if ((yyval.exp = new_exp (minus, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 9:
+#line 97 "plural.y"
+{
+ if ((yyval.exp = new_exp (mult, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 10:
+#line 102 "plural.y"
+{
+ if ((yyval.exp = new_exp (divide, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 11:
+#line 107 "plural.y"
+{
+ if ((yyval.exp = new_exp (module, yyvsp[-2].exp, yyvsp[0].exp, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 12:
+#line 112 "plural.y"
+{
+ if ((yyval.exp = new_exp (var, NULL)) == NULL)
+ YYABORT
+ ;
+ break;}
+case 13:
+#line 117 "plural.y"
+{
+ if ((yyval.exp = new_exp (num, NULL)) == NULL)
+ YYABORT;
+ yyval.exp->val.num = yyvsp[0].num
+ ;
+ break;}
+case 14:
+#line 123 "plural.y"
+{
+ yyval.exp = yyvsp[-1].exp
+ ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 543 "/usr/share/bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+
+ yyacceptlab:
+ /* YYACCEPT comes here. */
+ if (yyfree_stacks)
+ {
+ free (yyss);
+ free (yyvs);
+#ifdef YYLSP_NEEDED
+ free (yyls);
+#endif
+ }
+ return 0;
+
+ yyabortlab:
+ /* YYABORT comes here. */
+ if (yyfree_stacks)
+ {
+ free (yyss);
+ free (yyvs);
+#ifdef YYLSP_NEEDED
+ free (yyls);
+#endif
+ }
+ return 1;
+}
+#line 128 "plural.y"
+
+
+static struct expression *
+new_exp (enum operator op, ...)
+{
+ struct expression *newp = (struct expression *) malloc (sizeof (*newp));
+ va_list va;
+ struct expression *next;
+
+ va_start (va, op);
+
+ if (newp == NULL)
+ while ((next = va_arg (va, struct expression *)) != NULL)
+ __gettext_free_exp (next);
+ else
+ {
+ newp->operation = op;
+ next = va_arg (va, struct expression *);
+ if (next != NULL)
+ {
+ newp->val.args3.bexp = next;
+ next = va_arg (va, struct expression *);
+ if (next != NULL)
+ {
+ newp->val.args3.tbranch = next;
+ next = va_arg (va, struct expression *);
+ if (next != NULL)
+ newp->val.args3.fbranch = next;
+ }
+ }
+ }
+
+ va_end (va);
+
+ return newp;
+}
+
+void
+internal_function
+__gettext_free_exp (struct expression *exp)
+{
+ if (exp == NULL)
+ return;
+
+ /* Handle the recursive case. */
+ switch (exp->operation)
+ {
+ case qmop:
+ __gettext_free_exp (exp->val.args3.fbranch);
+ /* FALLTHROUGH */
+
+ case mult:
+ case divide:
+ case module:
+ case plus:
+ case minus:
+ case equal:
+ case not_equal:
+ case land:
+ case lor:
+ __gettext_free_exp (exp->val.args2.right);
+ __gettext_free_exp (exp->val.args2.left);
+ break;
+
+ default:
+ break;
+ }
+
+ free (exp);
+}
+
+
+static int
+yylex (YYSTYPE *lval, const char **pexp)
+{
+ const char *exp = *pexp;
+ int result;
+
+ while (1)
+ {
+ if (exp[0] == '\\' && exp[1] == '\n')
+ {
+ exp += 2;
+ continue;
+ }
+ if (exp[0] != '\0' && exp[0] != ' ' && exp[0] != '\t')
+ break;
+
+ ++exp;
+ }
+
+ result = *exp++;
+ switch (result)
+ {
+ case '0' ... '9':
+ {
+ unsigned long int n = exp[-1] - '0';
+ while (exp[0] >= '0' && exp[0] <= '9')
+ {
+ n *= 10;
+ n += exp[0] - '0';
+ ++exp;
+ }
+ lval->num = n;
+ result = NUMBER;
+ }
+ break;
+
+ case '=':
+ case '!':
+ if (exp[0] == '=')
+ ++exp;
+ else
+ result = YYERRCODE;
+ break;
+
+ case '&':
+ case '|':
+ if (exp[0] == result)
+ ++exp;
+ else
+ result = YYERRCODE;
+ break;
+
+ case 'n':
+ case '*':
+ case '/':
+ case '%':
+ case '+':
+ case '-':
+ case '?':
+ case ':':
+ case '(':
+ case ')':
+ /* Nothing, just return the character. */
+ break;
+
+ case '\n':
+ case '\0':
+ /* Be safe and let the user call this function again. */
+ --exp;
+ result = YYEOF;
+ break;
+
+ default:
+ result = YYERRCODE;
+#if YYDEBUG != 0
+ --exp;
+#endif
+ break;
+ }
+
+ *pexp = exp;
+
+ return result;
+}
+
+
+static void
+yyerror (const char *str)
+{
+ /* Do nothing. We don't print error messages here. */
+}
diff --git a/intl/plural.y b/intl/plural.y
new file mode 100644
index 0000000000..00b6fccb4f
--- /dev/null
+++ b/intl/plural.y
@@ -0,0 +1,290 @@
+%{
+/* Expression parsing for plural form selection.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+ Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "gettext.h"
+#include "gettextP.h"
+
+#define YYLEX_PARAM &((struct parse_args *) arg)->cp
+#define YYPARSE_PARAM arg
+%}
+%pure_parser
+%expect 10
+
+%union {
+ unsigned long int num;
+ struct expression *exp;
+}
+
+%{
+/* Prototypes for local functions. */
+static struct expression *new_exp (enum operator op, ...);
+static int yylex (YYSTYPE *lval, const char **pexp);
+static void yyerror (const char *str);
+%}
+
+%left '?'
+%left '|'
+%left '&'
+%left '=', '!'
+%left '+', '-'
+%left '*', '/', '%'
+%token <num> NUMBER
+%type <exp> exp
+
+%%
+
+start: exp
+ {
+ ((struct parse_args *) arg)->res = $1;
+ }
+ ;
+
+exp: exp '?' exp ':' exp
+ {
+ if (($$ = new_exp (qmop, $1, $3, $5, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '|' exp
+ {
+ if (($$ = new_exp (lor, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '&' exp
+ {
+ if (($$ = new_exp (land, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '=' exp
+ {
+ if (($$ = new_exp (equal, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '!' exp
+ {
+ if (($$ = new_exp (not_equal, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '+' exp
+ {
+ if (($$ = new_exp (plus, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '-' exp
+ {
+ if (($$ = new_exp (minus, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '*' exp
+ {
+ if (($$ = new_exp (mult, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '/' exp
+ {
+ if (($$ = new_exp (divide, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | exp '%' exp
+ {
+ if (($$ = new_exp (module, $1, $3, NULL)) == NULL)
+ YYABORT
+ }
+ | 'n'
+ {
+ if (($$ = new_exp (var, NULL)) == NULL)
+ YYABORT
+ }
+ | NUMBER
+ {
+ if (($$ = new_exp (num, NULL)) == NULL)
+ YYABORT;
+ $$->val.num = $1
+ }
+ | '(' exp ')'
+ {
+ $$ = $2
+ }
+ ;
+
+%%
+
+static struct expression *
+new_exp (enum operator op, ...)
+{
+ struct expression *newp = (struct expression *) malloc (sizeof (*newp));
+ va_list va;
+ struct expression *next;
+
+ va_start (va, op);
+
+ if (newp == NULL)
+ while ((next = va_arg (va, struct expression *)) != NULL)
+ __gettext_free_exp (next);
+ else
+ {
+ newp->operation = op;
+ next = va_arg (va, struct expression *);
+ if (next != NULL)
+ {
+ newp->val.args3.bexp = next;
+ next = va_arg (va, struct expression *);
+ if (next != NULL)
+ {
+ newp->val.args3.tbranch = next;
+ next = va_arg (va, struct expression *);
+ if (next != NULL)
+ newp->val.args3.fbranch = next;
+ }
+ }
+ }
+
+ va_end (va);
+
+ return newp;
+}
+
+void
+internal_function
+__gettext_free_exp (struct expression *exp)
+{
+ if (exp == NULL)
+ return;
+
+ /* Handle the recursive case. */
+ switch (exp->operation)
+ {
+ case qmop:
+ __gettext_free_exp (exp->val.args3.fbranch);
+ /* FALLTHROUGH */
+
+ case mult:
+ case divide:
+ case module:
+ case plus:
+ case minus:
+ case equal:
+ case not_equal:
+ case land:
+ case lor:
+ __gettext_free_exp (exp->val.args2.right);
+ __gettext_free_exp (exp->val.args2.left);
+ break;
+
+ default:
+ break;
+ }
+
+ free (exp);
+}
+
+
+static int
+yylex (YYSTYPE *lval, const char **pexp)
+{
+ const char *exp = *pexp;
+ int result;
+
+ while (1)
+ {
+ if (exp[0] == '\\' && exp[1] == '\n')
+ {
+ exp += 2;
+ continue;
+ }
+ if (exp[0] != '\0' && exp[0] != ' ' && exp[0] != '\t')
+ break;
+
+ ++exp;
+ }
+
+ result = *exp++;
+ switch (result)
+ {
+ case '0' ... '9':
+ {
+ unsigned long int n = exp[-1] - '0';
+ while (exp[0] >= '0' && exp[0] <= '9')
+ {
+ n *= 10;
+ n += exp[0] - '0';
+ ++exp;
+ }
+ lval->num = n;
+ result = NUMBER;
+ }
+ break;
+
+ case '=':
+ case '!':
+ if (exp[0] == '=')
+ ++exp;
+ else
+ result = YYERRCODE;
+ break;
+
+ case '&':
+ case '|':
+ if (exp[0] == result)
+ ++exp;
+ else
+ result = YYERRCODE;
+ break;
+
+ case 'n':
+ case '*':
+ case '/':
+ case '%':
+ case '+':
+ case '-':
+ case '?':
+ case ':':
+ case '(':
+ case ')':
+ /* Nothing, just return the character. */
+ break;
+
+ case '\n':
+ case '\0':
+ /* Be safe and let the user call this function again. */
+ --exp;
+ result = YYEOF;
+ break;
+
+ default:
+ result = YYERRCODE;
+#if YYDEBUG != 0
+ --exp;
+#endif
+ break;
+ }
+
+ *pexp = exp;
+
+ return result;
+}
+
+
+static void
+yyerror (const char *str)
+{
+ /* Do nothing. We don't print error messages here. */
+}
diff --git a/intl/po2test.sed b/intl/po2test.sed
new file mode 100644
index 0000000000..d1c2f3d086
--- /dev/null
+++ b/intl/po2test.sed
@@ -0,0 +1,70 @@
+# po2test.sed - Convert Uniforum style .po file to C code for testing.
+# Copyright (C) 2000 Free Software Foundation, Inc.
+# Ulrich Drepper <drepper@cygnus.com>, 2000.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#
+# We copy the original message as a comment into the .msg file. But enclose
+# them with INPUT ( ).
+#
+/^msgid/ {
+ s/msgid[ ]*"\(.*\)"/INPUT ("\1")/
+# Clear flag from last substitution.
+ tb
+# Append the next line.
+ :b
+ N
+# Look whether second part is a continuation line.
+ s/\(.*\)")\(\n\)"\(.*\)"/\1\\\2\3")/
+# Yes, then branch.
+ ta
+ P
+ D
+# Note that `D' includes a jump to the start!!
+# We found a continuation line. But before printing insert '\'.
+ :a
+ s/\(.*\)")\(\n.*\)/\1\\\2/
+ P
+# We cannot use the sed command `D' here
+ s/.*\n\(.*\)/\1/
+ tb
+}
+#
+# Copy the translations as well and enclose them with OUTPUT ( ).
+#
+/^msgstr/ {
+ s/msgstr[ ]*"\(.*\)"/OUTPUT ("\1")/
+# Clear flag from last substitution.
+ tb
+# Append the next line.
+ :b
+ N
+# Look whether second part is a continuation line.
+ s/\(.*\)")\(\n\)"\(.*\)"/\1\\\2\3")/
+# Yes, then branch.
+ ta
+ P
+ D
+# Note that `D' includes a jump to the start!!
+# We found a continuation line. But before printing insert '\'.
+ :a
+ s/\(.*\)")\(\n.*\)/\1\\\2/
+ P
+# We cannot use the sed command `D' here
+ s/.*\n\(.*\)/\1/
+ tb
+}
+d
diff --git a/intl/tst-gettext.c b/intl/tst-gettext.c
new file mode 100644
index 0000000000..9ce11903aa
--- /dev/null
+++ b/intl/tst-gettext.c
@@ -0,0 +1,322 @@
+/* Test of the gettext functions.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+const struct
+{
+ const char *msgid;
+ const char *msgstr;
+} msgs[] =
+{
+#define INPUT(Str) { Str,
+#define OUTPUT(Str) Str },
+#include TESTSTRS_H
+};
+
+const char *catname[] =
+{
+ [LC_MESSAGES] = "LC_MESSAGES",
+ [LC_TIME] = "LC_TIME",
+ [LC_NUMERIC] = "LC_NUMERIC"
+};
+
+
+static int positive_gettext_test (void);
+static int negative_gettext_test (void);
+static int positive_dgettext_test (const char *domain);
+static int positive_dcgettext_test (const char *domain, int category);
+static int negative_dcgettext_test (const char *domain, int category);
+
+
+int
+main (int argc, char *argv[])
+{
+ int result = 0;
+
+ /* This is the place where the .mo files are placed. */
+ if (argc > 1)
+ {
+ bindtextdomain ("existing-domain", argv[1]);
+ bindtextdomain ("existing-time-domain", argv[1]);
+ bindtextdomain ("non-existing-domain", argv[1]);
+ }
+
+ /* The locale the catalog is created for is "existing-category". Now
+ set the various variables in question to this value and run the
+ test. */
+ setenv ("LANGUAGE", "existing-locale", 1);
+ setenv ("LC_ALL", "non-existing-locale", 1);
+ setenv ("LC_MESSAGES", "non-existing-locale", 1);
+ setenv ("LANG", "non-existing-locale", 1);
+ /* This is the name of the existing domain with a catalog for the
+ LC_MESSAGES category. */
+ textdomain ("existing-domain");
+ puts ("test `gettext' with LANGUAGE set");
+ if (positive_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ /* This is the name of a non-existing domain with a catalog for the
+ LC_MESSAGES category. We leave this value set for the `dgettext'
+ and `dcgettext' tests. */
+ textdomain ("non-existing-domain");
+ puts ("test `gettext' with LANGUAGE set");
+ if (negative_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ puts ("test `dgettext' with LANGUAGE set");
+ if (positive_dgettext_test ("existing-domain") != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+
+ /* Now the same tests with LC_ALL deciding. */
+ unsetenv ("LANGUAGE");
+ setenv ("LC_ALL", "existing-locale", 1);
+ puts ("test `gettext' with LC_ALL set");
+ /* This is the name of the existing domain with a catalog for the
+ LC_MESSAGES category. */
+ textdomain ("existing-domain");
+ if (positive_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ /* This is the name of a non-existing domain with a catalog for the
+ LC_MESSAGES category. We leave this value set for the `dgettext'
+ and `dcgettext' tests. */
+ textdomain ("non-existing-domain");
+ puts ("test `gettext' with LANGUAGE set");
+ if (negative_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ puts ("test `dgettext' with LANGUAGE set");
+ if (positive_dgettext_test ("existing-domain") != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+
+ /* Now the same tests with LC_MESSAGES deciding. */
+ unsetenv ("LC_ALL");
+ setenv ("LC_MESSAGES", "existing-locale", 1);
+ setenv ("LC_TIME", "existing-locale", 1);
+ setenv ("LC_NUMERIC", "non-existing-locale", 1);
+ puts ("test `gettext' with LC_ALL set");
+ /* This is the name of the existing domain with a catalog for the
+ LC_MESSAGES category. */
+ textdomain ("existing-domain");
+ if (positive_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ /* This is the name of a non-existing domain with a catalog for the
+ LC_MESSAGES category. We leave this value set for the `dgettext'
+ and `dcgettext' tests. */
+ textdomain ("non-existing-domain");
+ puts ("test `gettext' with LANGUAGE set");
+ if (negative_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ puts ("test `dgettext' with LANGUAGE set");
+ if (positive_dgettext_test ("existing-domain") != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ puts ("test `dcgettext' with LANGUAGE set (LC_MESSAGES)");
+ if (positive_dcgettext_test ("existing-domain", LC_MESSAGES) != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ /* Try a different category. For this we also switch the domain. */
+ puts ("test `dcgettext' with LANGUAGE set (LC_TIME)");
+ if (positive_dcgettext_test ("existing-time-domain", LC_TIME) != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ /* This time use a category for which there is no catalog. */
+ puts ("test `dcgettext' with LANGUAGE set (LC_NUMERIC)");
+ if (negative_dcgettext_test ("existing-domain", LC_NUMERIC) != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+
+ /* Now the same tests with LANG deciding. */
+ unsetenv ("LC_MESSAGES");
+ setenv ("LANG", "existing-locale", 1);
+ /* This is the name of the existing domain with a catalog for the
+ LC_MESSAGES category. */
+ textdomain ("existing-domain");
+ puts ("test `gettext' with LC_ALL set");
+ if (positive_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ /* This is the name of a non-existing domain with a catalog for the
+ LC_MESSAGES category. We leave this value set for the `dgettext'
+ and `dcgettext' tests. */
+ textdomain ("non-existing-domain");
+ puts ("test `gettext' with LANGUAGE set");
+ if (negative_gettext_test () != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+ puts ("test `dgettext' with LANGUAGE set");
+ if (positive_dgettext_test ("existing-domain") != 0)
+ {
+ puts ("FAILED");
+ result = 1;
+ }
+
+ return result;
+}
+
+
+static int
+positive_gettext_test (void)
+{
+ size_t cnt;
+ int result = 0;
+
+ for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
+ {
+ const char *found = gettext (msgs[cnt].msgid);
+
+ if (found == NULL || strcmp (found, msgs[cnt].msgstr) != 0)
+ {
+ /* Oops, shouldn't happen. */
+ printf (" gettext (\"%s\") failed, returned \"%s\"\n",
+ msgs[cnt].msgid, found);
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+
+static int
+negative_gettext_test (void)
+{
+ size_t cnt;
+ int result = 0;
+
+ for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
+ {
+ const char *found = gettext (msgs[cnt].msgid);
+
+ if (found != msgs[cnt].msgid)
+ {
+ /* Oops, shouldn't happen. */
+ printf (" gettext (\"%s\") failed\n", msgs[cnt].msgid);
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+
+static int
+positive_dgettext_test (const char *domain)
+{
+ size_t cnt;
+ int result = 0;
+
+ for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
+ {
+ const char *found = dgettext (domain, msgs[cnt].msgid);
+
+ if (found == NULL || strcmp (found, msgs[cnt].msgstr) != 0)
+ {
+ /* Oops, shouldn't happen. */
+ printf (" dgettext (\"%s\", \"%s\") failed, returned \"%s\"\n",
+ domain, msgs[cnt].msgid, found);
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+
+static int
+positive_dcgettext_test (const char *domain, int category)
+{
+ size_t cnt;
+ int result = 0;
+
+ for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
+ {
+ const char *found = dcgettext (domain, msgs[cnt].msgid, category);
+
+ if (found == NULL || strcmp (found, msgs[cnt].msgstr) != 0)
+ {
+ /* Oops, shouldn't happen. */
+ printf (" dcgettext (\"%s\", \"%s\", %s) failed, returned \"%s\"\n",
+ domain, msgs[cnt].msgid, catname[category], found);
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+
+static int
+negative_dcgettext_test (const char *domain, int category)
+{
+ size_t cnt;
+ int result = 0;
+
+ for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
+ {
+ const char *found = dcgettext (domain, msgs[cnt].msgid, category);
+
+ if (found != msgs[cnt].msgid)
+ {
+ /* Oops, shouldn't happen. */
+ printf (" dcgettext (\"%s\", \"%s\", %s) failed\n",
+ domain, msgs[cnt].msgid, catname[category]);
+ result = 1;
+ }
+ }
+
+ return result;
+}
diff --git a/intl/tst-gettext.sh b/intl/tst-gettext.sh
new file mode 100755
index 0000000000..db3653ad73
--- /dev/null
+++ b/intl/tst-gettext.sh
@@ -0,0 +1,41 @@
+#! /bin/sh
+# Test of gettext functions.
+# Copyright (C) 2000 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with the GNU C Library; see the file COPYING.LIB. If
+# not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+common_objpfx=$1
+objpfx=$2
+
+# Generate the test data.
+test -d ${objpfx}domaindir || mkdir ${objpfx}domaindir
+# Create the locale directories.
+test -d ${objpfx}domaindir/existing-locale || mkdir ${objpfx}domaindir/existing-locale
+test -d ${objpfx}domaindir/existing-locale/LC_MESSAGES || mkdir ${objpfx}domaindir/existing-locale/LC_MESSAGES
+test -d ${objpfx}domaindir/existing-locale/LC_TIME || mkdir ${objpfx}domaindir/existing-locale/LC_TIME
+
+# Populate them.
+msgfmt -o ${objpfx}domaindir/existing-locale/LC_MESSAGES/existing-domain.mo \
+ ../po/de.po
+msgfmt -o ${objpfx}domaindir/existing-locale/LC_TIME/existing-time-domain.mo \
+ ../po/de.po
+
+# Now run the test.
+${common_objpfx}elf/ld.so --library-path $common_objpfx \
+${objpfx}tst-gettext > ${objpfx}tst-gettext.out ${objpfx}domaindir
+
+exit $?