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