1 /* $FreeBSD: stable/9/lib/libc/iconv/citrus_mapper.c 223296 2011-06-19 13:35:46Z kan $ */
2 /* $NetBSD: citrus_mapper.c,v 1.7 2008/07/25 14:05:25 christos 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 struct _citrus_mapper_area {
59 _CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE) ma_cache;
60 char *ma_dir;
61 };
62
63 /*
64 * _citrus_mapper_create_area:
65 * create mapper area
66 */
67
68 int
_citrus_mapper_create_area(struct _citrus_mapper_area * __restrict * __restrict rma,const char * __restrict area)69 _citrus_mapper_create_area(
70 struct _citrus_mapper_area *__restrict *__restrict rma,
71 const char *__restrict area)
72 {
73 struct _citrus_mapper_area *ma;
74 struct stat st;
75 char path[PATH_MAX];
76 int ret;
77
78 WLOCK;
79
80 if (*rma != NULL) {
81 ret = 0;
82 goto quit;
83 }
84
85 snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
86
87 ret = stat(path, &st);
88 if (ret)
89 goto quit;
90
91 ma = malloc(sizeof(*ma));
92 if (ma == NULL) {
93 ret = errno;
94 goto quit;
95 }
96 ma->ma_dir = strdup(area);
97 if (ma->ma_dir == NULL) {
98 ret = errno;
99 free(ma->ma_dir);
100 goto quit;
101 }
102 _CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
103
104 *rma = ma;
105 ret = 0;
106 quit:
107 UNLOCK;
108
109 return (ret);
110 }
111
112
113 /*
114 * lookup_mapper_entry:
115 * lookup mapper.dir entry in the specified directory.
116 *
117 * line format of iconv.dir file:
118 * mapper module arg
119 * mapper : mapper name.
120 * module : mapper module name.
121 * arg : argument for the module (generally, description file name)
122 */
123
124 static int
lookup_mapper_entry(const char * dir,const char * mapname,void * linebuf,size_t linebufsize,const char ** module,const char ** variable)125 lookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
126 size_t linebufsize, const char **module, const char **variable)
127 {
128 struct _region r;
129 struct _memstream ms;
130 const char *cp, *cq;
131 char *p;
132 char path[PATH_MAX];
133 size_t len;
134 int ret;
135
136 /* create mapper.dir path */
137 snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
138
139 /* open read stream */
140 ret = _map_file(&r, path);
141 if (ret)
142 return (ret);
143
144 _memstream_bind(&ms, &r);
145
146 /* search the line matching to the map name */
147 cp = _memstream_matchline(&ms, mapname, &len, 0);
148 if (!cp) {
149 ret = ENOENT;
150 goto quit;
151 }
152 if (!len || len > linebufsize - 1) {
153 ret = EINVAL;
154 goto quit;
155 }
156
157 p = linebuf;
158 /* get module name */
159 *module = p;
160 cq = _bcs_skip_nonws_len(cp, &len);
161 strlcpy(p, cp, (size_t)(cq - cp + 1));
162 p += cq - cp + 1;
163
164 /* get variable */
165 *variable = p;
166 cp = _bcs_skip_ws_len(cq, &len);
167 strlcpy(p, cp, len + 1);
168
169 ret = 0;
170
171 quit:
172 _unmap_file(&r);
173 return (ret);
174 }
175
176 /*
177 * mapper_close:
178 * simply close a mapper. (without handling hash)
179 */
180 static void
mapper_close(struct _citrus_mapper * cm)181 mapper_close(struct _citrus_mapper *cm)
182 {
183 if (cm->cm_module) {
184 if (cm->cm_ops) {
185 if (cm->cm_closure)
186 (*cm->cm_ops->mo_uninit)(cm);
187 free(cm->cm_ops);
188 }
189 _citrus_unload_module(cm->cm_module);
190 }
191 free(cm->cm_traits);
192 free(cm);
193 }
194
195 /*
196 * mapper_open:
197 * simply open a mapper. (without handling hash)
198 */
199 static int
mapper_open(struct _citrus_mapper_area * __restrict ma,struct _citrus_mapper * __restrict * __restrict rcm,const char * __restrict module,const char * __restrict variable)200 mapper_open(struct _citrus_mapper_area *__restrict ma,
201 struct _citrus_mapper * __restrict * __restrict rcm,
202 const char * __restrict module,
203 const char * __restrict variable)
204 {
205 struct _citrus_mapper *cm;
206 _citrus_mapper_getops_t getops;
207 int ret;
208
209 /* initialize mapper handle */
210 cm = malloc(sizeof(*cm));
211 if (!cm)
212 return (errno);
213
214 cm->cm_module = NULL;
215 cm->cm_ops = NULL;
216 cm->cm_closure = NULL;
217 cm->cm_traits = NULL;
218 cm->cm_refcount = 0;
219 cm->cm_key = NULL;
220
221 /* load module */
222 ret = _citrus_load_module(&cm->cm_module, module);
223 if (ret)
224 goto err;
225
226 /* get operators */
227 getops = (_citrus_mapper_getops_t)
228 _citrus_find_getops(cm->cm_module, module, "mapper");
229 if (!getops) {
230 ret = EOPNOTSUPP;
231 goto err;
232 }
233 cm->cm_ops = malloc(sizeof(*cm->cm_ops));
234 if (!cm->cm_ops) {
235 ret = errno;
236 goto err;
237 }
238 ret = (*getops)(cm->cm_ops);
239 if (ret)
240 goto err;
241
242 if (!cm->cm_ops->mo_init ||
243 !cm->cm_ops->mo_uninit ||
244 !cm->cm_ops->mo_convert ||
245 !cm->cm_ops->mo_init_state)
246 goto err;
247
248 /* allocate traits structure */
249 cm->cm_traits = malloc(sizeof(*cm->cm_traits));
250 if (cm->cm_traits == NULL) {
251 ret = errno;
252 goto err;
253 }
254 /* initialize the mapper */
255 ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
256 (const void *)variable, strlen(variable) + 1,
257 cm->cm_traits, sizeof(*cm->cm_traits));
258 if (ret)
259 goto err;
260
261 *rcm = cm;
262
263 return (0);
264
265 err:
266 mapper_close(cm);
267 return (ret);
268 }
269
270 /*
271 * _citrus_mapper_open_direct:
272 * open a mapper.
273 */
274 int
_citrus_mapper_open_direct(struct _citrus_mapper_area * __restrict ma,struct _citrus_mapper * __restrict * __restrict rcm,const char * __restrict module,const char * __restrict variable)275 _citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
276 struct _citrus_mapper * __restrict * __restrict rcm,
277 const char * __restrict module, const char * __restrict variable)
278 {
279
280 return (mapper_open(ma, rcm, module, variable));
281 }
282
283 /*
284 * hash_func
285 */
286 static __inline int
hash_func(const char * key)287 hash_func(const char *key)
288 {
289
290 return (_string_hash_func(key, CM_HASH_SIZE));
291 }
292
293 /*
294 * match_func
295 */
296 static __inline int
match_func(struct _citrus_mapper * cm,const char * key)297 match_func(struct _citrus_mapper *cm, const char *key)
298 {
299
300 return (strcmp(cm->cm_key, key));
301 }
302
303 /*
304 * _citrus_mapper_open:
305 * open a mapper with looking up "mapper.dir".
306 */
307 int
_citrus_mapper_open(struct _citrus_mapper_area * __restrict ma,struct _citrus_mapper * __restrict * __restrict rcm,const char * __restrict mapname)308 _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
309 struct _citrus_mapper * __restrict * __restrict rcm,
310 const char * __restrict mapname)
311 {
312 struct _citrus_mapper *cm;
313 char linebuf[PATH_MAX];
314 const char *module, *variable;
315 int hashval, ret;
316
317 variable = NULL;
318
319 WLOCK;
320
321 /* search in the cache */
322 hashval = hash_func(mapname);
323 _CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
324 hashval);
325 if (cm) {
326 /* found */
327 cm->cm_refcount++;
328 *rcm = cm;
329 ret = 0;
330 goto quit;
331 }
332
333 /* search mapper entry */
334 ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
335 (size_t)PATH_MAX, &module, &variable);
336 if (ret)
337 goto quit;
338
339 /* open mapper */
340 UNLOCK;
341 ret = mapper_open(ma, &cm, module, variable);
342 WLOCK;
343 if (ret)
344 goto quit;
345 cm->cm_key = strdup(mapname);
346 if (cm->cm_key == NULL) {
347 ret = errno;
348 _mapper_close(cm);
349 goto quit;
350 }
351
352 /* insert to the cache */
353 cm->cm_refcount = 1;
354 _CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
355
356 *rcm = cm;
357 ret = 0;
358 quit:
359 UNLOCK;
360
361 return (ret);
362 }
363
364 /*
365 * _citrus_mapper_close:
366 * close the specified mapper.
367 */
368 void
_citrus_mapper_close(struct _citrus_mapper * cm)369 _citrus_mapper_close(struct _citrus_mapper *cm)
370 {
371
372 if (cm) {
373 WLOCK;
374 if (cm->cm_refcount == REFCOUNT_PERSISTENT)
375 goto quit;
376 if (cm->cm_refcount > 0) {
377 if (--cm->cm_refcount > 0)
378 goto quit;
379 _CITRUS_HASH_REMOVE(cm, cm_entry);
380 free(cm->cm_key);
381 }
382 mapper_close(cm);
383 quit:
384 UNLOCK;
385 }
386 }
387
388 /*
389 * _citrus_mapper_set_persistent:
390 * set persistent count.
391 */
392 void
_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)393 _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
394 {
395
396 WLOCK;
397 cm->cm_refcount = REFCOUNT_PERSISTENT;
398 UNLOCK;
399 }
400