Home | History | Annotate | Line # | Download | only in citrus
citrus_iconv.c revision 1.7.6.2
      1  1.7.6.2  christos /*	$NetBSD: citrus_iconv.c,v 1.7.6.2 2008/07/25 14:05:26 christos Exp $	*/
      2  1.7.6.2  christos 
      3  1.7.6.2  christos /*-
      4  1.7.6.2  christos  * Copyright (c)2003 Citrus Project,
      5  1.7.6.2  christos  * All rights reserved.
      6  1.7.6.2  christos  *
      7  1.7.6.2  christos  * Redistribution and use in source and binary forms, with or without
      8  1.7.6.2  christos  * modification, are permitted provided that the following conditions
      9  1.7.6.2  christos  * are met:
     10  1.7.6.2  christos  * 1. Redistributions of source code must retain the above copyright
     11  1.7.6.2  christos  *    notice, this list of conditions and the following disclaimer.
     12  1.7.6.2  christos  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.7.6.2  christos  *    notice, this list of conditions and the following disclaimer in the
     14  1.7.6.2  christos  *    documentation and/or other materials provided with the distribution.
     15  1.7.6.2  christos  *
     16  1.7.6.2  christos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  1.7.6.2  christos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  1.7.6.2  christos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  1.7.6.2  christos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  1.7.6.2  christos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  1.7.6.2  christos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  1.7.6.2  christos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  1.7.6.2  christos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  1.7.6.2  christos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  1.7.6.2  christos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  1.7.6.2  christos  * SUCH DAMAGE.
     27  1.7.6.2  christos  */
     28  1.7.6.2  christos 
     29  1.7.6.2  christos #include <sys/cdefs.h>
     30  1.7.6.2  christos #if defined(LIBC_SCCS) && !defined(lint)
     31  1.7.6.2  christos __RCSID("$NetBSD: citrus_iconv.c,v 1.7.6.2 2008/07/25 14:05:26 christos Exp $");
     32  1.7.6.2  christos #endif /* LIBC_SCCS and not lint */
     33  1.7.6.2  christos 
     34  1.7.6.2  christos #include "namespace.h"
     35  1.7.6.2  christos #include "reentrant.h"
     36  1.7.6.2  christos #include <assert.h>
     37  1.7.6.2  christos #include <stdio.h>
     38  1.7.6.2  christos #include <stdlib.h>
     39  1.7.6.2  christos #include <string.h>
     40  1.7.6.2  christos #include <errno.h>
     41  1.7.6.2  christos #include <limits.h>
     42  1.7.6.2  christos #include <unistd.h>
     43  1.7.6.2  christos #include <paths.h>
     44  1.7.6.2  christos #include <dirent.h>
     45  1.7.6.2  christos #include <sys/types.h>
     46  1.7.6.2  christos #include <sys/queue.h>
     47  1.7.6.2  christos 
     48  1.7.6.2  christos #include "citrus_namespace.h"
     49  1.7.6.2  christos #include "citrus_bcs.h"
     50  1.7.6.2  christos #include "citrus_region.h"
     51  1.7.6.2  christos #include "citrus_memstream.h"
     52  1.7.6.2  christos #include "citrus_mmap.h"
     53  1.7.6.2  christos #include "citrus_module.h"
     54  1.7.6.2  christos #include "citrus_lookup.h"
     55  1.7.6.2  christos #include "citrus_hash.h"
     56  1.7.6.2  christos #include "citrus_iconv.h"
     57  1.7.6.2  christos 
     58  1.7.6.2  christos #define _CITRUS_ICONV_DIR	"iconv.dir"
     59  1.7.6.2  christos #define _CITRUS_ICONV_ALIAS	"iconv.alias"
     60  1.7.6.2  christos 
     61  1.7.6.2  christos #define CI_HASH_SIZE 101
     62  1.7.6.2  christos #define CI_INITIAL_MAX_REUSE	5
     63  1.7.6.2  christos #define CI_ENV_MAX_REUSE	"ICONV_MAX_REUSE"
     64  1.7.6.2  christos 
     65  1.7.6.2  christos #ifdef _REENTRANT
     66  1.7.6.2  christos static rwlock_t lock = RWLOCK_INITIALIZER;
     67  1.7.6.2  christos #endif
     68  1.7.6.2  christos static int isinit = 0;
     69  1.7.6.2  christos static _CITRUS_HASH_HEAD(, _citrus_iconv_shared, CI_HASH_SIZE) shared_pool;
     70  1.7.6.2  christos static TAILQ_HEAD(, _citrus_iconv_shared) shared_unused;
     71  1.7.6.2  christos static int shared_num_unused, shared_max_reuse;
     72  1.7.6.2  christos 
     73  1.7.6.2  christos static __inline void
     74  1.7.6.2  christos init_cache(void)
     75  1.7.6.2  christos {
     76  1.7.6.2  christos 	rwlock_wrlock(&lock);
     77  1.7.6.2  christos 	if (!isinit) {
     78  1.7.6.2  christos 		_CITRUS_HASH_INIT(&shared_pool, CI_HASH_SIZE);
     79  1.7.6.2  christos 		TAILQ_INIT(&shared_unused);
     80  1.7.6.2  christos 		shared_max_reuse = -1;
     81  1.7.6.2  christos 		if (!issetugid() && getenv(CI_ENV_MAX_REUSE))
     82  1.7.6.2  christos 			shared_max_reuse = atoi(getenv(CI_ENV_MAX_REUSE));
     83  1.7.6.2  christos 		if (shared_max_reuse < 0)
     84  1.7.6.2  christos 			shared_max_reuse = CI_INITIAL_MAX_REUSE;
     85  1.7.6.2  christos 		isinit = 1;
     86  1.7.6.2  christos 	}
     87  1.7.6.2  christos 	rwlock_unlock(&lock);
     88  1.7.6.2  christos }
     89  1.7.6.2  christos 
     90  1.7.6.2  christos /*
     91  1.7.6.2  christos  * lookup_iconv_entry:
     92  1.7.6.2  christos  *	lookup iconv.dir entry in the specified directory.
     93  1.7.6.2  christos  *
     94  1.7.6.2  christos  * line format of iconv.dir file:
     95  1.7.6.2  christos  *	key  module  arg
     96  1.7.6.2  christos  * key    : lookup key.
     97  1.7.6.2  christos  * module : iconv module name.
     98  1.7.6.2  christos  * arg    : argument for the module (generally, description file name)
     99  1.7.6.2  christos  *
    100  1.7.6.2  christos  */
    101  1.7.6.2  christos static __inline int
    102  1.7.6.2  christos lookup_iconv_entry(const char *curdir, const char *key,
    103  1.7.6.2  christos 		   char *linebuf, size_t linebufsize,
    104  1.7.6.2  christos 		   const char **module, const char **variable)
    105  1.7.6.2  christos {
    106  1.7.6.2  christos 	const char *cp, *cq;
    107  1.7.6.2  christos 	char *p, path[PATH_MAX];
    108  1.7.6.2  christos 
    109  1.7.6.2  christos 	/* iconv.dir path */
    110  1.7.6.2  christos 	snprintf(path, (size_t)PATH_MAX, "%s/" _CITRUS_ICONV_DIR, curdir);
    111  1.7.6.2  christos 
    112  1.7.6.2  christos 	/* lookup db */
    113  1.7.6.2  christos 	cp = p = _lookup_simple(path, key, linebuf, linebufsize,
    114  1.7.6.2  christos 				_LOOKUP_CASE_IGNORE);
    115  1.7.6.2  christos 	if (p == NULL)
    116  1.7.6.2  christos 		return ENOENT;
    117  1.7.6.2  christos 
    118  1.7.6.2  christos 	/* get module name */
    119  1.7.6.2  christos 	*module = p;
    120  1.7.6.2  christos 	cq = _bcs_skip_nonws(cp);
    121  1.7.6.2  christos 	p[cq-cp] = '\0';
    122  1.7.6.2  christos 	p += cq-cp+1;
    123  1.7.6.2  christos 	cq++;
    124  1.7.6.2  christos 
    125  1.7.6.2  christos 	/* get variable */
    126  1.7.6.2  christos 	cp = _bcs_skip_ws(cq);
    127  1.7.6.2  christos 	*variable = p += cp - cq;
    128  1.7.6.2  christos 	cq = _bcs_skip_nonws(cp);
    129  1.7.6.2  christos 	p[cq-cp] = '\0';
    130  1.7.6.2  christos 
    131  1.7.6.2  christos 	return 0;
    132  1.7.6.2  christos }
    133  1.7.6.2  christos 
    134  1.7.6.2  christos static __inline void
    135  1.7.6.2  christos close_shared(struct _citrus_iconv_shared *ci)
    136  1.7.6.2  christos {
    137  1.7.6.2  christos 	if (ci) {
    138  1.7.6.2  christos 		if (ci->ci_module) {
    139  1.7.6.2  christos 			if (ci->ci_ops) {
    140  1.7.6.2  christos 				if (ci->ci_closure)
    141  1.7.6.2  christos 					(*ci->ci_ops->io_uninit_shared)(ci);
    142  1.7.6.2  christos 				free(ci->ci_ops);
    143  1.7.6.2  christos 			}
    144  1.7.6.2  christos 			_citrus_unload_module(ci->ci_module);
    145  1.7.6.2  christos 		}
    146  1.7.6.2  christos 		free(ci);
    147  1.7.6.2  christos 	}
    148  1.7.6.2  christos }
    149  1.7.6.2  christos 
    150  1.7.6.2  christos static __inline int
    151  1.7.6.2  christos open_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
    152  1.7.6.2  christos 	    const char * __restrict basedir, const char * __restrict convname,
    153  1.7.6.2  christos 	    const char * __restrict src, const char * __restrict dst)
    154  1.7.6.2  christos {
    155  1.7.6.2  christos 	int ret;
    156  1.7.6.2  christos 	struct _citrus_iconv_shared *ci;
    157  1.7.6.2  christos 	_citrus_iconv_getops_t getops;
    158  1.7.6.2  christos 	char linebuf[LINE_MAX];
    159  1.7.6.2  christos 	const char *module, *variable;
    160  1.7.6.2  christos 	size_t len_convname;
    161  1.7.6.2  christos 
    162  1.7.6.2  christos 	/* search converter entry */
    163  1.7.6.2  christos 	ret = lookup_iconv_entry(basedir, convname, linebuf, sizeof(linebuf),
    164  1.7.6.2  christos 				 &module, &variable);
    165  1.7.6.2  christos 	if (ret) {
    166  1.7.6.2  christos 		if (ret == ENOENT)
    167  1.7.6.2  christos 			/* fallback */
    168  1.7.6.2  christos 			ret = lookup_iconv_entry(basedir, "*",
    169  1.7.6.2  christos 						 linebuf, sizeof(linebuf),
    170  1.7.6.2  christos 						 &module, &variable);
    171  1.7.6.2  christos 		if (ret)
    172  1.7.6.2  christos 			return ret;
    173  1.7.6.2  christos 	}
    174  1.7.6.2  christos 
    175  1.7.6.2  christos 	/* initialize iconv handle */
    176  1.7.6.2  christos 	len_convname = strlen(convname);
    177  1.7.6.2  christos 	ci = malloc(sizeof(*ci)+len_convname+1);
    178  1.7.6.2  christos 	if (!ci) {
    179  1.7.6.2  christos 		ret = errno;
    180  1.7.6.2  christos 		goto err;
    181  1.7.6.2  christos 	}
    182  1.7.6.2  christos 	ci->ci_module = NULL;
    183  1.7.6.2  christos 	ci->ci_ops = NULL;
    184  1.7.6.2  christos 	ci->ci_closure = NULL;
    185  1.7.6.2  christos 	ci->ci_convname = (void *)&ci[1];
    186  1.7.6.2  christos 	memcpy(ci->ci_convname, convname, len_convname+1);
    187  1.7.6.2  christos 
    188  1.7.6.2  christos 	/* load module */
    189  1.7.6.2  christos 	ret = _citrus_load_module(&ci->ci_module, module);
    190  1.7.6.2  christos 	if (ret)
    191  1.7.6.2  christos 		goto err;
    192  1.7.6.2  christos 
    193  1.7.6.2  christos 	/* get operators */
    194  1.7.6.2  christos 	getops = (_citrus_iconv_getops_t)
    195  1.7.6.2  christos 	    _citrus_find_getops(ci->ci_module, module, "iconv");
    196  1.7.6.2  christos 	if (!getops) {
    197  1.7.6.2  christos 		ret = EOPNOTSUPP;
    198  1.7.6.2  christos 		goto err;
    199  1.7.6.2  christos 	}
    200  1.7.6.2  christos 	ci->ci_ops = malloc(sizeof(*ci->ci_ops));
    201  1.7.6.2  christos 	if (!ci->ci_ops) {
    202  1.7.6.2  christos 		ret = errno;
    203  1.7.6.2  christos 		goto err;
    204  1.7.6.2  christos 	}
    205  1.7.6.2  christos 	ret = (*getops)(ci->ci_ops, sizeof(*ci->ci_ops),
    206  1.7.6.2  christos 			_CITRUS_ICONV_ABI_VERSION);
    207  1.7.6.2  christos 	if (ret)
    208  1.7.6.2  christos 		goto err;
    209  1.7.6.2  christos 
    210  1.7.6.2  christos 	/* version check */
    211  1.7.6.2  christos 	if (ci->ci_ops->io_abi_version == 1) {
    212  1.7.6.2  christos 		/* binary compatibility broken at ver.2 */
    213  1.7.6.2  christos 		ret = EINVAL;
    214  1.7.6.2  christos 		goto err;
    215  1.7.6.2  christos 	}
    216  1.7.6.2  christos 
    217  1.7.6.2  christos 	if (ci->ci_ops->io_init_shared == NULL ||
    218  1.7.6.2  christos 	    ci->ci_ops->io_uninit_shared == NULL ||
    219  1.7.6.2  christos 	    ci->ci_ops->io_init_context == NULL ||
    220  1.7.6.2  christos 	    ci->ci_ops->io_uninit_context == NULL ||
    221  1.7.6.2  christos 	    ci->ci_ops->io_convert == NULL)
    222  1.7.6.2  christos 		goto err;
    223  1.7.6.2  christos 
    224  1.7.6.2  christos 	/* initialize the converter */
    225  1.7.6.2  christos 	ret = (*ci->ci_ops->io_init_shared)(ci, basedir, src, dst,
    226  1.7.6.2  christos 					    (const void *)variable,
    227  1.7.6.2  christos 					    strlen(variable)+1);
    228  1.7.6.2  christos 	if (ret)
    229  1.7.6.2  christos 		goto err;
    230  1.7.6.2  christos 
    231  1.7.6.2  christos 	*rci = ci;
    232  1.7.6.2  christos 
    233  1.7.6.2  christos 	return 0;
    234  1.7.6.2  christos err:
    235  1.7.6.2  christos 	close_shared(ci);
    236  1.7.6.2  christos 	return ret;
    237  1.7.6.2  christos }
    238  1.7.6.2  christos 
    239  1.7.6.2  christos static __inline int
    240  1.7.6.2  christos hash_func(const char *key)
    241  1.7.6.2  christos {
    242  1.7.6.2  christos 	return _string_hash_func(key, CI_HASH_SIZE);
    243  1.7.6.2  christos }
    244  1.7.6.2  christos 
    245  1.7.6.2  christos static __inline int
    246  1.7.6.2  christos match_func(struct _citrus_iconv_shared * __restrict ci,
    247  1.7.6.2  christos 	   const char * __restrict key)
    248  1.7.6.2  christos {
    249  1.7.6.2  christos 	return strcmp(ci->ci_convname, key);
    250  1.7.6.2  christos }
    251  1.7.6.2  christos 
    252  1.7.6.2  christos static int
    253  1.7.6.2  christos get_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
    254  1.7.6.2  christos 	   const char *basedir, const char *src, const char *dst)
    255  1.7.6.2  christos {
    256  1.7.6.2  christos 	int ret = 0;
    257  1.7.6.2  christos 	int hashval;
    258  1.7.6.2  christos 	struct _citrus_iconv_shared * ci;
    259  1.7.6.2  christos 	char convname[PATH_MAX];
    260  1.7.6.2  christos 
    261  1.7.6.2  christos 	snprintf(convname, sizeof(convname), "%s/%s", src, dst);
    262  1.7.6.2  christos 
    263  1.7.6.2  christos 	rwlock_wrlock(&lock);
    264  1.7.6.2  christos 
    265  1.7.6.2  christos 	/* lookup alread existing entry */
    266  1.7.6.2  christos 	hashval = hash_func(convname);
    267  1.7.6.2  christos 	_CITRUS_HASH_SEARCH(&shared_pool, ci, ci_hash_entry, match_func,
    268  1.7.6.2  christos 			    convname, hashval);
    269  1.7.6.2  christos 	if (ci != NULL) {
    270  1.7.6.2  christos 		/* found */
    271  1.7.6.2  christos 		if (ci->ci_used_count == 0) {
    272  1.7.6.2  christos 			TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry);
    273  1.7.6.2  christos 			shared_num_unused--;
    274  1.7.6.2  christos 		}
    275  1.7.6.2  christos 		ci->ci_used_count++;
    276  1.7.6.2  christos 		*rci = ci;
    277  1.7.6.2  christos 		goto quit;
    278  1.7.6.2  christos 	}
    279  1.7.6.2  christos 
    280  1.7.6.2  christos 	/* create new entry */
    281  1.7.6.2  christos 	ret = open_shared(&ci, basedir, convname, src, dst);
    282  1.7.6.2  christos 	if (ret)
    283  1.7.6.2  christos 		goto quit;
    284  1.7.6.2  christos 
    285  1.7.6.2  christos 	_CITRUS_HASH_INSERT(&shared_pool, ci, ci_hash_entry, hashval);
    286  1.7.6.2  christos 	ci->ci_used_count = 1;
    287  1.7.6.2  christos 	*rci = ci;
    288  1.7.6.2  christos 
    289  1.7.6.2  christos quit:
    290  1.7.6.2  christos 	rwlock_unlock(&lock);
    291  1.7.6.2  christos 
    292  1.7.6.2  christos 	return ret;
    293  1.7.6.2  christos }
    294  1.7.6.2  christos 
    295  1.7.6.2  christos static void
    296  1.7.6.2  christos release_shared(struct _citrus_iconv_shared * __restrict ci)
    297  1.7.6.2  christos {
    298  1.7.6.2  christos 	rwlock_wrlock(&lock);
    299  1.7.6.2  christos 
    300  1.7.6.2  christos 	ci->ci_used_count--;
    301  1.7.6.2  christos 	if (ci->ci_used_count == 0) {
    302  1.7.6.2  christos 		/* put it into unused list */
    303  1.7.6.2  christos 		shared_num_unused++;
    304  1.7.6.2  christos 		TAILQ_INSERT_TAIL(&shared_unused, ci, ci_tailq_entry);
    305  1.7.6.2  christos 		/* flood out */
    306  1.7.6.2  christos 		while (shared_num_unused > shared_max_reuse) {
    307  1.7.6.2  christos 			ci = TAILQ_FIRST(&shared_unused);
    308  1.7.6.2  christos 			_DIAGASSERT(ci != NULL);
    309  1.7.6.2  christos 			TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry);
    310  1.7.6.2  christos 			_CITRUS_HASH_REMOVE(ci, ci_hash_entry);
    311  1.7.6.2  christos 			shared_num_unused--;
    312  1.7.6.2  christos 			close_shared(ci);
    313  1.7.6.2  christos 		}
    314  1.7.6.2  christos 	}
    315  1.7.6.2  christos 
    316  1.7.6.2  christos 	rwlock_unlock(&lock);
    317  1.7.6.2  christos }
    318  1.7.6.2  christos 
    319  1.7.6.2  christos /*
    320  1.7.6.2  christos  * _citrus_iconv_open:
    321  1.7.6.2  christos  *	open a converter for the specified in/out codes.
    322  1.7.6.2  christos  */
    323  1.7.6.2  christos int
    324  1.7.6.2  christos _citrus_iconv_open(struct _citrus_iconv * __restrict * __restrict rcv,
    325  1.7.6.2  christos 		   const char * __restrict basedir,
    326  1.7.6.2  christos 		   const char * __restrict src, const char * __restrict dst)
    327  1.7.6.2  christos {
    328  1.7.6.2  christos 	int ret;
    329  1.7.6.2  christos 	struct _citrus_iconv_shared *ci = NULL;
    330  1.7.6.2  christos 	struct _citrus_iconv *cv;
    331  1.7.6.2  christos 	char realsrc[PATH_MAX], realdst[PATH_MAX];
    332  1.7.6.2  christos 	char buf[PATH_MAX], path[PATH_MAX];
    333  1.7.6.2  christos 
    334  1.7.6.2  christos 	init_cache();
    335  1.7.6.2  christos 
    336  1.7.6.2  christos 	/* resolve codeset name aliases */
    337  1.7.6.2  christos 	snprintf(path, sizeof(path), "%s/%s", basedir, _CITRUS_ICONV_ALIAS);
    338  1.7.6.2  christos 	strlcpy(realsrc,
    339  1.7.6.2  christos 		_lookup_alias(path, src, buf, (size_t)PATH_MAX,
    340  1.7.6.2  christos 		_LOOKUP_CASE_IGNORE),
    341  1.7.6.2  christos 		(size_t)PATH_MAX);
    342  1.7.6.2  christos 	strlcpy(realdst,
    343  1.7.6.2  christos 		_lookup_alias(path, dst, buf, (size_t)PATH_MAX,
    344  1.7.6.2  christos 		_LOOKUP_CASE_IGNORE),
    345  1.7.6.2  christos 		(size_t)PATH_MAX);
    346  1.7.6.2  christos 
    347  1.7.6.2  christos 	/* sanity check */
    348  1.7.6.2  christos 	if (strchr(realsrc, '/') != NULL || strchr(realdst, '/'))
    349  1.7.6.2  christos 		return EINVAL;
    350  1.7.6.2  christos 
    351  1.7.6.2  christos 	/* get shared record */
    352  1.7.6.2  christos 	ret = get_shared(&ci, basedir, realsrc, realdst);
    353  1.7.6.2  christos 	if (ret)
    354  1.7.6.2  christos 		return ret;
    355  1.7.6.2  christos 
    356  1.7.6.2  christos 	/* create/init context */
    357  1.7.6.2  christos 	cv = malloc(sizeof(*cv));
    358  1.7.6.2  christos 	if (cv == NULL) {
    359  1.7.6.2  christos 		ret = errno;
    360  1.7.6.2  christos 		release_shared(ci);
    361  1.7.6.2  christos 		return ret;
    362  1.7.6.2  christos 	}
    363  1.7.6.2  christos 	cv->cv_shared = ci;
    364  1.7.6.2  christos 	ret = (*ci->ci_ops->io_init_context)(cv);
    365  1.7.6.2  christos 	if (ret) {
    366  1.7.6.2  christos 		release_shared(ci);
    367  1.7.6.2  christos 		free(cv);
    368  1.7.6.2  christos 		return ret;
    369  1.7.6.2  christos 	}
    370  1.7.6.2  christos 	*rcv = cv;
    371  1.7.6.2  christos 
    372  1.7.6.2  christos 	return 0;
    373  1.7.6.2  christos }
    374  1.7.6.2  christos 
    375  1.7.6.2  christos /*
    376  1.7.6.2  christos  * _citrus_iconv_close:
    377  1.7.6.2  christos  *	close the specified converter.
    378  1.7.6.2  christos  */
    379  1.7.6.2  christos void
    380  1.7.6.2  christos _citrus_iconv_close(struct _citrus_iconv *cv)
    381  1.7.6.2  christos {
    382  1.7.6.2  christos 	if (cv) {
    383  1.7.6.2  christos 		(*cv->cv_shared->ci_ops->io_uninit_context)(cv);
    384  1.7.6.2  christos 		release_shared(cv->cv_shared);
    385  1.7.6.2  christos 		free(cv);
    386  1.7.6.2  christos 	}
    387  1.7.6.2  christos }
    388