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