1139815Simp/*-
2206361Sjoel * Copyright (c) 2000-2001 Boris Popov
375332Sbp * All rights reserved.
475332Sbp *
575332Sbp * Redistribution and use in source and binary forms, with or without
675332Sbp * modification, are permitted provided that the following conditions
775332Sbp * are met:
875332Sbp * 1. Redistributions of source code must retain the above copyright
975332Sbp *    notice, this list of conditions and the following disclaimer.
1075332Sbp * 2. Redistributions in binary form must reproduce the above copyright
1175332Sbp *    notice, this list of conditions and the following disclaimer in the
1275332Sbp *    documentation and/or other materials provided with the distribution.
1375332Sbp *
1475332Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1575332Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1675332Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1775332Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1875332Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1975332Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2075332Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2175332Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2275332Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2375332Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2475332Sbp * SUCH DAMAGE.
2575332Sbp */
26116189Sobrien
27116189Sobrien#include <sys/cdefs.h>
28116189Sobrien__FBSDID("$FreeBSD: stable/10/sys/libkern/iconv.c 330512 2018-03-05 16:00:05Z dab $");
29116189Sobrien
3075332Sbp#include <sys/param.h>
3175332Sbp#include <sys/systm.h>
3275332Sbp#include <sys/kernel.h>
3375332Sbp#include <sys/iconv.h>
3475332Sbp#include <sys/malloc.h>
35120492Sfjoe#include <sys/mount.h>
36185652Sjhb#include <sys/sx.h>
37120492Sfjoe#include <sys/syslog.h>
3875332Sbp
3975332Sbp#include "iconv_converter_if.h"
4075332Sbp
4175332SbpSYSCTL_DECL(_kern_iconv);
4275332Sbp
4375332SbpSYSCTL_NODE(_kern, OID_AUTO, iconv, CTLFLAG_RW, NULL, "kernel iconv interface");
4475332Sbp
45151897SrwatsonMALLOC_DEFINE(M_ICONV, "iconv", "ICONV structures");
46227293Sedstatic MALLOC_DEFINE(M_ICONVDATA, "iconv_data", "ICONV data");
4775332Sbp
48120492SfjoeMODULE_VERSION(libiconv, 2);
4975332Sbp
50185652Sjhbstatic struct sx iconv_lock;
51185652Sjhb
5275332Sbp#ifdef notnow
5375332Sbp/*
5475332Sbp * iconv converter instance
5575332Sbp */
5675332Sbpstruct iconv_converter {
5775332Sbp	KOBJ_FIELDS;
5875332Sbp	void *			c_data;
5975332Sbp};
6075332Sbp#endif
6175332Sbp
6275332Sbpstruct sysctl_oid *iconv_oid_hook = &sysctl___kern_iconv;
6375332Sbp
6475332Sbp/*
6575332Sbp * List of loaded converters
6675332Sbp */
6775332Sbpstatic TAILQ_HEAD(iconv_converter_list, iconv_converter_class)
6875332Sbp    iconv_converters = TAILQ_HEAD_INITIALIZER(iconv_converters);
6975332Sbp
7075332Sbp/*
7175332Sbp * List of supported/loaded charsets pairs
7275332Sbp */
7375332Sbpstatic TAILQ_HEAD(, iconv_cspair)
7475332Sbp    iconv_cslist = TAILQ_HEAD_INITIALIZER(iconv_cslist);
7575332Sbpstatic int iconv_csid = 1;
7675332Sbp
7775332Sbpstatic char iconv_unicode_string[] = "unicode";	/* save eight bytes when possible */
7875332Sbp
7975332Sbpstatic void iconv_unregister_cspair(struct iconv_cspair *csp);
8075332Sbp
8175332Sbpstatic int
8275332Sbpiconv_mod_unload(void)
8375332Sbp{
8475332Sbp	struct iconv_cspair *csp;
8575332Sbp
86185652Sjhb	sx_xlock(&iconv_lock);
87236899Smjg	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
88236899Smjg		if (csp->cp_refcount) {
89236899Smjg			sx_xunlock(&iconv_lock);
9075332Sbp			return EBUSY;
91236899Smjg		}
92185652Sjhb	}
93185652Sjhb
94185652Sjhb	while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL)
9575332Sbp		iconv_unregister_cspair(csp);
96185652Sjhb	sx_xunlock(&iconv_lock);
97185652Sjhb	sx_destroy(&iconv_lock);
9875332Sbp	return 0;
9975332Sbp}
10075332Sbp
10175332Sbpstatic int
10275332Sbpiconv_mod_handler(module_t mod, int type, void *data)
10375332Sbp{
10475332Sbp	int error;
10575332Sbp
10675332Sbp	switch (type) {
10775332Sbp	    case MOD_LOAD:
10875332Sbp		error = 0;
109185652Sjhb		sx_init(&iconv_lock, "iconv");
11075332Sbp		break;
11175332Sbp	    case MOD_UNLOAD:
11275332Sbp		error = iconv_mod_unload();
11375332Sbp		break;
11475332Sbp	    default:
11575332Sbp		error = EINVAL;
11675332Sbp	}
11775332Sbp	return error;
11875332Sbp}
11975332Sbp
12075332Sbpstatic moduledata_t iconv_mod = {
12175332Sbp	"iconv", iconv_mod_handler, NULL
12275332Sbp};
12375332Sbp
12475332SbpDECLARE_MODULE(iconv, iconv_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
12575332Sbp
12675332Sbpstatic int
12775332Sbpiconv_register_converter(struct iconv_converter_class *dcp)
12875332Sbp{
12975332Sbp	kobj_class_compile((struct kobj_class*)dcp);
13075332Sbp	dcp->refs++;
13175332Sbp	TAILQ_INSERT_TAIL(&iconv_converters, dcp, cc_link);
13275332Sbp	return 0;
13375332Sbp}
13475332Sbp
13575332Sbpstatic int
13675332Sbpiconv_unregister_converter(struct iconv_converter_class *dcp)
13775332Sbp{
138235712Skevlo	dcp->refs--;
13975332Sbp	if (dcp->refs > 1) {
14075332Sbp		ICDEBUG("converter have %d referenses left\n", dcp->refs);
14175332Sbp		return EBUSY;
14275332Sbp	}
14375332Sbp	TAILQ_REMOVE(&iconv_converters, dcp, cc_link);
14475332Sbp	kobj_class_free((struct kobj_class*)dcp);
14575332Sbp	return 0;
14675332Sbp}
14775332Sbp
14875332Sbpstatic int
14975332Sbpiconv_lookupconv(const char *name, struct iconv_converter_class **dcpp)
15075332Sbp{
15175332Sbp	struct iconv_converter_class *dcp;
15275332Sbp
15375332Sbp	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
15475332Sbp		if (name == NULL)
15575332Sbp			continue;
15675332Sbp		if (strcmp(name, ICONV_CONVERTER_NAME(dcp)) == 0) {
15775332Sbp			if (dcpp)
15875332Sbp				*dcpp = dcp;
15975332Sbp			return 0;
16075332Sbp		}
16175332Sbp	}
16275332Sbp	return ENOENT;
16375332Sbp}
16475332Sbp
16575332Sbpstatic int
16675332Sbpiconv_lookupcs(const char *to, const char *from, struct iconv_cspair **cspp)
16775332Sbp{
16875332Sbp	struct iconv_cspair *csp;
16975332Sbp
17075332Sbp	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
171267980Sjhb		if (strcasecmp(csp->cp_to, to) == 0 &&
172267980Sjhb		    strcasecmp(csp->cp_from, from) == 0) {
17375332Sbp			if (cspp)
17475332Sbp				*cspp = csp;
17575332Sbp			return 0;
17675332Sbp		}
17775332Sbp	}
17875332Sbp	return ENOENT;
17975332Sbp}
18075332Sbp
18175332Sbpstatic int
18275332Sbpiconv_register_cspair(const char *to, const char *from,
18375332Sbp	struct iconv_converter_class *dcp, void *data,
18475332Sbp	struct iconv_cspair **cspp)
18575332Sbp{
18675332Sbp	struct iconv_cspair *csp;
18775332Sbp	char *cp;
18875332Sbp	int csize, ucsto, ucsfrom;
18975332Sbp
19075332Sbp	if (iconv_lookupcs(to, from, NULL) == 0)
19175332Sbp		return EEXIST;
19275332Sbp	csize = sizeof(*csp);
19375332Sbp	ucsto = strcmp(to, iconv_unicode_string) == 0;
19475332Sbp	if (!ucsto)
19575332Sbp		csize += strlen(to) + 1;
19675332Sbp	ucsfrom = strcmp(from, iconv_unicode_string) == 0;
19775332Sbp	if (!ucsfrom)
19875332Sbp		csize += strlen(from) + 1;
199111119Simp	csp = malloc(csize, M_ICONV, M_WAITOK);
20075332Sbp	bzero(csp, csize);
20175332Sbp	csp->cp_id = iconv_csid++;
20275332Sbp	csp->cp_dcp = dcp;
20375332Sbp	cp = (char*)(csp + 1);
20475332Sbp	if (!ucsto) {
20575332Sbp		strcpy(cp, to);
20675332Sbp		csp->cp_to = cp;
20775332Sbp		cp += strlen(cp) + 1;
20875332Sbp	} else
20975332Sbp		csp->cp_to = iconv_unicode_string;
21075332Sbp	if (!ucsfrom) {
21175332Sbp		strcpy(cp, from);
21275332Sbp		csp->cp_from = cp;
21375332Sbp	} else
21475332Sbp		csp->cp_from = iconv_unicode_string;
21575332Sbp	csp->cp_data = data;
21675332Sbp
21775332Sbp	TAILQ_INSERT_TAIL(&iconv_cslist, csp, cp_link);
21875332Sbp	*cspp = csp;
21975332Sbp	return 0;
22075332Sbp}
22175332Sbp
22275332Sbpstatic void
22375332Sbpiconv_unregister_cspair(struct iconv_cspair *csp)
22475332Sbp{
22575332Sbp	TAILQ_REMOVE(&iconv_cslist, csp, cp_link);
22675332Sbp	if (csp->cp_data)
22775332Sbp		free(csp->cp_data, M_ICONVDATA);
22875332Sbp	free(csp, M_ICONV);
22975332Sbp}
23075332Sbp
23175332Sbp/*
23275332Sbp * Lookup and create an instance of converter.
23375332Sbp * Currently this layer didn't have associated 'instance' structure
23475332Sbp * to avoid unnesessary memory allocation.
23575332Sbp */
23675332Sbpint
23775332Sbpiconv_open(const char *to, const char *from, void **handle)
23875332Sbp{
23975332Sbp	struct iconv_cspair *csp, *cspfrom, *cspto;
24075332Sbp	struct iconv_converter_class *dcp;
24175332Sbp	const char *cnvname;
24275332Sbp	int error;
24375332Sbp
24475332Sbp	/*
24575332Sbp	 * First, lookup fully qualified cspairs
24675332Sbp	 */
24775332Sbp	error = iconv_lookupcs(to, from, &csp);
24875332Sbp	if (error == 0)
24975332Sbp		return ICONV_CONVERTER_OPEN(csp->cp_dcp, csp, NULL, handle);
25075332Sbp
25175332Sbp	/*
25275332Sbp	 * Well, nothing found. Now try to construct a composite conversion
25375332Sbp	 * ToDo: add a 'capability' field to converter
25475332Sbp	 */
25575332Sbp	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
25675332Sbp		cnvname = ICONV_CONVERTER_NAME(dcp);
25775332Sbp		if (cnvname == NULL)
25875332Sbp			continue;
25975332Sbp		error = iconv_lookupcs(cnvname, from, &cspfrom);
26075332Sbp		if (error)
26175332Sbp			continue;
26275332Sbp		error = iconv_lookupcs(to, cnvname, &cspto);
26375332Sbp		if (error)
26475332Sbp			continue;
26575332Sbp		/*
26675332Sbp		 * Fine, we're found a pair which can be combined together
26775332Sbp		 */
26875332Sbp		return ICONV_CONVERTER_OPEN(dcp, cspto, cspfrom, handle);
26975332Sbp	}
27075332Sbp	return ENOENT;
27175332Sbp}
27275332Sbp
27375332Sbpint
27475332Sbpiconv_close(void *handle)
27575332Sbp{
27675332Sbp	return ICONV_CONVERTER_CLOSE(handle);
27775332Sbp}
27875332Sbp
27975332Sbpint
28075332Sbpiconv_conv(void *handle, const char **inbuf,
28175332Sbp	size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
28275332Sbp{
283120492Sfjoe	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, 0);
28475332Sbp}
28575332Sbp
286120492Sfjoeint
287120492Sfjoeiconv_conv_case(void *handle, const char **inbuf,
288120492Sfjoe	size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
289120492Sfjoe{
290120492Sfjoe	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 0, casetype);
291120492Sfjoe}
292120492Sfjoe
293120492Sfjoeint
294120492Sfjoeiconv_convchr(void *handle, const char **inbuf,
295120492Sfjoe	size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
296120492Sfjoe{
297120492Sfjoe	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, 0);
298120492Sfjoe}
299120492Sfjoe
300120492Sfjoeint
301120492Sfjoeiconv_convchr_case(void *handle, const char **inbuf,
302120492Sfjoe	size_t *inbytesleft, char **outbuf, size_t *outbytesleft, int casetype)
303120492Sfjoe{
304120492Sfjoe	return ICONV_CONVERTER_CONV(handle, inbuf, inbytesleft, outbuf, outbytesleft, 1, casetype);
305120492Sfjoe}
306120492Sfjoe
307194638Sdelphijint
308194638Sdelphijtowlower(int c, void *handle)
309194638Sdelphij{
310194638Sdelphij	return ICONV_CONVERTER_TOLOWER(handle, c);
311194638Sdelphij}
312194638Sdelphij
313194638Sdelphijint
314194638Sdelphijtowupper(int c, void *handle)
315194638Sdelphij{
316194638Sdelphij	return ICONV_CONVERTER_TOUPPER(handle, c);
317194638Sdelphij}
318194638Sdelphij
31975332Sbp/*
32075332Sbp * Give a list of loaded converters. Each name terminated with 0.
32175332Sbp * An empty string terminates the list.
32275332Sbp */
32375332Sbpstatic int
32475332Sbpiconv_sysctl_drvlist(SYSCTL_HANDLER_ARGS)
32575332Sbp{
32675332Sbp	struct iconv_converter_class *dcp;
32775332Sbp	const char *name;
32875332Sbp	char spc;
32975332Sbp	int error;
33075332Sbp
33175332Sbp	error = 0;
332185652Sjhb	sx_slock(&iconv_lock);
33375332Sbp	TAILQ_FOREACH(dcp, &iconv_converters, cc_link) {
33475332Sbp		name = ICONV_CONVERTER_NAME(dcp);
33575332Sbp		if (name == NULL)
33675332Sbp			continue;
33775332Sbp		error = SYSCTL_OUT(req, name, strlen(name) + 1);
33875332Sbp		if (error)
33975332Sbp			break;
34075332Sbp	}
341185652Sjhb	sx_sunlock(&iconv_lock);
34275332Sbp	if (error)
34375332Sbp		return error;
34475332Sbp	spc = 0;
34575332Sbp	error = SYSCTL_OUT(req, &spc, sizeof(spc));
34675332Sbp	return error;
34775332Sbp}
34875332Sbp
34975332SbpSYSCTL_PROC(_kern_iconv, OID_AUTO, drvlist, CTLFLAG_RD | CTLTYPE_OPAQUE,
35075332Sbp	    NULL, 0, iconv_sysctl_drvlist, "S,xlat", "registered converters");
35175332Sbp
35275332Sbp/*
35375332Sbp * List all available charset pairs.
35475332Sbp */
35575332Sbpstatic int
35675332Sbpiconv_sysctl_cslist(SYSCTL_HANDLER_ARGS)
35775332Sbp{
35875332Sbp	struct iconv_cspair *csp;
35975332Sbp	struct iconv_cspair_info csi;
36075332Sbp	int error;
36175332Sbp
36275332Sbp	error = 0;
36375332Sbp	bzero(&csi, sizeof(csi));
36475332Sbp	csi.cs_version = ICONV_CSPAIR_INFO_VER;
365185652Sjhb	sx_slock(&iconv_lock);
36675332Sbp	TAILQ_FOREACH(csp, &iconv_cslist, cp_link) {
36775332Sbp		csi.cs_id = csp->cp_id;
36875332Sbp		csi.cs_refcount = csp->cp_refcount;
36975332Sbp		csi.cs_base = csp->cp_base ? csp->cp_base->cp_id : 0;
37075332Sbp		strcpy(csi.cs_to, csp->cp_to);
37175332Sbp		strcpy(csi.cs_from, csp->cp_from);
37275332Sbp		error = SYSCTL_OUT(req, &csi, sizeof(csi));
37375332Sbp		if (error)
37475332Sbp			break;
37575332Sbp	}
376185652Sjhb	sx_sunlock(&iconv_lock);
37775332Sbp	return error;
37875332Sbp}
37975332Sbp
38075332SbpSYSCTL_PROC(_kern_iconv, OID_AUTO, cslist, CTLFLAG_RD | CTLTYPE_OPAQUE,
38175332Sbp	    NULL, 0, iconv_sysctl_cslist, "S,xlat", "registered charset pairs");
38275332Sbp
383227650Skevloint
384227650Skevloiconv_add(const char *converter, const char *to, const char *from)
385227650Skevlo{
386227650Skevlo	struct iconv_converter_class *dcp;
387227650Skevlo	struct iconv_cspair *csp;
388227650Skevlo
389227650Skevlo	if (iconv_lookupconv(converter, &dcp) != 0)
390227650Skevlo		return EINVAL;
391227650Skevlo
392227650Skevlo	return iconv_register_cspair(to, from, dcp, NULL, &csp);
393227650Skevlo}
394227650Skevlo
39575332Sbp/*
39675332Sbp * Add new charset pair
39775332Sbp */
39875332Sbpstatic int
39975332Sbpiconv_sysctl_add(SYSCTL_HANDLER_ARGS)
40075332Sbp{
40175332Sbp	struct iconv_converter_class *dcp;
40275332Sbp	struct iconv_cspair *csp;
40375332Sbp	struct iconv_add_in din;
40475332Sbp	struct iconv_add_out dout;
40575332Sbp	int error;
40675332Sbp
40775332Sbp	error = SYSCTL_IN(req, &din, sizeof(din));
40875332Sbp	if (error)
40975332Sbp		return error;
41075332Sbp	if (din.ia_version != ICONV_ADD_VER)
41175332Sbp		return EINVAL;
41275332Sbp	if (din.ia_datalen > ICONV_CSMAXDATALEN)
41375332Sbp		return EINVAL;
414330512Sdab	if (strnlen(din.ia_from, sizeof(din.ia_from)) >= ICONV_CSNMAXLEN)
415149415Simura		return EINVAL;
416330512Sdab	if (strnlen(din.ia_to, sizeof(din.ia_to)) >= ICONV_CSNMAXLEN)
417149415Simura		return EINVAL;
418330512Sdab	if (strnlen(din.ia_converter, sizeof(din.ia_converter)) >= ICONV_CNVNMAXLEN)
419149415Simura		return EINVAL;
42075332Sbp	if (iconv_lookupconv(din.ia_converter, &dcp) != 0)
42175332Sbp		return EINVAL;
422185652Sjhb	sx_xlock(&iconv_lock);
42375332Sbp	error = iconv_register_cspair(din.ia_to, din.ia_from, dcp, NULL, &csp);
424185652Sjhb	if (error) {
425185652Sjhb		sx_xunlock(&iconv_lock);
42675332Sbp		return error;
427185652Sjhb	}
42875332Sbp	if (din.ia_datalen) {
429111119Simp		csp->cp_data = malloc(din.ia_datalen, M_ICONVDATA, M_WAITOK);
43075332Sbp		error = copyin(din.ia_data, csp->cp_data, din.ia_datalen);
43175332Sbp		if (error)
43275332Sbp			goto bad;
43375332Sbp	}
43475332Sbp	dout.ia_csid = csp->cp_id;
43575332Sbp	error = SYSCTL_OUT(req, &dout, sizeof(dout));
43675332Sbp	if (error)
43775332Sbp		goto bad;
438185652Sjhb	sx_xunlock(&iconv_lock);
439120492Sfjoe	ICDEBUG("%s => %s, %d bytes\n",din.ia_from, din.ia_to, din.ia_datalen);
44075332Sbp	return 0;
44175332Sbpbad:
44275332Sbp	iconv_unregister_cspair(csp);
443185652Sjhb	sx_xunlock(&iconv_lock);
44475332Sbp	return error;
44575332Sbp}
44675332Sbp
44775332SbpSYSCTL_PROC(_kern_iconv, OID_AUTO, add, CTLFLAG_RW | CTLTYPE_OPAQUE,
44875332Sbp	    NULL, 0, iconv_sysctl_add, "S,xlat", "register charset pair");
44975332Sbp
45075332Sbp/*
45175332Sbp * Default stubs for converters
45275332Sbp */
45375332Sbpint
45475332Sbpiconv_converter_initstub(struct iconv_converter_class *dp)
45575332Sbp{
45675332Sbp	return 0;
45775332Sbp}
45875332Sbp
45975332Sbpint
46075332Sbpiconv_converter_donestub(struct iconv_converter_class *dp)
46175332Sbp{
46275332Sbp	return 0;
46375332Sbp}
46475332Sbp
46575332Sbpint
466194638Sdelphijiconv_converter_tolowerstub(int c, void *handle)
467194638Sdelphij{
468194638Sdelphij	return (c);
469194638Sdelphij}
470194638Sdelphij
471194638Sdelphijint
47275332Sbpiconv_converter_handler(module_t mod, int type, void *data)
47375332Sbp{
47475332Sbp	struct iconv_converter_class *dcp = data;
47575332Sbp	int error;
47675332Sbp
47775332Sbp	switch (type) {
47875332Sbp	    case MOD_LOAD:
479185652Sjhb		sx_xlock(&iconv_lock);
48075332Sbp		error = iconv_register_converter(dcp);
481185652Sjhb		if (error) {
482185652Sjhb			sx_xunlock(&iconv_lock);
48375332Sbp			break;
484185652Sjhb		}
48575332Sbp		error = ICONV_CONVERTER_INIT(dcp);
48675332Sbp		if (error)
48775332Sbp			iconv_unregister_converter(dcp);
488185652Sjhb		sx_xunlock(&iconv_lock);
48975332Sbp		break;
49075332Sbp	    case MOD_UNLOAD:
491185652Sjhb		sx_xlock(&iconv_lock);
49275332Sbp		ICONV_CONVERTER_DONE(dcp);
49375332Sbp		error = iconv_unregister_converter(dcp);
494185652Sjhb		sx_xunlock(&iconv_lock);
49575332Sbp		break;
49675332Sbp	    default:
49775332Sbp		error = EINVAL;
49875332Sbp	}
49975332Sbp	return error;
50075332Sbp}
50175332Sbp
50275332Sbp/*
503120492Sfjoe * Common used functions (don't use with unicode)
50475332Sbp */
50575332Sbpchar *
50675332Sbpiconv_convstr(void *handle, char *dst, const char *src)
50775332Sbp{
50875332Sbp	char *p = dst;
509104568Smux	size_t inlen, outlen;
510104568Smux	int error;
51175332Sbp
51275332Sbp	if (handle == NULL) {
51375332Sbp		strcpy(dst, src);
51475332Sbp		return dst;
51575332Sbp	}
516148342Simura	inlen = outlen = strlen(src);
51775332Sbp	error = iconv_conv(handle, NULL, NULL, &p, &outlen);
51875332Sbp	if (error)
51975332Sbp		return NULL;
52075332Sbp	error = iconv_conv(handle, &src, &inlen, &p, &outlen);
52175332Sbp	if (error)
52275332Sbp		return NULL;
52375332Sbp	*p = 0;
52475332Sbp	return dst;
52575332Sbp}
52675332Sbp
52775332Sbpvoid *
52875332Sbpiconv_convmem(void *handle, void *dst, const void *src, int size)
52975332Sbp{
53075332Sbp	const char *s = src;
53175332Sbp	char *d = dst;
532104568Smux	size_t inlen, outlen;
533104568Smux	int error;
53475332Sbp
53575332Sbp	if (size == 0)
53675332Sbp		return dst;
53775332Sbp	if (handle == NULL) {
53875332Sbp		memcpy(dst, src, size);
53975332Sbp		return dst;
54075332Sbp	}
541148342Simura	inlen = outlen = size;
54275332Sbp	error = iconv_conv(handle, NULL, NULL, &d, &outlen);
54375332Sbp	if (error)
54475332Sbp		return NULL;
54575332Sbp	error = iconv_conv(handle, &s, &inlen, &d, &outlen);
54675332Sbp	if (error)
54775332Sbp		return NULL;
54875332Sbp	return dst;
54975332Sbp}
55075332Sbp
55175332Sbpint
55275332Sbpiconv_lookupcp(char **cpp, const char *s)
55375332Sbp{
55475332Sbp	if (cpp == NULL) {
555235711Skevlo		ICDEBUG("warning a NULL list passed\n", "");
55675332Sbp		return ENOENT;
55775332Sbp	}
55875332Sbp	for (; *cpp; cpp++)
55975332Sbp		if (strcmp(*cpp, s) == 0)
56075332Sbp			return 0;
56175332Sbp	return ENOENT;
56275332Sbp}
563120492Sfjoe
564120492Sfjoe/*
565120492Sfjoe * Return if fsname is in use of not
566120492Sfjoe */
567120492Sfjoeint
568120492Sfjoeiconv_vfs_refcount(const char *fsname)
569120492Sfjoe{
570120492Sfjoe	struct vfsconf *vfsp;
571120492Sfjoe
572132710Sphk	vfsp = vfs_byname(fsname);
573132710Sphk	if (vfsp != NULL && vfsp->vfc_refcount > 0)
574132710Sphk		return (EBUSY);
575120492Sfjoe	return (0);
576120492Sfjoe}
577