]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/iconv/citrus_mapper.c
libc: Fix most issues reported by mandoc
[FreeBSD/FreeBSD.git] / lib / libc / iconv / citrus_mapper.c
1 /* $FreeBSD$ */
2 /*      $NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $        */
3
4 /*-
5  * SPDX-License-Identifier: BSD-2-Clause
6  *
7  * Copyright (c)2003 Citrus Project,
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/queue.h>
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "citrus_namespace.h"
45 #include "citrus_types.h"
46 #include "citrus_region.h"
47 #include "citrus_lock.h"
48 #include "citrus_memstream.h"
49 #include "citrus_bcs.h"
50 #include "citrus_mmap.h"
51 #include "citrus_module.h"
52 #include "citrus_hash.h"
53 #include "citrus_mapper.h"
54
55 #define _CITRUS_MAPPER_DIR      "mapper.dir"
56
57 #define CM_HASH_SIZE 101
58 #define REFCOUNT_PERSISTENT     -1
59
60 static pthread_rwlock_t         cm_lock = PTHREAD_RWLOCK_INITIALIZER;
61
62 struct _citrus_mapper_area {
63         _CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE)        ma_cache;
64         char                                                    *ma_dir;
65 };
66
67 /*
68  * _citrus_mapper_create_area:
69  *      create mapper area
70  */
71
72 int
73 _citrus_mapper_create_area(
74     struct _citrus_mapper_area *__restrict *__restrict rma,
75     const char *__restrict area)
76 {
77         struct _citrus_mapper_area *ma;
78         struct stat st;
79         char path[PATH_MAX];
80         int ret;
81
82         WLOCK(&cm_lock);
83
84         if (*rma != NULL) {
85                 ret = 0;
86                 goto quit;
87         }
88
89         snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
90
91         ret = stat(path, &st);
92         if (ret)
93                 goto quit;
94
95         ma = malloc(sizeof(*ma));
96         if (ma == NULL) {
97                 ret = errno;
98                 goto quit;
99         }
100         ma->ma_dir = strdup(area);
101         if (ma->ma_dir == NULL) {
102                 ret = errno;
103                 free(ma);
104                 goto quit;
105         }
106         _CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
107
108         *rma = ma;
109         ret = 0;
110 quit:
111         UNLOCK(&cm_lock);
112
113         return (ret);
114 }
115
116
117 /*
118  * lookup_mapper_entry:
119  *      lookup mapper.dir entry in the specified directory.
120  *
121  * line format of iconv.dir file:
122  *      mapper  module  arg
123  * mapper : mapper name.
124  * module : mapper module name.
125  * arg    : argument for the module (generally, description file name)
126  */
127
128 static int
129 lookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
130     size_t linebufsize, const char **module, const char **variable)
131 {
132         struct _region r;
133         struct _memstream ms;
134         const char *cp, *cq;
135         char *p;
136         char path[PATH_MAX];
137         size_t len;
138         int ret;
139
140         /* create mapper.dir path */
141         snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
142
143         /* open read stream */
144         ret = _map_file(&r, path);
145         if (ret)
146                 return (ret);
147
148         _memstream_bind(&ms, &r);
149
150         /* search the line matching to the map name */
151         cp = _memstream_matchline(&ms, mapname, &len, 0);
152         if (!cp) {
153                 ret = ENOENT;
154                 goto quit;
155         }
156         if (!len || len > linebufsize - 1) {
157                 ret = EINVAL;
158                 goto quit;
159         }
160
161         p = linebuf;
162         /* get module name */
163         *module = p;
164         cq = _bcs_skip_nonws_len(cp, &len);
165         strlcpy(p, cp, (size_t)(cq - cp + 1));
166         p += cq - cp + 1;
167
168         /* get variable */
169         *variable = p;
170         cp = _bcs_skip_ws_len(cq, &len);
171         strlcpy(p, cp, len + 1);
172
173         ret = 0;
174
175 quit:
176         _unmap_file(&r);
177         return (ret);
178 }
179
180 /*
181  * mapper_close:
182  *      simply close a mapper. (without handling hash)
183  */
184 static void
185 mapper_close(struct _citrus_mapper *cm)
186 {
187         if (cm->cm_module) {
188                 if (cm->cm_ops) {
189                         if (cm->cm_closure)
190                                 (*cm->cm_ops->mo_uninit)(cm);
191                         free(cm->cm_ops);
192                 }
193                 _citrus_unload_module(cm->cm_module);
194         }
195         free(cm->cm_traits);
196         free(cm);
197 }
198
199 /*
200  * mapper_open:
201  *      simply open a mapper. (without handling hash)
202  */
203 static int
204 mapper_open(struct _citrus_mapper_area *__restrict ma,
205     struct _citrus_mapper * __restrict * __restrict rcm,
206     const char * __restrict module,
207     const char * __restrict variable)
208 {
209         struct _citrus_mapper *cm;
210         _citrus_mapper_getops_t getops;
211         int ret;
212
213         /* initialize mapper handle */
214         cm = malloc(sizeof(*cm));
215         if (!cm)
216                 return (errno);
217
218         cm->cm_module = NULL;
219         cm->cm_ops = NULL;
220         cm->cm_closure = NULL;
221         cm->cm_traits = NULL;
222         cm->cm_refcount = 0;
223         cm->cm_key = NULL;
224
225         /* load module */
226         ret = _citrus_load_module(&cm->cm_module, module);
227         if (ret)
228                 goto err;
229
230         /* get operators */
231         getops = (_citrus_mapper_getops_t)
232             _citrus_find_getops(cm->cm_module, module, "mapper");
233         if (!getops) {
234                 ret = EOPNOTSUPP;
235                 goto err;
236         }
237         cm->cm_ops = malloc(sizeof(*cm->cm_ops));
238         if (!cm->cm_ops) {
239                 ret = errno;
240                 goto err;
241         }
242         ret = (*getops)(cm->cm_ops);
243         if (ret)
244                 goto err;
245
246         if (!cm->cm_ops->mo_init ||
247             !cm->cm_ops->mo_uninit ||
248             !cm->cm_ops->mo_convert ||
249             !cm->cm_ops->mo_init_state) {
250                 ret = EINVAL;
251                 goto err;
252         }
253
254         /* allocate traits structure */
255         cm->cm_traits = malloc(sizeof(*cm->cm_traits));
256         if (cm->cm_traits == NULL) {
257                 ret = errno;
258                 goto err;
259         }
260         /* initialize the mapper */
261         ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
262             (const void *)variable, strlen(variable) + 1,
263             cm->cm_traits, sizeof(*cm->cm_traits));
264         if (ret)
265                 goto err;
266
267         *rcm = cm;
268
269         return (0);
270
271 err:
272         mapper_close(cm);
273         return (ret);
274 }
275
276 /*
277  * _citrus_mapper_open_direct:
278  *      open a mapper.
279  */
280 int
281 _citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
282     struct _citrus_mapper * __restrict * __restrict rcm,
283     const char * __restrict module, const char * __restrict variable)
284 {
285
286         return (mapper_open(ma, rcm, module, variable));
287 }
288
289 /*
290  * hash_func
291  */
292 static __inline int
293 hash_func(const char *key)
294 {
295
296         return (_string_hash_func(key, CM_HASH_SIZE));
297 }
298
299 /*
300  * match_func
301  */
302 static __inline int
303 match_func(struct _citrus_mapper *cm, const char *key)
304 {
305
306         return (strcmp(cm->cm_key, key));
307 }
308
309 /*
310  * _citrus_mapper_open:
311  *      open a mapper with looking up "mapper.dir".
312  */
313 int
314 _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
315     struct _citrus_mapper * __restrict * __restrict rcm,
316     const char * __restrict mapname)
317 {
318         struct _citrus_mapper *cm;
319         char linebuf[PATH_MAX];
320         const char *module, *variable;
321         int hashval, ret;
322
323         variable = NULL;
324
325         WLOCK(&cm_lock);
326
327         /* search in the cache */
328         hashval = hash_func(mapname);
329         _CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
330             hashval);
331         if (cm) {
332                 /* found */
333                 cm->cm_refcount++;
334                 *rcm = cm;
335                 ret = 0;
336                 goto quit;
337         }
338
339         /* search mapper entry */
340         ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
341             (size_t)PATH_MAX, &module, &variable);
342         if (ret)
343                 goto quit;
344
345         /* open mapper */
346         UNLOCK(&cm_lock);
347         ret = mapper_open(ma, &cm, module, variable);
348         WLOCK(&cm_lock);
349         if (ret)
350                 goto quit;
351         cm->cm_key = strdup(mapname);
352         if (cm->cm_key == NULL) {
353                 ret = errno;
354                 _mapper_close(cm);
355                 goto quit;      
356         }
357
358         /* insert to the cache */
359         cm->cm_refcount = 1;
360         _CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
361
362         *rcm = cm;
363         ret = 0;
364 quit:
365         UNLOCK(&cm_lock);
366
367         return (ret);
368 }
369
370 /*
371  * _citrus_mapper_close:
372  *      close the specified mapper.
373  */
374 void
375 _citrus_mapper_close(struct _citrus_mapper *cm)
376 {
377
378         if (cm) {
379                 WLOCK(&cm_lock);
380                 if (cm->cm_refcount == REFCOUNT_PERSISTENT)
381                         goto quit;
382                 if (cm->cm_refcount > 0) {
383                         if (--cm->cm_refcount > 0)
384                                 goto quit;
385                         _CITRUS_HASH_REMOVE(cm, cm_entry);
386                         free(cm->cm_key);
387                 }
388                 UNLOCK(&cm_lock);
389                 mapper_close(cm);
390                 return;
391 quit:
392                 UNLOCK(&cm_lock);
393         }
394 }
395
396 /*
397  * _citrus_mapper_set_persistent:
398  *      set persistent count.
399  */
400 void
401 _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
402 {
403
404         WLOCK(&cm_lock);
405         cm->cm_refcount = REFCOUNT_PERSISTENT;
406         UNLOCK(&cm_lock);
407 }