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