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