2 * Copyright (c) 2000-2001, Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * $FreeBSD: src/sys/libkern/iconv.c,v 1.1.2.1 2001/05/21 08:28:07 bp Exp $
33 * $DragonFly: src/sys/libiconv/iconv.c,v 1.6 2007/03/08 16:42:34 dillon Exp $
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/iconv.h>
39 #include <sys/malloc.h>
41 #include "iconv_converter_if.h"
43 SYSCTL_DECL(_kern_iconv
);
45 SYSCTL_NODE(_kern
, OID_AUTO
, iconv
, CTLFLAG_RW
, NULL
, "kernel iconv interface");
47 MALLOC_DEFINE(M_ICONV
, "ICONV", "ICONV structures");
48 MALLOC_DEFINE(M_ICONVDATA
, "ICONV data", "ICONV data");
50 MODULE_VERSION(libiconv
, 1);
54 * iconv converter instance
56 struct iconv_converter
{
62 struct sysctl_oid
*iconv_oid_hook
= &sysctl___kern_iconv
;
65 * List of loaded converters
67 static TAILQ_HEAD(iconv_converter_list
, iconv_converter_class
)
68 iconv_converters
= TAILQ_HEAD_INITIALIZER(iconv_converters
);
71 * List of supported/loaded charsets pairs
73 static TAILQ_HEAD(, iconv_cspair
)
74 iconv_cslist
= TAILQ_HEAD_INITIALIZER(iconv_cslist
);
75 static int iconv_csid
= 1;
77 static char iconv_unicode_string
[] = "unicode"; /* save eight bytes when possible */
79 static void iconv_unregister_cspair(struct iconv_cspair
*csp
);
82 iconv_mod_unload(void)
84 struct iconv_cspair
*csp
;
86 while ((csp
= TAILQ_FIRST(&iconv_cslist
)) != NULL
) {
89 iconv_unregister_cspair(csp
);
95 iconv_mod_handler(module_t mod
, int type
, void *data
)
104 error
= iconv_mod_unload();
112 static moduledata_t iconv_mod
= {
113 "iconv", iconv_mod_handler
, NULL
116 DECLARE_MODULE(iconv
, iconv_mod
, SI_SUB_DRIVERS
, SI_ORDER_SECOND
);
119 iconv_register_converter(struct iconv_converter_class
*dcp
)
121 kobj_class_instantiate((kobj_class_t
)dcp
);
122 TAILQ_INSERT_TAIL(&iconv_converters
, dcp
, cc_link
);
127 iconv_unregister_converter(struct iconv_converter_class
*dcp
)
129 if (dcp
->refs
!= 1) {
130 ICDEBUG("converter have %d referenses left\n", dcp
->refs
);
133 TAILQ_REMOVE(&iconv_converters
, dcp
, cc_link
);
134 kobj_class_uninstantiate((kobj_class_t
)dcp
);
139 iconv_lookupconv(const char *name
, struct iconv_converter_class
**dcpp
)
141 struct iconv_converter_class
*dcp
;
143 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
146 if (strcmp(name
, ICONV_CONVERTER_NAME(dcp
)) == 0) {
156 iconv_lookupcs(const char *to
, const char *from
, struct iconv_cspair
**cspp
)
158 struct iconv_cspair
*csp
;
160 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
161 if (strcmp(csp
->cp_to
, to
) == 0 &&
162 strcmp(csp
->cp_from
, from
) == 0) {
172 iconv_register_cspair(const char *to
, const char *from
,
173 struct iconv_converter_class
*dcp
, void *data
,
174 struct iconv_cspair
**cspp
)
176 struct iconv_cspair
*csp
;
178 int csize
, ucsto
, ucsfrom
;
180 if (iconv_lookupcs(to
, from
, NULL
) == 0)
182 csize
= sizeof(*csp
);
183 ucsto
= strcmp(to
, iconv_unicode_string
) == 0;
185 csize
+= strlen(to
) + 1;
186 ucsfrom
= strcmp(from
, iconv_unicode_string
) == 0;
188 csize
+= strlen(from
) + 1;
189 csp
= kmalloc(csize
, M_ICONV
, M_WAITOK
);
191 csp
->cp_id
= iconv_csid
++;
193 cp
= (char*)(csp
+ 1);
197 cp
+= strlen(cp
) + 1;
199 csp
->cp_to
= iconv_unicode_string
;
204 csp
->cp_from
= iconv_unicode_string
;
207 TAILQ_INSERT_TAIL(&iconv_cslist
, csp
, cp_link
);
213 iconv_unregister_cspair(struct iconv_cspair
*csp
)
215 TAILQ_REMOVE(&iconv_cslist
, csp
, cp_link
);
217 kfree(csp
->cp_data
, M_ICONVDATA
);
222 * Lookup and create an instance of converter.
223 * Currently this layer didn't have associated 'instance' structure
224 * to avoid unnesessary memory allocation.
227 iconv_open(const char *to
, const char *from
, void **handle
)
229 struct iconv_cspair
*csp
, *cspfrom
, *cspto
;
230 struct iconv_converter_class
*dcp
;
235 * First, lookup fully qualified cspairs
237 error
= iconv_lookupcs(to
, from
, &csp
);
239 return ICONV_CONVERTER_OPEN(csp
->cp_dcp
, csp
, NULL
, handle
);
242 * Well, nothing found. Now try to construct a composite conversion
243 * ToDo: add a 'capability' field to converter
245 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
246 cnvname
= ICONV_CONVERTER_NAME(dcp
);
249 error
= iconv_lookupcs(cnvname
, from
, &cspfrom
);
252 error
= iconv_lookupcs(to
, cnvname
, &cspto
);
256 * Fine, we're found a pair which can be combined together
258 return ICONV_CONVERTER_OPEN(dcp
, cspto
, cspfrom
, handle
);
264 iconv_close(void *handle
)
266 return ICONV_CONVERTER_CLOSE(handle
);
270 iconv_conv(void *handle
, const char **inbuf
,
271 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
)
273 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
);
277 * Give a list of loaded converters. Each name terminated with 0.
278 * An empty string terminates the list.
281 iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS
)
283 struct iconv_converter_class
*dcp
;
290 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
291 name
= ICONV_CONVERTER_NAME(dcp
);
294 error
= SYSCTL_OUT(req
, name
, strlen(name
) + 1);
301 error
= SYSCTL_OUT(req
, &spc
, sizeof(spc
));
305 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, drvlist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
306 NULL
, 0, iconv_sysctl_drvlist
, "S,xlat", "registered converters");
309 * List all available charset pairs.
312 iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS
)
314 struct iconv_cspair
*csp
;
315 struct iconv_cspair_info csi
;
319 bzero(&csi
, sizeof(csi
));
320 csi
.cs_version
= ICONV_CSPAIR_INFO_VER
;
322 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
323 csi
.cs_id
= csp
->cp_id
;
324 csi
.cs_refcount
= csp
->cp_refcount
;
325 csi
.cs_base
= csp
->cp_base
? csp
->cp_base
->cp_id
: 0;
326 strcpy(csi
.cs_to
, csp
->cp_to
);
327 strcpy(csi
.cs_from
, csp
->cp_from
);
328 error
= SYSCTL_OUT(req
, &csi
, sizeof(csi
));
335 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, cslist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
336 NULL
, 0, iconv_sysctl_cslist
, "S,xlat", "registered charset pairs");
339 * Add new charset pair
342 iconv_sysctl_add(SYSCTL_HANDLER_ARGS
)
344 struct iconv_converter_class
*dcp
;
345 struct iconv_cspair
*csp
;
346 struct iconv_add_in din
;
347 struct iconv_add_out dout
;
350 error
= SYSCTL_IN(req
, &din
, sizeof(din
));
353 if (din
.ia_version
!= ICONV_ADD_VER
)
355 if (din
.ia_datalen
> ICONV_CSMAXDATALEN
)
359 * Make sure all user-supplied strings are terminated before
362 din
.ia_converter
[ICONV_CNVNMAXLEN
-1] = 0;
363 din
.ia_to
[ICONV_CSNMAXLEN
-1] = 0;
364 din
.ia_from
[ICONV_CSNMAXLEN
-1] = 0;
366 if (iconv_lookupconv(din
.ia_converter
, &dcp
) != 0)
368 error
= iconv_register_cspair(din
.ia_to
, din
.ia_from
, dcp
, NULL
, &csp
);
371 if (din
.ia_datalen
) {
372 csp
->cp_data
= kmalloc(din
.ia_datalen
, M_ICONVDATA
, M_WAITOK
);
373 error
= copyin(din
.ia_data
, csp
->cp_data
, din
.ia_datalen
);
377 dout
.ia_csid
= csp
->cp_id
;
378 error
= SYSCTL_OUT(req
, &dout
, sizeof(dout
));
383 iconv_unregister_cspair(csp
);
387 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, add
, CTLFLAG_RW
| CTLTYPE_OPAQUE
,
388 NULL
, 0, iconv_sysctl_add
, "S,xlat", "register charset pair");
391 * Default stubs for converters
394 iconv_converter_initstub(struct iconv_converter_class
*dp
)
400 iconv_converter_donestub(struct iconv_converter_class
*dp
)
406 iconv_converter_handler(module_t mod
, int type
, void *data
)
408 struct iconv_converter_class
*dcp
= data
;
413 error
= iconv_register_converter(dcp
);
416 error
= ICONV_CONVERTER_INIT(dcp
);
418 iconv_unregister_converter(dcp
);
421 ICONV_CONVERTER_DONE(dcp
);
422 error
= iconv_unregister_converter(dcp
);
431 * Common used functions
434 iconv_convstr(void *handle
, char *dst
, const char *src
)
437 int inlen
, outlen
, error
;
439 if (handle
== NULL
) {
443 inlen
= outlen
= strlen(src
);
444 error
= iconv_conv(handle
, NULL
, NULL
, &p
, &outlen
);
447 error
= iconv_conv(handle
, &src
, &inlen
, &p
, &outlen
);
455 iconv_convmem(void *handle
, void *dst
, const void *src
, int size
)
459 int inlen
, outlen
, error
;
463 if (handle
== NULL
) {
464 memcpy(dst
, src
, size
);
467 inlen
= outlen
= size
;
468 error
= iconv_conv(handle
, NULL
, NULL
, &d
, &outlen
);
471 error
= iconv_conv(handle
, &s
, &inlen
, &d
, &outlen
);
478 iconv_lookupcp(char **cpp
, const char *s
)
481 ICDEBUG("warning a NULL list passed\n");
485 if (strcmp(*cpp
, s
) == 0)