xref: /freebsd/lib/libc/iconv/iconv.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /* $FreeBSD$ */
2 /* $NetBSD: iconv.c,v 1.11 2009/03/03 16:22:33 explorer Exp $ */
3 
4 /*-
5  * Copyright (c) 2003 Citrus Project,
6  * Copyright (c) 2009, 2010 Gabor Kovesdan <gabor@FreeBSD.org>,
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 #include <sys/queue.h>
33 #include <sys/types.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <iconv.h>
38 #include <limits.h>
39 #include <paths.h>
40 #include <stdbool.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "citrus_types.h"
45 #include "citrus_module.h"
46 #include "citrus_esdb.h"
47 #include "citrus_hash.h"
48 #include "citrus_iconv.h"
49 
50 #ifdef __weak_alias
51 __weak_alias(libiconv, _iconv)
52 __weak_alias(libiconv_open, _iconv_open)
53 __weak_alias(libiconv_open_into, _iconv_open_into)
54 __weak_alias(libiconv_close, _iconv_close)
55 __weak_alias(libiconvlist, _iconvlist)
56 __weak_alias(libiconvctl, _iconvctl)
57 __weak_alias(libiconv_set_relocation_prefix, _iconv_set_relocation_prefix)
58 __weak_alias(iconv_canonicalize, _iconv_canonicalize)
59 #endif
60 
61 #define ISBADF(_h_)	(!(_h_) || (_h_) == (iconv_t)-1)
62 
63 int _libiconv_version = _LIBICONV_VERSION;
64 
65 iconv_t		 _iconv_open(const char *out, const char *in,
66 		    struct _citrus_iconv *prealloc);
67 
68 iconv_t
69 _iconv_open(const char *out, const char *in, struct _citrus_iconv *prealloc)
70 {
71 	struct _citrus_iconv *handle;
72 	char *out_truncated, *p;
73 	int ret;
74 
75 	handle = prealloc;
76 
77 	/*
78 	 * Remove anything following a //, as these are options (like
79 	 * //ignore, //translate, etc) and we just don't handle them.
80 	 * This is for compatibilty with software that uses thees
81 	 * blindly.
82 	 */
83 	out_truncated = strdup(out);
84 	if (out_truncated == NULL) {
85 		errno = ENOMEM;
86 		return ((iconv_t)-1);
87 	}
88 
89 	p = out_truncated;
90         while (*p != 0) {
91                 if (p[0] == '/' && p[1] == '/') {
92                         *p = '\0';
93                         break;
94                 }
95                 p++;
96         }
97 
98 	ret = _citrus_iconv_open(&handle, in, out_truncated);
99 	free(out_truncated);
100 	if (ret) {
101 		errno = ret == ENOENT ? EINVAL : ret;
102 		return ((iconv_t)-1);
103 	}
104 
105 	handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE");
106 	handle->cv_shared->ci_hooks = NULL;
107 
108 	return ((iconv_t)(void *)handle);
109 }
110 
111 iconv_t
112 libiconv_open(const char *out, const char *in)
113 {
114 
115 	return (_iconv_open(out, in, NULL));
116 }
117 
118 int
119 libiconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr)
120 {
121 	struct _citrus_iconv *handle;
122 
123 	handle = (struct _citrus_iconv *)ptr;
124 	return ((_iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0);
125 }
126 
127 int
128 libiconv_close(iconv_t handle)
129 {
130 
131 	if (ISBADF(handle)) {
132 		errno = EBADF;
133 		return (-1);
134 	}
135 
136 	_citrus_iconv_close((struct _citrus_iconv *)(void *)handle);
137 
138 	return (0);
139 }
140 
141 size_t
142 libiconv(iconv_t handle, char **in, size_t *szin, char **out, size_t *szout)
143 {
144 	size_t ret;
145 	int err;
146 
147 	if (ISBADF(handle)) {
148 		errno = EBADF;
149 		return ((size_t)-1);
150 	}
151 
152 	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
153 	    in, szin, out, szout, 0, &ret);
154 	if (err) {
155 		errno = err;
156 		ret = (size_t)-1;
157 	}
158 
159 	return (ret);
160 }
161 
162 size_t
163 __iconv(iconv_t handle, char **in, size_t *szin, char **out,
164     size_t *szout, uint32_t flags, size_t *invalids)
165 {
166 	size_t ret;
167 	int err;
168 
169 	if (ISBADF(handle)) {
170 		errno = EBADF;
171 		return ((size_t)-1);
172 	}
173 
174 	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
175 	    in, szin, out, szout, flags, &ret);
176 	if (invalids)
177 		*invalids = ret;
178 	if (err) {
179 		errno = err;
180 		ret = (size_t)-1;
181 	}
182 
183 	return (ret);
184 }
185 
186 int
187 __iconv_get_list(char ***rlist, size_t *rsz, bool sorted)
188 {
189 	int ret;
190 
191 	ret = _citrus_esdb_get_list(rlist, rsz, sorted);
192 	if (ret) {
193 		errno = ret;
194 		return (-1);
195 	}
196 
197 	return (0);
198 }
199 
200 void
201 __iconv_free_list(char **list, size_t sz)
202 {
203 
204 	_citrus_esdb_free_list(list, sz);
205 }
206 
207 /*
208  * GNU-compatibile non-standard interfaces.
209  */
210 static int
211 qsort_helper(const void *first, const void *second)
212 {
213 	const char * const *s1;
214 	const char * const *s2;
215 
216 	s1 = first;
217 	s2 = second;
218 	return (strcmp(*s1, *s2));
219 }
220 
221 void
222 libiconvlist(int (*do_one) (unsigned int, const char * const *,
223     void *), void *data)
224 {
225 	char **list, **names;
226 	const char * const *np;
227 	char *curitem, *curkey, *slashpos;
228 	size_t sz;
229 	unsigned int i, j;
230 
231 	i = 0;
232 
233 	if (__iconv_get_list(&list, &sz, true))
234 		list = NULL;
235 	qsort((void *)list, sz, sizeof(char *), qsort_helper);
236 	while (i < sz) {
237 		j = 0;
238 		slashpos = strchr(list[i], '/');
239 		curkey = (char *)malloc(slashpos - list[i] + 2);
240 		names = (char **)malloc(sz * sizeof(char *));
241 		if ((curkey == NULL) || (names == NULL)) {
242 			__iconv_free_list(list, sz);
243 			return;
244 		}
245 		strlcpy(curkey, list[i], slashpos - list[i] + 1);
246 		names[j++] = strdup(curkey);
247 		for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) {
248 			slashpos = strchr(list[i], '/');
249 			curitem = (char *)malloc(strlen(slashpos) + 1);
250 			if (curitem == NULL) {
251 				__iconv_free_list(list, sz);
252 				return;
253 			}
254 			strlcpy(curitem, &slashpos[1], strlen(slashpos) + 1);
255 			if (strcmp(curkey, curitem) == 0) {
256 				continue;
257 			}
258 			names[j++] = strdup(curitem);
259 		}
260 		np = (const char * const *)names;
261 		do_one(j, np, data);
262 		free(names);
263 	}
264 
265 	__iconv_free_list(list, sz);
266 }
267 
268 __inline const char
269 *iconv_canonicalize(const char *name)
270 {
271 
272 	return (_citrus_iconv_canonicalize(name));
273 }
274 
275 int
276 libiconvctl(iconv_t cd, int request, void *argument)
277 {
278 	struct _citrus_iconv *cv;
279 	struct iconv_hooks *hooks;
280 	const char *convname;
281 	char src[PATH_MAX], *dst;
282 	int *i;
283 
284 	cv = (struct _citrus_iconv *)(void *)cd;
285 	hooks = (struct iconv_hooks *)argument;
286 	i = (int *)argument;
287 
288 	if (ISBADF(cd)) {
289 		errno = EBADF;
290 		return (-1);
291 	}
292 
293 	switch (request) {
294 	case ICONV_TRIVIALP:
295 		convname = cv->cv_shared->ci_convname;
296 		dst = strchr(convname, '/');
297 
298 		strlcpy(src, convname, dst - convname + 1);
299 		dst++;
300 		if ((convname == NULL) || (src == NULL) || (dst == NULL))
301 			return (-1);
302 		*i = strcmp(src, dst) == 0 ? 1 : 0;
303 		return (0);
304 	case ICONV_GET_TRANSLITERATE:
305 		*i = 1;
306 		return (0);
307 	case ICONV_SET_TRANSLITERATE:
308 		return  ((*i == 1) ? 0 : -1);
309 	case ICONV_GET_DISCARD_ILSEQ:
310 		*i = cv->cv_shared->ci_discard_ilseq ? 1 : 0;
311 		return (0);
312 	case ICONV_SET_DISCARD_ILSEQ:
313 		cv->cv_shared->ci_discard_ilseq = *i;
314 		return (0);
315 	case ICONV_SET_HOOKS:
316 		cv->cv_shared->ci_hooks = hooks;
317 		return (0);
318 	case ICONV_SET_FALLBACKS:
319 		errno = EOPNOTSUPP;
320 		return (-1);
321 	default:
322 		errno = EINVAL;
323 		return (-1);
324 	}
325 }
326 
327 void
328 libiconv_set_relocation_prefix(const char *orig_prefix __unused,
329     const char *curr_prefix __unused)
330 {
331 
332 }
333