]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - lib/libc/iconv/citrus_mapper.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.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 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                 goto err;
249
250         /* allocate traits structure */
251         cm->cm_traits = malloc(sizeof(*cm->cm_traits));
252         if (cm->cm_traits == NULL) {
253                 ret = errno;
254                 goto err;
255         }
256         /* initialize the mapper */
257         ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
258             (const void *)variable, strlen(variable) + 1,
259             cm->cm_traits, sizeof(*cm->cm_traits));
260         if (ret)
261                 goto err;
262
263         *rcm = cm;
264
265         return (0);
266
267 err:
268         mapper_close(cm);
269         return (ret);
270 }
271
272 /*
273  * _citrus_mapper_open_direct:
274  *      open a mapper.
275  */
276 int
277 _citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
278     struct _citrus_mapper * __restrict * __restrict rcm,
279     const char * __restrict module, const char * __restrict variable)
280 {
281
282         return (mapper_open(ma, rcm, module, variable));
283 }
284
285 /*
286  * hash_func
287  */
288 static __inline int
289 hash_func(const char *key)
290 {
291
292         return (_string_hash_func(key, CM_HASH_SIZE));
293 }
294
295 /*
296  * match_func
297  */
298 static __inline int
299 match_func(struct _citrus_mapper *cm, const char *key)
300 {
301
302         return (strcmp(cm->cm_key, key));
303 }
304
305 /*
306  * _citrus_mapper_open:
307  *      open a mapper with looking up "mapper.dir".
308  */
309 int
310 _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
311     struct _citrus_mapper * __restrict * __restrict rcm,
312     const char * __restrict mapname)
313 {
314         struct _citrus_mapper *cm;
315         char linebuf[PATH_MAX];
316         const char *module, *variable;
317         int hashval, ret;
318
319         variable = NULL;
320
321         WLOCK(&cm_lock);
322
323         /* search in the cache */
324         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;
360 quit:
361         UNLOCK(&cm_lock);
362
363         return (ret);
364 }
365
366 /*
367  * _citrus_mapper_close:
368  *      close the specified mapper.
369  */
370 void
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                 mapper_close(cm);
385 quit:
386                 UNLOCK(&cm_lock);
387         }
388 }
389
390 /*
391  * _citrus_mapper_set_persistent:
392  *      set persistent count.
393  */
394 void
395 _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
396 {
397
398         WLOCK(&cm_lock);
399         cm->cm_refcount = REFCOUNT_PERSISTENT;
400         UNLOCK(&cm_lock);
401 }