Tests for recent variant changes.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "msvcrt.h"
27
28 #include "msvcrt/mbctype.h"
29 #include "msvcrt/mbstring.h"
30 #include "msvcrt/stdlib.h"
31 #include "msvcrt/string.h"
32 #include "msvcrt/wctype.h"
33
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
38
39 unsigned char MSVCRT_mbctype[257];
40 int MSVCRT___mb_cur_max = 1;
41
42 static MSVCRT_wchar_t msvcrt_mbc_to_wc(unsigned int ch)
43 {
44   MSVCRT_wchar_t chW;
45   char mbch[2];
46   int n_chars;
47
48   if (ch <= 0xff) {
49     mbch[0] = ch;
50     n_chars = 1;
51   } else {
52     mbch[0] = (ch >> 8) & 0xff;
53     mbch[1] = ch & 0xff;
54     n_chars = 2;
55   }
56   if (!MultiByteToWideChar(MSVCRT_current_lc_all_cp, 0, mbch, n_chars, &chW, 1))
57   {
58     WARN("MultiByteToWideChar failed on %x\n", ch);
59     return 0;
60   }
61   return chW;
62 }
63
64 /*********************************************************************
65  *              __p__mbctype (MSVCRT.@)
66  */
67 unsigned char* __p__mbctype(void)
68 {
69   return MSVCRT_mbctype;
70 }
71
72 /*********************************************************************
73  *              __p___mb_cur_max(MSVCRT.@)
74  */
75 int* __p___mb_cur_max(void)
76 {
77   return &MSVCRT___mb_cur_max;
78 }
79
80 /*********************************************************************
81  *              _mbsnextc(MSVCRT.@)
82  */
83 unsigned int _mbsnextc(const unsigned char* str)
84 {
85   if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*str))
86     return *str << 8 | str[1];
87   return *str; /* ASCII CP or SB char */
88 }
89
90 /*********************************************************************
91  *              _mbctolower(MSVCRT.@)
92  */
93 unsigned int _mbctolower(unsigned int c)
94 {
95     if (MSVCRT_isleadbyte(c))
96     {
97       FIXME("Handle MBC chars\n");
98       return c;
99     }
100     return tolower(c); /* ASCII CP or SB char */
101 }
102
103 /*********************************************************************
104  *              _mbctoupper(MSVCRT.@)
105  */
106 unsigned int _mbctoupper(unsigned int c)
107 {
108     if (MSVCRT_isleadbyte(c))
109     {
110       FIXME("Handle MBC chars\n");
111       return c;
112     }
113     return toupper(c); /* ASCII CP or SB char */
114 }
115
116 /*********************************************************************
117  *              _mbsdec(MSVCRT.@)
118  */
119 unsigned char* _mbsdec(const unsigned char* start, const unsigned char* cur)
120 {
121   if(MSVCRT___mb_cur_max > 1)
122     return (char *)(_ismbstrail(start,cur-1) ? cur - 2 : cur -1);
123
124   return (char *)cur - 1; /* ASCII CP or SB char */
125 }
126
127 /*********************************************************************
128  *              _mbsinc(MSVCRT.@)
129  */
130 unsigned char* _mbsinc(const unsigned char* str)
131 {
132   if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*str))
133     return (unsigned char*)str + 2; /* MB char */
134
135   return (unsigned char*)str + 1; /* ASCII CP or SB char */
136 }
137
138 /*********************************************************************
139  *              _mbsninc(MSVCRT.@)
140  */
141 unsigned char* _mbsninc(const unsigned char* str, MSVCRT_size_t num)
142 {
143   if(!str || num < 1)
144     return NULL;
145   if(MSVCRT___mb_cur_max > 1)
146   {
147     while(num--)
148       str = _mbsinc(str);
149     return (unsigned char*)str;
150   }
151   return (unsigned char*)str + num; /* ASCII CP */
152 }
153
154 /*********************************************************************
155  *              _mbclen(MSVCRT.@)
156  */
157 unsigned int _mbclen(const unsigned char* str)
158 {
159   return MSVCRT_isleadbyte(*str) ? 2 : 1;
160 }
161
162 /*********************************************************************
163  *              mblen(MSVCRT.@)
164  */
165 int MSVCRT_mblen(const char* str, MSVCRT_size_t size)
166 {
167   if (str && *str && size)
168   {
169     if(MSVCRT___mb_cur_max == 1)
170       return 1; /* ASCII CP */
171
172     return !MSVCRT_isleadbyte(*str) ? 1 : (size>1 ? 2 : -1);
173   }
174   return 0;
175 }
176
177 /*********************************************************************
178  *              _mbslen(MSVCRT.@)
179  */
180 MSVCRT_size_t _mbslen(const unsigned char* str)
181 {
182   if(MSVCRT___mb_cur_max > 1)
183   {
184     MSVCRT_size_t len = 0;
185     while(*str)
186     {
187       str += MSVCRT_isleadbyte(*str) ? 2 : 1;
188       len++;
189     }
190     return len;
191   }
192   return strlen(str); /* ASCII CP */
193 }
194
195 /*********************************************************************
196  *              _mbstrlen(MSVCRT.@)
197  */
198 MSVCRT_size_t _mbstrlen(const char* str)
199 {
200   if(MSVCRT___mb_cur_max > 1)
201   {
202     MSVCRT_size_t len = 0;
203     while(*str)
204     {
205       /* FIXME: According to the documentation we are supposed to test for
206        * multi-byte character validity. Whatever that means
207        */
208       str += MSVCRT_isleadbyte(*str) ? 2 : 1;
209       len++;
210     }
211     return len;
212   }
213   return strlen(str); /* ASCII CP */
214 }
215
216 /*********************************************************************
217  *              _mbccpy(MSVCRT.@)
218  */
219 void _mbccpy(unsigned char* dest, const unsigned char* src)
220 {
221   *dest++ = *src;
222   if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*src))
223     *dest = *++src; /* MB char */
224   else
225     ERR("failure.. is this ok?\n");
226 }
227
228 /*********************************************************************
229  *              _mbsncpy(MSVCRT.@)
230  */
231 unsigned char* _mbsncpy(unsigned char* dst, const unsigned char* src, MSVCRT_size_t n)
232 {
233   if(!n)
234     return dst;
235   if(MSVCRT___mb_cur_max > 1)
236   {
237     unsigned char* ret = dst;
238     while (*src && n--)
239     {
240       *dst++ = *src;
241       if (MSVCRT_isleadbyte(*src++))
242           *dst++ = *src++;
243     }
244     while(n--)
245       *dst++ = '\0';
246     return ret;
247   }
248   return strncpy(dst, src, n); /* ASCII CP */
249 }
250
251 /*********************************************************************
252  *              _mbsnbcpy(MSVCRT.@)
253  */
254 unsigned char* _mbsnbcpy(unsigned char* dst, const unsigned char* src, MSVCRT_size_t n)
255 {
256   if(!n)
257     return dst;
258   if(MSVCRT___mb_cur_max > 1)
259   {
260     unsigned char* ret = dst;
261     while (*src && (n-- > 1))
262     {
263       *dst++ = *src;
264       if (MSVCRT_isleadbyte(*src++))
265       {
266         *dst++ = *src++;
267         n--;
268       }
269     }
270     if (*src && n && !MSVCRT_isleadbyte(*src))
271     {
272       /* If the last character is a multi-byte character then
273        * we cannot copy it since we have only one byte left
274        */
275       *dst++ = *src;
276       n--;
277     }
278     while (n--)
279       *dst++ = '\0';
280     return ret;
281   }
282   return strncpy(dst, src, n); /* ASCII CP */
283 }
284
285 /*********************************************************************
286  *              _mbscmp(MSVCRT.@)
287  */
288 int _mbscmp(const unsigned char* str, const unsigned char* cmp)
289 {
290   if(MSVCRT___mb_cur_max > 1)
291   {
292     unsigned int strc, cmpc;
293     do {
294       if(!*str)
295         return *cmp ? -1 : 0;
296       if(!*cmp)
297         return 1;
298       strc = _mbsnextc(str);
299       cmpc = _mbsnextc(cmp);
300       if(strc != cmpc)
301         return strc < cmpc ? -1 : 1;
302       str +=(strc > 255) ? 2 : 1;
303       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
304     } while(1);
305   }
306   return strcmp(str, cmp); /* ASCII CP */
307 }
308
309 /*********************************************************************
310  *              _mbsicoll(MSVCRT.@)
311  * FIXME: handle locales.
312  */
313 int _mbsicoll(const unsigned char* str, const unsigned char* cmp)
314 {
315   if(MSVCRT___mb_cur_max > 1)
316   {
317     unsigned int strc, cmpc;
318     do {
319       if(!*str)
320         return *cmp ? -1 : 0;
321       if(!*cmp)
322         return 1;
323       strc = _mbctolower(_mbsnextc(str));
324       cmpc = _mbctolower(_mbsnextc(cmp));
325       if(strc != cmpc)
326         return strc < cmpc ? -1 : 1;
327       str +=(strc > 255) ? 2 : 1;
328       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
329     } while(1);
330   }
331   return strcasecmp(str, cmp); /* ASCII CP */
332 }
333
334
335 /*********************************************************************
336  *              _mbsicmp(MSVCRT.@)
337  */
338 int _mbsicmp(const unsigned char* str, const unsigned char* cmp)
339 {
340   if(MSVCRT___mb_cur_max > 1)
341   {
342     unsigned int strc, cmpc;
343     do {
344       if(!*str)
345         return *cmp ? -1 : 0;
346       if(!*cmp)
347         return 1;
348       strc = _mbctolower(_mbsnextc(str));
349       cmpc = _mbctolower(_mbsnextc(cmp));
350       if(strc != cmpc)
351         return strc < cmpc ? -1 : 1;
352       str +=(strc > 255) ? 2 : 1;
353       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
354     } while(1);
355   }
356   return strcasecmp(str, cmp); /* ASCII CP */
357 }
358
359 /*********************************************************************
360  *              _mbsncmp(MSVCRT.@)
361  */
362 int _mbsncmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
363 {
364   if(!len)
365     return 0;
366
367   if(MSVCRT___mb_cur_max > 1)
368   {
369     unsigned int strc, cmpc;
370     while(len--)
371     {
372       int inc;
373       if(!*str)
374         return *cmp ? -1 : 0;
375       if(!*cmp)
376         return 1;
377       strc = _mbsnextc(str);
378       cmpc = _mbsnextc(cmp);
379       if(strc != cmpc)
380         return strc < cmpc ? -1 : 1;
381       inc=(strc > 255) ? 2 : 1; /* Equal, use same increment */
382       str += inc;
383       cmp += inc;
384     }
385     return 0; /* Matched len chars */
386   }
387   return strncmp(str, cmp, len); /* ASCII CP */
388 }
389
390 /*********************************************************************
391  *              _mbsnbcmp(MSVCRT.@)
392  */
393 int _mbsnbcmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
394 {
395   if (!len)
396     return 0;
397   if(MSVCRT___mb_cur_max > 1)
398   {
399     unsigned int strc, cmpc;
400     while (len)
401     {
402       int clen;
403       if(!*str)
404         return *cmp ? -1 : 0;
405       if(!*cmp)
406         return 1;
407       if (MSVCRT_isleadbyte(*str))
408       {
409         strc=(len>=2)?_mbsnextc(str):0;
410         clen=2;
411       }
412       else
413       {
414         strc=*str;
415         clen=1;
416       }
417       if (MSVCRT_isleadbyte(*cmp))
418         cmpc=(len>=2)?_mbsnextc(cmp):0;
419       else
420         cmpc=*str;
421       if(strc != cmpc)
422         return strc < cmpc ? -1 : 1;
423       len -= clen;
424       str += clen;
425       cmp += clen;
426     }
427     return 0; /* Matched len chars */
428   }
429   return strncmp(str,cmp,len);
430 }
431
432 /*********************************************************************
433  *              _mbsnicmp(MSVCRT.@)
434  *
435  * Compare two multibyte strings case insensitively to 'len' characters.
436  */
437 int _mbsnicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
438 {
439   /* FIXME: No tolower() for mb strings yet */
440   if(MSVCRT___mb_cur_max > 1)
441   {
442     unsigned int strc, cmpc;
443     while(len--)
444     {
445       if(!*str)
446         return *cmp ? -1 : 0;
447       if(!*cmp)
448         return 1;
449       strc = _mbctolower(_mbsnextc(str));
450       cmpc = _mbctolower(_mbsnextc(cmp));
451       if(strc != cmpc)
452         return strc < cmpc ? -1 : 1;
453       str +=(strc > 255) ? 2 : 1;
454       cmp +=(strc > 255) ? 2 : 1; /* Equal, use same increment */
455     }
456     return 0; /* Matched len chars */
457   }
458   return strncasecmp(str, cmp, len); /* ASCII CP */
459 }
460
461 /*********************************************************************
462  *              _mbsnbicmp(MSVCRT.@)
463  */
464 int _mbsnbicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
465 {
466   if (!len)
467     return 0;
468   if(MSVCRT___mb_cur_max > 1)
469   {
470     unsigned int strc, cmpc;
471     while (len)
472     {
473       int clen;
474       if(!*str)
475         return *cmp ? -1 : 0;
476       if(!*cmp)
477         return 1;
478       if (MSVCRT_isleadbyte(*str))
479       {
480         strc=(len>=2)?_mbsnextc(str):0;
481         clen=2;
482       }
483       else
484       {
485         strc=*str;
486         clen=1;
487       }
488       if (MSVCRT_isleadbyte(*cmp))
489         cmpc=(len>=2)?_mbsnextc(cmp):0;
490       else
491         cmpc=*str;
492       strc = _mbctolower(strc);
493       cmpc = _mbctolower(cmpc);
494       if(strc != cmpc)
495         return strc < cmpc ? -1 : 1;
496       len -= clen;
497       str += clen;
498       cmp += clen;
499     }
500     return 0; /* Matched len bytes */
501   }
502   return strncmp(str,cmp,len);
503 }
504
505 /*********************************************************************
506  *              _mbschr(MSVCRT.@)
507  *
508  * Find a multibyte character in a multibyte string.
509  */
510 unsigned char* _mbschr(const unsigned char* s, unsigned int x)
511 {
512   if(MSVCRT___mb_cur_max > 1)
513   {
514     unsigned int c;
515     while (1)
516     {
517       c = _mbsnextc(s);
518       if (c == x)
519         return (unsigned char*)s;
520       if (!c)
521         return NULL;
522       s += c > 255 ? 2 : 1;
523     }
524   }
525   return strchr(s, x); /* ASCII CP */
526 }
527
528 /*********************************************************************
529  *              _mbsrchr(MSVCRT.@)
530  */
531 unsigned char* _mbsrchr(const unsigned char* s, unsigned int x)
532 {
533   if(MSVCRT___mb_cur_max > 1)
534   {
535     unsigned int c;
536     unsigned char* match=NULL;
537     if(!s)
538       return NULL;
539     while (1) {
540       c = _mbsnextc(s);
541       if (c == x)
542         match=(unsigned char*)s;
543       if (!c)
544         return match;
545       s +=(c > 255) ? 2 : 1;
546     }
547   }
548   return strrchr(s,x);
549 }
550
551 /*********************************************************************
552  *              _mbstok(MSVCRT.@)
553  *
554  * Find and extract tokens from strings
555  */
556 unsigned char* _mbstok(unsigned char *str, const unsigned char *delim)
557 {
558     MSVCRT_thread_data *data = msvcrt_get_thread_data();
559     char *ret;
560
561     if(MSVCRT___mb_cur_max > 1)
562     {
563         unsigned int c;
564
565         if (!str)
566             if (!(str = data->mbstok_next)) return NULL;
567
568         while ((c = _mbsnextc(str)) && _mbschr(delim, c)) {
569             str += c > 255 ? 2 : 1;
570         }
571         if (!*str) return NULL;
572         ret = str++;
573         while ((c = _mbsnextc(str)) && !_mbschr(delim, c)) {
574             str += c > 255 ? 2 : 1;
575         }
576         if (*str) {
577             *str++ = 0;
578             if (c > 255) *str++ = 0;
579         }
580         data->mbstok_next = str;
581         return ret;
582     }
583     return strtok(str, delim);  /* ASCII CP */
584 }
585
586 /*********************************************************************
587  *              mbtowc(MSVCRT.@)
588  */
589 int MSVCRT_mbtowc(MSVCRT_wchar_t *dst, const char* str, MSVCRT_size_t n)
590 {
591     /* temp var needed because MultiByteToWideChar wants non NULL destination */
592     MSVCRT_wchar_t tmpdst = '\0';
593
594     if(n <= 0 || !str)
595         return 0;
596     if(!MultiByteToWideChar(CP_ACP, 0, str, n, &tmpdst, 1))
597         return -1;
598     if(dst)
599         *dst = tmpdst;
600     /* return the number of bytes from src that have been used */
601     if(!*str)
602         return 0;
603     if(n >= 2 && MSVCRT_isleadbyte(*str) && str[1])
604         return 2;
605     return 1;
606 }
607
608 /*********************************************************************
609  *              _mbbtombc(MSVCRT.@)
610  */
611 unsigned int _mbbtombc(unsigned int c)
612 {
613   if(MSVCRT___mb_cur_max > 1 &&
614      ((c >= 0x20 && c <=0x7e) ||(c >= 0xa1 && c <= 0xdf)))
615   {
616     /* FIXME: I can't get this function to return anything
617      * different to what I pass it...
618      */
619   }
620   return c;  /* ASCII CP or no MB char */
621 }
622
623 /*********************************************************************
624  *              _ismbbkana(MSVCRT.@)
625  */
626 int _ismbbkana(unsigned int c)
627 {
628   /* FIXME: use lc_ctype when supported, not lc_all */
629   if(MSVCRT_current_lc_all_cp == 932)
630   {
631     /* Japanese/Katakana, CP 932 */
632     return (c >= 0xa1 && c <= 0xdf);
633   }
634   return 0;
635 }
636
637 /*********************************************************************
638  *              _ismbcdigit(MSVCRT.@)
639  */
640 int _ismbcdigit(unsigned int ch)
641 {
642     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
643     return (get_char_typeW( wch ) & C1_DIGIT);
644 }
645
646 /*********************************************************************
647  *              _ismbcgraph(MSVCRT.@)
648  */
649 int _ismbcgraph(unsigned int ch)
650 {
651     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
652     return (get_char_typeW( wch ) & (C1_UPPER | C1_LOWER | C1_DIGIT | C1_PUNCT | C1_ALPHA));
653 }
654
655 /*********************************************************************
656  *              _ismbcalpha (MSVCRT.@)
657  */
658 int _ismbcalpha(unsigned int ch)
659 {
660     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
661     return (get_char_typeW( wch ) & C1_ALPHA);
662 }
663
664 /*********************************************************************
665  *              _ismbclower (MSVCRT.@)
666  */
667 int _ismbclower(unsigned int ch)
668 {
669     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
670     return (get_char_typeW( wch ) & C1_UPPER);
671 }
672
673 /*********************************************************************
674  *              _ismbcupper (MSVCRT.@)
675  */
676 int _ismbcupper(unsigned int ch)
677 {
678     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
679     return (get_char_typeW( wch ) & C1_LOWER);
680 }
681
682 /*********************************************************************
683  *              _ismbcsymbol(MSVCRT.@)
684  */
685 int _ismbcsymbol(unsigned int ch)
686 {
687     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
688     WORD ctype;
689     if (!GetStringTypeW(CT_CTYPE3, &wch, 1, &ctype))
690     {
691         WARN("GetStringTypeW failed on %x\n", ch);
692         return 0;
693     }
694     return ((ctype & C3_SYMBOL) != 0);
695 }
696
697 /*********************************************************************
698  *              _ismbcalnum (MSVCRT.@)
699  */
700 int _ismbcalnum(unsigned int ch)
701 {
702     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
703     return (get_char_typeW( wch ) & (C1_ALPHA | C1_DIGIT));
704 }
705
706 /*********************************************************************
707  *              _ismbcspace (MSVCRT.@)
708  */
709 int _ismbcspace(unsigned int ch)
710 {
711     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
712     return (get_char_typeW( wch ) & C1_SPACE);
713 }
714
715 /*********************************************************************
716  *              _ismbcprint (MSVCRT.@)
717  */
718 int _ismbcprint(unsigned int ch)
719 {
720     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
721     return (get_char_typeW( wch ) & (C1_UPPER | C1_LOWER | C1_DIGIT | C1_PUNCT | C1_ALPHA | C1_SPACE));
722 }
723
724 /*********************************************************************
725  *              _ismbcpunct(MSVCRT.@)
726  */
727 int _ismbcpunct(unsigned int ch)
728 {
729     MSVCRT_wchar_t wch = msvcrt_mbc_to_wc( ch );
730     return (get_char_typeW( wch ) & C1_PUNCT);
731 }
732
733 /*********************************************************************
734  *              _ismbchira(MSVCRT.@)
735  */
736 int _ismbchira(unsigned int c)
737 {
738   /* FIXME: use lc_ctype when supported, not lc_all */
739   if(MSVCRT_current_lc_all_cp == 932)
740   {
741     /* Japanese/Hiragana, CP 932 */
742     return (c >= 0x829f && c <= 0x82f1);
743   }
744   return 0;
745 }
746
747 /*********************************************************************
748  *              _ismbckata(MSVCRT.@)
749  */
750 int _ismbckata(unsigned int c)
751 {
752   /* FIXME: use lc_ctype when supported, not lc_all */
753   if(MSVCRT_current_lc_all_cp == 932)
754   {
755     if(c < 256)
756       return _ismbbkana(c);
757     /* Japanese/Katakana, CP 932 */
758     return (c >= 0x8340 && c <= 0x8396 && c != 0x837f);
759   }
760   return 0;
761 }
762
763 /*********************************************************************
764  *              _ismbblead(MSVCRT.@)
765  */
766 int _ismbblead(unsigned int c)
767 {
768   /* FIXME: should reference MSVCRT_mbctype */
769   return MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(c);
770 }
771
772
773 /*********************************************************************
774  *              _ismbbtrail(MSVCRT.@)
775  */
776 int _ismbbtrail(unsigned int c)
777 {
778   /* FIXME: should reference MSVCRT_mbctype */
779   return !_ismbblead(c);
780 }
781
782 /*********************************************************************
783  *              _ismbslead(MSVCRT.@)
784  */
785 int _ismbslead(const unsigned char* start, const unsigned char* str)
786 {
787   /* Lead bytes can also be trail bytes if caller messed up
788    * iterating through the string...
789    */
790   if(MSVCRT___mb_cur_max > 1)
791   {
792     while(start < str)
793       start += MSVCRT_isleadbyte(*str) ? 2 : 1;
794
795     if(start == str)
796       return MSVCRT_isleadbyte(*str);
797   }
798   return 0; /* Must have been a trail, we skipped it */
799 }
800
801 /*********************************************************************
802  *              _ismbstrail(MSVCRT.@)
803  */
804 int _ismbstrail(const unsigned char* start, const unsigned char* str)
805 {
806   /* Must not be a lead, and must be preceeded by one */
807   return !_ismbslead(start, str) && MSVCRT_isleadbyte(str[-1]);
808 }
809
810 /*********************************************************************
811  *              _mbsset(MSVCRT.@)
812  */
813 unsigned char* _mbsset(unsigned char* str, unsigned int c)
814 {
815   unsigned char* ret = str;
816
817   if(MSVCRT___mb_cur_max == 1 || c < 256)
818     return _strset(str, c); /* ASCII CP or SB char */
819
820   c &= 0xffff; /* Strip high bits */
821
822   while(str[0] && str[1])
823   {
824     *str++ = c >> 8;
825     *str++ = c & 0xff;
826   }
827   if(str[0])
828     str[0] = '\0'; /* FIXME: OK to shorten? */
829
830   return ret;
831 }
832
833 /*********************************************************************
834  *              _mbsnbset(MSVCRT.@)
835  */
836 unsigned char* _mbsnbset(unsigned char *str, unsigned int c, MSVCRT_size_t len)
837 {
838     unsigned char *ret = str;
839
840     if(!len)
841         return ret;
842
843     if(MSVCRT___mb_cur_max == 1 || c < 256)
844         return _strnset(str, c, len); /* ASCII CP or SB char */
845
846     c &= 0xffff; /* Strip high bits */
847
848     while(str[0] && str[1] && (len > 1))
849     {
850         *str++ = c >> 8;
851         len--;
852         *str++ = c & 0xff;
853         len--;
854     }
855     if(len && str[0]) {
856         /* as per msdn pad with a blank character */
857         str[0] = ' ';
858     }
859
860     return ret;
861 }
862
863 /*********************************************************************
864  *              _mbsnset(MSVCRT.@)
865  */
866 unsigned char* _mbsnset(unsigned char* str, unsigned int c, MSVCRT_size_t len)
867 {
868   unsigned char *ret = str;
869
870   if(!len)
871     return ret;
872
873   if(MSVCRT___mb_cur_max == 1 || c < 256)
874     return _strnset(str, c, len); /* ASCII CP or SB char */
875
876   c &= 0xffff; /* Strip high bits */
877
878   while(str[0] && str[1] && len--)
879   {
880     *str++ = c >> 8;
881     *str++ = c & 0xff;
882   }
883   if(len && str[0])
884     str[0] = '\0'; /* FIXME: OK to shorten? */
885
886   return ret;
887 }
888
889 /*********************************************************************
890  *              _mbsnccnt(MSVCRT.@)
891  * 'c' is for 'character'.
892  */
893 MSVCRT_size_t _mbsnccnt(const unsigned char* str, MSVCRT_size_t len)
894 {
895   MSVCRT_size_t ret;
896   if(MSVCRT___mb_cur_max > 1)
897   {
898     ret=0;
899     while(*str && len-- > 0)
900     {
901       if(MSVCRT_isleadbyte(*str))
902       {
903         if (!len)
904           break;
905         len--;
906         str++;
907       }
908       str++;
909       ret++;
910     }
911     return ret;
912   }
913   ret=strlen(str);
914   return min(ret, len); /* ASCII CP */
915 }
916
917 /*********************************************************************
918  *              _mbsnbcnt(MSVCRT.@)
919  * 'b' is for byte count.
920  */
921 MSVCRT_size_t _mbsnbcnt(const unsigned char* str, MSVCRT_size_t len)
922 {
923   MSVCRT_size_t ret;
924   if(MSVCRT___mb_cur_max > 1)
925   {
926     const unsigned char* xstr = str;
927     while(*xstr && len-- > 0)
928     {
929       if (MSVCRT_isleadbyte(*xstr++))
930         xstr++;
931     }
932     return xstr-str;
933   }
934   ret=strlen(str);
935   return min(ret, len); /* ASCII CP */
936 }
937
938
939 /*********************************************************************
940  *              _mbsnbcat(MSVCRT.@)
941  */
942 unsigned char* _mbsnbcat(unsigned char* dst, const unsigned char* src, MSVCRT_size_t len)
943 {
944     if(MSVCRT___mb_cur_max > 1)
945     {
946         char *res = dst;
947         while (*dst) {
948             if (MSVCRT_isleadbyte(*dst++)) {
949                 if (*dst) {
950                     dst++;
951                 } else {
952                     /* as per msdn overwrite the lead byte in front of '\0' */
953                     dst--;
954                     break;
955                 }
956             }
957         }
958         while (*src && len--) *dst++ = *src++;
959         *dst = '\0';
960         return res;
961     }
962     return strncat(dst, src, len); /* ASCII CP */
963 }
964
965
966 /*********************************************************************
967  *              _mbsncat(MSVCRT.@)
968  */
969 unsigned char* _mbsncat(unsigned char* dst, const unsigned char* src, MSVCRT_size_t len)
970 {
971   if(MSVCRT___mb_cur_max > 1)
972   {
973     char *res = dst;
974     while (*dst)
975     {
976       if (MSVCRT_isleadbyte(*dst++))
977         dst++;
978     }
979     while (*src && len--)
980     {
981       *dst++ = *src;
982       if(MSVCRT_isleadbyte(*src++))
983         *dst++ = *src++;
984     }
985     *dst = '\0';
986     return res;
987   }
988   return strncat(dst, src, len); /* ASCII CP */
989 }
990
991
992 /*********************************************************************
993  *              _mbslwr(MSVCRT.@)
994  */
995 unsigned char* _mbslwr(unsigned char* s)
996 {
997   if (!s)
998     return NULL;
999   if (MSVCRT___mb_cur_max > 1)
1000   {
1001     unsigned int c;
1002     unsigned char* p=s;
1003     while (*s)
1004     {
1005       c = _mbctolower(_mbsnextc(s));
1006       /* Note that I assume that the size of the character is unchanged */
1007       if (c > 255)
1008       {
1009           *s++=(c>>8);
1010           c=c & 0xff;
1011       }
1012       *s++=c;
1013     }
1014     return p;
1015   }
1016   return _strlwr(s);
1017 }
1018
1019
1020 /*********************************************************************
1021  *              _mbsupr(MSVCRT.@)
1022  */
1023 unsigned char* _mbsupr(unsigned char* s)
1024 {
1025   if (!s)
1026     return NULL;
1027   if (MSVCRT___mb_cur_max > 1)
1028   {
1029     unsigned int c;
1030     unsigned char* p=s;
1031     while (*s)
1032     {
1033       c = _mbctoupper(_mbsnextc(s));
1034       /* Note that I assume that the size of the character is unchanged */
1035       if (c > 255)
1036       {
1037           *s++=(c>>8);
1038           c=c & 0xff;
1039       }
1040       *s++=c;
1041     }
1042     return p;
1043   }
1044   return _strupr(s);
1045 }
1046
1047
1048 /*********************************************************************
1049  *              _mbsspn (MSVCRT.@)
1050  */
1051 MSVCRT_size_t _mbsspn(const unsigned char* string, const unsigned char* set)
1052 {
1053   const unsigned char *p, *q;
1054
1055   for (p = string; *p; p++)
1056     {
1057       if (MSVCRT_isleadbyte(*p))
1058         {
1059           for (q = set; *q; q++)
1060             {
1061               if (!q[1])
1062                 break;
1063               if ((*p == *q) &&  (p[1] == q[1]))
1064                 break;
1065               q++;
1066             }
1067           if (*++p == '\0')
1068             break;
1069         }
1070       else
1071         for (q = set; *q; q++)
1072           if (*p == *q)
1073             break;
1074     }
1075   return p - string;
1076 }
1077
1078 /*********************************************************************
1079  *              _mbscspn(MSVCRT.@)
1080  */
1081 MSVCRT_size_t _mbscspn(const unsigned char* str, const unsigned char* cmp)
1082 {
1083   if (MSVCRT___mb_cur_max > 1)
1084     FIXME("don't handle double character case\n");
1085   return strcspn(str, cmp);
1086 }
1087
1088 /*********************************************************************
1089  *              _mbsrev (MSVCRT.@)
1090  */
1091 unsigned char* _mbsrev(unsigned char* str)
1092 {
1093     int i, len = _mbslen(str);
1094     unsigned char *p, *temp=MSVCRT_malloc(len*2);
1095
1096     if(!temp)
1097         return str;
1098
1099     /* unpack multibyte string to temp buffer */
1100     p=str;
1101     for(i=0; i<len; i++)
1102     {
1103         if (MSVCRT_isleadbyte(*p))
1104         {
1105             temp[i*2]=*p++;
1106             temp[i*2+1]=*p++;
1107         }
1108         else
1109         {
1110             temp[i*2]=*p++;
1111             temp[i*2+1]=0;
1112         }
1113     }
1114
1115     /* repack it in the reverse order */
1116     p=str;
1117     for(i=len-1; i>=0; i--)
1118     {
1119         if(MSVCRT_isleadbyte(temp[i*2]))
1120         {
1121             *p++=temp[i*2];
1122             *p++=temp[i*2+1];
1123         }
1124         else
1125         {
1126             *p++=temp[i*2];
1127         }
1128     }
1129
1130     MSVCRT_free(temp);
1131
1132     return str;
1133 }
1134
1135 /*********************************************************************
1136  *              _mbspbrk (MSVCRT.@)
1137  */
1138 unsigned char* _mbspbrk(const unsigned char* str, const unsigned char* accept)
1139 {
1140     const unsigned char* p;
1141
1142     while(*str)
1143     {
1144         for(p = accept; *p; p += (MSVCRT_isleadbyte(*p)?2:1) )
1145         {
1146             if (*p == *str)
1147                 if( !MSVCRT_isleadbyte(*p) || ( *(p+1) == *(str+1) ) )
1148                      return (unsigned char*)str;
1149         }
1150         str += (MSVCRT_isleadbyte(*str)?2:1);
1151     }
1152     return NULL;
1153 }