src/stdlib/SDL_iconv.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1519 c99e7a9c9bc9
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

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