xref: /onnv-gate/usr/src/lib/libast/common/comp/iconv.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin 
244887Schin /*
254887Schin  * Glenn Fowler
264887Schin  * AT&T Research
274887Schin  *
284887Schin  * iconv intercept
294887Schin  * minimally provides { utf*<=>bin ascii<=>ebcdic* }
304887Schin  */
314887Schin 
324887Schin #include <ast.h>
334887Schin #include <dirent.h>
344887Schin 
354887Schin #define DEBUG_TRACE		0
364887Schin #define _ICONV_LIST_PRIVATE_
374887Schin 
384887Schin #include <ccode.h>
394887Schin #include <ctype.h>
404887Schin #include <iconv.h>
414887Schin 
424887Schin #include "lclib.h"
434887Schin 
444887Schin #if !_lib_iconv_open
454887Schin 
464887Schin #define _ast_iconv_t		iconv_t
474887Schin #define _ast_iconv_f		iconv_f
484887Schin #define _ast_iconv_list_t	iconv_list_t
494887Schin #define _ast_iconv_open		iconv_open
504887Schin #define _ast_iconv		iconv
514887Schin #define _ast_iconv_close	iconv_close
524887Schin #define _ast_iconv_list		iconv_list
534887Schin #define _ast_iconv_move		iconv_move
544887Schin #define _ast_iconv_name		iconv_name
554887Schin #define _ast_iconv_write	iconv_write
564887Schin 
574887Schin #endif
584887Schin 
594887Schin #ifndef E2BIG
604887Schin #define E2BIG			ENOMEM
614887Schin #endif
624887Schin #ifndef EILSEQ
634887Schin #define EILSEQ			EIO
644887Schin #endif
654887Schin 
664887Schin #define RETURN(e,n,fn) \
674887Schin 	if (*fn && !e) e = E2BIG; \
684887Schin 	if (e) { errno = e; return (size_t)(-1); } \
694887Schin 	return n;
704887Schin 
714887Schin typedef struct Map_s
724887Schin {
734887Schin 	char*			name;
744887Schin 	const unsigned char*	map;
754887Schin 	_ast_iconv_f		fun;
764887Schin 	int			index;
774887Schin } Map_t;
784887Schin 
794887Schin typedef struct Conv_s
804887Schin {
814887Schin 	iconv_t			cvt;
824887Schin 	char*			buf;
834887Schin 	size_t			size;
844887Schin 	Map_t			from;
854887Schin 	Map_t			to;
864887Schin } Conv_t;
874887Schin 
884887Schin static Conv_t*			freelist[4];
894887Schin static int			freeindex;
904887Schin 
914887Schin static const char		name_local[] = "local";
924887Schin static const char		name_native[] = "native";
934887Schin 
944887Schin static const _ast_iconv_list_t	codes[] =
954887Schin {
964887Schin 	{
974887Schin 	"utf",
984887Schin 	"un|unicode|utf",
994887Schin 	"multibyte 8-bit unicode",
1004887Schin 	"UTF-%s",
1014887Schin 	"8",
1024887Schin 	CC_UTF,
1034887Schin 	},
1044887Schin 
1054887Schin 	{
1064887Schin 	"ume",
1074887Schin 	"um|ume|utf?(-)7",
1084887Schin 	"multibyte 7-bit unicode",
1094887Schin 	"UTF-7",
1104887Schin 	0,
1114887Schin 	CC_UME,
1124887Schin 	},
1134887Schin 
1144887Schin 	{
1154887Schin 	"euc",
1164887Schin 	"(big|euc)*",
1174887Schin 	"euc family",
1184887Schin 	0,
1194887Schin 	0,
1204887Schin 	CC_ICONV,
1214887Schin 	},
1224887Schin 
1234887Schin 	{
1244887Schin 	"dos",
1254887Schin 	"dos?(-)?(855)",
1264887Schin 	"dos code page",
1274887Schin 	"DOS855",
1284887Schin 	0,
1294887Schin 	CC_ICONV,
1304887Schin 	},
1314887Schin 
1324887Schin 	{
1334887Schin 	"ucs",
1344887Schin 	"ucs?(-)?(2)?(be)|utf-16?(be)",
1354887Schin 	"unicode runes",
1364887Schin 	"UCS-%s",
1374887Schin 	"2",
1384887Schin 	CC_UCS,
1394887Schin 	},
1404887Schin 
1414887Schin 	{
1424887Schin 	"ucs-le",
1434887Schin 	"ucs?(-)?(2)le|utf-16le",
1444887Schin 	"little endian unicode runes",
1454887Schin 	"UCS-%sLE",
1464887Schin 	"2",
1474887Schin 	CC_SCU,
1484887Schin 	},
1494887Schin 
1504887Schin 	{ 0 },
1514887Schin };
1524887Schin 
1534887Schin #if _UWIN
1544887Schin 
1554887Schin #include <ast_windows.h>
1564887Schin 
1574887Schin #ifndef CP_UCS2
1584887Schin #define CP_UCS2	0x0000
1594887Schin #endif
1604887Schin 
1614887Schin static char	_win_maps[] = "/reg/local_machine/SOFTWARE/Classes/MIME/Database/Charset";
1624887Schin 
1634887Schin /*
1644887Schin  * return the codeset index given its name or alias
1654887Schin  * the map is in the what? oh, the registry
1664887Schin  */
1674887Schin 
1684887Schin static int
_win_codeset(const char * name)1694887Schin _win_codeset(const char* name)
1704887Schin {
1714887Schin 	register char*	s;
1724887Schin 	char*		e;
1734887Schin 	int		n;
1744887Schin 	Sfio_t*		sp;
1754887Schin 	char		aka[128];
1764887Schin 	char		tmp[128];
1774887Schin 
1784887Schin #if DEBUG_TRACE
1794887Schin error(DEBUG_TRACE, "AHA#%d _win_codeset name=%s", __LINE__, name);
1804887Schin #endif
1814887Schin 	if (name == name_native)
1824887Schin 		return CP_ACP;
1834887Schin 	if (!strcasecmp(name, "utf") || !strcasecmp(name, "utf8") || !strcasecmp(name, "utf-8"))
1844887Schin 		return CP_UTF8;
1854887Schin 	if (!strcasecmp(name, "ucs") || !strcasecmp(name, "ucs2") || !strcasecmp(name, "ucs-2"))
1864887Schin 		return CP_UCS2;
1874887Schin 	if (name[0] == '0' && name[1] == 'x' && (n = strtol(name, &e, 0)) > 0 && !*e)
1884887Schin 		return n;
1894887Schin 	for (;;)
1904887Schin 	{
1914887Schin 		sfsprintf(tmp, sizeof(tmp), "%s/%s", _win_maps, name);
1924887Schin 		if (!(sp = sfopen(0, tmp, "r")))
1934887Schin 		{
1944887Schin 			s = (char*)name;
1954887Schin 			if ((s[0] == 'c' || s[0] == 'C') && (s[1] == 'p' || s[1] == 'P'))
1964887Schin 				s += 2;
1974887Schin 			if (!isdigit(s[0]))
1984887Schin 				break;
1994887Schin 			sfsprintf(tmp, sizeof(tmp), "%s/windows-%s", _win_maps, s);
2004887Schin 			if (!(sp = sfopen(0, tmp, "r")))
2014887Schin 				break;
2024887Schin 		}
2034887Schin 		for (;;)
2044887Schin 		{
2054887Schin 			if (!(s = sfgetr(sp, '\n', 0)))
2064887Schin 			{
2074887Schin 				sfclose(sp);
2084887Schin 				return -1;
2094887Schin 			}
2104887Schin 			if (!strncasecmp(s, "AliasForCharSet=", 16))
2114887Schin 			{
2124887Schin 				n = sfvalue(sp) - 17;
2134887Schin 				s += 16;
2144887Schin 				if (n >= sizeof(aka))
2154887Schin 					n = sizeof(aka) - 1;
2164887Schin 				memcpy(aka, s, n);
2174887Schin 				aka[n] = 0;
2184887Schin 				sfclose(sp);
2194887Schin 				name = (const char*)aka;
2204887Schin 				break;
2214887Schin 			}
2224887Schin 			if (!strncasecmp(s, "CodePage=", 9))
2234887Schin 			{
2244887Schin 				s += 9;
2254887Schin 				n = strtol(s, 0, 0);
2264887Schin 				sfclose(sp);
2274887Schin 				return n;
2284887Schin 			}
2294887Schin 		}
2304887Schin 	}
2314887Schin 	return -1;
2324887Schin }
2334887Schin 
2344887Schin /*
2354887Schin  * get and check the codeset indices
2364887Schin  */
2374887Schin 
2384887Schin static _ast_iconv_t
_win_iconv_open(register Conv_t * cc,const char * t,const char * f)2394887Schin _win_iconv_open(register Conv_t* cc, const char* t, const char* f)
2404887Schin {
2414887Schin #if DEBUG_TRACE
2424887Schin error(DEBUG_TRACE, "AHA#%d _win_iconv_open f=%s t=%s\n", __LINE__, f, t);
2434887Schin #endif
2444887Schin 	if ((cc->from.index = _win_codeset(f)) < 0)
2454887Schin 		return (_ast_iconv_t)(-1);
2464887Schin 	if ((cc->to.index = _win_codeset(t)) < 0)
2474887Schin 		return (_ast_iconv_t)(-1);
2484887Schin #if DEBUG_TRACE
2494887Schin error(DEBUG_TRACE, "AHA#%d _win_iconv_open f=0x%04x t=0x%04x\n", __LINE__, cc->from.index, cc->to.index);
2504887Schin #endif
2514887Schin 	return (_ast_iconv_t)cc;
2524887Schin }
2534887Schin 
2544887Schin /*
2554887Schin  * even though the indices already check out
2564887Schin  * they could still be rejected
2574887Schin  */
2584887Schin 
2594887Schin static size_t
_win_iconv(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)2604887Schin _win_iconv(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
2614887Schin {
2624887Schin 	Conv_t*	cc = (Conv_t*)cd;
2634887Schin 	size_t	un;
2644887Schin 	size_t	tz;
2654887Schin 	size_t	fz;
2664887Schin 	size_t	bz;
2674887Schin 	size_t	pz;
2684887Schin 	size_t	oz;
2694887Schin 	LPWSTR	ub;
2704887Schin 
2714887Schin #if DEBUG_TRACE
2724887Schin error(DEBUG_TRACE, "AHA#%d _win_iconv from=0x%04x to=0x%04x\n", __LINE__, cc->from.index, cc->to.index);
2734887Schin #endif
2744887Schin 	if (cc->from.index == cc->to.index)
2754887Schin 	{
2764887Schin 		/*
2774887Schin 		 * easy
2784887Schin 		 */
2794887Schin 
2804887Schin 		fz = tz = (*fn < *tn) ? *fn : *tn;
2814887Schin 		memcpy(*tb, *fb, fz);
2824887Schin 	}
2834887Schin 	else
2844887Schin 	{
2854887Schin 		ub = 0;
2864887Schin 		un = *fn;
2874887Schin 
2884887Schin 		/*
2894887Schin 		 * from => ucs-2
2904887Schin 		 */
2914887Schin 
2924887Schin 		if (cc->to.index == CP_UCS2)
2934887Schin 		{
2944887Schin 			if ((tz = MultiByteToWideChar(cc->from.index, 0, (LPCSTR)*fb, (int)*fn, (LPWSTR)*tb, *tn)) && tz <= *tn)
2954887Schin 			{
2964887Schin 				fz = *fn;
2974887Schin 				tz *= sizeof(WCHAR);
2984887Schin 			}
2994887Schin 			else
3004887Schin 			{
3014887Schin 				/*
3024887Schin 				 * target too small
3034887Schin 				 * binary search on input size to make it fit
3044887Schin 				 */
3054887Schin 
3064887Schin 				oz = 0;
3074887Schin 				pz = *fn / 2;
3084887Schin 				fz = *fn - pz;
3094887Schin 				for (;;)
3104887Schin 				{
3114887Schin 					while (!(tz = MultiByteToWideChar(cc->from.index, 0, (LPCSTR)*fb, (int)fz, (LPWSTR)*tb, 0)))
3124887Schin 						if (++fz >= *fn)
3134887Schin 							goto nope;
3144887Schin 					tz *= sizeof(WCHAR);
3154887Schin 					if (tz == *tn)
3164887Schin 						break;
3174887Schin 					if (!(pz /= 2))
3184887Schin 					{
3194887Schin 						if (!(fz = oz))
3204887Schin 							goto nope;
3214887Schin 						break;
3224887Schin 					}
3234887Schin 					if (tz > *tn)
3244887Schin 						fz -= pz;
3254887Schin 					else
3264887Schin 					{
3274887Schin 						oz = fz;
3284887Schin 						fz += pz;
3294887Schin 					}
3304887Schin 				}
3314887Schin 			}
3324887Schin 		}
3334887Schin 		else
3344887Schin 		{
3354887Schin 			if (cc->from.index == CP_UCS2)
3364887Schin 			{
3374887Schin 				un = *fn / sizeof(WCHAR);
3384887Schin 				ub = (LPWSTR)*fb;
3394887Schin 			}
3404887Schin 			else if (!(un = MultiByteToWideChar(cc->from.index, 0, (LPCSTR)*fb, (int)*fn, (LPWSTR)*tb, 0)))
3414887Schin 				goto nope;
3424887Schin 			else if (!(ub = (LPWSTR)malloc(un * sizeof(WCHAR))))
3434887Schin 				goto nope;
3444887Schin 			else if (!(un = MultiByteToWideChar(cc->from.index, 0, (LPCSTR)*fb, (int)*fn, (LPWSTR)ub, un)))
3454887Schin 				goto nope;
3464887Schin 
3474887Schin 			/*
3484887Schin 			 * ucs-2 => to
3494887Schin 			 */
3504887Schin 
3514887Schin 			if (tz = WideCharToMultiByte(cc->to.index, 0, (LPCWSTR)ub, un, *tb, *tn, 0, 0))
3524887Schin 				fz = *fn;
3534887Schin 			else
3544887Schin 			{
3554887Schin 				/*
3564887Schin 				 * target too small
3574887Schin 				 * binary search on input size to make it fit
3584887Schin 				 */
3594887Schin 
3604887Schin 				oz = 0;
3614887Schin 				pz = *fn / 2;
3624887Schin 				bz = *fn - pz;
3634887Schin 				for (;;)
3644887Schin 				{
3654887Schin 					while (!(fz = MultiByteToWideChar(cc->from.index, 0, (LPCSTR)*fb, (int)bz, (LPWSTR)ub, un)))
3664887Schin 						if (++bz > *fn)
3674887Schin 							goto nope;
3684887Schin 					if (!(tz = WideCharToMultiByte(cc->to.index, 0, (LPCWSTR)ub, fz, *tb, 0, 0, 0)))
3694887Schin 						goto nope;
3704887Schin 					if (tz == *tn)
3714887Schin 						break;
3724887Schin 					if (!(pz /= 2))
3734887Schin 					{
3744887Schin 						if (!(fz = oz))
3754887Schin 							goto nope;
3764887Schin 						break;
3774887Schin 					}
3784887Schin 					if (tz > *tn)
3794887Schin 						bz -= pz;
3804887Schin 					else
3814887Schin 					{
3824887Schin 						oz = bz;
3834887Schin 						bz += pz;
3844887Schin 					}
3854887Schin 				}
3864887Schin 				if (!(tz = WideCharToMultiByte(cc->to.index, 0, (LPCWSTR)ub, fz, *tb, tz, 0, 0)))
3874887Schin 					goto nope;
3884887Schin #if DEBUG_TRACE
3894887Schin error(DEBUG_TRACE, "AHA#%d _win_iconv *fn=%u fz=%u[%u] *tn=%u tz=%u\n", __LINE__, *fn, fz, fz * sizeof(WCHAR), *tn, tz);
3904887Schin #endif
3914887Schin #if 0
3924887Schin 				fz *= sizeof(WCHAR);
3934887Schin #endif
3944887Schin 			}
3954887Schin 			if (ub != (LPWSTR)*fb)
3964887Schin 				free(ub);
3974887Schin 		}
3984887Schin 	}
3994887Schin 	*fb += fz;
4004887Schin 	*fn -= fz;
4014887Schin 	*tb += tz;
4024887Schin 	*tn -= tz;
4034887Schin 	return fz;
4044887Schin  nope:
4054887Schin 	if (ub && ub != (LPWSTR)*fb)
4064887Schin 		free(ub);
4074887Schin 	errno = EINVAL;
4084887Schin 	return (size_t)(-1);
4094887Schin }
4104887Schin 
4114887Schin #endif
4124887Schin 
4134887Schin /*
4144887Schin  * return canonical character code set name for m
4154887Schin  * if b!=0 then canonical name placed in b of size n
4164887Schin  * <ccode.h> index returned
4174887Schin  */
4184887Schin 
4194887Schin int
_ast_iconv_name(register const char * m,register char * b,size_t n)4204887Schin _ast_iconv_name(register const char* m, register char* b, size_t n)
4214887Schin {
4224887Schin 	register const _ast_iconv_list_t*	cp;
4234887Schin 	const _ast_iconv_list_t*		bp;
4244887Schin 	register int				c;
4254887Schin 	register char*				e;
4264887Schin 	int					sub[2];
4274887Schin 	char					buf[16];
4284887Schin #if DEBUG_TRACE
4294887Schin 	char*					o;
4304887Schin #endif
4314887Schin 
4324887Schin 	if (!b)
4334887Schin 	{
4344887Schin 		b = buf;
4354887Schin 		n = sizeof(buf);
4364887Schin 	}
4374887Schin #if DEBUG_TRACE
4384887Schin 	o = b;
4394887Schin #endif
4404887Schin 	e = b + n - 1;
4414887Schin 	bp = 0;
4424887Schin 	n = 0;
4434887Schin 	cp = ccmaplist(NiL);
4444887Schin #if DEBUG_TRACE
4454887Schin if (error_info.trace < DEBUG_TRACE) sfprintf(sfstderr, "%s: debug-%d: AHA%d _ast_iconv_name m=\"%s\"\n", error_info.id, error_info.trace, __LINE__, m);
4464887Schin #endif
4474887Schin 	for (;;)
4484887Schin 	{
4494887Schin #if DEBUG_TRACE
4504887Schin if (error_info.trace < DEBUG_TRACE) sfprintf(sfstderr, "%s: debug-%d: AHA%d _ast_iconv_name n=%d bp=%p cp=%p ccode=%d name=\"%s\"\n", error_info.id, error_info.trace, __LINE__, n, bp, cp, cp->ccode, cp->name);
4514887Schin #endif
4524887Schin 		if (strgrpmatch(m, cp->match, sub, elementsof(sub) / 2, STR_MAXIMAL|STR_LEFT|STR_ICASE))
4534887Schin 		{
4544887Schin 			if (!(c = m[sub[1]]))
4554887Schin 			{
4564887Schin 				bp = cp;
4574887Schin 				break;
4584887Schin 			}
4594887Schin 			if (sub[1] > n && !isalpha(c))
4604887Schin 			{
4614887Schin 				bp = cp;
4624887Schin 				n = sub[1];
4634887Schin 			}
4644887Schin 		}
4654887Schin 		if (cp->ccode < 0)
4664887Schin 		{
4674887Schin 			if (!(++cp)->name)
4684887Schin 				break;
4694887Schin 		}
4704887Schin 		else if (!(cp = (const _ast_iconv_list_t*)ccmaplist((_ast_iconv_list_t*)cp)))
4714887Schin 			cp = codes;
4724887Schin 	}
4734887Schin 	if (cp = bp)
4744887Schin 	{
4754887Schin 		if (cp->canon)
4764887Schin 		{
4774887Schin 			if (cp->index)
4784887Schin 			{
4794887Schin 				for (m += sub[1]; *m && !isalnum(*m); m++);
4804887Schin 				if (!isdigit(*m))
4814887Schin 					m = cp->index;
4824887Schin 			}
4834887Schin 			else
4844887Schin 				m = "1";
4854887Schin 			b += sfsprintf(b, e - b, cp->canon, m);
4864887Schin 		}
4874887Schin 		else if (cp->ccode == CC_NATIVE)
4884887Schin 		{
4894887Schin 			if ((locales[AST_LC_CTYPE]->flags & LC_default) || !locales[AST_LC_CTYPE]->charset || !(m = locales[AST_LC_CTYPE]->charset->code) || streq(m, "iso8859-1"))
4904887Schin 				switch (CC_NATIVE)
4914887Schin 				{
4924887Schin 				case CC_EBCDIC:
4934887Schin 					m = (const char*)"EBCDIC";
4944887Schin 					break;
4954887Schin 				case CC_EBCDIC_I:
4964887Schin 					m = (const char*)"EBCDIC-I";
4974887Schin 					break;
4984887Schin 				case CC_EBCDIC_O:
4994887Schin 					m = (const char*)"EBCDIC-O";
5004887Schin 					break;
5014887Schin 				default:
5024887Schin 					m = (const char*)"ISO-8859-1";
5034887Schin 					break;
5044887Schin 				}
5054887Schin 			b += sfsprintf(b, e - b, "%s", m);
5064887Schin 		}
5074887Schin 		*b = 0;
5084887Schin #if DEBUG_TRACE
5094887Schin if (error_info.trace < DEBUG_TRACE) sfprintf(sfstderr, "%s: debug-%d: AHA%d _ast_iconv_name ccode=%d canon=\"%s\"\n", error_info.id, error_info.trace, __LINE__, cp->ccode, o);
5104887Schin #endif
5114887Schin 		return cp->ccode;
5124887Schin 	}
5134887Schin 	while (b < e && (c = *m++))
5144887Schin 	{
5154887Schin 		if (islower(c))
5164887Schin 			c = toupper(c);
5174887Schin 		*b++ = c;
5184887Schin 	}
5194887Schin 	*b = 0;
5204887Schin #if DEBUG_TRACE
5214887Schin if (error_info.trace < DEBUG_TRACE) sfprintf(sfstderr, "%s: debug-%d: AHA%d _ast_iconv_name ccode=%d canon=\"%s\"\n", error_info.id, error_info.trace, __LINE__, CC_ICONV, o);
5224887Schin #endif
5234887Schin 	return CC_ICONV;
5244887Schin }
5254887Schin 
5264887Schin /*
5274887Schin  * convert utf-8 to bin
5284887Schin  */
5294887Schin 
5304887Schin static size_t
utf2bin(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)5314887Schin utf2bin(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
5324887Schin {
5334887Schin 	register unsigned char*		f;
5344887Schin 	register unsigned char*		fe;
5354887Schin 	register unsigned char*		t;
5364887Schin 	register unsigned char*		te;
5374887Schin 	register unsigned char*		p;
5384887Schin 	register int			c;
5394887Schin 	register int			w;
5404887Schin 	size_t				n;
5414887Schin 	int				e;
5424887Schin 
5434887Schin 	e = 0;
5444887Schin 	f = (unsigned char*)(*fb);
5454887Schin 	fe = f + (*fn);
5464887Schin 	t = (unsigned char*)(*tb);
5474887Schin 	te = t + (*tn);
5484887Schin 	while (t < te && f < fe)
5494887Schin 	{
5504887Schin 		p = f;
5514887Schin 		c = *f++;
5524887Schin 		if (c & 0x80)
5534887Schin 		{
5544887Schin 			if (!(c & 0x40))
5554887Schin 			{
5564887Schin 				f = p;
5574887Schin 				e = EILSEQ;
5584887Schin 				break;
5594887Schin 			}
5604887Schin 			if (c & 0x20)
5614887Schin 			{
5624887Schin 				w = (c & 0x0F) << 12;
5634887Schin 				if (f >= fe)
5644887Schin 				{
5654887Schin 					f = p;
5664887Schin 					e = EINVAL;
5674887Schin 					break;
5684887Schin 				}
5694887Schin 				c = *f++;
5704887Schin 				if (c & 0x40)
5714887Schin 				{
5724887Schin 					f = p;
5734887Schin 					e = EILSEQ;
5744887Schin 					break;
5754887Schin 				}
5764887Schin 				w |= (c & 0x3F) << 6;
5774887Schin 			}
5784887Schin 			else
5794887Schin 				w = (c & 0x1F) << 6;
5804887Schin 			if (f >= fe)
5814887Schin 			{
5824887Schin 				f = p;
5834887Schin 				e = EINVAL;
5844887Schin 				break;
5854887Schin 			}
5864887Schin 			c = *f++;
5874887Schin 			w |= (c & 0x3F);
5884887Schin 		}
5894887Schin 		else
5904887Schin 			w = c;
5914887Schin 		*t++ = w;
5924887Schin 	}
5934887Schin 	*fn -= (char*)f - (*fb);
5944887Schin 	*fb = (char*)f;
5954887Schin 	*tn -= (n = (char*)t - (*tb));
5964887Schin 	*tb = (char*)t;
5974887Schin 	RETURN(e, n, fn);
5984887Schin }
5994887Schin 
6004887Schin /*
6014887Schin  * convert bin to utf-8
6024887Schin  */
6034887Schin 
6044887Schin static size_t
bin2utf(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)6054887Schin bin2utf(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
6064887Schin {
6074887Schin 	register unsigned char*		f;
6084887Schin 	register unsigned char*		fe;
6094887Schin 	register unsigned char*		t;
6104887Schin 	register unsigned char*		te;
6114887Schin 	register int			c;
6124887Schin 	wchar_t				w;
6134887Schin 	size_t				n;
6144887Schin 	int				e;
6154887Schin 
6164887Schin 	e = 0;
6174887Schin 	f = (unsigned char*)(*fb);
6184887Schin 	fe = f + (*fn);
6194887Schin 	t = (unsigned char*)(*tb);
6204887Schin 	te = t + (*tn);
6214887Schin 	while (f < fe && t < te)
6224887Schin 	{
6234887Schin 		if (!mbwide())
6244887Schin 		{
6254887Schin 			c = 1;
6264887Schin 			w = *f;
6274887Schin 		}
6284887Schin 		else if ((c = (*_ast_info.mb_towc)(&w, (char*)f, fe - f)) < 0)
6294887Schin 		{
6304887Schin 			e = EINVAL;
6314887Schin 			break;
6324887Schin 		}
6334887Schin 		else if (!c)
6344887Schin 			c = 1;
6354887Schin 		if (!(w & ~0x7F))
6364887Schin 			*t++ = w;
6374887Schin 		else
6384887Schin 		{
6394887Schin 			if (!(w & ~0x7FF))
6404887Schin 			{
6414887Schin 				if (t >= (te - 2))
6424887Schin 				{
6434887Schin 					e = E2BIG;
6444887Schin 					break;
6454887Schin 				}
6464887Schin 				*t++ = 0xC0 + (w >> 6);
6474887Schin 			}
6484887Schin 			else if (!(w & ~0xffff))
6494887Schin 			{
6504887Schin 				if (t >= (te - 3))
6514887Schin 				{
6524887Schin 					e = E2BIG;
6534887Schin 					break;
6544887Schin 				}
6554887Schin 				*t++ = 0xE0 + (w >> 12);
6564887Schin 				*t++ = 0x80 + ((w >> 6 ) & 0x3F);
6574887Schin 			}
6584887Schin 			else
6594887Schin 			{
6604887Schin 				e = EILSEQ;
6614887Schin 				break;
6624887Schin 			}
6634887Schin 			*t++ = 0x80 + (w & 0x3F);
6644887Schin 		}
6654887Schin 		f += c;
6664887Schin 	}
6674887Schin 	*fn -= (n = (char*)f - (*fb));
6684887Schin 	*fb = (char*)f;
6694887Schin 	*tn -= (char*)t - (*tb);
6704887Schin 	*tb = (char*)t;
6714887Schin 	RETURN(e, n, fn);
6724887Schin }
6734887Schin 
6744887Schin static const unsigned char	ume_D[] =
6754887Schin "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'(),-./:?!\"#$%&*;<=>@[]^_`{|} \t\n";
6764887Schin 
6774887Schin static const unsigned char	ume_M[] =
6784887Schin "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6794887Schin 
6804887Schin static unsigned char		ume_d[UCHAR_MAX+1];
6814887Schin 
6824887Schin static unsigned char		ume_m[UCHAR_MAX+1];
6834887Schin 
6844887Schin #define NOE			0xFF
6854887Schin #define UMEINIT()		(ume_d[ume_D[0]]?0:umeinit())
6864887Schin 
6874887Schin /*
6884887Schin  * initialize the ume tables
6894887Schin  */
6904887Schin 
6914887Schin static int
umeinit(void)6924887Schin umeinit(void)
6934887Schin {
6944887Schin 	register const unsigned char*	s;
6954887Schin 	register int			i;
6964887Schin 	register int			c;
6974887Schin 
6984887Schin 	if (!ume_d[ume_D[0]])
6994887Schin 	{
7004887Schin 		s = ume_D;
7014887Schin 		while (c = *s++)
7024887Schin 			ume_d[c] = 1;
7034887Schin 		memset(ume_m, NOE, sizeof(ume_m));
7044887Schin 		for (i = 0; c = ume_M[i]; i++)
7054887Schin 			ume_m[c] = i;
7064887Schin 	}
7074887Schin 	return 0;
7084887Schin }
7094887Schin 
7104887Schin /*
7114887Schin  * convert utf-7 to bin
7124887Schin  */
7134887Schin 
7144887Schin static size_t
ume2bin(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)7154887Schin ume2bin(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
7164887Schin {
7174887Schin 	register unsigned char*		f;
7184887Schin 	register unsigned char*		fe;
7194887Schin 	register unsigned char*		t;
7204887Schin 	register unsigned char*		te;
7214887Schin 	register unsigned char*		p;
7224887Schin 	register int			s;
7234887Schin 	register int			c;
7244887Schin 	register int			w;
7254887Schin 	size_t				n;
7264887Schin 	int				e;
7274887Schin 
7284887Schin 	e = 0;
7294887Schin 	UMEINIT();
7304887Schin 	f = (unsigned char*)(*fb);
7314887Schin 	fe = f + (*fn);
7324887Schin 	t = (unsigned char*)(*tb);
7334887Schin 	te = t + (*tn);
7344887Schin 	s = 0;
7354887Schin 	while (f < fe && t < te)
7364887Schin 	{
7374887Schin 		p = f;
7384887Schin 		c = *f++;
7394887Schin 		if (s)
7404887Schin 		{
7414887Schin 			if (c == '-' && s > 1)
7424887Schin 				s = 0;
7434887Schin 			else if ((w = ume_m[c]) == NOE)
7444887Schin 			{
7454887Schin 				s = 0;
7464887Schin 				*t++ = c;
7474887Schin 			}
7484887Schin 			else if (f >= (fe - 2))
7494887Schin 			{
7504887Schin 				f = p;
7514887Schin 				e = EINVAL;
7524887Schin 				break;
7534887Schin 			}
7544887Schin 			else
7554887Schin 			{
7564887Schin 				s = 2;
7574887Schin 				w = (w << 6) | ume_m[*f++];
7584887Schin 				w = (w << 6) | ume_m[*f++];
7594887Schin 				if (!(w & ~0xFF))
7604887Schin 					*t++ = w;
7614887Schin 				else if (t >= (te - 1))
7624887Schin 				{
7634887Schin 					f = p;
7644887Schin 					e = E2BIG;
7654887Schin 					break;
7664887Schin 				}
7674887Schin 				else
7684887Schin 				{
7694887Schin 					*t++ = (w >> 8) & 0xFF;
7704887Schin 					*t++ = w & 0xFF;
7714887Schin 				}
7724887Schin 			}
7734887Schin 		}
7744887Schin 		else if (c == '+')
7754887Schin 			s = 1;
7764887Schin 		else
7774887Schin 			*t++ = c;
7784887Schin 	}
7794887Schin 	*fn -= (char*)f - (*fb);
7804887Schin 	*fb = (char*)f;
7814887Schin 	*tn -= (n = (char*)t - (*tb));
7824887Schin 	*tb = (char*)t;
7834887Schin 	RETURN(e, n, fn);
7844887Schin }
7854887Schin 
7864887Schin /*
7874887Schin  * convert bin to utf-7
7884887Schin  */
7894887Schin 
7904887Schin static size_t
bin2ume(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)7914887Schin bin2ume(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
7924887Schin {
7934887Schin 	register unsigned char*		f;
7944887Schin 	register unsigned char*		fe;
7954887Schin 	register unsigned char*		t;
7964887Schin 	register unsigned char*		te;
7974887Schin 	register int			c;
7984887Schin 	register int			s;
7994887Schin 	wchar_t				w;
8004887Schin 	size_t				n;
8014887Schin 	int				e;
8024887Schin 
8034887Schin 	e = 0;
8044887Schin 	UMEINIT();
8054887Schin 	f = (unsigned char*)(*fb);
8064887Schin 	fe = f + (*fn);
8074887Schin 	t = (unsigned char*)(*tb);
8084887Schin 	te = t + (*tn);
8094887Schin 	s = 0;
8104887Schin 	while (f < fe && t < (te - s))
8114887Schin 	{
8124887Schin 		if (!mbwide())
8134887Schin 		{
8144887Schin 			c = 1;
8154887Schin 			w = *f;
8164887Schin 		}
8174887Schin 		else if ((c = (*_ast_info.mb_towc)(&w, (char*)f, fe - f)) < 0)
8184887Schin 		{
8194887Schin 			e = EINVAL;
8204887Schin 			break;
8214887Schin 		}
8224887Schin 		else if (!c)
8234887Schin 			c = 1;
8244887Schin 		if (!(w & ~0x7F) && ume_d[w])
8254887Schin 		{
8264887Schin 			if (s)
8274887Schin 			{
8284887Schin 				s = 0;
8294887Schin 				*t++ = '-';
8304887Schin 			}
8314887Schin 			*t++ = w;
8324887Schin 		}
8334887Schin 		else if (t >= (te - (4 + s)))
8344887Schin 		{
8354887Schin 			e = E2BIG;
8364887Schin 			break;
8374887Schin 		}
8384887Schin 		else
8394887Schin 		{
8404887Schin 			if (!s)
8414887Schin 			{
8424887Schin 				s = 1;
8434887Schin 				*t++ = '+';
8444887Schin 			}
8454887Schin 			*t++ = ume_M[(w >> 12) & 0x3F];
8464887Schin 			*t++ = ume_M[(w >> 6) & 0x3F];
8474887Schin 			*t++ = ume_M[w & 0x3F];
8484887Schin 		}
8494887Schin 		f += c;
8504887Schin 	}
8514887Schin 	if (s)
8524887Schin 		*t++ = '-';
8534887Schin 	*fn -= (n = (char*)f - (*fb));
8544887Schin 	*fb = (char*)f;
8554887Schin 	*tn -= (char*)t - (*tb);
8564887Schin 	*tb = (char*)t;
8574887Schin 	RETURN(e, n, fn);
8584887Schin }
8594887Schin 
8604887Schin /*
8614887Schin  * convert ucs-2 to bin with no byte swap
8624887Schin  */
8634887Schin 
8644887Schin static size_t
ucs2bin(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)8654887Schin ucs2bin(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
8664887Schin {
8674887Schin 	register unsigned char*		f;
8684887Schin 	register unsigned char*		fe;
8694887Schin 	register unsigned char*		t;
8704887Schin 	register unsigned char*		te;
8714887Schin 	register int			w;
8724887Schin 	size_t				n;
8734887Schin 	int				e;
8744887Schin 
8754887Schin 	e = 0;
8764887Schin 	f = (unsigned char*)(*fb);
8774887Schin 	fe = f + (*fn);
8784887Schin 	t = (unsigned char*)(*tb);
8794887Schin 	te = t + (*tn);
8804887Schin 	while (f < (fe - 1) && t < te)
8814887Schin 	{
8824887Schin 		w = *f++;
8834887Schin 		w = (w << 8) | *f++;
8844887Schin 		if (!(w & ~0xFF))
8854887Schin 			*t++ = w;
8864887Schin 		else if (t >= (te - 1))
8874887Schin 		{
8884887Schin 			f -= 2;
8894887Schin 			e = E2BIG;
8904887Schin 			break;
8914887Schin 		}
8924887Schin 		else
8934887Schin 		{
8944887Schin 			*t++ = (w >> 8) & 0xFF;
8954887Schin 			*t++ = w & 0xFF;
8964887Schin 		}
8974887Schin 	}
8984887Schin 	*fn -= (char*)f - (*fb);
8994887Schin 	*fb = (char*)f;
9004887Schin 	*tn -= (n = (char*)t - (*tb));
9014887Schin 	*tb = (char*)t;
9024887Schin 	RETURN(e, n, fn);
9034887Schin }
9044887Schin 
9054887Schin /*
9064887Schin  * convert bin to ucs-2 with no byte swap
9074887Schin  */
9084887Schin 
9094887Schin static size_t
bin2ucs(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)9104887Schin bin2ucs(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
9114887Schin {
9124887Schin 	register unsigned char*		f;
9134887Schin 	register unsigned char*		fe;
9144887Schin 	register unsigned char*		t;
9154887Schin 	register unsigned char*		te;
9164887Schin 	register int			c;
9174887Schin 	wchar_t				w;
9184887Schin 	size_t				n;
9194887Schin 	int				e;
9204887Schin 
9214887Schin 	e = 0;
9224887Schin 	f = (unsigned char*)(*fb);
9234887Schin 	fe = f + (*fn);
9244887Schin 	t = (unsigned char*)(*tb);
9254887Schin 	te = t + (*tn);
9264887Schin 	while (f < fe && t < (te - 1))
9274887Schin 	{
9284887Schin 		if (!mbwide())
9294887Schin 		{
9304887Schin 			c = 1;
9314887Schin 			w = *f;
9324887Schin 		}
9334887Schin 		if ((c = (*_ast_info.mb_towc)(&w, (char*)f, fe - f)) < 0)
9344887Schin 		{
9354887Schin 			e = EINVAL;
9364887Schin 			break;
9374887Schin 		}
9384887Schin 		else if (!c)
9394887Schin 			c = 1;
9404887Schin 		*t++ = (w >> 8) & 0xFF;
9414887Schin 		*t++ = w & 0xFF;
9424887Schin 		f += c;
9434887Schin 	}
9444887Schin 	*fn -= (n = (char*)f - (*fb));
9454887Schin 	*fb = (char*)f;
9464887Schin 	*tn -= (char*)t - (*tb);
9474887Schin 	*tb = (char*)t;
9484887Schin 	RETURN(e, n, fn);
9494887Schin }
9504887Schin 
9514887Schin /*
9524887Schin  * convert ucs-2 to bin with byte swap
9534887Schin  */
9544887Schin 
9554887Schin static size_t
scu2bin(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)9564887Schin scu2bin(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
9574887Schin {
9584887Schin 	register unsigned char*		f;
9594887Schin 	register unsigned char*		fe;
9604887Schin 	register unsigned char*		t;
9614887Schin 	register unsigned char*		te;
9624887Schin 	register int			w;
9634887Schin 	size_t				n;
9644887Schin 	int				e;
9654887Schin 
9664887Schin 	e = 0;
9674887Schin 	f = (unsigned char*)(*fb);
9684887Schin 	fe = f + (*fn);
9694887Schin 	t = (unsigned char*)(*tb);
9704887Schin 	te = t + (*tn);
9714887Schin 	while (f < (fe - 1) && t < te)
9724887Schin 	{
9734887Schin 		w = *f++;
9744887Schin 		w = w | (*f++ << 8);
9754887Schin 		if (!(w & ~0xFF))
9764887Schin 			*t++ = w;
9774887Schin 		else if (t >= (te - 1))
9784887Schin 		{
9794887Schin 			f -= 2;
9804887Schin 			e = E2BIG;
9814887Schin 			break;
9824887Schin 		}
9834887Schin 		else
9844887Schin 		{
9854887Schin 			*t++ = (w >> 8) & 0xFF;
9864887Schin 			*t++ = w & 0xFF;
9874887Schin 		}
9884887Schin 	}
9894887Schin 	*fn -= (char*)f - (*fb);
9904887Schin 	*fb = (char*)f;
9914887Schin 	*tn -= (n = (char*)t - (*tb));
9924887Schin 	*tb = (char*)t;
9934887Schin 	RETURN(e, n, fn);
9944887Schin }
9954887Schin 
9964887Schin /*
9974887Schin  * convert bin to ucs-2 with byte swap
9984887Schin  */
9994887Schin 
10004887Schin static size_t
bin2scu(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)10014887Schin bin2scu(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
10024887Schin {
10034887Schin 	register unsigned char*		f;
10044887Schin 	register unsigned char*		fe;
10054887Schin 	register unsigned char*		t;
10064887Schin 	register unsigned char*		te;
10074887Schin 	register int			c;
10084887Schin 	wchar_t				w;
10094887Schin 	size_t				n;
10104887Schin 	int				e;
10114887Schin 
10124887Schin 	e = 0;
10134887Schin 	f = (unsigned char*)(*fb);
10144887Schin 	fe = f + (*fn);
10154887Schin 	t = (unsigned char*)(*tb);
10164887Schin 	te = t + (*tn);
10174887Schin 	while (f < fe && t < (te - 1))
10184887Schin 	{
10194887Schin 		if (!mbwide())
10204887Schin 		{
10214887Schin 			c = 1;
10224887Schin 			w = *f;
10234887Schin 		}
10244887Schin 		else if ((c = (*_ast_info.mb_towc)(&w, (char*)f, fe - f)) < 0)
10254887Schin 		{
10264887Schin 			e = EINVAL;
10274887Schin 			break;
10284887Schin 		}
10294887Schin 		else if (!c)
10304887Schin 			c = 1;
10314887Schin 		*t++ = w & 0xFF;
10324887Schin 		*t++ = (w >> 8) & 0xFF;
10334887Schin 		f += c;
10344887Schin 	}
10354887Schin 	*fn -= (n = (char*)f - (*fb));
10364887Schin 	*fb = (char*)f;
10374887Schin 	*tn -= (char*)t - (*tb);
10384887Schin 	*tb = (char*)t;
10394887Schin 	RETURN(e, n, fn);
10404887Schin }
10414887Schin 
10424887Schin /*
10434887Schin  * open a character code conversion map from f to t
10444887Schin  */
10454887Schin 
10464887Schin _ast_iconv_t
_ast_iconv_open(const char * t,const char * f)10474887Schin _ast_iconv_open(const char* t, const char* f)
10484887Schin {
10494887Schin 	register Conv_t*	cc;
10504887Schin 	int			fc;
10514887Schin 	int			tc;
10524887Schin 	int			i;
10534887Schin 
10544887Schin 	char			fr[64];
10554887Schin 	char			to[64];
10564887Schin 
10574887Schin #if DEBUG_TRACE
10584887Schin error(DEBUG_TRACE, "AHA#%d _ast_iconv_open f=%s t=%s\n", __LINE__, f, t);
10594887Schin #endif
10604887Schin 	if (!t || !*t || *t == '-' && !*(t + 1) || !strcasecmp(t, name_local) || !strcasecmp(t, name_native))
10614887Schin 		t = name_native;
10624887Schin 	if (!f || !*f || *f == '-' && !*(f + 1) || !strcasecmp(t, name_local) || !strcasecmp(f, name_native))
10634887Schin 		f = name_native;
10644887Schin 
10654887Schin 	/*
10664887Schin 	 * the ast identify is always (iconv_t)(0)
10674887Schin 	 */
10684887Schin 
10694887Schin 	if (t == f)
10704887Schin 		return (iconv_t)(0);
10714887Schin 	fc = _ast_iconv_name(f, fr, sizeof(fr));
10724887Schin 	tc = _ast_iconv_name(t, to, sizeof(to));
10734887Schin #if DEBUG_TRACE
10744887Schin error(DEBUG_TRACE, "AHA#%d _ast_iconv_open f=%s:%s:%d t=%s:%s:%d\n", __LINE__, f, fr, fc, t, to, tc);
10754887Schin #endif
10764887Schin 	if (fc != CC_ICONV && fc == tc || streq(fr, to))
10774887Schin 		return (iconv_t)(0);
10784887Schin 
10794887Schin 	/*
10804887Schin 	 * first check the free list
10814887Schin 	 */
10824887Schin 
10834887Schin 	for (i = 0; i < elementsof(freelist); i++)
10844887Schin 		if ((cc = freelist[i]) && streq(to, cc->to.name) && streq(fr, cc->from.name))
10854887Schin 		{
10864887Schin 			freelist[i] = 0;
10874887Schin #if _lib_iconv_open
10884887Schin 			/*
10894887Schin 			 * reset the shift state if any
10904887Schin 			 */
10914887Schin 
10924887Schin 			if (cc->cvt != (iconv_t)(-1))
10934887Schin 				iconv(cc->cvt, NiL, NiL, NiL, NiL);
10944887Schin #endif
10954887Schin 			return cc;
10964887Schin 		}
10974887Schin 
10984887Schin 	/*
10994887Schin 	 * allocate a new one
11004887Schin 	 */
11014887Schin 
11024887Schin 	if (!(cc = newof(0, Conv_t, 1, strlen(to) + strlen(fr) + 2)))
11034887Schin 		return (iconv_t)(-1);
11044887Schin 	cc->to.name = (char*)(cc + 1);
11054887Schin 	cc->from.name = strcopy(cc->to.name, to) + 1;
11064887Schin 	strcpy(cc->from.name, fr);
11074887Schin 	cc->cvt = (iconv_t)(-1);
11084887Schin 
11094887Schin 	/*
11104887Schin 	 * 8 bit maps are the easiest
11114887Schin 	 */
11124887Schin 
11134887Schin 	if (fc >= 0 && tc >= 0)
11144887Schin 		cc->from.map = ccmap(fc, tc);
11154887Schin #if _lib_iconv_open
11164887Schin 	else if ((cc->cvt = iconv_open(to, fr)) != (iconv_t)(-1))
11174887Schin 		cc->from.fun = (_ast_iconv_f)iconv;
11184887Schin #endif
11194887Schin #if _UWIN
11204887Schin 	else if ((cc->cvt = _win_iconv_open(cc, to, fr)) != (_ast_iconv_t)(-1))
11214887Schin 		cc->from.fun = (_ast_iconv_f)_win_iconv;
11224887Schin #endif
11234887Schin 	else
11244887Schin 	{
11254887Schin 		switch (fc)
11264887Schin 		{
11274887Schin 		case CC_UTF:
11284887Schin 			cc->from.fun = utf2bin;
11294887Schin 			break;
11304887Schin 		case CC_UME:
11314887Schin 			cc->from.fun = ume2bin;
11324887Schin 			break;
11334887Schin 		case CC_UCS:
11344887Schin 			cc->from.fun = ucs2bin;
11354887Schin 			break;
11364887Schin 		case CC_SCU:
11374887Schin 			cc->from.fun = scu2bin;
11384887Schin 			break;
11394887Schin 		case CC_ASCII:
11404887Schin 			break;
11414887Schin 		default:
11424887Schin 			if (fc < 0)
11434887Schin 				goto nope;
11444887Schin 			cc->from.map = ccmap(fc, CC_ASCII);
11454887Schin 			break;
11464887Schin 		}
11474887Schin 		switch (tc)
11484887Schin 		{
11494887Schin 		case CC_UTF:
11504887Schin 			cc->to.fun = bin2utf;
11514887Schin 			break;
11524887Schin 		case CC_UME:
11534887Schin 			cc->to.fun = bin2ume;
11544887Schin 			break;
11554887Schin 		case CC_UCS:
11564887Schin 			cc->to.fun = bin2ucs;
11574887Schin 			break;
11584887Schin 		case CC_SCU:
11594887Schin 			cc->to.fun = bin2scu;
11604887Schin 			break;
11614887Schin 		case CC_ASCII:
11624887Schin 			break;
11634887Schin 		default:
11644887Schin 			if (tc < 0)
11654887Schin 				goto nope;
11664887Schin 			cc->to.map = ccmap(CC_ASCII, tc);
11674887Schin 			break;
11684887Schin 		}
11694887Schin 	}
11704887Schin 	return (iconv_t)cc;
11714887Schin  nope:
11724887Schin 	return (iconv_t)(-1);
11734887Schin }
11744887Schin 
11754887Schin /*
11764887Schin  * close a character code conversion map
11774887Schin  */
11784887Schin 
11794887Schin int
_ast_iconv_close(_ast_iconv_t cd)11804887Schin _ast_iconv_close(_ast_iconv_t cd)
11814887Schin {
11824887Schin 	Conv_t*	cc;
11834887Schin 	Conv_t*	oc;
11844887Schin 	int	i;
11854887Schin 	int	r = 0;
11864887Schin 
11874887Schin 	if (cd == (_ast_iconv_t)(-1))
11884887Schin 		return -1;
11894887Schin 	if (!(cc = (Conv_t*)cd))
11904887Schin 		return 0;
11914887Schin 
11924887Schin 	/*
11934887Schin 	 * add to the free list
11944887Schin 	 */
11954887Schin 
11964887Schin 	i = freeindex;
11974887Schin 	for (;;)
11984887Schin 	{
11994887Schin 		if (++ i >= elementsof(freelist))
12004887Schin 			i = 0;
12014887Schin 		if (!freelist[i])
12024887Schin 			break;
12034887Schin 		if (i == freeindex)
12044887Schin 		{
12054887Schin 			if (++ i >= elementsof(freelist))
12064887Schin 				i = 0;
12074887Schin 
12084887Schin 			/*
12094887Schin 			 * close the oldest
12104887Schin 			 */
12114887Schin 
12124887Schin 			if (oc = freelist[i])
12134887Schin 			{
12144887Schin #if _lib_iconv_open
12154887Schin 				if (oc->cvt != (iconv_t)(-1))
12164887Schin 					r = iconv_close(oc->cvt);
12174887Schin #endif
12184887Schin 				if (oc->buf)
12194887Schin 					free(oc->buf);
12204887Schin 				free(oc);
12214887Schin 			}
12224887Schin 			break;
12234887Schin 		}
12244887Schin 	}
12254887Schin 	freelist[freeindex = i] = cc;
12264887Schin 	return r;
12274887Schin }
12284887Schin 
12294887Schin /*
12304887Schin  * copy *fb size *fn to *tb size *tn
12314887Schin  * fb,fn tb,tn updated on return
12324887Schin  */
12334887Schin 
12344887Schin size_t
_ast_iconv(_ast_iconv_t cd,char ** fb,size_t * fn,char ** tb,size_t * tn)12354887Schin _ast_iconv(_ast_iconv_t cd, char** fb, size_t* fn, char** tb, size_t* tn)
12364887Schin {
12374887Schin 	Conv_t*				cc = (Conv_t*)cd;
12384887Schin 	register unsigned char*		f;
12394887Schin 	register unsigned char*		t;
12404887Schin 	register unsigned char*		e;
12414887Schin 	register const unsigned char*	m;
12424887Schin 	register size_t			n;
12434887Schin 	char*				b;
12444887Schin 	char*				tfb;
12454887Schin 	size_t				tfn;
12464887Schin 	size_t				i;
12474887Schin 
12484887Schin 	if (!fb || !*fb)
12494887Schin 	{
12504887Schin 		/* TODO: reset to the initial state */
12514887Schin 		if (!tb || !*tb)
12524887Schin 			return 0;
12534887Schin 		/* TODO: write the initial state shift sequence */
12544887Schin 		return 0;
12554887Schin 	}
12564887Schin 	n = *tn;
12574887Schin 	if (cc)
12584887Schin 	{
12594887Schin 		if (cc->from.fun)
12604887Schin 		{
12614887Schin 			if (cc->to.fun)
12624887Schin 			{
12634887Schin 				if (!cc->buf && !(cc->buf = oldof(0, char, cc->size = SF_BUFSIZE, 0)))
12644887Schin 				{
12654887Schin 					errno = ENOMEM;
12664887Schin 					return -1;
12674887Schin 				}
12684887Schin 				b = cc->buf;
12694887Schin 				i = cc->size;
12704887Schin 				tfb = *fb;
12714887Schin 				tfn = *fn;
12724887Schin 				if ((*cc->from.fun)(cc->cvt, &tfb, &tfn, &b, &i) == (size_t)(-1))
12734887Schin 					return -1;
12744887Schin 				tfn = b - cc->buf;
12754887Schin 				tfb = cc->buf;
12764887Schin 				n = (*cc->to.fun)(cc->cvt, &tfb, &tfn, tb, tn);
12774887Schin 				i = tfb - cc->buf;
12784887Schin 				*fb += i;
12794887Schin 				*fn -= i;
12804887Schin 				return n;
12814887Schin 			}
12824887Schin 			if ((*cc->from.fun)(cc->cvt, fb, fn, tb, tn) == (size_t)(-1))
12834887Schin 				return -1;
12844887Schin 			n -= *tn;
12854887Schin 			if (m = cc->to.map)
12864887Schin 			{
12874887Schin 				e = (unsigned char*)(*tb);
12884887Schin 				for (t = e - n; t < e; t++)
12894887Schin 					*t = m[*t];
12904887Schin 			}
12914887Schin 			return n;
12924887Schin 		}
12934887Schin 		else if (cc->to.fun)
12944887Schin 		{
12954887Schin 			if (!(m = cc->from.map))
12964887Schin 				return (*cc->to.fun)(cc->cvt, fb, fn, tb, tn);
12974887Schin 			if (!cc->buf && !(cc->buf = oldof(0, char, cc->size = SF_BUFSIZE, 0)))
12984887Schin 			{
12994887Schin 				errno = ENOMEM;
13004887Schin 				return -1;
13014887Schin 			}
13024887Schin 			if ((n = *fn) > cc->size)
13034887Schin 				n = cc->size;
13044887Schin 			f = (unsigned char*)(*fb);
13054887Schin 			e = f + n;
13064887Schin 			t = (unsigned char*)(b = cc->buf);
13074887Schin 			while (f < e)
13084887Schin 				*t++ = m[*f++];
13094887Schin 			n = (*cc->to.fun)(cc->cvt, &b, fn, tb, tn);
13104887Schin 			*fb += b - cc->buf;
13114887Schin 			return n;
13124887Schin 		}
13134887Schin 	}
13144887Schin 	if (n > *fn)
13154887Schin 		n = *fn;
13164887Schin 	if (cc && (m = cc->from.map))
13174887Schin 	{
13184887Schin 		f = (unsigned char*)(*fb);
13194887Schin 		e = f + n;
13204887Schin 		t = (unsigned char*)(*tb);
13214887Schin 		while (f < e)
13224887Schin 			*t++ = m[*f++];
13234887Schin 	}
13244887Schin 	else
13254887Schin 		memcpy(*tb, *fb, n);
13264887Schin 	*fb += n;
13274887Schin 	*fn -= n;
13284887Schin 	*tb += n;
13294887Schin 	*tn -= n;
13304887Schin 	return n;
13314887Schin }
13324887Schin 
13334887Schin /*
13344887Schin  * write *fb size *fn to op
13354887Schin  * fb,fn updated on return
13364887Schin  * total bytes written to op returned
13374887Schin  */
13384887Schin 
13394887Schin ssize_t
_ast_iconv_write(_ast_iconv_t cd,Sfio_t * op,char ** fb,size_t * fn,size_t * e)13404887Schin _ast_iconv_write(_ast_iconv_t cd, Sfio_t* op, char** fb, size_t* fn, size_t* e)
13414887Schin {
13424887Schin 	char*		tb;
13434887Schin 	char*		ts;
13444887Schin 	size_t		tn;
13454887Schin 	size_t		r;
13464887Schin 
13474887Schin 	r = 0;
13484887Schin 	tn = 0;
13494887Schin 	while (*fn > 0)
13504887Schin 	{
13514887Schin 		if (!(tb = (char*)sfreserve(op, -(tn + 1), SF_WRITE|SF_LOCKR)))
13524887Schin 			return r ? r : -1;
13534887Schin 		ts = tb;
13544887Schin 		tn = sfvalue(op);
13554887Schin #if DEBUG_TRACE
13564887Schin error(DEBUG_TRACE, "AHA#%d iconv_write ts=%p tn=%d", __LINE__, ts, tn);
13574887Schin 		for (;;)
13584887Schin #else
13594887Schin 		while (_ast_iconv(cd, fb, fn, &ts, &tn) == (size_t)(-1))
13604887Schin #endif
13614887Schin 		{
13624887Schin #if DEBUG_TRACE
13634887Schin 			ssize_t	_r;
13644887Schin error(DEBUG_TRACE, "AHA#%d iconv_write %d => %d `%-.*s'", __LINE__, *fn, tn, *fn, *fb);
13654887Schin 			_r = _ast_iconv(cd, fb, fn, &ts, &tn);
13664887Schin error(DEBUG_TRACE, "AHA#%d iconv_write %d => %d [%d]", __LINE__, *fn, tn, _r);
13674887Schin 			if (_r != (size_t)(-1))
13684887Schin 				break;
13694887Schin #endif
13704887Schin 			if (errno == E2BIG)
13714887Schin 				break;
13724887Schin 			if (e)
13734887Schin 				(*e)++;
13744887Schin 			if (!tn)
13754887Schin 				break;
13764887Schin 			*ts++ = *(*fb)++;
13774887Schin 			tn--;
13784887Schin 			(*fn)--;
13794887Schin 		}
13804887Schin #if DEBUG_TRACE
13814887Schin error(DEBUG_TRACE, "AHA#%d iconv_write %d", __LINE__, ts - tb);
13824887Schin #endif
13834887Schin 
13844887Schin 		sfwrite(op, tb, ts - tb);
13854887Schin 		r += ts - tb;
13864887Schin 	}
13874887Schin 	return r;
13884887Schin }
13894887Schin 
13904887Schin /*
13914887Schin  * move n bytes from ip to op
13924887Schin  */
13934887Schin 
13944887Schin ssize_t
_ast_iconv_move(_ast_iconv_t cd,Sfio_t * ip,Sfio_t * op,size_t n,size_t * e)13954887Schin _ast_iconv_move(_ast_iconv_t cd, Sfio_t* ip, Sfio_t* op, size_t n, size_t* e)
13964887Schin {
13974887Schin 	char*		fb;
13984887Schin 	char*		fs;
13994887Schin 	char*		tb;
14004887Schin 	char*		ts;
14014887Schin 	size_t		fn;
14024887Schin 	size_t		fo;
14034887Schin 	size_t		tn;
14044887Schin 	size_t		i;
14054887Schin 	ssize_t		r = 0;
14064887Schin 	int		locked;
14074887Schin 
14084887Schin 	fn = n;
14094887Schin 	for (;;)
14104887Schin 	{
14114887Schin 		if (fn != SF_UNBOUND)
14124887Schin 			fn = -((ssize_t)(fn & (((size_t)(~0))>>1)));
14134887Schin 		if (!(fb = (char*)sfreserve(ip, fn, locked = SF_LOCKR)) &&
14144887Schin 		    !(fb = (char*)sfreserve(ip, fn, locked = 0)))
14154887Schin 			break;
14164887Schin 		fs = fb;
14174887Schin 		fn = fo = sfvalue(ip);
14184887Schin 		if (!(tb = (char*)sfreserve(op, SF_UNBOUND, SF_WRITE|SF_LOCKR)))
14194887Schin 		{
14204887Schin 			sfread(ip, fb, 0);
14214887Schin 			return r ? r : -1;
14224887Schin 		}
14234887Schin 		ts = tb;
14244887Schin 		tn = sfvalue(op);
14254887Schin 		while (_ast_iconv(cd, &fs, &fn, &ts, &tn) != (size_t)(-1) && fn > 0)
14264887Schin 		{
14274887Schin 			if (tn > 0)
14284887Schin 			{
14294887Schin 				*ts++ = '_';
14304887Schin 				tn--;
14314887Schin 			}
14324887Schin 			if (e)
14334887Schin 				(*e)++;
14344887Schin 			fs++;
14354887Schin 			fn--;
14364887Schin 		}
14374887Schin 		sfwrite(op, tb, ts - tb);
14384887Schin 		r += ts - tb;
14394887Schin 		if (locked)
14404887Schin 			sfread(ip, fb, fs - fb);
14414887Schin 		else
14424887Schin 			for (i = fn; --i >= (fs - fb);)
14434887Schin 				sfungetc(ip, fb[i]);
14444887Schin 		if (n != SF_UNBOUND)
14454887Schin 		{
14464887Schin 			if (n <= (fs - fb))
14474887Schin 				break;
14484887Schin 			n -= fs - fb;
14494887Schin 		}
14504887Schin 		if (fn == fo)
14514887Schin 			fn++;
14524887Schin 	}
14534887Schin 	return r;
14544887Schin }
14554887Schin 
14564887Schin /*
14574887Schin  * iconv_list_t iterator
14584887Schin  * call with arg 0 to start
14594887Schin  * prev return value is current arg
14604887Schin  */
14614887Schin 
14624887Schin _ast_iconv_list_t*
_ast_iconv_list(_ast_iconv_list_t * cp)14634887Schin _ast_iconv_list(_ast_iconv_list_t* cp)
14644887Schin {
14654887Schin #if _UWIN
14664887Schin 	struct dirent*	ent;
14674887Schin 
14684887Schin 	if (!cp)
14694887Schin 	{
14704887Schin 		if (!(cp = newof(0, _ast_iconv_list_t, 1, 0)))
14714887Schin 			return ccmaplist(NiL);
14724887Schin 		if (!(cp->data = opendir(_win_maps)))
14734887Schin 		{
14744887Schin 			free(cp);
14754887Schin 			return ccmaplist(NiL);
14764887Schin 		}
14774887Schin 	}
14784887Schin 	if (cp->data)
14794887Schin 	{
14804887Schin 		if (ent = readdir((DIR*)cp->data))
14814887Schin 		{
14824887Schin 			cp->name = cp->match = cp->desc = (const char*)ent->d_name;
14834887Schin 			return cp;
14844887Schin 		}
14854887Schin 		closedir((DIR*)cp->data);
14864887Schin 		free(cp);
14874887Schin 		return ccmaplist(NiL);
14884887Schin 	}
14894887Schin #else
14904887Schin 	if (!cp)
14914887Schin 		return ccmaplist(NiL);
14924887Schin #endif
14934887Schin 	if (cp->ccode >= 0)
14944887Schin 		return (cp = ccmaplist(cp)) ? cp : (_ast_iconv_list_t*)codes;
14954887Schin 	return (++cp)->name ? cp : (_ast_iconv_list_t*)0;
14964887Schin }
1497