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