Moved scrollbar tracking code to scroll.c.
[wine] / unicode / wctomb.c
1 /*
2  * WideCharToMultiByte implementation
3  *
4  * Copyright 2000 Alexandre Julliard
5  */
6
7 #include <string.h>
8
9 #include "winnls.h"
10 #include "wine/unicode.h"
11
12 /* search for a character in the unicode_compose_table; helper for compose() */
13 static inline int binary_search( WCHAR ch, int low, int high )
14 {
15     extern const WCHAR unicode_compose_table[];
16     while (low <= high)
17     {
18         int pos = (low + high) / 2;
19         if (unicode_compose_table[2*pos] < ch)
20         {
21             low = pos + 1;
22             continue;
23         }
24         if (unicode_compose_table[2*pos] > ch)
25         {
26             high = pos - 1;
27             continue;
28         }
29         return pos;
30     }
31     return -1;
32 }
33
34 /* return the result of the composition of two Unicode chars, or 0 if none */
35 static WCHAR compose( const WCHAR *str )
36 {
37     extern const WCHAR unicode_compose_table[];
38     extern const unsigned int unicode_compose_table_size;
39
40     int idx = 1, low = 0, high = unicode_compose_table_size - 1;
41     for (;;)
42     {
43         int pos = binary_search( str[idx], low, high );
44         if (pos == -1) return 0;
45         if (!idx--) return unicode_compose_table[2*pos+1];
46         low = unicode_compose_table[2*pos+1];
47         high = unicode_compose_table[2*pos+3] - 1;
48     }
49 }
50
51
52 /****************************************************************/
53 /* sbcs support */
54
55 /* check if 'ch' is an acceptable sbcs mapping for 'wch' */
56 static inline int is_valid_sbcs_mapping( const struct sbcs_table *table, int flags,
57                                          WCHAR wch, unsigned char ch )
58 {
59     if (flags & WC_NO_BEST_FIT_CHARS) return (table->cp2uni[ch] == wch);
60     if (ch != (unsigned char)table->info.def_char) return 1;
61     return (wch == table->info.def_unicode_char);
62 }
63
64 /* query necessary dst length for src string */
65 static inline int get_length_sbcs( const struct sbcs_table *table, int flags,
66                                    const WCHAR *src, unsigned int srclen )
67 {
68     unsigned int ret = srclen;
69
70     if (flags & WC_COMPOSITECHECK)
71     {
72         const unsigned char  * const uni2cp_low = table->uni2cp_low;
73         const unsigned short * const uni2cp_high = table->uni2cp_high;
74         WCHAR composed;
75
76         for (ret = 0; srclen > 1; ret++, srclen--, src++)
77         {
78             if (!(composed = compose(src))) continue;
79             /* check if we should skip the next char */
80
81             /* in WC_DEFAULTCHAR and WC_DISCARDNS mode, we always skip */
82             /* the next char no matter if the composition is valid or not */
83             if (!(flags & (WC_DEFAULTCHAR|WC_DISCARDNS)))
84             {
85                 unsigned char ch = uni2cp_low[uni2cp_high[composed >> 8] + (composed & 0xff)];
86                 if (!is_valid_sbcs_mapping( table, flags, composed, ch )) continue;
87             }
88             src++;
89             srclen--;
90         }
91         if (srclen) ret++;  /* last char */
92     }
93     return ret;
94 }
95
96 /* wcstombs for single-byte code page */
97 static inline int wcstombs_sbcs( const struct sbcs_table *table,
98                                  const WCHAR *src, unsigned int srclen,
99                                  char *dst, unsigned int dstlen )
100 {
101     const unsigned char  * const uni2cp_low = table->uni2cp_low;
102     const unsigned short * const uni2cp_high = table->uni2cp_high;
103     int ret = srclen;
104
105     if (dstlen < srclen)
106     {
107         /* buffer too small: fill it up to dstlen and return error */
108         srclen = dstlen;
109         ret = -1;
110     }
111
112     for (;;)
113     {
114         switch(srclen)
115         {
116         default:
117         case 16: dst[15] = uni2cp_low[uni2cp_high[src[15] >> 8] + (src[15] & 0xff)];
118         case 15: dst[14] = uni2cp_low[uni2cp_high[src[14] >> 8] + (src[14] & 0xff)];
119         case 14: dst[13] = uni2cp_low[uni2cp_high[src[13] >> 8] + (src[13] & 0xff)];
120         case 13: dst[12] = uni2cp_low[uni2cp_high[src[12] >> 8] + (src[12] & 0xff)];
121         case 12: dst[11] = uni2cp_low[uni2cp_high[src[11] >> 8] + (src[11] & 0xff)];
122         case 11: dst[10] = uni2cp_low[uni2cp_high[src[10] >> 8] + (src[10] & 0xff)];
123         case 10: dst[9]  = uni2cp_low[uni2cp_high[src[9]  >> 8] + (src[9]  & 0xff)];
124         case 9:  dst[8]  = uni2cp_low[uni2cp_high[src[8]  >> 8] + (src[8]  & 0xff)];
125         case 8:  dst[7]  = uni2cp_low[uni2cp_high[src[7]  >> 8] + (src[7]  & 0xff)];
126         case 7:  dst[6]  = uni2cp_low[uni2cp_high[src[6]  >> 8] + (src[6]  & 0xff)];
127         case 6:  dst[5]  = uni2cp_low[uni2cp_high[src[5]  >> 8] + (src[5]  & 0xff)];
128         case 5:  dst[4]  = uni2cp_low[uni2cp_high[src[4]  >> 8] + (src[4]  & 0xff)];
129         case 4:  dst[3]  = uni2cp_low[uni2cp_high[src[3]  >> 8] + (src[3]  & 0xff)];
130         case 3:  dst[2]  = uni2cp_low[uni2cp_high[src[2]  >> 8] + (src[2]  & 0xff)];
131         case 2:  dst[1]  = uni2cp_low[uni2cp_high[src[1]  >> 8] + (src[1]  & 0xff)];
132         case 1:  dst[0]  = uni2cp_low[uni2cp_high[src[0]  >> 8] + (src[0]  & 0xff)];
133         case 0: break;
134         }
135         if (srclen < 16) return ret;
136         dst += 16;
137         src += 16;
138         srclen -= 16;
139     }
140 }
141
142 /* slow version of wcstombs_sbcs that handles the various flags */
143 static int wcstombs_sbcs_slow( const struct sbcs_table *table, int flags,
144                                const WCHAR *src, unsigned int srclen,
145                                char *dst, unsigned int dstlen,
146                                const char *defchar, int *used )
147 {
148     const unsigned char  * const uni2cp_low = table->uni2cp_low;
149     const unsigned short * const uni2cp_high = table->uni2cp_high;
150     const unsigned char table_default = table->info.def_char & 0xff;
151     unsigned int len;
152     int tmp;
153     WCHAR composed;
154
155     if (!defchar) defchar = &table_default;
156     if (!used) used = &tmp;  /* avoid checking on every char */
157     *used = 0;
158
159     for (len = dstlen; srclen && len; dst++, len--, src++, srclen--)
160     {
161         WCHAR wch = *src;
162
163         if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose(src)))
164         {
165             /* now check if we can use the composed char */
166             *dst = uni2cp_low[uni2cp_high[composed >> 8] + (composed & 0xff)];
167             if (is_valid_sbcs_mapping( table, flags, composed, *dst ))
168             {
169                 /* we have a good mapping, use it */
170                 src++;
171                 srclen--;
172                 continue;
173             }
174             /* no mapping for the composed char, check the other flags */
175             if (flags & WC_DEFAULTCHAR) /* use the default char instead */
176             {
177                 *dst = *defchar;
178                 *used = 1;
179                 src++;  /* skip the non-spacing char */
180                 srclen--;
181                 continue;
182             }
183             if (flags & WC_DISCARDNS) /* skip the second char of the composition */
184             {
185                 src++;
186                 srclen--;
187             }
188             /* WC_SEPCHARS is the default */
189         }
190
191         *dst = uni2cp_low[uni2cp_high[wch >> 8] + (wch & 0xff)];
192         if (!is_valid_sbcs_mapping( table, flags, wch, *dst ))
193         {
194             *dst = *defchar;
195             *used = 1;
196         }
197     }
198     if (srclen) return -1;  /* overflow */
199     return dstlen - len;
200 }
201
202
203 /****************************************************************/
204 /* dbcs support */
205
206 /* check if 'ch' is an acceptable dbcs mapping for 'wch' */
207 static inline int is_valid_dbcs_mapping( const struct dbcs_table *table, int flags,
208                                          WCHAR wch, unsigned short ch )
209 {
210     if (ch == table->info.def_char && wch != table->info.def_unicode_char) return 0;
211     if (flags & WC_NO_BEST_FIT_CHARS)
212     {
213         /* check if char maps back to the same Unicode value */
214         if (ch & 0xff00)
215         {
216             unsigned char off = table->cp2uni_leadbytes[ch >> 8];
217             return (table->cp2uni[(off << 8) + (ch & 0xff)] == wch);
218         }
219         return (table->cp2uni[ch & 0xff] == wch);
220     }
221     return 1;
222 }
223
224 /* query necessary dst length for src string */
225 static int get_length_dbcs( const struct dbcs_table *table, int flags,
226                             const WCHAR *src, unsigned int srclen,
227                             const char *defchar )
228 {
229     const unsigned short * const uni2cp_low = table->uni2cp_low;
230     const unsigned short * const uni2cp_high = table->uni2cp_high;
231     WCHAR defchar_value = table->info.def_char;
232     WCHAR composed;
233     int len;
234
235     if (!defchar && !(flags & WC_COMPOSITECHECK))
236     {
237         for (len = 0; srclen; srclen--, src++, len++)
238         {
239             if (uni2cp_low[uni2cp_high[*src >> 8] + (*src & 0xff)] & 0xff00) len++;
240         }
241         return len;
242     }
243
244     if (defchar) defchar_value = defchar[1] ? ((defchar[0] << 8) | defchar[1]) : defchar[0];
245     for (len = 0; srclen; len++, srclen--, src++)
246     {
247         unsigned short res;
248         WCHAR wch = *src;
249
250         if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose(src)))
251         {
252             /* now check if we can use the composed char */
253             res = uni2cp_low[uni2cp_high[composed >> 8] + (composed & 0xff)];
254
255             if (is_valid_dbcs_mapping( table, flags, composed, res ))
256             {
257                 /* we have a good mapping for the composed char, use it */
258                 if (res & 0xff00) len++;
259                 src++;
260                 srclen--;
261                 continue;
262             }
263             /* no mapping for the composed char, check the other flags */
264             if (flags & WC_DEFAULTCHAR) /* use the default char instead */
265             {
266                 if (defchar_value & 0xff00) len++;
267                 src++;  /* skip the non-spacing char */
268                 srclen--;
269                 continue;
270             }
271             if (flags & WC_DISCARDNS) /* skip the second char of the composition */
272             {
273                 src++;
274                 srclen--;
275             }
276             /* WC_SEPCHARS is the default */
277         }
278
279         res = uni2cp_low[uni2cp_high[wch >> 8] + (wch & 0xff)];
280         if (!is_valid_dbcs_mapping( table, flags, wch, res )) res = defchar_value;
281         if (res & 0xff00) len++;
282     }
283     return len;
284 }
285
286 /* wcstombs for double-byte code page */
287 static inline int wcstombs_dbcs( const struct dbcs_table *table,
288                                  const WCHAR *src, unsigned int srclen,
289                                  char *dst, unsigned int dstlen )
290 {
291     const unsigned short * const uni2cp_low = table->uni2cp_low;
292     const unsigned short * const uni2cp_high = table->uni2cp_high;
293     int len;
294
295     for (len = dstlen; srclen && len; len--, srclen--, src++)
296     {
297         unsigned short res = uni2cp_low[uni2cp_high[*src >> 8] + (*src & 0xff)];
298         if (res & 0xff00)
299         {
300             if (len == 1) break;  /* do not output a partial char */
301             len--;
302             *dst++ = res >> 8;
303         }
304         *dst++ = (char)res;
305     }
306     if (srclen) return -1;  /* overflow */
307     return dstlen - len;
308 }
309
310 /* slow version of wcstombs_dbcs that handles the various flags */
311 static int wcstombs_dbcs_slow( const struct dbcs_table *table, int flags,
312                                const WCHAR *src, unsigned int srclen,
313                                char *dst, unsigned int dstlen,
314                                const char *defchar, int *used )
315 {
316     const unsigned short * const uni2cp_low = table->uni2cp_low;
317     const unsigned short * const uni2cp_high = table->uni2cp_high;
318     WCHAR defchar_value = table->info.def_char;
319     WCHAR composed;
320     int len, tmp;
321
322     if (defchar) defchar_value = defchar[1] ? ((defchar[0] << 8) | defchar[1]) : defchar[0];
323     if (!used) used = &tmp;  /* avoid checking on every char */
324     *used = 0;
325
326     for (len = dstlen; srclen && len; len--, srclen--, src++)
327     {
328         unsigned short res;
329         WCHAR wch = *src;
330
331         if ((flags & WC_COMPOSITECHECK) && (srclen > 1) && (composed = compose(src)))
332         {
333             /* now check if we can use the composed char */
334             res = uni2cp_low[uni2cp_high[composed >> 8] + (composed & 0xff)];
335
336             if (is_valid_dbcs_mapping( table, flags, composed, res ))
337             {
338                 /* we have a good mapping for the composed char, use it */
339                 src++;
340                 srclen--;
341                 goto output_char;
342             }
343             /* no mapping for the composed char, check the other flags */
344             if (flags & WC_DEFAULTCHAR) /* use the default char instead */
345             {
346                 res = defchar_value;
347                 *used = 1;
348                 src++;  /* skip the non-spacing char */
349                 srclen--;
350                 goto output_char;
351             }
352             if (flags & WC_DISCARDNS) /* skip the second char of the composition */
353             {
354                 src++;
355                 srclen--;
356             }
357             /* WC_SEPCHARS is the default */
358         }
359
360         res = uni2cp_low[uni2cp_high[wch >> 8] + (wch & 0xff)];
361         if (!is_valid_dbcs_mapping( table, flags, wch, res ))
362         {
363             res = defchar_value;
364             *used = 1;
365         }
366
367     output_char:
368         if (res & 0xff00)
369         {
370             if (len == 1) break;  /* do not output a partial char */
371             len--;
372             *dst++ = res >> 8;
373         }
374         *dst++ = (char)res;
375     }
376     if (srclen) return -1;  /* overflow */
377     return dstlen - len;
378 }
379
380 /* wide char to multi byte string conversion */
381 /* return -1 on dst buffer overflow */
382 int cp_wcstombs( const union cptable *table, int flags,
383                  const WCHAR *src, int srclen,
384                  char *dst, int dstlen, const char *defchar, int *used )
385 {
386     if (table->info.char_size == 1)
387     {
388         if (!dstlen) return get_length_sbcs( &table->sbcs, flags, src, srclen );
389         if (flags || defchar || used)
390             return wcstombs_sbcs_slow( &table->sbcs, flags, src, srclen,
391                                        dst, dstlen, defchar, used );
392         return wcstombs_sbcs( &table->sbcs, src, srclen, dst, dstlen );
393     }
394     else /* mbcs */
395     {
396         if (!dstlen) return get_length_dbcs( &table->dbcs, flags, src, srclen, defchar );
397         if (flags || defchar || used)
398             return wcstombs_dbcs_slow( &table->dbcs, flags, src, srclen,
399                                        dst, dstlen, defchar, used );
400         return wcstombs_dbcs( &table->dbcs, src, srclen, dst, dstlen );
401     }
402 }