citrus_mapper.c revision 252464
1284677Sdim/* $FreeBSD: head/lib/libc/iconv/citrus_mapper.c 252464 2013-07-01 08:38:31Z peter $ */
2284677Sdim/* $NetBSD: citrus_mapper.c,v 1.7 2008/07/25 14:05:25 christos Exp $ */
3284677Sdim
4284677Sdim/*-
5284677Sdim * Copyright (c)2003 Citrus Project,
6284677Sdim * All rights reserved.
7284677Sdim *
8284677Sdim * Redistribution and use in source and binary forms, with or without
9284677Sdim * modification, are permitted provided that the following conditions
10284677Sdim * are met:
11341825Sdim * 1. Redistributions of source code must retain the above copyright
12284677Sdim *    notice, this list of conditions and the following disclaimer.
13284677Sdim * 2. Redistributions in binary form must reproduce the above copyright
14284677Sdim *    notice, this list of conditions and the following disclaimer in the
15284677Sdim *    documentation and/or other materials provided with the distribution.
16284677Sdim *
17284677Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18284677Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19314564Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20284677Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21284677Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22341825Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23314564Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24314564Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25314564Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26314564Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27314564Sdim * SUCH DAMAGE.
28314564Sdim */
29284677Sdim
30314564Sdim#include <sys/cdefs.h>
31314564Sdim#include <sys/types.h>
32314564Sdim#include <sys/stat.h>
33314564Sdim#include <sys/queue.h>
34314564Sdim
35284677Sdim#include <assert.h>
36314564Sdim#include <errno.h>
37284677Sdim#include <limits.h>
38284677Sdim#include <stdio.h>
39314564Sdim#include <stdlib.h>
40314564Sdim#include <string.h>
41314564Sdim
42314564Sdim#include "citrus_namespace.h"
43314564Sdim#include "citrus_types.h"
44341825Sdim#include "citrus_region.h"
45314564Sdim#include "citrus_lock.h"
46314564Sdim#include "citrus_memstream.h"
47314564Sdim#include "citrus_bcs.h"
48314564Sdim#include "citrus_mmap.h"
49314564Sdim#include "citrus_module.h"
50284677Sdim#include "citrus_hash.h"
51284677Sdim#include "citrus_mapper.h"
52284677Sdim
53341825Sdim#define _CITRUS_MAPPER_DIR	"mapper.dir"
54341825Sdim
55309124Sdim#define CM_HASH_SIZE 101
56309124Sdim#define REFCOUNT_PERSISTENT	-1
57341825Sdim
58341825Sdimstruct _citrus_mapper_area {
59341825Sdim	_CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE)	 ma_cache;
60341825Sdim	char							*ma_dir;
61341825Sdim};
62341825Sdim
63341825Sdim/*
64284677Sdim * _citrus_mapper_create_area:
65341825Sdim *	create mapper area
66284677Sdim */
67309124Sdim
68309124Sdimint
69309124Sdim_citrus_mapper_create_area(
70309124Sdim    struct _citrus_mapper_area *__restrict *__restrict rma,
71309124Sdim    const char *__restrict area)
72309124Sdim{
73309124Sdim	struct _citrus_mapper_area *ma;
74309124Sdim	struct stat st;
75309124Sdim	char path[PATH_MAX];
76309124Sdim	int ret;
77309124Sdim
78309124Sdim	WLOCK;
79309124Sdim
80309124Sdim	if (*rma != NULL) {
81309124Sdim		ret = 0;
82309124Sdim		goto quit;
83309124Sdim	}
84309124Sdim
85309124Sdim	snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
86309124Sdim
87309124Sdim	ret = stat(path, &st);
88309124Sdim	if (ret)
89309124Sdim		goto quit;
90309124Sdim
91309124Sdim	ma = malloc(sizeof(*ma));
92309124Sdim	if (ma == NULL) {
93309124Sdim		ret = errno;
94309124Sdim		goto quit;
95309124Sdim	}
96309124Sdim	ma->ma_dir = strdup(area);
97309124Sdim	if (ma->ma_dir == NULL) {
98309124Sdim		ret = errno;
99309124Sdim		free(ma);
100309124Sdim		goto quit;
101309124Sdim	}
102309124Sdim	_CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
103309124Sdim
104314564Sdim	*rma = ma;
105314564Sdim	ret = 0;
106314564Sdimquit:
107314564Sdim	UNLOCK;
108314564Sdim
109314564Sdim	return (ret);
110314564Sdim}
111314564Sdim
112314564Sdim
113314564Sdim/*
114314564Sdim * lookup_mapper_entry:
115314564Sdim *	lookup mapper.dir entry in the specified directory.
116309124Sdim *
117309124Sdim * line format of iconv.dir file:
118309124Sdim *	mapper	module	arg
119309124Sdim * mapper : mapper name.
120309124Sdim * module : mapper module name.
121284677Sdim * arg    : argument for the module (generally, description file name)
122284677Sdim */
123284677Sdim
124284677Sdimstatic int
125284677Sdimlookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
126284677Sdim    size_t linebufsize, const char **module, const char **variable)
127284677Sdim{
128284677Sdim	struct _region r;
129284677Sdim	struct _memstream ms;
130284677Sdim	const char *cp, *cq;
131284677Sdim	char *p;
132284677Sdim	char path[PATH_MAX];
133284677Sdim	size_t len;
134284677Sdim	int ret;
135284677Sdim
136284677Sdim	/* create mapper.dir path */
137284677Sdim	snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
138284677Sdim
139284677Sdim	/* open read stream */
140284677Sdim	ret = _map_file(&r, path);
141284677Sdim	if (ret)
142284677Sdim		return (ret);
143284677Sdim
144284677Sdim	_memstream_bind(&ms, &r);
145284677Sdim
146284677Sdim	/* search the line matching to the map name */
147284677Sdim	cp = _memstream_matchline(&ms, mapname, &len, 0);
148284677Sdim	if (!cp) {
149284677Sdim		ret = ENOENT;
150284677Sdim		goto quit;
151284677Sdim	}
152341825Sdim	if (!len || len > linebufsize - 1) {
153341825Sdim		ret = EINVAL;
154341825Sdim		goto quit;
155341825Sdim	}
156341825Sdim
157284677Sdim	p = linebuf;
158284677Sdim	/* get module name */
159284677Sdim	*module = p;
160284677Sdim	cq = _bcs_skip_nonws_len(cp, &len);
161284677Sdim	strlcpy(p, cp, (size_t)(cq - cp + 1));
162284677Sdim	p += cq - cp + 1;
163309124Sdim
164284677Sdim	/* get variable */
165284677Sdim	*variable = p;
166284677Sdim	cp = _bcs_skip_ws_len(cq, &len);
167284677Sdim	strlcpy(p, cp, len + 1);
168284677Sdim
169284677Sdim	ret = 0;
170284677Sdim
171284677Sdimquit:
172284677Sdim	_unmap_file(&r);
173284677Sdim	return (ret);
174284677Sdim}
175284677Sdim
176284677Sdim/*
177284677Sdim * mapper_close:
178284677Sdim *	simply close a mapper. (without handling hash)
179284677Sdim */
180284677Sdimstatic void
181284677Sdimmapper_close(struct _citrus_mapper *cm)
182284677Sdim{
183284677Sdim	if (cm->cm_module) {
184284677Sdim		if (cm->cm_ops) {
185284677Sdim			if (cm->cm_closure)
186284677Sdim				(*cm->cm_ops->mo_uninit)(cm);
187284677Sdim			free(cm->cm_ops);
188284677Sdim		}
189284677Sdim		_citrus_unload_module(cm->cm_module);
190284677Sdim	}
191284677Sdim	free(cm->cm_traits);
192284677Sdim	free(cm);
193284677Sdim}
194284677Sdim
195284677Sdim/*
196284677Sdim * mapper_open:
197284677Sdim *	simply open a mapper. (without handling hash)
198284677Sdim */
199284677Sdimstatic int
200284677Sdimmapper_open(struct _citrus_mapper_area *__restrict ma,
201284677Sdim    struct _citrus_mapper * __restrict * __restrict rcm,
202284677Sdim    const char * __restrict module,
203284677Sdim    const char * __restrict variable)
204284677Sdim{
205284677Sdim	struct _citrus_mapper *cm;
206284677Sdim	_citrus_mapper_getops_t getops;
207284677Sdim	int ret;
208284677Sdim
209284677Sdim	/* initialize mapper handle */
210284677Sdim	cm = malloc(sizeof(*cm));
211284677Sdim	if (!cm)
212284677Sdim		return (errno);
213284677Sdim
214284677Sdim	cm->cm_module = NULL;
215284677Sdim	cm->cm_ops = NULL;
216284677Sdim	cm->cm_closure = NULL;
217284677Sdim	cm->cm_traits = NULL;
218284677Sdim	cm->cm_refcount = 0;
219284677Sdim	cm->cm_key = NULL;
220284677Sdim
221327952Sdim	/* load module */
222327952Sdim	ret = _citrus_load_module(&cm->cm_module, module);
223327952Sdim	if (ret)
224327952Sdim		goto err;
225327952Sdim
226341825Sdim	/* get operators */
227341825Sdim	getops = (_citrus_mapper_getops_t)
228341825Sdim	    _citrus_find_getops(cm->cm_module, module, "mapper");
229341825Sdim	if (!getops) {
230341825Sdim		ret = EOPNOTSUPP;
231341825Sdim		goto err;
232341825Sdim	}
233341825Sdim	cm->cm_ops = malloc(sizeof(*cm->cm_ops));
234341825Sdim	if (!cm->cm_ops) {
235341825Sdim		ret = errno;
236341825Sdim		goto err;
237341825Sdim	}
238341825Sdim	ret = (*getops)(cm->cm_ops);
239341825Sdim	if (ret)
240341825Sdim		goto err;
241341825Sdim
242341825Sdim	if (!cm->cm_ops->mo_init ||
243341825Sdim	    !cm->cm_ops->mo_uninit ||
244341825Sdim	    !cm->cm_ops->mo_convert ||
245341825Sdim	    !cm->cm_ops->mo_init_state)
246341825Sdim		goto err;
247341825Sdim
248341825Sdim	/* allocate traits structure */
249341825Sdim	cm->cm_traits = malloc(sizeof(*cm->cm_traits));
250341825Sdim	if (cm->cm_traits == NULL) {
251341825Sdim		ret = errno;
252341825Sdim		goto err;
253341825Sdim	}
254284677Sdim	/* initialize the mapper */
255284677Sdim	ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
256284677Sdim	    (const void *)variable, strlen(variable) + 1,
257284677Sdim	    cm->cm_traits, sizeof(*cm->cm_traits));
258284677Sdim	if (ret)
259284677Sdim		goto err;
260284677Sdim
261284677Sdim	*rcm = cm;
262284677Sdim
263284677Sdim	return (0);
264321369Sdim
265321369Sdimerr:
266321369Sdim	mapper_close(cm);
267321369Sdim	return (ret);
268321369Sdim}
269321369Sdim
270321369Sdim/*
271321369Sdim * _citrus_mapper_open_direct:
272321369Sdim *	open a mapper.
273284677Sdim */
274309124Sdimint
275309124Sdim_citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
276309124Sdim    struct _citrus_mapper * __restrict * __restrict rcm,
277309124Sdim    const char * __restrict module, const char * __restrict variable)
278309124Sdim{
279309124Sdim
280314564Sdim	return (mapper_open(ma, rcm, module, variable));
281284677Sdim}
282284677Sdim
283296417Sdim/*
284314564Sdim * hash_func
285314564Sdim */
286341825Sdimstatic __inline int
287296417Sdimhash_func(const char *key)
288296417Sdim{
289309124Sdim
290309124Sdim	return (_string_hash_func(key, CM_HASH_SIZE));
291309124Sdim}
292314564Sdim
293284677Sdim/*
294309124Sdim * match_func
295341825Sdim */
296284677Sdimstatic __inline int
297309124Sdimmatch_func(struct _citrus_mapper *cm, const char *key)
298284677Sdim{
299284677Sdim
300284677Sdim	return (strcmp(cm->cm_key, key));
301309124Sdim}
302341825Sdim
303284677Sdim/*
304284677Sdim * _citrus_mapper_open:
305284677Sdim *	open a mapper with looking up "mapper.dir".
306284677Sdim */
307309124Sdimint
308341825Sdim_citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
309284677Sdim    struct _citrus_mapper * __restrict * __restrict rcm,
310284677Sdim    const char * __restrict mapname)
311284677Sdim{
312341825Sdim	struct _citrus_mapper *cm;
313309124Sdim	char linebuf[PATH_MAX];
314321369Sdim	const char *module, *variable;
315284677Sdim	int hashval, ret;
316284677Sdim
317284677Sdim	variable = NULL;
318284677Sdim
319284677Sdim	WLOCK;
320284677Sdim
321341825Sdim	/* search in the cache */
322309124Sdim	hashval = hash_func(mapname);
323341825Sdim	_CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
324309124Sdim	    hashval);
325309124Sdim	if (cm) {
326284677Sdim		/* found */
327284677Sdim		cm->cm_refcount++;
328284677Sdim		*rcm = cm;
329341825Sdim		ret = 0;
330309124Sdim		goto quit;
331341825Sdim	}
332309124Sdim
333309124Sdim	/* search mapper entry */
334284677Sdim	ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
335284677Sdim	    (size_t)PATH_MAX, &module, &variable);
336284677Sdim	if (ret)
337341825Sdim		goto quit;
338309124Sdim
339284677Sdim	/* open mapper */
340284677Sdim	UNLOCK;
341309124Sdim	ret = mapper_open(ma, &cm, module, variable);
342284677Sdim	WLOCK;
343284677Sdim	if (ret)
344284677Sdim		goto quit;
345341825Sdim	cm->cm_key = strdup(mapname);
346309124Sdim	if (cm->cm_key == NULL) {
347309124Sdim		ret = errno;
348309124Sdim		_mapper_close(cm);
349309124Sdim		goto quit;
350309124Sdim	}
351284677Sdim
352314564Sdim	/* insert to the cache */
353341825Sdim	cm->cm_refcount = 1;
354309124Sdim	_CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
355309124Sdim
356284677Sdim	*rcm = cm;
357314564Sdim	ret = 0;
358341825Sdimquit:
359309124Sdim	UNLOCK;
360309124Sdim
361341825Sdim	return (ret);
362341825Sdim}
363309124Sdim
364309124Sdim/*
365309124Sdim * _citrus_mapper_close:
366309124Sdim *	close the specified mapper.
367314564Sdim */
368341825Sdimvoid
369309124Sdim_citrus_mapper_close(struct _citrus_mapper *cm)
370341825Sdim{
371341825Sdim
372309124Sdim	if (cm) {
373284677Sdim		WLOCK;
374284677Sdim		if (cm->cm_refcount == REFCOUNT_PERSISTENT)
375284677Sdim			goto quit;
376341825Sdim		if (cm->cm_refcount > 0) {
377341825Sdim			if (--cm->cm_refcount > 0)
378341825Sdim				goto quit;
379309124Sdim			_CITRUS_HASH_REMOVE(cm, cm_entry);
380321369Sdim			free(cm->cm_key);
381321369Sdim		}
382309124Sdim		mapper_close(cm);
383284677Sdimquit:
384314564Sdim		UNLOCK;
385341825Sdim	}
386309124Sdim}
387321369Sdim
388321369Sdim/*
389321369Sdim * _citrus_mapper_set_persistent:
390309124Sdim *	set persistent count.
391296417Sdim */
392284677Sdimvoid
393341825Sdim_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
394341825Sdim{
395321369Sdim
396309124Sdim	WLOCK;
397284677Sdim	cm->cm_refcount = REFCOUNT_PERSISTENT;
398341825Sdim	UNLOCK;
399284677Sdim}
400341825Sdim