]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libc/iconv/citrus_mapper.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / lib / libc / iconv / citrus_mapper.c
1 /* $FreeBSD$ */
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
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
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
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
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
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
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
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
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
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
393 _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
394 {
395
396         WLOCK;
397         cm->cm_refcount = REFCOUNT_PERSISTENT;
398         UNLOCK;
399 }