1 /* $NetBSD: gettext_iconv.c,v 1.8 2009/02/18 13:08:22 yamt Exp $ */
4 * Copyright (c) 2004 Citrus Project,
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/types.h>
33 #include <sys/param.h>
43 #include "libintl_local.h"
46 const char *c_origmsg
;
47 const char *c_resultmsg
;
50 static const struct cache
*cache_find(const char *, struct domainbinding
*);
51 static int cache_enter(const char *, const char *);
52 static int cache_cmp(const void *, const void *);
54 static void *cacheroot
;
57 static const struct cache
*
58 cache_find(const char *msg
, struct domainbinding
*db
)
64 c
= tfind(&key
, &cacheroot
, cache_cmp
);
70 cache_enter(const char *origmsg
, const char *resultmsg
)
74 c
= malloc(sizeof(*c
));
78 c
->c_origmsg
= origmsg
;
79 c
->c_resultmsg
= resultmsg
;
81 if (tsearch(c
, &cacheroot
, cache_cmp
) == NULL
) {
90 cache_cmp(const void *va
, const void *vb
)
92 const struct cache
*a
= va
;
93 const struct cache
*b
= vb
;
96 if (a
->c_origmsg
> b
->c_origmsg
) {
98 } else if (a
->c_origmsg
< b
->c_origmsg
) {
107 #define GETTEXT_ICONV_MALLOC_CHUNK (16 * 1024)
110 __gettext_iconv(const char *origmsg
, struct domainbinding
*db
)
113 const char *fromcode
= db
->mohandle
.mo
.mo_charset
;
114 const struct cache
*cache
;
123 int savederrno
= errno
;
126 * static buffer for converted texts.
129 * we never free buffers once returned to callers.
130 * because of interface design of gettext, we can't know
131 * the lifetime of them.
134 static size_t bufferlen
;
137 * don't convert message if *.mo doesn't specify codeset.
139 if (fromcode
== NULL
)
142 tocode
= db
->codeset
;
143 if (tocode
== NULL
) {
145 * codeset isn't specified explicitly by
146 * bind_textdomain_codeset().
147 * use current locale(LC_CTYPE)'s codeset.
149 * XXX maybe wrong; it can mismatch with
150 * environment variable setting.
152 tocode
= nl_langinfo(CODESET
);
156 * shortcut if possible.
157 * XXX should handle aliases
159 if (!strcasecmp(tocode
, fromcode
))
164 /* XXX should detect change of tocode and purge caches? */
167 * see if we have already converted this message.
169 cache
= cache_find(origmsg
, db
);
171 result
= cache
->c_resultmsg
;
175 origlen
= strlen(origmsg
) + 1;
177 cd
= iconv_open(tocode
, fromcode
);
178 if (cd
== (iconv_t
)-1) {
187 nvalid
= iconv(cd
, &src
, &srclen
, &dst
, &dstlen
);
190 if (nvalid
== (size_t)-1) {
192 * try to allocate a new buffer.
194 * just give up if GETTEXT_ICONV_MALLOC_CHUNK was not enough.
196 if (errno
== E2BIG
&&
197 bufferlen
!= GETTEXT_ICONV_MALLOC_CHUNK
) {
198 buffer
= malloc(GETTEXT_ICONV_MALLOC_CHUNK
);
200 bufferlen
= GETTEXT_ICONV_MALLOC_CHUNK
;
206 } else if (cache_enter(origmsg
, buffer
)) {
208 * failed to enter cache. give up.
212 size_t resultlen
= dst
- buffer
;
215 bufferlen
-= resultlen
;