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