2 * WideCharToMultiByte implementation
4 * Copyright 2000 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/unicode.h"
29 extern WCHAR
wine_compose( const WCHAR
*str
) DECLSPEC_HIDDEN
;
31 /****************************************************************/
34 /* check if 'ch' is an acceptable sbcs mapping for 'wch' */
35 static inline int is_valid_sbcs_mapping( const struct sbcs_table
*table
, int flags
,
36 WCHAR wch
, unsigned char ch
)
38 if ((flags
& WC_NO_BEST_FIT_CHARS
) || ch
== (unsigned char)table
->info
.def_char
)
39 return (table
->cp2uni
[ch
] == wch
);
43 /* query necessary dst length for src string */
44 static int get_length_sbcs( const struct sbcs_table
*table
, int flags
,
45 const WCHAR
*src
, unsigned int srclen
, int *used
)
47 const unsigned char * const uni2cp_low
= table
->uni2cp_low
;
48 const unsigned short * const uni2cp_high
= table
->uni2cp_high
;
52 if (!used
) used
= &tmp
; /* avoid checking on every char */
55 for (ret
= 0; srclen
; ret
++, src
++, srclen
--)
60 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= wine_compose(src
)))
62 /* now check if we can use the composed char */
63 ch
= uni2cp_low
[uni2cp_high
[composed
>> 8] + (composed
& 0xff)];
64 if (is_valid_sbcs_mapping( table
, flags
, composed
, ch
))
66 /* we have a good mapping, use it */
71 /* no mapping for the composed char, check the other flags */
72 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
75 src
++; /* skip the non-spacing char */
79 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
84 /* WC_SEPCHARS is the default */
88 ch
= uni2cp_low
[uni2cp_high
[wch
>> 8] + (wch
& 0xff)];
89 *used
= !is_valid_sbcs_mapping( table
, flags
, wch
, ch
);
95 /* wcstombs for single-byte code page */
96 static inline int wcstombs_sbcs( const struct sbcs_table
*table
,
97 const WCHAR
*src
, unsigned int srclen
,
98 char *dst
, unsigned int dstlen
)
100 const unsigned char * const uni2cp_low
= table
->uni2cp_low
;
101 const unsigned short * const uni2cp_high
= table
->uni2cp_high
;
106 /* buffer too small: fill it up to dstlen and return error */
113 dst
[0] = uni2cp_low
[uni2cp_high
[src
[0] >> 8] + (src
[0] & 0xff)];
114 dst
[1] = uni2cp_low
[uni2cp_high
[src
[1] >> 8] + (src
[1] & 0xff)];
115 dst
[2] = uni2cp_low
[uni2cp_high
[src
[2] >> 8] + (src
[2] & 0xff)];
116 dst
[3] = uni2cp_low
[uni2cp_high
[src
[3] >> 8] + (src
[3] & 0xff)];
117 dst
[4] = uni2cp_low
[uni2cp_high
[src
[4] >> 8] + (src
[4] & 0xff)];
118 dst
[5] = uni2cp_low
[uni2cp_high
[src
[5] >> 8] + (src
[5] & 0xff)];
119 dst
[6] = uni2cp_low
[uni2cp_high
[src
[6] >> 8] + (src
[6] & 0xff)];
120 dst
[7] = uni2cp_low
[uni2cp_high
[src
[7] >> 8] + (src
[7] & 0xff)];
121 dst
[8] = uni2cp_low
[uni2cp_high
[src
[8] >> 8] + (src
[8] & 0xff)];
122 dst
[9] = uni2cp_low
[uni2cp_high
[src
[9] >> 8] + (src
[9] & 0xff)];
123 dst
[10] = uni2cp_low
[uni2cp_high
[src
[10] >> 8] + (src
[10] & 0xff)];
124 dst
[11] = uni2cp_low
[uni2cp_high
[src
[11] >> 8] + (src
[11] & 0xff)];
125 dst
[12] = uni2cp_low
[uni2cp_high
[src
[12] >> 8] + (src
[12] & 0xff)];
126 dst
[13] = uni2cp_low
[uni2cp_high
[src
[13] >> 8] + (src
[13] & 0xff)];
127 dst
[14] = uni2cp_low
[uni2cp_high
[src
[14] >> 8] + (src
[14] & 0xff)];
128 dst
[15] = uni2cp_low
[uni2cp_high
[src
[15] >> 8] + (src
[15] & 0xff)];
134 /* now handle remaining characters */
139 case 15: dst
[-15] = uni2cp_low
[uni2cp_high
[src
[-15] >> 8] + (src
[-15] & 0xff)];
140 case 14: dst
[-14] = uni2cp_low
[uni2cp_high
[src
[-14] >> 8] + (src
[-14] & 0xff)];
141 case 13: dst
[-13] = uni2cp_low
[uni2cp_high
[src
[-13] >> 8] + (src
[-13] & 0xff)];
142 case 12: dst
[-12] = uni2cp_low
[uni2cp_high
[src
[-12] >> 8] + (src
[-12] & 0xff)];
143 case 11: dst
[-11] = uni2cp_low
[uni2cp_high
[src
[-11] >> 8] + (src
[-11] & 0xff)];
144 case 10: dst
[-10] = uni2cp_low
[uni2cp_high
[src
[-10] >> 8] + (src
[-10] & 0xff)];
145 case 9: dst
[-9] = uni2cp_low
[uni2cp_high
[src
[-9] >> 8] + (src
[-9] & 0xff)];
146 case 8: dst
[-8] = uni2cp_low
[uni2cp_high
[src
[-8] >> 8] + (src
[-8] & 0xff)];
147 case 7: dst
[-7] = uni2cp_low
[uni2cp_high
[src
[-7] >> 8] + (src
[-7] & 0xff)];
148 case 6: dst
[-6] = uni2cp_low
[uni2cp_high
[src
[-6] >> 8] + (src
[-6] & 0xff)];
149 case 5: dst
[-5] = uni2cp_low
[uni2cp_high
[src
[-5] >> 8] + (src
[-5] & 0xff)];
150 case 4: dst
[-4] = uni2cp_low
[uni2cp_high
[src
[-4] >> 8] + (src
[-4] & 0xff)];
151 case 3: dst
[-3] = uni2cp_low
[uni2cp_high
[src
[-3] >> 8] + (src
[-3] & 0xff)];
152 case 2: dst
[-2] = uni2cp_low
[uni2cp_high
[src
[-2] >> 8] + (src
[-2] & 0xff)];
153 case 1: dst
[-1] = uni2cp_low
[uni2cp_high
[src
[-1] >> 8] + (src
[-1] & 0xff)];
159 /* slow version of wcstombs_sbcs that handles the various flags */
160 static int wcstombs_sbcs_slow( const struct sbcs_table
*table
, int flags
,
161 const WCHAR
*src
, unsigned int srclen
,
162 char *dst
, unsigned int dstlen
,
163 const char *defchar
, int *used
)
165 const unsigned char * const uni2cp_low
= table
->uni2cp_low
;
166 const unsigned short * const uni2cp_high
= table
->uni2cp_high
;
173 def
= table
->info
.def_char
& 0xff;
177 if (!used
) used
= &tmp
; /* avoid checking on every char */
180 for (len
= dstlen
; srclen
&& len
; dst
++, len
--, src
++, srclen
--)
184 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= wine_compose(src
)))
186 /* now check if we can use the composed char */
187 *dst
= uni2cp_low
[uni2cp_high
[composed
>> 8] + (composed
& 0xff)];
188 if (is_valid_sbcs_mapping( table
, flags
, composed
, *dst
))
190 /* we have a good mapping, use it */
195 /* no mapping for the composed char, check the other flags */
196 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
200 src
++; /* skip the non-spacing char */
204 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
209 /* WC_SEPCHARS is the default */
212 *dst
= uni2cp_low
[uni2cp_high
[wch
>> 8] + (wch
& 0xff)];
213 if (!is_valid_sbcs_mapping( table
, flags
, wch
, *dst
))
219 if (srclen
) return -1; /* overflow */
224 /****************************************************************/
227 /* check if 'ch' is an acceptable dbcs mapping for 'wch' */
228 static inline int is_valid_dbcs_mapping( const struct dbcs_table
*table
, int flags
,
229 WCHAR wch
, unsigned short ch
)
231 if ((flags
& WC_NO_BEST_FIT_CHARS
) || ch
== table
->info
.def_char
)
233 /* check if char maps back to the same Unicode value */
236 unsigned char off
= table
->cp2uni_leadbytes
[ch
>> 8];
237 return (table
->cp2uni
[(off
<< 8) + (ch
& 0xff)] == wch
);
239 return (table
->cp2uni
[ch
& 0xff] == wch
);
244 /* compute the default char for the dbcs case */
245 static inline WCHAR
get_defchar_dbcs( const struct dbcs_table
*table
, const char *defchar
)
247 if (!defchar
) return table
->info
.def_char
;
248 if (!defchar
[1]) return (unsigned char)defchar
[0];
249 return ((unsigned char)defchar
[0] << 8) | (unsigned char)defchar
[1];
252 /* query necessary dst length for src string */
253 static int get_length_dbcs( const struct dbcs_table
*table
, int flags
,
254 const WCHAR
*src
, unsigned int srclen
,
255 const char *defchar
, int *used
)
257 const unsigned short * const uni2cp_low
= table
->uni2cp_low
;
258 const unsigned short * const uni2cp_high
= table
->uni2cp_high
;
259 WCHAR defchar_value
, composed
;
262 if (!defchar
&& !used
&& !(flags
& WC_COMPOSITECHECK
))
264 for (len
= 0; srclen
; srclen
--, src
++, len
++)
266 if (uni2cp_low
[uni2cp_high
[*src
>> 8] + (*src
& 0xff)] & 0xff00) len
++;
271 defchar_value
= get_defchar_dbcs( table
, defchar
);
272 if (!used
) used
= &tmp
; /* avoid checking on every char */
274 for (len
= 0; srclen
; len
++, srclen
--, src
++)
279 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= wine_compose(src
)))
281 /* now check if we can use the composed char */
282 res
= uni2cp_low
[uni2cp_high
[composed
>> 8] + (composed
& 0xff)];
284 if (is_valid_dbcs_mapping( table
, flags
, composed
, res
))
286 /* we have a good mapping for the composed char, use it */
287 if (res
& 0xff00) len
++;
292 /* no mapping for the composed char, check the other flags */
293 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
295 if (defchar_value
& 0xff00) len
++;
297 src
++; /* skip the non-spacing char */
301 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
306 /* WC_SEPCHARS is the default */
309 res
= uni2cp_low
[uni2cp_high
[wch
>> 8] + (wch
& 0xff)];
310 if (!is_valid_dbcs_mapping( table
, flags
, wch
, res
))
315 if (res
& 0xff00) len
++;
320 /* wcstombs for double-byte code page */
321 static inline int wcstombs_dbcs( const struct dbcs_table
*table
,
322 const WCHAR
*src
, unsigned int srclen
,
323 char *dst
, unsigned int dstlen
)
325 const unsigned short * const uni2cp_low
= table
->uni2cp_low
;
326 const unsigned short * const uni2cp_high
= table
->uni2cp_high
;
329 for (len
= dstlen
; srclen
&& len
; len
--, srclen
--, src
++)
331 unsigned short res
= uni2cp_low
[uni2cp_high
[*src
>> 8] + (*src
& 0xff)];
334 if (len
== 1) break; /* do not output a partial char */
340 if (srclen
) return -1; /* overflow */
344 /* slow version of wcstombs_dbcs that handles the various flags */
345 static int wcstombs_dbcs_slow( const struct dbcs_table
*table
, int flags
,
346 const WCHAR
*src
, unsigned int srclen
,
347 char *dst
, unsigned int dstlen
,
348 const char *defchar
, int *used
)
350 const unsigned short * const uni2cp_low
= table
->uni2cp_low
;
351 const unsigned short * const uni2cp_high
= table
->uni2cp_high
;
352 WCHAR defchar_value
= get_defchar_dbcs( table
, defchar
);
356 if (!used
) used
= &tmp
; /* avoid checking on every char */
359 for (len
= dstlen
; srclen
&& len
; len
--, srclen
--, src
++)
364 if ((flags
& WC_COMPOSITECHECK
) && (srclen
> 1) && (composed
= wine_compose(src
)))
366 /* now check if we can use the composed char */
367 res
= uni2cp_low
[uni2cp_high
[composed
>> 8] + (composed
& 0xff)];
369 if (is_valid_dbcs_mapping( table
, flags
, composed
, res
))
371 /* we have a good mapping for the composed char, use it */
376 /* no mapping for the composed char, check the other flags */
377 if (flags
& WC_DEFAULTCHAR
) /* use the default char instead */
381 src
++; /* skip the non-spacing char */
385 if (flags
& WC_DISCARDNS
) /* skip the second char of the composition */
390 /* WC_SEPCHARS is the default */
393 res
= uni2cp_low
[uni2cp_high
[wch
>> 8] + (wch
& 0xff)];
394 if (!is_valid_dbcs_mapping( table
, flags
, wch
, res
))
403 if (len
== 1) break; /* do not output a partial char */
409 if (srclen
) return -1; /* overflow */
413 /* wide char to multi byte string conversion */
414 /* return -1 on dst buffer overflow */
415 int wine_cp_wcstombs_obsolete( const union cptable
*table
, int flags
,
416 const WCHAR
*src
, int srclen
,
417 char *dst
, int dstlen
, const char *defchar
, int *used
)
419 if (table
->info
.char_size
== 1)
421 if (flags
|| defchar
|| used
)
423 if (!dstlen
) return get_length_sbcs( &table
->sbcs
, flags
, src
, srclen
, used
);
424 return wcstombs_sbcs_slow( &table
->sbcs
, flags
, src
, srclen
,
425 dst
, dstlen
, defchar
, used
);
427 if (!dstlen
) return srclen
;
428 return wcstombs_sbcs( &table
->sbcs
, src
, srclen
, dst
, dstlen
);
432 if (!dstlen
) return get_length_dbcs( &table
->dbcs
, flags
, src
, srclen
, defchar
, used
);
433 if (flags
|| defchar
|| used
)
434 return wcstombs_dbcs_slow( &table
->dbcs
, flags
, src
, srclen
,
435 dst
, dstlen
, defchar
, used
);
436 return wcstombs_dbcs( &table
->dbcs
, src
, srclen
, dst
, dstlen
);
440 __ASM_OBSOLETE(wine_cp_wcstombs
);
442 #endif /* __ASM_OBSOLETE */