diff options
author | Bruno Haible <bruno@clisp.org> | 2001-01-12 17:00:31 +0000 |
---|---|---|
committer | Bruno Haible <bruno@clisp.org> | 2001-01-12 17:00:31 +0000 |
commit | 321a5de3ac9fa6888f7229f44c317a2d3b3eb9f3 (patch) | |
tree | bd8c4bc9dc94348ca9315698acc18f7e0d8643a1 /intl | |
parent | b479432a465fabfc80e6edfbd5a79b70987c283c (diff) | |
download | external_gettext-321a5de3ac9fa6888f7229f44c317a2d3b3eb9f3.zip external_gettext-321a5de3ac9fa6888f7229f44c317a2d3b3eb9f3.tar.gz external_gettext-321a5de3ac9fa6888f7229f44c317a2d3b3eb9f3.tar.bz2 |
Plural form handling.
Diffstat (limited to 'intl')
-rw-r--r-- | intl/ChangeLog | 27 | ||||
-rw-r--r-- | intl/dcigettext.c | 259 | ||||
-rw-r--r-- | intl/intl-compat.c | 38 | ||||
-rw-r--r-- | intl/intlh.inst.in | 10 | ||||
-rw-r--r-- | intl/libgettext.h | 18 | ||||
-rw-r--r-- | intl/loadinfo.h | 2 | ||||
-rw-r--r-- | intl/loadmsgcat.c | 3 |
7 files changed, 217 insertions, 140 deletions
diff --git a/intl/ChangeLog b/intl/ChangeLog index 2521469..1fc4033 100644 --- a/intl/ChangeLog +++ b/intl/ChangeLog @@ -1,3 +1,30 @@ +2001-01-01 Bruno Haible <haible@clisp.cons.org> + + Finish implementation of plural form handling. + * dcigettext.c (known_translation_t): Rename 'domain' field to + 'domainname'. Remove 'plindex' field. Add 'domain' and + 'translation_length' fields. + (transcmp): Don't compare 'plindex' fields. + (plural_lookup): New function. + (DCIGETTEXT): Change cache handing in the plural case. Don't call + plural_eval before the translation and its catalog file have been + found. Remove plindex from cache key. Add 'translation_length' and + 'domain' to cache result. + (_nl_find_msg): Remove index argument, return length of translation + to the caller instead. Weaken comparison of string lengths, to account + for plural entries. Call iconv() on the entire result string, not + only on the portion needed so far. + * loadinfo.h (_nl_find_msg): Remove index argument, add lengthp + argument. + * loadmsgcat.c (_nl_load_domain): Adapt to _nl_find_msg change. + + * intl-compat.c (dcngettext, dngettext, ngettext): New functions. + * libgettext.h (ngettext__, dngettext__, dcngettext__): New + declarations. + (ngettext, dngettext): Add missing macro argument. + + * intlh.inst.in (ngettext, dngettext): Add missing macro argument. + 2000-12-31 Bruno Haible <haible@clisp.cons.org> * gettextP.h (ZERO): New macro. diff --git a/intl/dcigettext.c b/intl/dcigettext.c index 5dcb90a..fd50bd7 100644 --- a/intl/dcigettext.c +++ b/intl/dcigettext.c @@ -1,5 +1,5 @@ /* Implementation of the internal dcigettext function. - Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc. + Copyright (C) 1995-1999, 2000, 2001 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 @@ -203,10 +203,7 @@ static void *mempcpy PARAMS ((void *dest, const void *src, size_t n)); struct known_translation_t { /* Domain in which to search. */ - char *domain; - - /* Plural index. */ - unsigned long int plindex; + char *domainname; /* The category. */ int category; @@ -214,8 +211,12 @@ struct known_translation_t /* State of the catalog counter at the point the string was found. */ int counter; + /* Catalog where the string was found. */ + struct loaded_l10nfile *domain; + /* And finally the translation. */ const char *translation; + size_t translation_length; /* Pointer to the string in question. */ char msgid[ZERO]; @@ -243,16 +244,12 @@ transcmp (const void *p1, const void *p2) result = strcmp (s1->msgid, s2->msgid); if (result == 0) { - result = strcmp (s1->domain, s2->domain); + result = strcmp (s1->domainname, s2->domainname); 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; - } + /* 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; @@ -274,8 +271,14 @@ const char _nl_default_dirname[] = LOCALEDIR; 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 char *plural_lookup PARAMS ((struct loaded_l10nfile *domain, + unsigned long int n, + const char *translation, + size_t translation_len)) + internal_function; +static unsigned long int plural_eval PARAMS ((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)) @@ -384,6 +387,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) char *dirname, *xdomainname; char *single_locale; char *retval; + size_t retlen; int saved_errno; #if defined HAVE_TSEARCH || defined _LIBC struct known_translation_t *search; @@ -407,23 +411,26 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) #if defined HAVE_TSEARCH || defined _LIBC msgid_len = strlen (msgid1) + 1; - if (plural == 0) + /* Try to find the translation among those which we found at + some time. */ + search = (struct known_translation_t *) + alloca (offsetof (struct known_translation_t, msgid) + msgid_len); + memcpy (search->msgid, msgid1, msgid_len); + search->domainname = (char *) domainname; + search->category = category; + + foundp = (struct known_translation_t **) tfind (search, &root, transcmp); + if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr) { - /* 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) - { - __libc_rwlock_unlock (_nl_state_lock); - return (char *) (*foundp)->translation; - } + /* Now deal with plural. */ + if (plural) + retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation, + (*foundp)->translation_length); + else + retval = (char *) (*foundp)->translation; + + __libc_rwlock_unlock (_nl_state_lock); + return retval; } #endif @@ -558,39 +565,7 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) if (domain != NULL) { - unsigned long int index = 0; - - if (plural != 0) - { - struct loaded_domain *domaindata = - (struct loaded_domain *) domain->data; - index = plural_eval (domaindata->plural, n); - if (index >= domaindata->nplurals) - /* This should never happen. It means the plural expression - and the given maximum value do not match. */ - index = 0; - -#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, msgid1, msgid_len); - search->domain = (char *) domainname; - search->plindex = index; - search->category = category; - - foundp = (struct known_translation_t **) tfind (search, &root, - transcmp); - if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr) - { - __libc_rwlock_unlock (_nl_state_lock); - return (char *) (*foundp)->translation; - } -#endif - } - - retval = _nl_find_msg (domain, msgid1, index); + retval = _nl_find_msg (domain, msgid1, &retlen); if (retval == NULL) { @@ -599,15 +574,20 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) for (cnt = 0; domain->successor[cnt] != NULL; ++cnt) { retval = _nl_find_msg (domain->successor[cnt], msgid1, - index); + &retlen); if (retval != NULL) - break; + { + domain = domain->successor[cnt]; + break; + } } } if (retval != NULL) { + /* Found the translation of MSGID1 in domain DOMAIN: + starting at RETVAL, RETLEN bytes. */ FREE_BLOCKS (block_list); __set_errno (saved_errno); #if defined HAVE_TSEARCH || defined _LIBC @@ -621,12 +601,14 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) + msgid_len + domainname_len + 1); if (newp != NULL) { - newp->domain = mempcpy (newp->msgid, msgid1, msgid_len); - memcpy (newp->domain, domainname, domainname_len + 1); - newp->plindex = index; + newp->domainname = + mempcpy (newp->msgid, msgid1, msgid_len); + memcpy (newp->domainname, domainname, domainname_len + 1); newp->category = category; newp->counter = _nl_msg_cat_cntr; + newp->domain = domain; newp->translation = retval; + newp->translation_length = retlen; /* Insert the entry in the search tree. */ foundp = (struct known_translation_t **) @@ -641,9 +623,15 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) { /* We can update the existing entry. */ (*foundp)->counter = _nl_msg_cat_cntr; + (*foundp)->domain = domain; (*foundp)->translation = retval; + (*foundp)->translation_length = retlen; } #endif + /* Now deal with plural. */ + if (plural) + retval = plural_lookup (domain, n, retval, retlen); + __libc_rwlock_unlock (_nl_state_lock); return retval; } @@ -655,14 +643,15 @@ DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category) char * internal_function -_nl_find_msg (domain_file, msgid, index) +_nl_find_msg (domain_file, msgid, lengthp) struct loaded_l10nfile *domain_file; const char *msgid; - unsigned long int index; + size_t *lengthp; { struct loaded_domain *domain; size_t act; char *result; + size_t resultlen; if (domain_file->decided == 0) _nl_load_domain (domain_file); @@ -686,17 +675,21 @@ _nl_find_msg (domain_file, msgid, index) /* 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) - { - act = nstr - 1; - goto found; - } - while (1) { + /* Compare msgid with the original string at index nstr-1. + We compare the lengths with >=, not ==, because plural entries + are represented by strings with an embedded NUL. */ + 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)) + { + act = nstr - 1; + goto found; + } + if (idx >= domain->hash_size - incr) idx -= domain->hash_size - incr; else @@ -706,16 +699,6 @@ _nl_find_msg (domain_file, msgid, index) 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)) - { - act = nstr - 1; - goto found; - } } /* NOTREACHED */ } @@ -751,6 +734,7 @@ _nl_find_msg (domain_file, msgid, index) string to use a different character set, this is the time. */ result = ((char *) domain->data + W (domain->must_swap, domain->trans_tab[act].offset)); + resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1; #if defined _LIBC || HAVE_ICONV if ( @@ -767,9 +751,10 @@ _nl_find_msg (domain_file, msgid, index) appropriate table with the same structure as the table of translations in the file, where we can put the pointers to the converted strings in. - There is a slight complication with the INDEX: We don't know - a priori which entries are plural entries. Therefore at any - moment we can only translate the variants 0 .. INDEX. */ + There is a slight complication with plural entries. They + are represented by consecutive NUL terminated strings. We + handle this case by converting RESULTLEN bytes, including + NULs. */ if (domain->conv_tab == NULL && ((domain->conv_tab = (char **) calloc (domain->nstrings, @@ -782,8 +767,7 @@ _nl_find_msg (domain_file, msgid, index) /* Nothing we can do, no more memory. */ goto converted; - if (domain->conv_tab[act] == NULL - || *(nls_uint32 *) domain->conv_tab[act] < index) + if (domain->conv_tab[act] == NULL) { /* We haven't used this string so far, so it is not translated yet. Do this now. */ @@ -795,7 +779,6 @@ _nl_find_msg (domain_file, msgid, index) static unsigned char *freemem; static size_t freemem_size; - size_t resultlen; const unsigned char *inbuf; unsigned char *outbuf; int malloc_count; @@ -803,21 +786,10 @@ _nl_find_msg (domain_file, msgid, index) transmem_block_t *transmem_list = NULL; # endif - /* Note that we translate (index + 1) consecutive strings at - once, including the final NUL byte. */ - { - unsigned long int i = index; - char *p = result; - do - p += strlen (p) + 1; - while (i-- > 0); - resultlen = p - result; - } - __libc_lock_lock (lock); inbuf = result; - outbuf = freemem + sizeof (nls_uint32); + outbuf = freemem + sizeof (size_t); malloc_count = 0; while (1) @@ -827,13 +799,13 @@ _nl_find_msg (domain_file, msgid, index) size_t non_reversible; int res; - if (freemem_size < sizeof (nls_uint32)) + if (freemem_size < sizeof (size_t)) goto resize_freemem; res = __gconv (domain->conv, &inbuf, inbuf + resultlen, &outbuf, - outbuf + freemem_size - sizeof (nls_uint32), + outbuf + freemem_size - sizeof (size_t), &non_reversible); if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT) @@ -853,10 +825,10 @@ _nl_find_msg (domain_file, msgid, index) char *outptr = (char *) outbuf; size_t outleft; - if (freemem_size < sizeof (nls_uint32)) + if (freemem_size < sizeof (size_t)) goto resize_freemem; - outleft = freemem_size - sizeof (nls_uint32); + outleft = freemem_size - sizeof (size_t); if (iconv (domain->conv, &inptr, &inleft, &outptr, &outleft) != (size_t) (-1)) { @@ -918,25 +890,26 @@ _nl_find_msg (domain_file, msgid, index) freemem = newmem; # endif - outbuf = freemem + sizeof (nls_uint32); + outbuf = freemem + sizeof (size_t); } /* We have now in our buffer a converted string. Put this into the table of conversions. */ - *(nls_uint32 *) freemem = index; + *(size_t *) freemem = outbuf - freemem - sizeof (size_t); domain->conv_tab[act] = freemem; /* Shrink freemem, but keep it aligned. */ freemem_size -= outbuf - freemem; freemem = outbuf; - freemem += freemem_size & (alignof (nls_uint32) - 1); - freemem_size = freemem_size & ~ (alignof (nls_uint32) - 1); + freemem += freemem_size & (alignof (size_t) - 1); + freemem_size = freemem_size & ~ (alignof (size_t) - 1); __libc_lock_unlock (lock); } - /* Now domain->conv_tab[act] contains the translation of at least - the variants 0 .. INDEX. */ - result = domain->conv_tab[act] + sizeof (nls_uint32); + /* Now domain->conv_tab[act] contains the translation of all + the plural variants. */ + result = domain->conv_tab[act] + sizeof (size_t); + resultlen = *(size_t *) domain->conv_tab[act]; } converted: @@ -944,26 +917,58 @@ _nl_find_msg (domain_file, msgid, index) #endif /* _LIBC || HAVE_ICONV */ - /* Now skip some strings. How much depends on the index passed in. */ + *lengthp = resultlen; + return result; +} + + +/* Look up a plural variant. */ +static char * +internal_function +plural_lookup (domain, n, translation, translation_len) + struct loaded_l10nfile *domain; + unsigned long int n; + const char *translation; + size_t translation_len; +{ + struct loaded_domain *domaindata = (struct loaded_domain *) domain->data; + unsigned long int index; + const char *p; + + index = plural_eval (domaindata->plural, n); + if (index >= domaindata->nplurals) + /* This should never happen. It means the plural expression and the + given maximum value do not match. */ + index = 0; + + /* Skip INDEX strings at TRANSLATION. */ + p = translation; while (index-- > 0) { #ifdef _LIBC - result = __rawmemchr (result, '\0'); + p = __rawmemchr (p, '\0'); #else - result = strchr (result, '\0'); + p = strchr (p, '\0'); #endif /* And skip over the NUL byte. */ - ++result; - } + p++; - return result; + if (p >= translation + translation_len) + /* This should never happen. It means the plural expression + evaluated to a value larger than the number of variants + available for MSGID1. */ + return (char *) translation; + } + return (char *) p; } /* 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) +plural_eval (pexp, n) + struct expression *pexp; + unsigned long int n; { switch (pexp->operation) { diff --git a/intl/intl-compat.c b/intl/intl-compat.c index c6aeb46..299773b 100644 --- a/intl/intl-compat.c +++ b/intl/intl-compat.c @@ -1,6 +1,6 @@ /* intl-compat.c - Stub functions to call gettext functions from GNU gettext Library. - Copyright (C) 1995, 2000 Software Foundation, Inc. + Copyright (C) 1995, 2000, 2001 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 @@ -28,6 +28,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #undef gettext #undef dgettext #undef dcgettext +#undef ngettext +#undef dngettext +#undef dcngettext #undef textdomain #undef bindtextdomain #undef bind_textdomain_codeset @@ -79,6 +82,39 @@ gettext (msgid) char * +dcngettext (domainname, msgid1, msgid2, n, category) + const char *domainname; + const char *msgid1; + const char *msgid2; + unsigned long int n; + int category; +{ + return dcngettext__ (domainname, msgid1, msgid2, n, category); +} + + +char * +dngettext (domainname, msgid1, msgid2, n) + const char *domainname; + const char *msgid1; + const char *msgid2; + unsigned long int n; +{ + return dngettext__ (domainname, msgid1, msgid2, n); +} + + +char * +ngettext (msgid1, msgid2, n) + const char *msgid1; + const char *msgid2; + unsigned long int n; +{ + return ngettext__ (msgid1, msgid2, n); +} + + +char * textdomain (domainname) const char *domainname; { diff --git a/intl/intlh.inst.in b/intl/intlh.inst.in index 8a30511..fe8281f 100644 --- a/intl/intlh.inst.in +++ b/intl/intlh.inst.in @@ -1,5 +1,5 @@ /* Message catalogs for internationalization. - Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc. + Copyright (C) 1995-1997, 2000, 2001 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 @@ -95,11 +95,11 @@ extern char *bind_textdomain_codeset PARAMS ((const char *__domainname, # define dgettext(domainname, msgid) \ dcgettext (domainname, msgid, LC_MESSAGES) -# define ngettext(msgid, N) \ - dngettext (NULL, msgid, N) +# define ngettext(msgid1, Msgid2, N) \ + dngettext (NULL, msgid1, Msgid2, N) -# define dngettext(domainname, msgid, n) \ - dcngettext (domainname, msgid, n, LC_MESSAGES) +# define dngettext(domainname, msgid1, Msgid2, n) \ + dcngettext (domainname, msgid1, Msgid2, n, LC_MESSAGES) #endif /* Optimizing. */ diff --git a/intl/libgettext.h b/intl/libgettext.h index 58c83e6..ff453ec 100644 --- a/intl/libgettext.h +++ b/intl/libgettext.h @@ -1,5 +1,5 @@ /* Message catalogs for internationalization. - Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. + Copyright (C) 1995-1998, 2000, 2001 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 @@ -112,17 +112,25 @@ extern char *dcgettext__ PARAMS ((const char *__domainname, number N. */ extern char *ngettext PARAMS ((const char *__msgid1, const char *__msgid2, unsigned long int __n)); +extern char *ngettext__ PARAMS ((const char *__msgid1, const char *__msgid2, + unsigned long int __n)); /* Similar to `dgettext' but select the plural form corresponding to the number N. */ extern char *dngettext PARAMS ((const char *__domainname, 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)); /* Similar to `dcgettext' but select the plural form corresponding to the number N. */ extern char *dcngettext PARAMS ((const char *__domainname, const char *__msgid1, const char *__msgid2, unsigned long int __n, int __category)); +extern char *dcngettext__ PARAMS ((const char *__domainname, + const char *__msgid1, const char *__msgid2, + unsigned long int __n, int __category)); /* Set the current default message catalog to DOMAINNAME. @@ -158,11 +166,11 @@ extern char *bind_textdomain_codeset__ PARAMS ((const char *__domainname, # define dgettext(Domainname, Msgid) \ dcgettext (Domainname, Msgid, LC_MESSAGES) -# define ngettext(Msgid, N) \ - dngettext (NULL, Msgid, N) +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (NULL, Msgid1, Msgid2, N) -# define dngettext(Domainname, Msgid, N) \ - dcngettext (Domainname, Msgid, N, LC_MESSAGES) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + dcngettext (Domainname, Msgid1, Msgid2, N, LC_MESSAGES) # endif diff --git a/intl/loadinfo.h b/intl/loadinfo.h index 3942763..3218450 100644 --- a/intl/loadinfo.h +++ b/intl/loadinfo.h @@ -100,7 +100,7 @@ extern char *_nl_find_language PARAMS ((const char *name)); extern char *_nl_find_msg PARAMS ((struct loaded_l10nfile *domain_file, - const char *msgid, unsigned long int index)) + const char *msgid, size_t *lengthp)) internal_function; #endif /* loadinfo.h */ diff --git a/intl/loadmsgcat.c b/intl/loadmsgcat.c index a679de4..6324553 100644 --- a/intl/loadmsgcat.c +++ b/intl/loadmsgcat.c @@ -163,6 +163,7 @@ _nl_load_domain (domain_file) int use_mmap = 0; struct loaded_domain *domain; char *nullentry; + size_t nullentrylen; domain_file->decided = 1; domain_file->data = NULL; @@ -306,7 +307,7 @@ _nl_load_domain (domain_file) # endif #endif domain->conv_tab = NULL; - nullentry = _nl_find_msg (domain_file, "", 0); + nullentry = _nl_find_msg (domain_file, "", &nullentrylen); if (nullentry != NULL) { #if defined _LIBC || HAVE_ICONV |