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.12.2.1.2.1 2009/04/15 03:14:26 kensmith Exp $
33 * $DragonFly: src/sys/libiconv/iconv.c,v 1.8 2008/01/05 14:02:38 swildner Exp $
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/iconv.h>
40 #include <sys/malloc.h>
41 #include <sys/mount.h>
42 #include <sys/syslog.h>
43 #include "iconv_converter_if.h"
45 SYSCTL_DECL(_kern_iconv
);
47 SYSCTL_NODE(_kern
, OID_AUTO
, iconv
, CTLFLAG_RW
, NULL
, "kernel iconv interface");
49 MALLOC_DEFINE(M_ICONV
, "iconv", "ICONV structures");
50 MALLOC_DEFINE(M_ICONVDATA
, "iconv_data", "ICONV data");
52 MODULE_VERSION(libiconv
, 2);
56 * iconv converter instance
58 struct iconv_converter
{
64 struct sysctl_oid
*iconv_oid_hook
= &sysctl___kern_iconv
;
67 * List of loaded converters
69 static TAILQ_HEAD(iconv_converter_list
, iconv_converter_class
)
70 iconv_converters
= TAILQ_HEAD_INITIALIZER(iconv_converters
);
73 * List of supported/loaded charsets pairs
75 static TAILQ_HEAD(, iconv_cspair
)
76 iconv_cslist
= TAILQ_HEAD_INITIALIZER(iconv_cslist
);
77 static int iconv_csid
= 1;
79 static char iconv_unicode_string
[] = "unicode"; /* save eight bytes when possible */
81 static void iconv_unregister_cspair(struct iconv_cspair
*csp
);
84 iconv_mod_unload(void)
86 struct iconv_cspair
*csp
;
88 while ((csp
= TAILQ_FIRST(&iconv_cslist
)) != NULL
) {
93 while ((csp
= TAILQ_FIRST(&iconv_cslist
)) != NULL
)
94 iconv_unregister_cspair(csp
);
99 iconv_mod_handler(module_t mod
, int type
, void *data
)
108 error
= iconv_mod_unload();
116 static moduledata_t iconv_mod
= {
117 "iconv", iconv_mod_handler
, NULL
120 DECLARE_MODULE(iconv
, iconv_mod
, SI_SUB_DRIVERS
, SI_ORDER_SECOND
);
123 iconv_register_converter(struct iconv_converter_class
*dcp
)
125 kobj_class_instantiate((kobj_class_t
)dcp
);
126 TAILQ_INSERT_TAIL(&iconv_converters
, dcp
, cc_link
);
131 iconv_unregister_converter(struct iconv_converter_class
*dcp
)
133 if (dcp
->refs
!= 1) {
134 ICDEBUG("converter has %d references left\n", dcp
->refs
);
137 TAILQ_REMOVE(&iconv_converters
, dcp
, cc_link
);
138 kobj_class_uninstantiate((kobj_class_t
)dcp
);
143 iconv_lookupconv(const char *name
, struct iconv_converter_class
**dcpp
)
145 struct iconv_converter_class
*dcp
;
147 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
150 if (strcmp(name
, ICONV_CONVERTER_NAME(dcp
)) == 0) {
160 iconv_lookupcs(const char *to
, const char *from
, struct iconv_cspair
**cspp
)
162 struct iconv_cspair
*csp
;
164 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
165 if (strcmp(csp
->cp_to
, to
) == 0 &&
166 strcmp(csp
->cp_from
, from
) == 0) {
176 iconv_register_cspair(const char *to
, const char *from
,
177 struct iconv_converter_class
*dcp
, void *data
,
178 struct iconv_cspair
**cspp
)
180 struct iconv_cspair
*csp
;
182 int csize
, ucsto
, ucsfrom
;
184 if (iconv_lookupcs(to
, from
, NULL
) == 0)
186 csize
= sizeof(*csp
);
187 ucsto
= strcmp(to
, iconv_unicode_string
) == 0;
189 csize
+= strlen(to
) + 1;
190 ucsfrom
= strcmp(from
, iconv_unicode_string
) == 0;
192 csize
+= strlen(from
) + 1;
193 csp
= kmalloc(csize
, M_ICONV
, M_WAITOK
| M_ZERO
);
194 csp
->cp_id
= iconv_csid
++;
196 cp
= (char*)(csp
+ 1);
200 cp
+= strlen(cp
) + 1;
202 csp
->cp_to
= iconv_unicode_string
;
207 csp
->cp_from
= iconv_unicode_string
;
210 TAILQ_INSERT_TAIL(&iconv_cslist
, csp
, cp_link
);
216 iconv_unregister_cspair(struct iconv_cspair
*csp
)
218 TAILQ_REMOVE(&iconv_cslist
, csp
, cp_link
);
220 kfree(csp
->cp_data
, M_ICONVDATA
);
225 * Lookup and create an instance of converter.
226 * Currently this layer didn't have associated 'instance' structure
227 * to avoid unnesessary memory allocation.
230 iconv_open(const char *to
, const char *from
, void **handle
)
232 struct iconv_cspair
*csp
, *cspfrom
, *cspto
;
233 struct iconv_converter_class
*dcp
;
238 * First, lookup fully qualified cspairs
240 error
= iconv_lookupcs(to
, from
, &csp
);
242 return ICONV_CONVERTER_OPEN(csp
->cp_dcp
, csp
, NULL
, handle
);
245 * Well, nothing found. Now try to construct a composite conversion
246 * ToDo: add a 'capability' field to converter
248 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
249 cnvname
= ICONV_CONVERTER_NAME(dcp
);
252 error
= iconv_lookupcs(cnvname
, from
, &cspfrom
);
255 error
= iconv_lookupcs(to
, cnvname
, &cspto
);
259 * Fine, we're found a pair which can be combined together
261 return ICONV_CONVERTER_OPEN(dcp
, cspto
, cspfrom
, handle
);
267 iconv_close(void *handle
)
269 return ICONV_CONVERTER_CLOSE(handle
);
273 iconv_conv(void *handle
, const char **inbuf
,
274 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
)
276 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 0, 0);
280 iconv_conv_case(void *handle
, const char **inbuf
,
281 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
, int casetype
)
283 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 0, casetype
);
287 iconv_convchr(void *handle
, const char **inbuf
,
288 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
)
290 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 1, 0);
294 iconv_convchr_case(void *handle
, const char **inbuf
,
295 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
, int casetype
)
297 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 1, casetype
);
301 * Give a list of loaded converters. Each name terminated with 0.
302 * An empty string terminates the list.
305 iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS
)
307 struct iconv_converter_class
*dcp
;
314 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
315 name
= ICONV_CONVERTER_NAME(dcp
);
318 error
= SYSCTL_OUT(req
, name
, strlen(name
) + 1);
325 error
= SYSCTL_OUT(req
, &spc
, sizeof(spc
));
329 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, drvlist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
330 NULL
, 0, iconv_sysctl_drvlist
, "S,xlat", "registered converters");
333 * List all available charset pairs.
336 iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS
)
338 struct iconv_cspair
*csp
;
339 struct iconv_cspair_info csi
;
343 bzero(&csi
, sizeof(csi
));
344 csi
.cs_version
= ICONV_CSPAIR_INFO_VER
;
346 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
347 csi
.cs_id
= csp
->cp_id
;
348 csi
.cs_refcount
= csp
->cp_refcount
;
349 csi
.cs_base
= csp
->cp_base
? csp
->cp_base
->cp_id
: 0;
350 strcpy(csi
.cs_to
, csp
->cp_to
);
351 strcpy(csi
.cs_from
, csp
->cp_from
);
352 error
= SYSCTL_OUT(req
, &csi
, sizeof(csi
));
359 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, cslist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
360 NULL
, 0, iconv_sysctl_cslist
, "S,xlat", "registered charset pairs");
363 * Add new charset pair
366 iconv_sysctl_add(SYSCTL_HANDLER_ARGS
)
368 struct iconv_converter_class
*dcp
;
369 struct iconv_cspair
*csp
;
370 struct iconv_add_in din
;
371 struct iconv_add_out dout
;
374 error
= SYSCTL_IN(req
, &din
, sizeof(din
));
377 if (din
.ia_version
!= ICONV_ADD_VER
)
379 if (din
.ia_datalen
> ICONV_CSMAXDATALEN
)
383 * Make sure all user-supplied strings are terminated before
386 * XXX return EINVAL if strings are not properly terminated
388 din
.ia_converter
[ICONV_CNVNMAXLEN
-1] = 0;
389 din
.ia_to
[ICONV_CSNMAXLEN
-1] = 0;
390 din
.ia_from
[ICONV_CSNMAXLEN
-1] = 0;
392 if (iconv_lookupconv(din
.ia_converter
, &dcp
) != 0)
394 error
= iconv_register_cspair(din
.ia_to
, din
.ia_from
, dcp
, NULL
, &csp
);
397 if (din
.ia_datalen
) {
398 csp
->cp_data
= kmalloc(din
.ia_datalen
, M_ICONVDATA
, M_WAITOK
);
399 error
= copyin(din
.ia_data
, csp
->cp_data
, din
.ia_datalen
);
403 dout
.ia_csid
= csp
->cp_id
;
404 error
= SYSCTL_OUT(req
, &dout
, sizeof(dout
));
407 ICDEBUG("%s => %s, %d bytes\n",din
.ia_from
, din
.ia_to
, din
.ia_datalen
);
410 iconv_unregister_cspair(csp
);
414 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, add
, CTLFLAG_RW
| CTLTYPE_OPAQUE
,
415 NULL
, 0, iconv_sysctl_add
, "S,xlat", "register charset pair");
418 * Default stubs for converters
421 iconv_converter_initstub(struct iconv_converter_class
*dp
)
427 iconv_converter_donestub(struct iconv_converter_class
*dp
)
433 iconv_converter_handler(module_t mod
, int type
, void *data
)
435 struct iconv_converter_class
*dcp
= data
;
440 error
= iconv_register_converter(dcp
);
443 error
= ICONV_CONVERTER_INIT(dcp
);
445 iconv_unregister_converter(dcp
);
448 ICONV_CONVERTER_DONE(dcp
);
449 error
= iconv_unregister_converter(dcp
);
458 * Common used functions (don't use with unicode)
461 iconv_convstr(void *handle
, char *dst
, const char *src
)
465 size_t inlen
, outlen
;
467 if (handle
== NULL
) {
471 inlen
= outlen
= strlen(src
);
472 error
= iconv_conv(handle
, NULL
, NULL
, &p
, &outlen
);
475 error
= iconv_conv(handle
, &src
, &inlen
, &p
, &outlen
);
483 iconv_convmem(void *handle
, void *dst
, const void *src
, int size
)
487 size_t inlen
, outlen
;
492 if (handle
== NULL
) {
493 memcpy(dst
, src
, size
);
496 inlen
= outlen
= size
;
497 error
= iconv_conv(handle
, NULL
, NULL
, &d
, &outlen
);
500 error
= iconv_conv(handle
, &s
, &inlen
, &d
, &outlen
);
507 iconv_lookupcp(char **cpp
, const char *s
)
510 ICDEBUG("warning a NULL list passed\n", ""); /* XXX ISO variadic macros cannot
516 if (strcmp(*cpp
, s
) == 0)
522 * Return if fsname is in use of not
525 iconv_vfs_refcount(const char *fsname
)
527 struct vfsconf
*vfsp
;
529 vfsp
= vfsconf_find_by_name(fsname
);
530 if (vfsp
!= NULL
&& vfsp
->vfc_refcount
> 0)