xref: /freebsd/lib/libc/iconv/citrus_mapper.c (revision 2f1217877e155a8a7de97e74eb7e82096a5ef316)
1 /* $FreeBSD$ */
2 /*	$NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $	*/
3 
4 /*-
5  * Copyright (c)2003 Citrus Project,
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/queue.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "citrus_namespace.h"
43 #include "citrus_types.h"
44 #include "citrus_region.h"
45 #include "citrus_lock.h"
46 #include "citrus_memstream.h"
47 #include "citrus_bcs.h"
48 #include "citrus_mmap.h"
49 #include "citrus_module.h"
50 #include "citrus_hash.h"
51 #include "citrus_mapper.h"
52 
53 #define _CITRUS_MAPPER_DIR	"mapper.dir"
54 
55 #define CM_HASH_SIZE 101
56 #define REFCOUNT_PERSISTENT	-1
57 
58 static pthread_rwlock_t		cm_lock = PTHREAD_RWLOCK_INITIALIZER;
59 
60 struct _citrus_mapper_area {
61 	_CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE)	 ma_cache;
62 	char							*ma_dir;
63 };
64 
65 /*
66  * _citrus_mapper_create_area:
67  *	create mapper area
68  */
69 
70 int
71 _citrus_mapper_create_area(
72     struct _citrus_mapper_area *__restrict *__restrict rma,
73     const char *__restrict area)
74 {
75 	struct _citrus_mapper_area *ma;
76 	struct stat st;
77 	char path[PATH_MAX];
78 	int ret;
79 
80 	WLOCK(&cm_lock);
81 
82 	if (*rma != NULL) {
83 		ret = 0;
84 		goto quit;
85 	}
86 
87 	snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
88 
89 	ret = stat(path, &st);
90 	if (ret)
91 		goto quit;
92 
93 	ma = malloc(sizeof(*ma));
94 	if (ma == NULL) {
95 		ret = errno;
96 		goto quit;
97 	}
98 	ma->ma_dir = strdup(area);
99 	if (ma->ma_dir == NULL) {
100 		ret = errno;
101 		free(ma);
102 		goto quit;
103 	}
104 	_CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
105 
106 	*rma = ma;
107 	ret = 0;
108 quit:
109 	UNLOCK(&cm_lock);
110 
111 	return (ret);
112 }
113 
114 
115 /*
116  * lookup_mapper_entry:
117  *	lookup mapper.dir entry in the specified directory.
118  *
119  * line format of iconv.dir file:
120  *	mapper	module	arg
121  * mapper : mapper name.
122  * module : mapper module name.
123  * arg    : argument for the module (generally, description file name)
124  */
125 
126 static int
127 lookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
128     size_t linebufsize, const char **module, const char **variable)
129 {
130 	struct _region r;
131 	struct _memstream ms;
132 	const char *cp, *cq;
133 	char *p;
134 	char path[PATH_MAX];
135 	size_t len;
136 	int ret;
137 
138 	/* create mapper.dir path */
139 	snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
140 
141 	/* open read stream */
142 	ret = _map_file(&r, path);
143 	if (ret)
144 		return (ret);
145 
146 	_memstream_bind(&ms, &r);
147 
148 	/* search the line matching to the map name */
149 	cp = _memstream_matchline(&ms, mapname, &len, 0);
150 	if (!cp) {
151 		ret = ENOENT;
152 		goto quit;
153 	}
154 	if (!len || len > linebufsize - 1) {
155 		ret = EINVAL;
156 		goto quit;
157 	}
158 
159 	p = linebuf;
160 	/* get module name */
161 	*module = p;
162 	cq = _bcs_skip_nonws_len(cp, &len);
163 	strlcpy(p, cp, (size_t)(cq - cp + 1));
164 	p += cq - cp + 1;
165 
166 	/* get variable */
167 	*variable = p;
168 	cp = _bcs_skip_ws_len(cq, &len);
169 	strlcpy(p, cp, len + 1);
170 
171 	ret = 0;
172 
173 quit:
174 	_unmap_file(&r);
175 	return (ret);
176 }
177 
178 /*
179  * mapper_close:
180  *	simply close a mapper. (without handling hash)
181  */
182 static void
183 mapper_close(struct _citrus_mapper *cm)
184 {
185 	if (cm->cm_module) {
186 		if (cm->cm_ops) {
187 			if (cm->cm_closure)
188 				(*cm->cm_ops->mo_uninit)(cm);
189 			free(cm->cm_ops);
190 		}
191 		_citrus_unload_module(cm->cm_module);
192 	}
193 	free(cm->cm_traits);
194 	free(cm);
195 }
196 
197 /*
198  * mapper_open:
199  *	simply open a mapper. (without handling hash)
200  */
201 static int
202 mapper_open(struct _citrus_mapper_area *__restrict ma,
203     struct _citrus_mapper * __restrict * __restrict rcm,
204     const char * __restrict module,
205     const char * __restrict variable)
206 {
207 	struct _citrus_mapper *cm;
208 	_citrus_mapper_getops_t getops;
209 	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 
269 err:
270 	mapper_close(cm);
271 	return (ret);
272 }
273 
274 /*
275  * _citrus_mapper_open_direct:
276  *	open a mapper.
277  */
278 int
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  */
290 static __inline int
291 hash_func(const char *key)
292 {
293 
294 	return (_string_hash_func(key, CM_HASH_SIZE));
295 }
296 
297 /*
298  * match_func
299  */
300 static __inline int
301 match_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  */
311 int
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;
362 quit:
363 	UNLOCK(&cm_lock);
364 
365 	return (ret);
366 }
367 
368 /*
369  * _citrus_mapper_close:
370  *	close the specified mapper.
371  */
372 void
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;
389 quit:
390 		UNLOCK(&cm_lock);
391 	}
392 }
393 
394 /*
395  * _citrus_mapper_set_persistent:
396  *	set persistent count.
397  */
398 void
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