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