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