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