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/dcigettext.c | |
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/dcigettext.c')
-rw-r--r-- | intl/dcigettext.c | 259 |
1 files changed, 132 insertions, 127 deletions
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) { |