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