src/stdlib/SDL_iconv.c
author Sam Lantinga
Wed, 04 Jul 2007 04:27:47 +0000
branchSDL-1.2
changeset 3997 6a4f3a32c2e6
parent 3987 00486a9c2893
child 3998 098ac044cd2f
permissions -rw-r--r--
Fixed bug #349

Solaris doesn't support the LATIN1 character set alias.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* This file contains portable iconv functions for SDL */
    25 
    26 #include "SDL_stdinc.h"
    27 #include "SDL_endian.h"
    28 
    29 #ifdef HAVE_ICONV
    30 
    31 /* Depending on which standard the iconv() was implemented with,
    32    iconv() may or may not use const char ** for the inbuf param.
    33    If we get this wrong, it's just a warning, so no big deal.
    34 */
    35 #if defined(_XGP6) || \
    36     defined(__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
    37 #define ICONV_INBUF_NONCONST
    38 #endif
    39 
    40 #include <errno.h>
    41 
    42 size_t SDL_iconv(SDL_iconv_t cd,
    43                  const char **inbuf, size_t *inbytesleft,
    44                  char **outbuf, size_t *outbytesleft)
    45 {
    46 	size_t retCode;
    47 #ifdef ICONV_INBUF_NONCONST
    48 	retCode = iconv(cd, (char **)inbuf, inbytesleft, outbuf, outbytesleft);
    49 #else
    50 	retCode = iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft);
    51 #endif
    52 	if ( retCode == (size_t)-1 ) {
    53 		switch(errno) {
    54 		    case E2BIG:
    55 			return SDL_ICONV_E2BIG;
    56 		    case EILSEQ:
    57 			return SDL_ICONV_EILSEQ;
    58 		    case EINVAL:
    59 			return SDL_ICONV_EINVAL;
    60 		    default:
    61 			return SDL_ICONV_ERROR;
    62 		}
    63 	}
    64 	return retCode;
    65 }
    66 
    67 #else
    68 
    69 /* Lots of useful information on Unicode at:
    70 	http://www.cl.cam.ac.uk/~mgk25/unicode.html
    71 */
    72 
    73 #define UNICODE_BOM	0xFEFF
    74 
    75 #define UNKNOWN_ASCII	'?'
    76 #define UNKNOWN_UNICODE	0xFFFD
    77 
    78 enum {
    79 	ENCODING_UNKNOWN,
    80 	ENCODING_ASCII,
    81 	ENCODING_LATIN1,
    82 	ENCODING_UTF8,
    83 	ENCODING_UTF16,		/* Needs byte order marker */
    84 	ENCODING_UTF16BE,
    85 	ENCODING_UTF16LE,
    86 	ENCODING_UTF32,		/* Needs byte order marker */
    87 	ENCODING_UTF32BE,
    88 	ENCODING_UTF32LE,
    89 	ENCODING_UCS2,		/* Native byte order assumed */
    90 	ENCODING_UCS4,		/* Native byte order assumed */
    91 };
    92 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
    93 #define ENCODING_UTF16NATIVE	ENCODING_UTF16BE
    94 #define ENCODING_UTF32NATIVE	ENCODING_UTF32BE
    95 #else
    96 #define ENCODING_UTF16NATIVE	ENCODING_UTF16LE
    97 #define ENCODING_UTF32NATIVE	ENCODING_UTF32LE
    98 #endif
    99 
   100 struct _SDL_iconv_t
   101 {
   102 	int src_fmt;
   103 	int dst_fmt;
   104 };
   105 
   106 static struct {
   107 	const char *name;
   108 	int format;
   109 } encodings[] = {
   110 	{ "646",	ENCODING_ASCII },
   111 	{ "ASCII",	ENCODING_ASCII },
   112 	{ "US-ASCII",	ENCODING_ASCII },
   113 	{ "LATIN1",	ENCODING_LATIN1 },
   114 	{ "8859-1",	ENCODING_LATIN1 },
   115 	{ "ISO-8859-1",	ENCODING_LATIN1 },
   116 	{ "UTF8",	ENCODING_UTF8 },
   117 	{ "UTF-8",	ENCODING_UTF8 },
   118 	{ "UTF16",	ENCODING_UTF16 },
   119 	{ "UTF-16",	ENCODING_UTF16 },
   120 	{ "UTF16BE",	ENCODING_UTF16BE },
   121 	{ "UTF-16BE",	ENCODING_UTF16BE },
   122 	{ "UTF16LE",	ENCODING_UTF16LE },
   123 	{ "UTF-16LE",	ENCODING_UTF16LE },
   124 	{ "UTF32",	ENCODING_UTF32 },
   125 	{ "UTF-32",	ENCODING_UTF32 },
   126 	{ "UTF32BE",	ENCODING_UTF32BE },
   127 	{ "UTF-32BE",	ENCODING_UTF32BE },
   128 	{ "UTF32LE",	ENCODING_UTF32LE },
   129 	{ "UTF-32LE",	ENCODING_UTF32LE },
   130 	{ "UCS2",	ENCODING_UCS2 },
   131 	{ "UCS-2",	ENCODING_UCS2 },
   132 	{ "UCS4",	ENCODING_UCS4 },
   133 	{ "UCS-4",	ENCODING_UCS4 },
   134 };
   135 
   136 SDL_iconv_t SDL_iconv_open(const char *tocode, const char *fromcode)
   137 {
   138 	int src_fmt = ENCODING_UNKNOWN;
   139 	int dst_fmt = ENCODING_UNKNOWN;
   140 	int i;
   141 
   142 	for ( i = 0; i < SDL_arraysize(encodings); ++i ) {
   143 		if ( SDL_strcasecmp(fromcode, encodings[i].name) == 0 ) {
   144 			src_fmt = encodings[i].format;
   145 			if ( dst_fmt != ENCODING_UNKNOWN ) {
   146 				break;
   147 			}
   148 		}
   149 		if ( SDL_strcasecmp(tocode, encodings[i].name) == 0 ) {
   150 			dst_fmt = encodings[i].format;
   151 			if ( src_fmt != ENCODING_UNKNOWN ) {
   152 				break;
   153 			}
   154 		}
   155 	}
   156 	if ( src_fmt != ENCODING_UNKNOWN && dst_fmt != ENCODING_UNKNOWN ) {
   157 		SDL_iconv_t cd = (SDL_iconv_t)SDL_malloc(sizeof(*cd));
   158 		if ( cd ) {
   159 			cd->src_fmt = src_fmt;
   160 			cd->dst_fmt = dst_fmt;
   161 			return cd;
   162 		}
   163 	}
   164 	return (SDL_iconv_t)-1;
   165 }
   166 
   167 size_t SDL_iconv(SDL_iconv_t cd,
   168                  const char **inbuf, size_t *inbytesleft,
   169                  char **outbuf, size_t *outbytesleft)
   170 {
   171 	/* For simplicity, we'll convert everything to and from UCS-4 */
   172 	const char *src;
   173 	char *dst;
   174 	size_t srclen, dstlen;
   175 	Uint32 ch = 0;
   176 	size_t total;
   177 
   178 	if ( !inbuf || !*inbuf ) {
   179 		/* Reset the context */
   180 		return 0;
   181 	}
   182 	if ( !outbuf || !*outbuf || !outbytesleft || !*outbytesleft ) {
   183 		return SDL_ICONV_E2BIG;
   184 	}
   185 	src = *inbuf;
   186 	srclen = (inbytesleft ? *inbytesleft : 0);
   187 	dst = *outbuf;
   188 	dstlen = *outbytesleft;
   189 
   190 	switch ( cd->src_fmt ) {
   191 	    case ENCODING_UTF16:
   192 		/* Scan for a byte order marker */
   193 		{
   194 			Uint8 *p = (Uint8 *)src;
   195 			size_t n = srclen / 2;
   196 			while ( n ) {
   197 				if ( p[0] == 0xFF && p[1] == 0xFE ) {
   198 					cd->src_fmt = ENCODING_UTF16BE;
   199 					break;
   200 				} else if ( p[0] == 0xFE && p[1] == 0xFF ) {
   201 					cd->src_fmt = ENCODING_UTF16LE;
   202 					break;
   203 				}
   204 				p += 2;
   205 				--n;
   206 			}
   207 			if ( n == 0 ) {
   208 				/* We can't tell, default to host order */
   209 				cd->src_fmt = ENCODING_UTF16NATIVE;
   210 			}
   211 		}
   212 		break;
   213 	    case ENCODING_UTF32:
   214 		/* Scan for a byte order marker */
   215 		{
   216 			Uint8 *p = (Uint8 *)src;
   217 			size_t n = srclen / 4;
   218 			while ( n ) {
   219 				if ( p[0] == 0xFF && p[1] == 0xFE &&
   220 				     p[2] == 0x00 && p[3] == 0x00 ) {
   221 					cd->src_fmt = ENCODING_UTF32BE;
   222 					break;
   223 				} else if ( p[0] == 0x00 && p[1] == 0x00 &&
   224 				            p[2] == 0xFE && p[3] == 0xFF ) {
   225 					cd->src_fmt = ENCODING_UTF32LE;
   226 					break;
   227 				}
   228 				p += 4;
   229 				--n;
   230 			}
   231 			if ( n == 0 ) {
   232 				/* We can't tell, default to host order */
   233 				cd->src_fmt = ENCODING_UTF32NATIVE;
   234 			}
   235 		}
   236 		break;
   237 	}
   238 
   239 	switch ( cd->dst_fmt ) {
   240 	    case ENCODING_UTF16:
   241 		/* Default to host order, need to add byte order marker */
   242 		if ( dstlen < 2 ) {
   243 			return SDL_ICONV_E2BIG;
   244 		}
   245 		*(Uint16 *)dst = UNICODE_BOM;
   246 		dst += 2;
   247 		dstlen -= 2;
   248 		cd->dst_fmt = ENCODING_UTF16NATIVE;
   249 		break;
   250 	    case ENCODING_UTF32:
   251 		/* Default to host order, need to add byte order marker */
   252 		if ( dstlen < 4 ) {
   253 			return SDL_ICONV_E2BIG;
   254 		}
   255 		*(Uint32 *)dst = UNICODE_BOM;
   256 		dst += 4;
   257 		dstlen -= 4;
   258 		cd->dst_fmt = ENCODING_UTF32NATIVE;
   259 		break;
   260 	}
   261 
   262 	total = 0;
   263 	while ( srclen > 0 ) {
   264 		/* Decode a character */
   265 		switch ( cd->src_fmt ) {
   266 		    case ENCODING_ASCII:
   267 			{
   268 				Uint8 *p = (Uint8 *)src;
   269 				ch = (Uint32)(p[0] & 0x7F);
   270 				++src;
   271 				--srclen;
   272 			}
   273 			break;
   274 		    case ENCODING_LATIN1:
   275 			{
   276 				Uint8 *p = (Uint8 *)src;
   277 				ch = (Uint32)p[0];
   278 				++src;
   279 				--srclen;
   280 			}
   281 			break;
   282 		    case ENCODING_UTF8: /* RFC 3629 */
   283 			{
   284 				Uint8 *p = (Uint8 *)src;
   285 				size_t left = 0;
   286 				SDL_bool overlong = SDL_FALSE;
   287 				if ( p[0] >= 0xFC ) {
   288 					if ( (p[0] & 0xFE) != 0xFC ) {
   289 						/* Skip illegal sequences
   290 						return SDL_ICONV_EILSEQ;
   291 						*/
   292 						ch = UNKNOWN_UNICODE;
   293 					} else {
   294 						if ( p[0] == 0xFC ) {
   295 							overlong = SDL_TRUE;
   296 						}
   297 						ch = (Uint32)(p[0] & 0x01);
   298 						left = 5;
   299 					}
   300 				} else if ( p[0] >= 0xF8 ) {
   301 					if ( (p[0] & 0xFC) != 0xF8 ) {
   302 						/* Skip illegal sequences
   303 						return SDL_ICONV_EILSEQ;
   304 						*/
   305 						ch = UNKNOWN_UNICODE;
   306 					} else {
   307 						if ( p[0] == 0xF8 ) {
   308 							overlong = SDL_TRUE;
   309 						}
   310 						ch = (Uint32)(p[0] & 0x03);
   311 						left = 4;
   312 					}
   313 				} else if ( p[0] >= 0xF0 ) {
   314 					if ( (p[0] & 0xF8) != 0xF0 ) {
   315 						/* Skip illegal sequences
   316 						return SDL_ICONV_EILSEQ;
   317 						*/
   318 						ch = UNKNOWN_UNICODE;
   319 					} else {
   320 						if ( p[0] == 0xF0 ) {
   321 							overlong = SDL_TRUE;
   322 						}
   323 						ch = (Uint32)(p[0] & 0x07);
   324 						left = 3;
   325 					}
   326 				} else if ( p[0] >= 0xE0 ) {
   327 					if ( (p[0] & 0xF0) != 0xE0 ) {
   328 						/* Skip illegal sequences
   329 						return SDL_ICONV_EILSEQ;
   330 						*/
   331 						ch = UNKNOWN_UNICODE;
   332 					} else {
   333 						if ( p[0] == 0xE0 ) {
   334 							overlong = SDL_TRUE;
   335 						}
   336 						ch = (Uint32)(p[0] & 0x0F);
   337 						left = 2;
   338 					}
   339 				} else if ( p[0] >= 0xC0 ) {
   340 					if ( (p[0] & 0xE0) != 0xC0 ) {
   341 						/* Skip illegal sequences
   342 						return SDL_ICONV_EILSEQ;
   343 						*/
   344 						ch = UNKNOWN_UNICODE;
   345 					} else {
   346 						if ( (p[0] & 0xCE) == 0xC0 ) {
   347 							overlong = SDL_TRUE;
   348 						}
   349 						ch = (Uint32)(p[0] & 0x1F);
   350 						left = 1;
   351 					}
   352 				} else {
   353 					if ( (p[0] & 0x80) != 0x00 ) {
   354 						/* Skip illegal sequences
   355 						return SDL_ICONV_EILSEQ;
   356 						*/
   357 						ch = UNKNOWN_UNICODE;
   358 					} else {
   359 						ch = (Uint32)p[0];
   360 					}
   361 				}
   362 				++src;
   363 				--srclen;
   364 				if ( srclen < left ) {
   365 					return SDL_ICONV_EINVAL;
   366 				}
   367 				while ( left-- ) {
   368 					++p;
   369 					if ( (p[0] & 0xC0) != 0x80 ) {
   370 						/* Skip illegal sequences
   371 						return SDL_ICONV_EILSEQ;
   372 						*/
   373 						ch = UNKNOWN_UNICODE;
   374 						break;
   375 					}
   376 					ch <<= 6;
   377 					ch |= (p[0] & 0x3F);
   378 					++src;
   379 					--srclen;
   380 				}
   381 				if ( overlong ) {
   382 					/* Potential security risk
   383 					return SDL_ICONV_EILSEQ;
   384 					*/
   385 					ch = UNKNOWN_UNICODE;
   386 				}
   387 				if ( (ch >= 0xD800 && ch <= 0xDFFF) ||
   388 				     (ch == 0xFFFE || ch == 0xFFFF) ||
   389 				     ch > 0x10FFFF ) {
   390 					/* Skip illegal sequences
   391 					return SDL_ICONV_EILSEQ;
   392 					*/
   393 					ch = UNKNOWN_UNICODE;
   394 				}
   395 			}
   396 			break;
   397 		    case ENCODING_UTF16BE: /* RFC 2781 */
   398 			{
   399 				Uint8 *p = (Uint8 *)src;
   400 				Uint16 W1, W2;
   401 				if ( srclen < 2 ) {
   402 					return SDL_ICONV_EINVAL;
   403 				}
   404 				W1 = ((Uint16)p[0] << 8) |
   405 				      (Uint16)p[1];
   406 				src += 2;
   407 				srclen -= 2;
   408 				if ( W1 < 0xD800 || W1 > 0xDFFF ) {
   409 					ch = (Uint32)W1;
   410 					break;
   411 				}
   412 				if ( W1 > 0xDBFF ) {
   413 					/* Skip illegal sequences
   414 					return SDL_ICONV_EILSEQ;
   415 					*/
   416 					ch = UNKNOWN_UNICODE;
   417 					break;
   418 				}
   419 				if ( srclen < 2 ) {
   420 					return SDL_ICONV_EINVAL;
   421 				}
   422 				p = (Uint8 *)src;
   423 				W2 = ((Uint16)p[0] << 8) |
   424 				      (Uint16)p[1];
   425 				src += 2;
   426 				srclen -= 2;
   427 				if ( W2 < 0xDC00 || W2 > 0xDFFF ) {
   428 					/* Skip illegal sequences
   429 					return SDL_ICONV_EILSEQ;
   430 					*/
   431 					ch = UNKNOWN_UNICODE;
   432 					break;
   433 				}
   434 				ch = (((Uint32)(W1 & 0x3FF) << 10) |
   435 				      (Uint32)(W2 & 0x3FF)) + 0x10000;
   436 			}
   437 			break;
   438 		    case ENCODING_UTF16LE: /* RFC 2781 */
   439 			{
   440 				Uint8 *p = (Uint8 *)src;
   441 				Uint16 W1, W2;
   442 				if ( srclen < 2 ) {
   443 					return SDL_ICONV_EINVAL;
   444 				}
   445 				W1 = ((Uint16)p[1] << 8) |
   446 				      (Uint16)p[0];
   447 				src += 2;
   448 				srclen -= 2;
   449 				if ( W1 < 0xD800 || W1 > 0xDFFF ) {
   450 					ch = (Uint32)W1;
   451 					break;
   452 				}
   453 				if ( W1 > 0xDBFF ) {
   454 					/* Skip illegal sequences
   455 					return SDL_ICONV_EILSEQ;
   456 					*/
   457 					ch = UNKNOWN_UNICODE;
   458 					break;
   459 				}
   460 				if ( srclen < 2 ) {
   461 					return SDL_ICONV_EINVAL;
   462 				}
   463 				p = (Uint8 *)src;
   464 				W2 = ((Uint16)p[1] << 8) |
   465 				      (Uint16)p[0];
   466 				src += 2;
   467 				srclen -= 2;
   468 				if ( W2 < 0xDC00 || W2 > 0xDFFF ) {
   469 					/* Skip illegal sequences
   470 					return SDL_ICONV_EILSEQ;
   471 					*/
   472 					ch = UNKNOWN_UNICODE;
   473 					break;
   474 				}
   475 				ch = (((Uint32)(W1 & 0x3FF) << 10) |
   476 				      (Uint32)(W2 & 0x3FF)) + 0x10000;
   477 			}
   478 			break;
   479 		    case ENCODING_UTF32BE:
   480 			{
   481 				Uint8 *p = (Uint8 *)src;
   482 				if ( srclen < 4 ) {
   483 					return SDL_ICONV_EINVAL;
   484 				}
   485 				ch = ((Uint32)p[0] << 24) |
   486 				     ((Uint32)p[1] << 16) |
   487 				     ((Uint32)p[2] << 8) |
   488 				      (Uint32)p[3];
   489 				src += 4;
   490 				srclen -= 4;
   491 			}
   492 			break;
   493 		    case ENCODING_UTF32LE:
   494 			{
   495 				Uint8 *p = (Uint8 *)src;
   496 				if ( srclen < 4 ) {
   497 					return SDL_ICONV_EINVAL;
   498 				}
   499 				ch = ((Uint32)p[3] << 24) |
   500 				     ((Uint32)p[2] << 16) |
   501 				     ((Uint32)p[1] << 8) |
   502 				      (Uint32)p[0];
   503 				src += 4;
   504 				srclen -= 4;
   505 			}
   506 			break;
   507 		    case ENCODING_UCS2:
   508 			{
   509 				Uint16 *p = (Uint16 *)src;
   510 				if ( srclen < 2 ) {
   511 					return SDL_ICONV_EINVAL;
   512 				}
   513 				ch = *p;
   514 				src += 2;
   515 				srclen -= 2;
   516 			}
   517 			break;
   518 		    case ENCODING_UCS4:
   519 			{
   520 				Uint32 *p = (Uint32 *)src;
   521 				if ( srclen < 4 ) {
   522 					return SDL_ICONV_EINVAL;
   523 				}
   524 				ch = *p;
   525 				src += 4;
   526 				srclen -= 4;
   527 			}
   528 			break;
   529 		}
   530 
   531 		/* Encode a character */
   532 		switch ( cd->dst_fmt ) {
   533 		    case ENCODING_ASCII:
   534 			{
   535 				Uint8 *p = (Uint8 *)dst;
   536 				if ( dstlen < 1 ) {
   537 					return SDL_ICONV_E2BIG;
   538 				}
   539 				if ( ch > 0x7F ) {
   540 					*p = UNKNOWN_ASCII;
   541 				} else {
   542 					*p = (Uint8)ch;
   543 				}
   544 				++dst;
   545 				--dstlen;
   546 			}
   547 			break;
   548 		    case ENCODING_LATIN1:
   549 			{
   550 				Uint8 *p = (Uint8 *)dst;
   551 				if ( dstlen < 1 ) {
   552 					return SDL_ICONV_E2BIG;
   553 				}
   554 				if ( ch > 0xFF ) {
   555 					*p = UNKNOWN_ASCII;
   556 				} else {
   557 					*p = (Uint8)ch;
   558 				}
   559 				++dst;
   560 				--dstlen;
   561 			}
   562 			break;
   563 		    case ENCODING_UTF8: /* RFC 3629 */
   564 			{
   565 				Uint8 *p = (Uint8 *)dst;
   566 				if ( ch > 0x10FFFF ) {
   567 					ch = UNKNOWN_UNICODE;
   568 				}
   569 				if ( ch <= 0x7F ) {
   570 					if ( dstlen < 1 ) {
   571 						return SDL_ICONV_E2BIG;
   572 					}
   573 					*p = (Uint8)ch;
   574 					++dst;
   575 					--dstlen;
   576 				} else if ( ch <= 0x7FF ) {
   577 					if ( dstlen < 2 ) {
   578 						return SDL_ICONV_E2BIG;
   579 					}
   580 					p[0] = 0xC0 | (Uint8)((ch >> 6) & 0x1F);
   581 					p[1] = 0x80 | (Uint8)(ch & 0x3F);
   582 					dst += 2;
   583 					dstlen -= 2;
   584 				} else if ( ch <= 0xFFFF ) {
   585 					if ( dstlen < 3 ) {
   586 						return SDL_ICONV_E2BIG;
   587 					}
   588 					p[0] = 0xE0 | (Uint8)((ch >> 12) & 0x0F);
   589 					p[1] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   590 					p[2] = 0x80 | (Uint8)(ch & 0x3F);
   591 					dst += 3;
   592 					dstlen -= 3;
   593 				} else if ( ch <= 0x1FFFFF ) {
   594 					if ( dstlen < 4 ) {
   595 						return SDL_ICONV_E2BIG;
   596 					}
   597 					p[0] = 0xF0 | (Uint8)((ch >> 18) & 0x07);
   598 					p[1] = 0x80 | (Uint8)((ch >> 12) & 0x3F);
   599 					p[2] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   600 					p[3] = 0x80 | (Uint8)(ch & 0x3F);
   601 					dst += 4;
   602 					dstlen -= 4;
   603 				} else if ( ch <= 0x3FFFFFF ) {
   604 					if ( dstlen < 5 ) {
   605 						return SDL_ICONV_E2BIG;
   606 					}
   607 					p[0] = 0xF8 | (Uint8)((ch >> 24) & 0x03);
   608 					p[1] = 0x80 | (Uint8)((ch >> 18) & 0x3F);
   609 					p[2] = 0x80 | (Uint8)((ch >> 12) & 0x3F);
   610 					p[3] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   611 					p[4] = 0x80 | (Uint8)(ch & 0x3F);
   612 					dst += 5;
   613 					dstlen -= 5;
   614 				} else {
   615 					if ( dstlen < 6 ) {
   616 						return SDL_ICONV_E2BIG;
   617 					}
   618 					p[0] = 0xFC | (Uint8)((ch >> 30) & 0x01);
   619 					p[1] = 0x80 | (Uint8)((ch >> 24) & 0x3F);
   620 					p[2] = 0x80 | (Uint8)((ch >> 18) & 0x3F);
   621 					p[3] = 0x80 | (Uint8)((ch >> 12) & 0x3F);
   622 					p[4] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   623 					p[5] = 0x80 | (Uint8)(ch & 0x3F);
   624 					dst += 6;
   625 					dstlen -= 6;
   626 				}
   627 			}
   628 			break;
   629 		    case ENCODING_UTF16BE: /* RFC 2781 */
   630 			{
   631 				Uint8 *p = (Uint8 *)dst;
   632 				if ( ch > 0x10FFFF ) {
   633 					ch = UNKNOWN_UNICODE;
   634 				}
   635 				if ( ch < 0x10000 ) {
   636 					if ( dstlen < 2 ) {
   637 						return SDL_ICONV_E2BIG;
   638 					}
   639 					p[0] = (Uint8)(ch >> 8);
   640 					p[1] = (Uint8)ch;
   641 					dst += 2;
   642 					dstlen -= 2;
   643 				} else {
   644 					Uint16 W1, W2;
   645 					if ( dstlen < 4 ) {
   646 						return SDL_ICONV_E2BIG;
   647 					}
   648 					ch = ch - 0x10000;
   649 					W1 = 0xD800 | (Uint16)((ch >> 10) & 0x3FF);
   650 					W2 = 0xDC00 | (Uint16)(ch & 0x3FF);
   651 					p[0] = (Uint8)(W1 >> 8);
   652 					p[1] = (Uint8)W1;
   653 					p[2] = (Uint8)(W2 >> 8);
   654 					p[3] = (Uint8)W2;
   655 					dst += 4;
   656 					dstlen -= 4;
   657 				}
   658 			}
   659 			break;
   660 		    case ENCODING_UTF16LE: /* RFC 2781 */
   661 			{
   662 				Uint8 *p = (Uint8 *)dst;
   663 				if ( ch > 0x10FFFF ) {
   664 					ch = UNKNOWN_UNICODE;
   665 				}
   666 				if ( ch < 0x10000 ) {
   667 					if ( dstlen < 2 ) {
   668 						return SDL_ICONV_E2BIG;
   669 					}
   670 					p[1] = (Uint8)(ch >> 8);
   671 					p[0] = (Uint8)ch;
   672 					dst += 2;
   673 					dstlen -= 2;
   674 				} else {
   675 					Uint16 W1, W2;
   676 					if ( dstlen < 4 ) {
   677 						return SDL_ICONV_E2BIG;
   678 					}
   679 					ch = ch - 0x10000;
   680 					W1 = 0xD800 | (Uint16)((ch >> 10) & 0x3FF);
   681 					W2 = 0xDC00 | (Uint16)(ch & 0x3FF);
   682 					p[1] = (Uint8)(W1 >> 8);
   683 					p[0] = (Uint8)W1;
   684 					p[3] = (Uint8)(W2 >> 8);
   685 					p[2] = (Uint8)W2;
   686 					dst += 4;
   687 					dstlen -= 4;
   688 				}
   689 			}
   690 			break;
   691 		    case ENCODING_UTF32BE:
   692 			{
   693 				Uint8 *p = (Uint8 *)dst;
   694 				if ( ch > 0x10FFFF ) {
   695 					ch = UNKNOWN_UNICODE;
   696 				}
   697 				if ( dstlen < 4 ) {
   698 					return SDL_ICONV_E2BIG;
   699 				}
   700 				p[0] = (Uint8)(ch >> 24);
   701 				p[1] = (Uint8)(ch >> 16);
   702 				p[2] = (Uint8)(ch >> 8);
   703 				p[3] = (Uint8)ch;
   704 				dst += 4;
   705 				dstlen -= 4;
   706 			}
   707 			break;
   708 		    case ENCODING_UTF32LE:
   709 			{
   710 				Uint8 *p = (Uint8 *)dst;
   711 				if ( ch > 0x10FFFF ) {
   712 					ch = UNKNOWN_UNICODE;
   713 				}
   714 				if ( dstlen < 4 ) {
   715 					return SDL_ICONV_E2BIG;
   716 				}
   717 				p[3] = (Uint8)(ch >> 24);
   718 				p[2] = (Uint8)(ch >> 16);
   719 				p[1] = (Uint8)(ch >> 8);
   720 				p[0] = (Uint8)ch;
   721 				dst += 4;
   722 				dstlen -= 4;
   723 			}
   724 			break;
   725 		    case ENCODING_UCS2:
   726 			{
   727 				Uint16 *p = (Uint16 *)dst;
   728 				if ( ch > 0xFFFF ) {
   729 					ch = UNKNOWN_UNICODE;
   730 				}
   731 				if ( dstlen < 2 ) {
   732 					return SDL_ICONV_E2BIG;
   733 				}
   734 				*p = (Uint16)ch;
   735 				dst += 2;
   736 				dstlen -= 2;
   737 			}
   738 			break;
   739 		    case ENCODING_UCS4:
   740 			{
   741 				Uint32 *p = (Uint32 *)dst;
   742 				if ( ch > 0x7FFFFFFF ) {
   743 					ch = UNKNOWN_UNICODE;
   744 				}
   745 				if ( dstlen < 4 ) {
   746 					return SDL_ICONV_E2BIG;
   747 				}
   748 				*p = ch;
   749 				dst += 4;
   750 				dstlen -= 4;
   751 			}
   752 			break;
   753 		}
   754 
   755 		/* Update state */
   756 		*inbuf = src;
   757 		*inbytesleft = srclen;
   758 		*outbuf = dst;
   759 		*outbytesleft = dstlen;
   760 		++total;
   761 	}
   762 	return total;
   763 }
   764 
   765 int SDL_iconv_close(SDL_iconv_t cd)
   766 {
   767 	if ( cd && cd != (SDL_iconv_t)-1 ) {
   768 		SDL_free(cd);
   769 	}
   770 	return 0;
   771 }
   772 
   773 #endif /* !HAVE_ICONV */
   774 
   775 char *SDL_iconv_string(const char *tocode, const char *fromcode, const char *inbuf, size_t inbytesleft)
   776 {
   777 	SDL_iconv_t cd;
   778 	char *string;
   779 	size_t stringsize;
   780 	char *outbuf;
   781 	size_t outbytesleft;
   782 	size_t retCode = 0;
   783 
   784 	cd = SDL_iconv_open(tocode, fromcode);
   785 	if ( cd == (SDL_iconv_t)-1 ) {
   786 		return NULL;
   787 	}
   788 
   789 	stringsize = inbytesleft > 4 ? inbytesleft : 4;
   790 	string = SDL_malloc(stringsize);
   791 	if ( !string ) {
   792 		SDL_iconv_close(cd);
   793 		return NULL;
   794 	}
   795 	outbuf = string;
   796 	outbytesleft = stringsize;
   797 	SDL_memset(outbuf, 0, 4);
   798 
   799 	while ( inbytesleft > 0 ) {
   800 		retCode = SDL_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
   801 		switch (retCode) {
   802 		    case SDL_ICONV_E2BIG:
   803 			{
   804 				char *oldstring = string;
   805 				stringsize *= 2;
   806 				string = SDL_realloc(string, stringsize);
   807 				if ( !string ) {
   808 					SDL_iconv_close(cd);
   809 					return NULL;
   810 				}
   811 				outbuf = string + (outbuf - oldstring);
   812 				outbytesleft = stringsize - (outbuf - string);
   813 				SDL_memset(outbuf, 0, 4);
   814 			}
   815 			break;
   816 		    case SDL_ICONV_EILSEQ:
   817 			/* Try skipping some input data - not perfect, but... */
   818 			++inbuf;
   819 			--inbytesleft;
   820 			break;
   821 		    case SDL_ICONV_EINVAL:
   822 		    case SDL_ICONV_ERROR:
   823 			/* We can't continue... */
   824 			inbytesleft = 0;
   825 			break;
   826 		}
   827 	}
   828 	SDL_iconv_close(cd);
   829 
   830 	return string;
   831 }