iconv.c revision 206361
155682Smarkm/*-
255682Smarkm * Copyright (c) 2000-2001 Boris Popov
355682Smarkm * All rights reserved.
455682Smarkm *
555682Smarkm * Redistribution and use in source and binary forms, with or without
655682Smarkm * modification, are permitted provided that the following conditions
755682Smarkm * are met:
855682Smarkm * 1. Redistributions of source code must retain the above copyright
955682Smarkm *    notice, this list of conditions and the following disclaimer.
1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer in the
1255682Smarkm *    documentation and/or other materials provided with the distribution.
1355682Smarkm *
1455682Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1555682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2255682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455682Smarkm * SUCH DAMAGE.
2555682Smarkm */
2655682Smarkm
2755682Smarkm#include <sys/cdefs.h>
2855682Smarkm__FBSDID("$FreeBSD: head/sys/libkern/iconv.c 206361 2010-04-07 16:50:38Z joel $");
2955682Smarkm
3055682Smarkm#include <sys/param.h>
3155682Smarkm#include <sys/systm.h>
3255682Smarkm#include <sys/kernel.h>
3355682Smarkm#include <sys/iconv.h>
3455682Smarkm#include <sys/malloc.h>
3555682Smarkm#include <sys/mount.h>
3655682Smarkm#include <sys/sx.h>
3755682Smarkm#include <sys/syslog.h>
3855682Smarkm
3955682Smarkm#include "iconv_converter_if.h"
4055682Smarkm
4155682SmarkmSYSCTL_DECL(_kern_iconv);
4255682Smarkm
4355682SmarkmSYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW, NULL, "kernel iconv interface");
4455682Smarkm
4555682SmarkmMALLOC_DEFINE(M_ICONV, "iconv", "ICONV structures");
4655682SmarkmMALLOC_DEFINE(M_ICONVDATA, "iconv_data", "ICONV data");
4755682Smarkm
4855682SmarkmMODULE_VERSION(libiconv, 2);
4955682Smarkm
5055682Smarkmstatic struct sx iconv_lock;
5155682Smarkm
5255682Smarkm#ifdef notnow
5355682Smarkm/*
5455682Smarkm * iconv converter instance
5555682Smarkm */
5655682Smarkmstruct iconv_converter {
5755682Smarkm	KOBJ_FIELDS;
5855682Smarkm	void *			c_data;
5955682Smarkm};
6055682Smarkm#endif
6155682Smarkm
6255682Smarkmstruct sysctl_oid *iconv_oid_hook = &sysctl___kern_iconv;
6355682Smarkm
6455682Smarkm/*
6555682Smarkm * List of loaded converters
6655682Smarkm */
6755682Smarkmstatic TAILQ_HEAD(iconv_converter_list, iconv_converter_class)
6855682Smarkm    iconv_converters = TAILQ_HEAD_INITIALIZER(iconv_converters);
6955682Smarkm
7055682Smarkm/*
7155682Smarkm * List of supported/loaded charsets pairs
7255682Smarkm */
7355682Smarkmstatic TAILQ_HEAD(, iconv_cspair)
7455682Smarkm    iconv_cslist = TAILQ_HEAD_INITIALIZER(iconv_cslist);
7555682Smarkmstatic int iconv_csid = 1;
7655682Smarkm
7755682Smarkmstatic char iconv_unicode_string[] = "unicode";	/* save eight bytes when possible */
7855682Smarkm
7955682Smarkmstatic void iconv_unregister_cspair(struct iconv_cspair *csp);
8055682Smarkm
8155682Smarkmstatic int
8255682Smarkmiconv_mod_unload(void)
8355682Smarkm{
8455682Smarkm	struct iconv_cspair *csp;
8555682Smarkm
8655682Smarkm	sx_xlock(&iconv_lock);
8755682Smarkm	while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL) {
8855682Smarkm		if (csp->cp_refcount)
8955682Smarkm			return EBUSY;
9055682Smarkm	}
9155682Smarkm
9255682Smarkm	while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL)
9355682Smarkm		iconv_unregister_cspair(csp);
9455682Smarkm	sx_xunlock(&iconv_lock);
9555682Smarkm	sx_destroy(&iconv_lock);
9655682Smarkm	return 0;
9755682Smarkm}
9855682Smarkm
9955682Smarkmstatic int
10055682Smarkmiconv_mod_handler(module_t mod, int type, void *data)
10155682Smarkm{
10255682Smarkm	int error;
10355682Smarkm
10455682Smarkm	switch (type) {
10555682Smarkm	    case MOD_LOAD:
10655682Smarkm		error = 0;
10755682Smarkm		sx_init(&iconv_lock, "iconv");
10855682Smarkm		break;
10955682Smarkm	    case MOD_UNLOAD:
11055682Smarkm		error = iconv_mod_unload();
11155682Smarkm		break;
11255682Smarkm	    default:
11355682Smarkm		error = EINVAL;
11455682Smarkm	}
11555682Smarkm	return error;
11655682Smarkm}
11755682Smarkm
11855682Smarkmstatic moduledata_t iconv_mod = {
11955682Smarkm	"iconv", iconv_mod_handler, NULL
12055682Smarkm};
12155682Smarkm
12255682SmarkmDECLARE_MODULE(iconv, iconv_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
12355682Smarkm
12455682Smarkmstatic int
12555682Smarkmiconv_register_converter(struct iconv_converter_class *dcp)
12655682Smarkm{
12755682Smarkm	kobj_class_compile((struct kobj_class*)dcp);
12855682Smarkm	dcp->refs++;
12955682Smarkm	TAILQ_INSERT_TAIL(&iconv_converters, dcp, cc_link);
13055682Smarkm	return 0;
13155682Smarkm}
13255682Smarkm
13355682Smarkmstatic int
13455682Smarkmiconv_unregister_converter(struct iconv_converter_class *dcp)
13555682Smarkm{
13655682Smarkm	if (dcp->refs > 1) {
13755682Smarkm		ICDEBUG("converter have %d referenses left\n", dcp->refs);
13855682Smarkm		return EBUSY;
13955682Smarkm	}
14055682Smarkm	TAILQ_REMOVE(&iconv_converters, dcp, cc_link);
14155682Smarkm	kobj_class_free((struct kobj_class*)dcp);
14255682Smarkm	return 0;
14355682Smarkm}
14455682Smarkm
14555682Smarkmstatic int
14655682Smarkmiconv_lookupconv(const char *name, struct iconv_converter_class **dcpp)
14755682Smarkm{
14855682Smarkm	struct iconv_converter_class *dcp;
14955682Smarkm
15055682Smarkm	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
15155682Smarkm		if (name == NULL)
15255682Smarkm			continue;
15355682Smarkm		if (strcmp(name, ICONV_CONVERTER_NAME(dcp)) == 0) {
15455682Smarkm			if (dcpp)
15555682Smarkm				*dcpp = dcp;
15655682Smarkm			return 0;
15755682Smarkm		}
15855682Smarkm	}
15955682Smarkm	return ENOENT;
16055682Smarkm}
16155682Smarkm
16255682Smarkmstatic int
16355682Smarkmiconv_lookupcs(const char *to, const char *from, struct iconv_cspair **cspp)
16455682Smarkm{
16555682Smarkm	struct iconv_cspair *csp;
16655682Smarkm
16755682Smarkm	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
16855682Smarkm		if (strcmp(csp->cp_to, to) == 0 &&
16955682Smarkm		    strcmp(csp->cp_from, from) == 0) {
17055682Smarkm			if (cspp)
17155682Smarkm				*cspp = csp;
17255682Smarkm			return 0;
17355682Smarkm		}
17455682Smarkm	}
17555682Smarkm	return ENOENT;
17655682Smarkm}
17755682Smarkm
17855682Smarkmstatic int
17955682Smarkmiconv_register_cspair(const char *to, const char *from,
18055682Smarkm	struct iconv_converter_class *dcp, void *data,
18155682Smarkm	struct iconv_cspair **cspp)
18255682Smarkm{
18355682Smarkm	struct iconv_cspair *csp;
18455682Smarkm	char *cp;
18555682Smarkm	int csize, ucsto, ucsfrom;
18655682Smarkm
18755682Smarkm	if (iconv_lookupcs(to, from, NULL) == 0)
18855682Smarkm		return EEXIST;
18955682Smarkm	csize = sizeof(*csp);
19055682Smarkm	ucsto = strcmp(to, iconv_unicode_string) == 0;
19155682Smarkm	if (!ucsto)
19255682Smarkm		csize += strlen(to) + 1;
19355682Smarkm	ucsfrom = strcmp(from, iconv_unicode_string) == 0;
19455682Smarkm	if (!ucsfrom)
19555682Smarkm		csize += strlen(from) + 1;
19655682Smarkm	csp = malloc(csize, M_ICONV, M_WAITOK);
19755682Smarkm	bzero(csp, csize);
19855682Smarkm	csp->cp_id = iconv_csid++;
19955682Smarkm	csp->cp_dcp = dcp;
20055682Smarkm	cp = (char*)(csp + 1);
20155682Smarkm	if (!ucsto) {
20255682Smarkm		strcpy(cp, to);
20355682Smarkm		csp->cp_to = cp;
20455682Smarkm		cp += strlen(cp) + 1;
20555682Smarkm	} else
20655682Smarkm		csp->cp_to = iconv_unicode_string;
20755682Smarkm	if (!ucsfrom) {
20855682Smarkm		strcpy(cp, from);
20955682Smarkm		csp->cp_from = cp;
21055682Smarkm	} else
21155682Smarkm		csp->cp_from = iconv_unicode_string;
21255682Smarkm	csp->cp_data = data;
21355682Smarkm
21455682Smarkm	TAILQ_INSERT_TAIL(&iconv_cslist, csp, cp_link);
21555682Smarkm	*cspp = csp;
21655682Smarkm	return 0;
21755682Smarkm}
21855682Smarkm
21955682Smarkmstatic void
22055682Smarkmiconv_unregister_cspair(struct iconv_cspair *csp)
22155682Smarkm{
22255682Smarkm	TAILQ_REMOVE(&iconv_cslist, csp, cp_link);
22355682Smarkm	if (csp->cp_data)
22455682Smarkm		free(csp->cp_data, M_ICONVDATA);
22555682Smarkm	free(csp, M_ICONV);
22655682Smarkm}
22755682Smarkm
22855682Smarkm/*
22955682Smarkm * Lookup and create an instance of converter.
23055682Smarkm * Currently this layer didn't have associated 'instance' structure
23155682Smarkm * to avoid unnesessary memory allocation.
23255682Smarkm */
23355682Smarkmint
23455682Smarkmiconv_open(const char *to, const char *from, void **handle)
23555682Smarkm{
23655682Smarkm	struct iconv_cspair *csp, *cspfrom, *cspto;
23755682Smarkm	struct iconv_converter_class *dcp;
23855682Smarkm	const char *cnvname;
23955682Smarkm	int error;
24055682Smarkm
24155682Smarkm	/*
24255682Smarkm	 * First, lookup fully qualified cspairs
24355682Smarkm	 */
24455682Smarkm	error = iconv_lookupcs(to, from, &csp);
24555682Smarkm	if (error == 0)
24655682Smarkm		return ICONV_CONVERTER_OPEN(csp->cp_dcp, csp, NULL, handle);
24755682Smarkm
24855682Smarkm	/*
24955682Smarkm	 * Well, nothing found. Now try to construct a composite conversion
25055682Smarkm	 * ToDo: add a 'capability' field to converter
25155682Smarkm	 */
25255682Smarkm	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
25355682Smarkm		cnvname = ICONV_CONVERTER_NAME(dcp);
25455682Smarkm		if (cnvname == NULL)
25555682Smarkm			continue;
25655682Smarkm		error = iconv_lookupcs(cnvname, from, &cspfrom);
25755682Smarkm		if (error)
25855682Smarkm			continue;
25955682Smarkm		error = iconv_lookupcs(to, cnvname, &cspto);
26055682Smarkm		if (error)
26155682Smarkm			continue;
26255682Smarkm		/*
26355682Smarkm		 * Fine, we're found a pair which can be combined together
26455682Smarkm		 */
26555682Smarkm		return ICONV_CONVERTER_OPEN(dcp, cspto, cspfrom, handle);
26655682Smarkm	}
26755682Smarkm	return ENOENT;
26855682Smarkm}
26955682Smarkm
27055682Smarkmint
27155682Smarkmiconv_close(void *handle)
27255682Smarkm{
27355682Smarkm	return ICONV_CONVERTER_CLOSE(handle);
27455682Smarkm}
27555682Smarkm
27655682Smarkmint
27755682Smarkmiconv_conv(void *handle, const char **inbuf,
27855682Smarkm	size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
27955682Smarkm{
28055682Smarkm	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, 0);
28155682Smarkm}
28255682Smarkm
28355682Smarkmint
28455682Smarkmiconv_conv_case(void *handle, const char **inbuf,
28555682Smarkm	size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
28655682Smarkm{
28755682Smarkm	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, casetype);
28855682Smarkm}
28955682Smarkm
29055682Smarkmint
29155682Smarkmiconv_convchr(void *handle, const char **inbuf,
29255682Smarkm	size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
29355682Smarkm{
29455682Smarkm	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, 0);
29555682Smarkm}
29655682Smarkm
29755682Smarkmint
29855682Smarkmiconv_convchr_case(void *handle, const char **inbuf,
29955682Smarkm	size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
30055682Smarkm{
30155682Smarkm	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, casetype);
30255682Smarkm}
30355682Smarkm
30455682Smarkmint
30555682Smarkmtowlower(int c, void *handle)
30655682Smarkm{
30755682Smarkm	return ICONV_CONVERTER_TOLOWER(handle, c);
30855682Smarkm}
30955682Smarkm
31055682Smarkmint
31155682Smarkmtowupper(int c, void *handle)
31255682Smarkm{
31355682Smarkm	return ICONV_CONVERTER_TOUPPER(handle, c);
31455682Smarkm}
31555682Smarkm
31655682Smarkm/*
31755682Smarkm * Give a list of loaded converters. Each name terminated with 0.
31855682Smarkm * An empty string terminates the list.
31955682Smarkm */
32055682Smarkmstatic int
32155682Smarkmiconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS)
32255682Smarkm{
32355682Smarkm	struct iconv_converter_class *dcp;
32455682Smarkm	const char *name;
32555682Smarkm	char spc;
32655682Smarkm	int error;
32755682Smarkm
32855682Smarkm	error = 0;
32955682Smarkm	sx_slock(&iconv_lock);
33055682Smarkm	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
33155682Smarkm		name = ICONV_CONVERTER_NAME(dcp);
33255682Smarkm		if (name == NULL)
33355682Smarkm			continue;
33455682Smarkm		error = SYSCTL_OUT(req, name, strlen(name) + 1);
33555682Smarkm		if (error)
33655682Smarkm			break;
33755682Smarkm	}
33855682Smarkm	sx_sunlock(&iconv_lock);
33955682Smarkm	if (error)
34055682Smarkm		return error;
34155682Smarkm	spc = 0;
34255682Smarkm	error = SYSCTL_OUT(req, &spc, sizeof(spc));
34355682Smarkm	return error;
34455682Smarkm}
34555682Smarkm
34655682SmarkmSYSCTL_PROC(_kern_iconv, OID_AUTO, drvlist, CTLFLAG_RD | CTLTYPE_OPAQUE,
34755682Smarkm	    NULL, 0, iconv_sysctl_drvlist, "S,xlat", "registered converters");
34855682Smarkm
34955682Smarkm/*
35055682Smarkm * List all available charset pairs.
35155682Smarkm */
35255682Smarkmstatic int
35355682Smarkmiconv_sysctl_cslist(SYSCTL_HANDLER_ARGS)
35455682Smarkm{
35555682Smarkm	struct iconv_cspair *csp;
35655682Smarkm	struct iconv_cspair_info csi;
35755682Smarkm	int error;
35855682Smarkm
35955682Smarkm	error = 0;
36055682Smarkm	bzero(&csi, sizeof(csi));
36155682Smarkm	csi.cs_version = ICONV_CSPAIR_INFO_VER;
36255682Smarkm	sx_slock(&iconv_lock);
36355682Smarkm	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
36455682Smarkm		csi.cs_id = csp->cp_id;
36555682Smarkm		csi.cs_refcount = csp->cp_refcount;
36655682Smarkm		csi.cs_base = csp->cp_base ? csp->cp_base->cp_id : 0;
36755682Smarkm		strcpy(csi.cs_to, csp->cp_to);
36855682Smarkm		strcpy(csi.cs_from, csp->cp_from);
36955682Smarkm		error = SYSCTL_OUT(req, &csi, sizeof(csi));
37055682Smarkm		if (error)
37155682Smarkm			break;
37255682Smarkm	}
37355682Smarkm	sx_sunlock(&iconv_lock);
37455682Smarkm	return error;
37555682Smarkm}
37655682Smarkm
37755682SmarkmSYSCTL_PROC(_kern_iconv, OID_AUTO, cslist, CTLFLAG_RD | CTLTYPE_OPAQUE,
37855682Smarkm	    NULL, 0, iconv_sysctl_cslist, "S,xlat", "registered charset pairs");
37955682Smarkm
38055682Smarkm/*
38155682Smarkm * Add new charset pair
38255682Smarkm */
38355682Smarkmstatic int
38455682Smarkmiconv_sysctl_add(SYSCTL_HANDLER_ARGS)
38555682Smarkm{
38655682Smarkm	struct iconv_converter_class *dcp;
38755682Smarkm	struct iconv_cspair *csp;
38855682Smarkm	struct iconv_add_in din;
38955682Smarkm	struct iconv_add_out dout;
39055682Smarkm	int error;
39155682Smarkm
39255682Smarkm	error = SYSCTL_IN(req, &din, sizeof(din));
39355682Smarkm	if (error)
39455682Smarkm		return error;
39555682Smarkm	if (din.ia_version != ICONV_ADD_VER)
39655682Smarkm		return EINVAL;
39755682Smarkm	if (din.ia_datalen > ICONV_CSMAXDATALEN)
39855682Smarkm		return EINVAL;
39955682Smarkm	if (strlen(din.ia_from) >= ICONV_CSNMAXLEN)
40055682Smarkm		return EINVAL;
40155682Smarkm	if (strlen(din.ia_to) >= ICONV_CSNMAXLEN)
40255682Smarkm		return EINVAL;
40355682Smarkm	if (strlen(din.ia_converter) >= ICONV_CNVNMAXLEN)
40455682Smarkm		return EINVAL;
40555682Smarkm	if (iconv_lookupconv(din.ia_converter, &dcp) != 0)
40655682Smarkm		return EINVAL;
40755682Smarkm	sx_xlock(&iconv_lock);
40855682Smarkm	error = iconv_register_cspair(din.ia_to, din.ia_from, dcp, NULL, &csp);
40955682Smarkm	if (error) {
41055682Smarkm		sx_xunlock(&iconv_lock);
41155682Smarkm		return error;
41255682Smarkm	}
41355682Smarkm	if (din.ia_datalen) {
41455682Smarkm		csp->cp_data = malloc(din.ia_datalen, M_ICONVDATA, M_WAITOK);
41555682Smarkm		error = copyin(din.ia_data, csp->cp_data, din.ia_datalen);
41655682Smarkm		if (error)
41755682Smarkm			goto bad;
41855682Smarkm	}
41955682Smarkm	dout.ia_csid = csp->cp_id;
42055682Smarkm	error = SYSCTL_OUT(req, &dout, sizeof(dout));
42155682Smarkm	if (error)
42255682Smarkm		goto bad;
42355682Smarkm	sx_xunlock(&iconv_lock);
42455682Smarkm	ICDEBUG("%s => %s, %d bytes\n",din.ia_from, din.ia_to, din.ia_datalen);
42555682Smarkm	return 0;
42655682Smarkmbad:
42755682Smarkm	iconv_unregister_cspair(csp);
42855682Smarkm	sx_xunlock(&iconv_lock);
42955682Smarkm	return error;
43055682Smarkm}
43155682Smarkm
43255682SmarkmSYSCTL_PROC(_kern_iconv, OID_AUTO, add, CTLFLAG_RW | CTLTYPE_OPAQUE,
43355682Smarkm	    NULL, 0, iconv_sysctl_add, "S,xlat", "register charset pair");
43455682Smarkm
43555682Smarkm/*
43655682Smarkm * Default stubs for converters
43755682Smarkm */
43855682Smarkmint
43955682Smarkmiconv_converter_initstub(struct iconv_converter_class *dp)
44055682Smarkm{
44155682Smarkm	return 0;
44255682Smarkm}
44355682Smarkm
44455682Smarkmint
44555682Smarkmiconv_converter_donestub(struct iconv_converter_class *dp)
44655682Smarkm{
44755682Smarkm	return 0;
44855682Smarkm}
44955682Smarkm
45055682Smarkmint
45155682Smarkmiconv_converter_tolowerstub(int c, void *handle)
45255682Smarkm{
45355682Smarkm	return (c);
45455682Smarkm}
45555682Smarkm
45655682Smarkmint
45755682Smarkmiconv_converter_handler(module_t mod, int type, void *data)
45855682Smarkm{
45955682Smarkm	struct iconv_converter_class *dcp = data;
46055682Smarkm	int error;
46155682Smarkm
46255682Smarkm	switch (type) {
46355682Smarkm	    case MOD_LOAD:
46455682Smarkm		sx_xlock(&iconv_lock);
46555682Smarkm		error = iconv_register_converter(dcp);
46655682Smarkm		if (error) {
46755682Smarkm			sx_xunlock(&iconv_lock);
46855682Smarkm			break;
46955682Smarkm		}
47055682Smarkm		error = ICONV_CONVERTER_INIT(dcp);
47155682Smarkm		if (error)
47255682Smarkm			iconv_unregister_converter(dcp);
47355682Smarkm		sx_xunlock(&iconv_lock);
47455682Smarkm		break;
47555682Smarkm	    case MOD_UNLOAD:
47655682Smarkm		sx_xlock(&iconv_lock);
47755682Smarkm		ICONV_CONVERTER_DONE(dcp);
47855682Smarkm		error = iconv_unregister_converter(dcp);
47955682Smarkm		sx_xunlock(&iconv_lock);
48055682Smarkm		break;
48155682Smarkm	    default:
48255682Smarkm		error = EINVAL;
48355682Smarkm	}
48455682Smarkm	return error;
48555682Smarkm}
48655682Smarkm
48755682Smarkm/*
48855682Smarkm * Common used functions (don't use with unicode)
48955682Smarkm */
49055682Smarkmchar *
49155682Smarkmiconv_convstr(void *handle, char *dst, const char *src)
49255682Smarkm{
49355682Smarkm	char *p = dst;
49455682Smarkm	size_t inlen, outlen;
49555682Smarkm	int error;
49655682Smarkm
49755682Smarkm	if (handle == NULL) {
49855682Smarkm		strcpy(dst, src);
49955682Smarkm		return dst;
50055682Smarkm	}
50155682Smarkm	inlen = outlen = strlen(src);
50255682Smarkm	error = iconv_conv(handle, NULL, NULL, &p, &outlen);
50355682Smarkm	if (error)
50455682Smarkm		return NULL;
50555682Smarkm	error = iconv_conv(handle, &src, &inlen, &p, &outlen);
50655682Smarkm	if (error)
50755682Smarkm		return NULL;
50855682Smarkm	*p = 0;
50955682Smarkm	return dst;
51055682Smarkm}
51155682Smarkm
51255682Smarkmvoid *
51355682Smarkmiconv_convmem(void *handle, void *dst, const void *src, int size)
51455682Smarkm{
51555682Smarkm	const char *s = src;
51655682Smarkm	char *d = dst;
51755682Smarkm	size_t inlen, outlen;
51855682Smarkm	int error;
51955682Smarkm
52055682Smarkm	if (size == 0)
52155682Smarkm		return dst;
52255682Smarkm	if (handle == NULL) {
52355682Smarkm		memcpy(dst, src, size);
52455682Smarkm		return dst;
52555682Smarkm	}
52655682Smarkm	inlen = outlen = size;
52755682Smarkm	error = iconv_conv(handle, NULL, NULL, &d, &outlen);
52855682Smarkm	if (error)
52955682Smarkm		return NULL;
53055682Smarkm	error = iconv_conv(handle, &s, &inlen, &d, &outlen);
53155682Smarkm	if (error)
53255682Smarkm		return NULL;
53355682Smarkm	return dst;
53455682Smarkm}
53555682Smarkm
53655682Smarkmint
53755682Smarkmiconv_lookupcp(char **cpp, const char *s)
53855682Smarkm{
53955682Smarkm	if (cpp == NULL) {
54055682Smarkm		ICDEBUG("warning a NULL list passed\n", ""); /* XXX ISO variadic								macros cannot
54155682Smarkm								leave out the
54255682Smarkm								variadic args */
54355682Smarkm		return ENOENT;
54455682Smarkm	}
54555682Smarkm	for (; *cpp; cpp++)
54655682Smarkm		if (strcmp(*cpp, s) == 0)
54755682Smarkm			return 0;
54855682Smarkm	return ENOENT;
54955682Smarkm}
55055682Smarkm
55155682Smarkm/*
55255682Smarkm * Return if fsname is in use of not
55355682Smarkm */
55455682Smarkmint
55555682Smarkmiconv_vfs_refcount(const char *fsname)
55655682Smarkm{
55755682Smarkm	struct vfsconf *vfsp;
55855682Smarkm
55955682Smarkm	vfsp = vfs_byname(fsname);
56055682Smarkm	if (vfsp != NULL && vfsp->vfc_refcount > 0)
56155682Smarkm		return (EBUSY);
56255682Smarkm	return (0);
56355682Smarkm}
56455682Smarkm