xref: /illumos-gate/usr/src/lib/libc/port/gen/iconv.c (revision 4a38094c)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57257d1b4Sraf  * Common Development and Distribution License (the "License").
67257d1b4Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217257d1b4Sraf 
227c478bd9Sstevel@tonic-gate /*
23b8d94837SIenup Sung  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277257d1b4Sraf #include "lint.h"
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
297c478bd9Sstevel@tonic-gate #include <sys/stat.h>
307c478bd9Sstevel@tonic-gate #include <sys/mman.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <stdio.h>
337c478bd9Sstevel@tonic-gate #include <dlfcn.h>
347c478bd9Sstevel@tonic-gate #include <fcntl.h>
357c478bd9Sstevel@tonic-gate #include <unistd.h>
367c478bd9Sstevel@tonic-gate #include <string.h>
377c478bd9Sstevel@tonic-gate #include <errno.h>
387c478bd9Sstevel@tonic-gate #include <sys/param.h>
397c478bd9Sstevel@tonic-gate #include <alloca.h>
407c478bd9Sstevel@tonic-gate #include "iconv.h"
417c478bd9Sstevel@tonic-gate #include "iconvP.h"
427c478bd9Sstevel@tonic-gate #include "../i18n/_loc_path.h"
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate static iconv_p	iconv_open_all(const char *, const char *, char *);
457c478bd9Sstevel@tonic-gate static iconv_p	iconv_open_private(const char *, const char *);
467c478bd9Sstevel@tonic-gate static iconv_p	iconv_search_alias(const char *, const char *, char *);
47b8d94837SIenup Sung static size_t	passthru_icv_iconv(iconv_t, const char **, size_t *, char **,
48*4a38094cSToomas Soome     size_t *);
49b8d94837SIenup Sung static void	passthru_icv_close(iconv_t);
50b8d94837SIenup Sung 
51b8d94837SIenup Sung #define	PASSTHRU_MAGIC_NUMBER	(0x53756e)
52b8d94837SIenup Sung 
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate /*
55b8d94837SIenup Sung  * These functions are mainly implemented by using a shared object and
56b8d94837SIenup Sung  * the dlopen() functions. The actual conversion algorithm for a particular
57b8d94837SIenup Sung  * conversion is implemented via a shared object as a loadable conversion
58b8d94837SIenup Sung  * module which is linked dynamically at run time.
59b8d94837SIenup Sung  *
60b8d94837SIenup Sung  * The loadable conversion module resides as either:
61b8d94837SIenup Sung  *
62b8d94837SIenup Sung  *	/usr/lib/iconv/geniconvtbl.so
63b8d94837SIenup Sung  *
64b8d94837SIenup Sung  * if the conversion is supported through a geniconvtbl code conversion
65b8d94837SIenup Sung  * binary table or as a module that directly specifies the conversion at:
66b8d94837SIenup Sung  *
677c478bd9Sstevel@tonic-gate  *	/usr/lib/iconv/fromcode%tocode.so
68b8d94837SIenup Sung  *
697c478bd9Sstevel@tonic-gate  * where fromcode is the source encoding and tocode is the target encoding.
70b8d94837SIenup Sung  * The modules have 3 entries: _icv_open(), _icv_iconv(), and _icv_close().
71b8d94837SIenup Sung  *
72b8d94837SIenup Sung  * If there is no code conversion supported and if the fromcode and the tocode
73b8d94837SIenup Sung  * are specifying the same codeset, then, the byte-by-byte, pass-through code
74b8d94837SIenup Sung  * conversion that is embedded in the libc is used instead.
75b8d94837SIenup Sung  *
76b8d94837SIenup Sung  * The following are the related PSARC cases:
77b8d94837SIenup Sung  *
78b8d94837SIenup Sung  *	PSARC/1993/153 iconv/iconv_open/iconv_close
79b8d94837SIenup Sung  *	PSARC/1999/292 Addition of geniconvtbl(1)
80b8d94837SIenup Sung  *	PSARC/2001/072 GNU gettext support
81b8d94837SIenup Sung  *	PSARC/2009/561 Pass-through iconv code conversion
82b8d94837SIenup Sung  *
83b8d94837SIenup Sung  * The PSARC/2001/072 includes the /usr/lib/iconv/alias interface.
847c478bd9Sstevel@tonic-gate  */
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate iconv_t
iconv_open(const char * tocode,const char * fromcode)877257d1b4Sraf iconv_open(const char *tocode, const char *fromcode)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate 	iconv_t	cd;
907c478bd9Sstevel@tonic-gate 	char	*ipath;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	if ((cd = malloc(sizeof (struct _iconv_info))) == NULL)
937c478bd9Sstevel@tonic-gate 		return ((iconv_t)-1);
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	/*
967c478bd9Sstevel@tonic-gate 	 * Memory for ipath is allocated/released in this function.
977c478bd9Sstevel@tonic-gate 	 */
987c478bd9Sstevel@tonic-gate 	ipath = malloc(MAXPATHLEN);
997c478bd9Sstevel@tonic-gate 	if (ipath == NULL) {
1007c478bd9Sstevel@tonic-gate 		free(cd);
1017c478bd9Sstevel@tonic-gate 		return ((iconv_t)-1);
1027c478bd9Sstevel@tonic-gate 	}
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	cd->_conv = iconv_open_all(tocode, fromcode, ipath);
1057c478bd9Sstevel@tonic-gate 	if (cd->_conv != (iconv_p)-1) {
1067c478bd9Sstevel@tonic-gate 		/* found a valid module for this conversion */
1077c478bd9Sstevel@tonic-gate 		free(ipath);
1087c478bd9Sstevel@tonic-gate 		return (cd);
1097c478bd9Sstevel@tonic-gate 	}
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	/*
1127c478bd9Sstevel@tonic-gate 	 * Now, try using the encoding name aliasing table
1137c478bd9Sstevel@tonic-gate 	 */
1147c478bd9Sstevel@tonic-gate 	cd->_conv = iconv_search_alias(tocode, fromcode, ipath);
1157c478bd9Sstevel@tonic-gate 	free(ipath);
1167c478bd9Sstevel@tonic-gate 	if (cd->_conv == (iconv_p)-1) {
117b8d94837SIenup Sung 		/*
118b8d94837SIenup Sung 		 * As the last resort, check if the tocode and the fromcode
119b8d94837SIenup Sung 		 * are referring to the same codeset name or not. If so,
120b8d94837SIenup Sung 		 * assign the embedded pass-through code conversion.
121b8d94837SIenup Sung 		 */
122b8d94837SIenup Sung 		if (strcasecmp(tocode, fromcode) != 0) {
123b8d94837SIenup Sung 			/*
124b8d94837SIenup Sung 			 * No valid conversion available. Do failure retrun
125b8d94837SIenup Sung 			 * with the errno set by iconv_search_alias().
126b8d94837SIenup Sung 			 */
127b8d94837SIenup Sung 			free(cd);
128b8d94837SIenup Sung 			return ((iconv_t)-1);
129b8d94837SIenup Sung 		}
130b8d94837SIenup Sung 
131b8d94837SIenup Sung 		/*
132b8d94837SIenup Sung 		 * For a pass-through byte-by-byte code conversion, allocate
133b8d94837SIenup Sung 		 * an internal conversion descriptor and initialize the data
134b8d94837SIenup Sung 		 * fields appropriately and we are done.
135b8d94837SIenup Sung 		 */
136b8d94837SIenup Sung 		cd->_conv = malloc(sizeof (struct _iconv_fields));
137b8d94837SIenup Sung 		if (cd->_conv == NULL) {
138b8d94837SIenup Sung 			free(cd);
139b8d94837SIenup Sung 			return ((iconv_t)-1);
140b8d94837SIenup Sung 		}
141b8d94837SIenup Sung 
142b8d94837SIenup Sung 		cd->_conv->_icv_handle = NULL;
143b8d94837SIenup Sung 		cd->_conv->_icv_iconv = passthru_icv_iconv;
144b8d94837SIenup Sung 		cd->_conv->_icv_close = passthru_icv_close;
145b8d94837SIenup Sung 		cd->_conv->_icv_state = (void *)PASSTHRU_MAGIC_NUMBER;
1467c478bd9Sstevel@tonic-gate 	}
147b8d94837SIenup Sung 
1487c478bd9Sstevel@tonic-gate 	/* found a valid module for this conversion */
1497c478bd9Sstevel@tonic-gate 	return (cd);
1507c478bd9Sstevel@tonic-gate }
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate static size_t
search_alias(char ** paddr,size_t size,const char * variant)1537c478bd9Sstevel@tonic-gate search_alias(char **paddr, size_t size, const char *variant)
1547c478bd9Sstevel@tonic-gate {
1557c478bd9Sstevel@tonic-gate 	char	*addr = *paddr;
156*4a38094cSToomas Soome 	char	*p, *sp, *q;
1577c478bd9Sstevel@tonic-gate 	size_t	var_len, can_len;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	var_len = strlen(variant);
1607c478bd9Sstevel@tonic-gate 	p = addr;
1617c478bd9Sstevel@tonic-gate 	q = addr + size;
1627c478bd9Sstevel@tonic-gate 	while (q > p) {
1637c478bd9Sstevel@tonic-gate 		if (*p == '#') {
1647c478bd9Sstevel@tonic-gate 			/*
1657c478bd9Sstevel@tonic-gate 			 * Line beginning with '#' is a comment
1667c478bd9Sstevel@tonic-gate 			 */
1677c478bd9Sstevel@tonic-gate 			p++;
1687c478bd9Sstevel@tonic-gate 			while ((q > p) && (*p++ != '\n'))
1697c478bd9Sstevel@tonic-gate 				;
1707c478bd9Sstevel@tonic-gate 			continue;
1717c478bd9Sstevel@tonic-gate 		}
1727c478bd9Sstevel@tonic-gate 		/* skip leading spaces */
1737c478bd9Sstevel@tonic-gate 		while ((q > p) &&
1747257d1b4Sraf 		    ((*p == ' ') || (*p == '\t')))
1757c478bd9Sstevel@tonic-gate 			p++;
1767c478bd9Sstevel@tonic-gate 		if (q <= p)
1777c478bd9Sstevel@tonic-gate 			break;
1787c478bd9Sstevel@tonic-gate 		sp = p;
1797c478bd9Sstevel@tonic-gate 		while ((q > p) && (*p != ' ') &&
1807257d1b4Sraf 		    (*p != '\t') && (*p != '\n'))
1817c478bd9Sstevel@tonic-gate 			p++;
1827c478bd9Sstevel@tonic-gate 		if (q <= p) {
1837c478bd9Sstevel@tonic-gate 			/* invalid entry */
1847c478bd9Sstevel@tonic-gate 			break;
1857c478bd9Sstevel@tonic-gate 		}
1867c478bd9Sstevel@tonic-gate 		if (*p == '\n') {
1877c478bd9Sstevel@tonic-gate 			/* invalid entry */
1887c478bd9Sstevel@tonic-gate 			p++;
1897c478bd9Sstevel@tonic-gate 			continue;
1907c478bd9Sstevel@tonic-gate 		}
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 		if (((p - sp) != var_len) ||
1937257d1b4Sraf 		    ((strncmp(sp, variant, var_len) != 0) &&
1947257d1b4Sraf 		    (strncasecmp(sp, variant, var_len) != 0))) {
1957c478bd9Sstevel@tonic-gate 			/*
1967c478bd9Sstevel@tonic-gate 			 * didn't match
1977c478bd9Sstevel@tonic-gate 			 */
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 			/* skip remaining chars in this line */
2007c478bd9Sstevel@tonic-gate 			p++;
2017c478bd9Sstevel@tonic-gate 			while ((q > p) && (*p++ != '\n'))
2027c478bd9Sstevel@tonic-gate 				;
2037c478bd9Sstevel@tonic-gate 			continue;
2047c478bd9Sstevel@tonic-gate 		}
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 		/* matching entry found */
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 		/* skip spaces */
2097c478bd9Sstevel@tonic-gate 		while ((q > p) &&
2107257d1b4Sraf 		    ((*p == ' ') || (*p == '\t')))
2117c478bd9Sstevel@tonic-gate 			p++;
2127c478bd9Sstevel@tonic-gate 		if (q <= p)
2137c478bd9Sstevel@tonic-gate 			break;
2147c478bd9Sstevel@tonic-gate 		sp = p;
2157c478bd9Sstevel@tonic-gate 		while ((q > p) && (*p != ' ') &&
2167257d1b4Sraf 		    (*p != '\t') && (*p != '\n'))
2177c478bd9Sstevel@tonic-gate 			p++;
2187c478bd9Sstevel@tonic-gate 		can_len = p - sp;
2197c478bd9Sstevel@tonic-gate 		if (can_len == 0) {
2207c478bd9Sstevel@tonic-gate 			while ((q > p) && (*p++ != '\n'))
2217c478bd9Sstevel@tonic-gate 				;
2227c478bd9Sstevel@tonic-gate 			continue;
2237c478bd9Sstevel@tonic-gate 		}
2247c478bd9Sstevel@tonic-gate 		*paddr = sp;
2257c478bd9Sstevel@tonic-gate 		return (can_len);
2267c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
2277c478bd9Sstevel@tonic-gate 	}
2287c478bd9Sstevel@tonic-gate 	return (0);
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate static iconv_p
iconv_open_all(const char * to,const char * from,char * ipath)2327c478bd9Sstevel@tonic-gate iconv_open_all(const char *to, const char *from, char *ipath)
2337c478bd9Sstevel@tonic-gate {
2347c478bd9Sstevel@tonic-gate 	iconv_p	cv;
2357c478bd9Sstevel@tonic-gate 	int	len;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	/*
2387c478bd9Sstevel@tonic-gate 	 * First, try using the geniconvtbl conversion, which is
2397c478bd9Sstevel@tonic-gate 	 * performed by /usr/lib/iconv/geniconvtbl.so with
2407c478bd9Sstevel@tonic-gate 	 * the conversion table file:
2417c478bd9Sstevel@tonic-gate 	 * /usr/lib/iconv/geniconvtbl/binarytables/fromcode%tocode.bt
2427c478bd9Sstevel@tonic-gate 	 *
2437c478bd9Sstevel@tonic-gate 	 * If the geniconvtbl conversion cannot be done,
2447c478bd9Sstevel@tonic-gate 	 * try the conversion by the individual shared object.
2457c478bd9Sstevel@tonic-gate 	 */
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	len = snprintf(ipath, MAXPATHLEN, _GENICONVTBL_PATH, from, to);
2487c478bd9Sstevel@tonic-gate 	if ((len <= MAXPATHLEN) && (access(ipath, R_OK) == 0)) {
2497c478bd9Sstevel@tonic-gate 		/*
2507c478bd9Sstevel@tonic-gate 		 * from%to.bt exists in the table dir
2517c478bd9Sstevel@tonic-gate 		 */
2527c478bd9Sstevel@tonic-gate 		cv = iconv_open_private(_GENICONVTBL_INT_PATH, ipath);
2537c478bd9Sstevel@tonic-gate 		if (cv != (iconv_p)-1) {
2547c478bd9Sstevel@tonic-gate 			/* found a valid module for this conversion */
2557c478bd9Sstevel@tonic-gate 			return (cv);
2567c478bd9Sstevel@tonic-gate 		}
2577c478bd9Sstevel@tonic-gate 	}
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	/* Next, try /usr/lib/iconv/from%to.so */
2607c478bd9Sstevel@tonic-gate 	len = snprintf(ipath, MAXPATHLEN, _ICONV_PATH, from, to);
2617c478bd9Sstevel@tonic-gate 	if ((len <= MAXPATHLEN) && (access(ipath, R_OK) == 0)) {
2627c478bd9Sstevel@tonic-gate 		/*
2637c478bd9Sstevel@tonic-gate 		 * /usr/lib/iconv/from%to.so exists
2647c478bd9Sstevel@tonic-gate 		 * errno will be set by iconv_open_private on error
2657c478bd9Sstevel@tonic-gate 		 */
2667c478bd9Sstevel@tonic-gate 		return (iconv_open_private(ipath, NULL));
2677c478bd9Sstevel@tonic-gate 	}
2687c478bd9Sstevel@tonic-gate 	/* no valid module for this conversion found */
2697c478bd9Sstevel@tonic-gate 	errno = EINVAL;
2707c478bd9Sstevel@tonic-gate 	return ((iconv_p)-1);
2717c478bd9Sstevel@tonic-gate }
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate static iconv_p
iconv_search_alias(const char * tocode,const char * fromcode,char * ipath)2747c478bd9Sstevel@tonic-gate iconv_search_alias(const char *tocode, const char *fromcode, char *ipath)
2757c478bd9Sstevel@tonic-gate {
2767c478bd9Sstevel@tonic-gate 	char	*p;
2777c478bd9Sstevel@tonic-gate 	char	*to_canonical, *from_canonical;
2787c478bd9Sstevel@tonic-gate 	size_t	tolen, fromlen;
2797c478bd9Sstevel@tonic-gate 	iconv_p	cv;
2807c478bd9Sstevel@tonic-gate 	int	fd;
2817c478bd9Sstevel@tonic-gate 	struct stat64	statbuf;
2827c478bd9Sstevel@tonic-gate 	caddr_t	addr;
2837c478bd9Sstevel@tonic-gate 	size_t	buflen;
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	fd = open(_ENCODING_ALIAS_PATH, O_RDONLY);
2867c478bd9Sstevel@tonic-gate 	if (fd == -1) {
2877c478bd9Sstevel@tonic-gate 		/*
2887c478bd9Sstevel@tonic-gate 		 * if no alias file found,
2897c478bd9Sstevel@tonic-gate 		 * errno will be set to EINVAL.
2907c478bd9Sstevel@tonic-gate 		 */
2917c478bd9Sstevel@tonic-gate 		errno = EINVAL;
2927c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
2937c478bd9Sstevel@tonic-gate 	}
2947c478bd9Sstevel@tonic-gate 	if (fstat64(fd, &statbuf) == -1) {
2957c478bd9Sstevel@tonic-gate 		(void) close(fd);
2967c478bd9Sstevel@tonic-gate 		/* use errno set by fstat64 */
2977c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
2987c478bd9Sstevel@tonic-gate 	}
2997c478bd9Sstevel@tonic-gate 	buflen = (size_t)statbuf.st_size;
3007c478bd9Sstevel@tonic-gate 	addr = mmap(NULL, buflen, PROT_READ, MAP_SHARED, fd, 0);
3017c478bd9Sstevel@tonic-gate 	(void) close(fd);
3027c478bd9Sstevel@tonic-gate 	if (addr == MAP_FAILED) {
3037c478bd9Sstevel@tonic-gate 		/* use errno set by mmap */
3047c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
3057c478bd9Sstevel@tonic-gate 	}
3067c478bd9Sstevel@tonic-gate 	p = (char *)addr;
3077c478bd9Sstevel@tonic-gate 	tolen = search_alias(&p, buflen, tocode);
3087c478bd9Sstevel@tonic-gate 	if (tolen) {
3097c478bd9Sstevel@tonic-gate 		to_canonical = alloca(tolen + 1);
3107c478bd9Sstevel@tonic-gate 		(void) memcpy(to_canonical, p, tolen);
3117c478bd9Sstevel@tonic-gate 		to_canonical[tolen] = '\0';
3127c478bd9Sstevel@tonic-gate 	} else {
3137c478bd9Sstevel@tonic-gate 		to_canonical = (char *)tocode;
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 	p = (char *)addr;
3167c478bd9Sstevel@tonic-gate 	fromlen = search_alias(&p, buflen, fromcode);
3177c478bd9Sstevel@tonic-gate 	if (fromlen) {
3187c478bd9Sstevel@tonic-gate 		from_canonical = alloca(fromlen + 1);
3197c478bd9Sstevel@tonic-gate 		(void) memcpy(from_canonical, p, fromlen);
3207c478bd9Sstevel@tonic-gate 		from_canonical[fromlen] = '\0';
3217c478bd9Sstevel@tonic-gate 	} else {
3227c478bd9Sstevel@tonic-gate 		from_canonical = (char *)fromcode;
3237c478bd9Sstevel@tonic-gate 	}
3247c478bd9Sstevel@tonic-gate 	(void) munmap(addr, buflen);
3257c478bd9Sstevel@tonic-gate 	if (tolen == 0 && fromlen == 0) {
3267c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3277c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
3287c478bd9Sstevel@tonic-gate 	}
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 	cv = iconv_open_all(to_canonical, from_canonical, ipath);
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	/* errno set by iconv_open_all on error */
3337c478bd9Sstevel@tonic-gate 	return (cv);
3347c478bd9Sstevel@tonic-gate }
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate static iconv_p
iconv_open_private(const char * lib,const char * tbl)3377c478bd9Sstevel@tonic-gate iconv_open_private(const char *lib, const char *tbl)
3387c478bd9Sstevel@tonic-gate {
3397c478bd9Sstevel@tonic-gate 	iconv_t (*fptr)(const char *);
3407c478bd9Sstevel@tonic-gate 	iconv_p cdpath;
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 	if ((cdpath = malloc(sizeof (struct _iconv_fields))) == NULL)
3437c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	if ((cdpath->_icv_handle = dlopen(lib, RTLD_LAZY)) == 0) {
3467c478bd9Sstevel@tonic-gate 		free(cdpath);
3477c478bd9Sstevel@tonic-gate 		/* dlopen does not define error no */
3487c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3497c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
3507c478bd9Sstevel@tonic-gate 	}
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	/* gets address of _icv_open */
3537c478bd9Sstevel@tonic-gate 	if ((fptr = (iconv_t(*)(const char *))dlsym(cdpath->_icv_handle,
3547257d1b4Sraf 	    "_icv_open")) == NULL) {
3557c478bd9Sstevel@tonic-gate 		(void) dlclose(cdpath->_icv_handle);
3567c478bd9Sstevel@tonic-gate 		free(cdpath);
3577c478bd9Sstevel@tonic-gate 		/* dlsym does not define errno */
3587c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3597c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
3607c478bd9Sstevel@tonic-gate 	}
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	/*
3637c478bd9Sstevel@tonic-gate 	 * gets address of _icv_iconv in the loadable conversion module
3647c478bd9Sstevel@tonic-gate 	 * and stores it in cdpath->_icv_iconv
3657c478bd9Sstevel@tonic-gate 	 */
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	if ((cdpath->_icv_iconv = (size_t(*)(iconv_t, const char **,
3687257d1b4Sraf 	    size_t *, char **, size_t *))dlsym(cdpath->_icv_handle,
3697257d1b4Sraf 	    "_icv_iconv")) == NULL) {
3707c478bd9Sstevel@tonic-gate 		(void) dlclose(cdpath->_icv_handle);
3717c478bd9Sstevel@tonic-gate 		free(cdpath);
3727c478bd9Sstevel@tonic-gate 		/* dlsym does not define errno */
3737c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3747c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
3757c478bd9Sstevel@tonic-gate 	}
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	/*
3787c478bd9Sstevel@tonic-gate 	 * gets address of _icv_close in the loadable conversion module
3797c478bd9Sstevel@tonic-gate 	 * and stores it in cd->_icv_close
3807c478bd9Sstevel@tonic-gate 	 */
3817c478bd9Sstevel@tonic-gate 	if ((cdpath->_icv_close = (void(*)(iconv_t))dlsym(cdpath->_icv_handle,
3827257d1b4Sraf 	    "_icv_close")) == NULL) {
3837c478bd9Sstevel@tonic-gate 		(void) dlclose(cdpath->_icv_handle);
3847c478bd9Sstevel@tonic-gate 		free(cdpath);
3857c478bd9Sstevel@tonic-gate 		/* dlsym does not define errno */
3867c478bd9Sstevel@tonic-gate 		errno = EINVAL;
3877c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
3887c478bd9Sstevel@tonic-gate 	}
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	/*
3917c478bd9Sstevel@tonic-gate 	 * initialize the state of the actual _icv_iconv conversion routine
3927c478bd9Sstevel@tonic-gate 	 * For the normal iconv module, NULL will be passed as an argument
3937257d1b4Sraf 	 * although the iconv_open() of the module won't use that.
3947c478bd9Sstevel@tonic-gate 	 */
3957c478bd9Sstevel@tonic-gate 	cdpath->_icv_state = (void *)(*fptr)(tbl);
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	if (cdpath->_icv_state == (struct _icv_state *)-1) {
3987c478bd9Sstevel@tonic-gate 		(void) dlclose(cdpath->_icv_handle);
3997c478bd9Sstevel@tonic-gate 		free(cdpath);
4007c478bd9Sstevel@tonic-gate 		/* this module does not satisfy this conversion */
4017c478bd9Sstevel@tonic-gate 		errno = EINVAL;
4027c478bd9Sstevel@tonic-gate 		return ((iconv_p)-1);
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	return (cdpath);
4067c478bd9Sstevel@tonic-gate }
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate int
iconv_close(iconv_t cd)4097257d1b4Sraf iconv_close(iconv_t cd)
4107c478bd9Sstevel@tonic-gate {
4117c478bd9Sstevel@tonic-gate 	if (cd == NULL) {
4127c478bd9Sstevel@tonic-gate 		errno = EBADF;
4137c478bd9Sstevel@tonic-gate 		return (-1);
4147c478bd9Sstevel@tonic-gate 	}
4157c478bd9Sstevel@tonic-gate 	(*(cd->_conv)->_icv_close)(cd->_conv->_icv_state);
416b8d94837SIenup Sung 	if (cd->_conv->_icv_handle != NULL)
417b8d94837SIenup Sung 		(void) dlclose(cd->_conv->_icv_handle);
4187c478bd9Sstevel@tonic-gate 	free(cd->_conv);
4197c478bd9Sstevel@tonic-gate 	free(cd);
4207c478bd9Sstevel@tonic-gate 	return (0);
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate 
423b8d94837SIenup Sung /*
424b8d94837SIenup Sung  * To have minimal performance impact to the existing run-time behavior,
425b8d94837SIenup Sung  * we supply a dummy passthru_icv_close() that will just return.
426b8d94837SIenup Sung  */
427b8d94837SIenup Sung static void
passthru_icv_close(iconv_t cd __unused)428*4a38094cSToomas Soome passthru_icv_close(iconv_t cd __unused)
429b8d94837SIenup Sung {
430b8d94837SIenup Sung }
431b8d94837SIenup Sung 
4327c478bd9Sstevel@tonic-gate size_t
iconv(iconv_t cd,const char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft)4337257d1b4Sraf iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft,
434*4a38094cSToomas Soome     char **outbuf, size_t *outbytesleft)
4357c478bd9Sstevel@tonic-gate {
4367c478bd9Sstevel@tonic-gate 	/* check if cd is valid */
4375f7cdf77SKeith M Wesolowski 	if (cd == NULL || cd == (iconv_t)-1) {
4387c478bd9Sstevel@tonic-gate 		errno = EBADF;
4397c478bd9Sstevel@tonic-gate 		return ((size_t)-1);
4407c478bd9Sstevel@tonic-gate 	}
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	/* direct conversion */
4437c478bd9Sstevel@tonic-gate 	return ((*(cd->_conv)->_icv_iconv)(cd->_conv->_icv_state,
4447257d1b4Sraf 	    inbuf, inbytesleft, outbuf, outbytesleft));
4457c478bd9Sstevel@tonic-gate }
446b8d94837SIenup Sung 
447b8d94837SIenup Sung static size_t
passthru_icv_iconv(iconv_t cd,const char ** inbuf,size_t * inbufleft,char ** outbuf,size_t * outbufleft)448b8d94837SIenup Sung passthru_icv_iconv(iconv_t cd, const char **inbuf, size_t *inbufleft,
449*4a38094cSToomas Soome     char **outbuf, size_t *outbufleft)
450b8d94837SIenup Sung {
451b8d94837SIenup Sung 	size_t ibl;
452b8d94837SIenup Sung 	size_t obl;
453b8d94837SIenup Sung 	size_t len;
454b8d94837SIenup Sung 	size_t ret_val;
455b8d94837SIenup Sung 
456b8d94837SIenup Sung 	/* Check if the conversion descriptor is a valid one. */
457b8d94837SIenup Sung 	if (cd != (iconv_t)PASSTHRU_MAGIC_NUMBER) {
458b8d94837SIenup Sung 		errno = EBADF;
459b8d94837SIenup Sung 		return ((size_t)-1);
460b8d94837SIenup Sung 	}
461b8d94837SIenup Sung 
462b8d94837SIenup Sung 	/* For any state reset request, return success. */
463b8d94837SIenup Sung 	if (inbuf == NULL || *inbuf == NULL)
464b8d94837SIenup Sung 		return (0);
465b8d94837SIenup Sung 
466b8d94837SIenup Sung 	/*
467b8d94837SIenup Sung 	 * Initialize internally used variables for a better performance
468b8d94837SIenup Sung 	 * and prepare for a couple of the return values before the actual
469b8d94837SIenup Sung 	 * copying of the bytes.
470b8d94837SIenup Sung 	 */
471b8d94837SIenup Sung 	ibl = *inbufleft;
472b8d94837SIenup Sung 	obl = *outbufleft;
473b8d94837SIenup Sung 
474b8d94837SIenup Sung 	if (ibl > obl) {
475b8d94837SIenup Sung 		len = obl;
476b8d94837SIenup Sung 		errno = E2BIG;
477b8d94837SIenup Sung 		ret_val = (size_t)-1;
478b8d94837SIenup Sung 	} else {
479b8d94837SIenup Sung 		len = ibl;
480b8d94837SIenup Sung 		ret_val = 0;
481b8d94837SIenup Sung 	}
482b8d94837SIenup Sung 
483b8d94837SIenup Sung 	/*
484b8d94837SIenup Sung 	 * Do the copy using memmove(). There are no EILSEQ or EINVAL
485b8d94837SIenup Sung 	 * checkings since this is a simple copying.
486b8d94837SIenup Sung 	 */
487b8d94837SIenup Sung 	(void) memmove((void *)*outbuf, (const void *)*inbuf, len);
488b8d94837SIenup Sung 
489b8d94837SIenup Sung 	/* Update the return values related to the buffers then do return. */
490b8d94837SIenup Sung 	*inbuf = *inbuf + len;
491b8d94837SIenup Sung 	*outbuf = *outbuf + len;
492b8d94837SIenup Sung 	*inbufleft = ibl - len;
493b8d94837SIenup Sung 	*outbufleft = obl - len;
494b8d94837SIenup Sung 
495b8d94837SIenup Sung 	return (ret_val);
496b8d94837SIenup Sung }
497