2000-01-21  Ulrich Drepper  <drepper@cygnus.com>

	* intl/Makefile (routines): Add dcigettext, dcngettext, dngettxt,
	ngettext, and plural.
	(distribute): Add plural.y, po2test.sed, and tst-gettext.sh.
	(test-srcs): Add tst-gettext.
	(before-compile): Add $(objpfx)msgs.h.
	Add rules for plural.c and msgs.h generation and running tst-gettext.\
	* intl/Versions [GLIBC_2.2]: Add __dcngettext, dcngettext, dngettext,
	and ngettext.
	* intl/dcgettext.c: Move most code into dcigettext.c.  Add call
	dcigettext with appropriate parameters.
	* intl/dcigettext.c: New file.
	* intl/dcngettext.c: New file.
	* intl/dngettext.c: New file.
	* intl/ngettext.c: New file.
	* intl/gettextP.h (struct expression): Define.
	(struct loaded_domain): Add plural and nplurals members.
	Add prototypes for new internal functions.
	* intl/libintl.h: Declare new functions.  Add optimizations for them.
	* intl/loadinfo.h: Add new parameter to _nl_find_msg declaration.
	* intl/loadmsgcat.c (_nl_load_domain): Search for plural information in
	header entry and parse and store the expression.
	* intl/plural.y: New file.
	* intl/po2test.sed: New file.
	* intl/tst-gettext.c: New file.
	* intl/tst-gettext.sh: New file.

	* intl/gettext.c: Call __dcgettext directly.
This commit is contained in:
Ulrich Drepper 2000-01-22 05:50:49 +00:00
parent 0b9fbf003a
commit abbffdf981
19 changed files with 3416 additions and 847 deletions

View File

@ -1,3 +1,33 @@
2000-01-21 Ulrich Drepper <drepper@cygnus.com>
* intl/Makefile (routines): Add dcigettext, dcngettext, dngettxt,
ngettext, and plural.
(distribute): Add plural.y, po2test.sed, and tst-gettext.sh.
(test-srcs): Add tst-gettext.
(before-compile): Add $(objpfx)msgs.h.
Add rules for plural.c and msgs.h generation and running tst-gettext.\
* intl/Versions [GLIBC_2.2]: Add __dcngettext, dcngettext, dngettext,
and ngettext.
* intl/dcgettext.c: Move most code into dcigettext.c. Add call
dcigettext with appropriate parameters.
* intl/dcigettext.c: New file.
* intl/dcngettext.c: New file.
* intl/dngettext.c: New file.
* intl/ngettext.c: New file.
* intl/gettextP.h (struct expression): Define.
(struct loaded_domain): Add plural and nplurals members.
Add prototypes for new internal functions.
* intl/libintl.h: Declare new functions. Add optimizations for them.
* intl/loadinfo.h: Add new parameter to _nl_find_msg declaration.
* intl/loadmsgcat.c (_nl_load_domain): Search for plural information in
header entry and parse and store the expression.
* intl/plural.y: New file.
* intl/po2test.sed: New file.
* intl/tst-gettext.c: New file.
* intl/tst-gettext.sh: New file.
* intl/gettext.c: Call __dcgettext directly.
2000-01-20 Ulrich Drepper <drepper@cygnus.com>
* manual/getopt.texi (Using the getopt function): Fix description of

9
NEWS
View File

@ -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

View File

@ -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)

View File

@ -18,4 +18,14 @@ libc {
# t*
textdomain;
}
GLIBC_2.2 {
# functions used in inline functions or macros
__dcngettext;
# d*
dcngettext; dngettext;
# n*
ngettext;
}
}

View File

@ -1,5 +1,5 @@
/* Implementation of the dcgettext(3) function.
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
@ -23,70 +23,6 @@
# include <config.h>
#endif
#include <sys/types.h>
#if defined __GNUC__ && !defined C_ALLOCA
# define alloca __builtin_alloca
# define HAVE_ALLOCA 1
#else
# if (defined HAVE_ALLOCA_H || defined _LIBC) && !defined C_ALLOCA
# include <alloca.h>
# else
# ifdef _AIX
#pragma alloca
# else
# ifndef alloca
char *alloca ();
# endif
# endif
# endif
#endif
#include <errno.h>
#ifndef errno
extern int errno;
#endif
#ifndef __set_errno
# define __set_errno(val) errno = (val)
#endif
#if defined STDC_HEADERS || defined _LIBC
# include <stdlib.h>
#else
char *getenv ();
# ifdef HAVE_MALLOC_H
# include <malloc.h>
# else
void free ();
# endif
#endif
#if defined HAVE_STRING_H || defined _LIBC
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
# include <string.h>
#else
# include <strings.h>
#endif
#if !HAVE_STRCHR && !defined _LIBC
# ifndef strchr
# define strchr index
# endif
#endif
#if defined HAVE_UNISTD_H || defined _LIBC
# include <unistd.h>
#endif
#if defined HAVE_LOCALE_H || defined _LIBC
# include <locale.h>
#endif
#if defined HAVE_SYS_PARAM_H || defined _LIBC
# include <sys/param.h>
#endif
#include "gettext.h"
#include "gettextP.h"
#ifdef _LIBC
@ -94,225 +30,19 @@ void free ();
#else
# include "libgettext.h"
#endif
#include "hash-string.h"
/* Thread safetyness. */
#ifdef _LIBC
# include <bits/libc-lock.h>
#endif
/* @@ end of prolog @@ */
#ifdef _LIBC
/* Rename the non ANSI C functions. This is required by the standard
because some ANSI C functions will require linking with this object
file and the name space must not be polluted. */
# define getcwd __getcwd
# ifndef stpcpy
# define stpcpy __stpcpy
# endif
#else
# if !defined HAVE_GETCWD
char *getwd ();
# define getcwd(buf, max) getwd (buf)
# else
char *getcwd ();
# endif
# ifndef HAVE_STPCPY
static char *stpcpy PARAMS ((char *dest, const char *src));
# endif
# ifndef HAVE_MEMPCPY
static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
# endif
#endif
/* Amount to increase buffer size by in each try. */
#define PATH_INCR 32
/* The following is from pathmax.h. */
/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
PATH_MAX but might cause redefinition warnings when sys/param.h is
later included (as on MORE/BSD 4.3). */
#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
# include <limits.h>
#endif
#ifndef _POSIX_PATH_MAX
# define _POSIX_PATH_MAX 255
#endif
#if !defined PATH_MAX && defined _PC_PATH_MAX
# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
#endif
/* Don't include sys/param.h if it already has been. */
#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
# include <sys/param.h>
#endif
#if !defined PATH_MAX && defined MAXPATHLEN
# define PATH_MAX MAXPATHLEN
#endif
#ifndef PATH_MAX
# define PATH_MAX _POSIX_PATH_MAX
#endif
/* XPG3 defines the result of `setlocale (category, NULL)' as:
``Directs `setlocale()' to query `category' and return the current
setting of `local'.''
However it does not specify the exact format. And even worse: POSIX
defines this not at all. So we can use this feature only on selected
system (e.g. those using GNU C Library). */
#ifdef _LIBC
# define HAVE_LOCALE_NULL
#endif
/* We want to allocate a string at the end of the struct. gcc makes
this easy. */
#ifdef __GNUC__
# define ZERO 0
#else
# define ZERO 1
#endif
/* This is the type used for the search tree where known translations
are stored. */
struct known_translation_t
{
/* Domain in which to search. */
char *domain;
/* The category. */
int category;
/* State of the catalog counter at the point the string was found. */
int counter;
/* And finally the translation. */
const char *translation;
/* Pointer to the string in question. */
char msgid[ZERO];
};
/* Root of the search tree with known translations. We can use this
only if the system provides the `tsearch' function family. */
#if defined HAVE_TSEARCH || defined _LIBC
# include <search.h>
static void *root;
# ifdef _LIBC
# define tsearch __tsearch
# endif
/* Function to compare two entries in the table of known translations. */
static int
transcmp (const void *p1, const void *p2)
{
struct known_translation_t *s1 = (struct known_translation_t *) p1;
struct known_translation_t *s2 = (struct known_translation_t *) p2;
int result;
result = strcmp (s1->msgid, s2->msgid);
if (result == 0)
{
result = strcmp (s1->msgid, s2->msgid);
if (result == 0)
/* We compare the category last (though this is the cheapest
operation) since it is hopefully always the same (namely
LC_MESSAGES). */
result = s1->category - s2->category;
}
return result;
}
#endif
/* Name of the default domain used for gettext(3) prior any call to
textdomain(3). The default value for this is "messages". */
const char _nl_default_default_domain[] = "messages";
/* Value used as the default domain for gettext(3). */
const char *_nl_current_default_domain = _nl_default_default_domain;
/* Contains the default location of the message catalogs. */
const char _nl_default_dirname[] = GNULOCALEDIR;
/* List with bindings of specific domains created by bindtextdomain()
calls. */
struct binding *_nl_domain_bindings;
/* Prototypes for local functions. */
static const char *category_to_name PARAMS ((int category)) internal_function;
static const char *guess_category_value PARAMS ((int category,
const char *categoryname))
internal_function;
/* For those loosing systems which don't have `alloca' we have to add
some additional code emulating it. */
#ifdef HAVE_ALLOCA
/* Nothing has to be done. */
# define ADD_BLOCK(list, address) /* nothing */
# define FREE_BLOCKS(list) /* nothing */
#else
struct block_list
{
void *address;
struct block_list *next;
};
# define ADD_BLOCK(list, addr) \
do { \
struct block_list *newp = (struct block_list *) malloc (sizeof (*newp)); \
/* If we cannot get a free block we cannot add the new element to \
the list. */ \
if (newp != NULL) { \
newp->address = (addr); \
newp->next = (list); \
(list) = newp; \
} \
} while (0)
# define FREE_BLOCKS(list) \
do { \
while (list != NULL) { \
struct block_list *old = list; \
list = list->next; \
free (old); \
} \
} while (0)
# undef alloca
# define alloca(size) (malloc (size))
#endif /* have alloca */
/* Names for the libintl functions are a problem. They must not clash
with existing names and they should follow ANSI C. But this source
code is also used in GNU C Library where the names have a __
prefix. So we have to make a difference here. */
#ifdef _LIBC
# define DCGETTEXT __dcgettext
# define DCIGETTEXT __dcigettext
#else
# define DCGETTEXT dcgettext__
#endif
/* Checking whether the binaries runs SUID must be done and glibc provides
easier methods therefore we make a difference here. */
#ifdef _LIBC
# define ENABLE_SECURE __libc_enable_secure
# define DETERMINE_SECURE
#else
static int enable_secure;
# define ENABLE_SECURE (enable_secure == 1)
# define DETERMINE_SECURE \
if (enable_secure == 0) \
{ \
if (getuid () != geteuid () || getgid () != getegid ()) \
enable_secure = 1; \
else \
enable_secure = -1; \
}
# define DCIGETTEXT dcigettext__
#endif
/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
@ -323,567 +53,10 @@ DCGETTEXT (domainname, msgid, category)
const char *msgid;
int category;
{
#ifndef HAVE_ALLOCA
struct block_list *block_list = NULL;
#endif
struct loaded_l10nfile *domain;
struct binding *binding;
const char *categoryname;
const char *categoryvalue;
char *dirname, *xdomainname;
char *single_locale;
char *retval;
int saved_errno;
#if defined HAVE_TSEARCH || defined _LIBC
struct known_translation_t *search;
struct known_translation_t **foundp;
size_t msgid_len = strlen (msgid) + 1;
#endif
size_t domainname_len;
/* If no real MSGID is given return NULL. */
if (msgid == NULL)
return NULL;
#if defined HAVE_TSEARCH || defined _LIBC
/* Try to find the translation among those which we found at some time. */
search = (struct known_translation_t *) alloca (sizeof (*search)
+ msgid_len);
memcpy (search->msgid, msgid, msgid_len);
search->domain = (char *) domainname;
search->category = category;
foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
return (char *) (*foundp)->translation;
#endif
/* Preserve the `errno' value. */
saved_errno = errno;
/* See whether this is a SUID binary or not. */
DETERMINE_SECURE;
/* If DOMAINNAME is NULL, we are interested in the default domain. If
CATEGORY is not LC_MESSAGES this might not make much sense but the
definition left this undefined. */
if (domainname == NULL)
domainname = _nl_current_default_domain;
/* First find matching binding. */
for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
{
int compare = strcmp (domainname, binding->domainname);
if (compare == 0)
/* We found it! */
break;
if (compare < 0)
{
/* It is not in the list. */
binding = NULL;
break;
}
}
if (binding == NULL)
dirname = (char *) _nl_default_dirname;
else if (binding->dirname[0] == '/')
dirname = binding->dirname;
else
{
/* We have a relative path. Make it absolute now. */
size_t dirname_len = strlen (binding->dirname) + 1;
size_t path_max;
char *ret;
path_max = (unsigned int) PATH_MAX;
path_max += 2; /* The getcwd docs say to do this. */
dirname = (char *) alloca (path_max + dirname_len);
ADD_BLOCK (block_list, dirname);
__set_errno (0);
while ((ret = getcwd (dirname, path_max)) == NULL && errno == ERANGE)
{
path_max += PATH_INCR;
dirname = (char *) alloca (path_max + dirname_len);
ADD_BLOCK (block_list, dirname);
__set_errno (0);
}
if (ret == NULL)
{
/* We cannot get the current working directory. Don't signal an
error but simply return the default string. */
FREE_BLOCKS (block_list);
__set_errno (saved_errno);
return (char *) msgid;
}
stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
}
/* Now determine the symbolic name of CATEGORY and its value. */
categoryname = category_to_name (category);
categoryvalue = guess_category_value (category, categoryname);
domainname_len = strlen (domainname);
xdomainname = (char *) alloca (strlen (categoryname)
+ domainname_len + 5);
ADD_BLOCK (block_list, xdomainname);
stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
domainname, domainname_len),
".mo");
/* Creating working area. */
single_locale = (char *) alloca (strlen (categoryvalue) + 1);
ADD_BLOCK (block_list, single_locale);
/* Search for the given string. This is a loop because we perhaps
got an ordered list of languages to consider for the translation. */
while (1)
{
/* Make CATEGORYVALUE point to the next element of the list. */
while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
++categoryvalue;
if (categoryvalue[0] == '\0')
{
/* The whole contents of CATEGORYVALUE has been searched but
no valid entry has been found. We solve this situation
by implicitly appending a "C" entry, i.e. no translation
will take place. */
single_locale[0] = 'C';
single_locale[1] = '\0';
}
else
{
char *cp = single_locale;
while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
*cp++ = *categoryvalue++;
*cp = '\0';
/* When this is a SUID binary we must not allow accessing files
outside the dedicated directories. */
if (ENABLE_SECURE
&& (memchr (single_locale, '/',
_nl_find_language (single_locale) - single_locale)
!= NULL))
/* Ingore this entry. */
continue;
}
/* If the current locale value is C (or POSIX) we don't load a
domain. Return the MSGID. */
if (strcmp (single_locale, "C") == 0
|| strcmp (single_locale, "POSIX") == 0)
{
FREE_BLOCKS (block_list);
__set_errno (saved_errno);
return (char *) msgid;
}
/* Find structure describing the message catalog matching the
DOMAINNAME and CATEGORY. */
domain = _nl_find_domain (dirname, single_locale, xdomainname);
if (domain != NULL)
{
retval = _nl_find_msg (domain, msgid);
if (retval == NULL)
{
int cnt;
for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
{
retval = _nl_find_msg (domain->successor[cnt], msgid);
if (retval != NULL)
break;
}
}
if (retval != NULL)
{
FREE_BLOCKS (block_list);
__set_errno (saved_errno);
#if defined HAVE_TSEARCH || defined _LIBC
if (foundp == NULL)
{
/* Create a new entry and add it to the search tree. */
struct known_translation_t *newp;
newp = (struct known_translation_t *)
malloc (sizeof (*newp) + msgid_len
+ domainname_len + 1 - ZERO);
if (newp != NULL)
{
newp->domain = mempcpy (newp->msgid, msgid, msgid_len);
memcpy (newp->domain, domainname, domainname_len + 1);
newp->category = category;
newp->counter = _nl_msg_cat_cntr;
newp->translation = retval;
/* Insert the entry in the search tree. */
foundp = (struct known_translation_t **)
tsearch (newp, &root, transcmp);
if (&newp != foundp)
/* The insert failed. */
free (newp);
}
}
else
{
/* We can update the existing entry. */
(*foundp)->counter = _nl_msg_cat_cntr;
(*foundp)->translation = retval;
}
#endif
return retval;
}
}
}
/* NOTREACHED */
return DCIGETTEXT (domainname, msgid, NULL, 0, 0, category);
}
#ifdef _LIBC
/* Alias for function name in GNU C Library. */
weak_alias (__dcgettext, dcgettext);
#endif
char *
internal_function
_nl_find_msg (domain_file, msgid)
struct loaded_l10nfile *domain_file;
const char *msgid;
{
size_t act = 0;
size_t top, bottom;
struct loaded_domain *domain;
if (domain_file->decided == 0)
_nl_load_domain (domain_file);
if (domain_file->data == NULL)
return NULL;
domain = (struct loaded_domain *) domain_file->data;
/* Locate the MSGID and its translation. */
if (domain->hash_size > 2 && domain->hash_tab != NULL)
{
/* Use the hashing table. */
nls_uint32 len = strlen (msgid);
nls_uint32 hash_val = hash_string (msgid);
nls_uint32 idx = hash_val % domain->hash_size;
nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
if (nstr == 0)
/* Hash table entry is empty. */
return NULL;
if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
&& strcmp (msgid,
domain->data + W (domain->must_swap,
domain->orig_tab[nstr - 1].offset)) == 0)
{
/* We found an entry. If we have to convert the string to use
a different character set this is the time. */
char *result =
(char *) domain->data + W (domain->must_swap,
domain->trans_tab[nstr - 1].offset);
if (
#ifdef _LIBC
domain->conv != (__gconv_t) -1
#else
# if HAVE_ICONV
domain->conv != (iconv_t) -1
# endif
#endif
)
{
/* We are supposed to do a conversion. First allocate an
appropriate table with the same structure as the hash
table in the file where we can put the pointers to the
converted strings in. */
if (domain->conv_tab == NULL
&& ((domain->conv_tab = (char **) calloc (domain->hash_size,
sizeof (char *)))
== NULL))
/* Mark that we didn't succeed allocating a table. */
domain->conv_tab = (char **) -1;
if (domain->conv_tab == (char **) -1)
/* Nothing we can do, no more memory. */
return NULL;
if (domain->conv_tab[idx] == NULL)
{
/* We haven't used this string so far, so it is not
translated yet. Do this now. */
#ifdef _LIBC
/* For glibc we use a bit more efficient memory handling.
We allocate always larger blocks which get used over
time. This is faster than many small allocations. */
__libc_lock_define_initialized (static, lock)
static unsigned char *freemem;
static size_t freemem_size;
/* Note that we include the NUL byte. */
size_t resultlen = strlen (result) + 1;
const unsigned char *inbuf = result;
unsigned char *outbuf = freemem;
size_t written;
int res;
__libc_lock_lock (lock);
while ((res = __gconv (domain->conv,
&inbuf, inbuf + resultlen,
&outbuf, outbuf + freemem_size,
&written)) == __GCONV_OK)
{
if (res != __GCONV_FULL_OUTPUT)
goto out;
/* We must resize the buffer. */
freemem_size = MAX (2 * freemem_size, 4064);
freemem = (char *) malloc (freemem_size);
if (freemem == NULL)
goto out;
inbuf = result;
outbuf = freemem;
}
/* We have now in our buffer a converted string. Put this
in the hash table */
domain->conv_tab[idx] = freemem;
freemem_size -= outbuf - freemem;
freemem = outbuf;
out:
__libc_lock_unlock (lock);
#endif
}
result = domain->conv_tab[idx];
}
return result;
}
while (1)
{
if (idx >= domain->hash_size - incr)
idx -= domain->hash_size - incr;
else
idx += incr;
nstr = W (domain->must_swap, domain->hash_tab[idx]);
if (nstr == 0)
/* Hash table entry is empty. */
return NULL;
if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) == len
&& (strcmp (msgid,
domain->data + W (domain->must_swap,
domain->orig_tab[nstr - 1].offset))
== 0))
return ((char *) domain->data
+ W (domain->must_swap,
domain->trans_tab[nstr - 1].offset));
}
/* NOTREACHED */
}
/* Now we try the default method: binary search in the sorted
array of messages. */
bottom = 0;
top = domain->nstrings;
while (bottom < top)
{
int cmp_val;
act = (bottom + top) / 2;
cmp_val = strcmp (msgid, (domain->data
+ W (domain->must_swap,
domain->orig_tab[act].offset)));
if (cmp_val < 0)
top = act;
else if (cmp_val > 0)
bottom = act + 1;
else
break;
}
/* If an translation is found return this. */
return bottom >= top ? NULL : ((char *) domain->data
+ W (domain->must_swap,
domain->trans_tab[act].offset));
}
/* Return string representation of locale CATEGORY. */
static const char *
internal_function
category_to_name (category)
int category;
{
const char *retval;
switch (category)
{
#ifdef LC_COLLATE
case LC_COLLATE:
retval = "LC_COLLATE";
break;
#endif
#ifdef LC_CTYPE
case LC_CTYPE:
retval = "LC_CTYPE";
break;
#endif
#ifdef LC_MONETARY
case LC_MONETARY:
retval = "LC_MONETARY";
break;
#endif
#ifdef LC_NUMERIC
case LC_NUMERIC:
retval = "LC_NUMERIC";
break;
#endif
#ifdef LC_TIME
case LC_TIME:
retval = "LC_TIME";
break;
#endif
#ifdef LC_MESSAGES
case LC_MESSAGES:
retval = "LC_MESSAGES";
break;
#endif
#ifdef LC_RESPONSE
case LC_RESPONSE:
retval = "LC_RESPONSE";
break;
#endif
#ifdef LC_ALL
case LC_ALL:
/* This might not make sense but is perhaps better than any other
value. */
retval = "LC_ALL";
break;
#endif
default:
/* If you have a better idea for a default value let me know. */
retval = "LC_XXX";
}
return retval;
}
/* Guess value of current locale from value of the environment variables. */
static const char *
internal_function
guess_category_value (category, categoryname)
int category;
const char *categoryname;
{
const char *retval;
/* The highest priority value is the `LANGUAGE' environment
variable. This is a GNU extension. */
retval = getenv ("LANGUAGE");
if (retval != NULL && retval[0] != '\0')
return retval;
/* `LANGUAGE' is not set. So we have to proceed with the POSIX
methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
systems this can be done by the `setlocale' function itself. */
#if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL
return setlocale (category, NULL);
#else
/* Setting of LC_ALL overwrites all other. */
retval = getenv ("LC_ALL");
if (retval != NULL && retval[0] != '\0')
return retval;
/* Next comes the name of the desired category. */
retval = getenv (categoryname);
if (retval != NULL && retval[0] != '\0')
return retval;
/* Last possibility is the LANG environment variable. */
retval = getenv ("LANG");
if (retval != NULL && retval[0] != '\0')
return retval;
/* We use C as the default domain. POSIX says this is implementation
defined. */
return "C";
#endif
}
/* @@ begin of epilog @@ */
/* We don't want libintl.a to depend on any other library. So we
avoid the non-standard function stpcpy. In GNU C Library this
function is available, though. Also allow the symbol HAVE_STPCPY
to be defined. */
#if !_LIBC && !HAVE_STPCPY
static char *
stpcpy (dest, src)
char *dest;
const char *src;
{
while ((*dest++ = *src++) != '\0')
/* Do nothing. */ ;
return dest - 1;
}
#endif
#if !_LIBC && !HAVE_MEMPCPY
static void *
mempcpy (dest, src, n)
void *dest;
const void *src;
size_t n;
{
return (void *) ((char *) memcpy (dst, src, n) + n);
}
#endif
#ifdef _LIBC
/* If we want to free all resources we have to do some work at
program's end. */
static void __attribute__ ((unused))
free_mem (void)
{
struct binding *runp;
for (runp = _nl_domain_bindings; runp != NULL; runp = runp->next)
{
free (runp->domainname);
if (runp->dirname != _nl_default_dirname)
/* Yes, this is a pointer comparison. */
free (runp->dirname);
}
if (_nl_current_default_domain != _nl_default_default_domain)
/* Yes, again a pointer comparison. */
free ((char *) _nl_current_default_domain);
/* Remove the search tree with the know translations. */
__tdestroy (root, free);
}
text_set_element (__libc_subfreeres, free_mem);
#endif

1000
intl/dcigettext.c Normal file

File diff suppressed because it is too large Load Diff

64
intl/dcngettext.c Normal file
View File

@ -0,0 +1,64 @@
/* Implementation of the dcngettext(3) function.
Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "gettext.h"
#include "gettextP.h"
#ifdef _LIBC
# include <libintl.h>
#else
# include "libgettext.h"
#endif
/* @@ end of prolog @@ */
/* Names for the libintl functions are a problem. They must not clash
with existing names and they should follow ANSI C. But this source
code is also used in GNU C Library where the names have a __
prefix. So we have to make a difference here. */
#ifdef _LIBC
# define DCNGETTEXT __dcngettext
# define DCIGETTEXT __dcigettext
#else
# define DCNGETTEXT dcngettext__
# define DCIGETTEXT dcigettext__
#endif
/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
locale. */
char *
DCNGETTEXT (domainname, msgid1, msgid2, n, category)
const char *domainname;
const char *msgid1;
const char *msgid2;
unsigned long int n;
int category;
{
return DCIGETTEXT (domainname, msgid1, msgid2, 1, n, category);
}
#ifdef _LIBC
/* Alias for function name in GNU C Library. */
weak_alias (__dcngettext, dcngettext);
#endif

67
intl/dngettext.c Normal file
View File

@ -0,0 +1,67 @@
/* Implementation of the dngettext(3) function.
Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#if defined HAVE_LOCALE_H || defined _LIBC
# include <locale.h>
#endif
#include "gettext.h"
#include "gettextP.h"
#ifdef _LIBC
# include <libintl.h>
#else
# include "libgettext.h"
#endif
/* @@ end of prolog @@ */
/* Names for the libintl functions are a problem. They must not clash
with existing names and they should follow ANSI C. But this source
code is also used in GNU C Library where the names have a __
prefix. So we have to make a difference here. */
#ifdef _LIBC
# define DNGETTEXT __dngettext
# define DCNGETTEXT __dcngettext
#else
# define DNGETTEXT dngettext__
# define DCNGETTEXT dcngettext__
#endif
/* Look up MSGID in the DOMAINNAME message catalog of the current
LC_MESSAGES locale and skip message according to the plural form. */
char *
DNGETTEXT (domainname, msgid1, msgid2, n)
const char *domainname;
const char *msgid1;
const char *msgid2;
unsigned long int n;
{
return DCNGETTEXT (domainname, msgid1, msgid2, n, LC_MESSAGES);
}
#ifdef _LIBC
/* Alias for function name in GNU C Library. */
weak_alias (__dngettext, dngettext);
#endif

View File

@ -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

View File

@ -1,5 +1,5 @@
/* Header describing internals of gettext library
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
Written by Ulrich Drepper <drepper@cygnus.com>, 1995.
The GNU C Library is free software; you can redistribute it and/or
@ -64,6 +64,51 @@ SWAP (i)
#endif
/* This is the representation of the expressions to determine the
plural form. */
struct expression
{
enum operator
{
var, /* The variable "n". */
num, /* Decimal number. */
mult, /* Multiplication. */
divide, /* Division. */
module, /* Module operation. */
plus, /* Addition. */
minus, /* Subtraction. */
equal, /* Comparision for equality. */
not_equal, /* Comparision for inequality. */
land, /* Logical AND. */
lor, /* Logical OR. */
qmop /* Question mark operator. */
} operation;
union
{
unsigned long int num; /* Number value for `num'. */
struct
{
struct expression *left; /* Left expression in binary operation. */
struct expression *right; /* Right expression in binary operation. */
} args2;
struct
{
struct expression *bexp; /* Boolean expression in ?: operation. */
struct expression *tbranch; /* True-branch in ?: operation. */
struct expression *fbranch; /* False-branch in ?: operation. */
} args3;
} val;
};
/* This is the data structure to pass information to the parser and get
the result in a thread-safe way. */
struct parse_args
{
const char *cp;
struct expression *res;
};
struct loaded_domain
{
const char *data;
@ -83,6 +128,9 @@ struct loaded_domain
# endif
#endif
char **conv_tab;
struct expression *plural;
unsigned long int nplurals;
};
struct binding
@ -103,6 +151,34 @@ void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain))
void _nl_unload_domain PARAMS ((struct loaded_domain *__domain))
internal_function;
#ifdef _LIBC
extern char *__ngettext PARAMS ((const char *msgid1, const char *msgid2,
unsigned long int n));
extern char *__dngettext PARAMS ((const char *domainname, const char *msgid1,
const char *msgid2, unsigned long int n));
extern char *__dcngettext PARAMS ((const char *domainname, const char *msgid1,
const char *msgid2, unsigned long int n,
int category));
extern char *__dcigettext PARAMS ((const char *domainname, const char *msgid1,
const char *msgid2, int plural,
unsigned long int n, int category));
#else
extern char *ngettext__ PARAMS ((const char *msgid1, const char *msgid2,
unsigned long int n));
extern char *dngettext__ PARAMS ((const char *domainname, const char *msgid1,
const char *msgid2, unsigned long int n));
extern char *dcngettext__ PARAMS ((const char *domainname, const char *msgid1,
const char *msgid2, unsigned long int n,
int category));
extern char *dcigettext__ PARAMS ((const char *domainname, const char *msgid1,
const char *msgid2, int plural,
unsigned long int n, int category));
#endif
extern int __gettextdebug;
extern void __gettext_free_exp (struct expression *exp) internal_function;
extern int __gettextparse (void *arg);
/* @@ begin of epilog @@ */
#endif /* gettextP.h */

View File

@ -1,6 +1,5 @@
/* Message catalogs for internationalization.
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
This file is derived from the file libgettext.h in the GNU gettext package.
This file is part of the GNU C Library. Its master source is NOT part of
@ -52,6 +51,23 @@ extern char *__dcgettext (__const char *__domainname,
__const char *__msgid, int __category) __THROW;
/* Similar to `gettext' but select the plural form corresponding to the
number N. */
extern char *ngettext (__const char *__msgid1, __const char *__msgid2,
unsigned long int __n) __THROW;
/* Similar to `dgettext' but select the plural form corresponding to the
number N. */
extern char *dngettext (__const char *__domainname, __const char *__msgid1,
__const char *__msgid2, unsigned long int __n) __THROW;
/* Similar to `dxgettext' but select the plural form corresponding to the
number N. */
extern char *dcngettext (__const char *__domainname, __const char *__msgid1,
__const char *__msgid2, unsigned long int __n,
int __category) __THROW;
/* Set the current default message catalog to DOMAINNAME.
If DOMAINNAME is null, return the current default.
If DOMAINNAME is "", reset to the default of "messages". */
@ -82,6 +98,11 @@ extern char *bindtextdomain (__const char *__domainname,
# define dgettext(domainname, msgid) \
dcgettext (domainname, msgid, LC_MESSAGES)
# define ngettext(msgid, n) dngettext (NULL, msgid, n)
# define dngettext(domainname, msgid, n) \
dcngettext (domainname, msgid, n, LC_MESSAGES)
#endif /* Optimizing. */
__END_DECLS

View File

@ -1,4 +1,4 @@
/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
/* Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
@ -89,7 +89,7 @@ extern char *_nl_find_language PARAMS ((const char *name));
extern char *_nl_find_msg PARAMS ((struct loaded_l10nfile *domain_file,
const char *msgid))
const char *msgid, unsigned long int index))
internal_function;
#endif /* loadinfo.h */

View File

@ -1,5 +1,5 @@
/* Load needed message catalogs.
Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
@ -23,6 +23,7 @@
# include <config.h>
#endif
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -82,6 +83,32 @@
cached by one of GCC's features. */
int _nl_msg_cat_cntr;
/* These structs are the constant expression for the germanic plural
form determination. */
static const struct expression plvar =
{
.operation = var,
};
static const struct expression plone =
{
.operation = num,
.val =
{
.num = 1
}
};
static struct expression germanic_plural =
{
.operation = not_equal,
.val =
{
.args2 = {
.left = (struct expression *) &plvar,
.right = (struct expression *) &plone
}
}
};
/* Load the message catalogs specified by FILENAME. If it is no valid
message catalog do nothing. */
@ -230,10 +257,12 @@ _nl_load_domain (domain_file)
domain->conv = (iconv_t) -1;
# endif
#endif
nullentry = _nl_find_msg (domain_file, "");
nullentry = _nl_find_msg (domain_file, "", 0);
if (nullentry != NULL)
{
char *charsetstr = strstr (nullentry, "charset=");
const char *charsetstr = strstr (nullentry, "charset=");
const char *plural;
const char *nplurals;
if (charsetstr != NULL)
{
@ -270,6 +299,42 @@ _nl_load_domain (domain_file)
# endif
#endif
}
/* Also look for a plural specification. */
plural = strstr (nullentry, "plural=");
nplurals = strstr (nullentry, "nplurals=");
if (plural == NULL || nplurals == NULL)
{
/* By default we are using the Germanic form: singular form only
for `one', the plural form otherwise. Yes, this is also what
English is using since English is a Germanic language. */
no_plural:
domain->plural = &germanic_plural;
domain->nplurals = 2;
}
else
{
/* First get the number. */
char *endp;
struct parse_args args;
nplurals += 9;
while (*nplurals != '\0' && isspace (*nplurals))
++nplurals;
domain->nplurals = strtoul (nplurals, &endp, 10);
if (nplurals == endp)
goto no_plural;
/* Due to the restrictions bison imposes onto the interface of the
scanner function we have to put the input string and the result
passed up from the parser into the same structure which address
is passed down to the parser. */
plural += 7;
args.cp = plural;
if (__gettextparse (&args) != 0)
goto no_plural;
domain->plural = args.res;
}
}
}
@ -280,6 +345,19 @@ internal_function
_nl_unload_domain (domain)
struct loaded_domain *domain;
{
if (domain->plural != &germanic_plural)
__gettext_free_exp (domain->plural);
#ifdef _LIBC
if (domain->conv != (__gconv_t) -1)
__gconv_close (domain->conv);
#else
# if HAVE_ICONV
if (domain->conv != (iconv_t) -1)
iconv_close (domain->conv);
# endif
#endif
#ifdef _POSIX_MAPPED_FILES
if (domain->use_mmap)
munmap ((caddr_t) domain->data, domain->mmap_size);

78
intl/ngettext.c Normal file
View File

@ -0,0 +1,78 @@
/* Implementation of ngettext(3) function.
Copyright (C) 1995, 1997, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library. Its master source is NOT part of
the C library, however.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef _LIBC
# define __need_NULL
# include <stddef.h>
#else
# ifdef STDC_HEADERS
# include <stdlib.h> /* Just for NULL. */
# else
# ifdef HAVE_STRING_H
# include <string.h>
# else
# define NULL ((void *) 0)
# endif
# endif
#endif
#include "gettext.h"
#include "gettextP.h"
#ifdef _LIBC
# include <libintl.h>
#else
# include "libgettext.h"
#endif
/* @@ end of prolog @@ */
/* Names for the libintl functions are a problem. They must not clash
with existing names and they should follow ANSI C. But this source
code is also used in GNU C Library where the names have a __
prefix. So we have to make a difference here. */
#ifdef _LIBC
# define NGETTEXT __ngettext
# define DCNGETTEXT __dcngettext
#else
# define NGETTEXT gettext__
# define DCNGETTEXT dcngettext__
#endif
/* Look up MSGID in the current default message catalog for the current
LC_MESSAGES locale. If not found, returns MSGID itself (the default
text). */
char *
NGETTEXT (msgid1, msgid2, n)
const char *msgid1;
const char *msgid2;
unsigned long int n;
{
return DCNGETTEXT (NULL, msgid1, msgid2, n, LC_MESSAGES);
}
#ifdef _LIBC
/* Alias for function name in GNU C Library. */
weak_alias (__ngettext, ngettext);
#endif

1218
intl/plural.c Normal file

File diff suppressed because it is too large Load Diff

290
intl/plural.y Normal file
View File

@ -0,0 +1,290 @@
%{
/* Expression parsing for plural form selection.
Copyright (C) 2000 Free Software Foundation, Inc.
Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <stdarg.h>
#include <stdlib.h>
#include "gettext.h"
#include "gettextP.h"
#define YYLEX_PARAM &((struct parse_args *) arg)->cp
#define YYPARSE_PARAM arg
%}
%pure_parser
%expect 10
%union {
unsigned long int num;
struct expression *exp;
}
%{
/* Prototypes for local functions. */
static struct expression *new_exp (enum operator op, ...);
static int yylex (YYSTYPE *lval, const char **pexp);
static void yyerror (const char *str);
%}
%left '?'
%left '|'
%left '&'
%left '=', '!'
%left '+', '-'
%left '*', '/', '%'
%token <num> NUMBER
%type <exp> exp
%%
start: exp
{
((struct parse_args *) arg)->res = $1;
}
;
exp: exp '?' exp ':' exp
{
if (($$ = new_exp (qmop, $1, $3, $5, NULL)) == NULL)
YYABORT
}
| exp '|' exp
{
if (($$ = new_exp (lor, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '&' exp
{
if (($$ = new_exp (land, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '=' exp
{
if (($$ = new_exp (equal, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '!' exp
{
if (($$ = new_exp (not_equal, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '+' exp
{
if (($$ = new_exp (plus, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '-' exp
{
if (($$ = new_exp (minus, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '*' exp
{
if (($$ = new_exp (mult, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '/' exp
{
if (($$ = new_exp (divide, $1, $3, NULL)) == NULL)
YYABORT
}
| exp '%' exp
{
if (($$ = new_exp (module, $1, $3, NULL)) == NULL)
YYABORT
}
| 'n'
{
if (($$ = new_exp (var, NULL)) == NULL)
YYABORT
}
| NUMBER
{
if (($$ = new_exp (num, NULL)) == NULL)
YYABORT;
$$->val.num = $1
}
| '(' exp ')'
{
$$ = $2
}
;
%%
static struct expression *
new_exp (enum operator op, ...)
{
struct expression *newp = (struct expression *) malloc (sizeof (*newp));
va_list va;
struct expression *next;
va_start (va, op);
if (newp == NULL)
while ((next = va_arg (va, struct expression *)) != NULL)
__gettext_free_exp (next);
else
{
newp->operation = op;
next = va_arg (va, struct expression *);
if (next != NULL)
{
newp->val.args3.bexp = next;
next = va_arg (va, struct expression *);
if (next != NULL)
{
newp->val.args3.tbranch = next;
next = va_arg (va, struct expression *);
if (next != NULL)
newp->val.args3.fbranch = next;
}
}
}
va_end (va);
return newp;
}
void
internal_function
__gettext_free_exp (struct expression *exp)
{
if (exp == NULL)
return;
/* Handle the recursive case. */
switch (exp->operation)
{
case qmop:
__gettext_free_exp (exp->val.args3.fbranch);
/* FALLTHROUGH */
case mult:
case divide:
case module:
case plus:
case minus:
case equal:
case not_equal:
case land:
case lor:
__gettext_free_exp (exp->val.args2.right);
__gettext_free_exp (exp->val.args2.left);
break;
default:
break;
}
free (exp);
}
static int
yylex (YYSTYPE *lval, const char **pexp)
{
const char *exp = *pexp;
int result;
while (1)
{
if (exp[0] == '\\' && exp[1] == '\n')
{
exp += 2;
continue;
}
if (exp[0] != '\0' && exp[0] != ' ' && exp[0] != '\t')
break;
++exp;
}
result = *exp++;
switch (result)
{
case '0' ... '9':
{
unsigned long int n = exp[-1] - '0';
while (exp[0] >= '0' && exp[0] <= '9')
{
n *= 10;
n += exp[0] - '0';
++exp;
}
lval->num = n;
result = NUMBER;
}
break;
case '=':
case '!':
if (exp[0] == '=')
++exp;
else
result = YYERRCODE;
break;
case '&':
case '|':
if (exp[0] == result)
++exp;
else
result = YYERRCODE;
break;
case 'n':
case '*':
case '/':
case '%':
case '+':
case '-':
case '?':
case ':':
case '(':
case ')':
/* Nothing, just return the character. */
break;
case '\n':
case '\0':
/* Be safe and let the user call this function again. */
--exp;
result = YYEOF;
break;
default:
result = YYERRCODE;
#if YYDEBUG != 0
--exp;
#endif
break;
}
*pexp = exp;
return result;
}
static void
yyerror (const char *str)
{
/* Do nothing. We don't print error messages here. */
}

70
intl/po2test.sed Normal file
View File

@ -0,0 +1,70 @@
# po2test.sed - Convert Uniforum style .po file to C code for testing.
# Copyright (C) 2000 Free Software Foundation, Inc.
# Ulrich Drepper <drepper@cygnus.com>, 2000.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
# We copy the original message as a comment into the .msg file. But enclose
# them with INPUT ( ).
#
/^msgid/ {
s/msgid[ ]*"\(.*\)"/INPUT ("\1")/
# Clear flag from last substitution.
tb
# Append the next line.
:b
N
# Look whether second part is a continuation line.
s/\(.*\)")\(\n\)"\(.*\)"/\1\\\2\3")/
# Yes, then branch.
ta
P
D
# Note that `D' includes a jump to the start!!
# We found a continuation line. But before printing insert '\'.
:a
s/\(.*\)")\(\n.*\)/\1\\\2/
P
# We cannot use the sed command `D' here
s/.*\n\(.*\)/\1/
tb
}
#
# Copy the translations as well and enclose them with OUTPUT ( ).
#
/^msgstr/ {
s/msgstr[ ]*"\(.*\)"/OUTPUT ("\1")/
# Clear flag from last substitution.
tb
# Append the next line.
:b
N
# Look whether second part is a continuation line.
s/\(.*\)")\(\n\)"\(.*\)"/\1\\\2\3")/
# Yes, then branch.
ta
P
D
# Note that `D' includes a jump to the start!!
# We found a continuation line. But before printing insert '\'.
:a
s/\(.*\)")\(\n.*\)/\1\\\2/
P
# We cannot use the sed command `D' here
s/.*\n\(.*\)/\1/
tb
}
d

322
intl/tst-gettext.c Normal file
View File

@ -0,0 +1,322 @@
/* Test of the gettext functions.
Copyright (C) 2000 Free Software Foundation, Inc.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 2000.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const struct
{
const char *msgid;
const char *msgstr;
} msgs[] =
{
#define INPUT(Str) { Str,
#define OUTPUT(Str) Str },
#include TESTSTRS_H
};
const char *catname[] =
{
[LC_MESSAGES] = "LC_MESSAGES",
[LC_TIME] = "LC_TIME",
[LC_NUMERIC] = "LC_NUMERIC"
};
static int positive_gettext_test (void);
static int negative_gettext_test (void);
static int positive_dgettext_test (const char *domain);
static int positive_dcgettext_test (const char *domain, int category);
static int negative_dcgettext_test (const char *domain, int category);
int
main (int argc, char *argv[])
{
int result = 0;
/* This is the place where the .mo files are placed. */
if (argc > 1)
{
bindtextdomain ("existing-domain", argv[1]);
bindtextdomain ("existing-time-domain", argv[1]);
bindtextdomain ("non-existing-domain", argv[1]);
}
/* The locale the catalog is created for is "existing-category". Now
set the various variables in question to this value and run the
test. */
setenv ("LANGUAGE", "existing-locale", 1);
setenv ("LC_ALL", "non-existing-locale", 1);
setenv ("LC_MESSAGES", "non-existing-locale", 1);
setenv ("LANG", "non-existing-locale", 1);
/* This is the name of the existing domain with a catalog for the
LC_MESSAGES category. */
textdomain ("existing-domain");
puts ("test `gettext' with LANGUAGE set");
if (positive_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
/* This is the name of a non-existing domain with a catalog for the
LC_MESSAGES category. We leave this value set for the `dgettext'
and `dcgettext' tests. */
textdomain ("non-existing-domain");
puts ("test `gettext' with LANGUAGE set");
if (negative_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
puts ("test `dgettext' with LANGUAGE set");
if (positive_dgettext_test ("existing-domain") != 0)
{
puts ("FAILED");
result = 1;
}
/* Now the same tests with LC_ALL deciding. */
unsetenv ("LANGUAGE");
setenv ("LC_ALL", "existing-locale", 1);
puts ("test `gettext' with LC_ALL set");
/* This is the name of the existing domain with a catalog for the
LC_MESSAGES category. */
textdomain ("existing-domain");
if (positive_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
/* This is the name of a non-existing domain with a catalog for the
LC_MESSAGES category. We leave this value set for the `dgettext'
and `dcgettext' tests. */
textdomain ("non-existing-domain");
puts ("test `gettext' with LANGUAGE set");
if (negative_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
puts ("test `dgettext' with LANGUAGE set");
if (positive_dgettext_test ("existing-domain") != 0)
{
puts ("FAILED");
result = 1;
}
/* Now the same tests with LC_MESSAGES deciding. */
unsetenv ("LC_ALL");
setenv ("LC_MESSAGES", "existing-locale", 1);
setenv ("LC_TIME", "existing-locale", 1);
setenv ("LC_NUMERIC", "non-existing-locale", 1);
puts ("test `gettext' with LC_ALL set");
/* This is the name of the existing domain with a catalog for the
LC_MESSAGES category. */
textdomain ("existing-domain");
if (positive_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
/* This is the name of a non-existing domain with a catalog for the
LC_MESSAGES category. We leave this value set for the `dgettext'
and `dcgettext' tests. */
textdomain ("non-existing-domain");
puts ("test `gettext' with LANGUAGE set");
if (negative_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
puts ("test `dgettext' with LANGUAGE set");
if (positive_dgettext_test ("existing-domain") != 0)
{
puts ("FAILED");
result = 1;
}
puts ("test `dcgettext' with LANGUAGE set (LC_MESSAGES)");
if (positive_dcgettext_test ("existing-domain", LC_MESSAGES) != 0)
{
puts ("FAILED");
result = 1;
}
/* Try a different category. For this we also switch the domain. */
puts ("test `dcgettext' with LANGUAGE set (LC_TIME)");
if (positive_dcgettext_test ("existing-time-domain", LC_TIME) != 0)
{
puts ("FAILED");
result = 1;
}
/* This time use a category for which there is no catalog. */
puts ("test `dcgettext' with LANGUAGE set (LC_NUMERIC)");
if (negative_dcgettext_test ("existing-domain", LC_NUMERIC) != 0)
{
puts ("FAILED");
result = 1;
}
/* Now the same tests with LANG deciding. */
unsetenv ("LC_MESSAGES");
setenv ("LANG", "existing-locale", 1);
/* This is the name of the existing domain with a catalog for the
LC_MESSAGES category. */
textdomain ("existing-domain");
puts ("test `gettext' with LC_ALL set");
if (positive_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
/* This is the name of a non-existing domain with a catalog for the
LC_MESSAGES category. We leave this value set for the `dgettext'
and `dcgettext' tests. */
textdomain ("non-existing-domain");
puts ("test `gettext' with LANGUAGE set");
if (negative_gettext_test () != 0)
{
puts ("FAILED");
result = 1;
}
puts ("test `dgettext' with LANGUAGE set");
if (positive_dgettext_test ("existing-domain") != 0)
{
puts ("FAILED");
result = 1;
}
return result;
}
static int
positive_gettext_test (void)
{
size_t cnt;
int result = 0;
for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
{
const char *found = gettext (msgs[cnt].msgid);
if (found == NULL || strcmp (found, msgs[cnt].msgstr) != 0)
{
/* Oops, shouldn't happen. */
printf (" gettext (\"%s\") failed, returned \"%s\"\n",
msgs[cnt].msgid, found);
result = 1;
}
}
return result;
}
static int
negative_gettext_test (void)
{
size_t cnt;
int result = 0;
for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
{
const char *found = gettext (msgs[cnt].msgid);
if (found != msgs[cnt].msgid)
{
/* Oops, shouldn't happen. */
printf (" gettext (\"%s\") failed\n", msgs[cnt].msgid);
result = 1;
}
}
return result;
}
static int
positive_dgettext_test (const char *domain)
{
size_t cnt;
int result = 0;
for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
{
const char *found = dgettext (domain, msgs[cnt].msgid);
if (found == NULL || strcmp (found, msgs[cnt].msgstr) != 0)
{
/* Oops, shouldn't happen. */
printf (" dgettext (\"%s\", \"%s\") failed, returned \"%s\"\n",
domain, msgs[cnt].msgid, found);
result = 1;
}
}
return result;
}
static int
positive_dcgettext_test (const char *domain, int category)
{
size_t cnt;
int result = 0;
for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
{
const char *found = dcgettext (domain, msgs[cnt].msgid, category);
if (found == NULL || strcmp (found, msgs[cnt].msgstr) != 0)
{
/* Oops, shouldn't happen. */
printf (" dcgettext (\"%s\", \"%s\", %s) failed, returned \"%s\"\n",
domain, msgs[cnt].msgid, catname[category], found);
result = 1;
}
}
return result;
}
static int
negative_dcgettext_test (const char *domain, int category)
{
size_t cnt;
int result = 0;
for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
{
const char *found = dcgettext (domain, msgs[cnt].msgid, category);
if (found != msgs[cnt].msgid)
{
/* Oops, shouldn't happen. */
printf (" dcgettext (\"%s\", \"%s\", %s) failed\n",
domain, msgs[cnt].msgid, catname[category]);
result = 1;
}
}
return result;
}

41
intl/tst-gettext.sh Executable file
View File

@ -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 $?