xref: /dflybsd-src/lib/libc/iconv/iconv.c (revision 0db70a6ab02af296a425c9de48294a4da59c442b)
10d5acd74SJohn Marino /* $FreeBSD: head/lib/libc/iconv/iconv.c 254273 2013-08-13 07:15:01Z peter $ */
20d5acd74SJohn Marino /* $NetBSD: iconv.c,v 1.11 2009/03/03 16:22:33 explorer Exp $ */
32180e8afSJoerg Sonnenberger 
42180e8afSJoerg Sonnenberger /*-
52180e8afSJoerg Sonnenberger  * Copyright (c) 2003 Citrus Project,
60d5acd74SJohn Marino  * Copyright (c) 2009, 2010 Gabor Kovesdan <gabor@FreeBSD.org>,
72180e8afSJoerg Sonnenberger  * All rights reserved.
82180e8afSJoerg Sonnenberger  *
92180e8afSJoerg Sonnenberger  * Redistribution and use in source and binary forms, with or without
102180e8afSJoerg Sonnenberger  * modification, are permitted provided that the following conditions
112180e8afSJoerg Sonnenberger  * are met:
122180e8afSJoerg Sonnenberger  * 1. Redistributions of source code must retain the above copyright
132180e8afSJoerg Sonnenberger  *    notice, this list of conditions and the following disclaimer.
142180e8afSJoerg Sonnenberger  * 2. Redistributions in binary form must reproduce the above copyright
152180e8afSJoerg Sonnenberger  *    notice, this list of conditions and the following disclaimer in the
162180e8afSJoerg Sonnenberger  *    documentation and/or other materials provided with the distribution.
172180e8afSJoerg Sonnenberger  *
182180e8afSJoerg Sonnenberger  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
192180e8afSJoerg Sonnenberger  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
202180e8afSJoerg Sonnenberger  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
212180e8afSJoerg Sonnenberger  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
222180e8afSJoerg Sonnenberger  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
232180e8afSJoerg Sonnenberger  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
242180e8afSJoerg Sonnenberger  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
252180e8afSJoerg Sonnenberger  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
262180e8afSJoerg Sonnenberger  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
272180e8afSJoerg Sonnenberger  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
282180e8afSJoerg Sonnenberger  * SUCH DAMAGE.
292180e8afSJoerg Sonnenberger  */
302180e8afSJoerg Sonnenberger 
310d5acd74SJohn Marino #include <sys/cdefs.h>
320d5acd74SJohn Marino #include <sys/queue.h>
330d5acd74SJohn Marino #include <sys/types.h>
340d5acd74SJohn Marino 
352180e8afSJoerg Sonnenberger #include <assert.h>
362180e8afSJoerg Sonnenberger #include <errno.h>
372180e8afSJoerg Sonnenberger #include <iconv.h>
380d5acd74SJohn Marino #include <limits.h>
390d5acd74SJohn Marino #include <paths.h>
400d5acd74SJohn Marino #include <stdbool.h>
410d5acd74SJohn Marino #include <stdlib.h>
420d5acd74SJohn Marino #include <string.h>
432180e8afSJoerg Sonnenberger 
442180e8afSJoerg Sonnenberger #include "../citrus/citrus_types.h"
452180e8afSJoerg Sonnenberger #include "../citrus/citrus_module.h"
462180e8afSJoerg Sonnenberger #include "../citrus/citrus_esdb.h"
472180e8afSJoerg Sonnenberger #include "../citrus/citrus_hash.h"
482180e8afSJoerg Sonnenberger #include "../citrus/citrus_iconv.h"
492180e8afSJoerg Sonnenberger 
502180e8afSJoerg Sonnenberger #define ISBADF(_h_)	(!(_h_) || (_h_) == (iconv_t)-1)
512180e8afSJoerg Sonnenberger 
520d5acd74SJohn Marino int _iconv_version = _ICONV_VERSION;
532180e8afSJoerg Sonnenberger 
540d5acd74SJohn Marino iconv_t		 _iconv_open(const char *out, const char *in,
550d5acd74SJohn Marino 		    struct _citrus_iconv *prealloc);
560d5acd74SJohn Marino 
570d5acd74SJohn Marino iconv_t
_iconv_open(const char * out,const char * in,struct _citrus_iconv * handle)580d5acd74SJohn Marino _iconv_open(const char *out, const char *in, struct _citrus_iconv *handle)
590d5acd74SJohn Marino {
600d5acd74SJohn Marino 	const char *out_slashes;
610d5acd74SJohn Marino 	char *out_noslashes;
620d5acd74SJohn Marino 	int ret;
630d5acd74SJohn Marino 
640d5acd74SJohn Marino 	/*
650d5acd74SJohn Marino 	 * Remove anything following a //, as these are options (like
660d5acd74SJohn Marino 	 * //ignore, //translate, etc) and we just don't handle them.
670d5acd74SJohn Marino 	 * This is for compatibility with software that uses these
680d5acd74SJohn Marino 	 * blindly.
690d5acd74SJohn Marino 	 */
700d5acd74SJohn Marino 	out_slashes = strstr(out, "//");
710d5acd74SJohn Marino 	if (out_slashes != NULL) {
720d5acd74SJohn Marino 		out_noslashes = strndup(out, out_slashes - out);
730d5acd74SJohn Marino 		if (out_noslashes == NULL) {
740d5acd74SJohn Marino 			errno = ENOMEM;
752180e8afSJoerg Sonnenberger 			return ((iconv_t)-1);
762180e8afSJoerg Sonnenberger 		}
770d5acd74SJohn Marino 		ret = _citrus_iconv_open(&handle, in, out_noslashes);
780d5acd74SJohn Marino 		free(out_noslashes);
790d5acd74SJohn Marino 	} else {
800d5acd74SJohn Marino 		ret = _citrus_iconv_open(&handle, in, out);
810d5acd74SJohn Marino 	}
820d5acd74SJohn Marino 
830d5acd74SJohn Marino 	if (ret) {
840d5acd74SJohn Marino 		errno = ret == ENOENT ? EINVAL : ret;
850d5acd74SJohn Marino 		return ((iconv_t)-1);
860d5acd74SJohn Marino 	}
870d5acd74SJohn Marino 
880d5acd74SJohn Marino 	handle->cv_shared->ci_discard_ilseq = strcasestr(out, "//IGNORE");
89edc9f937SJohn Marino 	handle->cv_shared->ci_ilseq_invalid = false;
900d5acd74SJohn Marino 	handle->cv_shared->ci_hooks = NULL;
912180e8afSJoerg Sonnenberger 
922180e8afSJoerg Sonnenberger 	return ((iconv_t)(void *)handle);
932180e8afSJoerg Sonnenberger }
942180e8afSJoerg Sonnenberger 
950d5acd74SJohn Marino iconv_t
iconv_open(const char * out,const char * in)960d5acd74SJohn Marino iconv_open(const char *out, const char *in)
972180e8afSJoerg Sonnenberger {
980d5acd74SJohn Marino 
990d5acd74SJohn Marino 	return (_iconv_open(out, in, NULL));
1000d5acd74SJohn Marino }
1010d5acd74SJohn Marino 
1020d5acd74SJohn Marino int
iconv_open_into(const char * out,const char * in,iconv_allocation_t * ptr)1030d5acd74SJohn Marino iconv_open_into(const char *out, const char *in, iconv_allocation_t *ptr)
1040d5acd74SJohn Marino {
1050d5acd74SJohn Marino 	struct _citrus_iconv *handle;
1060d5acd74SJohn Marino 
1070d5acd74SJohn Marino 	handle = (struct _citrus_iconv *)ptr;
1080d5acd74SJohn Marino 	return ((_iconv_open(out, in, handle) == (iconv_t)-1) ? -1 : 0);
1090d5acd74SJohn Marino }
1100d5acd74SJohn Marino 
1110d5acd74SJohn Marino int
iconv_close(iconv_t handle)1120d5acd74SJohn Marino iconv_close(iconv_t handle)
1130d5acd74SJohn Marino {
1140d5acd74SJohn Marino 
1152180e8afSJoerg Sonnenberger 	if (ISBADF(handle)) {
1162180e8afSJoerg Sonnenberger 		errno = EBADF;
1172180e8afSJoerg Sonnenberger 		return (-1);
1182180e8afSJoerg Sonnenberger 	}
1192180e8afSJoerg Sonnenberger 
1202180e8afSJoerg Sonnenberger 	_citrus_iconv_close((struct _citrus_iconv *)(void *)handle);
1212180e8afSJoerg Sonnenberger 
1222180e8afSJoerg Sonnenberger 	return (0);
1232180e8afSJoerg Sonnenberger }
1242180e8afSJoerg Sonnenberger 
1252180e8afSJoerg Sonnenberger size_t
iconv(iconv_t handle,char ** in,size_t * szin,char ** out,size_t * szout)126*0db70a6aSJohn Marino iconv(iconv_t handle, char **in, size_t *szin, char **out, size_t *szout)
1272180e8afSJoerg Sonnenberger {
1282180e8afSJoerg Sonnenberger 	size_t ret;
1290d5acd74SJohn Marino 	int err;
1302180e8afSJoerg Sonnenberger 
1312180e8afSJoerg Sonnenberger 	if (ISBADF(handle)) {
1322180e8afSJoerg Sonnenberger 		errno = EBADF;
1332180e8afSJoerg Sonnenberger 		return ((size_t)-1);
1342180e8afSJoerg Sonnenberger 	}
1352180e8afSJoerg Sonnenberger 
1360d5acd74SJohn Marino 	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
1370d5acd74SJohn Marino 	    in, szin, out, szout, 0, &ret);
1382180e8afSJoerg Sonnenberger 	if (err) {
1392180e8afSJoerg Sonnenberger 		errno = err;
1402180e8afSJoerg Sonnenberger 		ret = (size_t)-1;
1412180e8afSJoerg Sonnenberger 	}
1422180e8afSJoerg Sonnenberger 
1432180e8afSJoerg Sonnenberger 	return (ret);
1442180e8afSJoerg Sonnenberger }
1452180e8afSJoerg Sonnenberger 
1462180e8afSJoerg Sonnenberger size_t
__iconv(iconv_t handle,char ** in,size_t * szin,char ** out,size_t * szout,uint32_t flags,size_t * invalids)147*0db70a6aSJohn Marino __iconv(iconv_t handle, char **in, size_t *szin, char **out,
1480d5acd74SJohn Marino     size_t *szout, uint32_t flags, size_t *invalids)
1492180e8afSJoerg Sonnenberger {
1502180e8afSJoerg Sonnenberger 	size_t ret;
1510d5acd74SJohn Marino 	int err;
1522180e8afSJoerg Sonnenberger 
1532180e8afSJoerg Sonnenberger 	if (ISBADF(handle)) {
1542180e8afSJoerg Sonnenberger 		errno = EBADF;
1552180e8afSJoerg Sonnenberger 		return ((size_t)-1);
1562180e8afSJoerg Sonnenberger 	}
1572180e8afSJoerg Sonnenberger 
1580d5acd74SJohn Marino 	err = _citrus_iconv_convert((struct _citrus_iconv *)(void *)handle,
1590d5acd74SJohn Marino 	    in, szin, out, szout, flags, &ret);
1602180e8afSJoerg Sonnenberger 	if (invalids)
1612180e8afSJoerg Sonnenberger 		*invalids = ret;
1622180e8afSJoerg Sonnenberger 	if (err) {
1632180e8afSJoerg Sonnenberger 		errno = err;
1642180e8afSJoerg Sonnenberger 		ret = (size_t)-1;
1652180e8afSJoerg Sonnenberger 	}
1662180e8afSJoerg Sonnenberger 
1672180e8afSJoerg Sonnenberger 	return (ret);
1682180e8afSJoerg Sonnenberger }
1692180e8afSJoerg Sonnenberger 
1702180e8afSJoerg Sonnenberger int
__iconv_get_list(char *** rlist,size_t * rsz,bool sorted)1710d5acd74SJohn Marino __iconv_get_list(char ***rlist, size_t *rsz, bool sorted)
1722180e8afSJoerg Sonnenberger {
1732180e8afSJoerg Sonnenberger 	int ret;
1742180e8afSJoerg Sonnenberger 
1750d5acd74SJohn Marino 	ret = _citrus_esdb_get_list(rlist, rsz, sorted);
1762180e8afSJoerg Sonnenberger 	if (ret) {
1772180e8afSJoerg Sonnenberger 		errno = ret;
1780d5acd74SJohn Marino 		return (-1);
1792180e8afSJoerg Sonnenberger 	}
1802180e8afSJoerg Sonnenberger 
1810d5acd74SJohn Marino 	return (0);
1822180e8afSJoerg Sonnenberger }
1832180e8afSJoerg Sonnenberger 
1842180e8afSJoerg Sonnenberger void
__iconv_free_list(char ** list,size_t sz)1852180e8afSJoerg Sonnenberger __iconv_free_list(char **list, size_t sz)
1862180e8afSJoerg Sonnenberger {
1870d5acd74SJohn Marino 
1882180e8afSJoerg Sonnenberger 	_citrus_esdb_free_list(list, sz);
1892180e8afSJoerg Sonnenberger }
1902180e8afSJoerg Sonnenberger 
1910d5acd74SJohn Marino /*
1920d5acd74SJohn Marino  * GNU-compatibile non-standard interfaces.
1930d5acd74SJohn Marino  */
1940d5acd74SJohn Marino static int
qsort_helper(const void * first,const void * second)1950d5acd74SJohn Marino qsort_helper(const void *first, const void *second)
1960d5acd74SJohn Marino {
1970d5acd74SJohn Marino 	const char * const *s1;
1980d5acd74SJohn Marino 	const char * const *s2;
1990d5acd74SJohn Marino 
2000d5acd74SJohn Marino 	s1 = first;
2010d5acd74SJohn Marino 	s2 = second;
2020d5acd74SJohn Marino 	return (strcmp(*s1, *s2));
2030d5acd74SJohn Marino }
2040d5acd74SJohn Marino 
2050d5acd74SJohn Marino void
iconvlist(int (* do_one)(unsigned int,const char * const *,void *),void * data)2060d5acd74SJohn Marino iconvlist(int (*do_one) (unsigned int, const char * const *,
2070d5acd74SJohn Marino     void *), void *data)
2080d5acd74SJohn Marino {
2090d5acd74SJohn Marino 	char **list, **names;
2100d5acd74SJohn Marino 	const char * const *np;
2110d5acd74SJohn Marino 	char *curitem, *curkey, *slashpos;
2120d5acd74SJohn Marino 	size_t sz;
2130d5acd74SJohn Marino 	unsigned int i, j;
2140d5acd74SJohn Marino 
2150d5acd74SJohn Marino 	i = 0;
2160d5acd74SJohn Marino 
2170d5acd74SJohn Marino 	if (__iconv_get_list(&list, &sz, true))
2180d5acd74SJohn Marino 		list = NULL;
2190d5acd74SJohn Marino 	qsort((void *)list, sz, sizeof(char *), qsort_helper);
2200d5acd74SJohn Marino 	while (i < sz) {
2210d5acd74SJohn Marino 		j = 0;
2220d5acd74SJohn Marino 		slashpos = strchr(list[i], '/');
2230d5acd74SJohn Marino 		curkey = (char *)malloc(slashpos - list[i] + 2);
2240d5acd74SJohn Marino 		names = (char **)malloc(sz * sizeof(char *));
2250d5acd74SJohn Marino 		if ((curkey == NULL) || (names == NULL)) {
2260d5acd74SJohn Marino 			__iconv_free_list(list, sz);
2270d5acd74SJohn Marino 			return;
2280d5acd74SJohn Marino 		}
2290d5acd74SJohn Marino 		strlcpy(curkey, list[i], slashpos - list[i] + 1);
230edc9f937SJohn Marino 		names[j++] = curkey;
2310d5acd74SJohn Marino 		for (; (i < sz) && (memcmp(curkey, list[i], strlen(curkey)) == 0); i++) {
2320d5acd74SJohn Marino 			slashpos = strchr(list[i], '/');
2330d5acd74SJohn Marino 			curitem = (char *)malloc(strlen(slashpos) + 1);
2340d5acd74SJohn Marino 			if (curitem == NULL) {
2350d5acd74SJohn Marino 				__iconv_free_list(list, sz);
2360d5acd74SJohn Marino 				return;
2370d5acd74SJohn Marino 			}
2380d5acd74SJohn Marino 			strlcpy(curitem, &slashpos[1], strlen(slashpos) + 1);
2390d5acd74SJohn Marino 			if (strcmp(curkey, curitem) == 0) {
2400d5acd74SJohn Marino 				continue;
2410d5acd74SJohn Marino 			}
242edc9f937SJohn Marino 			names[j++] = curitem;
2430d5acd74SJohn Marino 		}
2440d5acd74SJohn Marino 		np = (const char * const *)names;
2450d5acd74SJohn Marino 		do_one(j, np, data);
2460d5acd74SJohn Marino 		free(names);
2470d5acd74SJohn Marino 	}
2480d5acd74SJohn Marino 
2490d5acd74SJohn Marino 	__iconv_free_list(list, sz);
2500d5acd74SJohn Marino }
2510d5acd74SJohn Marino 
2520d5acd74SJohn Marino __inline const char
iconv_canonicalize(const char * name)2530d5acd74SJohn Marino *iconv_canonicalize(const char *name)
2540d5acd74SJohn Marino {
2550d5acd74SJohn Marino 
2560d5acd74SJohn Marino 	return (_citrus_iconv_canonicalize(name));
2570d5acd74SJohn Marino }
2580d5acd74SJohn Marino 
2590d5acd74SJohn Marino int
iconvctl(iconv_t cd,int request,void * argument)2600d5acd74SJohn Marino iconvctl(iconv_t cd, int request, void *argument)
2610d5acd74SJohn Marino {
2620d5acd74SJohn Marino 	struct _citrus_iconv *cv;
2630d5acd74SJohn Marino 	struct iconv_hooks *hooks;
2640d5acd74SJohn Marino 	const char *convname;
2650d5acd74SJohn Marino 	char src[PATH_MAX], *dst;
2660d5acd74SJohn Marino 	int *i;
2670d5acd74SJohn Marino 
2680d5acd74SJohn Marino 	cv = (struct _citrus_iconv *)(void *)cd;
2690d5acd74SJohn Marino 	hooks = (struct iconv_hooks *)argument;
2700d5acd74SJohn Marino 	i = (int *)argument;
2710d5acd74SJohn Marino 
2720d5acd74SJohn Marino 	if (ISBADF(cd)) {
2730d5acd74SJohn Marino 		errno = EBADF;
2740d5acd74SJohn Marino 		return (-1);
2750d5acd74SJohn Marino 	}
2760d5acd74SJohn Marino 
2770d5acd74SJohn Marino 	switch (request) {
2780d5acd74SJohn Marino 	case ICONV_TRIVIALP:
2790d5acd74SJohn Marino 		convname = cv->cv_shared->ci_convname;
2800d5acd74SJohn Marino 		dst = strchr(convname, '/');
2810d5acd74SJohn Marino 
2820d5acd74SJohn Marino 		strlcpy(src, convname, dst - convname + 1);
2830d5acd74SJohn Marino 		dst++;
2840d5acd74SJohn Marino 		if ((convname == NULL) || (src == NULL) || (dst == NULL))
2850d5acd74SJohn Marino 			return (-1);
2860d5acd74SJohn Marino 		*i = strcmp(src, dst) == 0 ? 1 : 0;
2870d5acd74SJohn Marino 		return (0);
2880d5acd74SJohn Marino 	case ICONV_GET_TRANSLITERATE:
2890d5acd74SJohn Marino 		*i = 1;
2900d5acd74SJohn Marino 		return (0);
2910d5acd74SJohn Marino 	case ICONV_SET_TRANSLITERATE:
2920d5acd74SJohn Marino 		return  ((*i == 1) ? 0 : -1);
2930d5acd74SJohn Marino 	case ICONV_GET_DISCARD_ILSEQ:
2940d5acd74SJohn Marino 		*i = cv->cv_shared->ci_discard_ilseq ? 1 : 0;
2950d5acd74SJohn Marino 		return (0);
2960d5acd74SJohn Marino 	case ICONV_SET_DISCARD_ILSEQ:
2970d5acd74SJohn Marino 		cv->cv_shared->ci_discard_ilseq = *i;
2980d5acd74SJohn Marino 		return (0);
2990d5acd74SJohn Marino 	case ICONV_SET_HOOKS:
3000d5acd74SJohn Marino 		cv->cv_shared->ci_hooks = hooks;
3010d5acd74SJohn Marino 		return (0);
3020d5acd74SJohn Marino 	case ICONV_SET_FALLBACKS:
3030d5acd74SJohn Marino 		errno = EOPNOTSUPP;
3040d5acd74SJohn Marino 		return (-1);
3059d944071SJohn Marino 	case ICONV_GET_ILSEQ_INVALID:
3069d944071SJohn Marino 		*i = cv->cv_shared->ci_ilseq_invalid ? 1 : 0;
3079d944071SJohn Marino 		return (0);
3089d944071SJohn Marino 	case ICONV_SET_ILSEQ_INVALID:
3099d944071SJohn Marino 		cv->cv_shared->ci_ilseq_invalid = *i;
3109d944071SJohn Marino 		return (0);
3110d5acd74SJohn Marino 	default:
3120d5acd74SJohn Marino 		errno = EINVAL;
3130d5acd74SJohn Marino 		return (-1);
3140d5acd74SJohn Marino 	}
3150d5acd74SJohn Marino }
3160d5acd74SJohn Marino 
3170d5acd74SJohn Marino void
iconv_set_relocation_prefix(const char * orig_prefix __unused,const char * curr_prefix __unused)3180d5acd74SJohn Marino iconv_set_relocation_prefix(const char *orig_prefix __unused,
3190d5acd74SJohn Marino     const char *curr_prefix __unused)
3200d5acd74SJohn Marino {
3210d5acd74SJohn Marino 
3220d5acd74SJohn Marino }
323