citrus_iconv.c revision 219019
1219019Sgabor/* $FreeBSD: head/lib/libc/iconv/citrus_iconv.c 219019 2011-02-25 00:04:39Z gabor $ */ 2219019Sgabor/* $NetBSD: citrus_iconv.c,v 1.7 2008/07/25 14:05:25 christos Exp $ */ 3219019Sgabor 4219019Sgabor/*- 5219019Sgabor * Copyright (c)2003 Citrus Project, 6219019Sgabor * All rights reserved. 7219019Sgabor * 8219019Sgabor * Redistribution and use in source and binary forms, with or without 9219019Sgabor * modification, are permitted provided that the following conditions 10219019Sgabor * are met: 11219019Sgabor * 1. Redistributions of source code must retain the above copyright 12219019Sgabor * notice, this list of conditions and the following disclaimer. 13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 14219019Sgabor * notice, this list of conditions and the following disclaimer in the 15219019Sgabor * documentation and/or other materials provided with the distribution. 16219019Sgabor * 17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20219019Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27219019Sgabor * SUCH DAMAGE. 28219019Sgabor */ 29219019Sgabor 30219019Sgabor#include <sys/cdefs.h> 31219019Sgabor#include <sys/types.h> 32219019Sgabor#include <sys/queue.h> 33219019Sgabor 34219019Sgabor#include <assert.h> 35219019Sgabor#include <dirent.h> 36219019Sgabor#include <errno.h> 37219019Sgabor#include <iconv.h> 38219019Sgabor#include <langinfo.h> 39219019Sgabor#include <limits.h> 40219019Sgabor#include <paths.h> 41219019Sgabor#include <stdbool.h> 42219019Sgabor#include <stdio.h> 43219019Sgabor#include <stdlib.h> 44219019Sgabor#include <string.h> 45219019Sgabor#include <unistd.h> 46219019Sgabor 47219019Sgabor#include "citrus_namespace.h" 48219019Sgabor#include "citrus_bcs.h" 49219019Sgabor#include "citrus_esdb.h" 50219019Sgabor#include "citrus_region.h" 51219019Sgabor#include "citrus_memstream.h" 52219019Sgabor#include "citrus_mmap.h" 53219019Sgabor#include "citrus_module.h" 54219019Sgabor#include "citrus_lock.h" 55219019Sgabor#include "citrus_lookup.h" 56219019Sgabor#include "citrus_hash.h" 57219019Sgabor#include "citrus_iconv.h" 58219019Sgabor 59219019Sgabor#define _CITRUS_ICONV_DIR "iconv.dir" 60219019Sgabor#define _CITRUS_ICONV_ALIAS "iconv.alias" 61219019Sgabor 62219019Sgabor#define CI_HASH_SIZE 101 63219019Sgabor#define CI_INITIAL_MAX_REUSE 5 64219019Sgabor#define CI_ENV_MAX_REUSE "ICONV_MAX_REUSE" 65219019Sgabor 66219019Sgaborstatic bool isinit = false; 67219019Sgaborstatic int shared_max_reuse, shared_num_unused; 68219019Sgaborstatic _CITRUS_HASH_HEAD(, _citrus_iconv_shared, CI_HASH_SIZE) shared_pool; 69219019Sgaborstatic TAILQ_HEAD(, _citrus_iconv_shared) shared_unused; 70219019Sgabor 71219019Sgaborstatic __inline void 72219019Sgaborinit_cache(void) 73219019Sgabor{ 74219019Sgabor 75219019Sgabor WLOCK; 76219019Sgabor if (!isinit) { 77219019Sgabor _CITRUS_HASH_INIT(&shared_pool, CI_HASH_SIZE); 78219019Sgabor TAILQ_INIT(&shared_unused); 79219019Sgabor shared_max_reuse = -1; 80219019Sgabor if (!issetugid() && getenv(CI_ENV_MAX_REUSE)) 81219019Sgabor shared_max_reuse = atoi(getenv(CI_ENV_MAX_REUSE)); 82219019Sgabor if (shared_max_reuse < 0) 83219019Sgabor shared_max_reuse = CI_INITIAL_MAX_REUSE; 84219019Sgabor isinit = true; 85219019Sgabor } 86219019Sgabor UNLOCK; 87219019Sgabor} 88219019Sgabor 89219019Sgaborstatic __inline void 90219019Sgaborclose_shared(struct _citrus_iconv_shared *ci) 91219019Sgabor{ 92219019Sgabor 93219019Sgabor if (ci) { 94219019Sgabor if (ci->ci_module) { 95219019Sgabor if (ci->ci_ops) { 96219019Sgabor if (ci->ci_closure) 97219019Sgabor (*ci->ci_ops->io_uninit_shared)(ci); 98219019Sgabor free(ci->ci_ops); 99219019Sgabor } 100219019Sgabor _citrus_unload_module(ci->ci_module); 101219019Sgabor } 102219019Sgabor free(ci); 103219019Sgabor } 104219019Sgabor} 105219019Sgabor 106219019Sgaborstatic __inline int 107219019Sgaboropen_shared(struct _citrus_iconv_shared * __restrict * __restrict rci, 108219019Sgabor const char * __restrict convname, const char * __restrict src, 109219019Sgabor const char * __restrict dst) 110219019Sgabor{ 111219019Sgabor struct _citrus_iconv_shared *ci; 112219019Sgabor _citrus_iconv_getops_t getops; 113219019Sgabor const char *module; 114219019Sgabor size_t len_convname; 115219019Sgabor int ret; 116219019Sgabor 117219019Sgabor module = (strcmp(src, dst) != 0) ? "iconv_std" : "iconv_none"; 118219019Sgabor 119219019Sgabor /* initialize iconv handle */ 120219019Sgabor len_convname = strlen(convname); 121219019Sgabor ci = malloc(sizeof(*ci) + len_convname + 1); 122219019Sgabor if (!ci) { 123219019Sgabor ret = errno; 124219019Sgabor goto err; 125219019Sgabor } 126219019Sgabor ci->ci_module = NULL; 127219019Sgabor ci->ci_ops = NULL; 128219019Sgabor ci->ci_closure = NULL; 129219019Sgabor ci->ci_convname = (void *)&ci[1]; 130219019Sgabor memcpy(ci->ci_convname, convname, len_convname + 1); 131219019Sgabor 132219019Sgabor /* load module */ 133219019Sgabor ret = _citrus_load_module(&ci->ci_module, module); 134219019Sgabor if (ret) 135219019Sgabor goto err; 136219019Sgabor 137219019Sgabor /* get operators */ 138219019Sgabor getops = (_citrus_iconv_getops_t)_citrus_find_getops(ci->ci_module, 139219019Sgabor module, "iconv"); 140219019Sgabor if (!getops) { 141219019Sgabor ret = EOPNOTSUPP; 142219019Sgabor goto err; 143219019Sgabor } 144219019Sgabor ci->ci_ops = malloc(sizeof(*ci->ci_ops)); 145219019Sgabor if (!ci->ci_ops) { 146219019Sgabor ret = errno; 147219019Sgabor goto err; 148219019Sgabor } 149219019Sgabor ret = (*getops)(ci->ci_ops); 150219019Sgabor if (ret) 151219019Sgabor goto err; 152219019Sgabor 153219019Sgabor if (ci->ci_ops->io_init_shared == NULL || 154219019Sgabor ci->ci_ops->io_uninit_shared == NULL || 155219019Sgabor ci->ci_ops->io_init_context == NULL || 156219019Sgabor ci->ci_ops->io_uninit_context == NULL || 157219019Sgabor ci->ci_ops->io_convert == NULL) 158219019Sgabor goto err; 159219019Sgabor 160219019Sgabor /* initialize the converter */ 161219019Sgabor ret = (*ci->ci_ops->io_init_shared)(ci, src, dst); 162219019Sgabor if (ret) 163219019Sgabor goto err; 164219019Sgabor 165219019Sgabor *rci = ci; 166219019Sgabor 167219019Sgabor return (0); 168219019Sgaborerr: 169219019Sgabor close_shared(ci); 170219019Sgabor return (ret); 171219019Sgabor} 172219019Sgabor 173219019Sgaborstatic __inline int 174219019Sgaborhash_func(const char *key) 175219019Sgabor{ 176219019Sgabor 177219019Sgabor return (_string_hash_func(key, CI_HASH_SIZE)); 178219019Sgabor} 179219019Sgabor 180219019Sgaborstatic __inline int 181219019Sgabormatch_func(struct _citrus_iconv_shared * __restrict ci, 182219019Sgabor const char * __restrict key) 183219019Sgabor{ 184219019Sgabor 185219019Sgabor return (strcmp(ci->ci_convname, key)); 186219019Sgabor} 187219019Sgabor 188219019Sgaborstatic int 189219019Sgaborget_shared(struct _citrus_iconv_shared * __restrict * __restrict rci, 190219019Sgabor const char *src, const char *dst) 191219019Sgabor{ 192219019Sgabor struct _citrus_iconv_shared * ci; 193219019Sgabor char convname[PATH_MAX]; 194219019Sgabor int hashval, ret = 0; 195219019Sgabor 196219019Sgabor snprintf(convname, sizeof(convname), "%s/%s", src, dst); 197219019Sgabor 198219019Sgabor WLOCK; 199219019Sgabor 200219019Sgabor /* lookup alread existing entry */ 201219019Sgabor hashval = hash_func(convname); 202219019Sgabor _CITRUS_HASH_SEARCH(&shared_pool, ci, ci_hash_entry, match_func, 203219019Sgabor convname, hashval); 204219019Sgabor if (ci != NULL) { 205219019Sgabor /* found */ 206219019Sgabor if (ci->ci_used_count == 0) { 207219019Sgabor TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry); 208219019Sgabor shared_num_unused--; 209219019Sgabor } 210219019Sgabor ci->ci_used_count++; 211219019Sgabor *rci = ci; 212219019Sgabor goto quit; 213219019Sgabor } 214219019Sgabor 215219019Sgabor /* create new entry */ 216219019Sgabor ret = open_shared(&ci, convname, src, dst); 217219019Sgabor if (ret) 218219019Sgabor goto quit; 219219019Sgabor 220219019Sgabor _CITRUS_HASH_INSERT(&shared_pool, ci, ci_hash_entry, hashval); 221219019Sgabor ci->ci_used_count = 1; 222219019Sgabor *rci = ci; 223219019Sgabor 224219019Sgaborquit: 225219019Sgabor UNLOCK; 226219019Sgabor 227219019Sgabor return (ret); 228219019Sgabor} 229219019Sgabor 230219019Sgaborstatic void 231219019Sgaborrelease_shared(struct _citrus_iconv_shared * __restrict ci) 232219019Sgabor{ 233219019Sgabor 234219019Sgabor WLOCK; 235219019Sgabor ci->ci_used_count--; 236219019Sgabor if (ci->ci_used_count == 0) { 237219019Sgabor /* put it into unused list */ 238219019Sgabor shared_num_unused++; 239219019Sgabor TAILQ_INSERT_TAIL(&shared_unused, ci, ci_tailq_entry); 240219019Sgabor /* flood out */ 241219019Sgabor while (shared_num_unused > shared_max_reuse) { 242219019Sgabor ci = TAILQ_FIRST(&shared_unused); 243219019Sgabor TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry); 244219019Sgabor _CITRUS_HASH_REMOVE(ci, ci_hash_entry); 245219019Sgabor shared_num_unused--; 246219019Sgabor close_shared(ci); 247219019Sgabor } 248219019Sgabor } 249219019Sgabor 250219019Sgabor UNLOCK; 251219019Sgabor} 252219019Sgabor 253219019Sgabor/* 254219019Sgabor * _citrus_iconv_open: 255219019Sgabor * open a converter for the specified in/out codes. 256219019Sgabor */ 257219019Sgaborint 258219019Sgabor_citrus_iconv_open(struct _citrus_iconv * __restrict * __restrict rcv, 259219019Sgabor const char * __restrict src, const char * __restrict dst) 260219019Sgabor{ 261219019Sgabor struct _citrus_iconv *cv; 262219019Sgabor struct _citrus_iconv_shared *ci = NULL; 263219019Sgabor char realdst[PATH_MAX], realsrc[PATH_MAX]; 264219019Sgabor char buf[PATH_MAX], path[PATH_MAX]; 265219019Sgabor int ret; 266219019Sgabor 267219019Sgabor init_cache(); 268219019Sgabor 269219019Sgabor /* GNU behaviour, using locale encoding if "" or "char" is specified */ 270219019Sgabor if ((strcmp(src, "") == 0) || (strcmp(src, "char") == 0)) 271219019Sgabor src = nl_langinfo(CODESET); 272219019Sgabor if ((strcmp(dst, "") == 0) || (strcmp(dst, "char") == 0)) 273219019Sgabor dst = nl_langinfo(CODESET); 274219019Sgabor 275219019Sgabor /* resolve codeset name aliases */ 276219019Sgabor strlcpy(realsrc, _lookup_alias(path, src, buf, (size_t)PATH_MAX, 277219019Sgabor _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX); 278219019Sgabor strlcpy(realdst, _lookup_alias(path, dst, buf, (size_t)PATH_MAX, 279219019Sgabor _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX); 280219019Sgabor 281219019Sgabor /* sanity check */ 282219019Sgabor if (strchr(realsrc, '/') != NULL || strchr(realdst, '/')) 283219019Sgabor return (EINVAL); 284219019Sgabor 285219019Sgabor /* get shared record */ 286219019Sgabor ret = get_shared(&ci, realsrc, realdst); 287219019Sgabor if (ret) 288219019Sgabor return (ret); 289219019Sgabor 290219019Sgabor /* create/init context */ 291219019Sgabor if (*rcv == NULL) { 292219019Sgabor cv = malloc(sizeof(*cv)); 293219019Sgabor if (cv == NULL) { 294219019Sgabor ret = errno; 295219019Sgabor release_shared(ci); 296219019Sgabor return (ret); 297219019Sgabor } 298219019Sgabor *rcv = cv; 299219019Sgabor } 300219019Sgabor (*rcv)->cv_shared = ci; 301219019Sgabor ret = (*ci->ci_ops->io_init_context)(*rcv); 302219019Sgabor if (ret) { 303219019Sgabor release_shared(ci); 304219019Sgabor free(*rcv); 305219019Sgabor return (ret); 306219019Sgabor } 307219019Sgabor return (0); 308219019Sgabor} 309219019Sgabor 310219019Sgabor/* 311219019Sgabor * _citrus_iconv_close: 312219019Sgabor * close the specified converter. 313219019Sgabor */ 314219019Sgaborvoid 315219019Sgabor_citrus_iconv_close(struct _citrus_iconv *cv) 316219019Sgabor{ 317219019Sgabor 318219019Sgabor if (cv) { 319219019Sgabor (*cv->cv_shared->ci_ops->io_uninit_context)(cv); 320219019Sgabor release_shared(cv->cv_shared); 321219019Sgabor free(cv); 322219019Sgabor } 323219019Sgabor} 324219019Sgabor 325219019Sgaborconst char 326219019Sgabor*_citrus_iconv_canonicalize(const char *name) 327219019Sgabor{ 328219019Sgabor char *buf; 329219019Sgabor 330219019Sgabor if ((buf = malloc((size_t)PATH_MAX)) == NULL) 331219019Sgabor return (NULL); 332219019Sgabor memset((void *)buf, 0, (size_t)PATH_MAX); 333219019Sgabor _citrus_esdb_alias(name, buf, (size_t)PATH_MAX); 334219019Sgabor return (buf); 335219019Sgabor} 336