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