src/stdlib/SDL_iconv.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 15 Mar 2013 01:01:20 -0400
changeset 7003 eeaf77005c30
parent 6885 700f1b25f77f
child 7349 a96c85910792
permissions -rw-r--r--
Improvements to stdlib.

All SDL_* functions are always available as real symbols, so you can always
link against them as a stable ABI. By default, however, all the things that
might have dithered down to macros in your application are now force-inlined,
to give you the same effect as before and theoretically better performance,
but still solve the classic macro problems.

Elsewhere, we provide real functions for these things that simply wrap the
inline functions, in case one needs to have a real function available.

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