Added regedit unit test, a couple minor changes to regedit.
[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
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 /*********************************************************************
43  *              __p__mbctype (MSVCRT.@)
44  */
45 unsigned char* __p__mbctype(void)
46 {
47   return MSVCRT_mbctype;
48 }
49
50 /*********************************************************************
51  *              __p___mb_cur_max(MSVCRT.@)
52  */
53 int* __p___mb_cur_max(void)
54 {
55   return &MSVCRT___mb_cur_max;
56 }
57
58 /*********************************************************************
59  *              _mbsnextc(MSVCRT.@)
60  */
61 unsigned int _mbsnextc(const unsigned char* str)
62 {
63   if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*str))
64     return *str << 8 | str[1];
65   return *str; /* ASCII CP or SB char */
66 }
67
68 /*********************************************************************
69  *              _mbctolower(MSVCRT.@)
70  */
71 unsigned int _mbctolower(unsigned int c)
72 {
73     if (MSVCRT_isleadbyte(c))
74     {
75       FIXME("Handle MBC chars\n");
76       return c;
77     }
78     return tolower(c); /* ASCII CP or SB char */
79 }
80
81 /*********************************************************************
82  *              _mbctoupper(MSVCRT.@)
83  */
84 unsigned int _mbctoupper(unsigned int c)
85 {
86     if (MSVCRT_isleadbyte(c))
87     {
88       FIXME("Handle MBC chars\n");
89       return c;
90     }
91     return toupper(c); /* ASCII CP or SB char */
92 }
93
94 /*********************************************************************
95  *              _mbsdec(MSVCRT.@)
96  */
97 unsigned char* _mbsdec(const unsigned char* start, const unsigned char* cur)
98 {
99   if(MSVCRT___mb_cur_max > 1)
100     return (char *)(_ismbstrail(start,cur-1) ? cur - 2 : cur -1);
101
102   return (char *)cur - 1; /* ASCII CP or SB char */
103 }
104
105 /*********************************************************************
106  *              _mbsinc(MSVCRT.@)
107  */
108 unsigned char* _mbsinc(const unsigned char* str)
109 {
110   if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*str))
111     return (unsigned char*)str + 2; /* MB char */
112
113   return (unsigned char*)str + 1; /* ASCII CP or SB char */
114 }
115
116 /*********************************************************************
117  *              _mbsninc(MSVCRT.@)
118  */
119 unsigned char* _mbsninc(const unsigned char* str, MSVCRT_size_t num)
120 {
121   if(!str || num < 1)
122     return NULL;
123   if(MSVCRT___mb_cur_max > 1)
124   {
125     while(num--)
126       str = _mbsinc(str);
127     return (unsigned char*)str;
128   }
129   return (unsigned char*)str + num; /* ASCII CP */
130 }
131
132 /*********************************************************************
133  *              _mbclen(MSVCRT.@)
134  */
135 unsigned int _mbclen(const unsigned char* str)
136 {
137   return MSVCRT_isleadbyte(*str) ? 2 : 1;
138 }
139
140 /*********************************************************************
141  *              mblen(MSVCRT.@)
142  */
143 int MSVCRT_mblen(const char* str, MSVCRT_size_t size)
144 {
145   if (str && *str && size)
146   {
147     if(MSVCRT___mb_cur_max == 1)
148       return 1; /* ASCII CP */
149
150     return !MSVCRT_isleadbyte(*str) ? 1 : (size>1 ? 2 : -1);
151   }
152   return 0;
153 }
154
155 /*********************************************************************
156  *              _mbslen(MSVCRT.@)
157  */
158 MSVCRT_size_t _mbslen(const unsigned char* str)
159 {
160   if(MSVCRT___mb_cur_max > 1)
161   {
162     MSVCRT_size_t len = 0;
163     while(*str)
164     {
165       str += MSVCRT_isleadbyte(*str) ? 2 : 1;
166       len++;
167     }
168     return len;
169   }
170   return strlen(str); /* ASCII CP */
171 }
172
173 /*********************************************************************
174  *              _mbstrlen(MSVCRT.@)
175  */
176 MSVCRT_size_t _mbstrlen(const char* str)
177 {
178   if(MSVCRT___mb_cur_max > 1)
179   {
180     MSVCRT_size_t len = 0;
181     while(*str)
182     {
183       /* FIXME: According to the documentation we are supposed to test for
184        * multi-byte character validity. Whatever that means
185        */
186       str += MSVCRT_isleadbyte(*str) ? 2 : 1;
187       len++;
188     }
189     return len;
190   }
191   return strlen(str); /* ASCII CP */
192 }
193
194 /*********************************************************************
195  *              _mbccpy(MSVCRT.@)
196  */
197 void _mbccpy(unsigned char* dest, const unsigned char* src)
198 {
199   *dest++ = *src;
200   if(MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(*src))
201     *dest = *++src; /* MB char */
202   else
203     ERR("failure.. is this ok?\n");
204 }
205
206 /*********************************************************************
207  *              _mbsncpy(MSVCRT.@)
208  */
209 unsigned char* _mbsncpy(unsigned char* dst, const unsigned char* src, MSVCRT_size_t n)
210 {
211   if(!n)
212     return dst;
213   if(MSVCRT___mb_cur_max > 1)
214   {
215     unsigned char* ret = dst;
216     while (*src && n--)
217     {
218       *dst++ = *src;
219       if (MSVCRT_isleadbyte(*src++))
220           *dst++ = *src++;
221     }
222     while(n--)
223       *dst++ = '\0';
224     return ret;
225   }
226   return strncpy(dst, src, n); /* ASCII CP */
227 }
228
229 /*********************************************************************
230  *              _mbsnbcpy(MSVCRT.@)
231  */
232 unsigned char* _mbsnbcpy(unsigned char* dst, const unsigned char* src, MSVCRT_size_t n)
233 {
234   if(!n)
235     return dst;
236   if(MSVCRT___mb_cur_max > 1)
237   {
238     unsigned char* ret = dst;
239     while (*src && (n-- > 1))
240     {
241       *dst++ = *src;
242       if (MSVCRT_isleadbyte(*src++))
243       {
244         *dst++ = *src++;
245         n--;
246       }
247     }
248     if (*src && n && !MSVCRT_isleadbyte(*src))
249     {
250       /* If the last character is a multi-byte character then
251        * we cannot copy it since we have only one byte left
252        */
253       *dst++ = *src;
254       n--;
255     }
256     while (n--)
257       *dst++ = '\0';
258     return ret;
259   }
260   return strncpy(dst, src, n); /* ASCII CP */
261 }
262
263 /*********************************************************************
264  *              _mbscmp(MSVCRT.@)
265  */
266 int _mbscmp(const unsigned char* str, const unsigned char* cmp)
267 {
268   if(MSVCRT___mb_cur_max > 1)
269   {
270     unsigned int strc, cmpc;
271     do {
272       if(!*str)
273         return *cmp ? -1 : 0;
274       if(!*cmp)
275         return 1;
276       strc = _mbsnextc(str);
277       cmpc = _mbsnextc(cmp);
278       if(strc != cmpc)
279         return strc < cmpc ? -1 : 1;
280       str +=(strc > 255) ? 2 : 1;
281       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
282     } while(1);
283   }
284   return strcmp(str, cmp); /* ASCII CP */
285 }
286
287 /*********************************************************************
288  *              _mbsicmp(MSVCRT.@)
289  */
290 int _mbsicmp(const unsigned char* str, const unsigned char* cmp)
291 {
292   if(MSVCRT___mb_cur_max > 1)
293   {
294     unsigned int strc, cmpc;
295     do {
296       if(!*str)
297         return *cmp ? -1 : 0;
298       if(!*cmp)
299         return 1;
300       strc = _mbctolower(_mbsnextc(str));
301       cmpc = _mbctolower(_mbsnextc(cmp));
302       if(strc != cmpc)
303         return strc < cmpc ? -1 : 1;
304       str +=(strc > 255) ? 2 : 1;
305       cmp +=(strc > 255) ? 2 : 1; /* equal, use same increment */
306     } while(1);
307   }
308   return strcasecmp(str, cmp); /* ASCII CP */
309 }
310
311 /*********************************************************************
312  *              _mbsncmp(MSVCRT.@)
313  */
314 int _mbsncmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
315 {
316   if(!len)
317     return 0;
318
319   if(MSVCRT___mb_cur_max > 1)
320   {
321     unsigned int strc, cmpc;
322     while(len--)
323     {
324       int inc;
325       if(!*str)
326         return *cmp ? -1 : 0;
327       if(!*cmp)
328         return 1;
329       strc = _mbsnextc(str);
330       cmpc = _mbsnextc(cmp);
331       if(strc != cmpc)
332         return strc < cmpc ? -1 : 1;
333       inc=(strc > 255) ? 2 : 1; /* Equal, use same increment */
334       str += inc;
335       cmp += inc;
336     }
337     return 0; /* Matched len chars */
338   }
339   return strncmp(str, cmp, len); /* ASCII CP */
340 }
341
342 /*********************************************************************
343  *              _mbsnbcmp(MSVCRT.@)
344  */
345 int _mbsnbcmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
346 {
347   if (!len)
348     return 0;
349   if(MSVCRT___mb_cur_max > 1)
350   {
351     unsigned int strc, cmpc;
352     while (len)
353     {
354       int clen;
355       if(!*str)
356         return *cmp ? -1 : 0;
357       if(!*cmp)
358         return 1;
359       if (MSVCRT_isleadbyte(*str))
360       {
361         strc=(len>=2)?_mbsnextc(str):0;
362         clen=2;
363       }
364       else
365       {
366         strc=*str;
367         clen=1;
368       }
369       if (MSVCRT_isleadbyte(*cmp))
370         cmpc=(len>=2)?_mbsnextc(cmp):0;
371       else
372         cmpc=*str;
373       if(strc != cmpc)
374         return strc < cmpc ? -1 : 1;
375       len -= clen;
376       str += clen;
377       cmp += clen;
378     }
379     return 0; /* Matched len chars */
380       FIXME("%s %s %d\n",str,cmp,len);
381   }
382   return strncmp(str,cmp,len);
383 }
384
385 /*********************************************************************
386  *              _mbsnicmp(MSVCRT.@)
387  *
388  * Compare two multibyte strings case insensitively to 'len' characters.
389  */
390 int _mbsnicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
391 {
392   /* FIXME: No tolower() for mb strings yet */
393   if(MSVCRT___mb_cur_max > 1)
394   {
395     unsigned int strc, cmpc;
396     while(len--)
397     {
398       if(!*str)
399         return *cmp ? -1 : 0;
400       if(!*cmp)
401         return 1;
402       strc = _mbctolower(_mbsnextc(str));
403       cmpc = _mbctolower(_mbsnextc(cmp));
404       if(strc != cmpc)
405         return strc < cmpc ? -1 : 1;
406       str +=(strc > 255) ? 2 : 1;
407       cmp +=(strc > 255) ? 2 : 1; /* Equal, use same increment */
408     }
409     return 0; /* Matched len chars */
410   }
411   return strncasecmp(str, cmp, len); /* ASCII CP */
412 }
413
414 /*********************************************************************
415  *              _mbsnbicmp(MSVCRT.@)
416  */
417 int _mbsnbicmp(const unsigned char* str, const unsigned char* cmp, MSVCRT_size_t len)
418 {
419   if (!len)
420     return 0;
421   if(MSVCRT___mb_cur_max > 1)
422   {
423     unsigned int strc, cmpc;
424     while (len)
425     {
426       int clen;
427       if(!*str)
428         return *cmp ? -1 : 0;
429       if(!*cmp)
430         return 1;
431       if (MSVCRT_isleadbyte(*str))
432       {
433         strc=(len>=2)?_mbsnextc(str):0;
434         clen=2;
435       }
436       else
437       {
438         strc=*str;
439         clen=1;
440       }
441       if (MSVCRT_isleadbyte(*cmp))
442         cmpc=(len>=2)?_mbsnextc(cmp):0;
443       else
444         cmpc=*str;
445       strc = _mbctolower(strc);
446       cmpc = _mbctolower(cmpc);
447       if(strc != cmpc)
448         return strc < cmpc ? -1 : 1;
449       len -= clen;
450       str += clen;
451       cmp += clen;
452     }
453     return 0; /* Matched len bytes */
454       FIXME("%s %s %d\n",str,cmp,len);
455   }
456   return strncmp(str,cmp,len);
457 }
458
459 /*********************************************************************
460  *              _mbschr(MSVCRT.@)
461  *
462  * Find a multibyte character in a multibyte string.
463  */
464 unsigned char* _mbschr(const unsigned char* s, unsigned int x)
465 {
466   if(MSVCRT___mb_cur_max > 1)
467   {
468     unsigned int c;
469     while (1)
470     {
471       c = _mbsnextc(s);
472       if (c == x)
473         return (unsigned char*)s;
474       if (!c)
475         return NULL;
476       s += c > 255 ? 2 : 1;
477     }
478   }
479   return strchr(s, x); /* ASCII CP */
480 }
481
482 /*********************************************************************
483  *              _mbsrchr(MSVCRT.@)
484  */
485 unsigned char* _mbsrchr(const unsigned char* s, unsigned int x)
486 {
487   if(MSVCRT___mb_cur_max > 1)
488   {
489     unsigned int c;
490     unsigned char* match=NULL;
491     if(!s)
492       return NULL;
493     while (1) {
494       c = _mbsnextc(s);
495       if (c == x)
496         match=(unsigned char*)s;
497       if (!c)
498         return match;
499       s +=(c > 255) ? 2 : 1;
500     }
501   }
502   return strrchr(s,x);
503 }
504
505 /*********************************************************************
506  *              mbtowc(MSVCRT.@)
507  */
508 int MSVCRT_mbtowc(WCHAR *dst, const char* str, MSVCRT_size_t n)
509 {
510   if(n <= 0 || !str)
511     return 0;
512   if(!MultiByteToWideChar(CP_ACP, 0, str, n, dst, 1))
513     return 0;
514   /* return the number of bytes from src that have been used */
515   if(!*str)
516     return 0;
517   if(n >= 2 && MSVCRT_isleadbyte(*str) && str[1])
518     return 2;
519   return 1;
520 }
521
522 /*********************************************************************
523  *              _mbbtombc(MSVCRT.@)
524  */
525 unsigned int _mbbtombc(unsigned int c)
526 {
527   if(MSVCRT___mb_cur_max > 1 &&
528      ((c >= 0x20 && c <=0x7e) ||(c >= 0xa1 && c <= 0xdf)))
529   {
530     /* FIXME: I can't get this function to return anything
531      * different to what I pass it...
532      */
533   }
534   return c;  /* ASCII CP or no MB char */
535 }
536
537 /*********************************************************************
538  *              _ismbbkana(MSVCRT.@)
539  */
540 int _ismbbkana(unsigned int c)
541 {
542   /* FIXME: use lc_ctype when supported, not lc_all */
543   if(MSVCRT_current_lc_all_cp == 932)
544   {
545     /* Japanese/Katakana, CP 932 */
546     return (c >= 0xa1 && c <= 0xdf);
547   }
548   return 0;
549 }
550
551 /*********************************************************************
552  *              _ismbcdigit(MSVCRT.@)
553  */
554 int _ismbcdigit(unsigned int ch)
555 {
556   if (ch <0x100)
557     return isdigit(ch);
558   else
559     {
560       FIXME("Handle MBC chars\n");
561       return 0;
562     }
563 }
564
565 /*********************************************************************
566  *              _ismbcspace (MSVCRT.@)
567  */
568 int _ismbcspace(unsigned int c)
569 {
570
571   if (c<0x100)
572     return isspace(c);
573   FIXME("%c\n",c);
574   return 0;
575 }
576
577 /*********************************************************************
578  *              _ismbchira(MSVCRT.@)
579  */
580 int _ismbchira(unsigned int c)
581 {
582   /* FIXME: use lc_ctype when supported, not lc_all */
583   if(MSVCRT_current_lc_all_cp == 932)
584   {
585     /* Japanese/Hiragana, CP 932 */
586     return (c >= 0x829f && c <= 0x82f1);
587   }
588   return 0;
589 }
590
591 /*********************************************************************
592  *              _ismbckata(MSVCRT.@)
593  */
594 int _ismbckata(unsigned int c)
595 {
596   /* FIXME: use lc_ctype when supported, not lc_all */
597   if(MSVCRT_current_lc_all_cp == 932)
598   {
599     if(c < 256)
600       return _ismbbkana(c);
601     /* Japanese/Katakana, CP 932 */
602     return (c >= 0x8340 && c <= 0x8396 && c != 0x837f);
603   }
604   return 0;
605 }
606
607 /*********************************************************************
608  *              _ismbblead(MSVCRT.@)
609  */
610 int _ismbblead(unsigned int c)
611 {
612   /* FIXME: should reference MSVCRT_mbctype */
613   return MSVCRT___mb_cur_max > 1 && MSVCRT_isleadbyte(c);
614 }
615
616
617 /*********************************************************************
618  *              _ismbbtrail(MSVCRT.@)
619  */
620 int _ismbbtrail(unsigned int c)
621 {
622   /* FIXME: should reference MSVCRT_mbctype */
623   return !_ismbblead(c);
624 }
625
626 /*********************************************************************
627  *              _ismbslead(MSVCRT.@)
628  */
629 int _ismbslead(const unsigned char* start, const unsigned char* str)
630 {
631   /* Lead bytes can also be trail bytes if caller messed up
632    * iterating through the string...
633    */
634   if(MSVCRT___mb_cur_max > 1)
635   {
636     while(start < str)
637       start += MSVCRT_isleadbyte(*str) ? 2 : 1;
638
639     if(start == str)
640       return MSVCRT_isleadbyte(*str);
641   }
642   return 0; /* Must have been a trail, we skipped it */
643 }
644
645 /*********************************************************************
646  *              _ismbstrail(MSVCRT.@)
647  */
648 int _ismbstrail(const unsigned char* start, const unsigned char* str)
649 {
650   /* Must not be a lead, and must be preceeded by one */
651   return !_ismbslead(start, str) && MSVCRT_isleadbyte(str[-1]);
652 }
653
654 /*********************************************************************
655  *              _mbsset(MSVCRT.@)
656  */
657 unsigned char* _mbsset(unsigned char* str, unsigned int c)
658 {
659   unsigned char* ret = str;
660
661   if(MSVCRT___mb_cur_max == 1 || c < 256)
662     return _strset(str, c); /* ASCII CP or SB char */
663
664   c &= 0xffff; /* Strip high bits */
665
666   while(str[0] && str[1])
667   {
668     *str++ = c >> 8;
669     *str++ = c & 0xff;
670   }
671   if(str[0])
672     str[0] = '\0'; /* FIXME: OK to shorten? */
673
674   return ret;
675 }
676
677 /*********************************************************************
678  *              _mbsnset(MSVCRT.@)
679  */
680 unsigned char* _mbsnset(unsigned char* str, unsigned int c, MSVCRT_size_t len)
681 {
682   unsigned char *ret = str;
683
684   if(!len)
685     return ret;
686
687   if(MSVCRT___mb_cur_max == 1 || c < 256)
688     return _strnset(str, c, len); /* ASCII CP or SB char */
689
690   c &= 0xffff; /* Strip high bits */
691
692   while(str[0] && str[1] && len--)
693   {
694     *str++ = c >> 8;
695     *str++ = c & 0xff;
696   }
697   if(len && str[0])
698     str[0] = '\0'; /* FIXME: OK to shorten? */
699
700   return ret;
701 }
702
703 /*********************************************************************
704  *              _mbsnccnt(MSVCRT.@)
705  * 'c' is for 'character'.
706  */
707 MSVCRT_size_t _mbsnccnt(const unsigned char* str, MSVCRT_size_t len)
708 {
709   MSVCRT_size_t ret;
710   if(MSVCRT___mb_cur_max > 1)
711   {
712     ret=0;
713     while(*str && len-- > 0)
714     {
715       if(MSVCRT_isleadbyte(*str))
716       {
717         if (!len)
718           break;
719         len--;
720         str++;
721       }
722       str++;
723       ret++;
724     }
725     return ret;
726   }
727   ret=strlen(str);
728   return min(ret, len); /* ASCII CP */
729 }
730
731 /*********************************************************************
732  *              _mbsnbcnt(MSVCRT.@)
733  * 'b' is for byte count.
734  */
735 MSVCRT_size_t _mbsnbcnt(const unsigned char* str, MSVCRT_size_t len)
736 {
737   MSVCRT_size_t ret;
738   if(MSVCRT___mb_cur_max > 1)
739   {
740     const unsigned char* xstr = str;
741     while(*xstr && len-- > 0)
742     {
743       if (MSVCRT_isleadbyte(*xstr++))
744         xstr++;
745     }
746     return xstr-str;
747   }
748   ret=strlen(str);
749   return min(ret, len); /* ASCII CP */
750 }
751
752
753 /*********************************************************************
754  *              _mbsncat(MSVCRT.@)
755  */
756 unsigned char* _mbsncat(unsigned char* dst, const unsigned char* src, MSVCRT_size_t len)
757 {
758   if(MSVCRT___mb_cur_max > 1)
759   {
760     char *res = dst;
761     while (*dst)
762     {
763       if (MSVCRT_isleadbyte(*dst++))
764         dst++;
765     }
766     while (*src && len--)
767     {
768       *dst++ = *src;
769       if(MSVCRT_isleadbyte(*src++))
770         *dst++ = *src++;
771     }
772     *dst = '\0';
773     return res;
774   }
775   return strncat(dst, src, len); /* ASCII CP */
776 }
777
778
779 /*********************************************************************
780  *              _mbslwr(MSVCRT.@)
781  */
782 unsigned char* _mbslwr(unsigned char* s)
783 {
784   if (!s)
785     return NULL;
786   if (MSVCRT___mb_cur_max > 1)
787   {
788     unsigned int c;
789     unsigned char* p=s;
790     while (*s)
791     {
792       c = _mbctolower(_mbsnextc(s));
793       /* Note that I assume that the size of the character is unchanged */
794       if (c > 255)
795       {
796           *s++=(c>>8);
797           c=c & 0xff;
798       }
799       *s++=c;
800     }
801     return p;
802   }
803   return _strlwr(s);
804 }
805
806
807 /*********************************************************************
808  *              _mbsupr(MSVCRT.@)
809  */
810 unsigned char* _mbsupr(unsigned char* s)
811 {
812   if (!s)
813     return NULL;
814   if (MSVCRT___mb_cur_max > 1)
815   {
816     unsigned int c;
817     unsigned char* p=s;
818     while (*s)
819     {
820       c = _mbctoupper(_mbsnextc(s));
821       /* Note that I assume that the size of the character is unchanged */
822       if (c > 255)
823       {
824           *s++=(c>>8);
825           c=c & 0xff;
826       }
827       *s++=c;
828     }
829     return p;
830   }
831   return _strupr(s);
832 }
833
834
835 /*********************************************************************
836  *              _mbsspn (MSVCRT.@)
837  */
838 MSVCRT_size_t _mbsspn(const unsigned char* string, const unsigned char* set)
839 {
840   const unsigned char *p, *q;
841
842   for (p = string; *p; p++)
843     {
844       if (MSVCRT_isleadbyte(*p))
845         {
846           for (q = set; *q; q++)
847             {
848               if (!q[1])
849                 break;
850               if ((*p == *q) &&  (p[1] == q[1]))
851                 break;
852               q++;
853             }
854           if (*++p == '\0')
855             break;
856         }
857       else
858         for (q = set; *q; q++)
859           if (*p == *q)
860             break;
861     }
862   return p - string;
863 }
864
865 /*********************************************************************
866  *              _mbscspn(MSVCRT.@)
867  */
868 MSVCRT_size_t _mbscspn(const unsigned char* str, const unsigned char* cmp)
869 {
870   if (MSVCRT___mb_cur_max > 1)
871     FIXME("don't handle double character case\n");
872   return strcspn(str, cmp);
873 }
874
875 /*********************************************************************
876  *              _mbsrev (MSVCRT.@)
877  */
878 unsigned char* _mbsrev(unsigned char* str)
879 {
880     int i, len = _mbslen(str);
881     unsigned char *p, *temp=MSVCRT_malloc(len*2);
882
883     if(!temp)
884         return str;
885
886     /* unpack multibyte string to temp buffer */
887     p=str;
888     for(i=0; i<len; i++)
889     {
890         if (MSVCRT_isleadbyte(*p))
891         {
892             temp[i*2]=*p++;
893             temp[i*2+1]=*p++;
894         }
895         else
896         {
897             temp[i*2]=*p++;
898             temp[i*2+1]=0;
899         }
900     }
901
902     /* repack it in the reverse order */
903     p=str;
904     for(i=len-1; i>=0; i--)
905     {
906         if(MSVCRT_isleadbyte(temp[i*2]))
907         {
908             *p++=temp[i*2];
909             *p++=temp[i*2+1];
910         }
911         else
912         {
913             *p++=temp[i*2];
914         }
915     }
916
917     MSVCRT_free(temp);
918
919     return str;
920 }
921
922 /*********************************************************************
923  *              _mbspbrk (MSVCRT.@)
924  */
925 unsigned char* _mbspbrk(const unsigned char* str, const unsigned char* accept)
926 {
927     const unsigned char* p;
928
929     while(*str)
930     {
931         for(p = accept; *p; p += (MSVCRT_isleadbyte(*p)?2:1) )
932         {
933             if (*p == *str)
934                 if( !MSVCRT_isleadbyte(*p) || ( *(p+1) == *(str+1) ) )
935                      return (unsigned char*)str;
936         }
937         str += (MSVCRT_isleadbyte(*str)?2:1);
938     }
939     return NULL;
940 }
941