src/stdlib/SDL_iconv.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 13 Mar 2006 01:17:22 +0000
changeset 1503 5e4dad24a5de
parent 1502 d403a39389da
child 1510 720f8bb49d7d
permissions -rw-r--r--
props yo
     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                  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 	char *src, *dst;
   157 	size_t srclen, dstlen;
   158 	Uint32 ch;
   159 	size_t total;
   160 
   161 	if ( !inbuf || !*inbuf ) {
   162 		/* Reset the context */
   163 		return 0;
   164 	}
   165 	if ( !outbuf || !*outbuf || !outbytesleft || !*outbytesleft ) {
   166 		return SDL_ICONV_E2BIG;
   167 	}
   168 	src = *inbuf;
   169 	srclen = (inbytesleft ? *inbytesleft : 0);
   170 	dst = *outbuf;
   171 	dstlen = *outbytesleft;
   172 
   173 	switch ( cd->src_fmt ) {
   174 	    case ENCODING_UTF16:
   175 		/* Scan for a byte order marker */
   176 		{
   177 			Uint8 *p = (Uint8 *)src;
   178 			size_t n = srclen / 2;
   179 			while ( n ) {
   180 				if ( p[0] == 0xFF && p[1] == 0xFE ) {
   181 					cd->src_fmt = ENCODING_UTF16BE;
   182 					break;
   183 				} else if ( p[0] == 0xFE && p[1] == 0xFF ) {
   184 					cd->src_fmt = ENCODING_UTF16LE;
   185 					break;
   186 				}
   187 				p += 2;
   188 				--n;
   189 			}
   190 			if ( n == 0 ) {
   191 				/* We can't tell, default to host order */
   192 				cd->src_fmt = ENCODING_UTF16NATIVE;
   193 			}
   194 		}
   195 		break;
   196 	    case ENCODING_UTF32:
   197 		/* Scan for a byte order marker */
   198 		{
   199 			Uint8 *p = (Uint8 *)src;
   200 			size_t n = srclen / 4;
   201 			while ( n ) {
   202 				if ( p[0] == 0xFF && p[1] == 0xFE &&
   203 				     p[2] == 0x00 && p[3] == 0x00 ) {
   204 					cd->src_fmt = ENCODING_UTF32BE;
   205 					break;
   206 				} else if ( p[0] == 0x00 && p[1] == 0x00 &&
   207 				            p[2] == 0xFE && p[3] == 0xFF ) {
   208 					cd->src_fmt = ENCODING_UTF32LE;
   209 					break;
   210 				}
   211 				p += 4;
   212 				--n;
   213 			}
   214 			if ( n == 0 ) {
   215 				/* We can't tell, default to host order */
   216 				cd->src_fmt = ENCODING_UTF32NATIVE;
   217 			}
   218 		}
   219 		break;
   220 	}
   221 
   222 	switch ( cd->dst_fmt ) {
   223 	    case ENCODING_UTF16:
   224 		/* Default to host order, need to add byte order marker */
   225 		if ( dstlen < 2 ) {
   226 			return SDL_ICONV_E2BIG;
   227 		}
   228 		*(Uint16 *)dst = UNICODE_BOM;
   229 		dst += 2;
   230 		dstlen -= 2;
   231 		cd->dst_fmt = ENCODING_UTF16NATIVE;
   232 		break;
   233 	    case ENCODING_UTF32:
   234 		/* Default to host order, need to add byte order marker */
   235 		if ( dstlen < 4 ) {
   236 			return SDL_ICONV_E2BIG;
   237 		}
   238 		*(Uint32 *)dst = UNICODE_BOM;
   239 		dst += 4;
   240 		dstlen -= 4;
   241 		cd->dst_fmt = ENCODING_UTF32NATIVE;
   242 		break;
   243 	}
   244 
   245 	total = 0;
   246 	while ( srclen > 0 ) {
   247 		/* Decode a character */
   248 		switch ( cd->src_fmt ) {
   249 		    case ENCODING_ASCII:
   250 			{
   251 				Uint8 *p = (Uint8 *)src;
   252 				ch = (Uint32)(p[0] & 0x7F);
   253 				++src;
   254 				--srclen;
   255 			}
   256 			break;
   257 		    case ENCODING_LATIN1:
   258 			{
   259 				Uint8 *p = (Uint8 *)src;
   260 				ch = (Uint32)p[0];
   261 				++src;
   262 				--srclen;
   263 			}
   264 			break;
   265 		    case ENCODING_UTF8: /* RFC 3629 */
   266 			{
   267 				Uint8 *p = (Uint8 *)src;
   268 				size_t left = 0;
   269 				SDL_bool overlong = SDL_FALSE;
   270 				if ( p[0] >= 0xFC ) {
   271 					if ( (p[0] & 0xFE) != 0xFC ) {
   272 						/* Skip illegal sequences
   273 						return SDL_ICONV_EILSEQ;
   274 						*/
   275 						ch = UNKNOWN_UNICODE;
   276 					} else {
   277 						if ( p[0] == 0xFC ) {
   278 							overlong = SDL_TRUE;
   279 						}
   280 						ch = (Uint32)(p[0] & 0x01);
   281 						left = 5;
   282 					}
   283 				} else if ( p[0] >= 0xF8 ) {
   284 					if ( (p[0] & 0xFC) != 0xF8 ) {
   285 						/* Skip illegal sequences
   286 						return SDL_ICONV_EILSEQ;
   287 						*/
   288 						ch = UNKNOWN_UNICODE;
   289 					} else {
   290 						if ( p[0] == 0xF8 ) {
   291 							overlong = SDL_TRUE;
   292 						}
   293 						ch = (Uint32)(p[0] & 0x03);
   294 						left = 4;
   295 					}
   296 				} else if ( p[0] >= 0xF0 ) {
   297 					if ( (p[0] & 0xF8) != 0xF0 ) {
   298 						/* Skip illegal sequences
   299 						return SDL_ICONV_EILSEQ;
   300 						*/
   301 						ch = UNKNOWN_UNICODE;
   302 					} else {
   303 						if ( p[0] == 0xF0 ) {
   304 							overlong = SDL_TRUE;
   305 						}
   306 						ch = (Uint32)(p[0] & 0x07);
   307 						left = 3;
   308 					}
   309 				} else if ( p[0] >= 0xE0 ) {
   310 					if ( (p[0] & 0xF0) != 0xE0 ) {
   311 						/* Skip illegal sequences
   312 						return SDL_ICONV_EILSEQ;
   313 						*/
   314 						ch = UNKNOWN_UNICODE;
   315 					} else {
   316 						if ( p[0] == 0xE0 ) {
   317 							overlong = SDL_TRUE;
   318 						}
   319 						ch = (Uint32)(p[0] & 0x0F);
   320 						left = 2;
   321 					}
   322 				} else if ( p[0] >= 0xC0 ) {
   323 					if ( (p[0] & 0xE0) != 0xC0 ) {
   324 						/* Skip illegal sequences
   325 						return SDL_ICONV_EILSEQ;
   326 						*/
   327 						ch = UNKNOWN_UNICODE;
   328 					} else {
   329 						if ( (p[0] & 0xCE) == 0xC0 ) {
   330 							overlong = SDL_TRUE;
   331 						}
   332 						ch = (Uint32)(p[0] & 0x1F);
   333 						left = 1;
   334 					}
   335 				} else {
   336 					if ( (p[0] & 0x80) != 0x00 ) {
   337 						/* Skip illegal sequences
   338 						return SDL_ICONV_EILSEQ;
   339 						*/
   340 						ch = UNKNOWN_UNICODE;
   341 					} else {
   342 						ch = (Uint32)p[0];
   343 					}
   344 				}
   345 				++src;
   346 				--srclen;
   347 				if ( srclen < left ) {
   348 					return SDL_ICONV_EINVAL;
   349 				}
   350 				while ( left-- ) {
   351 					++p;
   352 					if ( (p[0] & 0xC0) != 0x80 ) {
   353 						/* Skip illegal sequences
   354 						return SDL_ICONV_EILSEQ;
   355 						*/
   356 						ch = UNKNOWN_UNICODE;
   357 						break;
   358 					}
   359 					ch <<= 6;
   360 					ch |= (p[0] & 0x3F);
   361 					++src;
   362 					--srclen;
   363 				}
   364 				if ( overlong ) {
   365 					/* Potential security risk
   366 					return SDL_ICONV_EILSEQ;
   367 					*/
   368 					ch = UNKNOWN_UNICODE;
   369 				}
   370 				if ( (ch >= 0xD800 && ch <= 0xDFFF) ||
   371 				     (ch == 0xFFFE || ch == 0xFFFF) ) {
   372 					/* Skip illegal sequences
   373 					return SDL_ICONV_EILSEQ;
   374 					*/
   375 					ch = UNKNOWN_UNICODE;
   376 				}
   377 			}
   378 			break;
   379 		    case ENCODING_UTF16BE: /* RFC 2781 */
   380 			{
   381 				Uint8 *p = (Uint8 *)src;
   382 				Uint16 W1, W2;
   383 				if ( srclen < 2 ) {
   384 					return SDL_ICONV_EINVAL;
   385 				}
   386 				W1 = ((Uint32)p[0] << 8) |
   387 				      (Uint32)p[1];
   388 				src += 2;
   389 				srclen -= 2;
   390 				if ( W1 < 0xD800 || W1 > 0xDFFF ) {
   391 					ch = (Uint32)W1;
   392 					break;
   393 				}
   394 				if ( W1 > 0xDBFF ) {
   395 					/* Skip illegal sequences
   396 					return SDL_ICONV_EILSEQ;
   397 					*/
   398 					ch = UNKNOWN_UNICODE;
   399 					break;
   400 				}
   401 				if ( srclen < 2 ) {
   402 					return SDL_ICONV_EINVAL;
   403 				}
   404 				p = src;
   405 				W2 = ((Uint32)p[0] << 8) |
   406 				      (Uint32)p[1];
   407 				src += 2;
   408 				srclen -= 2;
   409 				if ( W2 < 0xDC00 || W2 > 0xDFFF ) {
   410 					/* Skip illegal sequences
   411 					return SDL_ICONV_EILSEQ;
   412 					*/
   413 					ch = UNKNOWN_UNICODE;
   414 					break;
   415 				}
   416 				ch = (((Uint32)(W1 & 0x3FF) << 10) |
   417 				      (Uint32)(W2 & 0x3FF)) + 0x10000;
   418 			}
   419 			break;
   420 		    case ENCODING_UTF16LE: /* RFC 2781 */
   421 			{
   422 				Uint8 *p = (Uint8 *)src;
   423 				Uint16 W1, W2;
   424 				if ( srclen < 2 ) {
   425 					return SDL_ICONV_EINVAL;
   426 				}
   427 				W1 = ((Uint32)p[1] << 8) |
   428 				      (Uint32)p[0];
   429 				src += 2;
   430 				srclen -= 2;
   431 				if ( W1 < 0xD800 || W1 > 0xDFFF ) {
   432 					ch = (Uint32)W1;
   433 					break;
   434 				}
   435 				if ( W1 > 0xDBFF ) {
   436 					/* Skip illegal sequences
   437 					return SDL_ICONV_EILSEQ;
   438 					*/
   439 					ch = UNKNOWN_UNICODE;
   440 					break;
   441 				}
   442 				if ( srclen < 2 ) {
   443 					return SDL_ICONV_EINVAL;
   444 				}
   445 				p = src;
   446 				W2 = ((Uint32)p[1] << 8) |
   447 				      (Uint32)p[0];
   448 				src += 2;
   449 				srclen -= 2;
   450 				if ( W2 < 0xDC00 || W2 > 0xDFFF ) {
   451 					/* Skip illegal sequences
   452 					return SDL_ICONV_EILSEQ;
   453 					*/
   454 					ch = UNKNOWN_UNICODE;
   455 					break;
   456 				}
   457 				ch = (((Uint32)(W1 & 0x3FF) << 10) |
   458 				      (Uint32)(W2 & 0x3FF)) + 0x10000;
   459 			}
   460 			break;
   461 		    case ENCODING_UTF32BE:
   462 			{
   463 				Uint8 *p = (Uint8 *)src;
   464 				if ( srclen < 4 ) {
   465 					return SDL_ICONV_EINVAL;
   466 				}
   467 				ch = ((Uint32)p[0] << 24) |
   468 				     ((Uint32)p[1] << 16) |
   469 				     ((Uint32)p[2] << 8) |
   470 				      (Uint32)p[3];
   471 				src += 4;
   472 				srclen -= 4;
   473 			}
   474 			break;
   475 		    case ENCODING_UTF32LE:
   476 			{
   477 				Uint8 *p = (Uint8 *)src;
   478 				if ( srclen < 4 ) {
   479 					return SDL_ICONV_EINVAL;
   480 				}
   481 				ch = ((Uint32)p[3] << 24) |
   482 				     ((Uint32)p[2] << 16) |
   483 				     ((Uint32)p[1] << 8) |
   484 				      (Uint32)p[0];
   485 				src += 4;
   486 				srclen -= 4;
   487 			}
   488 			break;
   489 		    case ENCODING_UCS2:
   490 			{
   491 				Uint16 *p = (Uint16 *)src;
   492 				if ( srclen < 2 ) {
   493 					return SDL_ICONV_EINVAL;
   494 				}
   495 				ch = *p;
   496 				src += 2;
   497 				srclen -= 2;
   498 			}
   499 			break;
   500 		    case ENCODING_UCS4:
   501 			{
   502 				Uint32 *p = (Uint32 *)src;
   503 				if ( srclen < 4 ) {
   504 					return SDL_ICONV_EINVAL;
   505 				}
   506 				ch = *p;
   507 				src += 4;
   508 				srclen -= 4;
   509 			}
   510 			break;
   511 		}
   512 
   513 		/* Encode a character */
   514 		switch ( cd->dst_fmt ) {
   515 		    case ENCODING_ASCII:
   516 			{
   517 				Uint8 *p = (Uint8 *)dst;
   518 				if ( dstlen < 1 ) {
   519 					return SDL_ICONV_E2BIG;
   520 				}
   521 				if ( ch > 0x7F ) {
   522 					*p = UNKNOWN_ASCII;
   523 				} else {
   524 					*p = (Uint8)ch;
   525 				}
   526 				++dst;
   527 				--dstlen;
   528 			}
   529 			break;
   530 		    case ENCODING_LATIN1:
   531 			{
   532 				Uint8 *p = (Uint8 *)dst;
   533 				if ( dstlen < 1 ) {
   534 					return SDL_ICONV_E2BIG;
   535 				}
   536 				if ( ch > 0xFF ) {
   537 					*p = UNKNOWN_ASCII;
   538 				} else {
   539 					*p = (Uint8)ch;
   540 				}
   541 				++dst;
   542 				--dstlen;
   543 			}
   544 			break;
   545 		    case ENCODING_UTF8: /* RFC 3629 */
   546 			{
   547 				Uint8 *p = (Uint8 *)dst;
   548 				if ( ch > 0x7FFFFFFF ) {
   549 					ch = UNKNOWN_UNICODE;
   550 				}
   551 				if ( ch <= 0x7F ) {
   552 					if ( dstlen < 1 ) {
   553 						return SDL_ICONV_E2BIG;
   554 					}
   555 					*p = (Uint8)ch;
   556 					++dst;
   557 					--dstlen;
   558 				} else if ( ch <= 0x7FF ) {
   559 					if ( dstlen < 2 ) {
   560 						return SDL_ICONV_E2BIG;
   561 					}
   562 					p[0] = 0xC0 | (Uint8)((ch >> 6) & 0x1F);
   563 					p[1] = 0x80 | (Uint8)(ch & 0x3F);
   564 					dst += 2;
   565 					dstlen -= 2;
   566 				} else if ( ch <= 0xFFFF ) {
   567 					if ( dstlen < 3 ) {
   568 						return SDL_ICONV_E2BIG;
   569 					}
   570 					p[0] = 0xE0 | (Uint8)((ch >> 12) & 0x0F);
   571 					p[1] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   572 					p[2] = 0x80 | (Uint8)(ch & 0x3F);
   573 					dst += 3;
   574 					dstlen -= 3;
   575 				} else if ( ch <= 0x1FFFFF ) {
   576 					if ( dstlen < 4 ) {
   577 						return SDL_ICONV_E2BIG;
   578 					}
   579 					p[0] = 0xF0 | (Uint8)((ch >> 18) & 0x07);
   580 					p[1] = 0x80 | (Uint8)((ch >> 12) & 0x3F);
   581 					p[2] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   582 					p[3] = 0x80 | (Uint8)(ch & 0x3F);
   583 					dst += 4;
   584 					dstlen -= 4;
   585 				} else if ( ch <= 0x3FFFFFF ) {
   586 					if ( dstlen < 5 ) {
   587 						return SDL_ICONV_E2BIG;
   588 					}
   589 					p[0] = 0xF8 | (Uint8)((ch >> 24) & 0x03);
   590 					p[1] = 0x80 | (Uint8)((ch >> 18) & 0x3F);
   591 					p[2] = 0x80 | (Uint8)((ch >> 12) & 0x3F);
   592 					p[3] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   593 					p[4] = 0x80 | (Uint8)(ch & 0x3F);
   594 					dst += 5;
   595 					dstlen -= 5;
   596 				} else {
   597 					if ( dstlen < 6 ) {
   598 						return SDL_ICONV_E2BIG;
   599 					}
   600 					p[0] = 0xFC | (Uint8)((ch >> 30) & 0x01);
   601 					p[1] = 0x80 | (Uint8)((ch >> 24) & 0x3F);
   602 					p[2] = 0x80 | (Uint8)((ch >> 18) & 0x3F);
   603 					p[3] = 0x80 | (Uint8)((ch >> 12) & 0x3F);
   604 					p[4] = 0x80 | (Uint8)((ch >> 6) & 0x3F);
   605 					p[5] = 0x80 | (Uint8)(ch & 0x3F);
   606 					dst += 6;
   607 					dstlen -= 6;
   608 				}
   609 			}
   610 			break;
   611 		    case ENCODING_UTF16BE: /* RFC 2781 */
   612 			{
   613 				Uint8 *p = (Uint8 *)dst;
   614 				if ( ch > 0x10FFFF ) {
   615 					ch = UNKNOWN_UNICODE;
   616 				}
   617 				if ( ch < 0x10000 ) {
   618 					if ( dstlen < 2 ) {
   619 						return SDL_ICONV_E2BIG;
   620 					}
   621 					p[0] = (Uint8)(ch >> 8);
   622 					p[1] = (Uint8)ch;
   623 					dst += 2;
   624 					dstlen -= 2;
   625 				} else {
   626 					Uint16 W1, W2;
   627 					if ( dstlen < 4 ) {
   628 						return SDL_ICONV_E2BIG;
   629 					}
   630 					ch = ch - 0x10000;
   631 					W1 = 0xD800 | (Uint16)((ch >> 10) & 0x3FF);
   632 					W2 = 0xDC00 | (Uint16)(ch & 0x3FF);
   633 					p[0] = (Uint8)(W1 >> 8);
   634 					p[1] = (Uint8)W1;
   635 					p[2] = (Uint8)(W2 >> 8);
   636 					p[3] = (Uint8)W2;
   637 					dst += 4;
   638 					dstlen -= 4;
   639 				}
   640 			}
   641 			break;
   642 		    case ENCODING_UTF16LE: /* RFC 2781 */
   643 			{
   644 				Uint8 *p = (Uint8 *)dst;
   645 				if ( ch > 0x10FFFF ) {
   646 					ch = UNKNOWN_UNICODE;
   647 				}
   648 				if ( ch < 0x10000 ) {
   649 					if ( dstlen < 2 ) {
   650 						return SDL_ICONV_E2BIG;
   651 					}
   652 					p[1] = (Uint8)(ch >> 8);
   653 					p[0] = (Uint8)ch;
   654 					dst += 2;
   655 					dstlen -= 2;
   656 				} else {
   657 					Uint16 W1, W2;
   658 					if ( dstlen < 4 ) {
   659 						return SDL_ICONV_E2BIG;
   660 					}
   661 					ch = ch - 0x10000;
   662 					W1 = 0xD800 | (Uint16)((ch >> 10) & 0x3FF);
   663 					W2 = 0xDC00 | (Uint16)(ch & 0x3FF);
   664 					p[1] = (Uint8)(W1 >> 8);
   665 					p[0] = (Uint8)W1;
   666 					p[3] = (Uint8)(W2 >> 8);
   667 					p[2] = (Uint8)W2;
   668 					dst += 4;
   669 					dstlen -= 4;
   670 				}
   671 			}
   672 			break;
   673 		    case ENCODING_UTF32BE:
   674 			{
   675 				Uint8 *p = (Uint8 *)dst;
   676 				if ( ch > 0x10FFFF ) {
   677 					ch = UNKNOWN_UNICODE;
   678 				}
   679 				if ( dstlen < 4 ) {
   680 					return SDL_ICONV_E2BIG;
   681 				}
   682 				p[0] = (Uint8)(ch >> 24);
   683 				p[1] = (Uint8)(ch >> 16);
   684 				p[2] = (Uint8)(ch >> 8);
   685 				p[3] = (Uint8)ch;
   686 				dst += 4;
   687 				dstlen -= 4;
   688 			}
   689 			break;
   690 		    case ENCODING_UTF32LE:
   691 			{
   692 				Uint8 *p = (Uint8 *)dst;
   693 				if ( ch > 0x10FFFF ) {
   694 					ch = UNKNOWN_UNICODE;
   695 				}
   696 				if ( dstlen < 4 ) {
   697 					return SDL_ICONV_E2BIG;
   698 				}
   699 				p[3] = (Uint8)(ch >> 24);
   700 				p[2] = (Uint8)(ch >> 16);
   701 				p[1] = (Uint8)(ch >> 8);
   702 				p[0] = (Uint8)ch;
   703 				dst += 4;
   704 				dstlen -= 4;
   705 			}
   706 			break;
   707 		    case ENCODING_UCS2:
   708 			{
   709 				Uint16 *p = (Uint16 *)dst;
   710 				if ( ch > 0xFFFF ) {
   711 					ch = UNKNOWN_UNICODE;
   712 				}
   713 				if ( dstlen < 2 ) {
   714 					return SDL_ICONV_E2BIG;
   715 				}
   716 				*p = (Uint16)ch;
   717 				dst += 2;
   718 				dstlen -= 2;
   719 			}
   720 			break;
   721 		    case ENCODING_UCS4:
   722 			{
   723 				Uint32 *p = (Uint32 *)dst;
   724 				if ( ch > 0x7FFFFFFF ) {
   725 					ch = UNKNOWN_UNICODE;
   726 				}
   727 				if ( dstlen < 4 ) {
   728 					return SDL_ICONV_E2BIG;
   729 				}
   730 				*p = ch;
   731 				dst += 4;
   732 				dstlen -= 4;
   733 			}
   734 			break;
   735 		}
   736 
   737 		/* Update state */
   738 		*inbuf = src;
   739 		*inbytesleft = srclen;
   740 		*outbuf = dst;
   741 		*outbytesleft = dstlen;
   742 		++total;
   743 	}
   744 	return total;
   745 }
   746 
   747 int SDL_iconv_close(SDL_iconv_t cd)
   748 {
   749 	if ( cd && cd != (SDL_iconv_t)-1 ) {
   750 		SDL_free(cd);
   751 	}
   752 	return 0;
   753 }
   754 
   755 #endif /* !HAVE_ICONV */
   756 
   757 char *SDL_iconv_string(const char *tocode, const char *fromcode, char *inbuf, size_t inbytesleft)
   758 {
   759 	SDL_iconv_t cd;
   760 	char *string;
   761 	size_t stringsize;
   762 	char *outbuf;
   763 	size_t outbytesleft;
   764 	size_t retCode = 0;
   765 
   766 	cd = SDL_iconv_open(tocode, fromcode);
   767 	if ( cd == (SDL_iconv_t)-1 ) {
   768 		return NULL;
   769 	}
   770 
   771 	stringsize = inbytesleft > 4 ? inbytesleft : 4;
   772 	string = SDL_malloc(stringsize);
   773 	if ( !string ) {
   774 		SDL_iconv_close(cd);
   775 		return NULL;
   776 	}
   777 	outbuf = string;
   778 	outbytesleft = stringsize;
   779 	SDL_memset(outbuf, 0, 4);
   780 
   781 	while ( inbytesleft > 0 ) {
   782 		retCode = SDL_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
   783 		switch (retCode) {
   784 		    case SDL_ICONV_E2BIG:
   785 			{
   786 				char *oldstring = string;
   787 				stringsize *= 2;
   788 				string = SDL_realloc(string, stringsize);
   789 				if ( !string ) {
   790 					SDL_iconv_close(cd);
   791 					return NULL;
   792 				}
   793 				outbuf = string + (outbuf - oldstring);
   794 				outbytesleft = stringsize - (outbuf - string);
   795 				SDL_memset(outbuf, 0, 4);
   796 			}
   797 			break;
   798 		    case SDL_ICONV_EILSEQ:
   799 			/* Try skipping some input data - not perfect, but... */
   800 			++inbuf;
   801 			--inbytesleft;
   802 			break;
   803 		    case SDL_ICONV_EINVAL:
   804 		    case SDL_ICONV_ERROR:
   805 			/* We can't continue... */
   806 			inbytesleft = 0;
   807 			break;
   808 		}
   809 	}
   810 	SDL_iconv_close(cd);
   811 
   812 	return string;
   813 }