bsd_iconv.c revision 250981
1240116Smarcel/* $FreeBSD: head/lib/libc/iconv/iconv.c 250981 2013-05-25 12:13:54Z ed $ */
2240116Smarcel/* $NetBSD: iconv.c,v 1.11 2009/03/03 16:22:33 explorer Exp $ */
3240116Smarcel
4240116Smarcel/*-
5240116Smarcel * Copyright (c) 2003 Citrus Project,
6240116Smarcel * Copyright (c) 2009, 2010 Gabor Kovesdan <gabor@FreeBSD.org>,
7240116Smarcel * All rights reserved.
8240116Smarcel *
9240116Smarcel * Redistribution and use in source and binary forms, with or without
10240116Smarcel * modification, are permitted provided that the following conditions
11240116Smarcel * are met:
12240116Smarcel * 1. Redistributions of source code must retain the above copyright
13240116Smarcel *    notice, this list of conditions and the following disclaimer.
14240116Smarcel * 2. Redistributions in binary form must reproduce the above copyright
15240116Smarcel *    notice, this list of conditions and the following disclaimer in the
16240116Smarcel *    documentation and/or other materials provided with the distribution.
17240116Smarcel *
18240116Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19240116Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20240116Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21240116Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22240116Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23240116Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24240116Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25240116Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26240116Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27240116Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28240116Smarcel * SUCH DAMAGE.
29240116Smarcel */
30240116Smarcel
31240116Smarcel#include <sys/cdefs.h>
32240116Smarcel#include <sys/queue.h>
33240116Smarcel#include <sys/types.h>
34240116Smarcel
35240116Smarcel#include <assert.h>
36240116Smarcel#include <errno.h>
37240116Smarcel#include <iconv.h>
38240116Smarcel#include <limits.h>
39240116Smarcel#include <paths.h>
40240116Smarcel#include <stdbool.h>
41240116Smarcel#include <stdlib.h>
42240116Smarcel#include <string.h>
43240116Smarcel
44240116Smarcel#include "citrus_types.h"
45240116Smarcel#include "citrus_module.h"
46240116Smarcel#include "citrus_esdb.h"
47240116Smarcel#include "citrus_hash.h"
48240116Smarcel#include "citrus_iconv.h"
49240116Smarcel
50240116Smarcel#ifdef __weak_alias
51240116Smarcel__weak_alias(libiconv, _iconv)
52240116Smarcel__weak_alias(libiconv_open, _iconv_open)
53240116Smarcel__weak_alias(libiconv_open_into, _iconv_open_into)
54240116Smarcel__weak_alias(libiconv_close, _iconv_close)
55240116Smarcel__weak_alias(libiconvlist, _iconvlist)
56240116Smarcel__weak_alias(libiconvctl, _iconvctl)
57243051Smarcel__weak_alias(libiconv_set_relocation_prefix, _iconv_set_relocation_prefix)
58240116Smarcel__weak_alias(iconv_canonicalize, _iconv_canonicalize)
59240116Smarcel#endif
60240116Smarcel
61240116Smarcel#define ISBADF(_h_)	(!(_h_) || (_h_) == (iconv_t)-1)
62240116Smarcel
63240116Smarcelint _libiconv_version = _LIBICONV_VERSION;
64240116Smarcel
65240116Smarceliconv_t		 _iconv_open(const char *out, const char *in,
66240116Smarcel		    struct _citrus_iconv *prealloc);
67240116Smarcel
68240116Smarceliconv_t
69240116Smarcel_iconv_open(const char *out, const char *in, struct _citrus_iconv *handle)
70240116Smarcel{
71240116Smarcel	const char *out_slashes;
72240116Smarcel	char *out_noslashes;
73240116Smarcel	int ret;
74240116Smarcel
75240116Smarcel	/*
76240116Smarcel	 * Remove anything following a //, as these are options (like
77240116Smarcel	 * //ignore, //translate, etc) and we just don't handle them.
78240116Smarcel	 * This is for compatibility with software that uses these
79240116Smarcel	 * blindly.
80240116Smarcel	 */
81240116Smarcel	out_slashes = strstr(out, "//");
82243051Smarcel	if (out_slashes != NULL) {
83240116Smarcel		out_noslashes = strndup(out, out_slashes - out);
84240116Smarcel		if (out_noslashes == NULL) {
85240116Smarcel			errno = ENOMEM;
86240116Smarcel			return ((iconv_t)-1);
87240116Smarcel		}
88240116Smarcel		ret = _citrus_iconv_open(&handle, in, out_noslashes);
89240116Smarcel		free(out_noslashes);
90240116Smarcel	} else {
91240116Smarcel		ret = _citrus_iconv_open(&handle, in, out);
92240116Smarcel	}
93240116Smarcel
94240116Smarcel	if (ret) {
95240116Smarcel		errno = ret == ENOENT ? EINVAL : ret;
96240116Smarcel		return ((iconv_t)-1);
97240116Smarcel	}
98240116Smarcel
99240116Smarcel	handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE");
100240116Smarcel	handle->cv_shared->ci_hooks = NULL;
101240116Smarcel
102240116Smarcel	return ((iconv_t)(void *)handle);
103240116Smarcel}
104240116Smarcel
105240116Smarceliconv_t
106240116Smarcellibiconv_open(const char *out, const char *in)
107240116Smarcel{
108240116Smarcel
109240116Smarcel	return (_iconv_open(out, in, NULL));
110240116Smarcel}
111240116Smarcel
112240116Smarcelint
113240116Smarcellibiconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr)
114240116Smarcel{
115240116Smarcel	struct _citrus_iconv *handle;
116240116Smarcel
117240116Smarcel	handle = (struct _citrus_iconv *)ptr;
118240116Smarcel	return ((_iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0);
119240116Smarcel}
120240116Smarcel
121240116Smarcelint
122240116Smarcellibiconv_close(iconv_t handle)
123240116Smarcel{
124240116Smarcel
125240116Smarcel	if (ISBADF(handle)) {
126240116Smarcel		errno = EBADF;
127240116Smarcel		return (-1);
128240116Smarcel	}
129240116Smarcel
130240116Smarcel	_citrus_iconv_close((struct _citrus_iconv *)(void *)handle);
131240116Smarcel
132240116Smarcel	return (0);
133240116Smarcel}
134240116Smarcel
135240116Smarcelsize_t
136240116Smarcellibiconv(iconv_t handle, char **in, size_t *szin, char **out, size_t *szout)
137240116Smarcel{
138240116Smarcel	size_t ret;
139240116Smarcel	int err;
140240116Smarcel
141240116Smarcel	if (ISBADF(handle)) {
142240116Smarcel		errno = EBADF;
143240116Smarcel		return ((size_t)-1);
144240116Smarcel	}
145240116Smarcel
146240116Smarcel	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
147240116Smarcel	    in, szin, out, szout, 0, &ret);
148240116Smarcel	if (err) {
149240116Smarcel		errno = err;
150240116Smarcel		ret = (size_t)-1;
151240116Smarcel	}
152240116Smarcel
153240116Smarcel	return (ret);
154240116Smarcel}
155240116Smarcel
156240116Smarcelsize_t
157240116Smarcel__iconv(iconv_t handle, char **in, size_t *szin, char **out,
158240116Smarcel    size_t *szout, uint32_t flags, size_t *invalids)
159240116Smarcel{
160240116Smarcel	size_t ret;
161240116Smarcel	int err;
162240116Smarcel
163240116Smarcel	if (ISBADF(handle)) {
164240116Smarcel		errno = EBADF;
165240116Smarcel		return ((size_t)-1);
166240116Smarcel	}
167240116Smarcel
168240116Smarcel	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
169240116Smarcel	    in, szin, out, szout, flags, &ret);
170240116Smarcel	if (invalids)
171260029Sjmmv		*invalids = ret;
172260029Sjmmv	if (err) {
173260029Sjmmv		errno = err;
174260029Sjmmv		ret = (size_t)-1;
175260029Sjmmv	}
176260029Sjmmv
177260029Sjmmv	return (ret);
178260029Sjmmv}
179260029Sjmmv
180260029Sjmmvint
181260029Sjmmv__iconv_get_list(char ***rlist, size_t *rsz, bool sorted)
182260029Sjmmv{
183260029Sjmmv	int ret;
184260029Sjmmv
185260029Sjmmv	ret = _citrus_esdb_get_list(rlist, rsz, sorted);
186240116Smarcel	if (ret) {
187240116Smarcel		errno = ret;
188240116Smarcel		return (-1);
189240116Smarcel	}
190240116Smarcel
191240116Smarcel	return (0);
192240116Smarcel}
193240116Smarcel
194260029Sjmmvvoid
195240116Smarcel__iconv_free_list(char **list, size_t sz)
196240116Smarcel{
197240116Smarcel
198	_citrus_esdb_free_list(list, sz);
199}
200
201/*
202 * GNU-compatibile non-standard interfaces.
203 */
204static int
205qsort_helper(const void *first, const void *second)
206{
207	const char * const *s1;
208	const char * const *s2;
209
210	s1 = first;
211	s2 = second;
212	return (strcmp(*s1, *s2));
213}
214
215void
216libiconvlist(int (*do_one) (unsigned int, const char * const *,
217    void *), void *data)
218{
219	char **list, **names;
220	const char * const *np;
221	char *curitem, *curkey, *slashpos;
222	size_t sz;
223	unsigned int i, j;
224
225	i = 0;
226
227	if (__iconv_get_list(&list, &sz, true))
228		list = NULL;
229	qsort((void *)list, sz, sizeof(char *), qsort_helper);
230	while (i < sz) {
231		j = 0;
232		slashpos = strchr(list[i], '/');
233		curkey = (char *)malloc(slashpos - list[i] + 2);
234		names = (char **)malloc(sz * sizeof(char *));
235		if ((curkey == NULL) || (names == NULL)) {
236			__iconv_free_list(list, sz);
237			return;
238		}
239		strlcpy(curkey, list[i], slashpos - list[i] + 1);
240		names[j++] = strdup(curkey);
241		for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) {
242			slashpos = strchr(list[i], '/');
243			curitem = (char *)malloc(strlen(slashpos) + 1);
244			if (curitem == NULL) {
245				__iconv_free_list(list, sz);
246				return;
247			}
248			strlcpy(curitem, &slashpos[1], strlen(slashpos) + 1);
249			if (strcmp(curkey, curitem) == 0) {
250				continue;
251			}
252			names[j++] = strdup(curitem);
253		}
254		np = (const char * const *)names;
255		do_one(j, np, data);
256		free(names);
257	}
258
259	__iconv_free_list(list, sz);
260}
261
262__inline const char
263*iconv_canonicalize(const char *name)
264{
265
266	return (_citrus_iconv_canonicalize(name));
267}
268
269int
270libiconvctl(iconv_t cd, int request, void *argument)
271{
272	struct _citrus_iconv *cv;
273	struct iconv_hooks *hooks;
274	const char *convname;
275	char src[PATH_MAX], *dst;
276	int *i;
277
278	cv = (struct _citrus_iconv *)(void *)cd;
279	hooks = (struct iconv_hooks *)argument;
280	i = (int *)argument;
281
282	if (ISBADF(cd)) {
283		errno = EBADF;
284		return (-1);
285	}
286
287	switch (request) {
288	case ICONV_TRIVIALP:
289		convname = cv->cv_shared->ci_convname;
290		dst = strchr(convname, '/');
291
292		strlcpy(src, convname, dst - convname + 1);
293		dst++;
294		if ((convname == NULL) || (src == NULL) || (dst == NULL))
295			return (-1);
296		*i = strcmp(src, dst) == 0 ? 1 : 0;
297		return (0);
298	case ICONV_GET_TRANSLITERATE:
299		*i = 1;
300		return (0);
301	case ICONV_SET_TRANSLITERATE:
302		return  ((*i == 1) ? 0 : -1);
303	case ICONV_GET_DISCARD_ILSEQ:
304		*i = cv->cv_shared->ci_discard_ilseq ? 1 : 0;
305		return (0);
306	case ICONV_SET_DISCARD_ILSEQ:
307		cv->cv_shared->ci_discard_ilseq = *i;
308		return (0);
309	case ICONV_SET_HOOKS:
310		cv->cv_shared->ci_hooks = hooks;
311		return (0);
312	case ICONV_SET_FALLBACKS:
313		errno = EOPNOTSUPP;
314		return (-1);
315	default:
316		errno = EINVAL;
317		return (-1);
318	}
319}
320
321void
322libiconv_set_relocation_prefix(const char *orig_prefix __unused,
323    const char *curr_prefix __unused)
324{
325
326}
327