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