xref: /freebsd/lib/libc/iconv/iconv.c (revision 595e514d0df2bac5b813d35f83e32875dbf16a83)
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 *handle)
70 {
71 	const char *out_slashes;
72 	char *out_noslashes;
73 	int ret;
74 
75 	/*
76 	 * Remove anything following a //, as these are options (like
77 	 * //ignore, //translate, etc) and we just don't handle them.
78 	 * This is for compatibility with software that uses these
79 	 * blindly.
80 	 */
81 	out_slashes = strstr(out, "//");
82 	if (out_slashes != NULL) {
83 		out_noslashes = strndup(out, out_slashes - out);
84 		if (out_noslashes == NULL) {
85 			errno = ENOMEM;
86 			return ((iconv_t)-1);
87 		}
88 		ret = _citrus_iconv_open(&handle, in, out_noslashes);
89 		free(out_noslashes);
90 	} else {
91 		ret = _citrus_iconv_open(&handle, in, out);
92 	}
93 
94 	if (ret) {
95 		errno = ret == ENOENT ? EINVAL : ret;
96 		return ((iconv_t)-1);
97 	}
98 
99 	handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE");
100 	handle->cv_shared->ci_hooks = NULL;
101 
102 	return ((iconv_t)(void *)handle);
103 }
104 
105 iconv_t
106 libiconv_open(const char *out, const char *in)
107 {
108 
109 	return (_iconv_open(out, in, NULL));
110 }
111 
112 int
113 libiconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr)
114 {
115 	struct _citrus_iconv *handle;
116 
117 	handle = (struct _citrus_iconv *)ptr;
118 	return ((_iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0);
119 }
120 
121 int
122 libiconv_close(iconv_t handle)
123 {
124 
125 	if (ISBADF(handle)) {
126 		errno = EBADF;
127 		return (-1);
128 	}
129 
130 	_citrus_iconv_close((struct _citrus_iconv *)(void *)handle);
131 
132 	return (0);
133 }
134 
135 size_t
136 libiconv(iconv_t handle, char **in, size_t *szin, char **out, size_t *szout)
137 {
138 	size_t ret;
139 	int err;
140 
141 	if (ISBADF(handle)) {
142 		errno = EBADF;
143 		return ((size_t)-1);
144 	}
145 
146 	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
147 	    in, szin, out, szout, 0, &ret);
148 	if (err) {
149 		errno = err;
150 		ret = (size_t)-1;
151 	}
152 
153 	return (ret);
154 }
155 
156 size_t
157 __iconv(iconv_t handle, char **in, size_t *szin, char **out,
158     size_t *szout, uint32_t flags, size_t *invalids)
159 {
160 	size_t ret;
161 	int err;
162 
163 	if (ISBADF(handle)) {
164 		errno = EBADF;
165 		return ((size_t)-1);
166 	}
167 
168 	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
169 	    in, szin, out, szout, flags, &ret);
170 	if (invalids)
171 		*invalids = ret;
172 	if (err) {
173 		errno = err;
174 		ret = (size_t)-1;
175 	}
176 
177 	return (ret);
178 }
179 
180 int
181 __iconv_get_list(char ***rlist, size_t *rsz, bool sorted)
182 {
183 	int ret;
184 
185 	ret = _citrus_esdb_get_list(rlist, rsz, sorted);
186 	if (ret) {
187 		errno = ret;
188 		return (-1);
189 	}
190 
191 	return (0);
192 }
193 
194 void
195 __iconv_free_list(char **list, size_t sz)
196 {
197 
198 	_citrus_esdb_free_list(list, sz);
199 }
200 
201 /*
202  * GNU-compatibile non-standard interfaces.
203  */
204 static int
205 qsort_helper(const void *first, const void *second)
206 {
207 	const char * const *s1;
208 	const char * const *s2;
209 
210 	s1 = first;
211 	s2 = second;
212 	return (strcmp(*s1, *s2));
213 }
214 
215 void
216 libiconvlist(int (*do_one) (unsigned int, const char * const *,
217     void *), void *data)
218 {
219 	char **list, **names;
220 	const char * const *np;
221 	char *curitem, *curkey, *slashpos;
222 	size_t sz;
223 	unsigned int i, j;
224 
225 	i = 0;
226 
227 	if (__iconv_get_list(&list, &sz, true))
228 		list = NULL;
229 	qsort((void *)list, sz, sizeof(char *), qsort_helper);
230 	while (i < sz) {
231 		j = 0;
232 		slashpos = strchr(list[i], '/');
233 		curkey = (char *)malloc(slashpos - list[i] + 2);
234 		names = (char **)malloc(sz * sizeof(char *));
235 		if ((curkey == NULL) || (names == NULL)) {
236 			__iconv_free_list(list, sz);
237 			return;
238 		}
239 		strlcpy(curkey, list[i], slashpos - list[i] + 1);
240 		names[j++] = strdup(curkey);
241 		for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) {
242 			slashpos = strchr(list[i], '/');
243 			curitem = (char *)malloc(strlen(slashpos) + 1);
244 			if (curitem == NULL) {
245 				__iconv_free_list(list, sz);
246 				return;
247 			}
248 			strlcpy(curitem, &slashpos[1], strlen(slashpos) + 1);
249 			if (strcmp(curkey, curitem) == 0) {
250 				continue;
251 			}
252 			names[j++] = strdup(curitem);
253 		}
254 		np = (const char * const *)names;
255 		do_one(j, np, data);
256 		free(names);
257 	}
258 
259 	__iconv_free_list(list, sz);
260 }
261 
262 __inline const char
263 *iconv_canonicalize(const char *name)
264 {
265 
266 	return (_citrus_iconv_canonicalize(name));
267 }
268 
269 int
270 libiconvctl(iconv_t cd, int request, void *argument)
271 {
272 	struct _citrus_iconv *cv;
273 	struct iconv_hooks *hooks;
274 	const char *convname;
275 	char src[PATH_MAX], *dst;
276 	int *i;
277 
278 	cv = (struct _citrus_iconv *)(void *)cd;
279 	hooks = (struct iconv_hooks *)argument;
280 	i = (int *)argument;
281 
282 	if (ISBADF(cd)) {
283 		errno = EBADF;
284 		return (-1);
285 	}
286 
287 	switch (request) {
288 	case ICONV_TRIVIALP:
289 		convname = cv->cv_shared->ci_convname;
290 		dst = strchr(convname, '/');
291 
292 		strlcpy(src, convname, dst - convname + 1);
293 		dst++;
294 		if ((convname == NULL) || (src == NULL) || (dst == NULL))
295 			return (-1);
296 		*i = strcmp(src, dst) == 0 ? 1 : 0;
297 		return (0);
298 	case ICONV_GET_TRANSLITERATE:
299 		*i = 1;
300 		return (0);
301 	case ICONV_SET_TRANSLITERATE:
302 		return  ((*i == 1) ? 0 : -1);
303 	case ICONV_GET_DISCARD_ILSEQ:
304 		*i = cv->cv_shared->ci_discard_ilseq ? 1 : 0;
305 		return (0);
306 	case ICONV_SET_DISCARD_ILSEQ:
307 		cv->cv_shared->ci_discard_ilseq = *i;
308 		return (0);
309 	case ICONV_SET_HOOKS:
310 		cv->cv_shared->ci_hooks = hooks;
311 		return (0);
312 	case ICONV_SET_FALLBACKS:
313 		errno = EOPNOTSUPP;
314 		return (-1);
315 	default:
316 		errno = EINVAL;
317 		return (-1);
318 	}
319 }
320 
321 void
322 libiconv_set_relocation_prefix(const char *orig_prefix __unused,
323     const char *curr_prefix __unused)
324 {
325 
326 }
327