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.8 2008/01/05 14:02:38 swildner 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 has %d references 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
| M_ZERO
);
190 csp
->cp_id
= iconv_csid
++;
192 cp
= (char*)(csp
+ 1);
196 cp
+= strlen(cp
) + 1;
198 csp
->cp_to
= iconv_unicode_string
;
203 csp
->cp_from
= iconv_unicode_string
;
206 TAILQ_INSERT_TAIL(&iconv_cslist
, csp
, cp_link
);
212 iconv_unregister_cspair(struct iconv_cspair
*csp
)
214 TAILQ_REMOVE(&iconv_cslist
, csp
, cp_link
);
216 kfree(csp
->cp_data
, M_ICONVDATA
);
221 * Lookup and create an instance of converter.
222 * Currently this layer didn't have associated 'instance' structure
223 * to avoid unnesessary memory allocation.
226 iconv_open(const char *to
, const char *from
, void **handle
)
228 struct iconv_cspair
*csp
, *cspfrom
, *cspto
;
229 struct iconv_converter_class
*dcp
;
234 * First, lookup fully qualified cspairs
236 error
= iconv_lookupcs(to
, from
, &csp
);
238 return ICONV_CONVERTER_OPEN(csp
->cp_dcp
, csp
, NULL
, handle
);
241 * Well, nothing found. Now try to construct a composite conversion
242 * ToDo: add a 'capability' field to converter
244 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
245 cnvname
= ICONV_CONVERTER_NAME(dcp
);
248 error
= iconv_lookupcs(cnvname
, from
, &cspfrom
);
251 error
= iconv_lookupcs(to
, cnvname
, &cspto
);
255 * Fine, we're found a pair which can be combined together
257 return ICONV_CONVERTER_OPEN(dcp
, cspto
, cspfrom
, handle
);
263 iconv_close(void *handle
)
265 return ICONV_CONVERTER_CLOSE(handle
);
269 iconv_conv(void *handle
, const char **inbuf
,
270 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
)
272 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
);
276 * Give a list of loaded converters. Each name terminated with 0.
277 * An empty string terminates the list.
280 iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS
)
282 struct iconv_converter_class
*dcp
;
289 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
290 name
= ICONV_CONVERTER_NAME(dcp
);
293 error
= SYSCTL_OUT(req
, name
, strlen(name
) + 1);
300 error
= SYSCTL_OUT(req
, &spc
, sizeof(spc
));
304 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, drvlist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
305 NULL
, 0, iconv_sysctl_drvlist
, "S,xlat", "registered converters");
308 * List all available charset pairs.
311 iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS
)
313 struct iconv_cspair
*csp
;
314 struct iconv_cspair_info csi
;
318 bzero(&csi
, sizeof(csi
));
319 csi
.cs_version
= ICONV_CSPAIR_INFO_VER
;
321 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
322 csi
.cs_id
= csp
->cp_id
;
323 csi
.cs_refcount
= csp
->cp_refcount
;
324 csi
.cs_base
= csp
->cp_base
? csp
->cp_base
->cp_id
: 0;
325 strcpy(csi
.cs_to
, csp
->cp_to
);
326 strcpy(csi
.cs_from
, csp
->cp_from
);
327 error
= SYSCTL_OUT(req
, &csi
, sizeof(csi
));
334 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, cslist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
335 NULL
, 0, iconv_sysctl_cslist
, "S,xlat", "registered charset pairs");
338 * Add new charset pair
341 iconv_sysctl_add(SYSCTL_HANDLER_ARGS
)
343 struct iconv_converter_class
*dcp
;
344 struct iconv_cspair
*csp
;
345 struct iconv_add_in din
;
346 struct iconv_add_out dout
;
349 error
= SYSCTL_IN(req
, &din
, sizeof(din
));
352 if (din
.ia_version
!= ICONV_ADD_VER
)
354 if (din
.ia_datalen
> ICONV_CSMAXDATALEN
)
358 * Make sure all user-supplied strings are terminated before
361 din
.ia_converter
[ICONV_CNVNMAXLEN
-1] = 0;
362 din
.ia_to
[ICONV_CSNMAXLEN
-1] = 0;
363 din
.ia_from
[ICONV_CSNMAXLEN
-1] = 0;
365 if (iconv_lookupconv(din
.ia_converter
, &dcp
) != 0)
367 error
= iconv_register_cspair(din
.ia_to
, din
.ia_from
, dcp
, NULL
, &csp
);
370 if (din
.ia_datalen
) {
371 csp
->cp_data
= kmalloc(din
.ia_datalen
, M_ICONVDATA
, M_WAITOK
);
372 error
= copyin(din
.ia_data
, csp
->cp_data
, din
.ia_datalen
);
376 dout
.ia_csid
= csp
->cp_id
;
377 error
= SYSCTL_OUT(req
, &dout
, sizeof(dout
));
382 iconv_unregister_cspair(csp
);
386 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, add
, CTLFLAG_RW
| CTLTYPE_OPAQUE
,
387 NULL
, 0, iconv_sysctl_add
, "S,xlat", "register charset pair");
390 * Default stubs for converters
393 iconv_converter_initstub(struct iconv_converter_class
*dp
)
399 iconv_converter_donestub(struct iconv_converter_class
*dp
)
405 iconv_converter_handler(module_t mod
, int type
, void *data
)
407 struct iconv_converter_class
*dcp
= data
;
412 error
= iconv_register_converter(dcp
);
415 error
= ICONV_CONVERTER_INIT(dcp
);
417 iconv_unregister_converter(dcp
);
420 ICONV_CONVERTER_DONE(dcp
);
421 error
= iconv_unregister_converter(dcp
);
430 * Common used functions
433 iconv_convstr(void *handle
, char *dst
, const char *src
)
436 int inlen
, outlen
, error
;
438 if (handle
== NULL
) {
442 inlen
= outlen
= strlen(src
);
443 error
= iconv_conv(handle
, NULL
, NULL
, &p
, &outlen
);
446 error
= iconv_conv(handle
, &src
, &inlen
, &p
, &outlen
);
454 iconv_convmem(void *handle
, void *dst
, const void *src
, int size
)
458 int inlen
, outlen
, error
;
462 if (handle
== NULL
) {
463 memcpy(dst
, src
, size
);
466 inlen
= outlen
= size
;
467 error
= iconv_conv(handle
, NULL
, NULL
, &d
, &outlen
);
470 error
= iconv_conv(handle
, &s
, &inlen
, &d
, &outlen
);
477 iconv_lookupcp(char **cpp
, const char *s
)
480 ICDEBUG("warning a NULL list passed\n");
484 if (strcmp(*cpp
, s
) == 0)