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