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.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: head/sys/libkern/iconv.c 267291 2014-06-09 19:27:47Z jhb $
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/iconv.h>
34 #include <sys/malloc.h>
35 #include <sys/mount.h>
36 #include <sys/syslog.h>
38 #include "iconv_converter_if.h"
40 SYSCTL_DECL(_kern_iconv
);
42 SYSCTL_NODE(_kern
, OID_AUTO
, iconv
, CTLFLAG_RW
, NULL
, "kernel iconv interface");
44 MALLOC_DEFINE(M_ICONV
, "iconv", "ICONV structures");
45 static MALLOC_DEFINE(M_ICONVDATA
, "iconv_data", "ICONV data");
47 MODULE_VERSION(libiconv
, 2);
49 static struct lock iconv_lock
;
53 * iconv converter instance
55 struct iconv_converter
{
61 struct sysctl_oid
*iconv_oid_hook
= &sysctl___kern_iconv
;
64 * List of loaded converters
66 static TAILQ_HEAD(iconv_converter_list
, iconv_converter_class
)
67 iconv_converters
= TAILQ_HEAD_INITIALIZER(iconv_converters
);
70 * List of supported/loaded charsets pairs
72 static TAILQ_HEAD(, iconv_cspair
)
73 iconv_cslist
= TAILQ_HEAD_INITIALIZER(iconv_cslist
);
74 static int iconv_csid
= 1;
76 static char iconv_unicode_string
[] = "unicode"; /* save eight bytes when possible */
78 static void iconv_unregister_cspair(struct iconv_cspair
*csp
);
81 iconv_mod_unload(void)
83 struct iconv_cspair
*csp
;
85 lockmgr(&iconv_lock
, LK_EXCLUSIVE
);
86 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
87 if (csp
->cp_refcount
) {
88 lockmgr(&iconv_lock
, LK_RELEASE
);
93 while ((csp
= TAILQ_FIRST(&iconv_cslist
)) != NULL
)
94 iconv_unregister_cspair(csp
);
95 lockmgr(&iconv_lock
, LK_RELEASE
);
96 lockuninit(&iconv_lock
);
101 iconv_mod_handler(module_t mod
, int type
, void *data
)
108 lockinit(&iconv_lock
, "iconv", 0, LK_CANRECURSE
);
111 error
= iconv_mod_unload();
119 static moduledata_t iconv_mod
= {
120 "iconv", iconv_mod_handler
, NULL
123 DECLARE_MODULE(iconv
, iconv_mod
, SI_SUB_DRIVERS
, SI_ORDER_SECOND
);
126 iconv_register_converter(struct iconv_converter_class
*dcp
)
128 kobj_class_instantiate((kobj_class_t
)dcp
);
129 TAILQ_INSERT_TAIL(&iconv_converters
, dcp
, cc_link
);
134 iconv_unregister_converter(struct iconv_converter_class
*dcp
)
136 if (dcp
->refs
!= 1) {
137 ICDEBUG("converter has %d references left\n", dcp
->refs
);
140 TAILQ_REMOVE(&iconv_converters
, dcp
, cc_link
);
141 kobj_class_uninstantiate((kobj_class_t
)dcp
);
146 iconv_lookupconv(const char *name
, struct iconv_converter_class
**dcpp
)
148 struct iconv_converter_class
*dcp
;
150 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
153 if (strcmp(name
, ICONV_CONVERTER_NAME(dcp
)) == 0) {
163 iconv_lookupcs(const char *to
, const char *from
, struct iconv_cspair
**cspp
)
165 struct iconv_cspair
*csp
;
167 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
168 if (strcasecmp(csp
->cp_to
, to
) == 0 &&
169 strcasecmp(csp
->cp_from
, from
) == 0) {
179 iconv_register_cspair(const char *to
, const char *from
,
180 struct iconv_converter_class
*dcp
, void *data
,
181 struct iconv_cspair
**cspp
)
183 struct iconv_cspair
*csp
;
185 int csize
, ucsto
, ucsfrom
;
187 if (iconv_lookupcs(to
, from
, NULL
) == 0)
189 csize
= sizeof(*csp
);
190 ucsto
= strcmp(to
, iconv_unicode_string
) == 0;
192 csize
+= strlen(to
) + 1;
193 ucsfrom
= strcmp(from
, iconv_unicode_string
) == 0;
195 csize
+= strlen(from
) + 1;
196 csp
= kmalloc(csize
, M_ICONV
, M_WAITOK
| M_ZERO
);
197 csp
->cp_id
= iconv_csid
++;
199 cp
= (char*)(csp
+ 1);
203 cp
+= strlen(cp
) + 1;
205 csp
->cp_to
= iconv_unicode_string
;
210 csp
->cp_from
= iconv_unicode_string
;
213 TAILQ_INSERT_TAIL(&iconv_cslist
, csp
, cp_link
);
219 iconv_unregister_cspair(struct iconv_cspair
*csp
)
221 TAILQ_REMOVE(&iconv_cslist
, csp
, cp_link
);
223 kfree(csp
->cp_data
, M_ICONVDATA
);
228 * Lookup and create an instance of converter.
229 * Currently this layer didn't have associated 'instance' structure
230 * to avoid unnesessary memory allocation.
233 iconv_open(const char *to
, const char *from
, void **handle
)
235 struct iconv_cspair
*csp
, *cspfrom
, *cspto
;
236 struct iconv_converter_class
*dcp
;
241 * First, lookup fully qualified cspairs
243 error
= iconv_lookupcs(to
, from
, &csp
);
245 return ICONV_CONVERTER_OPEN(csp
->cp_dcp
, csp
, NULL
, handle
);
248 * Well, nothing found. Now try to construct a composite conversion
249 * ToDo: add a 'capability' field to converter
251 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
252 cnvname
= ICONV_CONVERTER_NAME(dcp
);
255 error
= iconv_lookupcs(cnvname
, from
, &cspfrom
);
258 error
= iconv_lookupcs(to
, cnvname
, &cspto
);
262 * Fine, we're found a pair which can be combined together
264 return ICONV_CONVERTER_OPEN(dcp
, cspto
, cspfrom
, handle
);
270 iconv_close(void *handle
)
272 return ICONV_CONVERTER_CLOSE(handle
);
276 iconv_conv(void *handle
, const char **inbuf
,
277 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
)
279 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 0, 0);
283 iconv_conv_case(void *handle
, const char **inbuf
,
284 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
, int casetype
)
286 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 0, casetype
);
290 iconv_convchr(void *handle
, const char **inbuf
,
291 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
)
293 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 1, 0);
297 iconv_convchr_case(void *handle
, const char **inbuf
,
298 size_t *inbytesleft
, char **outbuf
, size_t *outbytesleft
, int casetype
)
300 return ICONV_CONVERTER_CONV(handle
, inbuf
, inbytesleft
, outbuf
, outbytesleft
, 1, casetype
);
304 towlower(int c
, void *handle
)
306 return ICONV_CONVERTER_TOLOWER(handle
, c
);
310 towupper(int c
, void *handle
)
312 return ICONV_CONVERTER_TOUPPER(handle
, c
);
316 * Give a list of loaded converters. Each name terminated with 0.
317 * An empty string terminates the list.
320 iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS
)
322 struct iconv_converter_class
*dcp
;
329 lockmgr(&iconv_lock
, LK_SHARED
);
330 TAILQ_FOREACH(dcp
, &iconv_converters
, cc_link
) {
331 name
= ICONV_CONVERTER_NAME(dcp
);
334 error
= SYSCTL_OUT(req
, name
, strlen(name
) + 1);
338 lockmgr(&iconv_lock
, LK_RELEASE
);
342 error
= SYSCTL_OUT(req
, &spc
, sizeof(spc
));
346 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, drvlist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
347 NULL
, 0, iconv_sysctl_drvlist
, "S,xlat", "registered converters");
350 * List all available charset pairs.
353 iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS
)
355 struct iconv_cspair
*csp
;
356 struct iconv_cspair_info csi
;
360 bzero(&csi
, sizeof(csi
));
361 csi
.cs_version
= ICONV_CSPAIR_INFO_VER
;
363 lockmgr(&iconv_lock
, LK_SHARED
);
364 TAILQ_FOREACH(csp
, &iconv_cslist
, cp_link
) {
365 csi
.cs_id
= csp
->cp_id
;
366 csi
.cs_refcount
= csp
->cp_refcount
;
367 csi
.cs_base
= csp
->cp_base
? csp
->cp_base
->cp_id
: 0;
368 strcpy(csi
.cs_to
, csp
->cp_to
);
369 strcpy(csi
.cs_from
, csp
->cp_from
);
370 error
= SYSCTL_OUT(req
, &csi
, sizeof(csi
));
374 lockmgr(&iconv_lock
, LK_RELEASE
);
378 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, cslist
, CTLFLAG_RD
| CTLTYPE_OPAQUE
,
379 NULL
, 0, iconv_sysctl_cslist
, "S,xlat", "registered charset pairs");
382 iconv_add(const char *converter
, const char *to
, const char *from
)
384 struct iconv_converter_class
*dcp
;
385 struct iconv_cspair
*csp
;
387 if (iconv_lookupconv(converter
, &dcp
) != 0)
390 return iconv_register_cspair(to
, from
, dcp
, NULL
, &csp
);
394 * Add new charset pair
397 iconv_sysctl_add(SYSCTL_HANDLER_ARGS
)
399 struct iconv_converter_class
*dcp
;
400 struct iconv_cspair
*csp
;
401 struct iconv_add_in din
;
402 struct iconv_add_out dout
;
405 error
= SYSCTL_IN(req
, &din
, sizeof(din
));
408 if (din
.ia_version
!= ICONV_ADD_VER
)
410 if (din
.ia_datalen
> ICONV_CSMAXDATALEN
)
412 if (strlen(din
.ia_from
) >= ICONV_CSNMAXLEN
)
414 if (strlen(din
.ia_to
) >= ICONV_CSNMAXLEN
)
416 if (strlen(din
.ia_converter
) >= ICONV_CNVNMAXLEN
)
418 if (iconv_lookupconv(din
.ia_converter
, &dcp
) != 0)
420 lockmgr(&iconv_lock
, LK_EXCLUSIVE
);
421 error
= iconv_register_cspair(din
.ia_to
, din
.ia_from
, dcp
, NULL
, &csp
);
423 lockmgr(&iconv_lock
, LK_RELEASE
);
426 if (din
.ia_datalen
) {
427 csp
->cp_data
= kmalloc(din
.ia_datalen
, M_ICONVDATA
, M_WAITOK
);
428 error
= copyin(din
.ia_data
, csp
->cp_data
, din
.ia_datalen
);
432 dout
.ia_csid
= csp
->cp_id
;
433 error
= SYSCTL_OUT(req
, &dout
, sizeof(dout
));
436 lockmgr(&iconv_lock
, LK_RELEASE
);
437 ICDEBUG("%s => %s, %d bytes\n",din
.ia_from
, din
.ia_to
, din
.ia_datalen
);
440 iconv_unregister_cspair(csp
);
441 lockmgr(&iconv_lock
, LK_RELEASE
);
445 SYSCTL_PROC(_kern_iconv
, OID_AUTO
, add
, CTLFLAG_RW
| CTLTYPE_OPAQUE
,
446 NULL
, 0, iconv_sysctl_add
, "S,xlat", "register charset pair");
449 * Default stubs for converters
452 iconv_converter_initstub(struct iconv_converter_class
*dp
)
458 iconv_converter_donestub(struct iconv_converter_class
*dp
)
464 iconv_converter_tolowerstub(int c
, void *handle
)
470 iconv_converter_handler(module_t mod
, int type
, void *data
)
472 struct iconv_converter_class
*dcp
= data
;
477 lockmgr(&iconv_lock
, LK_EXCLUSIVE
);
478 error
= iconv_register_converter(dcp
);
480 lockmgr(&iconv_lock
, LK_RELEASE
);
483 error
= ICONV_CONVERTER_INIT(dcp
);
485 iconv_unregister_converter(dcp
);
486 lockmgr(&iconv_lock
, LK_RELEASE
);
489 lockmgr(&iconv_lock
, LK_EXCLUSIVE
);
490 ICONV_CONVERTER_DONE(dcp
);
491 error
= iconv_unregister_converter(dcp
);
492 lockmgr(&iconv_lock
, LK_RELEASE
);
501 * Common used functions (don't use with unicode)
504 iconv_convstr(void *handle
, char *dst
, const char *src
)
507 size_t inlen
, outlen
;
510 if (handle
== NULL
) {
514 inlen
= outlen
= strlen(src
);
515 error
= iconv_conv(handle
, NULL
, NULL
, &p
, &outlen
);
518 error
= iconv_conv(handle
, &src
, &inlen
, &p
, &outlen
);
526 iconv_convmem(void *handle
, void *dst
, const void *src
, int size
)
530 size_t inlen
, outlen
;
535 if (handle
== NULL
) {
536 memcpy(dst
, src
, size
);
539 inlen
= outlen
= size
;
540 error
= iconv_conv(handle
, NULL
, NULL
, &d
, &outlen
);
543 error
= iconv_conv(handle
, &s
, &inlen
, &d
, &outlen
);
550 iconv_lookupcp(char **cpp
, const char *s
)
553 ICDEBUG("warning a NULL list passed\n", "");
557 if (strcmp(*cpp
, s
) == 0)
563 * Return if fsname is in use of not
566 iconv_vfs_refcount(const char *fsname
)
568 struct vfsconf
*vfsp
;
570 vfsp
= vfsconf_find_by_name(fsname
);
571 if (vfsp
!= NULL
&& vfsp
->vfc_refcount
> 0)