fdopen: don't rewind the file after creating the FILE* handle. Added
[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 WCHAR msvcrt_mbc_to_wc(unsigned int ch)
43 {
44   WCHAR 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  *              _mbsicmp(MSVCRT.@)
311  */
312 int _mbsicmp(const unsigned char* str, const unsigned char* cmp)
313 {
314   if(MSVCRT___mb_cur_max > 1)
315   {
316     unsigned int strc, cmpc;
317     do {
318       if(!*str)
319         return *cmp ? -1 : 0;
320       if(!*cmp)
321         return 1;
322       strc = _mbctolower(_mbsnextc(str));
323       cmpc = _mbctolower(_mbsnextc(cmp));
324       if(strc != cmpc)
325         return strc < cmpc ? -1 : 1;
326       str +=(strc > 255) ? 2 : 1;
327       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
328     } while(1);
329   }
330   return strcasecmp(str, cmp); /* ASCII CP */
331 }
332
333 /*********************************************************************
334  *              _mbsncmp(MSVCRT.@)
335  */
336 int _mbsncmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
337 {
338   if(!len)
339     return 0;
340
341   if(MSVCRT___mb_cur_max > 1)
342   {
343     unsigned int strc, cmpc;
344     while(len--)
345     {
346       int inc;
347       if(!*str)
348         return *cmp ? -1 : 0;
349       if(!*cmp)
350         return 1;
351       strc = _mbsnextc(str);
352       cmpc = _mbsnextc(cmp);
353       if(strc != cmpc)
354         return strc < cmpc ? -1 : 1;
355       inc=(strc > 255) ? 2 : 1; /* Equal, use same increment */
356       str += inc;
357       cmp += inc;
358     }
359     return 0; /* Matched len chars */
360   }
361   return strncmp(str, cmp, len); /* ASCII CP */
362 }
363
364 /*********************************************************************
365  *              _mbsnbcmp(MSVCRT.@)
366  */
367 int _mbsnbcmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
368 {
369   if (!len)
370     return 0;
371   if(MSVCRT___mb_cur_max > 1)
372   {
373     unsigned int strc, cmpc;
374     while (len)
375     {
376       int clen;
377       if(!*str)
378         return *cmp ? -1 : 0;
379       if(!*cmp)
380         return 1;
381       if (MSVCRT_isleadbyte(*str))
382       {
383         strc=(len>=2)?_mbsnextc(str):0;
384         clen=2;
385       }
386       else
387       {
388         strc=*str;
389         clen=1;
390       }
391       if (MSVCRT_isleadbyte(*cmp))
392         cmpc=(len>=2)?_mbsnextc(cmp):0;
393       else
394         cmpc=*str;
395       if(strc != cmpc)
396         return strc < cmpc ? -1 : 1;
397       len -= clen;
398       str += clen;
399       cmp += clen;
400     }
401     return 0; /* Matched len chars */
402       FIXME("%s %s %d\n",str,cmp,len);
403   }
404   return strncmp(str,cmp,len);
405 }
406
407 /*********************************************************************
408  *              _mbsnicmp(MSVCRT.@)
409  *
410  * Compare two multibyte strings case insensitively to 'len' characters.
411  */
412 int _mbsnicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
413 {
414   /* FIXME: No tolower() for mb strings yet */
415   if(MSVCRT___mb_cur_max > 1)
416   {
417     unsigned int strc, cmpc;
418     while(len--)
419     {
420       if(!*str)
421         return *cmp ? -1 : 0;
422       if(!*cmp)
423         return 1;
424       strc = _mbctolower(_mbsnextc(str));
425       cmpc = _mbctolower(_mbsnextc(cmp));
426       if(strc != cmpc)
427         return strc < cmpc ? -1 : 1;
428       str +=(strc > 255) ? 2 : 1;
429       cmp +=(strc > 255) ? 2 : 1; /* Equal, use same increment */
430     }
431     return 0; /* Matched len chars */
432   }
433   return strncasecmp(str, cmp, len); /* ASCII CP */
434 }
435
436 /*********************************************************************
437  *              _mbsnbicmp(MSVCRT.@)
438  */
439 int _mbsnbicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
440 {
441   if (!len)
442     return 0;
443   if(MSVCRT___mb_cur_max > 1)
444   {
445     unsigned int strc, cmpc;
446     while (len)
447     {
448       int clen;
449       if(!*str)
450         return *cmp ? -1 : 0;
451       if(!*cmp)
452         return 1;
453       if (MSVCRT_isleadbyte(*str))
454       {
455         strc=(len>=2)?_mbsnextc(str):0;
456         clen=2;
457       }
458       else
459       {
460         strc=*str;
461         clen=1;
462       }
463       if (MSVCRT_isleadbyte(*cmp))
464         cmpc=(len>=2)?_mbsnextc(cmp):0;
465       else
466         cmpc=*str;
467       strc = _mbctolower(strc);
468       cmpc = _mbctolower(cmpc);
469       if(strc != cmpc)
470         return strc < cmpc ? -1 : 1;
471       len -= clen;
472       str += clen;
473       cmp += clen;
474     }
475     return 0; /* Matched len bytes */
476       FIXME("%s %s %d\n",str,cmp,len);
477   }
478   return strncmp(str,cmp,len);
479 }
480
481 /*********************************************************************
482  *              _mbschr(MSVCRT.@)
483  *
484  * Find a multibyte character in a multibyte string.
485  */
486 unsigned char* _mbschr(const unsigned char* s, unsigned int x)
487 {
488   if(MSVCRT___mb_cur_max > 1)
489   {
490     unsigned int c;
491     while (1)
492     {
493       c = _mbsnextc(s);
494       if (c == x)
495         return (unsigned char*)s;
496       if (!c)
497         return NULL;
498       s += c > 255 ? 2 : 1;
499     }
500   }
501   return strchr(s, x); /* ASCII CP */
502 }
503
504 /*********************************************************************
505  *              _mbsrchr(MSVCRT.@)
506  */
507 unsigned char* _mbsrchr(const unsigned char* s, unsigned int x)
508 {
509   if(MSVCRT___mb_cur_max > 1)
510   {
511     unsigned int c;
512     unsigned char* match=NULL;
513     if(!s)
514       return NULL;
515     while (1) {
516       c = _mbsnextc(s);
517       if (c == x)
518         match=(unsigned char*)s;
519       if (!c)
520         return match;
521       s +=(c > 255) ? 2 : 1;
522     }
523   }
524   return strrchr(s,x);
525 }
526
527 /*********************************************************************
528  *              mbtowc(MSVCRT.@)
529  */
530 int MSVCRT_mbtowc(WCHAR *dst, const char* str, MSVCRT_size_t n)
531 {
532   if(n <= 0 || !str)
533     return 0;
534   if(!MultiByteToWideChar(CP_ACP, 0, str, n, dst, 1))
535     return 0;
536   /* return the number of bytes from src that have been used */
537   if(!*str)
538     return 0;
539   if(n >= 2 && MSVCRT_isleadbyte(*str) && str[1])
540     return 2;
541   return 1;
542 }
543
544 /*********************************************************************
545  *              _mbbtombc(MSVCRT.@)
546  */
547 unsigned int _mbbtombc(unsigned int c)
548 {
549   if(MSVCRT___mb_cur_max > 1 &&
550      ((c >= 0x20 && c <=0x7e) ||(c >= 0xa1 && c <= 0xdf)))
551   {
552     /* FIXME: I can't get this function to return anything
553      * different to what I pass it...
554      */
555   }
556   return c;  /* ASCII CP or no MB char */
557 }
558
559 /*********************************************************************
560  *              _ismbbkana(MSVCRT.@)
561  */
562 int _ismbbkana(unsigned int c)
563 {
564   /* FIXME: use lc_ctype when supported, not lc_all */
565   if(MSVCRT_current_lc_all_cp == 932)
566   {
567     /* Japanese/Katakana, CP 932 */
568     return (c >= 0xa1 && c <= 0xdf);
569   }
570   return 0;
571 }
572
573 /*********************************************************************
574  *              _ismbcdigit(MSVCRT.@)
575  */
576 int _ismbcdigit(unsigned int ch)
577 {
578     WCHAR wch = msvcrt_mbc_to_wc( ch );
579     return (get_char_typeW( wch ) & C1_DIGIT);
580 }
581
582 /*********************************************************************
583  *              _ismbcgraph(MSVCRT.@)
584  */
585 int _ismbcgraph(unsigned int ch)
586 {
587     WCHAR wch = msvcrt_mbc_to_wc( ch );
588     return (get_char_typeW( wch ) & (C1_UPPER | C1_LOWER | C1_DIGIT | C1_PUNCT | C1_ALPHA));
589 }
590
591 /*********************************************************************
592  *              _ismbcalpha (MSVCRT.@)
593  */
594 int _ismbcalpha(unsigned int ch)
595 {
596     WCHAR wch = msvcrt_mbc_to_wc( ch );
597     return (get_char_typeW( wch ) & C1_ALPHA);
598 }
599
600 /*********************************************************************
601  *              _ismbclower (MSVCRT.@)
602  */
603 int _ismbclower(unsigned int ch)
604 {
605     WCHAR wch = msvcrt_mbc_to_wc( ch );
606     return (get_char_typeW( wch ) & C1_UPPER);
607 }
608
609 /*********************************************************************
610  *              _ismbcupper (MSVCRT.@)
611  */
612 int _ismbcupper(unsigned int ch)
613 {
614     WCHAR wch = msvcrt_mbc_to_wc( ch );
615     return (get_char_typeW( wch ) & C1_LOWER);
616 }
617
618 /*********************************************************************
619  *              _ismbcsymbol(MSVCRT.@)
620  */
621 int _ismbcsymbol(unsigned int ch)
622 {
623     WCHAR wch = msvcrt_mbc_to_wc( ch );
624     WORD ctype;
625     if (!GetStringTypeW(CT_CTYPE3, &wch, 1, &ctype))
626     {
627         WARN("GetStringTypeW failed on %x\n", ch);
628         return 0;
629     }
630     return ((ctype & C3_SYMBOL) != 0);
631 }
632
633 /*********************************************************************
634  *              _ismbcalnum (MSVCRT.@)
635  */
636 int _ismbcalnum(unsigned int ch)
637 {
638     WCHAR wch = msvcrt_mbc_to_wc( ch );
639     return (get_char_typeW( wch ) & (C1_ALPHA | C1_DIGIT));
640 }
641
642 /*********************************************************************
643  *              _ismbcspace (MSVCRT.@)
644  */
645 int _ismbcspace(unsigned int ch)
646 {
647     WCHAR wch = msvcrt_mbc_to_wc( ch );
648     return (get_char_typeW( wch ) & C1_SPACE);
649 }
650
651 /*********************************************************************
652  *              _ismbcprint (MSVCRT.@)
653  */
654 int _ismbcprint(unsigned int ch)
655 {
656     WCHAR wch = msvcrt_mbc_to_wc( ch );
657     return (get_char_typeW( wch ) & (C1_UPPER | C1_LOWER | C1_DIGIT | C1_PUNCT | C1_ALPHA | C1_SPACE));
658 }
659
660 /*********************************************************************
661  *              _ismbcpunct(MSVCRT.@)
662  */
663 int _ismbcpunct(unsigned int ch)
664 {
665     WCHAR wch = msvcrt_mbc_to_wc( ch );
666     return (get_char_typeW( wch ) & C1_PUNCT);
667 }
668
669 /*********************************************************************
670  *              _ismbchira(MSVCRT.@)
671  */
672 int _ismbchira(unsigned int c)
673 {
674   /* FIXME: use lc_ctype when supported, not lc_all */
675   if(MSVCRT_current_lc_all_cp == 932)
676   {
677     /* Japanese/Hiragana, CP 932 */
678     return (c >= 0x829f && c <= 0x82f1);
679   }
680   return 0;
681 }
682
683 /*********************************************************************
684  *              _ismbckata(MSVCRT.@)
685  */
686 int _ismbckata(unsigned int c)
687 {
688   /* FIXME: use lc_ctype when supported, not lc_all */
689   if(MSVCRT_current_lc_all_cp == 932)
690   {
691     if(c < 256)
692       return _ismbbkana(c);
693     /* Japanese/Katakana, CP 932 */
694     return (c >= 0x8340 && c <= 0x8396 && c != 0x837f);
695   }
696   return 0;
697 }
698
699 /*********************************************************************
700  *              _ismbblead(MSVCRT.@)
701  */
702 int _ismbblead(unsigned int c)
703 {
704   /* FIXME: should reference MSVCRT_mbctype */
705   return MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(c);
706 }
707
708
709 /*********************************************************************
710  *              _ismbbtrail(MSVCRT.@)
711  */
712 int _ismbbtrail(unsigned int c)
713 {
714   /* FIXME: should reference MSVCRT_mbctype */
715   return !_ismbblead(c);
716 }
717
718 /*********************************************************************
719  *              _ismbslead(MSVCRT.@)
720  */
721 int _ismbslead(const unsigned char* start, const unsigned char* str)
722 {
723   /* Lead bytes can also be trail bytes if caller messed up
724    * iterating through the string...
725    */
726   if(MSVCRT___mb_cur_max > 1)
727   {
728     while(start < str)
729       start += MSVCRT_isleadbyte(*str) ? 2 : 1;
730
731     if(start == str)
732       return MSVCRT_isleadbyte(*str);
733   }
734   return 0; /* Must have been a trail, we skipped it */
735 }
736
737 /*********************************************************************
738  *              _ismbstrail(MSVCRT.@)
739  */
740 int _ismbstrail(const unsigned char* start, const unsigned char* str)
741 {
742   /* Must not be a lead, and must be preceeded by one */
743   return !_ismbslead(start, str) && MSVCRT_isleadbyte(str[-1]);
744 }
745
746 /*********************************************************************
747  *              _mbsset(MSVCRT.@)
748  */
749 unsigned char* _mbsset(unsigned char* str, unsigned int c)
750 {
751   unsigned char* ret = str;
752
753   if(MSVCRT___mb_cur_max == 1 || c < 256)
754     return _strset(str, c); /* ASCII CP or SB char */
755
756   c &= 0xffff; /* Strip high bits */
757
758   while(str[0] && str[1])
759   {
760     *str++ = c >> 8;
761     *str++ = c & 0xff;
762   }
763   if(str[0])
764     str[0] = '\0'; /* FIXME: OK to shorten? */
765
766   return ret;
767 }
768
769 /*********************************************************************
770  *              _mbsnset(MSVCRT.@)
771  */
772 unsigned char* _mbsnset(unsigned char* str, unsigned int c, MSVCRT_size_t len)
773 {
774   unsigned char *ret = str;
775
776   if(!len)
777     return ret;
778
779   if(MSVCRT___mb_cur_max == 1 || c < 256)
780     return _strnset(str, c, len); /* ASCII CP or SB char */
781
782   c &= 0xffff; /* Strip high bits */
783
784   while(str[0] && str[1] && len--)
785   {
786     *str++ = c >> 8;
787     *str++ = c & 0xff;
788   }
789   if(len && str[0])
790     str[0] = '\0'; /* FIXME: OK to shorten? */
791
792   return ret;
793 }
794
795 /*********************************************************************
796  *              _mbsnccnt(MSVCRT.@)
797  * 'c' is for 'character'.
798  */
799 MSVCRT_size_t _mbsnccnt(const unsigned char* str, MSVCRT_size_t len)
800 {
801   MSVCRT_size_t ret;
802   if(MSVCRT___mb_cur_max > 1)
803   {
804     ret=0;
805     while(*str && len-- > 0)
806     {
807       if(MSVCRT_isleadbyte(*str))
808       {
809         if (!len)
810           break;
811         len--;
812         str++;
813       }
814       str++;
815       ret++;
816     }
817     return ret;
818   }
819   ret=strlen(str);
820   return min(ret, len); /* ASCII CP */
821 }
822
823 /*********************************************************************
824  *              _mbsnbcnt(MSVCRT.@)
825  * 'b' is for byte count.
826  */
827 MSVCRT_size_t _mbsnbcnt(const unsigned char* str, MSVCRT_size_t len)
828 {
829   MSVCRT_size_t ret;
830   if(MSVCRT___mb_cur_max > 1)
831   {
832     const unsigned char* xstr = str;
833     while(*xstr && len-- > 0)
834     {
835       if (MSVCRT_isleadbyte(*xstr++))
836         xstr++;
837     }
838     return xstr-str;
839   }
840   ret=strlen(str);
841   return min(ret, len); /* ASCII CP */
842 }
843
844
845 /*********************************************************************
846  *              _mbsncat(MSVCRT.@)
847  */
848 unsigned char* _mbsncat(unsigned char* dst, const unsigned char* src, MSVCRT_size_t len)
849 {
850   if(MSVCRT___mb_cur_max > 1)
851   {
852     char *res = dst;
853     while (*dst)
854     {
855       if (MSVCRT_isleadbyte(*dst++))
856         dst++;
857     }
858     while (*src && len--)
859     {
860       *dst++ = *src;
861       if(MSVCRT_isleadbyte(*src++))
862         *dst++ = *src++;
863     }
864     *dst = '\0';
865     return res;
866   }
867   return strncat(dst, src, len); /* ASCII CP */
868 }
869
870
871 /*********************************************************************
872  *              _mbslwr(MSVCRT.@)
873  */
874 unsigned char* _mbslwr(unsigned char* s)
875 {
876   if (!s)
877     return NULL;
878   if (MSVCRT___mb_cur_max > 1)
879   {
880     unsigned int c;
881     unsigned char* p=s;
882     while (*s)
883     {
884       c = _mbctolower(_mbsnextc(s));
885       /* Note that I assume that the size of the character is unchanged */
886       if (c > 255)
887       {
888           *s++=(c>>8);
889           c=c & 0xff;
890       }
891       *s++=c;
892     }
893     return p;
894   }
895   return _strlwr(s);
896 }
897
898
899 /*********************************************************************
900  *              _mbsupr(MSVCRT.@)
901  */
902 unsigned char* _mbsupr(unsigned char* s)
903 {
904   if (!s)
905     return NULL;
906   if (MSVCRT___mb_cur_max > 1)
907   {
908     unsigned int c;
909     unsigned char* p=s;
910     while (*s)
911     {
912       c = _mbctoupper(_mbsnextc(s));
913       /* Note that I assume that the size of the character is unchanged */
914       if (c > 255)
915       {
916           *s++=(c>>8);
917           c=c & 0xff;
918       }
919       *s++=c;
920     }
921     return p;
922   }
923   return _strupr(s);
924 }
925
926
927 /*********************************************************************
928  *              _mbsspn (MSVCRT.@)
929  */
930 MSVCRT_size_t _mbsspn(const unsigned char* string, const unsigned char* set)
931 {
932   const unsigned char *p, *q;
933
934   for (p = string; *p; p++)
935     {
936       if (MSVCRT_isleadbyte(*p))
937         {
938           for (q = set; *q; q++)
939             {
940               if (!q[1])
941                 break;
942               if ((*p == *q) &&  (p[1] == q[1]))
943                 break;
944               q++;
945             }
946           if (*++p == '\0')
947             break;
948         }
949       else
950         for (q = set; *q; q++)
951           if (*p == *q)
952             break;
953     }
954   return p - string;
955 }
956
957 /*********************************************************************
958  *              _mbscspn(MSVCRT.@)
959  */
960 MSVCRT_size_t _mbscspn(const unsigned char* str, const unsigned char* cmp)
961 {
962   if (MSVCRT___mb_cur_max > 1)
963     FIXME("don't handle double character case\n");
964   return strcspn(str, cmp);
965 }
966
967 /*********************************************************************
968  *              _mbsrev (MSVCRT.@)
969  */
970 unsigned char* _mbsrev(unsigned char* str)
971 {
972     int i, len = _mbslen(str);
973     unsigned char *p, *temp=MSVCRT_malloc(len*2);
974
975     if(!temp)
976         return str;
977
978     /* unpack multibyte string to temp buffer */
979     p=str;
980     for(i=0; i<len; i++)
981     {
982         if (MSVCRT_isleadbyte(*p))
983         {
984             temp[i*2]=*p++;
985             temp[i*2+1]=*p++;
986         }
987         else
988         {
989             temp[i*2]=*p++;
990             temp[i*2+1]=0;
991         }
992     }
993
994     /* repack it in the reverse order */
995     p=str;
996     for(i=len-1; i>=0; i--)
997     {
998         if(MSVCRT_isleadbyte(temp[i*2]))
999         {
1000             *p++=temp[i*2];
1001             *p++=temp[i*2+1];
1002         }
1003         else
1004         {
1005             *p++=temp[i*2];
1006         }
1007     }
1008
1009     MSVCRT_free(temp);
1010
1011     return str;
1012 }
1013
1014 /*********************************************************************
1015  *              _mbspbrk (MSVCRT.@)
1016  */
1017 unsigned char* _mbspbrk(const unsigned char* str, const unsigned char* accept)
1018 {
1019     const unsigned char* p;
1020
1021     while(*str)
1022     {
1023         for(p = accept; *p; p += (MSVCRT_isleadbyte(*p)?2:1) )
1024         {
1025             if (*p == *str)
1026                 if( !MSVCRT_isleadbyte(*p) || ( *(p+1) == *(str+1) ) )
1027                      return (unsigned char*)str;
1028         }
1029         str += (MSVCRT_isleadbyte(*str)?2:1);
1030     }
1031     return NULL;
1032 }