xref: /freebsd-src/sys/libkern/iconv.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1d6ea0262SWarner Losh /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni  *
4d122d784SJoel Dahl  * Copyright (c) 2000-2001 Boris Popov
56f2d8adbSBoris Popov  * All rights reserved.
66f2d8adbSBoris Popov  *
76f2d8adbSBoris Popov  * Redistribution and use in source and binary forms, with or without
86f2d8adbSBoris Popov  * modification, are permitted provided that the following conditions
96f2d8adbSBoris Popov  * are met:
106f2d8adbSBoris Popov  * 1. Redistributions of source code must retain the above copyright
116f2d8adbSBoris Popov  *    notice, this list of conditions and the following disclaimer.
126f2d8adbSBoris Popov  * 2. Redistributions in binary form must reproduce the above copyright
136f2d8adbSBoris Popov  *    notice, this list of conditions and the following disclaimer in the
146f2d8adbSBoris Popov  *    documentation and/or other materials provided with the distribution.
156f2d8adbSBoris Popov  *
166f2d8adbSBoris Popov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
176f2d8adbSBoris Popov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
186f2d8adbSBoris Popov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
196f2d8adbSBoris Popov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
206f2d8adbSBoris Popov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
216f2d8adbSBoris Popov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
226f2d8adbSBoris Popov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
236f2d8adbSBoris Popov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
246f2d8adbSBoris Popov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256f2d8adbSBoris Popov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266f2d8adbSBoris Popov  * SUCH DAMAGE.
276f2d8adbSBoris Popov  */
28ab0de15bSDavid E. O'Brien 
296f2d8adbSBoris Popov #include <sys/param.h>
306f2d8adbSBoris Popov #include <sys/systm.h>
316f2d8adbSBoris Popov #include <sys/kernel.h>
326f2d8adbSBoris Popov #include <sys/iconv.h>
336f2d8adbSBoris Popov #include <sys/malloc.h>
34c4f02a89SMax Khon #include <sys/mount.h>
35ee03ef72SJohn Baldwin #include <sys/sx.h>
36c4f02a89SMax Khon #include <sys/syslog.h>
376f2d8adbSBoris Popov 
386f2d8adbSBoris Popov #include "iconv_converter_if.h"
396f2d8adbSBoris Popov 
406f2d8adbSBoris Popov SYSCTL_DECL(_kern_iconv);
416f2d8adbSBoris Popov 
427029da5cSPawel Biernacki SYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
437029da5cSPawel Biernacki     "kernel iconv interface");
446f2d8adbSBoris Popov 
455bb84bc8SRobert Watson MALLOC_DEFINE(M_ICONV, "iconv", "ICONV structures");
46d745c852SEd Schouten static MALLOC_DEFINE(M_ICONVDATA, "iconv_data", "ICONV data");
476f2d8adbSBoris Popov 
48c4f02a89SMax Khon MODULE_VERSION(libiconv, 2);
496f2d8adbSBoris Popov 
50ee03ef72SJohn Baldwin static struct sx iconv_lock;
51ee03ef72SJohn Baldwin 
526f2d8adbSBoris Popov #ifdef notnow
536f2d8adbSBoris Popov /*
546f2d8adbSBoris Popov  * iconv converter instance
556f2d8adbSBoris Popov  */
566f2d8adbSBoris Popov struct iconv_converter {
576f2d8adbSBoris Popov 	KOBJ_FIELDS;
586f2d8adbSBoris Popov 	void *			c_data;
596f2d8adbSBoris Popov };
606f2d8adbSBoris Popov #endif
616f2d8adbSBoris Popov 
626f2d8adbSBoris Popov struct sysctl_oid *iconv_oid_hook = &sysctl___kern_iconv;
636f2d8adbSBoris Popov 
646f2d8adbSBoris Popov /*
656f2d8adbSBoris Popov  * List of loaded converters
666f2d8adbSBoris Popov  */
676f2d8adbSBoris Popov static TAILQ_HEAD(iconv_converter_list, iconv_converter_class)
686f2d8adbSBoris Popov     iconv_converters = TAILQ_HEAD_INITIALIZER(iconv_converters);
696f2d8adbSBoris Popov 
706f2d8adbSBoris Popov /*
716f2d8adbSBoris Popov  * List of supported/loaded charsets pairs
726f2d8adbSBoris Popov  */
736f2d8adbSBoris Popov static TAILQ_HEAD(, iconv_cspair)
746f2d8adbSBoris Popov     iconv_cslist = TAILQ_HEAD_INITIALIZER(iconv_cslist);
756f2d8adbSBoris Popov static int iconv_csid = 1;
766f2d8adbSBoris Popov 
776f2d8adbSBoris Popov static char iconv_unicode_string[] = "unicode";	/* save eight bytes when possible */
786f2d8adbSBoris Popov 
796f2d8adbSBoris Popov static void iconv_unregister_cspair(struct iconv_cspair *csp);
806f2d8adbSBoris Popov 
816f2d8adbSBoris Popov static int
iconv_mod_unload(void)826f2d8adbSBoris Popov iconv_mod_unload(void)
836f2d8adbSBoris Popov {
846f2d8adbSBoris Popov 	struct iconv_cspair *csp;
856f2d8adbSBoris Popov 
86ee03ef72SJohn Baldwin 	sx_xlock(&iconv_lock);
87414d9b2eSMateusz Guzik 	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
88414d9b2eSMateusz Guzik 		if (csp->cp_refcount) {
89414d9b2eSMateusz Guzik 			sx_xunlock(&iconv_lock);
906f2d8adbSBoris Popov 			return EBUSY;
916f2d8adbSBoris Popov 		}
92414d9b2eSMateusz Guzik 	}
93ee03ef72SJohn Baldwin 
94ee03ef72SJohn Baldwin 	while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL)
95ee03ef72SJohn Baldwin 		iconv_unregister_cspair(csp);
96ee03ef72SJohn Baldwin 	sx_xunlock(&iconv_lock);
97ee03ef72SJohn Baldwin 	sx_destroy(&iconv_lock);
986f2d8adbSBoris Popov 	return 0;
996f2d8adbSBoris Popov }
1006f2d8adbSBoris Popov 
1016f2d8adbSBoris Popov static int
iconv_mod_handler(module_t mod,int type,void * data)1026f2d8adbSBoris Popov iconv_mod_handler(module_t mod, int type, void *data)
1036f2d8adbSBoris Popov {
1046f2d8adbSBoris Popov 	int error;
1056f2d8adbSBoris Popov 
1066f2d8adbSBoris Popov 	switch (type) {
1076f2d8adbSBoris Popov 	    case MOD_LOAD:
1086f2d8adbSBoris Popov 		error = 0;
109ee03ef72SJohn Baldwin 		sx_init(&iconv_lock, "iconv");
1106f2d8adbSBoris Popov 		break;
1116f2d8adbSBoris Popov 	    case MOD_UNLOAD:
1126f2d8adbSBoris Popov 		error = iconv_mod_unload();
1136f2d8adbSBoris Popov 		break;
1146f2d8adbSBoris Popov 	    default:
1156f2d8adbSBoris Popov 		error = EINVAL;
1166f2d8adbSBoris Popov 	}
1176f2d8adbSBoris Popov 	return error;
1186f2d8adbSBoris Popov }
1196f2d8adbSBoris Popov 
1206f2d8adbSBoris Popov static moduledata_t iconv_mod = {
1216f2d8adbSBoris Popov 	"iconv", iconv_mod_handler, NULL
1226f2d8adbSBoris Popov };
1236f2d8adbSBoris Popov 
1246f2d8adbSBoris Popov DECLARE_MODULE(iconv, iconv_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
1256f2d8adbSBoris Popov 
1266f2d8adbSBoris Popov static int
iconv_register_converter(struct iconv_converter_class * dcp)1276f2d8adbSBoris Popov iconv_register_converter(struct iconv_converter_class *dcp)
1286f2d8adbSBoris Popov {
1296f2d8adbSBoris Popov 	kobj_class_compile((struct kobj_class*)dcp);
1306f2d8adbSBoris Popov 	dcp->refs++;
1316f2d8adbSBoris Popov 	TAILQ_INSERT_TAIL(&iconv_converters, dcp, cc_link);
1326f2d8adbSBoris Popov 	return 0;
1336f2d8adbSBoris Popov }
1346f2d8adbSBoris Popov 
1356f2d8adbSBoris Popov static int
iconv_unregister_converter(struct iconv_converter_class * dcp)1366f2d8adbSBoris Popov iconv_unregister_converter(struct iconv_converter_class *dcp)
1376f2d8adbSBoris Popov {
138040b0e4eSKevin Lo 	dcp->refs--;
1396f2d8adbSBoris Popov 	if (dcp->refs > 1) {
14004660ed1SEitan Adler 		ICDEBUG("converter has %d references left\n", dcp->refs);
1416f2d8adbSBoris Popov 		return EBUSY;
1426f2d8adbSBoris Popov 	}
1436f2d8adbSBoris Popov 	TAILQ_REMOVE(&iconv_converters, dcp, cc_link);
1446f2d8adbSBoris Popov 	kobj_class_free((struct kobj_class*)dcp);
1456f2d8adbSBoris Popov 	return 0;
1466f2d8adbSBoris Popov }
1476f2d8adbSBoris Popov 
1486f2d8adbSBoris Popov static int
iconv_lookupconv(const char * name,struct iconv_converter_class ** dcpp)1496f2d8adbSBoris Popov iconv_lookupconv(const char *name, struct iconv_converter_class **dcpp)
1506f2d8adbSBoris Popov {
1516f2d8adbSBoris Popov 	struct iconv_converter_class *dcp;
1526f2d8adbSBoris Popov 
1536f2d8adbSBoris Popov 	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
1546f2d8adbSBoris Popov 		if (name == NULL)
1556f2d8adbSBoris Popov 			continue;
1566f2d8adbSBoris Popov 		if (strcmp(name, ICONV_CONVERTER_NAME(dcp)) == 0) {
1576f2d8adbSBoris Popov 			if (dcpp)
1586f2d8adbSBoris Popov 				*dcpp = dcp;
1596f2d8adbSBoris Popov 			return 0;
1606f2d8adbSBoris Popov 		}
1616f2d8adbSBoris Popov 	}
1626f2d8adbSBoris Popov 	return ENOENT;
1636f2d8adbSBoris Popov }
1646f2d8adbSBoris Popov 
1656f2d8adbSBoris Popov static int
iconv_lookupcs(const char * to,const char * from,struct iconv_cspair ** cspp)1666f2d8adbSBoris Popov iconv_lookupcs(const char *to, const char *from, struct iconv_cspair **cspp)
1676f2d8adbSBoris Popov {
1686f2d8adbSBoris Popov 	struct iconv_cspair *csp;
1696f2d8adbSBoris Popov 
1706f2d8adbSBoris Popov 	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
1710cbce067SJohn Baldwin 		if (strcasecmp(csp->cp_to, to) == 0 &&
1720cbce067SJohn Baldwin 		    strcasecmp(csp->cp_from, from) == 0) {
1736f2d8adbSBoris Popov 			if (cspp)
1746f2d8adbSBoris Popov 				*cspp = csp;
1756f2d8adbSBoris Popov 			return 0;
1766f2d8adbSBoris Popov 		}
1776f2d8adbSBoris Popov 	}
1786f2d8adbSBoris Popov 	return ENOENT;
1796f2d8adbSBoris Popov }
1806f2d8adbSBoris Popov 
1816f2d8adbSBoris Popov static int
iconv_register_cspair(const char * to,const char * from,struct iconv_converter_class * dcp,void * data,struct iconv_cspair ** cspp)1826f2d8adbSBoris Popov iconv_register_cspair(const char *to, const char *from,
1836f2d8adbSBoris Popov 	struct iconv_converter_class *dcp, void *data,
1846f2d8adbSBoris Popov 	struct iconv_cspair **cspp)
1856f2d8adbSBoris Popov {
1866f2d8adbSBoris Popov 	struct iconv_cspair *csp;
1876f2d8adbSBoris Popov 	char *cp;
1886f2d8adbSBoris Popov 	int csize, ucsto, ucsfrom;
1896f2d8adbSBoris Popov 
1906f2d8adbSBoris Popov 	if (iconv_lookupcs(to, from, NULL) == 0)
1916f2d8adbSBoris Popov 		return EEXIST;
1926f2d8adbSBoris Popov 	csize = sizeof(*csp);
1936f2d8adbSBoris Popov 	ucsto = strcmp(to, iconv_unicode_string) == 0;
1946f2d8adbSBoris Popov 	if (!ucsto)
1956f2d8adbSBoris Popov 		csize += strlen(to) + 1;
1966f2d8adbSBoris Popov 	ucsfrom = strcmp(from, iconv_unicode_string) == 0;
1976f2d8adbSBoris Popov 	if (!ucsfrom)
1986f2d8adbSBoris Popov 		csize += strlen(from) + 1;
199a163d034SWarner Losh 	csp = malloc(csize, M_ICONV, M_WAITOK);
2006f2d8adbSBoris Popov 	bzero(csp, csize);
2016f2d8adbSBoris Popov 	csp->cp_id = iconv_csid++;
2026f2d8adbSBoris Popov 	csp->cp_dcp = dcp;
2036f2d8adbSBoris Popov 	cp = (char*)(csp + 1);
2046f2d8adbSBoris Popov 	if (!ucsto) {
2056f2d8adbSBoris Popov 		strcpy(cp, to);
2066f2d8adbSBoris Popov 		csp->cp_to = cp;
2076f2d8adbSBoris Popov 		cp += strlen(cp) + 1;
2086f2d8adbSBoris Popov 	} else
2096f2d8adbSBoris Popov 		csp->cp_to = iconv_unicode_string;
2106f2d8adbSBoris Popov 	if (!ucsfrom) {
2116f2d8adbSBoris Popov 		strcpy(cp, from);
2126f2d8adbSBoris Popov 		csp->cp_from = cp;
2136f2d8adbSBoris Popov 	} else
2146f2d8adbSBoris Popov 		csp->cp_from = iconv_unicode_string;
2156f2d8adbSBoris Popov 	csp->cp_data = data;
2166f2d8adbSBoris Popov 
2176f2d8adbSBoris Popov 	TAILQ_INSERT_TAIL(&iconv_cslist, csp, cp_link);
2186f2d8adbSBoris Popov 	*cspp = csp;
2196f2d8adbSBoris Popov 	return 0;
2206f2d8adbSBoris Popov }
2216f2d8adbSBoris Popov 
2226f2d8adbSBoris Popov static void
iconv_unregister_cspair(struct iconv_cspair * csp)2236f2d8adbSBoris Popov iconv_unregister_cspair(struct iconv_cspair *csp)
2246f2d8adbSBoris Popov {
2256f2d8adbSBoris Popov 	TAILQ_REMOVE(&iconv_cslist, csp, cp_link);
2266f2d8adbSBoris Popov 	if (csp->cp_data)
2276f2d8adbSBoris Popov 		free(csp->cp_data, M_ICONVDATA);
2286f2d8adbSBoris Popov 	free(csp, M_ICONV);
2296f2d8adbSBoris Popov }
2306f2d8adbSBoris Popov 
2316f2d8adbSBoris Popov /*
2326f2d8adbSBoris Popov  * Lookup and create an instance of converter.
2336f2d8adbSBoris Popov  * Currently this layer didn't have associated 'instance' structure
2346f2d8adbSBoris Popov  * to avoid unnesessary memory allocation.
2356f2d8adbSBoris Popov  */
2366f2d8adbSBoris Popov int
iconv_open(const char * to,const char * from,void ** handle)2376f2d8adbSBoris Popov iconv_open(const char *to, const char *from, void **handle)
2386f2d8adbSBoris Popov {
2396f2d8adbSBoris Popov 	struct iconv_cspair *csp, *cspfrom, *cspto;
2406f2d8adbSBoris Popov 	struct iconv_converter_class *dcp;
2416f2d8adbSBoris Popov 	const char *cnvname;
2426f2d8adbSBoris Popov 	int error;
2436f2d8adbSBoris Popov 
2446f2d8adbSBoris Popov 	/*
2456f2d8adbSBoris Popov 	 * First, lookup fully qualified cspairs
2466f2d8adbSBoris Popov 	 */
2476f2d8adbSBoris Popov 	error = iconv_lookupcs(to, from, &csp);
2486f2d8adbSBoris Popov 	if (error == 0)
2496f2d8adbSBoris Popov 		return ICONV_CONVERTER_OPEN(csp->cp_dcp, csp, NULL, handle);
2506f2d8adbSBoris Popov 
2516f2d8adbSBoris Popov 	/*
2526f2d8adbSBoris Popov 	 * Well, nothing found. Now try to construct a composite conversion
2536f2d8adbSBoris Popov 	 * ToDo: add a 'capability' field to converter
2546f2d8adbSBoris Popov 	 */
2556f2d8adbSBoris Popov 	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
2566f2d8adbSBoris Popov 		cnvname = ICONV_CONVERTER_NAME(dcp);
2576f2d8adbSBoris Popov 		if (cnvname == NULL)
2586f2d8adbSBoris Popov 			continue;
2596f2d8adbSBoris Popov 		error = iconv_lookupcs(cnvname, from, &cspfrom);
2606f2d8adbSBoris Popov 		if (error)
2616f2d8adbSBoris Popov 			continue;
2626f2d8adbSBoris Popov 		error = iconv_lookupcs(to, cnvname, &cspto);
2636f2d8adbSBoris Popov 		if (error)
2646f2d8adbSBoris Popov 			continue;
2656f2d8adbSBoris Popov 		/*
2666f2d8adbSBoris Popov 		 * Fine, we're found a pair which can be combined together
2676f2d8adbSBoris Popov 		 */
2686f2d8adbSBoris Popov 		return ICONV_CONVERTER_OPEN(dcp, cspto, cspfrom, handle);
2696f2d8adbSBoris Popov 	}
2706f2d8adbSBoris Popov 	return ENOENT;
2716f2d8adbSBoris Popov }
2726f2d8adbSBoris Popov 
2736f2d8adbSBoris Popov int
iconv_close(void * handle)2746f2d8adbSBoris Popov iconv_close(void *handle)
2756f2d8adbSBoris Popov {
2766f2d8adbSBoris Popov 	return ICONV_CONVERTER_CLOSE(handle);
2776f2d8adbSBoris Popov }
2786f2d8adbSBoris Popov 
2796f2d8adbSBoris Popov int
iconv_conv(void * handle,const char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft)2806f2d8adbSBoris Popov iconv_conv(void *handle, const char **inbuf,
2816f2d8adbSBoris Popov 	size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
2826f2d8adbSBoris Popov {
283c4f02a89SMax Khon 	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, 0);
284c4f02a89SMax Khon }
285c4f02a89SMax Khon 
286c4f02a89SMax Khon int
iconv_conv_case(void * handle,const char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft,int casetype)287c4f02a89SMax Khon iconv_conv_case(void *handle, const char **inbuf,
288c4f02a89SMax Khon 	size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
289c4f02a89SMax Khon {
290c4f02a89SMax Khon 	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, casetype);
291c4f02a89SMax Khon }
292c4f02a89SMax Khon 
293c4f02a89SMax Khon int
iconv_convchr(void * handle,const char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft)294c4f02a89SMax Khon iconv_convchr(void *handle, const char **inbuf,
295c4f02a89SMax Khon 	size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
296c4f02a89SMax Khon {
297c4f02a89SMax Khon 	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, 0);
298c4f02a89SMax Khon }
299c4f02a89SMax Khon 
300c4f02a89SMax Khon int
iconv_convchr_case(void * handle,const char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft,int casetype)301c4f02a89SMax Khon iconv_convchr_case(void *handle, const char **inbuf,
302c4f02a89SMax Khon 	size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
303c4f02a89SMax Khon {
304c4f02a89SMax Khon 	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, casetype);
3056f2d8adbSBoris Popov }
3066f2d8adbSBoris Popov 
3076ac937c8SXin LI int
towlower(int c,void * handle)3086ac937c8SXin LI towlower(int c, void *handle)
3096ac937c8SXin LI {
3106ac937c8SXin LI 	return ICONV_CONVERTER_TOLOWER(handle, c);
3116ac937c8SXin LI }
3126ac937c8SXin LI 
3136ac937c8SXin LI int
towupper(int c,void * handle)3146ac937c8SXin LI towupper(int c, void *handle)
3156ac937c8SXin LI {
3166ac937c8SXin LI 	return ICONV_CONVERTER_TOUPPER(handle, c);
3176ac937c8SXin LI }
3186ac937c8SXin LI 
3196f2d8adbSBoris Popov /*
3206f2d8adbSBoris Popov  * Give a list of loaded converters. Each name terminated with 0.
3216f2d8adbSBoris Popov  * An empty string terminates the list.
3226f2d8adbSBoris Popov  */
3236f2d8adbSBoris Popov static int
iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS)3246f2d8adbSBoris Popov iconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS)
3256f2d8adbSBoris Popov {
3266f2d8adbSBoris Popov 	struct iconv_converter_class *dcp;
3276f2d8adbSBoris Popov 	const char *name;
3286f2d8adbSBoris Popov 	char spc;
3296f2d8adbSBoris Popov 	int error;
3306f2d8adbSBoris Popov 
3316f2d8adbSBoris Popov 	error = 0;
332ee03ef72SJohn Baldwin 	sx_slock(&iconv_lock);
3336f2d8adbSBoris Popov 	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
3346f2d8adbSBoris Popov 		name = ICONV_CONVERTER_NAME(dcp);
3356f2d8adbSBoris Popov 		if (name == NULL)
3366f2d8adbSBoris Popov 			continue;
3376f2d8adbSBoris Popov 		error = SYSCTL_OUT(req, name, strlen(name) + 1);
3386f2d8adbSBoris Popov 		if (error)
3396f2d8adbSBoris Popov 			break;
3406f2d8adbSBoris Popov 	}
341ee03ef72SJohn Baldwin 	sx_sunlock(&iconv_lock);
3426f2d8adbSBoris Popov 	if (error)
3436f2d8adbSBoris Popov 		return error;
3446f2d8adbSBoris Popov 	spc = 0;
3456f2d8adbSBoris Popov 	error = SYSCTL_OUT(req, &spc, sizeof(spc));
3466f2d8adbSBoris Popov 	return error;
3476f2d8adbSBoris Popov }
3486f2d8adbSBoris Popov 
3497029da5cSPawel Biernacki SYSCTL_PROC(_kern_iconv, OID_AUTO, drvlist,
3507029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0,
3517029da5cSPawel Biernacki     iconv_sysctl_drvlist, "S,xlat",
3527029da5cSPawel Biernacki     "registered converters");
3536f2d8adbSBoris Popov 
3546f2d8adbSBoris Popov /*
3556f2d8adbSBoris Popov  * List all available charset pairs.
3566f2d8adbSBoris Popov  */
3576f2d8adbSBoris Popov static int
iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS)3586f2d8adbSBoris Popov iconv_sysctl_cslist(SYSCTL_HANDLER_ARGS)
3596f2d8adbSBoris Popov {
3606f2d8adbSBoris Popov 	struct iconv_cspair *csp;
3616f2d8adbSBoris Popov 	struct iconv_cspair_info csi;
3626f2d8adbSBoris Popov 	int error;
3636f2d8adbSBoris Popov 
3646f2d8adbSBoris Popov 	error = 0;
3656f2d8adbSBoris Popov 	bzero(&csi, sizeof(csi));
3666f2d8adbSBoris Popov 	csi.cs_version = ICONV_CSPAIR_INFO_VER;
367ee03ef72SJohn Baldwin 	sx_slock(&iconv_lock);
3686f2d8adbSBoris Popov 	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
3696f2d8adbSBoris Popov 		csi.cs_id = csp->cp_id;
3706f2d8adbSBoris Popov 		csi.cs_refcount = csp->cp_refcount;
3716f2d8adbSBoris Popov 		csi.cs_base = csp->cp_base ? csp->cp_base->cp_id : 0;
3726f2d8adbSBoris Popov 		strcpy(csi.cs_to, csp->cp_to);
3736f2d8adbSBoris Popov 		strcpy(csi.cs_from, csp->cp_from);
3746f2d8adbSBoris Popov 		error = SYSCTL_OUT(req, &csi, sizeof(csi));
3756f2d8adbSBoris Popov 		if (error)
3766f2d8adbSBoris Popov 			break;
3776f2d8adbSBoris Popov 	}
378ee03ef72SJohn Baldwin 	sx_sunlock(&iconv_lock);
3796f2d8adbSBoris Popov 	return error;
3806f2d8adbSBoris Popov }
3816f2d8adbSBoris Popov 
3827029da5cSPawel Biernacki SYSCTL_PROC(_kern_iconv, OID_AUTO, cslist,
3837029da5cSPawel Biernacki     CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0,
3847029da5cSPawel Biernacki     iconv_sysctl_cslist, "S,xlat",
3857029da5cSPawel Biernacki     "registered charset pairs");
3866f2d8adbSBoris Popov 
38741f1dcccSKevin Lo int
iconv_add(const char * converter,const char * to,const char * from)38841f1dcccSKevin Lo iconv_add(const char *converter, const char *to, const char *from)
38941f1dcccSKevin Lo {
39041f1dcccSKevin Lo 	struct iconv_converter_class *dcp;
39141f1dcccSKevin Lo 	struct iconv_cspair *csp;
39241f1dcccSKevin Lo 
39341f1dcccSKevin Lo 	if (iconv_lookupconv(converter, &dcp) != 0)
39441f1dcccSKevin Lo 		return EINVAL;
39541f1dcccSKevin Lo 
39641f1dcccSKevin Lo 	return iconv_register_cspair(to, from, dcp, NULL, &csp);
39741f1dcccSKevin Lo }
39841f1dcccSKevin Lo 
3996f2d8adbSBoris Popov /*
4006f2d8adbSBoris Popov  * Add new charset pair
4016f2d8adbSBoris Popov  */
4026f2d8adbSBoris Popov static int
iconv_sysctl_add(SYSCTL_HANDLER_ARGS)4036f2d8adbSBoris Popov iconv_sysctl_add(SYSCTL_HANDLER_ARGS)
4046f2d8adbSBoris Popov {
4056f2d8adbSBoris Popov 	struct iconv_converter_class *dcp;
4066f2d8adbSBoris Popov 	struct iconv_cspair *csp;
4076f2d8adbSBoris Popov 	struct iconv_add_in din;
4086f2d8adbSBoris Popov 	struct iconv_add_out dout;
4096f2d8adbSBoris Popov 	int error;
4106f2d8adbSBoris Popov 
4116f2d8adbSBoris Popov 	error = SYSCTL_IN(req, &din, sizeof(din));
4126f2d8adbSBoris Popov 	if (error)
4136f2d8adbSBoris Popov 		return error;
4146f2d8adbSBoris Popov 	if (din.ia_version != ICONV_ADD_VER)
4156f2d8adbSBoris Popov 		return EINVAL;
4166f2d8adbSBoris Popov 	if (din.ia_datalen > ICONV_CSMAXDATALEN)
4176f2d8adbSBoris Popov 		return EINVAL;
4182b08b42bSDavid Bright 	if (strnlen(din.ia_from, sizeof(din.ia_from)) >= ICONV_CSNMAXLEN)
419f373a824SR. Imura 		return EINVAL;
4202b08b42bSDavid Bright 	if (strnlen(din.ia_to, sizeof(din.ia_to)) >= ICONV_CSNMAXLEN)
421f373a824SR. Imura 		return EINVAL;
4222b08b42bSDavid Bright 	if (strnlen(din.ia_converter, sizeof(din.ia_converter)) >= ICONV_CNVNMAXLEN)
423f373a824SR. Imura 		return EINVAL;
4246f2d8adbSBoris Popov 	if (iconv_lookupconv(din.ia_converter, &dcp) != 0)
4256f2d8adbSBoris Popov 		return EINVAL;
426ee03ef72SJohn Baldwin 	sx_xlock(&iconv_lock);
4276f2d8adbSBoris Popov 	error = iconv_register_cspair(din.ia_to, din.ia_from, dcp, NULL, &csp);
428ee03ef72SJohn Baldwin 	if (error) {
429ee03ef72SJohn Baldwin 		sx_xunlock(&iconv_lock);
4306f2d8adbSBoris Popov 		return error;
431ee03ef72SJohn Baldwin 	}
4326f2d8adbSBoris Popov 	if (din.ia_datalen) {
433a163d034SWarner Losh 		csp->cp_data = malloc(din.ia_datalen, M_ICONVDATA, M_WAITOK);
4346f2d8adbSBoris Popov 		error = copyin(din.ia_data, csp->cp_data, din.ia_datalen);
4356f2d8adbSBoris Popov 		if (error)
4366f2d8adbSBoris Popov 			goto bad;
4376f2d8adbSBoris Popov 	}
4386f2d8adbSBoris Popov 	dout.ia_csid = csp->cp_id;
4396f2d8adbSBoris Popov 	error = SYSCTL_OUT(req, &dout, sizeof(dout));
4406f2d8adbSBoris Popov 	if (error)
4416f2d8adbSBoris Popov 		goto bad;
442ee03ef72SJohn Baldwin 	sx_xunlock(&iconv_lock);
443c4f02a89SMax Khon 	ICDEBUG("%s => %s, %d bytes\n",din.ia_from, din.ia_to, din.ia_datalen);
4446f2d8adbSBoris Popov 	return 0;
4456f2d8adbSBoris Popov bad:
4466f2d8adbSBoris Popov 	iconv_unregister_cspair(csp);
447ee03ef72SJohn Baldwin 	sx_xunlock(&iconv_lock);
4486f2d8adbSBoris Popov 	return error;
4496f2d8adbSBoris Popov }
4506f2d8adbSBoris Popov 
4517029da5cSPawel Biernacki SYSCTL_PROC(_kern_iconv, OID_AUTO, add,
4527029da5cSPawel Biernacki     CTLFLAG_RW | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0,
4537029da5cSPawel Biernacki     iconv_sysctl_add, "S,xlat",
4547029da5cSPawel Biernacki     "register charset pair");
4556f2d8adbSBoris Popov 
4566f2d8adbSBoris Popov /*
4576f2d8adbSBoris Popov  * Default stubs for converters
4586f2d8adbSBoris Popov  */
4596f2d8adbSBoris Popov int
iconv_converter_initstub(struct iconv_converter_class * dp)4606f2d8adbSBoris Popov iconv_converter_initstub(struct iconv_converter_class *dp)
4616f2d8adbSBoris Popov {
4626f2d8adbSBoris Popov 	return 0;
4636f2d8adbSBoris Popov }
4646f2d8adbSBoris Popov 
4656f2d8adbSBoris Popov int
iconv_converter_donestub(struct iconv_converter_class * dp)4666f2d8adbSBoris Popov iconv_converter_donestub(struct iconv_converter_class *dp)
4676f2d8adbSBoris Popov {
4686f2d8adbSBoris Popov 	return 0;
4696f2d8adbSBoris Popov }
4706f2d8adbSBoris Popov 
4716f2d8adbSBoris Popov int
iconv_converter_tolowerstub(int c,void * handle)4726ac937c8SXin LI iconv_converter_tolowerstub(int c, void *handle)
4736ac937c8SXin LI {
4746ac937c8SXin LI 	return (c);
4756ac937c8SXin LI }
4766ac937c8SXin LI 
4776ac937c8SXin LI int
iconv_converter_handler(module_t mod,int type,void * data)4786f2d8adbSBoris Popov iconv_converter_handler(module_t mod, int type, void *data)
4796f2d8adbSBoris Popov {
4806f2d8adbSBoris Popov 	struct iconv_converter_class *dcp = data;
4816f2d8adbSBoris Popov 	int error;
4826f2d8adbSBoris Popov 
4836f2d8adbSBoris Popov 	switch (type) {
4846f2d8adbSBoris Popov 	    case MOD_LOAD:
485ee03ef72SJohn Baldwin 		sx_xlock(&iconv_lock);
4866f2d8adbSBoris Popov 		error = iconv_register_converter(dcp);
487ee03ef72SJohn Baldwin 		if (error) {
488ee03ef72SJohn Baldwin 			sx_xunlock(&iconv_lock);
4896f2d8adbSBoris Popov 			break;
490ee03ef72SJohn Baldwin 		}
4916f2d8adbSBoris Popov 		error = ICONV_CONVERTER_INIT(dcp);
4926f2d8adbSBoris Popov 		if (error)
4936f2d8adbSBoris Popov 			iconv_unregister_converter(dcp);
494ee03ef72SJohn Baldwin 		sx_xunlock(&iconv_lock);
4956f2d8adbSBoris Popov 		break;
4966f2d8adbSBoris Popov 	    case MOD_UNLOAD:
497ee03ef72SJohn Baldwin 		sx_xlock(&iconv_lock);
4986f2d8adbSBoris Popov 		ICONV_CONVERTER_DONE(dcp);
4996f2d8adbSBoris Popov 		error = iconv_unregister_converter(dcp);
500ee03ef72SJohn Baldwin 		sx_xunlock(&iconv_lock);
5016f2d8adbSBoris Popov 		break;
5026f2d8adbSBoris Popov 	    default:
5036f2d8adbSBoris Popov 		error = EINVAL;
5046f2d8adbSBoris Popov 	}
5056f2d8adbSBoris Popov 	return error;
5066f2d8adbSBoris Popov }
5076f2d8adbSBoris Popov 
5086f2d8adbSBoris Popov /*
509c4f02a89SMax Khon  * Common used functions (don't use with unicode)
5106f2d8adbSBoris Popov  */
5116f2d8adbSBoris Popov char *
iconv_convstr(void * handle,char * dst,const char * src)5126f2d8adbSBoris Popov iconv_convstr(void *handle, char *dst, const char *src)
5136f2d8adbSBoris Popov {
5146f2d8adbSBoris Popov 	char *p = dst;
5155ff95627SMaxime Henrion 	size_t inlen, outlen;
5165ff95627SMaxime Henrion 	int error;
5176f2d8adbSBoris Popov 
5186f2d8adbSBoris Popov 	if (handle == NULL) {
5196f2d8adbSBoris Popov 		strcpy(dst, src);
5206f2d8adbSBoris Popov 		return dst;
5216f2d8adbSBoris Popov 	}
5228fa523fbSR. Imura 	inlen = outlen = strlen(src);
5236f2d8adbSBoris Popov 	error = iconv_conv(handle, NULL, NULL, &p, &outlen);
5246f2d8adbSBoris Popov 	if (error)
5256f2d8adbSBoris Popov 		return NULL;
5266f2d8adbSBoris Popov 	error = iconv_conv(handle, &src, &inlen, &p, &outlen);
5276f2d8adbSBoris Popov 	if (error)
5286f2d8adbSBoris Popov 		return NULL;
5296f2d8adbSBoris Popov 	*p = 0;
5306f2d8adbSBoris Popov 	return dst;
5316f2d8adbSBoris Popov }
5326f2d8adbSBoris Popov 
5336f2d8adbSBoris Popov void *
iconv_convmem(void * handle,void * dst,const void * src,int size)5346f2d8adbSBoris Popov iconv_convmem(void *handle, void *dst, const void *src, int size)
5356f2d8adbSBoris Popov {
5366f2d8adbSBoris Popov 	const char *s = src;
5376f2d8adbSBoris Popov 	char *d = dst;
5385ff95627SMaxime Henrion 	size_t inlen, outlen;
5395ff95627SMaxime Henrion 	int error;
5406f2d8adbSBoris Popov 
5416f2d8adbSBoris Popov 	if (size == 0)
5426f2d8adbSBoris Popov 		return dst;
5436f2d8adbSBoris Popov 	if (handle == NULL) {
5446f2d8adbSBoris Popov 		memcpy(dst, src, size);
5456f2d8adbSBoris Popov 		return dst;
5466f2d8adbSBoris Popov 	}
5478fa523fbSR. Imura 	inlen = outlen = size;
5486f2d8adbSBoris Popov 	error = iconv_conv(handle, NULL, NULL, &d, &outlen);
5496f2d8adbSBoris Popov 	if (error)
5506f2d8adbSBoris Popov 		return NULL;
5516f2d8adbSBoris Popov 	error = iconv_conv(handle, &s, &inlen, &d, &outlen);
5526f2d8adbSBoris Popov 	if (error)
5536f2d8adbSBoris Popov 		return NULL;
5546f2d8adbSBoris Popov 	return dst;
5556f2d8adbSBoris Popov }
5566f2d8adbSBoris Popov 
5576f2d8adbSBoris Popov int
iconv_lookupcp(char ** cpp,const char * s)5586f2d8adbSBoris Popov iconv_lookupcp(char **cpp, const char *s)
5596f2d8adbSBoris Popov {
5606f2d8adbSBoris Popov 	if (cpp == NULL) {
561f529372eSKevin Lo 		ICDEBUG("warning a NULL list passed\n", "");
5626f2d8adbSBoris Popov 		return ENOENT;
5636f2d8adbSBoris Popov 	}
5646f2d8adbSBoris Popov 	for (; *cpp; cpp++)
5656f2d8adbSBoris Popov 		if (strcmp(*cpp, s) == 0)
5666f2d8adbSBoris Popov 			return 0;
5676f2d8adbSBoris Popov 	return ENOENT;
5686f2d8adbSBoris Popov }
569c4f02a89SMax Khon 
570c4f02a89SMax Khon /*
571c4f02a89SMax Khon  * Return if fsname is in use of not
572c4f02a89SMax Khon  */
573c4f02a89SMax Khon int
iconv_vfs_refcount(const char * fsname)574c4f02a89SMax Khon iconv_vfs_refcount(const char *fsname)
575c4f02a89SMax Khon {
576c4f02a89SMax Khon 	struct vfsconf *vfsp;
577c4f02a89SMax Khon 
5783dfe213eSPoul-Henning Kamp 	vfsp = vfs_byname(fsname);
5793dfe213eSPoul-Henning Kamp 	if (vfsp != NULL && vfsp->vfc_refcount > 0)
580c4f02a89SMax Khon 		return (EBUSY);
581c4f02a89SMax Khon 	return (0);
582c4f02a89SMax Khon }
583