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