implement gettext implicit iconv and bind_textdomain_codeset.
This commit is contained in:
parent
a28d5cecb2
commit
f3ec2d5725
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 *));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue