citrus_mapper.c revision 257039
1156952Sume/* $FreeBSD: stable/10/lib/libc/iconv/citrus_mapper.c 257039 2013-10-24 05:01:49Z delphij $ */
2156952Sume/* $NetBSD: citrus_mapper.c,v 1.7 2008/07/25 14:05:25 christos Exp $ */
3156952Sume
4156952Sume/*-
5156952Sume * Copyright (c)2003 Citrus Project,
6156952Sume * All rights reserved.
7156952Sume *
8156952Sume * Redistribution and use in source and binary forms, with or without
9156952Sume * modification, are permitted provided that the following conditions
10156952Sume * are met:
11156952Sume * 1. Redistributions of source code must retain the above copyright
12156952Sume *    notice, this list of conditions and the following disclaimer.
13156952Sume * 2. Redistributions in binary form must reproduce the above copyright
14156952Sume *    notice, this list of conditions and the following disclaimer in the
15156952Sume *    documentation and/or other materials provided with the distribution.
16156952Sume *
17156952Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18156952Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19156952Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20156952Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21156956Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22156956Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23156952Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24156952Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25156952Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26156952Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27156952Sume * SUCH DAMAGE.
28156952Sume */
29156952Sume
30156952Sume#include <sys/cdefs.h>
31156952Sume#include <sys/types.h>
32156952Sume#include <sys/stat.h>
33156952Sume#include <sys/queue.h>
34156952Sume
35156952Sume#include <assert.h>
36156952Sume#include <errno.h>
37156952Sume#include <limits.h>
38156956Sume#include <stdio.h>
39156952Sume#include <stdlib.h>
40156956Sume#include <string.h>
41156952Sume
42156952Sume#include "citrus_namespace.h"
43156952Sume#include "citrus_types.h"
44156952Sume#include "citrus_region.h"
45156952Sume#include "citrus_lock.h"
46156952Sume#include "citrus_memstream.h"
47156952Sume#include "citrus_bcs.h"
48156952Sume#include "citrus_mmap.h"
49156952Sume#include "citrus_module.h"
50156952Sume#include "citrus_hash.h"
51156952Sume#include "citrus_mapper.h"
52156952Sume
53156952Sume#define _CITRUS_MAPPER_DIR	"mapper.dir"
54156952Sume
55156952Sume#define CM_HASH_SIZE 101
56156952Sume#define REFCOUNT_PERSISTENT	-1
57156952Sume
58156952Sumestatic pthread_rwlock_t		cm_lock = PTHREAD_RWLOCK_INITIALIZER;
59156952Sume
60156952Sumestruct _citrus_mapper_area {
61156952Sume	_CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE)	 ma_cache;
62156952Sume	char							*ma_dir;
63156952Sume};
64156952Sume
65156952Sume/*
66156952Sume * _citrus_mapper_create_area:
67156952Sume *	create mapper area
68156952Sume */
69156952Sume
70156952Sumeint
71156952Sume_citrus_mapper_create_area(
72156952Sume    struct _citrus_mapper_area *__restrict *__restrict rma,
73156952Sume    const char *__restrict area)
74156952Sume{
75156952Sume	struct _citrus_mapper_area *ma;
76156952Sume	struct stat st;
77156952Sume	char path[PATH_MAX];
78156952Sume	int ret;
79156952Sume
80156952Sume	WLOCK(&cm_lock);
81156952Sume
82156952Sume	if (*rma != NULL) {
83156952Sume		ret = 0;
84156952Sume		goto quit;
85156952Sume	}
86156952Sume
87156952Sume	snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
88156952Sume
89156952Sume	ret = stat(path, &st);
90156952Sume	if (ret)
91156952Sume		goto quit;
92156952Sume
93156952Sume	ma = malloc(sizeof(*ma));
94156952Sume	if (ma == NULL) {
95156952Sume		ret = errno;
96156952Sume		goto quit;
97156952Sume	}
98156952Sume	ma->ma_dir = strdup(area);
99156952Sume	if (ma->ma_dir == NULL) {
100156952Sume		ret = errno;
101156952Sume		free(ma);
102156952Sume		goto quit;
103156952Sume	}
104156952Sume	_CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
105156952Sume
106156952Sume	*rma = ma;
107156952Sume	ret = 0;
108156956Sumequit:
109156952Sume	UNLOCK(&cm_lock);
110156952Sume
111156952Sume	return (ret);
112156952Sume}
113156952Sume
114156952Sume
115156952Sume/*
116156952Sume * lookup_mapper_entry:
117156952Sume *	lookup mapper.dir entry in the specified directory.
118156952Sume *
119156952Sume * line format of iconv.dir file:
120156952Sume *	mapper	module	arg
121156952Sume * mapper : mapper name.
122156952Sume * module : mapper module name.
123156952Sume * arg    : argument for the module (generally, description file name)
124156952Sume */
125156952Sume
126156952Sumestatic int
127156952Sumelookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
128156952Sume    size_t linebufsize, const char **module, const char **variable)
129156952Sume{
130156952Sume	struct _region r;
131156952Sume	struct _memstream ms;
132156952Sume	const char *cp, *cq;
133156952Sume	char *p;
134156952Sume	char path[PATH_MAX];
135156952Sume	size_t len;
136156952Sume	int ret;
137156952Sume
138156952Sume	/* create mapper.dir path */
139156952Sume	snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
140156952Sume
141156952Sume	/* open read stream */
142156952Sume	ret = _map_file(&r, path);
143156952Sume	if (ret)
144156952Sume		return (ret);
145156952Sume
146156952Sume	_memstream_bind(&ms, &r);
147156952Sume
148156952Sume	/* search the line matching to the map name */
149156952Sume	cp = _memstream_matchline(&ms, mapname, &len, 0);
150156952Sume	if (!cp) {
151156952Sume		ret = ENOENT;
152156952Sume		goto quit;
153156952Sume	}
154156952Sume	if (!len || len > linebufsize - 1) {
155156952Sume		ret = EINVAL;
156156952Sume		goto quit;
157156952Sume	}
158156952Sume
159156956Sume	p = linebuf;
160156952Sume	/* get module name */
161156952Sume	*module = p;
162156952Sume	cq = _bcs_skip_nonws_len(cp, &len);
163156952Sume	strlcpy(p, cp, (size_t)(cq - cp + 1));
164156952Sume	p += cq - cp + 1;
165156952Sume
166156952Sume	/* get variable */
167156952Sume	*variable = p;
168156952Sume	cp = _bcs_skip_ws_len(cq, &len);
169156956Sume	strlcpy(p, cp, len + 1);
170156952Sume
171156952Sume	ret = 0;
172156952Sume
173156952Sumequit:
174156952Sume	_unmap_file(&r);
175156952Sume	return (ret);
176156952Sume}
177156952Sume
178156952Sume/*
179156952Sume * mapper_close:
180156952Sume *	simply close a mapper. (without handling hash)
181156952Sume */
182156952Sumestatic void
183156952Sumemapper_close(struct _citrus_mapper *cm)
184156956Sume{
185156952Sume	if (cm->cm_module) {
186156952Sume		if (cm->cm_ops) {
187156952Sume			if (cm->cm_closure)
188156952Sume				(*cm->cm_ops->mo_uninit)(cm);
189156952Sume			free(cm->cm_ops);
190156952Sume		}
191156952Sume		_citrus_unload_module(cm->cm_module);
192156952Sume	}
193156952Sume	free(cm->cm_traits);
194156956Sume	free(cm);
195156952Sume}
196156952Sume
197156952Sume/*
198156952Sume * mapper_open:
199156952Sume *	simply open a mapper. (without handling hash)
200156952Sume */
201156952Sumestatic int
202156952Sumemapper_open(struct _citrus_mapper_area *__restrict ma,
203156952Sume    struct _citrus_mapper * __restrict * __restrict rcm,
204156952Sume    const char * __restrict module,
205156952Sume    const char * __restrict variable)
206156952Sume{
207156952Sume	struct _citrus_mapper *cm;
208156952Sume	_citrus_mapper_getops_t getops;
209156952Sume	int ret;
210156952Sume
211156956Sume	/* initialize mapper handle */
212156952Sume	cm = malloc(sizeof(*cm));
213156952Sume	if (!cm)
214156952Sume		return (errno);
215156952Sume
216156952Sume	cm->cm_module = NULL;
217156952Sume	cm->cm_ops = NULL;
218156952Sume	cm->cm_closure = NULL;
219156952Sume	cm->cm_traits = NULL;
220156952Sume	cm->cm_refcount = 0;
221156952Sume	cm->cm_key = NULL;
222156952Sume
223156956Sume	/* load module */
224156952Sume	ret = _citrus_load_module(&cm->cm_module, module);
225156952Sume	if (ret)
226156952Sume		goto err;
227156952Sume
228156952Sume	/* get operators */
229156952Sume	getops = (_citrus_mapper_getops_t)
230156956Sume	    _citrus_find_getops(cm->cm_module, module, "mapper");
231156952Sume	if (!getops) {
232156952Sume		ret = EOPNOTSUPP;
233156952Sume		goto err;
234156952Sume	}
235156952Sume	cm->cm_ops = malloc(sizeof(*cm->cm_ops));
236156952Sume	if (!cm->cm_ops) {
237156952Sume		ret = errno;
238156952Sume		goto err;
239156952Sume	}
240156956Sume	ret = (*getops)(cm->cm_ops);
241156952Sume	if (ret)
242156952Sume		goto err;
243156952Sume
244156952Sume	if (!cm->cm_ops->mo_init ||
245156952Sume	    !cm->cm_ops->mo_uninit ||
246156952Sume	    !cm->cm_ops->mo_convert ||
247156952Sume	    !cm->cm_ops->mo_init_state)
248156952Sume		goto err;
249156952Sume
250156952Sume	/* allocate traits structure */
251156952Sume	cm->cm_traits = malloc(sizeof(*cm->cm_traits));
252156952Sume	if (cm->cm_traits == NULL) {
253156952Sume		ret = errno;
254156952Sume		goto err;
255156952Sume	}
256156952Sume	/* initialize the mapper */
257156952Sume	ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
258156952Sume	    (const void *)variable, strlen(variable) + 1,
259156952Sume	    cm->cm_traits, sizeof(*cm->cm_traits));
260156952Sume	if (ret)
261156952Sume		goto err;
262156952Sume
263156952Sume	*rcm = cm;
264156952Sume
265156952Sume	return (0);
266156952Sume
267156952Sumeerr:
268156952Sume	mapper_close(cm);
269156952Sume	return (ret);
270156952Sume}
271156952Sume
272156952Sume/*
273156956Sume * _citrus_mapper_open_direct:
274156956Sume *	open a mapper.
275156956Sume */
276156956Sumeint
277156956Sume_citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
278156956Sume    struct _citrus_mapper * __restrict * __restrict rcm,
279156952Sume    const char * __restrict module, const char * __restrict variable)
280156952Sume{
281156952Sume
282156952Sume	return (mapper_open(ma, rcm, module, variable));
283156952Sume}
284156952Sume
285156952Sume/*
286156952Sume * hash_func
287156952Sume */
288156952Sumestatic __inline int
289156952Sumehash_func(const char *key)
290156952Sume{
291156952Sume
292156952Sume	return (_string_hash_func(key, CM_HASH_SIZE));
293156952Sume}
294156952Sume
295156952Sume/*
296156952Sume * match_func
297156952Sume */
298156952Sumestatic __inline int
299156952Sumematch_func(struct _citrus_mapper *cm, const char *key)
300156952Sume{
301156952Sume
302156952Sume	return (strcmp(cm->cm_key, key));
303156956Sume}
304156956Sume
305156956Sume/*
306156956Sume * _citrus_mapper_open:
307156956Sume *	open a mapper with looking up "mapper.dir".
308156956Sume */
309156956Sumeint
310156956Sume_citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
311156956Sume    struct _citrus_mapper * __restrict * __restrict rcm,
312156956Sume    const char * __restrict mapname)
313156956Sume{
314156956Sume	struct _citrus_mapper *cm;
315156956Sume	char linebuf[PATH_MAX];
316156956Sume	const char *module, *variable;
317156956Sume	int hashval, ret;
318156956Sume
319156956Sume	variable = NULL;
320156956Sume
321156956Sume	WLOCK(&cm_lock);
322156956Sume
323156956Sume	/* search in the cache */
324156952Sume	hashval = hash_func(mapname);
325	_CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
326	    hashval);
327	if (cm) {
328		/* found */
329		cm->cm_refcount++;
330		*rcm = cm;
331		ret = 0;
332		goto quit;
333	}
334
335	/* search mapper entry */
336	ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
337	    (size_t)PATH_MAX, &module, &variable);
338	if (ret)
339		goto quit;
340
341	/* open mapper */
342	UNLOCK(&cm_lock);
343	ret = mapper_open(ma, &cm, module, variable);
344	WLOCK(&cm_lock);
345	if (ret)
346		goto quit;
347	cm->cm_key = strdup(mapname);
348	if (cm->cm_key == NULL) {
349		ret = errno;
350		_mapper_close(cm);
351		goto quit;
352	}
353
354	/* insert to the cache */
355	cm->cm_refcount = 1;
356	_CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
357
358	*rcm = cm;
359	ret = 0;
360quit:
361	UNLOCK(&cm_lock);
362
363	return (ret);
364}
365
366/*
367 * _citrus_mapper_close:
368 *	close the specified mapper.
369 */
370void
371_citrus_mapper_close(struct _citrus_mapper *cm)
372{
373
374	if (cm) {
375		WLOCK(&cm_lock);
376		if (cm->cm_refcount == REFCOUNT_PERSISTENT)
377			goto quit;
378		if (cm->cm_refcount > 0) {
379			if (--cm->cm_refcount > 0)
380				goto quit;
381			_CITRUS_HASH_REMOVE(cm, cm_entry);
382			free(cm->cm_key);
383		}
384		UNLOCK(&cm_lock);
385		mapper_close(cm);
386		return;
387quit:
388		UNLOCK(&cm_lock);
389	}
390}
391
392/*
393 * _citrus_mapper_set_persistent:
394 *	set persistent count.
395 */
396void
397_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
398{
399
400	WLOCK(&cm_lock);
401	cm->cm_refcount = REFCOUNT_PERSISTENT;
402	UNLOCK(&cm_lock);
403}
404