mshtml: Implement IHTMLDOMNode replaceChild.
[wine] / dlls / msvcrt / mbcs.c
1 /*
2  * msvcrt.dll mbcs functions
3  *
4  * Copyright 1999 Alexandre Julliard
5  * Copyright 2000 Jon Griffths
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * FIXME
22  * Not currently binary compatible with win32. MSVCRT_mbctype must be
23  * populated correctly and the ismb* functions should reference it.
24  */
25
26 #include <stdio.h>
27
28 #include "msvcrt.h"
29 #include "wine/unicode.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
33
34 unsigned char MSVCRT_mbctype[257] = { 0 };
35
36 /* It seems that the data about valid trail bytes is not available from kernel32
37  * so we have to store is here. The format is the same as for lead bytes in CPINFO */
38 struct cp_extra_info_t
39 {
40     int cp;
41     BYTE TrailBytes[MAX_LEADBYTES];
42 };
43
44 static struct cp_extra_info_t g_cpextrainfo[] =
45 {
46     {932, {0x40, 0x7e, 0x80, 0xfc, 0, 0}},
47     {936, {0x40, 0xfe, 0, 0}},
48     {949, {0x41, 0xfe, 0, 0}},
49     {950, {0x40, 0x7e, 0xa1, 0xfe, 0, 0}},
50     {1361, {0x31, 0x7e, 0x81, 0xfe, 0, 0}},
51     {20932, {1, 255, 0, 0}},  /* seems to give different results on different systems */
52     {0, {1, 255, 0, 0}}       /* match all with FIXME */
53 };
54
55 /* Maps multibyte cp932 punctuation marks to single byte equivalents */
56 static const unsigned char mbctombb_932_punct[] = {
57   0x20,0xa4,0xa1,0x2c,0x2e,0xa5,0x3a,0x3b,0x3f,0x21,0xde,0xdf,0x00,0x00,0x00,0x5e,
58   0x7e,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x00,0x00,0x2f,0x00,
59   0x00,0x00,0x7c,0x00,0x00,0x60,0x27,0x00,0x22,0x28,0x29,0x00,0x00,0x5b,0x5d,0x7b,
60   0x7d,0x00,0x00,0x00,0x00,0xa2,0xa3,0x00,0x00,0x00,0x00,0x2b,0x2d,0x00,0x00,0x00,
61   0x00,0x3d,0x00,0x3c,0x3e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5c,
62   0x24,0x00,0x00,0x25,0x23,0x26,0x2a,0x40};
63
64 /* Maps multibyte cp932 hiragana/katakana to single-byte equivalents */
65 static const unsigned char mbctombb_932_kana[] = {
66   0xa7,0xb1,0xa8,0xb2,0xa9,0xb3,0xaa,0xb4,0xab,0xb5,0xb6,0xb6,0xb7,0xb7,0xb8,0xb8,
67   0xb9,0xb9,0xba,0xba,0xbb,0xbb,0xbc,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbf,0xc0,0xc0,
68   0xc1,0xc1,0xaf,0xc2,0xc2,0xc3,0xc3,0xc4,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xca,
69   0xca,0xcb,0xcb,0xcb,0xcc,0xcc,0xcc,0xcd,0xcd,0xcd,0xce,0xce,0xce,0xcf,0xd0,0xd1,
70   0xd2,0xd3,0xac,0xd4,0xad,0xd5,0xae,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdc,0xb2,
71   0xb4,0xa6,0xdd,0xb3,0xb6,0xb9};
72
73 static MSVCRT_wchar_t msvcrt_mbc_to_wc(unsigned int ch)
74 {
75   MSVCRT_wchar_t chW;
76   char mbch[2];
77   int n_chars;
78
79   if (ch <= 0xff) {
80     mbch[0] = ch;
81     n_chars = 1;
82   } else {
83     mbch[0] = (ch >> 8) & 0xff;
84     mbch[1] = ch & 0xff;
85     n_chars = 2;
86   }
87   if (!MultiByteToWideChar(get_mbcinfo()->mbcodepage, 0, mbch, n_chars, &chW, 1))
88   {
89     WARN("MultiByteToWideChar failed on %x\n", ch);
90     return 0;
91   }
92   return chW;
93 }
94
95 static inline MSVCRT_size_t u_strlen( const unsigned char *str )
96 {
97   return strlen( (const char*) str );
98 }
99
100 static inline unsigned char* u_strncat( unsigned char* dst, const unsigned char* src, MSVCRT_size_t len )
101 {
102   return (unsigned char*)strncat( (char*)dst, (const char*)src, len);
103 }
104
105 static inline int u_strcmp( const unsigned char *s1, const unsigned char *s2 )
106 {
107   return strcmp( (const char*)s1, (const char*)s2 );
108 }
109
110 static inline int u_strcasecmp( const unsigned char *s1, const unsigned char *s2 )
111 {
112   return strcasecmp( (const char*)s1, (const char*)s2 );
113 }
114
115 static inline int u_strncmp( const unsigned char *s1, const unsigned char *s2, MSVCRT_size_t len )
116 {
117   return strncmp( (const char*)s1, (const char*)s2, len );
118 }
119
120 static inline int u_strncasecmp( const unsigned char *s1, const unsigned char *s2, MSVCRT_size_t len )
121 {
122   return strncasecmp( (const char*)s1, (const char*)s2, len );
123 }
124
125 static inline unsigned char *u_strchr( const unsigned char *s, unsigned char x )
126 {
127   return (unsigned char*) strchr( (const char*)s, x );
128 }
129
130 static inline unsigned char *u_strrchr( const unsigned char *s, unsigned char x )
131 {
132   return (unsigned char*) strrchr( (const char*)s, x );
133 }
134
135 static inline unsigned char *u_strtok( unsigned char *s, const unsigned char *delim )
136 {
137   return (unsigned char*) strtok( (char*)s, (const char*)delim );
138 }
139
140 static inline unsigned char *u__strset( unsigned char *s, unsigned char c )
141 {
142   return (unsigned char*) _strset( (char*)s, c);
143 }
144
145 static inline unsigned char *u__strnset( unsigned char *s, unsigned char c, MSVCRT_size_t len )
146 {
147   return (unsigned char*) MSVCRT__strnset( (char*)s, c, len );
148 }
149
150 static inline MSVCRT_size_t u_strcspn( const unsigned char *s, const unsigned char *rej )
151 {
152   return strcspn( (const char *)s, (const char*)rej );
153 }
154
155 /*********************************************************************
156  *              __p__mbctype (MSVCRT.@)
157  */
158 unsigned char* CDECL __p__mbctype(void)
159 {
160   return get_mbcinfo()->mbctype;
161 }
162
163 /*********************************************************************
164  *              ___mb_cur_max_func(MSVCRT.@)
165  */
166 int* CDECL MSVCRT____mb_cur_max_func(void)
167 {
168   return &get_locinfo()->mb_cur_max;
169 }
170
171 /* ___mb_cur_max_l_func - not exported in native msvcrt */
172 int* CDECL ___mb_cur_max_l_func(MSVCRT__locale_t locale)
173 {
174   MSVCRT_pthreadlocinfo locinfo;
175
176   if(!locale)
177     locinfo = get_locinfo();
178   else
179     locinfo = locale->locinfo;
180
181   return &locinfo->mb_cur_max;
182 }
183
184 /*********************************************************************
185  *              _setmbcp (MSVCRT.@)
186  */
187 int CDECL _setmbcp(int cp)
188 {
189   const char format[] = ".%d";
190
191   MSVCRT_pthreadmbcinfo mbcinfo = get_mbcinfo();
192   char buf[32];
193   int newcp;
194   CPINFO cpi;
195   BYTE *bytes;
196   WORD chartypes[256];
197   WORD *curr_type;
198   char bufA[256];
199   WCHAR bufW[256];
200   int charcount;
201   int ret;
202   int i;
203
204   switch (cp)
205   {
206     case _MB_CP_ANSI:
207       newcp = GetACP();
208       break;
209     case _MB_CP_OEM:
210       newcp = GetOEMCP();
211       break;
212     case _MB_CP_LOCALE:
213       newcp = get_locinfo()->lc_codepage;
214       if(newcp)
215           break;
216       /* fall through (C locale) */
217     case _MB_CP_SBCS:
218       newcp = 20127;   /* ASCII */
219       break;
220     default:
221       newcp = cp;
222       break;
223   }
224
225   sprintf(buf, format, newcp);
226   mbcinfo->mblcid = MSVCRT_locale_to_LCID(buf);
227   if(mbcinfo->mblcid == -1)
228   {
229     WARN("Can't assign LCID to codepage (%d)\n", mbcinfo->mblcid);
230     mbcinfo->mblcid = 0;
231   }
232
233   if (!GetCPInfo(newcp, &cpi))
234   {
235     WARN("Codepage %d not found\n", newcp);
236     *MSVCRT__errno() = MSVCRT_EINVAL;
237     return -1;
238   }
239
240   /* setup the _mbctype */
241   memset(mbcinfo->mbctype, 0, sizeof(unsigned char[257]));
242   memset(mbcinfo->mbcasemap, 0, sizeof(unsigned char[256]));
243
244   bytes = cpi.LeadByte;
245   while (bytes[0] || bytes[1])
246   {
247     for (i = bytes[0]; i <= bytes[1]; i++)
248       mbcinfo->mbctype[i + 1] |= _M1;
249     bytes += 2;
250   }
251
252   if (cpi.MaxCharSize > 1)
253   {
254     /* trail bytes not available through kernel32 but stored in a structure in msvcrt */
255     struct cp_extra_info_t *cpextra = g_cpextrainfo;
256
257     mbcinfo->ismbcodepage = 1;
258     while (TRUE)
259     {
260       if (cpextra->cp == 0 || cpextra->cp == newcp)
261       {
262         if (cpextra->cp == 0)
263           FIXME("trail bytes data not available for DBCS codepage %d - assuming all bytes\n", newcp);
264
265         bytes = cpextra->TrailBytes;
266         while (bytes[0] || bytes[1])
267         {
268           for (i = bytes[0]; i <= bytes[1]; i++)
269             mbcinfo->mbctype[i + 1] |= _M2;
270           bytes += 2;
271         }
272         break;
273       }
274       cpextra++;
275     }
276   }
277   else
278     mbcinfo->ismbcodepage = 0;
279
280   /* we can't use GetStringTypeA directly because we don't have a locale - only a code page
281    */
282   charcount = 0;
283   for (i = 0; i < 256; i++)
284     if (!(mbcinfo->mbctype[i + 1] & _M1))
285       bufA[charcount++] = i;
286
287   ret = MultiByteToWideChar(newcp, 0, bufA, charcount, bufW, charcount);
288   if (ret != charcount)
289     ERR("MultiByteToWideChar of chars failed for cp %d, ret=%d (exp %d), error=%d\n", newcp, ret, charcount, GetLastError());
290
291   GetStringTypeW(CT_CTYPE1, bufW, charcount, chartypes);
292
293   curr_type = chartypes;
294   for (i = 0; i < 256; i++)
295     if (!(mbcinfo->mbctype[i + 1] & _M1))
296     {
297         if ((*curr_type) & C1_UPPER)
298             mbcinfo->mbctype[i + 1] |= _SBUP;
299         if ((*curr_type) & C1_LOWER)
300             mbcinfo->mbctype[i + 1] |= _SBLOW;
301         curr_type++;
302     }
303
304   if (newcp == 932)   /* CP932 only - set _MP and _MS */
305   {
306     /* On Windows it's possible to calculate the _MP and _MS from CT_CTYPE1
307      * and CT_CTYPE3. But as of Wine 0.9.43 we return wrong values what makes
308      * it hard. As this is set only for codepage 932 we hardcode it what gives
309      * also faster execution.
310      */
311     for (i = 161; i <= 165; i++)
312       mbcinfo->mbctype[i + 1] |= _MP;
313     for (i = 166; i <= 223; i++)
314       mbcinfo->mbctype[i + 1] |= _MS;
315   }
316
317   mbcinfo->mbcodepage = newcp;
318   if(mbcinfo == MSVCRT_locale->mbcinfo)
319     memcpy(MSVCRT_mbctype, MSVCRT_locale->mbcinfo->mbctype, sizeof(MSVCRT_mbctype));
320
321   return 0;
322 }
323
324 /*********************************************************************
325  *              _getmbcp (MSVCRT.@)
326  */
327 int CDECL _getmbcp(void)
328 {
329   return get_mbcinfo()->mbcodepage;
330 }
331
332 /*********************************************************************
333  *              _mbsnextc(MSVCRT.@)
334  */
335 unsigned int CDECL _mbsnextc(const unsigned char* str)
336 {
337   if(_ismbblead(*str))
338     return *str << 8 | str[1];
339   return *str;
340 }
341
342 /*********************************************************************
343  *              _mbctolower(MSVCRT.@)
344  */
345 unsigned int CDECL _mbctolower(unsigned int c)
346 {
347     if (_ismbblead(c))
348     {
349       FIXME("Handle MBC chars\n");
350       return c;
351     }
352     return tolower(c); /* ASCII CP or SB char */
353 }
354
355 /*********************************************************************
356  *              _mbctoupper(MSVCRT.@)
357  */
358 unsigned int CDECL _mbctoupper(unsigned int c)
359 {
360     if (_ismbblead(c))
361     {
362       FIXME("Handle MBC chars\n");
363       return c;
364     }
365     return toupper(c); /* ASCII CP or SB char */
366 }
367
368 /*********************************************************************
369  *              _mbctombb (MSVCRT.@)
370  */
371 unsigned int CDECL _mbctombb(unsigned int c)
372 {
373     unsigned int value;
374
375     if(get_mbcinfo()->mbcodepage == 932)
376     {
377         if(c >= 0x829f && c <= 0x82f1)    /* Hiragana */
378             return mbctombb_932_kana[c - 0x829f];
379         if(c >= 0x8340 && c <= 0x8396 && c != 0x837f)    /* Katakana */
380             return mbctombb_932_kana[c - 0x8340 - (c >= 0x837f ? 1 : 0)];
381         if(c >= 0x8140 && c <= 0x8197)    /* Punctuation */
382         {
383             value = mbctombb_932_punct[c - 0x8140];
384             return value ? value : c;
385         }
386         if((c >= 0x824f && c <= 0x8258) || /* Fullwidth digits */
387            (c >= 0x8260 && c <= 0x8279))   /* Fullwidth capitals letters */
388             return c - 0x821f;
389         if(c >= 0x8281 && c <= 0x829a)     /* Fullwidth small letters */
390             return c - 0x8220;
391         /* all other cases return c */
392     }
393     return c;
394 }
395
396 /*********************************************************************
397  *              _mbcjistojms(MSVCRT.@)
398  *
399  *              Converts a jis character to sjis.
400  *              Based on description from
401  *              http://www.slayers.ne.jp/~oouchi/code/jistosjis.html
402  */
403 unsigned int CDECL _mbcjistojms(unsigned int c)
404 {
405   /* Conversion takes place only when codepage is 932.
406      In all other cases, c is returned unchanged */
407   if(get_mbcinfo()->mbcodepage == 932)
408   {
409     if(HIBYTE(c) >= 0x21 && HIBYTE(c) <= 0x7e &&
410        LOBYTE(c) >= 0x21 && LOBYTE(c) <= 0x7e)
411     {
412       if(HIBYTE(c) % 2)
413         c += 0x1f;
414       else
415         c += 0x7d;
416
417       if(LOBYTE(c) > 0x7F)
418         c += 0x1;
419
420       c = (((HIBYTE(c) - 0x21)/2 + 0x81) << 8) | LOBYTE(c);
421
422       if(HIBYTE(c) > 0x9f)
423         c += 0x4000;
424     }
425     else
426       return 0; /* Codepage is 932, but c can't be converted */
427   }
428
429   return c;
430 }
431
432 /*********************************************************************
433  *              _mbsdec(MSVCRT.@)
434  */
435 unsigned char* CDECL _mbsdec(const unsigned char* start, const unsigned char* cur)
436 {
437   if(get_mbcinfo()->ismbcodepage)
438     return (unsigned char *)(_ismbstrail(start,cur-1) ? cur - 2 : cur -1);
439
440   return (unsigned char *)cur - 1; /* ASCII CP or SB char */
441 }
442
443 /*********************************************************************
444  *              _mbclen(MSVCRT.@)
445  */
446 unsigned int CDECL _mbclen(const unsigned char* str)
447 {
448   return _ismbblead(*str) ? 2 : 1;
449 }
450
451 /*********************************************************************
452  *              _mbsinc(MSVCRT.@)
453  */
454 unsigned char* CDECL _mbsinc(const unsigned char* str)
455 {
456   return (unsigned char *)(str + _mbclen(str));
457 }
458
459 /*********************************************************************
460  *              _mbsninc(MSVCRT.@)
461  */
462 unsigned char* CDECL _mbsninc(const unsigned char* str, MSVCRT_size_t num)
463 {
464   if(!str)
465     return NULL;
466
467   while (num > 0 && *str)
468   {
469     if (_ismbblead(*str))
470     {
471       if (!*(str+1))
472          break;
473       str++;
474     }
475     str++;
476     num--;
477   }
478
479   return (unsigned char*)str;
480 }
481
482 /*********************************************************************
483  *              _mbslen(MSVCRT.@)
484  */
485 MSVCRT_size_t CDECL _mbslen(const unsigned char* str)
486 {
487   MSVCRT_size_t len = 0;
488   while(*str)
489   {
490     if (_ismbblead(*str))
491     {
492       str++;
493       if (!*str)  /* count only full chars */
494         break;
495     }
496     str++;
497     len++;
498   }
499   return len;
500 }
501
502 /*********************************************************************
503  *              _mbccpy(MSVCRT.@)
504  */
505 void CDECL _mbccpy(unsigned char* dest, const unsigned char* src)
506 {
507   *dest = *src;
508   if(_ismbblead(*src))
509     *++dest = *++src; /* MB char */
510 }
511
512 /*********************************************************************
513  *              _mbsncpy(MSVCRT.@)
514  * REMARKS
515  *  The parameter n is the number or characters to copy, not the size of
516  *  the buffer. Use _mbsnbcpy for a function analogical to strncpy
517  */
518 unsigned char* CDECL _mbsncpy(unsigned char* dst, const unsigned char* src, MSVCRT_size_t n)
519 {
520   unsigned char* ret = dst;
521   if(!n)
522     return dst;
523   if (get_mbcinfo()->ismbcodepage)
524   {
525     while (*src && n)
526     {
527       n--;
528       if (_ismbblead(*src))
529       {
530         if (!*(src+1))
531         {
532             *dst++ = 0;
533             *dst++ = 0;
534             break;
535         }
536
537         *dst++ = *src++;
538       }
539
540       *dst++ = *src++;
541     }
542   }
543   else
544   {
545     while (n)
546     {
547         n--;
548         if (!(*dst++ = *src++)) break;
549     }
550   }
551   while (n--) *dst++ = 0;
552   return ret;
553 }
554
555 /*********************************************************************
556  *              _mbsnbcpy_s(MSVCRT.@)
557  * REMARKS
558  * Unlike _mbsnbcpy this function does not pad the rest of the dest
559  * string with 0
560  */
561 int CDECL _mbsnbcpy_s(unsigned char* dst, MSVCRT_size_t size, const unsigned char* src, MSVCRT_size_t n)
562 {
563     MSVCRT_size_t pos = 0;
564
565     if(!dst || size == 0)
566         return MSVCRT_EINVAL;
567     if(!src)
568     {
569         dst[0] = '\0';
570         return MSVCRT_EINVAL;
571     }
572     if(!n)
573         return 0;
574
575     if(get_mbcinfo()->ismbcodepage)
576     {
577         int is_lead = 0;
578         while (*src && n)
579         {
580             if(pos == size)
581             {
582                 dst[0] = '\0';
583                 return MSVCRT_ERANGE;
584             }
585             is_lead = (!is_lead && _ismbblead(*src));
586             n--;
587             dst[pos++] = *src++;
588         }
589
590         if (is_lead) /* if string ends with a lead, remove it */
591             dst[pos - 1] = 0;
592     }
593     else
594     {
595         while (n)
596         {
597             n--;
598             if(pos == size)
599             {
600                 dst[0] = '\0';
601                 return MSVCRT_ERANGE;
602             }
603
604             if(!(*src)) break;
605             dst[pos++] = *src++;
606         }
607     }
608
609     if(pos < size)
610         dst[pos] = '\0';
611     else
612     {
613         dst[0] = '\0';
614         return MSVCRT_ERANGE;
615     }
616
617     return 0;
618 }
619
620 /*********************************************************************
621  *              _mbsnbcpy(MSVCRT.@)
622  * REMARKS
623  *  Like strncpy this function doesn't enforce the string to be
624  *  NUL-terminated
625  */
626 unsigned char* CDECL _mbsnbcpy(unsigned char* dst, const unsigned char* src, MSVCRT_size_t n)
627 {
628   unsigned char* ret = dst;
629   if(!n)
630     return dst;
631   if(get_mbcinfo()->ismbcodepage)
632   {
633     int is_lead = 0;
634     while (*src && n)
635     {
636       is_lead = (!is_lead && _ismbblead(*src));
637       n--;
638       *dst++ = *src++;
639     }
640
641     if (is_lead) /* if string ends with a lead, remove it */
642         *(dst - 1) = 0;
643   }
644   else
645   {
646     while (n)
647     {
648         n--;
649         if (!(*dst++ = *src++)) break;
650     }
651   }
652   while (n--) *dst++ = 0;
653   return ret;
654 }
655
656 /*********************************************************************
657  *              _mbscmp(MSVCRT.@)
658  */
659 int CDECL _mbscmp(const unsigned char* str, const unsigned char* cmp)
660 {
661   if(get_mbcinfo()->ismbcodepage)
662   {
663     unsigned int strc, cmpc;
664     do {
665       if(!*str)
666         return *cmp ? -1 : 0;
667       if(!*cmp)
668         return 1;
669       strc = _mbsnextc(str);
670       cmpc = _mbsnextc(cmp);
671       if(strc != cmpc)
672         return strc < cmpc ? -1 : 1;
673       str +=(strc > 255) ? 2 : 1;
674       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
675     } while(1);
676   }
677   return u_strcmp(str, cmp); /* ASCII CP */
678 }
679
680 /*********************************************************************
681  *              _mbsicoll(MSVCRT.@)
682  * FIXME: handle locales.
683  */
684 int CDECL _mbsicoll(const unsigned char* str, const unsigned char* cmp)
685 {
686   if(get_mbcinfo()->ismbcodepage)
687   {
688     unsigned int strc, cmpc;
689     do {
690       if(!*str)
691         return *cmp ? -1 : 0;
692       if(!*cmp)
693         return 1;
694       strc = _mbctolower(_mbsnextc(str));
695       cmpc = _mbctolower(_mbsnextc(cmp));
696       if(strc != cmpc)
697         return strc < cmpc ? -1 : 1;
698       str +=(strc > 255) ? 2 : 1;
699       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
700     } while(1);
701   }
702   return u_strcasecmp(str, cmp); /* ASCII CP */
703 }
704
705 /*********************************************************************
706  *              _mbscoll(MSVCRT.@)
707  * Performs a case-sensitive comparison according to the current code page
708  * RETURN
709  *   _NLSCMPERROR if error
710  * FIXME: handle locales.
711  */
712 int CDECL _mbscoll(const unsigned char* str, const unsigned char* cmp)
713 {
714   if(get_mbcinfo()->ismbcodepage)
715   {
716     unsigned int strc, cmpc;
717     do {
718       if(!*str)
719         return *cmp ? -1 : 0;
720       if(!*cmp)
721         return 1;
722       strc = _mbsnextc(str);
723       cmpc = _mbsnextc(cmp);
724       if(strc != cmpc)
725         return strc < cmpc ? -1 : 1;
726       str +=(strc > 255) ? 2 : 1;
727       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
728     } while(1);
729   }
730   return u_strcmp(str, cmp); /* ASCII CP */
731 }
732
733
734 /*********************************************************************
735  *              _mbsicmp(MSVCRT.@)
736  */
737 int CDECL _mbsicmp(const unsigned char* str, const unsigned char* cmp)
738 {
739   if(get_mbcinfo()->ismbcodepage)
740   {
741     unsigned int strc, cmpc;
742     do {
743       if(!*str)
744         return *cmp ? -1 : 0;
745       if(!*cmp)
746         return 1;
747       strc = _mbctolower(_mbsnextc(str));
748       cmpc = _mbctolower(_mbsnextc(cmp));
749       if(strc != cmpc)
750         return strc < cmpc ? -1 : 1;
751       str +=(strc > 255) ? 2 : 1;
752       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
753     } while(1);
754   }
755   return u_strcasecmp(str, cmp); /* ASCII CP */
756 }
757
758 /*********************************************************************
759  *              _mbsncmp(MSVCRT.@)
760  */
761 int CDECL _mbsncmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
762 {
763   if(!len)
764     return 0;
765
766   if(get_mbcinfo()->ismbcodepage)
767   {
768     unsigned int strc, cmpc;
769     while(len--)
770     {
771       int inc;
772       if(!*str)
773         return *cmp ? -1 : 0;
774       if(!*cmp)
775         return 1;
776       strc = _mbsnextc(str);
777       cmpc = _mbsnextc(cmp);
778       if(strc != cmpc)
779         return strc < cmpc ? -1 : 1;
780       inc=(strc > 255) ? 2 : 1; /* Equal, use same increment */
781       str += inc;
782       cmp += inc;
783     }
784     return 0; /* Matched len chars */
785   }
786   return u_strncmp(str, cmp, len); /* ASCII CP */
787 }
788
789 /*********************************************************************
790  *              _mbsnbcmp(MSVCRT.@)
791  */
792 int CDECL _mbsnbcmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
793 {
794   if (!len)
795     return 0;
796   if(get_mbcinfo()->ismbcodepage)
797   {
798     unsigned int strc, cmpc;
799     while (len)
800     {
801       int clen;
802       if(!*str)
803         return *cmp ? -1 : 0;
804       if(!*cmp)
805         return 1;
806       if (_ismbblead(*str))
807       {
808         strc=(len>=2)?_mbsnextc(str):0;
809         clen=2;
810       }
811       else
812       {
813         strc=*str;
814         clen=1;
815       }
816       if (_ismbblead(*cmp))
817         cmpc=(len>=2)?_mbsnextc(cmp):0;
818       else
819         cmpc=*str;
820       if(strc != cmpc)
821         return strc < cmpc ? -1 : 1;
822       len -= clen;
823       str += clen;
824       cmp += clen;
825     }
826     return 0; /* Matched len chars */
827   }
828   return u_strncmp(str,cmp,len);
829 }
830
831 /*********************************************************************
832  *              _mbsnicmp(MSVCRT.@)
833  *
834  * Compare two multibyte strings case insensitively to 'len' characters.
835  */
836 int CDECL _mbsnicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
837 {
838   /* FIXME: No tolower() for mb strings yet */
839   if(get_mbcinfo()->ismbcodepage)
840   {
841     unsigned int strc, cmpc;
842     while(len--)
843     {
844       if(!*str)
845         return *cmp ? -1 : 0;
846       if(!*cmp)
847         return 1;
848       strc = _mbctolower(_mbsnextc(str));
849       cmpc = _mbctolower(_mbsnextc(cmp));
850       if(strc != cmpc)
851         return strc < cmpc ? -1 : 1;
852       str +=(strc > 255) ? 2 : 1;
853       cmp +=(strc > 255) ? 2 : 1; /* Equal, use same increment */
854     }
855     return 0; /* Matched len chars */
856   }
857   return u_strncasecmp(str, cmp, len); /* ASCII CP */
858 }
859
860 /*********************************************************************
861  *              _mbsnbicmp(MSVCRT.@)
862  */
863 int CDECL _mbsnbicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
864 {
865   if (!len)
866     return 0;
867   if(get_mbcinfo()->ismbcodepage)
868   {
869     unsigned int strc, cmpc;
870     while (len)
871     {
872       int clen;
873       if(!*str)
874         return *cmp ? -1 : 0;
875       if(!*cmp)
876         return 1;
877       if (_ismbblead(*str))
878       {
879         strc=(len>=2)?_mbsnextc(str):0;
880         clen=2;
881       }
882       else
883       {
884         strc=*str;
885         clen=1;
886       }
887       if (_ismbblead(*cmp))
888         cmpc=(len>=2)?_mbsnextc(cmp):0;
889       else
890         cmpc=*str;
891       strc = _mbctolower(strc);
892       cmpc = _mbctolower(cmpc);
893       if(strc != cmpc)
894         return strc < cmpc ? -1 : 1;
895       len -= clen;
896       str += clen;
897       cmp += clen;
898     }
899     return 0; /* Matched len bytes */
900   }
901   return u_strncasecmp(str,cmp,len);
902 }
903
904 /*********************************************************************
905  *              _mbscat (MSVCRT.@)
906  */
907 unsigned char * CDECL _mbscat( unsigned char *dst, const unsigned char *src )
908 {
909     strcat( (char *)dst, (const char *)src );
910     return dst;
911 }
912
913 /*********************************************************************
914  *              _mbscpy (MSVCRT.@)
915  */
916 unsigned char* CDECL _mbscpy( unsigned char *dst, const unsigned char *src )
917 {
918     strcpy( (char *)dst, (const char *)src );
919     return dst;
920 }
921
922 /*********************************************************************
923  *              _mbsstr (MSVCRT.@)
924  */
925 unsigned char * CDECL _mbsstr(const unsigned char *haystack, const unsigned char *needle)
926 {
927     return (unsigned char *)strstr( (const char *)haystack, (const char *)needle );
928 }
929
930 /*********************************************************************
931  *              _mbschr(MSVCRT.@)
932  *
933  * Find a multibyte character in a multibyte string.
934  */
935 unsigned char* CDECL _mbschr(const unsigned char* s, unsigned int x)
936 {
937   if(get_mbcinfo()->ismbcodepage)
938   {
939     unsigned int c;
940     while (1)
941     {
942       c = _mbsnextc(s);
943       if (c == x)
944         return (unsigned char*)s;
945       if (!c)
946         return NULL;
947       s += c > 255 ? 2 : 1;
948     }
949   }
950   return u_strchr(s, x); /* ASCII CP */
951 }
952
953 /*********************************************************************
954  *              _mbsrchr(MSVCRT.@)
955  */
956 unsigned char* CDECL _mbsrchr(const unsigned char* s, unsigned int x)
957 {
958   if(get_mbcinfo()->ismbcodepage)
959   {
960     unsigned int c;
961     unsigned char* match=NULL;
962     if(!s)
963       return NULL;
964     while (1) {
965       c = _mbsnextc(s);
966       if (c == x)
967         match=(unsigned char*)s;
968       if (!c)
969         return match;
970       s +=(c > 255) ? 2 : 1;
971     }
972   }
973   return u_strrchr(s, x);
974 }
975
976 /*********************************************************************
977  *              _mbstok(MSVCRT.@)
978  *
979  * Find and extract tokens from strings
980  */
981 unsigned char* CDECL _mbstok(unsigned char *str, const unsigned char *delim)
982 {
983     thread_data_t *data = msvcrt_get_thread_data();
984     unsigned char *ret;
985
986     if(get_mbcinfo()->ismbcodepage)
987     {
988         unsigned int c;
989
990         if (!str)
991             if (!(str = data->mbstok_next)) return NULL;
992
993         while ((c = _mbsnextc(str)) && _mbschr(delim, c)) {
994             str += c > 255 ? 2 : 1;
995         }
996         if (!*str) return NULL;
997         ret = str++;
998         while ((c = _mbsnextc(str)) && !_mbschr(delim, c)) {
999             str += c > 255 ? 2 : 1;
1000         }
1001         if (*str) {
1002             *str++ = 0;
1003             if (c > 255) *str++ = 0;
1004         }
1005         data->mbstok_next = str;
1006         return ret;
1007     }
1008     return u_strtok(str, delim); /* ASCII CP */
1009 }
1010
1011 /*********************************************************************
1012  *              _mbbtombc(MSVCRT.@)
1013  */
1014 unsigned int CDECL _mbbtombc(unsigned int c)
1015 {
1016   if(get_mbcinfo()->ismbcodepage &&
1017      ((c >= 0x20 && c <=0x7e) ||(c >= 0xa1 && c <= 0xdf)))
1018   {
1019     /* FIXME: I can't get this function to return anything
1020      * different from what I pass it...
1021      */
1022   }
1023   return c;  /* ASCII CP or no MB char */
1024 }
1025
1026 /*********************************************************************
1027  *              _mbbtype(MSVCRT.@)
1028  */
1029 int CDECL _mbbtype(unsigned char c, int type)
1030 {
1031     if (type == 1)
1032     {
1033         if ((c >= 0x20 && c <= 0x7e) || (c >= 0xa1 && c <= 0xdf))
1034             return _MBC_SINGLE;
1035         else if ((c >= 0x40 && c <= 0x7e) || (c >= 0x80 && c <= 0xfc))
1036             return _MBC_TRAIL;
1037         else
1038             return _MBC_ILLEGAL;
1039     }
1040     else
1041     {
1042         if ((c >= 0x20 && c <= 0x7e) || (c >= 0xa1 && c <= 0xdf))
1043             return _MBC_SINGLE;
1044         else if ((c >= 0x81 && c <= 0x9f) || (c >= 0xe0 && c <= 0xfc))
1045             return _MBC_LEAD;
1046         else
1047             return _MBC_ILLEGAL;
1048     }
1049 }
1050
1051 /*********************************************************************
1052  *              _ismbbkana(MSVCRT.@)
1053  */
1054 int CDECL _ismbbkana(unsigned int c)
1055 {
1056   /* FIXME: use lc_ctype when supported, not lc_all */
1057   if(get_mbcinfo()->mbcodepage == 932)
1058   {
1059     /* Japanese/Katakana, CP 932 */
1060     return (c >= 0xa1 && c <= 0xdf);
1061   }
1062   return 0;
1063 }
1064
1065 /*********************************************************************
1066  *              _ismbcdigit(MSVCRT.@)
1067  */
1068 int CDECL _ismbcdigit(unsigned int ch)
1069 {
1070     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1071     return (get_char_typeW( wch ) & C1_DIGIT);
1072 }
1073
1074 /*********************************************************************
1075  *              _ismbcgraph(MSVCRT.@)
1076  */
1077 int CDECL _ismbcgraph(unsigned int ch)
1078 {
1079     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1080     return (get_char_typeW( wch ) & (C1_UPPER | C1_LOWER | C1_DIGIT | C1_PUNCT | C1_ALPHA));
1081 }
1082
1083 /*********************************************************************
1084  *              _ismbcalpha (MSVCRT.@)
1085  */
1086 int CDECL _ismbcalpha(unsigned int ch)
1087 {
1088     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1089     return (get_char_typeW( wch ) & C1_ALPHA);
1090 }
1091
1092 /*********************************************************************
1093  *              _ismbclower (MSVCRT.@)
1094  */
1095 int CDECL _ismbclower(unsigned int ch)
1096 {
1097     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1098     return (get_char_typeW( wch ) & C1_UPPER);
1099 }
1100
1101 /*********************************************************************
1102  *              _ismbcupper (MSVCRT.@)
1103  */
1104 int CDECL _ismbcupper(unsigned int ch)
1105 {
1106     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1107     return (get_char_typeW( wch ) & C1_LOWER);
1108 }
1109
1110 /*********************************************************************
1111  *              _ismbcsymbol(MSVCRT.@)
1112  */
1113 int CDECL _ismbcsymbol(unsigned int ch)
1114 {
1115     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1116     WORD ctype;
1117     if (!GetStringTypeW(CT_CTYPE3, &wch, 1, &ctype))
1118     {
1119         WARN("GetStringTypeW failed on %x\n", ch);
1120         return 0;
1121     }
1122     return ((ctype & C3_SYMBOL) != 0);
1123 }
1124
1125 /*********************************************************************
1126  *              _ismbcalnum (MSVCRT.@)
1127  */
1128 int CDECL _ismbcalnum(unsigned int ch)
1129 {
1130     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1131     return (get_char_typeW( wch ) & (C1_ALPHA | C1_DIGIT));
1132 }
1133
1134 /*********************************************************************
1135  *              _ismbcspace (MSVCRT.@)
1136  */
1137 int CDECL _ismbcspace(unsigned int ch)
1138 {
1139     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1140     return (get_char_typeW( wch ) & C1_SPACE);
1141 }
1142
1143 /*********************************************************************
1144  *              _ismbcprint (MSVCRT.@)
1145  */
1146 int CDECL _ismbcprint(unsigned int ch)
1147 {
1148     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1149     return (get_char_typeW( wch ) & (C1_UPPER | C1_LOWER | C1_DIGIT | C1_PUNCT | C1_ALPHA | C1_SPACE));
1150 }
1151
1152 /*********************************************************************
1153  *              _ismbcpunct(MSVCRT.@)
1154  */
1155 int CDECL _ismbcpunct(unsigned int ch)
1156 {
1157     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
1158     return (get_char_typeW( wch ) & C1_PUNCT);
1159 }
1160
1161 /*********************************************************************
1162  *              _ismbchira(MSVCRT.@)
1163  */
1164 int CDECL _ismbchira(unsigned int c)
1165 {
1166   /* FIXME: use lc_ctype when supported, not lc_all */
1167   if(get_mbcinfo()->mbcodepage == 932)
1168   {
1169     /* Japanese/Hiragana, CP 932 */
1170     return (c >= 0x829f && c <= 0x82f1);
1171   }
1172   return 0;
1173 }
1174
1175 /*********************************************************************
1176  *              _ismbckata(MSVCRT.@)
1177  */
1178 int CDECL _ismbckata(unsigned int c)
1179 {
1180   /* FIXME: use lc_ctype when supported, not lc_all */
1181   if(get_mbcinfo()->mbcodepage == 932)
1182   {
1183     if(c < 256)
1184       return _ismbbkana(c);
1185     /* Japanese/Katakana, CP 932 */
1186     return (c >= 0x8340 && c <= 0x8396 && c != 0x837f);
1187   }
1188   return 0;
1189 }
1190
1191 /*********************************************************************
1192  *              _ismbblead(MSVCRT.@)
1193  */
1194 int CDECL _ismbblead(unsigned int c)
1195 {
1196   return (get_mbcinfo()->mbctype[(c&0xff) + 1] & _M1) != 0;
1197 }
1198
1199
1200 /*********************************************************************
1201  *              _ismbbtrail(MSVCRT.@)
1202  */
1203 int CDECL _ismbbtrail(unsigned int c)
1204 {
1205   return (get_mbcinfo()->mbctype[(c&0xff) + 1] & _M2) != 0;
1206 }
1207
1208 /*********************************************************************
1209  *              _ismbclegal(MSVCRT.@)
1210  */
1211 int CDECL _ismbclegal(unsigned int c)
1212 {
1213     return _ismbblead(HIBYTE(c)) && _ismbbtrail(LOBYTE(c));
1214 }
1215
1216 /*********************************************************************
1217  *              _ismbslead(MSVCRT.@)
1218  */
1219 int CDECL _ismbslead(const unsigned char* start, const unsigned char* str)
1220 {
1221   int lead = 0;
1222
1223   if(!get_mbcinfo()->ismbcodepage)
1224     return 0;
1225
1226   /* Lead bytes can also be trail bytes so we need to analyse the string
1227    */
1228   while (start <= str)
1229   {
1230     if (!*start)
1231       return 0;
1232     lead = !lead && _ismbblead(*start);
1233     start++;
1234   }
1235
1236   return lead ? -1 : 0;
1237 }
1238
1239 /*********************************************************************
1240  *              _ismbstrail(MSVCRT.@)
1241  */
1242 int CDECL _ismbstrail(const unsigned char* start, const unsigned char* str)
1243 {
1244   /* Note: this function doesn't check _ismbbtrail */
1245   if ((str > start) && _ismbslead(start, str-1))
1246     return -1;
1247   else
1248     return 0;
1249 }
1250
1251 /*********************************************************************
1252  *              _mbsbtype (MSVCRT.@)
1253  */
1254 int CDECL _mbsbtype(const unsigned char *str, MSVCRT_size_t count)
1255 {
1256   int lead = 0;
1257   const unsigned char *end = str + count;
1258
1259   /* Lead bytes can also be trail bytes so we need to analyse the string.
1260    * Also we must return _MBC_ILLEGAL for chars past the end of the string
1261    */
1262   while (str < end) /* Note: we skip the last byte - will check after the loop */
1263   {
1264     if (!*str)
1265       return _MBC_ILLEGAL;
1266     lead = get_mbcinfo()->ismbcodepage && !lead && _ismbblead(*str);
1267     str++;
1268   }
1269
1270   if (lead)
1271     if (_ismbbtrail(*str))
1272       return _MBC_TRAIL;
1273     else
1274       return _MBC_ILLEGAL;
1275   else
1276     if (_ismbblead(*str))
1277       return _MBC_LEAD;
1278     else
1279       return _MBC_SINGLE;
1280 }
1281
1282 /*********************************************************************
1283  *              _mbsset(MSVCRT.@)
1284  */
1285 unsigned char* CDECL _mbsset(unsigned char* str, unsigned int c)
1286 {
1287   unsigned char* ret = str;
1288
1289   if(!get_mbcinfo()->ismbcodepage || c < 256)
1290     return u__strset(str, c); /* ASCII CP or SB char */
1291
1292   c &= 0xffff; /* Strip high bits */
1293
1294   while(str[0] && str[1])
1295   {
1296     *str++ = c >> 8;
1297     *str++ = c & 0xff;
1298   }
1299   if(str[0])
1300     str[0] = '\0'; /* FIXME: OK to shorten? */
1301
1302   return ret;
1303 }
1304
1305 /*********************************************************************
1306  *              _mbsnbset(MSVCRT.@)
1307  */
1308 unsigned char* CDECL _mbsnbset(unsigned char *str, unsigned int c, MSVCRT_size_t len)
1309 {
1310     unsigned char *ret = str;
1311
1312     if(!len)
1313         return ret;
1314
1315     if(!get_mbcinfo()->ismbcodepage || c < 256)
1316         return u__strnset(str, c, len); /* ASCII CP or SB char */
1317
1318     c &= 0xffff; /* Strip high bits */
1319
1320     while(str[0] && str[1] && (len > 1))
1321     {
1322         *str++ = c >> 8;
1323         len--;
1324         *str++ = c & 0xff;
1325         len--;
1326     }
1327     if(len && str[0]) {
1328         /* as per msdn pad with a blank character */
1329         str[0] = ' ';
1330     }
1331
1332     return ret;
1333 }
1334
1335 /*********************************************************************
1336  *              _mbsnset(MSVCRT.@)
1337  */
1338 unsigned char* CDECL _mbsnset(unsigned char* str, unsigned int c, MSVCRT_size_t len)
1339 {
1340   unsigned char *ret = str;
1341
1342   if(!len)
1343     return ret;
1344
1345   if(!get_mbcinfo()->ismbcodepage || c < 256)
1346     return u__strnset(str, c, len); /* ASCII CP or SB char */
1347
1348   c &= 0xffff; /* Strip high bits */
1349
1350   while(str[0] && str[1] && len--)
1351   {
1352     *str++ = c >> 8;
1353     *str++ = c & 0xff;
1354   }
1355   if(len && str[0])
1356     str[0] = '\0'; /* FIXME: OK to shorten? */
1357
1358   return ret;
1359 }
1360
1361 /*********************************************************************
1362  *              _mbsnccnt(MSVCRT.@)
1363  * 'c' is for 'character'.
1364  */
1365 MSVCRT_size_t CDECL _mbsnccnt(const unsigned char* str, MSVCRT_size_t len)
1366 {
1367   MSVCRT_size_t ret;
1368   if(get_mbcinfo()->ismbcodepage)
1369   {
1370     ret=0;
1371     while(*str && len-- > 0)
1372     {
1373       if(_ismbblead(*str))
1374       {
1375         if (!len)
1376           break;
1377         len--;
1378         str++;
1379       }
1380       str++;
1381       ret++;
1382     }
1383     return ret;
1384   }
1385   ret=u_strlen(str);
1386   return min(ret, len); /* ASCII CP */
1387 }
1388
1389 /*********************************************************************
1390  *              _mbsnbcnt(MSVCRT.@)
1391  * 'b' is for byte count.
1392  */
1393 MSVCRT_size_t CDECL _mbsnbcnt(const unsigned char* str, MSVCRT_size_t len)
1394 {
1395   MSVCRT_size_t ret;
1396   if(get_mbcinfo()->ismbcodepage)
1397   {
1398     const unsigned char* xstr = str;
1399     while(*xstr && len-- > 0)
1400     {
1401       if (_ismbblead(*xstr++))
1402         xstr++;
1403     }
1404     return xstr-str;
1405   }
1406   ret=u_strlen(str);
1407   return min(ret, len); /* ASCII CP */
1408 }
1409
1410 /*********************************************************************
1411  *              _mbsnbcat(MSVCRT.@)
1412  */
1413 unsigned char* CDECL _mbsnbcat(unsigned char* dst, const unsigned char* src, MSVCRT_size_t len)
1414 {
1415     if(get_mbcinfo()->ismbcodepage)
1416     {
1417         unsigned char *res = dst;
1418         while (*dst) {
1419             if (_ismbblead(*dst++)) {
1420                 if (*dst) {
1421                     dst++;
1422                 } else {
1423                     /* as per msdn overwrite the lead byte in front of '\0' */
1424                     dst--;
1425                     break;
1426                 }
1427             }
1428         }
1429         while (*src && len--) *dst++ = *src++;
1430         *dst = '\0';
1431         return res;
1432     }
1433     return u_strncat(dst, src, len); /* ASCII CP */
1434 }
1435
1436 int CDECL _mbsnbcat_s(unsigned char *dst, MSVCRT_size_t size, const unsigned char *src, MSVCRT_size_t len)
1437 {
1438     unsigned char *ptr = dst;
1439     MSVCRT_size_t i;
1440
1441     if (!dst && !size && !src && !len)
1442         return 0;
1443
1444     if (!dst || !size || !src)
1445     {
1446         if (dst && size)
1447             *dst = '\0';
1448
1449         *MSVCRT__errno() = MSVCRT_EINVAL;
1450         return MSVCRT_EINVAL;
1451     }
1452
1453     /* Find the null terminator of the destination buffer. */
1454     while (size && *ptr)
1455         size--, ptr++;
1456
1457     if (!size)
1458     {
1459         *dst = '\0';
1460         *MSVCRT__errno() = MSVCRT_EINVAL;
1461         return MSVCRT_EINVAL;
1462     }
1463
1464     /* If necessary, check that the character preceding the null terminator is
1465      * a lead byte and move the pointer back by one for later overwrite. */
1466     if (ptr != dst && get_mbcinfo()->ismbcodepage && _ismbblead(*(ptr - 1)))
1467         size++, ptr--;
1468
1469     for (i = 0; *src && i < len; i++)
1470     {
1471         *ptr++ = *src++;
1472         size--;
1473
1474         if (!size)
1475         {
1476             *dst = '\0';
1477             *MSVCRT__errno() = MSVCRT_ERANGE;
1478             return MSVCRT_ERANGE;
1479         }
1480     }
1481
1482     *ptr = '\0';
1483     return 0;
1484 }
1485
1486 /*********************************************************************
1487  *              _mbsncat(MSVCRT.@)
1488  */
1489 unsigned char* CDECL _mbsncat(unsigned char* dst, const unsigned char* src, MSVCRT_size_t len)
1490 {
1491   if(get_mbcinfo()->ismbcodepage)
1492   {
1493     unsigned char *res = dst;
1494     while (*dst)
1495     {
1496       if (_ismbblead(*dst++))
1497         dst++;
1498     }
1499     while (*src && len--)
1500     {
1501       *dst++ = *src;
1502       if(_ismbblead(*src++))
1503         *dst++ = *src++;
1504     }
1505     *dst = '\0';
1506     return res;
1507   }
1508   return u_strncat(dst, src, len); /* ASCII CP */
1509 }
1510
1511
1512 /*********************************************************************
1513  *              _mbslwr(MSVCRT.@)
1514  */
1515 unsigned char* CDECL _mbslwr(unsigned char* s)
1516 {
1517   unsigned char *ret = s;
1518   if (!s)
1519     return NULL;
1520   if (get_mbcinfo()->ismbcodepage)
1521   {
1522     unsigned int c;
1523     while (*s)
1524     {
1525       c = _mbctolower(_mbsnextc(s));
1526       /* Note that I assume that the size of the character is unchanged */
1527       if (c > 255)
1528       {
1529           *s++=(c>>8);
1530           c=c & 0xff;
1531       }
1532       *s++=c;
1533     }
1534   }
1535   else for ( ; *s; s++) *s = tolower(*s);
1536   return ret;
1537 }
1538
1539
1540 /*********************************************************************
1541  *              _mbslwr_s(MSVCRT.@)
1542  */
1543 int CDECL _mbslwr_s(unsigned char* s, MSVCRT_size_t len)
1544 {
1545   if (!s && !len)
1546   {
1547     return 0;
1548   }
1549   else if (!s || !len)
1550   {
1551     *MSVCRT__errno() = MSVCRT_EINVAL;
1552     return MSVCRT_EINVAL;
1553   }
1554   if (get_mbcinfo()->ismbcodepage)
1555   {
1556     unsigned int c;
1557     for ( ; *s && len > 0; len--)
1558     {
1559       c = _mbctolower(_mbsnextc(s));
1560       /* Note that I assume that the size of the character is unchanged */
1561       if (c > 255)
1562       {
1563           *s++=(c>>8);
1564           c=c & 0xff;
1565       }
1566       *s++=c;
1567     }
1568   }
1569   else for ( ; *s && len > 0; s++, len--) *s = tolower(*s);
1570   if (*s)
1571   {
1572     *s = '\0';
1573     *MSVCRT__errno() = MSVCRT_EINVAL;
1574     return MSVCRT_EINVAL;
1575   }
1576   return 0;
1577 }
1578
1579
1580 /*********************************************************************
1581  *              _mbsupr(MSVCRT.@)
1582  */
1583 unsigned char* CDECL _mbsupr(unsigned char* s)
1584 {
1585   unsigned char *ret = s;
1586   if (!s)
1587     return NULL;
1588   if (get_mbcinfo()->ismbcodepage)
1589   {
1590     unsigned int c;
1591     while (*s)
1592     {
1593       c = _mbctoupper(_mbsnextc(s));
1594       /* Note that I assume that the size of the character is unchanged */
1595       if (c > 255)
1596       {
1597           *s++=(c>>8);
1598           c=c & 0xff;
1599       }
1600       *s++=c;
1601     }
1602   }
1603   else for ( ; *s; s++) *s = toupper(*s);
1604   return ret;
1605 }
1606
1607
1608 /*********************************************************************
1609  *              _mbsupr_s(MSVCRT.@)
1610  */
1611 int CDECL _mbsupr_s(unsigned char* s, MSVCRT_size_t len)
1612 {
1613   if (!s && !len)
1614   {
1615     return 0;
1616   }
1617   else if (!s || !len)
1618   {
1619     *MSVCRT__errno() = MSVCRT_EINVAL;
1620     return MSVCRT_EINVAL;
1621   }
1622   if (get_mbcinfo()->ismbcodepage)
1623   {
1624     unsigned int c;
1625     for ( ; *s && len > 0; len--)
1626     {
1627       c = _mbctoupper(_mbsnextc(s));
1628       /* Note that I assume that the size of the character is unchanged */
1629       if (c > 255)
1630       {
1631           *s++=(c>>8);
1632           c=c & 0xff;
1633       }
1634       *s++=c;
1635     }
1636   }
1637   else for ( ; *s && len > 0; s++, len--) *s = toupper(*s);
1638   if (*s)
1639   {
1640     *s = '\0';
1641     *MSVCRT__errno() = MSVCRT_EINVAL;
1642     return MSVCRT_EINVAL;
1643   }
1644   return 0;
1645 }
1646
1647
1648 /*********************************************************************
1649  *              _mbsspn (MSVCRT.@)
1650  */
1651 MSVCRT_size_t CDECL _mbsspn(const unsigned char* string, const unsigned char* set)
1652 {
1653     const unsigned char *p, *q;
1654
1655     for (p = string; *p; p++)
1656     {
1657         if (_ismbblead(*p))
1658         {
1659             for (q = set; *q; q++)
1660             {
1661                 if (!q[1])
1662                     break;
1663                 if ((*p == *q) &&  (p[1] == q[1]))
1664                     break;
1665                 q++;
1666             }
1667             if (!q[0] || !q[1]) break;
1668         }
1669         else
1670         {
1671             for (q = set; *q; q++)
1672                 if (*p == *q)
1673                     break;
1674             if (!*q) break;
1675         }
1676     }
1677     return p - string;
1678 }
1679
1680 /*********************************************************************
1681  *              _mbsspnp (MSVCRT.@)
1682  */
1683 unsigned char* CDECL _mbsspnp(const unsigned char* string, const unsigned char* set)
1684 {
1685     const unsigned char *p, *q;
1686
1687     for (p = string; *p; p++)
1688     {
1689         if (_ismbblead(*p))
1690         {
1691             for (q = set; *q; q++)
1692             {
1693                 if (!q[1])
1694                     break;
1695                 if ((*p == *q) &&  (p[1] == q[1]))
1696                     break;
1697                 q++;
1698             }
1699             if (!q[0] || !q[1]) break;
1700         }
1701         else
1702         {
1703             for (q = set; *q; q++)
1704                 if (*p == *q)
1705                     break;
1706             if (!*q) break;
1707         }
1708     }
1709     if (*p == '\0')
1710         return NULL;
1711     return (unsigned char *)p;
1712 }
1713
1714 /*********************************************************************
1715  *              _mbscspn(MSVCRT.@)
1716  */
1717 MSVCRT_size_t CDECL _mbscspn(const unsigned char* str, const unsigned char* cmp)
1718 {
1719   if (get_mbcinfo()->ismbcodepage)
1720     FIXME("don't handle double character case\n");
1721   return u_strcspn(str, cmp);
1722 }
1723
1724 /*********************************************************************
1725  *              _mbsrev (MSVCRT.@)
1726  */
1727 unsigned char* CDECL _mbsrev(unsigned char* str)
1728 {
1729     int i, len = _mbslen(str);
1730     unsigned char *p, *temp=MSVCRT_malloc(len*2);
1731
1732     if(!temp)
1733         return str;
1734
1735     /* unpack multibyte string to temp buffer */
1736     p=str;
1737     for(i=0; i<len; i++)
1738     {
1739         if (_ismbblead(*p))
1740         {
1741             temp[i*2]=*p++;
1742             temp[i*2+1]=*p++;
1743         }
1744         else
1745         {
1746             temp[i*2]=*p++;
1747             temp[i*2+1]=0;
1748         }
1749     }
1750
1751     /* repack it in the reverse order */
1752     p=str;
1753     for(i=len-1; i>=0; i--)
1754     {
1755         if(_ismbblead(temp[i*2]))
1756         {
1757             *p++=temp[i*2];
1758             *p++=temp[i*2+1];
1759         }
1760         else
1761         {
1762             *p++=temp[i*2];
1763         }
1764     }
1765
1766     MSVCRT_free(temp);
1767
1768     return str;
1769 }
1770
1771 /*********************************************************************
1772  *              _mbspbrk (MSVCRT.@)
1773  */
1774 unsigned char* CDECL _mbspbrk(const unsigned char* str, const unsigned char* accept)
1775 {
1776     const unsigned char* p;
1777
1778     while(*str)
1779     {
1780         for(p = accept; *p; p += (_ismbblead(*p)?2:1) )
1781         {
1782             if (*p == *str)
1783                 if( !_ismbblead(*p) || ( *(p+1) == *(str+1) ) )
1784                      return (unsigned char*)str;
1785         }
1786         str += (_ismbblead(*str)?2:1);
1787     }
1788     return NULL;
1789 }
1790
1791
1792 /*
1793  * Functions depending on locale codepage
1794  */
1795
1796 /*********************************************************************
1797  *              mblen(MSVCRT.@)
1798  * REMARKS
1799  *  Unlike most of the multibyte string functions this function uses
1800  *  the locale codepage, not the codepage set by _setmbcp
1801  */
1802 int CDECL MSVCRT_mblen(const char* str, MSVCRT_size_t size)
1803 {
1804   if (str && *str && size)
1805   {
1806     if(get_locinfo()->mb_cur_max == 1)
1807       return 1; /* ASCII CP */
1808
1809     return !MSVCRT_isleadbyte(*str) ? 1 : (size>1 ? 2 : -1);
1810   }
1811   return 0;
1812 }
1813
1814 /*********************************************************************
1815  *              _mbstrlen_l(MSVCRT.@)
1816  */
1817 MSVCRT_size_t CDECL _mbstrlen_l(const char* str, MSVCRT__locale_t locale)
1818 {
1819     MSVCRT_pthreadlocinfo locinfo;
1820
1821     if(!locale)
1822         locinfo = get_locinfo();
1823     else
1824         locinfo = locale->locinfo;
1825
1826     if(locinfo->mb_cur_max > 1) {
1827         MSVCRT_size_t len = 0;
1828         while(*str) {
1829             /* FIXME: According to the documentation we are supposed to test for
1830              * multi-byte character validity. Whatever that means
1831              */
1832             str += MSVCRT_isleadbyte(*str) ? 2 : 1;
1833             len++;
1834         }
1835         return len;
1836     }
1837
1838     return strlen(str);
1839 }
1840
1841 /*********************************************************************
1842  *              _mbstrlen(MSVCRT.@)
1843  */
1844 MSVCRT_size_t CDECL _mbstrlen(const char* str)
1845 {
1846     return _mbstrlen_l(str, NULL);
1847 }
1848
1849 /*********************************************************************
1850  *              _mbtowc_l(MSVCRT.@)
1851  */
1852 int CDECL MSVCRT_mbtowc_l(MSVCRT_wchar_t *dst, const char* str, MSVCRT_size_t n, MSVCRT__locale_t locale)
1853 {
1854     MSVCRT_pthreadlocinfo locinfo;
1855     MSVCRT_wchar_t tmpdst = '\0';
1856
1857     if(!locale)
1858         locinfo = get_locinfo();
1859     else
1860         locinfo = locale->locinfo;
1861
1862     if(n <= 0 || !str)
1863         return 0;
1864     if(!MultiByteToWideChar(locinfo->lc_codepage, 0, str, n, &tmpdst, 1))
1865         return -1;
1866     if(dst)
1867         *dst = tmpdst;
1868     /* return the number of bytes from src that have been used */
1869     if(!*str)
1870         return 0;
1871     if(n >= 2 && MSVCRT__isleadbyte_l(*str, locale) && str[1])
1872         return 2;
1873     return 1;
1874 }
1875
1876 /*********************************************************************
1877  *              mbtowc(MSVCRT.@)
1878  */
1879 int CDECL MSVCRT_mbtowc(MSVCRT_wchar_t *dst, const char* str, MSVCRT_size_t n)
1880 {
1881     return MSVCRT_mbtowc_l(dst, str, n, NULL);
1882 }
1883
1884 /*********************************************************************
1885  *              _mbstowcs_l(MSVCRT.@)
1886  */
1887 MSVCRT_size_t CDECL MSVCRT__mbstowcs_l(MSVCRT_wchar_t *wcstr, const char *mbstr,
1888         MSVCRT_size_t count, MSVCRT__locale_t locale)
1889 {
1890     MSVCRT_pthreadlocinfo locinfo;
1891     MSVCRT_size_t i, size;
1892
1893     if(!locale)
1894         locinfo = get_locinfo();
1895     else
1896         locinfo = locale->locinfo;
1897
1898     /* Ignore count parameter */
1899     if(!wcstr)
1900         return MultiByteToWideChar(locinfo->lc_codepage, 0, mbstr, -1, NULL, 0)-1;
1901
1902     for(i=0, size=0; i<count; i++) {
1903         if(mbstr[size] == '\0')
1904             break;
1905
1906         size += (MSVCRT_isleadbyte(mbstr[size]) ? 2 : 1);
1907     }
1908
1909     size = MultiByteToWideChar(locinfo->lc_codepage, 0,
1910             mbstr, size, wcstr, count);
1911
1912     if(size<count && wcstr)
1913         wcstr[size] = '\0';
1914
1915     return size;
1916 }
1917
1918 /*********************************************************************
1919  *              mbstowcs(MSVCRT.@)
1920  */
1921 MSVCRT_size_t CDECL MSVCRT_mbstowcs(MSVCRT_wchar_t *wcstr,
1922         const char *mbstr, MSVCRT_size_t count)
1923 {
1924     return MSVCRT__mbstowcs_l(wcstr, mbstr, count, NULL);
1925 }
1926
1927 /*********************************************************************
1928  *              _mbstowcs_s_l(MSVCRT.@)
1929  */
1930 int CDECL MSVCRT__mbstowcs_s_l(MSVCRT_size_t *ret, MSVCRT_wchar_t *wcstr,
1931         MSVCRT_size_t size, const char *mbstr,
1932         MSVCRT_size_t count, MSVCRT__locale_t locale)
1933 {
1934     MSVCRT_size_t conv;
1935
1936     if(!wcstr && !size) {
1937         conv = MSVCRT__mbstowcs_l(NULL, mbstr, 0, locale);
1938         if(ret)
1939             *ret = conv+1;
1940         return 0;
1941     }
1942
1943     if(!MSVCRT_CHECK_PMT(mbstr != NULL) || !MSVCRT_CHECK_PMT(wcstr != NULL)) {
1944         if(wcstr && size)
1945             wcstr[0] = '\0';
1946         *MSVCRT__errno() = MSVCRT_EINVAL;
1947         return MSVCRT_EINVAL;
1948     }
1949
1950     if(count==MSVCRT__TRUNCATE || size<count)
1951         conv = size;
1952     else
1953         conv = count;
1954
1955     conv = MSVCRT__mbstowcs_l(wcstr, mbstr, conv, locale);
1956     if(conv<size)
1957         wcstr[conv++] = '\0';
1958     else if(conv==size && (count==MSVCRT__TRUNCATE || wcstr[conv-1]=='\0'))
1959         wcstr[conv-1] = '\0';
1960     else {
1961         MSVCRT_INVALID_PMT("wcstr[size] is too small");
1962         if(size)
1963             wcstr[0] = '\0';
1964         *MSVCRT__errno() = MSVCRT_ERANGE;
1965         return MSVCRT_ERANGE;
1966     }
1967
1968     if(ret)
1969         *ret = conv;
1970     return 0;
1971 }
1972
1973 /*********************************************************************
1974  *              mbstowcs_s(MSVCRT.@)
1975  */
1976 int CDECL MSVCRT__mbstowcs_s(MSVCRT_size_t *ret, MSVCRT_wchar_t *wcstr,
1977         MSVCRT_size_t size, const char *mbstr, MSVCRT_size_t count)
1978 {
1979     return MSVCRT__mbstowcs_s_l(ret, wcstr, size, mbstr, count, NULL);
1980 }