1219019Sgabor/* $FreeBSD$ */
2219019Sgabor/* $NetBSD: citrus_mapper.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/stat.h>
33219019Sgabor#include <sys/queue.h>
34219019Sgabor
35219019Sgabor#include <assert.h>
36219019Sgabor#include <errno.h>
37219019Sgabor#include <limits.h>
38219019Sgabor#include <stdio.h>
39219019Sgabor#include <stdlib.h>
40219019Sgabor#include <string.h>
41219019Sgabor
42219019Sgabor#include "citrus_namespace.h"
43219019Sgabor#include "citrus_types.h"
44219019Sgabor#include "citrus_region.h"
45219019Sgabor#include "citrus_lock.h"
46219019Sgabor#include "citrus_memstream.h"
47219019Sgabor#include "citrus_bcs.h"
48219019Sgabor#include "citrus_mmap.h"
49219019Sgabor#include "citrus_module.h"
50219019Sgabor#include "citrus_hash.h"
51219019Sgabor#include "citrus_mapper.h"
52219019Sgabor
53219019Sgabor#define _CITRUS_MAPPER_DIR	"mapper.dir"
54219019Sgabor
55219019Sgabor#define CM_HASH_SIZE 101
56219019Sgabor#define REFCOUNT_PERSISTENT	-1
57219019Sgabor
58219019Sgaborstruct _citrus_mapper_area {
59219019Sgabor	_CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE)	 ma_cache;
60219019Sgabor	char							*ma_dir;
61219019Sgabor};
62219019Sgabor
63219019Sgabor/*
64219019Sgabor * _citrus_mapper_create_area:
65219019Sgabor *	create mapper area
66219019Sgabor */
67219019Sgabor
68219019Sgaborint
69219019Sgabor_citrus_mapper_create_area(
70219019Sgabor    struct _citrus_mapper_area *__restrict *__restrict rma,
71219019Sgabor    const char *__restrict area)
72219019Sgabor{
73219019Sgabor	struct _citrus_mapper_area *ma;
74219019Sgabor	struct stat st;
75219019Sgabor	char path[PATH_MAX];
76219019Sgabor	int ret;
77219019Sgabor
78219019Sgabor	WLOCK;
79219019Sgabor
80219019Sgabor	if (*rma != NULL) {
81219019Sgabor		ret = 0;
82219019Sgabor		goto quit;
83219019Sgabor	}
84219019Sgabor
85219019Sgabor	snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
86219019Sgabor
87219019Sgabor	ret = stat(path, &st);
88219019Sgabor	if (ret)
89219019Sgabor		goto quit;
90219019Sgabor
91219019Sgabor	ma = malloc(sizeof(*ma));
92219019Sgabor	if (ma == NULL) {
93219019Sgabor		ret = errno;
94219019Sgabor		goto quit;
95219019Sgabor	}
96219019Sgabor	ma->ma_dir = strdup(area);
97219019Sgabor	if (ma->ma_dir == NULL) {
98219019Sgabor		ret = errno;
99219019Sgabor		free(ma->ma_dir);
100219019Sgabor		goto quit;
101219019Sgabor	}
102219019Sgabor	_CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
103219019Sgabor
104219019Sgabor	*rma = ma;
105219019Sgabor	ret = 0;
106219019Sgaborquit:
107219019Sgabor	UNLOCK;
108219019Sgabor
109219019Sgabor	return (ret);
110219019Sgabor}
111219019Sgabor
112219019Sgabor
113219019Sgabor/*
114219019Sgabor * lookup_mapper_entry:
115219019Sgabor *	lookup mapper.dir entry in the specified directory.
116219019Sgabor *
117219019Sgabor * line format of iconv.dir file:
118219019Sgabor *	mapper	module	arg
119219019Sgabor * mapper : mapper name.
120219019Sgabor * module : mapper module name.
121219019Sgabor * arg    : argument for the module (generally, description file name)
122219019Sgabor */
123219019Sgabor
124219019Sgaborstatic int
125219019Sgaborlookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
126219019Sgabor    size_t linebufsize, const char **module, const char **variable)
127219019Sgabor{
128219019Sgabor	struct _region r;
129219019Sgabor	struct _memstream ms;
130219019Sgabor	const char *cp, *cq;
131219019Sgabor	char *p;
132219019Sgabor	char path[PATH_MAX];
133219019Sgabor	size_t len;
134219019Sgabor	int ret;
135219019Sgabor
136219019Sgabor	/* create mapper.dir path */
137219019Sgabor	snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
138219019Sgabor
139219019Sgabor	/* open read stream */
140219019Sgabor	ret = _map_file(&r, path);
141219019Sgabor	if (ret)
142219019Sgabor		return (ret);
143219019Sgabor
144219019Sgabor	_memstream_bind(&ms, &r);
145219019Sgabor
146219019Sgabor	/* search the line matching to the map name */
147219019Sgabor	cp = _memstream_matchline(&ms, mapname, &len, 0);
148219019Sgabor	if (!cp) {
149219019Sgabor		ret = ENOENT;
150219019Sgabor		goto quit;
151219019Sgabor	}
152219019Sgabor	if (!len || len > linebufsize - 1) {
153219019Sgabor		ret = EINVAL;
154219019Sgabor		goto quit;
155219019Sgabor	}
156219019Sgabor
157219019Sgabor	p = linebuf;
158219019Sgabor	/* get module name */
159219019Sgabor	*module = p;
160219019Sgabor	cq = _bcs_skip_nonws_len(cp, &len);
161219019Sgabor	strlcpy(p, cp, (size_t)(cq - cp + 1));
162219019Sgabor	p += cq - cp + 1;
163219019Sgabor
164219019Sgabor	/* get variable */
165219019Sgabor	*variable = p;
166219019Sgabor	cp = _bcs_skip_ws_len(cq, &len);
167219019Sgabor	strlcpy(p, cp, len + 1);
168219019Sgabor
169219019Sgabor	ret = 0;
170219019Sgabor
171219019Sgaborquit:
172219019Sgabor	_unmap_file(&r);
173219019Sgabor	return (ret);
174219019Sgabor}
175219019Sgabor
176219019Sgabor/*
177219019Sgabor * mapper_close:
178219019Sgabor *	simply close a mapper. (without handling hash)
179219019Sgabor */
180219019Sgaborstatic void
181219019Sgabormapper_close(struct _citrus_mapper *cm)
182219019Sgabor{
183219019Sgabor	if (cm->cm_module) {
184219019Sgabor		if (cm->cm_ops) {
185219019Sgabor			if (cm->cm_closure)
186219019Sgabor				(*cm->cm_ops->mo_uninit)(cm);
187219019Sgabor			free(cm->cm_ops);
188219019Sgabor		}
189219019Sgabor		_citrus_unload_module(cm->cm_module);
190219019Sgabor	}
191219019Sgabor	free(cm->cm_traits);
192219019Sgabor	free(cm);
193219019Sgabor}
194219019Sgabor
195219019Sgabor/*
196219019Sgabor * mapper_open:
197219019Sgabor *	simply open a mapper. (without handling hash)
198219019Sgabor */
199219019Sgaborstatic int
200219019Sgabormapper_open(struct _citrus_mapper_area *__restrict ma,
201219019Sgabor    struct _citrus_mapper * __restrict * __restrict rcm,
202219019Sgabor    const char * __restrict module,
203219019Sgabor    const char * __restrict variable)
204219019Sgabor{
205219019Sgabor	struct _citrus_mapper *cm;
206219019Sgabor	_citrus_mapper_getops_t getops;
207219019Sgabor	int ret;
208219019Sgabor
209219019Sgabor	/* initialize mapper handle */
210219019Sgabor	cm = malloc(sizeof(*cm));
211219019Sgabor	if (!cm)
212219019Sgabor		return (errno);
213219019Sgabor
214219019Sgabor	cm->cm_module = NULL;
215219019Sgabor	cm->cm_ops = NULL;
216219019Sgabor	cm->cm_closure = NULL;
217219019Sgabor	cm->cm_traits = NULL;
218219019Sgabor	cm->cm_refcount = 0;
219219019Sgabor	cm->cm_key = NULL;
220219019Sgabor
221219019Sgabor	/* load module */
222219019Sgabor	ret = _citrus_load_module(&cm->cm_module, module);
223219019Sgabor	if (ret)
224219019Sgabor		goto err;
225219019Sgabor
226219019Sgabor	/* get operators */
227219019Sgabor	getops = (_citrus_mapper_getops_t)
228219019Sgabor	    _citrus_find_getops(cm->cm_module, module, "mapper");
229219019Sgabor	if (!getops) {
230219019Sgabor		ret = EOPNOTSUPP;
231219019Sgabor		goto err;
232219019Sgabor	}
233219019Sgabor	cm->cm_ops = malloc(sizeof(*cm->cm_ops));
234219019Sgabor	if (!cm->cm_ops) {
235219019Sgabor		ret = errno;
236219019Sgabor		goto err;
237219019Sgabor	}
238219019Sgabor	ret = (*getops)(cm->cm_ops);
239219019Sgabor	if (ret)
240219019Sgabor		goto err;
241219019Sgabor
242219019Sgabor	if (!cm->cm_ops->mo_init ||
243219019Sgabor	    !cm->cm_ops->mo_uninit ||
244219019Sgabor	    !cm->cm_ops->mo_convert ||
245219019Sgabor	    !cm->cm_ops->mo_init_state)
246219019Sgabor		goto err;
247219019Sgabor
248219019Sgabor	/* allocate traits structure */
249219019Sgabor	cm->cm_traits = malloc(sizeof(*cm->cm_traits));
250219019Sgabor	if (cm->cm_traits == NULL) {
251219019Sgabor		ret = errno;
252219019Sgabor		goto err;
253219019Sgabor	}
254219019Sgabor	/* initialize the mapper */
255219019Sgabor	ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
256219019Sgabor	    (const void *)variable, strlen(variable) + 1,
257219019Sgabor	    cm->cm_traits, sizeof(*cm->cm_traits));
258219019Sgabor	if (ret)
259219019Sgabor		goto err;
260219019Sgabor
261219019Sgabor	*rcm = cm;
262219019Sgabor
263219019Sgabor	return (0);
264219019Sgabor
265219019Sgaborerr:
266219019Sgabor	mapper_close(cm);
267219019Sgabor	return (ret);
268219019Sgabor}
269219019Sgabor
270219019Sgabor/*
271219019Sgabor * _citrus_mapper_open_direct:
272219019Sgabor *	open a mapper.
273219019Sgabor */
274219019Sgaborint
275219019Sgabor_citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
276219019Sgabor    struct _citrus_mapper * __restrict * __restrict rcm,
277219019Sgabor    const char * __restrict module, const char * __restrict variable)
278219019Sgabor{
279219019Sgabor
280219019Sgabor	return (mapper_open(ma, rcm, module, variable));
281219019Sgabor}
282219019Sgabor
283219019Sgabor/*
284219019Sgabor * hash_func
285219019Sgabor */
286219019Sgaborstatic __inline int
287219019Sgaborhash_func(const char *key)
288219019Sgabor{
289219019Sgabor
290219019Sgabor	return (_string_hash_func(key, CM_HASH_SIZE));
291219019Sgabor}
292219019Sgabor
293219019Sgabor/*
294219019Sgabor * match_func
295219019Sgabor */
296219019Sgaborstatic __inline int
297219019Sgabormatch_func(struct _citrus_mapper *cm, const char *key)
298219019Sgabor{
299219019Sgabor
300219019Sgabor	return (strcmp(cm->cm_key, key));
301219019Sgabor}
302219019Sgabor
303219019Sgabor/*
304219019Sgabor * _citrus_mapper_open:
305219019Sgabor *	open a mapper with looking up "mapper.dir".
306219019Sgabor */
307219019Sgaborint
308219019Sgabor_citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
309219019Sgabor    struct _citrus_mapper * __restrict * __restrict rcm,
310219019Sgabor    const char * __restrict mapname)
311219019Sgabor{
312219019Sgabor	struct _citrus_mapper *cm;
313219019Sgabor	char linebuf[PATH_MAX];
314219019Sgabor	const char *module, *variable;
315219019Sgabor	int hashval, ret;
316219019Sgabor
317219019Sgabor	variable = NULL;
318219019Sgabor
319219019Sgabor	WLOCK;
320219019Sgabor
321219019Sgabor	/* search in the cache */
322219019Sgabor	hashval = hash_func(mapname);
323219019Sgabor	_CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
324219019Sgabor	    hashval);
325219019Sgabor	if (cm) {
326219019Sgabor		/* found */
327219019Sgabor		cm->cm_refcount++;
328219019Sgabor		*rcm = cm;
329219019Sgabor		ret = 0;
330219019Sgabor		goto quit;
331219019Sgabor	}
332219019Sgabor
333219019Sgabor	/* search mapper entry */
334219019Sgabor	ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
335219019Sgabor	    (size_t)PATH_MAX, &module, &variable);
336219019Sgabor	if (ret)
337219019Sgabor		goto quit;
338219019Sgabor
339219019Sgabor	/* open mapper */
340223296Skan	UNLOCK;
341219019Sgabor	ret = mapper_open(ma, &cm, module, variable);
342223296Skan	WLOCK;
343219019Sgabor	if (ret)
344219019Sgabor		goto quit;
345219019Sgabor	cm->cm_key = strdup(mapname);
346219019Sgabor	if (cm->cm_key == NULL) {
347219019Sgabor		ret = errno;
348219019Sgabor		_mapper_close(cm);
349219019Sgabor		goto quit;
350219019Sgabor	}
351219019Sgabor
352219019Sgabor	/* insert to the cache */
353219019Sgabor	cm->cm_refcount = 1;
354219019Sgabor	_CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
355219019Sgabor
356219019Sgabor	*rcm = cm;
357219019Sgabor	ret = 0;
358219019Sgaborquit:
359219019Sgabor	UNLOCK;
360219019Sgabor
361219019Sgabor	return (ret);
362219019Sgabor}
363219019Sgabor
364219019Sgabor/*
365219019Sgabor * _citrus_mapper_close:
366219019Sgabor *	close the specified mapper.
367219019Sgabor */
368219019Sgaborvoid
369219019Sgabor_citrus_mapper_close(struct _citrus_mapper *cm)
370219019Sgabor{
371219019Sgabor
372219019Sgabor	if (cm) {
373219019Sgabor		WLOCK;
374219019Sgabor		if (cm->cm_refcount == REFCOUNT_PERSISTENT)
375219019Sgabor			goto quit;
376219019Sgabor		if (cm->cm_refcount > 0) {
377219019Sgabor			if (--cm->cm_refcount > 0)
378219019Sgabor				goto quit;
379219019Sgabor			_CITRUS_HASH_REMOVE(cm, cm_entry);
380219019Sgabor			free(cm->cm_key);
381219019Sgabor		}
382219019Sgabor		mapper_close(cm);
383219019Sgaborquit:
384219019Sgabor		UNLOCK;
385219019Sgabor	}
386219019Sgabor}
387219019Sgabor
388219019Sgabor/*
389219019Sgabor * _citrus_mapper_set_persistent:
390219019Sgabor *	set persistent count.
391219019Sgabor */
392219019Sgaborvoid
393219019Sgabor_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
394219019Sgabor{
395219019Sgabor
396219019Sgabor	WLOCK;
397219019Sgabor	cm->cm_refcount = REFCOUNT_PERSISTENT;
398219019Sgabor	UNLOCK;
399219019Sgabor}
400