diff --git a/lib/libintl/Makefile b/lib/libintl/Makefile index 7b42f2723cec..61f91dc3bb27 100644 --- a/lib/libintl/Makefile +++ b/lib/libintl/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.2 2000/11/02 10:24:48 itojun Exp $ +# $NetBSD: Makefile,v 1.3 2004/01/18 08:40:40 yamt Exp $ .include LIB= intl -SRCS= gettext.c textdomain.c gettext_dummy.c +SRCS= gettext.c textdomain.c gettext_iconv.c gettext_dummy.c INCS= libintl.h INCSDIR=/usr/include diff --git a/lib/libintl/gettext.c b/lib/libintl/gettext.c index fbe0f5742d73..cfe66eb7fa19 100644 --- a/lib/libintl/gettext.c +++ b/lib/libintl/gettext.c @@ -1,4 +1,4 @@ -/* $NetBSD: gettext.c,v 1.17 2004/01/05 19:21:00 itojun Exp $ */ +/* $NetBSD: gettext.c,v 1.18 2004/01/18 08:40:40 yamt Exp $ */ /*- * Copyright (c) 2000, 2001 Citrus Project, @@ -29,7 +29,7 @@ */ #include -__RCSID("$NetBSD: gettext.c,v 1.17 2004/01/05 19:21:00 itojun Exp $"); +__RCSID("$NetBSD: gettext.c,v 1.18 2004/01/18 08:40:40 yamt Exp $"); #include #include @@ -60,7 +60,7 @@ static int unmapit __P((struct domainbinding *)); static const char *lookup_hash __P((const char *, struct domainbinding *)); static const char *lookup_bsearch __P((const char *, struct domainbinding *)); static const char *lookup __P((const char *, struct domainbinding *)); -static const char *get_lang_env(const char *); +static const char *get_lang_env __P((const char *)); /* * shortcut functions. the main implementation resides in dcngettext(). @@ -631,11 +631,13 @@ found: v = lookup(msgid, db); if (v) { /* - * XXX call iconv() here, if translated text is encoded - * differently from currently-selected encoding (locale). - * look at Content-type header in *.mo file, in string obtained - * by gettext(""). + * convert the translated message's encoding. + * + * special case: + * a result of gettext("") shouldn't need any conversion. */ + if (msgid[0]) + v = __gettext_iconv(v, db); /* * Given the amount of printf-format security issues, it may diff --git a/lib/libintl/gettext_iconv.c b/lib/libintl/gettext_iconv.c new file mode 100644 index 000000000000..991eb06e6d35 --- /dev/null +++ b/lib/libintl/gettext_iconv.c @@ -0,0 +1,201 @@ +/* $NetBSD: gettext_iconv.c,v 1.1 2004/01/18 08:40:40 yamt Exp $ */ + +/*- + * Copyright (c) 2004 Citrus Project, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Citrus$ + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "libintl_local.h" + +struct cache { + const char *c_origmsg; + const char *c_resultmsg; +}; + +static const struct cache *cache_find(const char *, struct domainbinding *); +static int cache_enter(const char *, const char *); +static int cache_cmp(const void *, const void *); + +static void *cacheroot; + +static const struct cache * +cache_find(const char *msg, struct domainbinding *db) +{ + struct cache key; + struct cache **c; + + key.c_origmsg = msg; + c = tfind(&key, &cacheroot, cache_cmp); + + return c ? *c : NULL; +} + +static int +cache_enter(const char *origmsg, const char *resultmsg) +{ + struct cache *c; + + c = malloc(sizeof(*c)); + if (c == NULL) + return -1; + + c->c_origmsg = origmsg; + c->c_resultmsg = resultmsg; + + if (tsearch(c, &cacheroot, cache_cmp) == NULL) { + free(c); + return -1; + } + + return 0; +} + +static int +cache_cmp(const void *va, const void *vb) +{ + const struct cache *a = va; + const struct cache *b = vb; + int result; + + result = a->c_origmsg - b->c_origmsg; + + return result; +} + +#define GETTEXT_ICONV_MALLOC_CHUNK (16*1024) + +const char * +__gettext_iconv(const char *origmsg, struct domainbinding *db) +{ + const char *tocode; + const char *fromcode = db->mohandle.mo.mo_charset; + const struct cache *cache; + const char *result; + iconv_t cd; + const char *src; + char *dst; + size_t origlen; + size_t srclen; + size_t dstlen; + size_t nvalid; + int savederrno = errno; + + static char *buffer; + static size_t bufferlen; + + tocode = db->codeset; + if (tocode == NULL) { + /* + * convert to current LC_MESSAGE's codeset. + * + * XXX maybe wrong; it can mismatch with + * environment variable setting. + */ + tocode = nl_langinfo(CODESET); + } + + /* + * shortcut if possible. + * XXX should handle aliases + */ + if (!strcasecmp(tocode, fromcode)) + return origmsg; + + /* XXX LOCK */ + + /* XXX should detect change of tocode and purge caches? */ + + /* + * see if we have already converted this message. + */ + cache = cache_find(origmsg, db); + if (cache) { + result = cache->c_resultmsg; + goto out; + } + + origlen = strlen(origmsg) + 1; +again: + cd = iconv_open(tocode, fromcode); + if (cd == (iconv_t)-1) { + result = origmsg; + goto out; + } + + src = origmsg; + srclen = origlen; + dst = buffer; + dstlen = bufferlen; + nvalid = iconv(cd, &src, &srclen, &dst, &dstlen); + iconv_close(cd); + + if (nvalid == (size_t)-1) { + /* + * try to allocate a new buffer. + * + * just give up if GETTEXT_ICONV_MALLOC_CHUNK was not enough. + */ + if (errno == E2BIG && + bufferlen != GETTEXT_ICONV_MALLOC_CHUNK) { + buffer = malloc(GETTEXT_ICONV_MALLOC_CHUNK); + if (buffer) { + bufferlen = GETTEXT_ICONV_MALLOC_CHUNK; + goto again; + } + } + + result = origmsg; + } else if (cache_enter(origmsg, buffer)) { + /* + * failed to enter cache. give up. + */ + result = origmsg; + } else { + size_t resultlen = dst - buffer; + + result = buffer; + bufferlen -= resultlen; + buffer += resultlen; + } + +out: + /* XXX UNLOCK */ + errno = savederrno; + + return result; +} diff --git a/lib/libintl/libintl_local.h b/lib/libintl/libintl_local.h index 99d1b0af61c9..239b00687ef1 100644 --- a/lib/libintl/libintl_local.h +++ b/lib/libintl/libintl_local.h @@ -1,4 +1,4 @@ -/* $NetBSD: libintl_local.h,v 1.6 2001/09/27 15:27:19 yamt Exp $ */ +/* $NetBSD: libintl_local.h,v 1.7 2004/01/18 08:40:40 yamt Exp $ */ /*- * Copyright (c) 2000, 2001 Citrus Project, @@ -78,8 +78,11 @@ struct domainbinding { struct domainbinding *next; char domainname[PATH_MAX]; char path[PATH_MAX]; + char *codeset; struct mohandle mohandle; }; extern struct domainbinding *__bindings; extern char __current_domainname[PATH_MAX]; + +const char *__gettext_iconv __P((const char *, struct domainbinding *)); diff --git a/lib/libintl/textdomain.c b/lib/libintl/textdomain.c index eff7135d673b..10912bd41a1a 100644 --- a/lib/libintl/textdomain.c +++ b/lib/libintl/textdomain.c @@ -1,4 +1,4 @@ -/* $NetBSD: textdomain.c,v 1.9 2004/01/05 19:21:00 itojun Exp $ */ +/* $NetBSD: textdomain.c,v 1.10 2004/01/18 08:40:40 yamt Exp $ */ /*- * Copyright (c) 2000, 2001 Citrus Project, @@ -27,7 +27,7 @@ */ #include -__RCSID("$NetBSD: textdomain.c,v 1.9 2004/01/05 19:21:00 itojun Exp $"); +__RCSID("$NetBSD: textdomain.c,v 1.10 2004/01/18 08:40:40 yamt Exp $"); #include @@ -44,6 +44,8 @@ static struct domainbinding __default_binding = { struct domainbinding *__bindings = &__default_binding; char __current_domainname[PATH_MAX] = DEFAULT_DOMAINNAME; +static struct domainbinding *domainbinding_lookup __P((const char *, int)); + /* * set the default domainname for dcngettext() and friends. */ @@ -90,10 +92,7 @@ bindtextdomain(domainname, dirname) if (strlen(domainname) + 1 > sizeof(p->domainname)) return NULL; - /* lookup binding for the domainname */ - for (p = __bindings; p; p = p->next) - if (strcmp(p->domainname, domainname) == 0) - break; + p = domainbinding_lookup(domainname, (dirname != NULL)); if (!dirname) { if (p) @@ -102,7 +101,47 @@ bindtextdomain(domainname, dirname) return _PATH_TEXTDOMAIN; } - if (!p) { + strlcpy(p->path, dirname, sizeof(p->path)); + p->mohandle.mo.mo_magic = 0; /* invalidate current mapping */ + + return (p->path); +} + +char * +bind_textdomain_codeset(domainname, codeset) + const char *domainname; + const char *codeset; +{ + struct domainbinding *p; + + p = domainbinding_lookup(domainname, (codeset != NULL)); + if (p == NULL) + return NULL; + + if (codeset) { + if (p->codeset) + free(p->codeset); + p->codeset = strdup(codeset); + } + + return p->codeset; +} + +/* + * lookup binding for the domainname + */ +static struct domainbinding * +domainbinding_lookup(domainname, alloc) + const char *domainname; + int alloc; +{ + struct domainbinding *p; + + for (p = __bindings; p; p = p->next) + if (strcmp(p->domainname, domainname) == 0) + break; + + if (!p && alloc) { p = (struct domainbinding *)malloc(sizeof(*p)); if (!p) return NULL; @@ -112,19 +151,5 @@ bindtextdomain(domainname, dirname) __bindings = p; } - strlcpy(p->path, dirname, sizeof(p->path)); - p->mohandle.mo.mo_magic = 0; /* invalidate current mapping */ - - return (p->path); -} - -/* ARGSUSED */ -char * -bind_textdomain_codeset(domainname, codeset) - const char *domainname; - const char *codeset; -{ - - /* yet to be done - always fail */ - return NULL; + return p; }