From abbffdf981c8bb03312024107715d58902914f11 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 22 Jan 2000 05:50:49 +0000 Subject: Update. 2000-01-21 Ulrich Drepper * 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. --- ChangeLog | 30 ++ NEWS | 9 +- intl/Makefile | 30 +- intl/Versions | 10 + intl/dcgettext.c | 835 +---------------------------------- intl/dcigettext.c | 1000 ++++++++++++++++++++++++++++++++++++++++++ intl/dcngettext.c | 64 +++ intl/dngettext.c | 67 +++ intl/gettext.c | 8 +- intl/gettextP.h | 78 +++- intl/libintl.h | 25 +- intl/loadinfo.h | 4 +- intl/loadmsgcat.c | 84 +++- intl/ngettext.c | 78 ++++ intl/plural.c | 1218 +++++++++++++++++++++++++++++++++++++++++++++++++++ intl/plural.y | 290 ++++++++++++ intl/po2test.sed | 70 +++ intl/tst-gettext.c | 322 ++++++++++++++ intl/tst-gettext.sh | 41 ++ 19 files changed, 3416 insertions(+), 847 deletions(-) create mode 100644 intl/dcigettext.c create mode 100644 intl/dcngettext.c create mode 100644 intl/dngettext.c create mode 100644 intl/ngettext.c create mode 100644 intl/plural.c create mode 100644 intl/plural.y create mode 100644 intl/po2test.sed create mode 100644 intl/tst-gettext.c create mode 100755 intl/tst-gettext.sh diff --git a/ChangeLog b/ChangeLog index 8f144c4bb4..861f5ba640 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2000-01-21 Ulrich Drepper + + * 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 * 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 #endif -#include - -#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 -# else -# ifdef _AIX - #pragma alloca -# else -# ifndef alloca -char *alloca (); -# endif -# endif -# endif -#endif - -#include -#ifndef errno -extern int errno; -#endif -#ifndef __set_errno -# define __set_errno(val) errno = (val) -#endif - -#if defined STDC_HEADERS || defined _LIBC -# include -#else -char *getenv (); -# ifdef HAVE_MALLOC_H -# include -# else -void free (); -# endif -#endif - -#if defined HAVE_STRING_H || defined _LIBC -# ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -# endif -# include -#else -# include -#endif -#if !HAVE_STRCHR && !defined _LIBC -# ifndef strchr -# define strchr index -# endif -#endif - -#if defined HAVE_UNISTD_H || defined _LIBC -# include -#endif - -#if defined HAVE_LOCALE_H || defined _LIBC -# include -#endif - -#if defined HAVE_SYS_PARAM_H || defined _LIBC -# include -#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 -#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 -#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 -#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 - -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 +#endif + +#include + +#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 +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca +char *alloca (); +# endif +# endif +# endif +#endif + +#include +#ifndef errno +extern int errno; +#endif +#ifndef __set_errno +# define __set_errno(val) errno = (val) +#endif + +#if defined STDC_HEADERS || defined _LIBC +# include +#else +char *getenv (); +# ifdef HAVE_MALLOC_H +# include +# else +void free (); +# endif +#endif + +#if defined HAVE_STRING_H || defined _LIBC +# ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +# endif +# include +#else +# include +#endif +#if !HAVE_STRCHR && !defined _LIBC +# ifndef strchr +# define strchr index +# endif +#endif + +#if defined HAVE_UNISTD_H || defined _LIBC +# include +#endif + +#if defined HAVE_LOCALE_H || defined _LIBC +# include +#endif + +#if defined HAVE_SYS_PARAM_H || defined _LIBC +# include +#endif + +#include "gettext.h" +#include "gettextP.h" +#ifdef _LIBC +# include +#else +# include "libgettext.h" +#endif +#include "hash-string.h" + +/* Thread safetyness. */ +#ifdef _LIBC +# include +#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 +#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 +#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 + +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 +#endif + +#include "gettext.h" +#include "gettextP.h" +#ifdef _LIBC +# include +#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 +#endif + +#if defined HAVE_LOCALE_H || defined _LIBC +# include +#endif + +#include "gettext.h" +#include "gettextP.h" +#ifdef _LIBC +# include +#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 , 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 , 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 , 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 #endif +#include #include #include #include @@ -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 +#endif + +#ifdef _LIBC +# define __need_NULL +# include +#else +# ifdef STDC_HEADERS +# include /* Just for NULL. */ +# else +# ifdef HAVE_STRING_H +# include +# else +# define NULL ((void *) 0) +# endif +# endif +#endif + +#include "gettext.h" +#include "gettextP.h" +#ifdef _LIBC +# include +#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 , 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 +#include +#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 + +#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 +#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 +#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 */ + #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 , 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 +#include +#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 NUMBER +%type 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 , 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 , 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 +#include +#include +#include + + +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 $? -- cgit 1.4.1