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