src/stdlib/SDL_iconv.c
author Ryan C. Gordon
Mon, 05 Jan 2015 01:41:42 -0500
changeset 9306 817656bd36ec
parent 8879 f6e4f24df1ac
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Clang static analysis builds should use C runtime directly.

This is a little macro magic to use malloc() directly instead of SDL_malloc(),
etc, so static analysis tests that know about the C runtime can function
properly, and understand that we are dealing with heap allocations, etc.

This changed our static analysis report from 5 outstanding bugs to 30.

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