Started a unit test case for the updown control.
[wine] / dlls / comctl32 / string.c
1 /*
2  * String manipulation functions
3  *
4  * Copyright 1998 Eric Kohl
5  *           1998 Juergen Schmied <j.schmied@metronet.de>
6  *           2000 Eric Kohl for CodeWeavers
7  * Copyright 2002 Jon Griffiths
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #include <stdarg.h>
26 #include <string.h>
27 #include <stdlib.h> /* atoi */
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "winnls.h"
33
34 #include "wine/unicode.h"
35
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
39
40 /*************************************************************************
41  * COMCTL32_ChrCmpHelperA
42  *
43  * Internal helper for ChrCmpA/COMCTL32_ChrCmpIA.
44  *
45  * NOTES
46  *  Both this function and its Unicode counterpart are very inneficient. To
47  *  fix this, CompareString must be completely implemented and optimised
48  *  first. Then the core character test can be taken out of that function and
49  *  placed here, so that it need never be called at all. Until then, do not
50  *  attempt to optimise this code unless you are willing to test that it
51  *  still performs correctly.
52  */
53 static BOOL COMCTL32_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
54 {
55   char str1[3], str2[3];
56
57   str1[0] = LOBYTE(ch1);
58   if (IsDBCSLeadByte(str1[0]))
59   {
60     str1[1] = HIBYTE(ch1);
61     str1[2] = '\0';
62   }
63   else
64     str1[1] = '\0';
65
66   str2[0] = LOBYTE(ch2);
67   if (IsDBCSLeadByte(str2[0]))
68   {
69     str2[1] = HIBYTE(ch2);
70     str2[2] = '\0';
71   }
72   else
73     str2[1] = '\0';
74
75   return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
76 }
77
78 /*************************************************************************
79  * COMCTL32_ChrCmpHelperW
80  *
81  * Internal helper for COMCTL32_ChrCmpW/ChrCmpIW.
82  */
83 static BOOL COMCTL32_ChrCmpHelperW(WCHAR ch1, WCHAR ch2, DWORD dwFlags)
84 {
85   WCHAR str1[2], str2[2];
86
87   str1[0] = ch1;
88   str1[1] = '\0';
89   str2[0] = ch2;
90   str2[1] = '\0';
91   return CompareStringW(GetThreadLocale(), dwFlags, str1, 2, str2, 2) - 2;
92 }
93
94 /*************************************************************************
95  * COMCTL32_ChrCmpA (internal)
96  *
97  * Internal helper function.
98  */
99 static BOOL COMCTL32_ChrCmpA(WORD ch1, WORD ch2)
100 {
101   return COMCTL32_ChrCmpHelperA(ch1, ch2, 0);
102 }
103
104 /*************************************************************************
105  * COMCTL32_ChrCmpIA    (internal)
106  *
107  * Compare two characters, ignoring case.
108  *
109  * PARAMS
110  *  ch1 [I] First character to compare
111  *  ch2 [I] Second character to compare
112  *
113  * RETURNS
114  *  FALSE, if the characters are equal.
115  *  Non-zero otherwise.
116  */
117 static BOOL COMCTL32_ChrCmpIA(WORD ch1, WORD ch2)
118 {
119   TRACE("(%d,%d)\n", ch1, ch2);
120
121   return COMCTL32_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
122 }
123
124 /*************************************************************************
125  * COMCTL32_ChrCmpW
126  *
127  * Internal helper function.
128  */
129 static BOOL COMCTL32_ChrCmpW(WCHAR ch1, WCHAR ch2)
130 {
131   return COMCTL32_ChrCmpHelperW(ch1, ch2, 0);
132 }
133
134 /*************************************************************************
135  * COMCTL32_ChrCmpIW
136  *
137  * Internal helper function.
138  */
139 static BOOL COMCTL32_ChrCmpIW(WCHAR ch1, WCHAR ch2)
140 {
141   return COMCTL32_ChrCmpHelperW(ch1, ch2, NORM_IGNORECASE);
142 }
143
144 /**************************************************************************
145  * StrChrA [COMCTL32.350]
146  *
147  * Find a given character in a string.
148  *
149  * PARAMS
150  *  lpszStr [I] String to search in.
151  *  ch      [I] Character to search for.
152  *
153  * RETURNS
154  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
155  *           not found.
156  *  Failure: NULL, if any arguments are invalid.
157  */
158 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
159 {
160   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
161
162   if (lpszStr)
163   {
164     while (*lpszStr)
165     {
166       if (!COMCTL32_ChrCmpA(*lpszStr, ch))
167         return (LPSTR)lpszStr;
168       lpszStr = CharNextA(lpszStr);
169     }
170   }
171   return NULL;
172 }
173
174 /*************************************************************************
175  * COMCTL32_StrStrHelperA
176  *
177  * Internal implementation of StrStrA/StrStrIA
178  */
179 static LPSTR COMCTL32_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
180                                     int (*pStrCmpFn)(LPCSTR,LPCSTR,size_t))
181 {
182   size_t iLen;
183
184   if (!lpszStr || !lpszSearch || !*lpszSearch)
185     return NULL;
186
187   iLen = strlen(lpszSearch);
188
189   while (*lpszStr)
190   {
191     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
192       return (LPSTR)lpszStr;
193     lpszStr = CharNextA(lpszStr);
194   }
195   return NULL;
196 }
197
198 /*************************************************************************
199  * COMCTL32_StrStrHelperW
200  *
201  * Internal implementation of StrStrW/StrStrIW
202  */
203 static LPWSTR COMCTL32_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch,
204                                      int (*pStrCmpFn)(LPCWSTR,LPCWSTR,int))
205 {
206   int iLen;
207
208   if (!lpszStr || !lpszSearch || !*lpszSearch)
209     return NULL;
210
211   iLen = strlenW(lpszSearch);
212
213   while (*lpszStr)
214   {
215     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
216       return (LPWSTR)lpszStr;
217     lpszStr = CharNextW(lpszStr);
218   }
219   return NULL;
220 }
221
222 /**************************************************************************
223  * StrStrIA [COMCTL32.355]
224  *
225  * Find a substring within a string, ignoring case.
226  *
227  * PARAMS
228  *  lpszStr    [I] String to search in
229  *  lpszSearch [I] String to look for
230  *
231  * RETURNS
232  *  The start of lpszSearch within lpszStr, or NULL if not found.
233  */
234 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
235 {
236   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
237
238   return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, strncasecmp);
239 }
240
241 /**************************************************************************
242  * StrToIntA [COMCTL32.357]
243  *
244  * Read a signed integer from a string.
245  *
246  * PARAMS
247  *  lpszStr [I] String to read integer from
248  *
249  * RETURNS
250  *   The signed integer value represented by the string, or 0 if no integer is
251  *   present.
252  */
253 INT WINAPI StrToIntA (LPSTR lpszStr)
254 {
255     return atoi(lpszStr);
256 }
257
258 /**************************************************************************
259  * StrStrIW [COMCTL32.363]
260  *
261  * See StrStrIA.
262  */
263 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
264 {
265   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
266
267   return COMCTL32_StrStrHelperW(lpszStr, lpszSearch, strncmpiW);
268 }
269
270 /**************************************************************************
271  * StrToIntW [COMCTL32.365]
272  *
273  * See StrToIntA.
274  */
275 INT WINAPI StrToIntW (LPWSTR lpString)
276 {
277     return atoiW(lpString);
278 }
279
280 /*************************************************************************
281  * COMCTL32_StrSpnHelperA (internal)
282  *
283  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
284  */
285 static int COMCTL32_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
286                                   LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
287                                   BOOL bInvert)
288 {
289   LPCSTR lpszRead = lpszStr;
290   if (lpszStr && *lpszStr && lpszMatch)
291   {
292     while (*lpszRead)
293     {
294       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
295
296       if (!bInvert && !lpszTest)
297         break;
298       if (bInvert && lpszTest)
299         break;
300       lpszRead = CharNextA(lpszRead);
301     };
302   }
303   return lpszRead - lpszStr;
304 }
305
306 /**************************************************************************
307  * StrCSpnA [COMCTL32.356]
308  *
309  * Find the length of the start of a string that does not contain certain
310  * characters.
311  *
312  * PARAMS
313  *  lpszStr   [I] String to search
314  *  lpszMatch [I] Characters that cannot be in the substring
315  *
316  * RETURNS
317  *  The length of the part of lpszStr containing only chars not in lpszMatch,
318  *  or 0 if any parameter is invalid.
319  */
320 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
321 {
322   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
323
324   return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
325 }
326
327 /**************************************************************************
328  * StrChrW [COMCTL32.358]
329  *
330  * See StrChrA.
331  */
332 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
333 {
334   LPWSTR lpszRet = NULL;
335
336   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
337
338   if (lpszStr)
339     lpszRet = strchrW(lpszStr, ch);
340   return lpszRet;
341 }
342
343 /**************************************************************************
344  * StrCmpNA [COMCTL32.352]
345  *
346  * Compare two strings, up to a maximum length.
347  *
348  * PARAMS
349  *  lpszStr  [I] First string to compare
350  *  lpszComp [I] Second string to compare
351  *  iLen     [I] Maximum number of chars to compare.
352  *
353  * RETURNS
354  *  An integer less than, equal to or greater than 0, indicating that
355  *  lpszStr is less than, the same, or greater than lpszComp.
356  */
357 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
358 {
359   INT iRet;
360
361   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
362
363   iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
364   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
365 }
366
367 /**************************************************************************
368  * StrCmpNIA [COMCTL32.353]
369  *
370  * Compare two strings, up to a maximum length, ignoring case.
371  *
372  * PARAMS
373  *  lpszStr  [I] First string to compare
374  *  lpszComp [I] Second string to compare
375  *  iLen     [I] Maximum number of chars to compare.
376  *
377  * RETURNS
378  *  An integer less than, equal to or greater than 0, indicating that
379  *  lpszStr is less than, the same, or greater than lpszComp.
380  */
381 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
382 {
383   INT iRet;
384
385   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
386
387   iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
388   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
389 }
390
391 /*************************************************************************
392  * StrCmpNIW    [COMCTL32.361]
393  *
394  * See StrCmpNIA.
395  */
396 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
397 {
398   INT iRet;
399
400   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
401
402   iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
403   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
404 }
405
406 /**************************************************************************
407  * StrCmpNW [COMCTL32.360]
408  *
409  * See StrCmpNA.
410  */
411 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
412 {
413   INT iRet;
414
415   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
416
417   iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
418   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
419 }
420
421 /**************************************************************************
422  * StrRChrA [COMCTL32.351]
423  *
424  * Find the last occurrence of a character in string.
425  *
426  * PARAMS
427  *  lpszStr [I] String to search in
428  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
429  *  ch      [I] Character to search for.
430  *
431  * RETURNS
432  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
433  *           or NULL if not found.
434  *  Failure: NULL, if any arguments are invalid.
435  */
436 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
437 {
438   LPCSTR lpszRet = NULL;
439
440   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
441
442   if (lpszStr)
443   {
444     WORD ch2;
445
446     if (!lpszEnd)
447       lpszEnd = lpszStr + lstrlenA(lpszStr);
448
449     while (*lpszStr && lpszStr <= lpszEnd)
450     {
451       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
452
453       if (!COMCTL32_ChrCmpA(ch, ch2))
454         lpszRet = lpszStr;
455       lpszStr = CharNextA(lpszStr);
456     }
457   }
458   return (LPSTR)lpszRet;
459 }
460
461
462 /**************************************************************************
463  * StrRChrW [COMCTL32.359]
464  *
465  * See StrRChrA.
466  */
467 LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
468 {
469   LPCWSTR lpszRet = NULL;
470
471   TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
472
473   if (lpszStr)
474   {
475     if (!lpszEnd)
476       lpszEnd = lpszStr + strlenW(lpszStr);
477
478     while (*lpszStr && lpszStr <= lpszEnd)
479     {
480       if (!COMCTL32_ChrCmpW(ch, *lpszStr))
481         lpszRet = lpszStr;
482       lpszStr = CharNextW(lpszStr);
483     }
484   }
485   return (LPWSTR)lpszRet;
486 }
487
488 /**************************************************************************
489  * StrStrA [COMCTL32.354]
490  *
491  * Find a substring within a string.
492  *
493  * PARAMS
494  *  lpszStr    [I] String to search in
495  *  lpszSearch [I] String to look for
496  *
497  * RETURNS
498  *  The start of lpszSearch within lpszStr, or NULL if not found.
499  */
500 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
501 {
502   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
503
504   return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, strncmp);
505 }
506
507 /**************************************************************************
508  * StrStrW [COMCTL32.362]
509  *
510  * See StrStrA.
511  */
512 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
513 {
514   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
515
516   return COMCTL32_StrStrHelperW(lpszStr, lpszSearch, strncmpW);
517 }
518
519 /*************************************************************************
520  * StrChrIA     [COMCTL32.366]
521  *
522  * Find a given character in a string, ignoring case.
523  *
524  * PARAMS
525  *  lpszStr [I] String to search in.
526  *  ch      [I] Character to search for.
527  *
528  * RETURNS
529  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
530  *           not found.
531  *  Failure: NULL, if any arguments are invalid.
532  */
533 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
534 {
535   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
536
537   if (lpszStr)
538   {
539     while (*lpszStr)
540     {
541       if (!COMCTL32_ChrCmpIA(*lpszStr, ch))
542         return (LPSTR)lpszStr;
543       lpszStr = CharNextA(lpszStr);
544     }
545   }
546   return NULL;
547 }
548
549 /*************************************************************************
550  * StrChrIW     [COMCTL32.367]
551  *
552  * See StrChrA.
553  */
554 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
555 {
556   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
557
558   if (lpszStr)
559   {
560     ch = toupperW(ch);
561     while (*lpszStr)
562     {
563       if (toupperW(*lpszStr) == ch)
564         return (LPWSTR)lpszStr;
565       lpszStr = CharNextW(lpszStr);
566     }
567     lpszStr = NULL;
568   }
569   return (LPWSTR)lpszStr;
570 }
571
572 /*************************************************************************
573  * StrRStrIA    [COMCTL32.372]
574  *
575  * Find the last occurrence of a substring within a string.
576  *
577  * PARAMS
578  *  lpszStr    [I] String to search in
579  *  lpszEnd    [I] End of lpszStr
580  *  lpszSearch [I] String to look for
581  *
582  * RETURNS
583  *  The last occurrence lpszSearch within lpszStr, or NULL if not found.
584  */
585 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
586 {
587   LPSTR lpszRet = NULL;
588   WORD ch1, ch2;
589   INT iLen;
590  
591   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
592  
593   if (!lpszStr || !lpszSearch || !*lpszSearch)
594     return NULL;
595
596   if (!lpszEnd)
597     lpszEnd = lpszStr + lstrlenA(lpszStr);
598
599   if (IsDBCSLeadByte(*lpszSearch))
600     ch1 = *lpszSearch << 8 | lpszSearch[1];
601   else
602     ch1 = *lpszSearch;
603   iLen = lstrlenA(lpszSearch);
604
605   while (lpszStr <= lpszEnd  && *lpszStr)
606   {
607     ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
608     if (!COMCTL32_ChrCmpIA(ch1, ch2))
609     {
610       if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
611         lpszRet = (LPSTR)lpszStr;
612     }
613     lpszStr = CharNextA(lpszStr);
614   }
615   return lpszRet;
616 }
617
618 /*************************************************************************
619  * StrRStrIW    [COMCTL32.373]
620  *
621  * See StrRStrIA.
622  */
623 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
624 {
625   LPWSTR lpszRet = NULL;
626   INT iLen;
627
628   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
629
630   if (!lpszStr || !lpszSearch || !*lpszSearch)
631     return NULL;
632
633   if (!lpszEnd)
634     lpszEnd = lpszStr + strlenW(lpszStr);
635
636   iLen = strlenW(lpszSearch);
637
638   while (lpszStr <= lpszEnd  && *lpszStr)
639   {
640     if (!COMCTL32_ChrCmpIW(*lpszSearch, *lpszStr))
641     {
642       if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
643         lpszRet = (LPWSTR)lpszStr;
644     }
645     lpszStr = CharNextW(lpszStr);
646   }
647   return lpszRet;
648 }
649
650 /*************************************************************************
651  * COMCTL32_StrSpnHelperW
652  *
653  * Internal implementation of StrSpnW/StrCSpnW/StrCSpnIW
654  */
655 static int COMCTL32_StrSpnHelperW(LPCWSTR lpszStr, LPCWSTR lpszMatch,
656                                   LPWSTR (WINAPI *pStrChrFn)(LPCWSTR,WCHAR),
657                                   BOOL bInvert)
658 {
659   LPCWSTR lpszRead = lpszStr;
660   if (lpszStr && *lpszStr && lpszMatch)
661   {
662     while (*lpszRead)
663     {
664       LPCWSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
665
666       if (!bInvert && !lpszTest)
667         break;
668       if (bInvert && lpszTest)
669         break;
670       lpszRead = CharNextW(lpszRead);
671     };
672   }
673   return lpszRead - lpszStr;
674 }
675
676 /*************************************************************************
677  * StrCSpnIA    [COMCTL32.374]
678  *
679  * Find the length of the start of a string that does not contain certain
680  * characters, ignoring case.
681  *
682  * PARAMS
683  *  lpszStr   [I] String to search
684  *  lpszMatch [I] Characters that cannot be in the substring
685  *
686  * RETURNS
687  *  The length of the part of lpszStr containing only chars not in lpszMatch,
688  *  or 0 if any parameter is invalid.
689  */
690 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
691 {
692   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
693
694   return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
695 }
696
697 /*************************************************************************
698  * StrCSpnIW    [COMCTL32.375]
699  *
700  * See StrCSpnIA.
701  */
702 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
703 {
704   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
705
706   return COMCTL32_StrSpnHelperW(lpszStr, lpszMatch, StrChrIW, TRUE);
707 }
708
709 /**************************************************************************
710  * StrRChrIA    [COMCTL32.368]
711  *
712  * Find the last occurrence of a character in string, ignoring case.
713  *
714  * PARAMS
715  *  lpszStr [I] String to search in
716  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
717  *  ch      [I] Character to search for.
718  *
719  * RETURNS
720  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
721  *           or NULL if not found.
722  *  Failure: NULL, if any arguments are invalid.
723  */
724 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
725 {
726   LPCSTR lpszRet = NULL;
727
728   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
729
730   if (lpszStr)
731   {
732     WORD ch2;
733
734     if (!lpszEnd)
735       lpszEnd = lpszStr + lstrlenA(lpszStr);
736
737     while (*lpszStr && lpszStr <= lpszEnd)
738     {
739       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
740
741       if (ch == ch2)
742         lpszRet = lpszStr;
743       lpszStr = CharNextA(lpszStr);
744     }
745   }
746   return (LPSTR)lpszRet;
747 }
748
749 /**************************************************************************
750  * StrRChrIW    [COMCTL32.369]
751  *
752  * See StrRChrIA.
753  */
754 LPWSTR WINAPI StrRChrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
755 {
756   LPCWSTR lpszRet = NULL;
757
758   TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
759
760   if (lpszStr)
761   {
762     if (!lpszEnd)
763       lpszEnd = lpszStr + strlenW(lpszStr);
764
765     while (*lpszStr && lpszStr <= lpszEnd)
766     {
767       if (ch == *lpszStr)
768         lpszRet = lpszStr;
769       lpszStr = CharNextW(lpszStr);
770     }
771   }
772   return (LPWSTR)lpszRet;
773 }
774
775 /*************************************************************************
776  * StrCSpnW     [COMCTL32.364]
777  *
778  * See StrCSpnA.
779  */
780 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
781 {
782   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
783
784   return COMCTL32_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, TRUE);
785 }
786
787 /*************************************************************************
788  * IntlStrEqWorkerA     [COMCTL32.376]
789  *
790  * Compare two strings.
791  *
792  * PARAMS
793  *  bCase    [I] Whether to compare case sensitively
794  *  lpszStr  [I] First string to compare
795  *  lpszComp [I] Second string to compare
796  *  iLen     [I] Length to compare
797  *
798  * RETURNS
799  *  TRUE  If the strings are equal.
800  *  FALSE Otherwise.
801  */
802 BOOL WINAPI IntlStrEqWorkerA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
803                              int iLen)
804 {
805   DWORD dwFlags = LOCALE_USE_CP_ACP;
806   int iRet;
807
808   TRACE("(%d,%s,%s,%d)\n", bCase,
809         debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
810
811   /* FIXME: These flags are undocumented and unknown by our CompareString.
812    *        We need defines for them.
813    */
814   dwFlags |= bCase ? 0x10000000 : 0x10000001;
815
816   iRet = CompareStringA(GetThreadLocale(),
817                         dwFlags, lpszStr, iLen, lpszComp, iLen);
818
819   if (!iRet)
820     iRet = CompareStringA(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
821
822   return iRet == 2 ? TRUE : FALSE;
823 }
824
825 /*************************************************************************
826  * IntlStrEqWorkerW     [COMCTL32.377]
827  *
828  * See IntlStrEqWorkerA.
829  */
830 BOOL WINAPI IntlStrEqWorkerW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
831                              int iLen)
832 {
833   DWORD dwFlags;
834   int iRet;
835
836   TRACE("(%d,%s,%s,%d)\n", bCase,
837         debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
838
839   /* FIXME: These flags are undocumented and unknown by our CompareString.
840    *        We need defines for them.
841    */
842   dwFlags = bCase ? 0x10000000 : 0x10000001;
843
844   iRet = CompareStringW(GetThreadLocale(),
845                         dwFlags, lpszStr, iLen, lpszComp, iLen);
846
847   if (!iRet)
848     iRet = CompareStringW(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
849
850   return iRet == 2 ? TRUE : FALSE;
851 }