| /* | |
| * MultiByteToWideChar implementation | |
| * | |
| * Copyright 2000 Alexandre Julliard | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include <string.h> | |
| #include "wine/unicode.h" | |
| /* get the decomposition of a Unicode char */ | |
| int get_decomposition( WCHAR src, WCHAR *dst, unsigned int dstlen ) | |
| { | |
| extern const WCHAR unicode_decompose_table[]; | |
| const WCHAR *ptr = unicode_decompose_table; | |
| int res; | |
| *dst = src; | |
| ptr = unicode_decompose_table + ptr[src >> 8]; | |
| ptr = unicode_decompose_table + ptr[(src >> 4) & 0x0f] + 2 * (src & 0x0f); | |
| if (!*ptr) return 1; | |
| if (dstlen <= 1) return 0; | |
| /* apply the decomposition recursively to the first char */ | |
| if ((res = get_decomposition( *ptr, dst, dstlen-1 ))) dst[res++] = ptr[1]; | |
| return res; | |
| } | |
| /* check src string for invalid chars; return non-zero if invalid char found */ | |
| static inline int check_invalid_chars_sbcs( const struct sbcs_table *table, | |
| const unsigned char *src, unsigned int srclen ) | |
| { | |
| const WCHAR * const cp2uni = table->cp2uni; | |
| while (srclen) | |
| { | |
| if (cp2uni[*src] == table->info.def_unicode_char && *src != table->info.def_char) | |
| break; | |
| src++; | |
| srclen--; | |
| } | |
| return srclen; | |
| } | |
| /* mbstowcs for single-byte code page */ | |
| /* all lengths are in characters, not bytes */ | |
| static inline int mbstowcs_sbcs( const struct sbcs_table *table, | |
| const unsigned char *src, unsigned int srclen, | |
| WCHAR *dst, unsigned int dstlen ) | |
| { | |
| const WCHAR * const cp2uni = table->cp2uni; | |
| int ret = srclen; | |
| if (dstlen < srclen) | |
| { | |
| /* buffer too small: fill it up to dstlen and return error */ | |
| srclen = dstlen; | |
| ret = -1; | |
| } | |
| for (;;) | |
| { | |
| switch(srclen) | |
| { | |
| default: | |
| case 16: dst[15] = cp2uni[src[15]]; | |
| case 15: dst[14] = cp2uni[src[14]]; | |
| case 14: dst[13] = cp2uni[src[13]]; | |
| case 13: dst[12] = cp2uni[src[12]]; | |
| case 12: dst[11] = cp2uni[src[11]]; | |
| case 11: dst[10] = cp2uni[src[10]]; | |
| case 10: dst[9] = cp2uni[src[9]]; | |
| case 9: dst[8] = cp2uni[src[8]]; | |
| case 8: dst[7] = cp2uni[src[7]]; | |
| case 7: dst[6] = cp2uni[src[6]]; | |
| case 6: dst[5] = cp2uni[src[5]]; | |
| case 5: dst[4] = cp2uni[src[4]]; | |
| case 4: dst[3] = cp2uni[src[3]]; | |
| case 3: dst[2] = cp2uni[src[2]]; | |
| case 2: dst[1] = cp2uni[src[1]]; | |
| case 1: dst[0] = cp2uni[src[0]]; | |
| case 0: break; | |
| } | |
| if (srclen < 16) return ret; | |
| dst += 16; | |
| src += 16; | |
| srclen -= 16; | |
| } | |
| } | |
| /* mbstowcs for single-byte code page with char decomposition */ | |
| static int mbstowcs_sbcs_decompose( const struct sbcs_table *table, | |
| const unsigned char *src, unsigned int srclen, | |
| WCHAR *dst, unsigned int dstlen ) | |
| { | |
| const WCHAR * const cp2uni = table->cp2uni; | |
| unsigned int len; | |
| if (!dstlen) /* compute length */ | |
| { | |
| WCHAR dummy[4]; /* no decomposition is larger than 4 chars */ | |
| for (len = 0; srclen; srclen--, src++) | |
| len += get_decomposition( cp2uni[*src], dummy, 4 ); | |
| return len; | |
| } | |
| for (len = dstlen; srclen && len; srclen--, src++) | |
| { | |
| int res = get_decomposition( cp2uni[*src], dst, len ); | |
| if (!res) break; | |
| len -= res; | |
| dst += res; | |
| } | |
| if (srclen) return -1; /* overflow */ | |
| return dstlen - len; | |
| } | |
| /* query necessary dst length for src string */ | |
| static inline int get_length_dbcs( const struct dbcs_table *table, | |
| const unsigned char *src, unsigned int srclen ) | |
| { | |
| const unsigned char * const cp2uni_lb = table->cp2uni_leadbytes; | |
| int len; | |
| for (len = 0; srclen; srclen--, src++, len++) | |
| { | |
| if (cp2uni_lb[*src]) | |
| { | |
| if (!--srclen) break; /* partial char, ignore it */ | |
| src++; | |
| } | |
| } | |
| return len; | |
| } | |
| /* check src string for invalid chars; return non-zero if invalid char found */ | |
| static inline int check_invalid_chars_dbcs( const struct dbcs_table *table, | |
| const unsigned char *src, unsigned int srclen ) | |
| { | |
| const WCHAR * const cp2uni = table->cp2uni; | |
| const unsigned char * const cp2uni_lb = table->cp2uni_leadbytes; | |
| while (srclen) | |
| { | |
| unsigned char off = cp2uni_lb[*src]; | |
| if (off) /* multi-byte char */ | |
| { | |
| if (srclen == 1) break; /* partial char, error */ | |
| if (cp2uni[(off << 8) + src[1]] == table->info.def_unicode_char && | |
| ((src[0] << 8) | src[1]) != table->info.def_char) break; | |
| src++; | |
| srclen--; | |
| } | |
| else if (cp2uni[*src] == table->info.def_unicode_char && | |
| *src != table->info.def_char) break; | |
| src++; | |
| srclen--; | |
| } | |
| return srclen; | |
| } | |
| /* mbstowcs for double-byte code page */ | |
| /* all lengths are in characters, not bytes */ | |
| static inline int mbstowcs_dbcs( const struct dbcs_table *table, | |
| const unsigned char *src, unsigned int srclen, | |
| WCHAR *dst, unsigned int dstlen ) | |
| { | |
| const WCHAR * const cp2uni = table->cp2uni; | |
| const unsigned char * const cp2uni_lb = table->cp2uni_leadbytes; | |
| unsigned int len; | |
| if (!dstlen) return get_length_dbcs( table, src, srclen ); | |
| for (len = dstlen; srclen && len; len--, srclen--, src++, dst++) | |
| { | |
| unsigned char off = cp2uni_lb[*src]; | |
| if (off) | |
| { | |
| if (!--srclen) break; /* partial char, ignore it */ | |
| src++; | |
| *dst = cp2uni[(off << 8) + *src]; | |
| } | |
| else *dst = cp2uni[*src]; | |
| } | |
| if (srclen) return -1; /* overflow */ | |
| return dstlen - len; | |
| } | |
| /* mbstowcs for double-byte code page with character decomposition */ | |
| static int mbstowcs_dbcs_decompose( const struct dbcs_table *table, | |
| const unsigned char *src, unsigned int srclen, | |
| WCHAR *dst, unsigned int dstlen ) | |
| { | |
| const WCHAR * const cp2uni = table->cp2uni; | |
| const unsigned char * const cp2uni_lb = table->cp2uni_leadbytes; | |
| unsigned int len; | |
| WCHAR ch; | |
| int res; | |
| if (!dstlen) /* compute length */ | |
| { | |
| WCHAR dummy[4]; /* no decomposition is larger than 4 chars */ | |
| for (len = 0; srclen; srclen--, src++) | |
| { | |
| unsigned char off = cp2uni_lb[*src]; | |
| if (off) | |
| { | |
| if (!--srclen) break; /* partial char, ignore it */ | |
| src++; | |
| ch = cp2uni[(off << 8) + *src]; | |
| } | |
| else ch = cp2uni[*src]; | |
| len += get_decomposition( ch, dummy, 4 ); | |
| } | |
| return len; | |
| } | |
| for (len = dstlen; srclen && len; srclen--, src++) | |
| { | |
| unsigned char off = cp2uni_lb[*src]; | |
| if (off) | |
| { | |
| if (!--srclen) break; /* partial char, ignore it */ | |
| src++; | |
| ch = cp2uni[(off << 8) + *src]; | |
| } | |
| else ch = cp2uni[*src]; | |
| if (!(res = get_decomposition( ch, dst, len ))) break; | |
| dst += res; | |
| len -= res; | |
| } | |
| if (srclen) return -1; /* overflow */ | |
| return dstlen - len; | |
| } | |
| /* return -1 on dst buffer overflow, -2 on invalid input char */ | |
| int wine_cp_mbstowcs( const union cptable *table, int flags, | |
| const char *src, int srclen, | |
| WCHAR *dst, int dstlen ) | |
| { | |
| if (table->info.char_size == 1) | |
| { | |
| if (flags & MB_ERR_INVALID_CHARS) | |
| { | |
| if (check_invalid_chars_sbcs( &table->sbcs, src, srclen )) return -2; | |
| } | |
| if (!(flags & MB_COMPOSITE)) | |
| { | |
| if (!dstlen) return srclen; | |
| return mbstowcs_sbcs( &table->sbcs, src, srclen, dst, dstlen ); | |
| } | |
| return mbstowcs_sbcs_decompose( &table->sbcs, src, srclen, dst, dstlen ); | |
| } | |
| else /* mbcs */ | |
| { | |
| if (flags & MB_ERR_INVALID_CHARS) | |
| { | |
| if (check_invalid_chars_dbcs( &table->dbcs, src, srclen )) return -2; | |
| } | |
| if (!(flags & MB_COMPOSITE)) | |
| return mbstowcs_dbcs( &table->dbcs, src, srclen, dst, dstlen ); | |
| else | |
| return mbstowcs_dbcs_decompose( &table->dbcs, src, srclen, dst, dstlen ); | |
| } | |
| } | |
| /* CP_SYMBOL implementation */ | |
| /* return -1 on dst buffer overflow */ | |
| int wine_cpsymbol_mbstowcs( const char *src, int srclen, WCHAR *dst, int dstlen) | |
| { | |
| int len, i; | |
| if( dstlen == 0) return srclen; | |
| len = dstlen > srclen ? srclen : dstlen; | |
| for( i = 0; i < len; i++) | |
| { | |
| unsigned char c = src [ i ]; | |
| if( c < 0x20 ) | |
| dst[i] = c; | |
| else | |
| dst[i] = c + 0xf000; | |
| } | |
| if( srclen > len) return -1; | |
| return len; | |
| } |