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