xref: /freebsd-src/lib/libc/iconv/citrus_iconv.c (revision 559a218c9b257775fb249b67945fe4a05b7a6b9f)
19ca40936STijl Coosemans /*	$NetBSD: citrus_iconv.c,v 1.10 2011/11/19 18:34:21 tnozaki Exp $	*/
2ad30f8e7SGabor Kovesdan 
3ad30f8e7SGabor Kovesdan /*-
4d915a14eSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause
5d915a14eSPedro F. Giffuni  *
6ad30f8e7SGabor Kovesdan  * Copyright (c)2003 Citrus Project,
7ad30f8e7SGabor Kovesdan  * All rights reserved.
8ad30f8e7SGabor Kovesdan  *
9ad30f8e7SGabor Kovesdan  * Redistribution and use in source and binary forms, with or without
10ad30f8e7SGabor Kovesdan  * modification, are permitted provided that the following conditions
11ad30f8e7SGabor Kovesdan  * are met:
12ad30f8e7SGabor Kovesdan  * 1. Redistributions of source code must retain the above copyright
13ad30f8e7SGabor Kovesdan  *    notice, this list of conditions and the following disclaimer.
14ad30f8e7SGabor Kovesdan  * 2. Redistributions in binary form must reproduce the above copyright
15ad30f8e7SGabor Kovesdan  *    notice, this list of conditions and the following disclaimer in the
16ad30f8e7SGabor Kovesdan  *    documentation and/or other materials provided with the distribution.
17ad30f8e7SGabor Kovesdan  *
18ad30f8e7SGabor Kovesdan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19ad30f8e7SGabor Kovesdan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20ad30f8e7SGabor Kovesdan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21ad30f8e7SGabor Kovesdan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22ad30f8e7SGabor Kovesdan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23ad30f8e7SGabor Kovesdan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24ad30f8e7SGabor Kovesdan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25ad30f8e7SGabor Kovesdan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26ad30f8e7SGabor Kovesdan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27ad30f8e7SGabor Kovesdan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28ad30f8e7SGabor Kovesdan  * SUCH DAMAGE.
29ad30f8e7SGabor Kovesdan  */
30ad30f8e7SGabor Kovesdan 
31ad30f8e7SGabor Kovesdan #include <sys/types.h>
32ad30f8e7SGabor Kovesdan #include <sys/queue.h>
33ad30f8e7SGabor Kovesdan 
34ad30f8e7SGabor Kovesdan #include <assert.h>
35ad30f8e7SGabor Kovesdan #include <dirent.h>
36ad30f8e7SGabor Kovesdan #include <errno.h>
37ad30f8e7SGabor Kovesdan #include <iconv.h>
38ad30f8e7SGabor Kovesdan #include <langinfo.h>
39ad30f8e7SGabor Kovesdan #include <limits.h>
40ad30f8e7SGabor Kovesdan #include <paths.h>
41ad30f8e7SGabor Kovesdan #include <stdbool.h>
42ad30f8e7SGabor Kovesdan #include <stdio.h>
43ad30f8e7SGabor Kovesdan #include <stdlib.h>
44ad30f8e7SGabor Kovesdan #include <string.h>
45ad30f8e7SGabor Kovesdan #include <unistd.h>
46ad30f8e7SGabor Kovesdan 
47ad30f8e7SGabor Kovesdan #include "citrus_namespace.h"
48ad30f8e7SGabor Kovesdan #include "citrus_bcs.h"
49ad30f8e7SGabor Kovesdan #include "citrus_esdb.h"
50ad30f8e7SGabor Kovesdan #include "citrus_region.h"
51ad30f8e7SGabor Kovesdan #include "citrus_memstream.h"
52ad30f8e7SGabor Kovesdan #include "citrus_mmap.h"
53ad30f8e7SGabor Kovesdan #include "citrus_module.h"
54ad30f8e7SGabor Kovesdan #include "citrus_lock.h"
55ad30f8e7SGabor Kovesdan #include "citrus_lookup.h"
56ad30f8e7SGabor Kovesdan #include "citrus_hash.h"
57ad30f8e7SGabor Kovesdan #include "citrus_iconv.h"
58ad30f8e7SGabor Kovesdan 
59ad30f8e7SGabor Kovesdan #define _CITRUS_ICONV_DIR	"iconv.dir"
60ad30f8e7SGabor Kovesdan #define _CITRUS_ICONV_ALIAS	"iconv.alias"
61ad30f8e7SGabor Kovesdan 
62ad30f8e7SGabor Kovesdan #define CI_HASH_SIZE 101
63ad30f8e7SGabor Kovesdan #define CI_INITIAL_MAX_REUSE	5
64ad30f8e7SGabor Kovesdan #define CI_ENV_MAX_REUSE	"ICONV_MAX_REUSE"
65ad30f8e7SGabor Kovesdan 
66ad30f8e7SGabor Kovesdan static bool			 isinit = false;
67ad30f8e7SGabor Kovesdan static int			 shared_max_reuse, shared_num_unused;
68ad30f8e7SGabor Kovesdan static _CITRUS_HASH_HEAD(, _citrus_iconv_shared, CI_HASH_SIZE) shared_pool;
69ad30f8e7SGabor Kovesdan static TAILQ_HEAD(, _citrus_iconv_shared) shared_unused;
70ad30f8e7SGabor Kovesdan 
71ff0b75b8SPeter Wemm static pthread_rwlock_t		 ci_lock = PTHREAD_RWLOCK_INITIALIZER;
72ff0b75b8SPeter Wemm 
73ad30f8e7SGabor Kovesdan static __inline void
init_cache(void)74ad30f8e7SGabor Kovesdan init_cache(void)
75ad30f8e7SGabor Kovesdan {
76ad30f8e7SGabor Kovesdan 
77ff0b75b8SPeter Wemm 	WLOCK(&ci_lock);
78ad30f8e7SGabor Kovesdan 	if (!isinit) {
79ad30f8e7SGabor Kovesdan 		_CITRUS_HASH_INIT(&shared_pool, CI_HASH_SIZE);
80ad30f8e7SGabor Kovesdan 		TAILQ_INIT(&shared_unused);
81ad30f8e7SGabor Kovesdan 		shared_max_reuse = -1;
8268ca8363SMark Johnston 		if (secure_getenv(CI_ENV_MAX_REUSE) != NULL)
8368ca8363SMark Johnston 			shared_max_reuse =
8468ca8363SMark Johnston 			    atoi(secure_getenv(CI_ENV_MAX_REUSE));
85ad30f8e7SGabor Kovesdan 		if (shared_max_reuse < 0)
86ad30f8e7SGabor Kovesdan 			shared_max_reuse = CI_INITIAL_MAX_REUSE;
87ad30f8e7SGabor Kovesdan 		isinit = true;
88ad30f8e7SGabor Kovesdan 	}
89ff0b75b8SPeter Wemm 	UNLOCK(&ci_lock);
90ad30f8e7SGabor Kovesdan }
91ad30f8e7SGabor Kovesdan 
92ad30f8e7SGabor Kovesdan static __inline void
close_shared(struct _citrus_iconv_shared * ci)93ad30f8e7SGabor Kovesdan close_shared(struct _citrus_iconv_shared *ci)
94ad30f8e7SGabor Kovesdan {
95ad30f8e7SGabor Kovesdan 
96ad30f8e7SGabor Kovesdan 	if (ci) {
97ad30f8e7SGabor Kovesdan 		if (ci->ci_module) {
98ad30f8e7SGabor Kovesdan 			if (ci->ci_ops) {
99ad30f8e7SGabor Kovesdan 				if (ci->ci_closure)
100ad30f8e7SGabor Kovesdan 					(*ci->ci_ops->io_uninit_shared)(ci);
101ad30f8e7SGabor Kovesdan 				free(ci->ci_ops);
102ad30f8e7SGabor Kovesdan 			}
103ad30f8e7SGabor Kovesdan 			_citrus_unload_module(ci->ci_module);
104ad30f8e7SGabor Kovesdan 		}
105ad30f8e7SGabor Kovesdan 		free(ci);
106ad30f8e7SGabor Kovesdan 	}
107ad30f8e7SGabor Kovesdan }
108ad30f8e7SGabor Kovesdan 
109ad30f8e7SGabor Kovesdan static __inline int
open_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,const char * __restrict convname,const char * __restrict src,const char * __restrict dst)110ad30f8e7SGabor Kovesdan open_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
111ad30f8e7SGabor Kovesdan     const char * __restrict convname, const char * __restrict src,
112ad30f8e7SGabor Kovesdan     const char * __restrict dst)
113ad30f8e7SGabor Kovesdan {
114ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_shared *ci;
115ad30f8e7SGabor Kovesdan 	_citrus_iconv_getops_t getops;
116ad30f8e7SGabor Kovesdan 	const char *module;
117ad30f8e7SGabor Kovesdan 	size_t len_convname;
118ad30f8e7SGabor Kovesdan 	int ret;
119ad30f8e7SGabor Kovesdan 
120f9562c17SPeter Wemm #ifdef INCOMPATIBLE_WITH_GNU_ICONV
121f9562c17SPeter Wemm 	/*
122f9562c17SPeter Wemm 	 * Sadly, the gnu tools expect iconv to actually parse the
123f9562c17SPeter Wemm 	 * byte stream and don't allow for a pass-through when
124f9562c17SPeter Wemm 	 * the (src,dest) encodings are the same.
125f9562c17SPeter Wemm 	 * See gettext-0.18.3+ NEWS:
126f9562c17SPeter Wemm 	 *   msgfmt now checks PO file headers more strictly with less
127f9562c17SPeter Wemm 	 *   false-positives.
128c1f46b8fSKyle Evans 	 * NetBSD, also, doesn't do the below pass-through.
129c1f46b8fSKyle Evans 	 *
130c1f46b8fSKyle Evans 	 * Also note that this currently falls short if dst options have been
131c1f46b8fSKyle Evans 	 * specified. It may be the case that we want to ignore EILSEQ, in which
132c1f46b8fSKyle Evans 	 * case we should also select iconv_std anyways.  This trick, while
133c1f46b8fSKyle Evans 	 * clever, may not be worth it.
134f9562c17SPeter Wemm 	 */
135ad30f8e7SGabor Kovesdan 	module = (strcmp(src, dst) != 0) ? "iconv_std" : "iconv_none";
136f9562c17SPeter Wemm #else
137f9562c17SPeter Wemm 	module = "iconv_std";
138f9562c17SPeter Wemm #endif
139ad30f8e7SGabor Kovesdan 
140ad30f8e7SGabor Kovesdan 	/* initialize iconv handle */
141ad30f8e7SGabor Kovesdan 	len_convname = strlen(convname);
142*939199a2SKyle Evans 	ci = calloc(1, sizeof(*ci) + len_convname + 1);
143ad30f8e7SGabor Kovesdan 	if (!ci) {
144ad30f8e7SGabor Kovesdan 		ret = errno;
145ad30f8e7SGabor Kovesdan 		goto err;
146ad30f8e7SGabor Kovesdan 	}
147ad30f8e7SGabor Kovesdan 	ci->ci_convname = (void *)&ci[1];
148ad30f8e7SGabor Kovesdan 	memcpy(ci->ci_convname, convname, len_convname + 1);
149ad30f8e7SGabor Kovesdan 
150ad30f8e7SGabor Kovesdan 	/* load module */
151ad30f8e7SGabor Kovesdan 	ret = _citrus_load_module(&ci->ci_module, module);
152ad30f8e7SGabor Kovesdan 	if (ret)
153ad30f8e7SGabor Kovesdan 		goto err;
154ad30f8e7SGabor Kovesdan 
155ad30f8e7SGabor Kovesdan 	/* get operators */
156ad30f8e7SGabor Kovesdan 	getops = (_citrus_iconv_getops_t)_citrus_find_getops(ci->ci_module,
157ad30f8e7SGabor Kovesdan 	    module, "iconv");
158ad30f8e7SGabor Kovesdan 	if (!getops) {
159ad30f8e7SGabor Kovesdan 		ret = EOPNOTSUPP;
160ad30f8e7SGabor Kovesdan 		goto err;
161ad30f8e7SGabor Kovesdan 	}
162ad30f8e7SGabor Kovesdan 	ci->ci_ops = malloc(sizeof(*ci->ci_ops));
163ad30f8e7SGabor Kovesdan 	if (!ci->ci_ops) {
164ad30f8e7SGabor Kovesdan 		ret = errno;
165ad30f8e7SGabor Kovesdan 		goto err;
166ad30f8e7SGabor Kovesdan 	}
167ad30f8e7SGabor Kovesdan 	ret = (*getops)(ci->ci_ops);
168ad30f8e7SGabor Kovesdan 	if (ret)
169ad30f8e7SGabor Kovesdan 		goto err;
170ad30f8e7SGabor Kovesdan 
171ad30f8e7SGabor Kovesdan 	if (ci->ci_ops->io_init_shared == NULL ||
172ad30f8e7SGabor Kovesdan 	    ci->ci_ops->io_uninit_shared == NULL ||
173ad30f8e7SGabor Kovesdan 	    ci->ci_ops->io_init_context == NULL ||
174ad30f8e7SGabor Kovesdan 	    ci->ci_ops->io_uninit_context == NULL ||
1759ca40936STijl Coosemans 	    ci->ci_ops->io_convert == NULL) {
1769ca40936STijl Coosemans 		ret = EINVAL;
177ad30f8e7SGabor Kovesdan 		goto err;
1789ca40936STijl Coosemans 	}
179ad30f8e7SGabor Kovesdan 
180ad30f8e7SGabor Kovesdan 	/* initialize the converter */
181ad30f8e7SGabor Kovesdan 	ret = (*ci->ci_ops->io_init_shared)(ci, src, dst);
182ad30f8e7SGabor Kovesdan 	if (ret)
183ad30f8e7SGabor Kovesdan 		goto err;
184ad30f8e7SGabor Kovesdan 
185ad30f8e7SGabor Kovesdan 	*rci = ci;
186ad30f8e7SGabor Kovesdan 
187ad30f8e7SGabor Kovesdan 	return (0);
188ad30f8e7SGabor Kovesdan err:
189ad30f8e7SGabor Kovesdan 	close_shared(ci);
190ad30f8e7SGabor Kovesdan 	return (ret);
191ad30f8e7SGabor Kovesdan }
192ad30f8e7SGabor Kovesdan 
193ad30f8e7SGabor Kovesdan static __inline int
hash_func(const char * key)194ad30f8e7SGabor Kovesdan hash_func(const char *key)
195ad30f8e7SGabor Kovesdan {
196ad30f8e7SGabor Kovesdan 
197ad30f8e7SGabor Kovesdan 	return (_string_hash_func(key, CI_HASH_SIZE));
198ad30f8e7SGabor Kovesdan }
199ad30f8e7SGabor Kovesdan 
200ad30f8e7SGabor Kovesdan static __inline int
match_func(struct _citrus_iconv_shared * __restrict ci,const char * __restrict key)201ad30f8e7SGabor Kovesdan match_func(struct _citrus_iconv_shared * __restrict ci,
202ad30f8e7SGabor Kovesdan     const char * __restrict key)
203ad30f8e7SGabor Kovesdan {
204ad30f8e7SGabor Kovesdan 
205ad30f8e7SGabor Kovesdan 	return (strcmp(ci->ci_convname, key));
206ad30f8e7SGabor Kovesdan }
207ad30f8e7SGabor Kovesdan 
208ad30f8e7SGabor Kovesdan static int
get_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,const char * src,const char * dst)209ad30f8e7SGabor Kovesdan get_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
210ad30f8e7SGabor Kovesdan     const char *src, const char *dst)
211ad30f8e7SGabor Kovesdan {
212ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_shared * ci;
213ad30f8e7SGabor Kovesdan 	char convname[PATH_MAX];
214ad30f8e7SGabor Kovesdan 	int hashval, ret = 0;
215ad30f8e7SGabor Kovesdan 
216ad30f8e7SGabor Kovesdan 	snprintf(convname, sizeof(convname), "%s/%s", src, dst);
217ad30f8e7SGabor Kovesdan 
218ff0b75b8SPeter Wemm 	WLOCK(&ci_lock);
219ad30f8e7SGabor Kovesdan 
220ad30f8e7SGabor Kovesdan 	/* lookup alread existing entry */
221ad30f8e7SGabor Kovesdan 	hashval = hash_func(convname);
222ad30f8e7SGabor Kovesdan 	_CITRUS_HASH_SEARCH(&shared_pool, ci, ci_hash_entry, match_func,
223ad30f8e7SGabor Kovesdan 	    convname, hashval);
224ad30f8e7SGabor Kovesdan 	if (ci != NULL) {
225ad30f8e7SGabor Kovesdan 		/* found */
226ad30f8e7SGabor Kovesdan 		if (ci->ci_used_count == 0) {
227ad30f8e7SGabor Kovesdan 			TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry);
228ad30f8e7SGabor Kovesdan 			shared_num_unused--;
229ad30f8e7SGabor Kovesdan 		}
230ad30f8e7SGabor Kovesdan 		ci->ci_used_count++;
231ad30f8e7SGabor Kovesdan 		*rci = ci;
232ad30f8e7SGabor Kovesdan 		goto quit;
233ad30f8e7SGabor Kovesdan 	}
234ad30f8e7SGabor Kovesdan 
235ad30f8e7SGabor Kovesdan 	/* create new entry */
236ad30f8e7SGabor Kovesdan 	ret = open_shared(&ci, convname, src, dst);
237ad30f8e7SGabor Kovesdan 	if (ret)
238ad30f8e7SGabor Kovesdan 		goto quit;
239ad30f8e7SGabor Kovesdan 
240ad30f8e7SGabor Kovesdan 	_CITRUS_HASH_INSERT(&shared_pool, ci, ci_hash_entry, hashval);
241ad30f8e7SGabor Kovesdan 	ci->ci_used_count = 1;
242ad30f8e7SGabor Kovesdan 	*rci = ci;
243ad30f8e7SGabor Kovesdan 
244ad30f8e7SGabor Kovesdan quit:
245ff0b75b8SPeter Wemm 	UNLOCK(&ci_lock);
246ad30f8e7SGabor Kovesdan 
247ad30f8e7SGabor Kovesdan 	return (ret);
248ad30f8e7SGabor Kovesdan }
249ad30f8e7SGabor Kovesdan 
250ad30f8e7SGabor Kovesdan static void
release_shared(struct _citrus_iconv_shared * __restrict ci)251ad30f8e7SGabor Kovesdan release_shared(struct _citrus_iconv_shared * __restrict ci)
252ad30f8e7SGabor Kovesdan {
253ad30f8e7SGabor Kovesdan 
254ff0b75b8SPeter Wemm 	WLOCK(&ci_lock);
255ad30f8e7SGabor Kovesdan 	ci->ci_used_count--;
256ad30f8e7SGabor Kovesdan 	if (ci->ci_used_count == 0) {
257ad30f8e7SGabor Kovesdan 		/* put it into unused list */
258ad30f8e7SGabor Kovesdan 		shared_num_unused++;
259ad30f8e7SGabor Kovesdan 		TAILQ_INSERT_TAIL(&shared_unused, ci, ci_tailq_entry);
260ad30f8e7SGabor Kovesdan 		/* flood out */
261ad30f8e7SGabor Kovesdan 		while (shared_num_unused > shared_max_reuse) {
262ad30f8e7SGabor Kovesdan 			ci = TAILQ_FIRST(&shared_unused);
263ad30f8e7SGabor Kovesdan 			TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry);
264ad30f8e7SGabor Kovesdan 			_CITRUS_HASH_REMOVE(ci, ci_hash_entry);
265ad30f8e7SGabor Kovesdan 			shared_num_unused--;
266ad30f8e7SGabor Kovesdan 			close_shared(ci);
267ad30f8e7SGabor Kovesdan 		}
268ad30f8e7SGabor Kovesdan 	}
269ad30f8e7SGabor Kovesdan 
270ff0b75b8SPeter Wemm 	UNLOCK(&ci_lock);
271ad30f8e7SGabor Kovesdan }
272ad30f8e7SGabor Kovesdan 
273ad30f8e7SGabor Kovesdan /*
274ad30f8e7SGabor Kovesdan  * _citrus_iconv_open:
275ad30f8e7SGabor Kovesdan  *	open a converter for the specified in/out codes.
276ad30f8e7SGabor Kovesdan  */
277ad30f8e7SGabor Kovesdan int
_citrus_iconv_open(struct _citrus_iconv * __restrict * __restrict rcv,const char * __restrict src,const char * __restrict dst)278ad30f8e7SGabor Kovesdan _citrus_iconv_open(struct _citrus_iconv * __restrict * __restrict rcv,
279ad30f8e7SGabor Kovesdan     const char * __restrict src, const char * __restrict dst)
280ad30f8e7SGabor Kovesdan {
281907b8031SEd Schouten 	struct _citrus_iconv *cv = NULL;
282ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_shared *ci = NULL;
283c1f46b8fSKyle Evans 	char realdst[PATH_MAX], realsrc[PATH_MAX], *slashes;
2843956b101SAlexander Kabaev #ifdef _PATH_ICONV
285ad30f8e7SGabor Kovesdan 	char buf[PATH_MAX], path[PATH_MAX];
2863956b101SAlexander Kabaev #endif
287ad30f8e7SGabor Kovesdan 	int ret;
288ad30f8e7SGabor Kovesdan 
289ad30f8e7SGabor Kovesdan 	init_cache();
290ad30f8e7SGabor Kovesdan 
291ad30f8e7SGabor Kovesdan 	/* GNU behaviour, using locale encoding if "" or "char" is specified */
292ad30f8e7SGabor Kovesdan 	if ((strcmp(src, "") == 0) || (strcmp(src, "char") == 0))
293ad30f8e7SGabor Kovesdan 		src = nl_langinfo(CODESET);
294ad30f8e7SGabor Kovesdan 	if ((strcmp(dst, "") == 0) || (strcmp(dst, "char") == 0))
295ad30f8e7SGabor Kovesdan 		dst = nl_langinfo(CODESET);
296ad30f8e7SGabor Kovesdan 
297c1f46b8fSKyle Evans 	strlcpy(realsrc, src, (size_t)PATH_MAX);
298c1f46b8fSKyle Evans 	if ((slashes = strstr(realsrc, "//")) != NULL)
299c1f46b8fSKyle Evans 		*slashes = '\0';
300c1f46b8fSKyle Evans 	strlcpy(realdst, dst, (size_t)PATH_MAX);
301c1f46b8fSKyle Evans 	if ((slashes = strstr(realdst, "//")) != NULL)
302c1f46b8fSKyle Evans 		*slashes = '\0';
303c1f46b8fSKyle Evans 
304ad30f8e7SGabor Kovesdan 	/* resolve codeset name aliases */
3053956b101SAlexander Kabaev #ifdef _PATH_ICONV
306c1f46b8fSKyle Evans 	/*
307c1f46b8fSKyle Evans 	 * Note that the below reads from realsrc and realdst while it's
308c1f46b8fSKyle Evans 	 * repopulating (writing to) realsrc and realdst, but it's done so with
309c1f46b8fSKyle Evans 	 * a trip through `buf`.
310c1f46b8fSKyle Evans 	 */
3113956b101SAlexander Kabaev 	snprintf(path, sizeof(path), "%s/%s", _PATH_ICONV, _CITRUS_ICONV_ALIAS);
312c1f46b8fSKyle Evans 	strlcpy(realsrc, _lookup_alias(path, realsrc, buf, (size_t)PATH_MAX,
313ad30f8e7SGabor Kovesdan 	    _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX);
314c1f46b8fSKyle Evans 	strlcpy(realdst, _lookup_alias(path, realdst, buf, (size_t)PATH_MAX,
315ad30f8e7SGabor Kovesdan 	    _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX);
3163956b101SAlexander Kabaev #endif
317ad30f8e7SGabor Kovesdan 
318ad30f8e7SGabor Kovesdan 	/* sanity check */
319ad30f8e7SGabor Kovesdan 	if (strchr(realsrc, '/') != NULL || strchr(realdst, '/'))
320ad30f8e7SGabor Kovesdan 		return (EINVAL);
321ad30f8e7SGabor Kovesdan 
322ad30f8e7SGabor Kovesdan 	/* get shared record */
323ad30f8e7SGabor Kovesdan 	ret = get_shared(&ci, realsrc, realdst);
324ad30f8e7SGabor Kovesdan 	if (ret)
325ad30f8e7SGabor Kovesdan 		return (ret);
326ad30f8e7SGabor Kovesdan 
327ad30f8e7SGabor Kovesdan 	/* create/init context */
328ad30f8e7SGabor Kovesdan 	if (*rcv == NULL) {
329ad30f8e7SGabor Kovesdan 		cv = malloc(sizeof(*cv));
330ad30f8e7SGabor Kovesdan 		if (cv == NULL) {
331ad30f8e7SGabor Kovesdan 			ret = errno;
332ad30f8e7SGabor Kovesdan 			release_shared(ci);
333ad30f8e7SGabor Kovesdan 			return (ret);
334ad30f8e7SGabor Kovesdan 		}
335ad30f8e7SGabor Kovesdan 		*rcv = cv;
336ad30f8e7SGabor Kovesdan 	}
337ad30f8e7SGabor Kovesdan 	(*rcv)->cv_shared = ci;
338ad30f8e7SGabor Kovesdan 	ret = (*ci->ci_ops->io_init_context)(*rcv);
339ad30f8e7SGabor Kovesdan 	if (ret) {
340ad30f8e7SGabor Kovesdan 		release_shared(ci);
341907b8031SEd Schouten 		free(cv);
342ad30f8e7SGabor Kovesdan 		return (ret);
343ad30f8e7SGabor Kovesdan 	}
344ad30f8e7SGabor Kovesdan 	return (0);
345ad30f8e7SGabor Kovesdan }
346ad30f8e7SGabor Kovesdan 
347ad30f8e7SGabor Kovesdan /*
348ad30f8e7SGabor Kovesdan  * _citrus_iconv_close:
349ad30f8e7SGabor Kovesdan  *	close the specified converter.
350ad30f8e7SGabor Kovesdan  */
351ad30f8e7SGabor Kovesdan void
_citrus_iconv_close(struct _citrus_iconv * cv)352ad30f8e7SGabor Kovesdan _citrus_iconv_close(struct _citrus_iconv *cv)
353ad30f8e7SGabor Kovesdan {
354ad30f8e7SGabor Kovesdan 
355ad30f8e7SGabor Kovesdan 	if (cv) {
356ad30f8e7SGabor Kovesdan 		(*cv->cv_shared->ci_ops->io_uninit_context)(cv);
357ad30f8e7SGabor Kovesdan 		release_shared(cv->cv_shared);
358ad30f8e7SGabor Kovesdan 		free(cv);
359ad30f8e7SGabor Kovesdan 	}
360ad30f8e7SGabor Kovesdan }
361ad30f8e7SGabor Kovesdan 
362ad30f8e7SGabor Kovesdan const char
_citrus_iconv_canonicalize(const char * name)363ad30f8e7SGabor Kovesdan *_citrus_iconv_canonicalize(const char *name)
364ad30f8e7SGabor Kovesdan {
365ad30f8e7SGabor Kovesdan 	char *buf;
366ad30f8e7SGabor Kovesdan 
3673fc3c30aSTijl Coosemans 	if ((buf = calloc((size_t)PATH_MAX, sizeof(*buf))) == NULL)
368ad30f8e7SGabor Kovesdan 		return (NULL);
369ad30f8e7SGabor Kovesdan 	_citrus_esdb_alias(name, buf, (size_t)PATH_MAX);
370ad30f8e7SGabor Kovesdan 	return (buf);
371ad30f8e7SGabor Kovesdan }
372