src/stdlib/SDL_iconv.c
author Sam Lantinga
Thu, 28 Jun 2007 06:47:35 +0000
branchSDL-1.2
changeset 3985 2f8efcf14c83
parent 3984 b74530a1dad6
child 3986 4f73308bbb32
permissions -rw-r--r--
Okay, apparently the newer standard specifies char** for the inbuf
parameter to iconv()
(See http://lists.debian.org/debian-glibc/2004/05/msg00006.html)

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