implement gettext implicit iconv and bind_textdomain_codeset.

This commit is contained in:
yamt 2004-01-18 08:40:40 +00:00
parent a28d5cecb2
commit f3ec2d5725
5 changed files with 263 additions and 32 deletions

View File

@ -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 <bsd.own.mk>
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

View File

@ -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 <sys/cdefs.h>
__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 <sys/param.h>
#include <sys/stat.h>
@ -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

201
lib/libintl/gettext_iconv.c Normal file
View File

@ -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 <sys/types.h>
#include <sys/param.h>
#include <errno.h>
#include <iconv.h>
#include <libintl.h>
#include <langinfo.h>
#include <search.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

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

View File

@ -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 <sys/cdefs.h>
__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 <sys/param.h>
@ -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;
}