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