Use system metrics values in TOOLBAR_DrawPattern instead of hardcoded
[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  * StrChrA [COMCTL32.350]
136  *
137  * Find a given character in a string.
138  *
139  * PARAMS
140  *  lpszStr [I] String to search in.
141  *  ch      [I] Character to search for.
142  *
143  * RETURNS
144  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
145  *           not found.
146  *  Failure: NULL, if any arguments are invalid.
147  */
148 LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch)
149 {
150   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
151
152   if (lpszStr)
153   {
154     while (*lpszStr)
155     {
156       if (!COMCTL32_ChrCmpA(*lpszStr, ch))
157         return (LPSTR)lpszStr;
158       lpszStr = CharNextA(lpszStr);
159     }
160   }
161   return NULL;
162 }
163
164 /*************************************************************************
165  * COMCTL32_StrStrHelperA
166  *
167  * Internal implementation of StrStrA/StrStrIA
168  */
169 static LPSTR COMCTL32_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
170                                     int (*pStrCmpFn)(LPCSTR,LPCSTR,size_t))
171 {
172   size_t iLen;
173
174   if (!lpszStr || !lpszSearch || !*lpszSearch)
175     return NULL;
176
177   iLen = strlen(lpszSearch);
178
179   while (*lpszStr)
180   {
181     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
182       return (LPSTR)lpszStr;
183     lpszStr = CharNextA(lpszStr);
184   }
185   return NULL;
186 }
187
188 /*************************************************************************
189  * COMCTL32_StrStrHelperW
190  *
191  * Internal implementation of StrStrW/StrStrIW
192  */
193 static LPWSTR COMCTL32_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch,
194                                      int (*pStrCmpFn)(LPCWSTR,LPCWSTR,int))
195 {
196   int iLen;
197
198   if (!lpszStr || !lpszSearch || !*lpszSearch)
199     return NULL;
200
201   iLen = strlenW(lpszSearch);
202
203   while (*lpszStr)
204   {
205     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
206       return (LPWSTR)lpszStr;
207     lpszStr = CharNextW(lpszStr);
208   }
209   return NULL;
210 }
211
212 /**************************************************************************
213  * StrStrIA [COMCTL32.355]
214  *
215  * Find a substring within a string, ignoring case.
216  *
217  * PARAMS
218  *  lpszStr    [I] String to search in
219  *  lpszSearch [I] String to look for
220  *
221  * RETURNS
222  *  The start of lpszSearch within lpszStr, or NULL if not found.
223  */
224 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
225 {
226   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
227
228   return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, strncasecmp);
229 }
230
231 /**************************************************************************
232  * StrToIntA [COMCTL32.357]
233  *
234  * Read a signed integer from a string.
235  *
236  * PARAMS
237  *  lpszStr [I] String to read integer from
238  *
239  * RETURNS
240  *   The signed integer value represented by the string, or 0 if no integer is
241  *   present.
242  */
243 INT WINAPI StrToIntA (LPSTR lpszStr)
244 {
245     return atoi(lpszStr);
246 }
247
248 /**************************************************************************
249  * StrStrIW [COMCTL32.363]
250  *
251  * See StrStrIA.
252  */
253 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
254 {
255   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
256
257   return COMCTL32_StrStrHelperW(lpszStr, lpszSearch, strncmpiW);
258 }
259
260 /**************************************************************************
261  * StrToIntW [COMCTL32.365]
262  *
263  * See StrToIntA.
264  */
265 INT WINAPI StrToIntW (LPWSTR lpString)
266 {
267     return atoiW(lpString);
268 }
269
270 /*************************************************************************
271  * COMCTL32_StrSpnHelperA (internal)
272  *
273  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
274  */
275 static int COMCTL32_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
276                                   LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
277                                   BOOL bInvert)
278 {
279   LPCSTR lpszRead = lpszStr;
280   if (lpszStr && *lpszStr && lpszMatch)
281   {
282     while (*lpszRead)
283     {
284       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
285
286       if (!bInvert && !lpszTest)
287         break;
288       if (bInvert && lpszTest)
289         break;
290       lpszRead = CharNextA(lpszRead);
291     };
292   }
293   return lpszRead - lpszStr;
294 }
295
296 /**************************************************************************
297  * StrCSpnA [COMCTL32.356]
298  *
299  * Find the length of the start of a string that does not contain certain
300  * characters.
301  *
302  * PARAMS
303  *  lpszStr   [I] String to search
304  *  lpszMatch [I] Characters that cannot be in the substring
305  *
306  * RETURNS
307  *  The length of the part of lpszStr containing only chars not in lpszMatch,
308  *  or 0 if any parameter is invalid.
309  */
310 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
311 {
312   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
313
314   return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
315 }
316
317 /**************************************************************************
318  * StrChrW [COMCTL32.358]
319  *
320  * See StrChrA.
321  */
322 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
323 {
324   LPWSTR lpszRet = NULL;
325
326   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
327
328   if (lpszStr)
329     lpszRet = strchrW(lpszStr, ch);
330   return lpszRet;
331 }
332
333 /**************************************************************************
334  * StrCmpNA [COMCTL32.352]
335  *
336  * Compare two strings, up to a maximum length.
337  *
338  * PARAMS
339  *  lpszStr  [I] First string to compare
340  *  lpszComp [I] Second string to compare
341  *  iLen     [I] Maximum number of chars to compare.
342  *
343  * RETURNS
344  *  An integer less than, equal to or greater than 0, indicating that
345  *  lpszStr is less than, the same, or greater than lpszComp.
346  */
347 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
348 {
349   INT iRet;
350
351   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
352
353   iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
354   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
355 }
356
357 /**************************************************************************
358  * StrCmpNIA [COMCTL32.353]
359  *
360  * Compare two strings, up to a maximum length, ignoring case.
361  *
362  * PARAMS
363  *  lpszStr  [I] First string to compare
364  *  lpszComp [I] Second string to compare
365  *  iLen     [I] Maximum number of chars to compare.
366  *
367  * RETURNS
368  *  An integer less than, equal to or greater than 0, indicating that
369  *  lpszStr is less than, the same, or greater than lpszComp.
370  */
371 int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
372 {
373   INT iRet;
374
375   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
376
377   iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
378   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
379 }
380
381 /*************************************************************************
382  * StrCmpNIW    [COMCTL32.361]
383  *
384  * See StrCmpNIA.
385  */
386 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, int iLen)
387 {
388   INT iRet;
389
390   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
391
392   iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen);
393   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
394 }
395
396 /**************************************************************************
397  * StrCmpNW [COMCTL32.360]
398  *
399  * See StrCmpNA.
400  */
401 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
402 {
403   INT iRet;
404
405   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
406
407   iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen);
408   return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0;
409 }
410
411 /**************************************************************************
412  * StrRChrA [COMCTL32.351]
413  *
414  * Find the last occurence of a character in string.
415  *
416  * PARAMS
417  *  lpszStr [I] String to search in
418  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
419  *  ch      [I] Character to search for.
420  *
421  * RETURNS
422  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
423  *           or NULL if not found.
424  *  Failure: NULL, if any arguments are invalid.
425  */
426 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
427 {
428   LPCSTR lpszRet = NULL;
429
430   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
431
432   if (lpszStr)
433   {
434     WORD ch2;
435
436     if (!lpszEnd)
437       lpszEnd = lpszStr + lstrlenA(lpszStr);
438
439     while (*lpszStr && lpszStr <= lpszEnd)
440     {
441       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
442
443       if (!COMCTL32_ChrCmpA(ch, ch2))
444         lpszRet = lpszStr;
445       lpszStr = CharNextA(lpszStr);
446     }
447   }
448   return (LPSTR)lpszRet;
449 }
450
451
452 /**************************************************************************
453  * StrRChrW [COMCTL32.359]
454  *
455  * See StrRChrA.
456  */
457 LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
458 {
459   LPCWSTR lpszRet = NULL;
460
461   TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
462
463   if (lpszStr)
464   {
465     if (!lpszEnd)
466       lpszEnd = lpszStr + strlenW(lpszStr);
467
468     while (*lpszStr && lpszStr <= lpszEnd)
469     {
470       if (!COMCTL32_ChrCmpW(ch, *lpszStr))
471         lpszRet = lpszStr;
472       lpszStr = CharNextW(lpszStr);
473     }
474   }
475   return (LPWSTR)lpszRet;
476 }
477
478 /**************************************************************************
479  * StrStrA [COMCTL32.354]
480  *
481  * Find a substring within a string.
482  *
483  * PARAMS
484  *  lpszStr    [I] String to search in
485  *  lpszSearch [I] String to look for
486  *
487  * RETURNS
488  *  The start of lpszSearch within lpszStr, or NULL if not found.
489  */
490 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
491 {
492   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
493
494   return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, strncmp);
495 }
496
497 /**************************************************************************
498  * StrStrW [COMCTL32.362]
499  *
500  * See StrStrA.
501  */
502 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
503 {
504   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
505
506   return COMCTL32_StrStrHelperW(lpszStr, lpszSearch, strncmpW);
507 }
508
509 /*************************************************************************
510  * StrChrIA     [COMCTL32.366]
511  *
512  * Find a given character in a string, ignoring case.
513  *
514  * PARAMS
515  *  lpszStr [I] String to search in.
516  *  ch      [I] Character to search for.
517  *
518  * RETURNS
519  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
520  *           not found.
521  *  Failure: NULL, if any arguments are invalid.
522  */
523 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
524 {
525   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
526
527   if (lpszStr)
528   {
529     while (*lpszStr)
530     {
531       if (!COMCTL32_ChrCmpIA(*lpszStr, ch))
532         return (LPSTR)lpszStr;
533       lpszStr = CharNextA(lpszStr);
534     }
535   }
536   return NULL;
537 }
538
539 /*************************************************************************
540  * StrChrIW     [COMCTL32.367]
541  *
542  * See StrChrA.
543  */
544 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
545 {
546   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
547
548   if (lpszStr)
549   {
550     ch = toupperW(ch);
551     while (*lpszStr)
552     {
553       if (toupperW(*lpszStr) == ch)
554         return (LPWSTR)lpszStr;
555       lpszStr = CharNextW(lpszStr);
556     }
557     lpszStr = NULL;
558   }
559   return (LPWSTR)lpszStr;
560 }
561
562 /*************************************************************************
563  * StrRStrIA    [COMCTL32.372]
564  *
565  * Find the last occurence of a substring within a string.
566  *
567  * PARAMS
568  *  lpszStr    [I] String to search in
569  *  lpszEnd    [I] End of lpszStr
570  *  lpszSearch [I] String to look for
571  *
572  * RETURNS
573  *  The last occurence lpszSearch within lpszStr, or NULL if not found.
574  */
575 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
576 {
577   LPSTR lpszRet = NULL;
578   WORD ch1, ch2;
579   INT iLen;
580  
581   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
582  
583   if (!lpszStr || !lpszSearch || !*lpszSearch)
584     return NULL;
585
586   if (!lpszEnd)
587     lpszEnd = lpszStr + lstrlenA(lpszStr);
588
589   if (IsDBCSLeadByte(*lpszSearch))
590     ch1 = *lpszSearch << 8 | lpszSearch[1];
591   else
592     ch1 = *lpszSearch;
593   iLen = lstrlenA(lpszSearch);
594
595   while (lpszStr <= lpszEnd  && *lpszStr)
596   {
597     ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
598     if (!COMCTL32_ChrCmpIA(ch1, ch2))
599     {
600       if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
601         lpszRet = (LPSTR)lpszStr;
602     }
603     lpszStr = CharNextA(lpszStr);
604   }
605   return lpszRet;
606 }
607
608 /*************************************************************************
609  * StrRStrIW    [COMCTL32.373]
610  *
611  * See StrRStrIA.
612  */
613 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
614 {
615   LPWSTR lpszRet = NULL;
616   INT iLen;
617
618   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
619
620   if (!lpszStr || !lpszSearch || !*lpszSearch)
621     return NULL;
622
623   if (!lpszEnd)
624     lpszEnd = lpszStr + strlenW(lpszStr);
625
626   iLen = strlenW(lpszSearch);
627
628   while (lpszStr <= lpszEnd  && *lpszStr)
629   {
630     if (!COMCTL32_ChrCmpIA(*lpszSearch, *lpszStr))
631     {
632       if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
633         lpszRet = (LPWSTR)lpszStr;
634     }
635     lpszStr = CharNextW(lpszStr);
636   }
637   return lpszRet;
638 }
639
640 /*************************************************************************
641  * COMCTL32_StrSpnHelperW
642  *
643  * Internal implementation of StrSpnW/StrCSpnW/StrCSpnIW
644  */
645 static int COMCTL32_StrSpnHelperW(LPCWSTR lpszStr, LPCWSTR lpszMatch,
646                                   LPWSTR (WINAPI *pStrChrFn)(LPCWSTR,WCHAR),
647                                   BOOL bInvert)
648 {
649   LPCWSTR lpszRead = lpszStr;
650   if (lpszStr && *lpszStr && lpszMatch)
651   {
652     while (*lpszRead)
653     {
654       LPCWSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
655
656       if (!bInvert && !lpszTest)
657         break;
658       if (bInvert && lpszTest)
659         break;
660       lpszRead = CharNextW(lpszRead);
661     };
662   }
663   return lpszRead - lpszStr;
664 }
665
666 /*************************************************************************
667  * StrCSpnIA    [COMCTL32.374]
668  *
669  * Find the length of the start of a string that does not contain certain
670  * characters, ignoring case.
671  *
672  * PARAMS
673  *  lpszStr   [I] String to search
674  *  lpszMatch [I] Characters that cannot be in the substring
675  *
676  * RETURNS
677  *  The length of the part of lpszStr containing only chars not in lpszMatch,
678  *  or 0 if any parameter is invalid.
679  */
680 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
681 {
682   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
683
684   return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
685 }
686
687 /*************************************************************************
688  * StrCSpnIW    [COMCTL32.375]
689  *
690  * See StrCSpnIA.
691  */
692 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
693 {
694   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
695
696   return COMCTL32_StrSpnHelperW(lpszStr, lpszMatch, StrChrIW, TRUE);
697 }
698
699 /**************************************************************************
700  * StrRChrIA    [COMCTL32.368]
701  *
702  * Find the last occurence of a character in string, ignoring case.
703  *
704  * PARAMS
705  *  lpszStr [I] String to search in
706  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
707  *  ch      [I] Character to search for.
708  *
709  * RETURNS
710  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
711  *           or NULL if not found.
712  *  Failure: NULL, if any arguments are invalid.
713  */
714 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
715 {
716   LPCSTR lpszRet = NULL;
717
718   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
719
720   if (lpszStr)
721   {
722     WORD ch2;
723
724     if (!lpszEnd)
725       lpszEnd = lpszStr + lstrlenA(lpszStr);
726
727     while (*lpszStr && lpszStr <= lpszEnd)
728     {
729       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
730
731       if (ch == ch2)
732         lpszRet = lpszStr;
733       lpszStr = CharNextA(lpszStr);
734     }
735   }
736   return (LPSTR)lpszRet;
737 }
738
739 /**************************************************************************
740  * StrRChrIW    [COMCTL32.369]
741  *
742  * See StrRChrIA.
743  */
744 LPWSTR WINAPI StrRChrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
745 {
746   LPCWSTR lpszRet = NULL;
747
748   TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
749
750   if (lpszStr)
751   {
752     if (!lpszEnd)
753       lpszEnd = lpszStr + strlenW(lpszStr);
754
755     while (*lpszStr && lpszStr <= lpszEnd)
756     {
757       if (ch == *lpszStr)
758         lpszRet = lpszStr;
759       lpszStr = CharNextW(lpszStr);
760     }
761   }
762   return (LPWSTR)lpszRet;
763 }
764
765 /*************************************************************************
766  * StrCSpnW     [COMCTL32.364]
767  *
768  * See StrCSpnA.
769  */
770 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
771 {
772   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
773
774   return COMCTL32_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, TRUE);
775 }
776
777 /*************************************************************************
778  * IntlStrEqWorkerA     [COMCTL32.376]
779  *
780  * Compare two strings.
781  *
782  * PARAMS
783  *  bCase    [I] Whether to compare case sensitively
784  *  lpszStr  [I] First string to compare
785  *  lpszComp [I] Second string to compare
786  *  iLen     [I] Length to compare
787  *
788  * RETURNS
789  *  TRUE  If the strings are equal.
790  *  FALSE Otherwise.
791  */
792 BOOL WINAPI IntlStrEqWorkerA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
793                              int iLen)
794 {
795   DWORD dwFlags = LOCALE_USE_CP_ACP;
796   int iRet;
797
798   TRACE("(%d,%s,%s,%d)\n", bCase,
799         debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
800
801   /* FIXME: These flags are undocumented and unknown by our CompareString.
802    *        We need defines for them.
803    */
804   dwFlags |= bCase ? 0x10000000 : 0x10000001;
805
806   iRet = CompareStringA(GetThreadLocale(),
807                         dwFlags, lpszStr, iLen, lpszComp, iLen);
808
809   if (!iRet)
810     iRet = CompareStringA(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
811
812   return iRet == 2 ? TRUE : FALSE;
813 }
814
815 /*************************************************************************
816  * IntlStrEqWorkerW     [COMCTL32.377]
817  *
818  * See IntlStrEqWorkerA.
819  */
820 BOOL WINAPI IntlStrEqWorkerW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
821                              int iLen)
822 {
823   DWORD dwFlags;
824   int iRet;
825
826   TRACE("(%d,%s,%s,%d)\n", bCase,
827         debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
828
829   /* FIXME: These flags are undocumented and unknown by our CompareString.
830    *        We need defines for them.
831    */
832   dwFlags = bCase ? 0x10000000 : 0x10000001;
833
834   iRet = CompareStringW(GetThreadLocale(),
835                         dwFlags, lpszStr, iLen, lpszComp, iLen);
836
837   if (!iRet)
838     iRet = CompareStringW(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
839
840   return iRet == 2 ? TRUE : FALSE;
841 }