citrus_mapper.c revision 264497
116880Sgpalmer/* $FreeBSD: stable/10/lib/libc/iconv/citrus_mapper.c 264497 2014-04-15 09:49:44Z tijl $ */ 216880Sgpalmer/* $NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $ */ 316880Sgpalmer 416880Sgpalmer/*- 516880Sgpalmer * Copyright (c)2003 Citrus Project, 616880Sgpalmer * All rights reserved. 716880Sgpalmer * 816880Sgpalmer * Redistribution and use in source and binary forms, with or without 916880Sgpalmer * modification, are permitted provided that the following conditions 1016880Sgpalmer * are met: 1116880Sgpalmer * 1. Redistributions of source code must retain the above copyright 1216880Sgpalmer * notice, this list of conditions and the following disclaimer. 1316880Sgpalmer * 2. Redistributions in binary form must reproduce the above copyright 1416880Sgpalmer * notice, this list of conditions and the following disclaimer in the 1516880Sgpalmer * documentation and/or other materials provided with the distribution. 1616880Sgpalmer * 1716880Sgpalmer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1816880Sgpalmer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1916880Sgpalmer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2016880Sgpalmer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2116880Sgpalmer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2216880Sgpalmer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2316880Sgpalmer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2416880Sgpalmer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2516880Sgpalmer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2616880Sgpalmer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2716880Sgpalmer * SUCH DAMAGE. 2816880Sgpalmer */ 2919984Smckay 3016880Sgpalmer#include <sys/cdefs.h> 3116880Sgpalmer#include <sys/types.h> 3219984Smckay#include <sys/stat.h> 3319984Smckay#include <sys/queue.h> 3419984Smckay 3519984Smckay#include <assert.h> 3619984Smckay#include <errno.h> 3716880Sgpalmer#include <limits.h> 3816880Sgpalmer#include <stdio.h> 3916880Sgpalmer#include <stdlib.h> 4016880Sgpalmer#include <string.h> 4116880Sgpalmer 4216880Sgpalmer#include "citrus_namespace.h" 4316880Sgpalmer#include "citrus_types.h" 4416880Sgpalmer#include "citrus_region.h" 4516880Sgpalmer#include "citrus_lock.h" 4619984Smckay#include "citrus_memstream.h" 4716880Sgpalmer#include "citrus_bcs.h" 4819984Smckay#include "citrus_mmap.h" 4916880Sgpalmer#include "citrus_module.h" 5016880Sgpalmer#include "citrus_hash.h" 5116880Sgpalmer#include "citrus_mapper.h" 5216880Sgpalmer 5316880Sgpalmer#define _CITRUS_MAPPER_DIR "mapper.dir" 5419984Smckay 5519984Smckay#define CM_HASH_SIZE 101 5616880Sgpalmer#define REFCOUNT_PERSISTENT -1 5719984Smckay 5816880Sgpalmerstatic pthread_rwlock_t cm_lock = PTHREAD_RWLOCK_INITIALIZER; 5916880Sgpalmer 6016880Sgpalmerstruct _citrus_mapper_area { 6116880Sgpalmer _CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE) ma_cache; 6216880Sgpalmer char *ma_dir; 6316880Sgpalmer}; 6416880Sgpalmer 6519984Smckay/* 6619984Smckay * _citrus_mapper_create_area: 6716880Sgpalmer * create mapper area 6816880Sgpalmer */ 6919984Smckay 7019984Smckayint 7116880Sgpalmer_citrus_mapper_create_area( 7216880Sgpalmer struct _citrus_mapper_area *__restrict *__restrict rma, 7316880Sgpalmer const char *__restrict area) 7416880Sgpalmer{ 7516880Sgpalmer struct _citrus_mapper_area *ma; 7616880Sgpalmer struct stat st; 7719984Smckay char path[PATH_MAX]; 7816880Sgpalmer int ret; 7916880Sgpalmer 8016880Sgpalmer WLOCK(&cm_lock); 8116880Sgpalmer 8218119Speter if (*rma != NULL) { 8318119Speter ret = 0; 8418119Speter goto quit; 8516880Sgpalmer } 8616880Sgpalmer 8716880Sgpalmer snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR); 8816880Sgpalmer 8919984Smckay ret = stat(path, &st); 9016880Sgpalmer if (ret) 9116880Sgpalmer goto quit; 9216880Sgpalmer 9316880Sgpalmer ma = malloc(sizeof(*ma)); 9416880Sgpalmer if (ma == NULL) { 9516880Sgpalmer ret = errno; 9619984Smckay goto quit; 9719984Smckay } 9816880Sgpalmer ma->ma_dir = strdup(area); 9919984Smckay if (ma->ma_dir == NULL) { 10019984Smckay ret = errno; 10116880Sgpalmer free(ma); 10216880Sgpalmer goto quit; 10319984Smckay } 10419984Smckay _CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE); 10516880Sgpalmer 10619984Smckay *rma = ma; 10716880Sgpalmer ret = 0; 10816880Sgpalmerquit: 10916880Sgpalmer UNLOCK(&cm_lock); 11019984Smckay 11116880Sgpalmer return (ret); 11219984Smckay} 11319984Smckay 11419984Smckay 11519984Smckay/* 11619984Smckay * lookup_mapper_entry: 11716880Sgpalmer * lookup mapper.dir entry in the specified directory. 11819984Smckay * 11919984Smckay * line format of iconv.dir file: 12019984Smckay * mapper module arg 12116880Sgpalmer * mapper : mapper name. 12219984Smckay * module : mapper module name. 12316880Sgpalmer * arg : argument for the module (generally, description file name) 12416880Sgpalmer */ 12516880Sgpalmer 12619984Smckaystatic int 12716880Sgpalmerlookup_mapper_entry(const char *dir, const char *mapname, void *linebuf, 12816880Sgpalmer size_t linebufsize, const char **module, const char **variable) 12919984Smckay{ 13016880Sgpalmer struct _region r; 13117131Sgpalmer struct _memstream ms; 13217131Sgpalmer const char *cp, *cq; 13319984Smckay char *p; 13417131Sgpalmer char path[PATH_MAX]; 13517131Sgpalmer size_t len; 13616880Sgpalmer int ret; 13719984Smckay 13819984Smckay /* create mapper.dir path */ 13919984Smckay snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR); 14019984Smckay 14119984Smckay /* open read stream */ 14219984Smckay ret = _map_file(&r, path); 14319984Smckay if (ret) 14419984Smckay return (ret); 14519984Smckay 14619984Smckay _memstream_bind(&ms, &r); 14719984Smckay 14816880Sgpalmer /* search the line matching to the map name */ 14919984Smckay cp = _memstream_matchline(&ms, mapname, &len, 0); 15017131Sgpalmer if (!cp) { 15116880Sgpalmer ret = ENOENT; 15217131Sgpalmer goto quit; 15319984Smckay } 15419984Smckay if (!len || len > linebufsize - 1) { 15516880Sgpalmer ret = EINVAL; 15616880Sgpalmer goto quit; 15716880Sgpalmer } 15816880Sgpalmer 15916880Sgpalmer p = linebuf; 16016880Sgpalmer /* get module name */ 16119984Smckay *module = p; 16219984Smckay cq = _bcs_skip_nonws_len(cp, &len); 16319984Smckay strlcpy(p, cp, (size_t)(cq - cp + 1)); 16419984Smckay p += cq - cp + 1; 16516880Sgpalmer 16619984Smckay /* get variable */ 16716880Sgpalmer *variable = p; 16816880Sgpalmer cp = _bcs_skip_ws_len(cq, &len); 16916880Sgpalmer strlcpy(p, cp, len + 1); 17019984Smckay 17119984Smckay ret = 0; 17219984Smckay 17316880Sgpalmerquit: 17416880Sgpalmer _unmap_file(&r); 17519984Smckay return (ret); 17616880Sgpalmer} 17719984Smckay 17816880Sgpalmer/* 17919984Smckay * mapper_close: 18019984Smckay * simply close a mapper. (without handling hash) 18116880Sgpalmer */ 18219984Smckaystatic void 18319984Smckaymapper_close(struct _citrus_mapper *cm) 18419984Smckay{ 18519984Smckay if (cm->cm_module) { 18619984Smckay if (cm->cm_ops) { 18719984Smckay if (cm->cm_closure) 18819984Smckay (*cm->cm_ops->mo_uninit)(cm); 18919984Smckay free(cm->cm_ops); 19019984Smckay } 19119984Smckay _citrus_unload_module(cm->cm_module); 19219984Smckay } 19319984Smckay free(cm->cm_traits); 19419984Smckay free(cm); 19519984Smckay} 19619984Smckay 19719984Smckay/* 19819984Smckay * mapper_open: 19919984Smckay * simply open a mapper. (without handling hash) 20019984Smckay */ 20119984Smckaystatic int 20219984Smckaymapper_open(struct _citrus_mapper_area *__restrict ma, 20319984Smckay struct _citrus_mapper * __restrict * __restrict rcm, 20419984Smckay const char * __restrict module, 20519984Smckay const char * __restrict variable) 20616880Sgpalmer{ 20719984Smckay struct _citrus_mapper *cm; 20819984Smckay _citrus_mapper_getops_t getops; 20916880Sgpalmer int ret; 210 211 /* initialize mapper handle */ 212 cm = malloc(sizeof(*cm)); 213 if (!cm) 214 return (errno); 215 216 cm->cm_module = NULL; 217 cm->cm_ops = NULL; 218 cm->cm_closure = NULL; 219 cm->cm_traits = NULL; 220 cm->cm_refcount = 0; 221 cm->cm_key = NULL; 222 223 /* load module */ 224 ret = _citrus_load_module(&cm->cm_module, module); 225 if (ret) 226 goto err; 227 228 /* get operators */ 229 getops = (_citrus_mapper_getops_t) 230 _citrus_find_getops(cm->cm_module, module, "mapper"); 231 if (!getops) { 232 ret = EOPNOTSUPP; 233 goto err; 234 } 235 cm->cm_ops = malloc(sizeof(*cm->cm_ops)); 236 if (!cm->cm_ops) { 237 ret = errno; 238 goto err; 239 } 240 ret = (*getops)(cm->cm_ops); 241 if (ret) 242 goto err; 243 244 if (!cm->cm_ops->mo_init || 245 !cm->cm_ops->mo_uninit || 246 !cm->cm_ops->mo_convert || 247 !cm->cm_ops->mo_init_state) { 248 ret = EINVAL; 249 goto err; 250 } 251 252 /* allocate traits structure */ 253 cm->cm_traits = malloc(sizeof(*cm->cm_traits)); 254 if (cm->cm_traits == NULL) { 255 ret = errno; 256 goto err; 257 } 258 /* initialize the mapper */ 259 ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir, 260 (const void *)variable, strlen(variable) + 1, 261 cm->cm_traits, sizeof(*cm->cm_traits)); 262 if (ret) 263 goto err; 264 265 *rcm = cm; 266 267 return (0); 268 269err: 270 mapper_close(cm); 271 return (ret); 272} 273 274/* 275 * _citrus_mapper_open_direct: 276 * open a mapper. 277 */ 278int 279_citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma, 280 struct _citrus_mapper * __restrict * __restrict rcm, 281 const char * __restrict module, const char * __restrict variable) 282{ 283 284 return (mapper_open(ma, rcm, module, variable)); 285} 286 287/* 288 * hash_func 289 */ 290static __inline int 291hash_func(const char *key) 292{ 293 294 return (_string_hash_func(key, CM_HASH_SIZE)); 295} 296 297/* 298 * match_func 299 */ 300static __inline int 301match_func(struct _citrus_mapper *cm, const char *key) 302{ 303 304 return (strcmp(cm->cm_key, key)); 305} 306 307/* 308 * _citrus_mapper_open: 309 * open a mapper with looking up "mapper.dir". 310 */ 311int 312_citrus_mapper_open(struct _citrus_mapper_area *__restrict ma, 313 struct _citrus_mapper * __restrict * __restrict rcm, 314 const char * __restrict mapname) 315{ 316 struct _citrus_mapper *cm; 317 char linebuf[PATH_MAX]; 318 const char *module, *variable; 319 int hashval, ret; 320 321 variable = NULL; 322 323 WLOCK(&cm_lock); 324 325 /* search in the cache */ 326 hashval = hash_func(mapname); 327 _CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname, 328 hashval); 329 if (cm) { 330 /* found */ 331 cm->cm_refcount++; 332 *rcm = cm; 333 ret = 0; 334 goto quit; 335 } 336 337 /* search mapper entry */ 338 ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf, 339 (size_t)PATH_MAX, &module, &variable); 340 if (ret) 341 goto quit; 342 343 /* open mapper */ 344 UNLOCK(&cm_lock); 345 ret = mapper_open(ma, &cm, module, variable); 346 WLOCK(&cm_lock); 347 if (ret) 348 goto quit; 349 cm->cm_key = strdup(mapname); 350 if (cm->cm_key == NULL) { 351 ret = errno; 352 _mapper_close(cm); 353 goto quit; 354 } 355 356 /* insert to the cache */ 357 cm->cm_refcount = 1; 358 _CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval); 359 360 *rcm = cm; 361 ret = 0; 362quit: 363 UNLOCK(&cm_lock); 364 365 return (ret); 366} 367 368/* 369 * _citrus_mapper_close: 370 * close the specified mapper. 371 */ 372void 373_citrus_mapper_close(struct _citrus_mapper *cm) 374{ 375 376 if (cm) { 377 WLOCK(&cm_lock); 378 if (cm->cm_refcount == REFCOUNT_PERSISTENT) 379 goto quit; 380 if (cm->cm_refcount > 0) { 381 if (--cm->cm_refcount > 0) 382 goto quit; 383 _CITRUS_HASH_REMOVE(cm, cm_entry); 384 free(cm->cm_key); 385 } 386 UNLOCK(&cm_lock); 387 mapper_close(cm); 388 return; 389quit: 390 UNLOCK(&cm_lock); 391 } 392} 393 394/* 395 * _citrus_mapper_set_persistent: 396 * set persistent count. 397 */ 398void 399_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm) 400{ 401 402 WLOCK(&cm_lock); 403 cm->cm_refcount = REFCOUNT_PERSISTENT; 404 UNLOCK(&cm_lock); 405} 406