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