1219019Sgabor/* $FreeBSD$ */
2219019Sgabor/* $NetBSD: iconv.c,v 1.11 2009/03/03 16:22:33 explorer Exp $ */
3219019Sgabor
4219019Sgabor/*-
5219019Sgabor * Copyright (c) 2003 Citrus Project,
6219019Sgabor * Copyright (c) 2009, 2010 Gabor Kovesdan <gabor@FreeBSD.org>,
7219019Sgabor * All rights reserved.
8219019Sgabor *
9219019Sgabor * Redistribution and use in source and binary forms, with or without
10219019Sgabor * modification, are permitted provided that the following conditions
11219019Sgabor * are met:
12219019Sgabor * 1. Redistributions of source code must retain the above copyright
13219019Sgabor *    notice, this list of conditions and the following disclaimer.
14219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
15219019Sgabor *    notice, this list of conditions and the following disclaimer in the
16219019Sgabor *    documentation and/or other materials provided with the distribution.
17219019Sgabor *
18219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28219019Sgabor * SUCH DAMAGE.
29219019Sgabor */
30219019Sgabor
31219019Sgabor#include <sys/cdefs.h>
32219019Sgabor#include <sys/queue.h>
33219019Sgabor#include <sys/types.h>
34219019Sgabor
35219019Sgabor#include <assert.h>
36219019Sgabor#include <errno.h>
37219019Sgabor#include <iconv.h>
38219019Sgabor#include <limits.h>
39219019Sgabor#include <paths.h>
40219019Sgabor#include <stdbool.h>
41219019Sgabor#include <stdlib.h>
42219019Sgabor#include <string.h>
43219019Sgabor
44219019Sgabor#include "citrus_types.h"
45219019Sgabor#include "citrus_module.h"
46219019Sgabor#include "citrus_esdb.h"
47219019Sgabor#include "citrus_hash.h"
48219019Sgabor#include "citrus_iconv.h"
49219019Sgabor
50258283Speter#include "iconv-internal.h"
51258283Speter
52219019Sgabor#define ISBADF(_h_)	(!(_h_) || (_h_) == (iconv_t)-1)
53219019Sgabor
54258283Speterstatic iconv_t
55258283Speter__bsd___iconv_open(const char *out, const char *in, struct _citrus_iconv *handle)
56219019Sgabor{
57250981Sed	const char *out_slashes;
58250981Sed	char *out_noslashes;
59219019Sgabor	int ret;
60219019Sgabor
61219019Sgabor	/*
62219019Sgabor	 * Remove anything following a //, as these are options (like
63219019Sgabor	 * //ignore, //translate, etc) and we just don't handle them.
64250981Sed	 * This is for compatibility with software that uses these
65219019Sgabor	 * blindly.
66219019Sgabor	 */
67250981Sed	out_slashes = strstr(out, "//");
68250981Sed	if (out_slashes != NULL) {
69250981Sed		out_noslashes = strndup(out, out_slashes - out);
70250981Sed		if (out_noslashes == NULL) {
71250981Sed			errno = ENOMEM;
72250981Sed			return ((iconv_t)-1);
73250981Sed		}
74250981Sed		ret = _citrus_iconv_open(&handle, in, out_noslashes);
75250981Sed		free(out_noslashes);
76250981Sed	} else {
77250981Sed		ret = _citrus_iconv_open(&handle, in, out);
78219019Sgabor	}
79223296Skan
80219019Sgabor	if (ret) {
81219019Sgabor		errno = ret == ENOENT ? EINVAL : ret;
82219019Sgabor		return ((iconv_t)-1);
83219019Sgabor	}
84219019Sgabor
85219019Sgabor	handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE");
86219019Sgabor	handle->cv_shared->ci_hooks = NULL;
87219019Sgabor
88219019Sgabor	return ((iconv_t)(void *)handle);
89219019Sgabor}
90219019Sgabor
91219019Sgaboriconv_t
92258283Speter__bsd_iconv_open(const char *out, const char *in)
93219019Sgabor{
94219019Sgabor
95258283Speter	return (__bsd___iconv_open(out, in, NULL));
96219019Sgabor}
97219019Sgabor
98219019Sgaborint
99258283Speter__bsd_iconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr)
100219019Sgabor{
101219019Sgabor	struct _citrus_iconv *handle;
102219019Sgabor
103219019Sgabor	handle = (struct _citrus_iconv *)ptr;
104258283Speter	return ((__bsd___iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0);
105219019Sgabor}
106219019Sgabor
107219019Sgaborint
108258283Speter__bsd_iconv_close(iconv_t handle)
109219019Sgabor{
110219019Sgabor
111219019Sgabor	if (ISBADF(handle)) {
112219019Sgabor		errno = EBADF;
113219019Sgabor		return (-1);
114219019Sgabor	}
115219019Sgabor
116219019Sgabor	_citrus_iconv_close((struct _citrus_iconv *)(void *)handle);
117219019Sgabor
118219019Sgabor	return (0);
119219019Sgabor}
120219019Sgabor
121219019Sgaborsize_t
122258283Speter__bsd_iconv(iconv_t handle, const char **in, size_t *szin, char **out, size_t *szout)
123219019Sgabor{
124219019Sgabor	size_t ret;
125219019Sgabor	int err;
126219019Sgabor
127219019Sgabor	if (ISBADF(handle)) {
128219019Sgabor		errno = EBADF;
129219019Sgabor		return ((size_t)-1);
130219019Sgabor	}
131219019Sgabor
132219019Sgabor	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
133219019Sgabor	    in, szin, out, szout, 0, &ret);
134219019Sgabor	if (err) {
135219019Sgabor		errno = err;
136219019Sgabor		ret = (size_t)-1;
137219019Sgabor	}
138219019Sgabor
139219019Sgabor	return (ret);
140219019Sgabor}
141219019Sgabor
142219019Sgaborsize_t
143258283Speter__bsd___iconv(iconv_t handle, const char **in, size_t *szin, char **out,
144219019Sgabor    size_t *szout, uint32_t flags, size_t *invalids)
145219019Sgabor{
146219019Sgabor	size_t ret;
147219019Sgabor	int err;
148219019Sgabor
149219019Sgabor	if (ISBADF(handle)) {
150219019Sgabor		errno = EBADF;
151219019Sgabor		return ((size_t)-1);
152219019Sgabor	}
153219019Sgabor
154219019Sgabor	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
155219019Sgabor	    in, szin, out, szout, flags, &ret);
156219019Sgabor	if (invalids)
157219019Sgabor		*invalids = ret;
158219019Sgabor	if (err) {
159219019Sgabor		errno = err;
160219019Sgabor		ret = (size_t)-1;
161219019Sgabor	}
162219019Sgabor
163219019Sgabor	return (ret);
164219019Sgabor}
165219019Sgabor
166219019Sgaborint
167258283Speter__bsd___iconv_get_list(char ***rlist, size_t *rsz, bool sorted)
168219019Sgabor{
169219019Sgabor	int ret;
170219019Sgabor
171219019Sgabor	ret = _citrus_esdb_get_list(rlist, rsz, sorted);
172219019Sgabor	if (ret) {
173219019Sgabor		errno = ret;
174219019Sgabor		return (-1);
175219019Sgabor	}
176219019Sgabor
177219019Sgabor	return (0);
178219019Sgabor}
179219019Sgabor
180219019Sgaborvoid
181258283Speter__bsd___iconv_free_list(char **list, size_t sz)
182219019Sgabor{
183219019Sgabor
184219019Sgabor	_citrus_esdb_free_list(list, sz);
185219019Sgabor}
186219019Sgabor
187219019Sgabor/*
188219019Sgabor * GNU-compatibile non-standard interfaces.
189219019Sgabor */
190219019Sgaborstatic int
191219019Sgaborqsort_helper(const void *first, const void *second)
192219019Sgabor{
193219019Sgabor	const char * const *s1;
194219019Sgabor	const char * const *s2;
195219019Sgabor
196219019Sgabor	s1 = first;
197219019Sgabor	s2 = second;
198219019Sgabor	return (strcmp(*s1, *s2));
199219019Sgabor}
200219019Sgabor
201219019Sgaborvoid
202258283Speter__bsd_iconvlist(int (*do_one) (unsigned int, const char * const *,
203219019Sgabor    void *), void *data)
204219019Sgabor{
205219019Sgabor	char **list, **names;
206219019Sgabor	const char * const *np;
207219019Sgabor	char *curitem, *curkey, *slashpos;
208219019Sgabor	size_t sz;
209219019Sgabor	unsigned int i, j;
210219019Sgabor
211219019Sgabor	i = 0;
212219019Sgabor
213258283Speter	if (__bsd___iconv_get_list(&list, &sz, true))
214219019Sgabor		list = NULL;
215219019Sgabor	qsort((void *)list, sz, sizeof(char *), qsort_helper);
216219019Sgabor	while (i < sz) {
217219019Sgabor		j = 0;
218219019Sgabor		slashpos = strchr(list[i], '/');
219219019Sgabor		curkey = (char *)malloc(slashpos - list[i] + 2);
220219019Sgabor		names = (char **)malloc(sz * sizeof(char *));
221219019Sgabor		if ((curkey == NULL) || (names == NULL)) {
222258283Speter			__bsd___iconv_free_list(list, sz);
223219019Sgabor			return;
224219019Sgabor		}
225219019Sgabor		strlcpy(curkey, list[i], slashpos - list[i] + 1);
226219019Sgabor		names[j++] = strdup(curkey);
227219019Sgabor		for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) {
228219019Sgabor			slashpos = strchr(list[i], '/');
229219019Sgabor			curitem = (char *)malloc(strlen(slashpos) + 1);
230219019Sgabor			if (curitem == NULL) {
231258283Speter				__bsd___iconv_free_list(list, sz);
232219019Sgabor				return;
233219019Sgabor			}
234219019Sgabor			strlcpy(curitem, &slashpos[1], strlen(slashpos) + 1);
235219019Sgabor			if (strcmp(curkey, curitem) == 0) {
236219019Sgabor				continue;
237219019Sgabor			}
238219019Sgabor			names[j++] = strdup(curitem);
239219019Sgabor		}
240219019Sgabor		np = (const char * const *)names;
241219019Sgabor		do_one(j, np, data);
242219019Sgabor		free(names);
243219019Sgabor	}
244219019Sgabor
245258283Speter	__bsd___iconv_free_list(list, sz);
246219019Sgabor}
247219019Sgabor
248258283Speter__inline const char *
249258283Speter__bsd_iconv_canonicalize(const char *name)
250219019Sgabor{
251219019Sgabor
252219019Sgabor	return (_citrus_iconv_canonicalize(name));
253219019Sgabor}
254219019Sgabor
255219019Sgaborint
256258283Speter__bsd_iconvctl(iconv_t cd, int request, void *argument)
257219019Sgabor{
258219019Sgabor	struct _citrus_iconv *cv;
259219019Sgabor	struct iconv_hooks *hooks;
260219019Sgabor	const char *convname;
261219019Sgabor	char src[PATH_MAX], *dst;
262219019Sgabor	int *i;
263219019Sgabor
264219019Sgabor	cv = (struct _citrus_iconv *)(void *)cd;
265219019Sgabor	hooks = (struct iconv_hooks *)argument;
266219019Sgabor	i = (int *)argument;
267219019Sgabor
268219019Sgabor	if (ISBADF(cd)) {
269219019Sgabor		errno = EBADF;
270219019Sgabor		return (-1);
271219019Sgabor	}
272219019Sgabor
273219019Sgabor	switch (request) {
274219019Sgabor	case ICONV_TRIVIALP:
275219019Sgabor		convname = cv->cv_shared->ci_convname;
276219019Sgabor		dst = strchr(convname, '/');
277219019Sgabor
278219019Sgabor		strlcpy(src, convname, dst - convname + 1);
279219019Sgabor		dst++;
280219019Sgabor		if ((convname == NULL) || (src == NULL) || (dst == NULL))
281219019Sgabor			return (-1);
282219019Sgabor		*i = strcmp(src, dst) == 0 ? 1 : 0;
283219019Sgabor		return (0);
284219019Sgabor	case ICONV_GET_TRANSLITERATE:
285219019Sgabor		*i = 1;
286219019Sgabor		return (0);
287219019Sgabor	case ICONV_SET_TRANSLITERATE:
288219019Sgabor		return  ((*i == 1) ? 0 : -1);
289219019Sgabor	case ICONV_GET_DISCARD_ILSEQ:
290219019Sgabor		*i = cv->cv_shared->ci_discard_ilseq ? 1 : 0;
291219019Sgabor		return (0);
292219019Sgabor	case ICONV_SET_DISCARD_ILSEQ:
293219019Sgabor		cv->cv_shared->ci_discard_ilseq = *i;
294219019Sgabor		return (0);
295219019Sgabor	case ICONV_SET_HOOKS:
296219019Sgabor		cv->cv_shared->ci_hooks = hooks;
297219019Sgabor		return (0);
298219019Sgabor	case ICONV_SET_FALLBACKS:
299219019Sgabor		errno = EOPNOTSUPP;
300219019Sgabor		return (-1);
301258537Shrs	case ICONV_GET_ILSEQ_INVALID:
302258537Shrs		*i = cv->cv_shared->ci_ilseq_invalid ? 1 : 0;
303258537Shrs		return (0);
304258537Shrs	case ICONV_SET_ILSEQ_INVALID:
305258537Shrs		cv->cv_shared->ci_ilseq_invalid = *i;
306258537Shrs		return (0);
307219019Sgabor	default:
308219019Sgabor		errno = EINVAL;
309219019Sgabor		return (-1);
310219019Sgabor	}
311219019Sgabor}
312219019Sgabor
313219019Sgaborvoid
314258283Speter__bsd_iconv_set_relocation_prefix(const char *orig_prefix __unused,
315219019Sgabor    const char *curr_prefix __unused)
316219019Sgabor{
317219019Sgabor
318219019Sgabor}
319