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