]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/iconv/citrus_iconv.c
Update subversion-1.8.0 -> 1.8.1. Update supporting
[FreeBSD/FreeBSD.git] / lib / libc / iconv / citrus_iconv.c
1 /* $FreeBSD$ */
2 /* $NetBSD: citrus_iconv.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/queue.h>
33
34 #include <assert.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #include <iconv.h>
38 #include <langinfo.h>
39 #include <limits.h>
40 #include <paths.h>
41 #include <stdbool.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "citrus_namespace.h"
48 #include "citrus_bcs.h"
49 #include "citrus_esdb.h"
50 #include "citrus_region.h"
51 #include "citrus_memstream.h"
52 #include "citrus_mmap.h"
53 #include "citrus_module.h"
54 #include "citrus_lock.h"
55 #include "citrus_lookup.h"
56 #include "citrus_hash.h"
57 #include "citrus_iconv.h"
58
59 #define _CITRUS_ICONV_DIR       "iconv.dir"
60 #define _CITRUS_ICONV_ALIAS     "iconv.alias"
61
62 #define CI_HASH_SIZE 101
63 #define CI_INITIAL_MAX_REUSE    5
64 #define CI_ENV_MAX_REUSE        "ICONV_MAX_REUSE"
65
66 static bool                      isinit = false;
67 static int                       shared_max_reuse, shared_num_unused;
68 static _CITRUS_HASH_HEAD(, _citrus_iconv_shared, CI_HASH_SIZE) shared_pool;
69 static TAILQ_HEAD(, _citrus_iconv_shared) shared_unused;
70
71 static pthread_rwlock_t          ci_lock = PTHREAD_RWLOCK_INITIALIZER;
72
73 static __inline void
74 init_cache(void)
75 {
76
77         WLOCK(&ci_lock);
78         if (!isinit) {
79                 _CITRUS_HASH_INIT(&shared_pool, CI_HASH_SIZE);
80                 TAILQ_INIT(&shared_unused);
81                 shared_max_reuse = -1;
82                 if (!issetugid() && getenv(CI_ENV_MAX_REUSE))
83                         shared_max_reuse = atoi(getenv(CI_ENV_MAX_REUSE));
84                 if (shared_max_reuse < 0)
85                         shared_max_reuse = CI_INITIAL_MAX_REUSE;
86                 isinit = true;
87         }
88         UNLOCK(&ci_lock);
89 }
90
91 static __inline void
92 close_shared(struct _citrus_iconv_shared *ci)
93 {
94
95         if (ci) {
96                 if (ci->ci_module) {
97                         if (ci->ci_ops) {
98                                 if (ci->ci_closure)
99                                         (*ci->ci_ops->io_uninit_shared)(ci);
100                                 free(ci->ci_ops);
101                         }
102                         _citrus_unload_module(ci->ci_module);
103                 }
104                 free(ci);
105         }
106 }
107
108 static __inline int
109 open_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
110     const char * __restrict convname, const char * __restrict src,
111     const char * __restrict dst)
112 {
113         struct _citrus_iconv_shared *ci;
114         _citrus_iconv_getops_t getops;
115         const char *module;
116         size_t len_convname;
117         int ret;
118
119         module = (strcmp(src, dst) != 0) ? "iconv_std" : "iconv_none";
120
121         /* initialize iconv handle */
122         len_convname = strlen(convname);
123         ci = malloc(sizeof(*ci) + len_convname + 1);
124         if (!ci) {
125                 ret = errno;
126                 goto err;
127         }
128         ci->ci_module = NULL;
129         ci->ci_ops = NULL;
130         ci->ci_closure = NULL;
131         ci->ci_convname = (void *)&ci[1];
132         memcpy(ci->ci_convname, convname, len_convname + 1);
133
134         /* load module */
135         ret = _citrus_load_module(&ci->ci_module, module);
136         if (ret)
137                 goto err;
138
139         /* get operators */
140         getops = (_citrus_iconv_getops_t)_citrus_find_getops(ci->ci_module,
141             module, "iconv");
142         if (!getops) {
143                 ret = EOPNOTSUPP;
144                 goto err;
145         }
146         ci->ci_ops = malloc(sizeof(*ci->ci_ops));
147         if (!ci->ci_ops) {
148                 ret = errno;
149                 goto err;
150         }
151         ret = (*getops)(ci->ci_ops);
152         if (ret)
153                 goto err;
154
155         if (ci->ci_ops->io_init_shared == NULL ||
156             ci->ci_ops->io_uninit_shared == NULL ||
157             ci->ci_ops->io_init_context == NULL ||
158             ci->ci_ops->io_uninit_context == NULL ||
159             ci->ci_ops->io_convert == NULL)
160                 goto err;
161
162         /* initialize the converter */
163         ret = (*ci->ci_ops->io_init_shared)(ci, src, dst);
164         if (ret)
165                 goto err;
166
167         *rci = ci;
168
169         return (0);
170 err:
171         close_shared(ci);
172         return (ret);
173 }
174
175 static __inline int
176 hash_func(const char *key)
177 {
178
179         return (_string_hash_func(key, CI_HASH_SIZE));
180 }
181
182 static __inline int
183 match_func(struct _citrus_iconv_shared * __restrict ci,
184     const char * __restrict key)
185 {
186
187         return (strcmp(ci->ci_convname, key));
188 }
189
190 static int
191 get_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
192     const char *src, const char *dst)
193 {
194         struct _citrus_iconv_shared * ci;
195         char convname[PATH_MAX];
196         int hashval, ret = 0;
197
198         snprintf(convname, sizeof(convname), "%s/%s", src, dst);
199
200         WLOCK(&ci_lock);
201
202         /* lookup alread existing entry */
203         hashval = hash_func(convname);
204         _CITRUS_HASH_SEARCH(&shared_pool, ci, ci_hash_entry, match_func,
205             convname, hashval);
206         if (ci != NULL) {
207                 /* found */
208                 if (ci->ci_used_count == 0) {
209                         TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry);
210                         shared_num_unused--;
211                 }
212                 ci->ci_used_count++;
213                 *rci = ci;
214                 goto quit;
215         }
216
217         /* create new entry */
218         ret = open_shared(&ci, convname, src, dst);
219         if (ret)
220                 goto quit;
221
222         _CITRUS_HASH_INSERT(&shared_pool, ci, ci_hash_entry, hashval);
223         ci->ci_used_count = 1;
224         *rci = ci;
225
226 quit:
227         UNLOCK(&ci_lock);
228
229         return (ret);
230 }
231
232 static void
233 release_shared(struct _citrus_iconv_shared * __restrict ci)
234 {
235
236         WLOCK(&ci_lock);
237         ci->ci_used_count--;
238         if (ci->ci_used_count == 0) {
239                 /* put it into unused list */
240                 shared_num_unused++;
241                 TAILQ_INSERT_TAIL(&shared_unused, ci, ci_tailq_entry);
242                 /* flood out */
243                 while (shared_num_unused > shared_max_reuse) {
244                         ci = TAILQ_FIRST(&shared_unused);
245                         TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry);
246                         _CITRUS_HASH_REMOVE(ci, ci_hash_entry);
247                         shared_num_unused--;
248                         close_shared(ci);
249                 }
250         }
251
252         UNLOCK(&ci_lock);
253 }
254
255 /*
256  * _citrus_iconv_open:
257  *      open a converter for the specified in/out codes.
258  */
259 int
260 _citrus_iconv_open(struct _citrus_iconv * __restrict * __restrict rcv,
261     const char * __restrict src, const char * __restrict dst)
262 {
263         struct _citrus_iconv *cv = NULL;
264         struct _citrus_iconv_shared *ci = NULL;
265         char realdst[PATH_MAX], realsrc[PATH_MAX];
266         char buf[PATH_MAX], path[PATH_MAX];
267         int ret;
268
269         init_cache();
270
271         /* GNU behaviour, using locale encoding if "" or "char" is specified */
272         if ((strcmp(src, "") == 0) || (strcmp(src, "char") == 0))
273                 src = nl_langinfo(CODESET);
274         if ((strcmp(dst, "") == 0) || (strcmp(dst, "char") == 0))
275                 dst = nl_langinfo(CODESET);
276
277         /* resolve codeset name aliases */
278         strlcpy(realsrc, _lookup_alias(path, src, buf, (size_t)PATH_MAX,
279             _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX);
280         strlcpy(realdst, _lookup_alias(path, dst, buf, (size_t)PATH_MAX,
281             _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX);
282
283         /* sanity check */
284         if (strchr(realsrc, '/') != NULL || strchr(realdst, '/'))
285                 return (EINVAL);
286
287         /* get shared record */
288         ret = get_shared(&ci, realsrc, realdst);
289         if (ret)
290                 return (ret);
291
292         /* create/init context */
293         if (*rcv == NULL) {
294                 cv = malloc(sizeof(*cv));
295                 if (cv == NULL) {
296                         ret = errno;
297                         release_shared(ci);
298                         return (ret);
299                 }
300                 *rcv = cv;
301         }
302         (*rcv)->cv_shared = ci;
303         ret = (*ci->ci_ops->io_init_context)(*rcv);
304         if (ret) {
305                 release_shared(ci);
306                 free(cv);
307                 return (ret);
308         }
309         return (0);
310 }
311
312 /*
313  * _citrus_iconv_close:
314  *      close the specified converter.
315  */
316 void
317 _citrus_iconv_close(struct _citrus_iconv *cv)
318 {
319
320         if (cv) {
321                 (*cv->cv_shared->ci_ops->io_uninit_context)(cv);
322                 release_shared(cv->cv_shared);
323                 free(cv);
324         }
325 }
326
327 const char
328 *_citrus_iconv_canonicalize(const char *name)
329 {
330         char *buf;
331
332         if ((buf = malloc((size_t)PATH_MAX)) == NULL)
333                 return (NULL);
334         memset((void *)buf, 0, (size_t)PATH_MAX);
335         _citrus_esdb_alias(name, buf, (size_t)PATH_MAX);
336         return (buf);
337 }