comctl32/pager: Don't rely on PGN_CALCSIZE to set the non-scrollable dimension of...
[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) - CSTR_EQUAL;
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) - CSTR_EQUAL;
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 %s)\n", lppDest, debugstr_w(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   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
303   return CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
304 }
305
306 /*************************************************************************
307  * StrCmpNIW    [COMCTL32.361]
308  *
309  * See StrCmpNIA.
310  */
311 INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
312 {
313   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
314   return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
315 }
316
317 /*************************************************************************
318  * COMCTL32_StrStrHelperA
319  *
320  * Internal implementation of StrStrA/StrStrIA
321  */
322 static LPSTR COMCTL32_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch,
323                                     INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT))
324 {
325   size_t iLen;
326
327   if (!lpszStr || !lpszSearch || !*lpszSearch)
328     return NULL;
329
330   iLen = strlen(lpszSearch);
331
332   while (*lpszStr)
333   {
334     if (!pStrCmpFn(lpszStr, lpszSearch, iLen))
335       return (LPSTR)lpszStr;
336     lpszStr = CharNextA(lpszStr);
337   }
338   return NULL;
339 }
340
341 /**************************************************************************
342  * StrStrIA [COMCTL32.355]
343  *
344  * Find a substring within a string, ignoring case.
345  *
346  * PARAMS
347  *  lpszStr    [I] String to search in
348  *  lpszSearch [I] String to look for
349  *
350  * RETURNS
351  *  The start of lpszSearch within lpszStr, or NULL if not found.
352  */
353 LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch)
354 {
355   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
356
357   return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA);
358 }
359
360 /**************************************************************************
361  * StrToIntA [COMCTL32.357]
362  *
363  * Read a signed integer from a string.
364  *
365  * PARAMS
366  *  lpszStr [I] String to read integer from
367  *
368  * RETURNS
369  *   The signed integer value represented by the string, or 0 if no integer is
370  *   present.
371  */
372 INT WINAPI StrToIntA (LPCSTR lpszStr)
373 {
374     return atoi(lpszStr);
375 }
376
377 /**************************************************************************
378  * StrStrIW [COMCTL32.363]
379  *
380  * See StrStrIA.
381  */
382 LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
383 {
384   int iLen;
385
386   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
387
388   if (!lpszStr || !lpszSearch || !*lpszSearch)
389     return NULL;
390
391   iLen = strlenW(lpszSearch);
392
393   while (*lpszStr)
394   {
395     if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
396       return (LPWSTR)lpszStr;
397     lpszStr++;
398   }
399   return NULL;
400 }
401
402 /**************************************************************************
403  * StrToIntW [COMCTL32.365]
404  *
405  * See StrToIntA.
406  */
407 INT WINAPI StrToIntW (LPCWSTR lpString)
408 {
409     return atoiW(lpString);
410 }
411
412 /*************************************************************************
413  * COMCTL32_StrSpnHelperA (internal)
414  *
415  * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
416  */
417 static int COMCTL32_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch,
418                                   LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD),
419                                   BOOL bInvert)
420 {
421   LPCSTR lpszRead = lpszStr;
422   if (lpszStr && *lpszStr && lpszMatch)
423   {
424     while (*lpszRead)
425     {
426       LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead);
427
428       if (!bInvert && !lpszTest)
429         break;
430       if (bInvert && lpszTest)
431         break;
432       lpszRead = CharNextA(lpszRead);
433     };
434   }
435   return lpszRead - lpszStr;
436 }
437
438 /**************************************************************************
439  * StrCSpnA [COMCTL32.356]
440  *
441  * Find the length of the start of a string that does not contain certain
442  * characters.
443  *
444  * PARAMS
445  *  lpszStr   [I] String to search
446  *  lpszMatch [I] Characters that cannot be in the substring
447  *
448  * RETURNS
449  *  The length of the part of lpszStr containing only chars not in lpszMatch,
450  *  or 0 if any parameter is invalid.
451  */
452 int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
453 {
454   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
455
456   return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
457 }
458
459 /**************************************************************************
460  * StrChrW [COMCTL32.358]
461  *
462  * See StrChrA.
463  */
464 LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
465 {
466   LPWSTR lpszRet = NULL;
467
468   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
469
470   if (lpszStr)
471     lpszRet = strchrW(lpszStr, ch);
472   return lpszRet;
473 }
474
475 /**************************************************************************
476  * StrCmpNA [COMCTL32.352]
477  *
478  * Compare two strings, up to a maximum length.
479  *
480  * PARAMS
481  *  lpszStr  [I] First string to compare
482  *  lpszComp [I] Second string to compare
483  *  iLen     [I] Maximum number of chars to compare.
484  *
485  * RETURNS
486  *  An integer less than, equal to or greater than 0, indicating that
487  *  lpszStr is less than, the same, or greater than lpszComp.
488  */
489 INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen)
490 {
491   TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
492   return CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
493 }
494
495 /**************************************************************************
496  * StrCmpNW [COMCTL32.360]
497  *
498  * See StrCmpNA.
499  */
500 INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
501 {
502   TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen);
503   return CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen) - CSTR_EQUAL;
504 }
505
506 /**************************************************************************
507  * StrRChrA [COMCTL32.351]
508  *
509  * Find the last occurrence of a character in string.
510  *
511  * PARAMS
512  *  lpszStr [I] String to search in
513  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
514  *  ch      [I] Character to search for.
515  *
516  * RETURNS
517  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
518  *           or NULL if not found.
519  *  Failure: NULL, if any arguments are invalid.
520  */
521 LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
522 {
523   LPCSTR lpszRet = NULL;
524
525   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
526
527   if (lpszStr)
528   {
529     WORD ch2;
530
531     if (!lpszEnd)
532       lpszEnd = lpszStr + lstrlenA(lpszStr);
533
534     while (*lpszStr && lpszStr <= lpszEnd)
535     {
536       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
537
538       if (!COMCTL32_ChrCmpA(ch, ch2))
539         lpszRet = lpszStr;
540       lpszStr = CharNextA(lpszStr);
541     }
542   }
543   return (LPSTR)lpszRet;
544 }
545
546
547 /**************************************************************************
548  * StrRChrW [COMCTL32.359]
549  *
550  * See StrRChrA.
551  */
552 LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
553 {
554     WCHAR *ret = NULL;
555
556     if (!str) return NULL;
557     if (!end) end = str + strlenW(str);
558     while (str < end)
559     {
560         if (*str == ch) ret = (WCHAR *)str;
561         str++;
562     }
563     return ret;
564 }
565
566 /**************************************************************************
567  * StrStrA [COMCTL32.354]
568  *
569  * Find a substring within a string.
570  *
571  * PARAMS
572  *  lpszStr    [I] String to search in
573  *  lpszSearch [I] String to look for
574  *
575  * RETURNS
576  *  The start of lpszSearch within lpszStr, or NULL if not found.
577  */
578 LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
579 {
580   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
581
582   return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA);
583 }
584
585 /**************************************************************************
586  * StrStrW [COMCTL32.362]
587  *
588  * See StrStrA.
589  */
590 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
591 {
592     if (!lpszStr || !lpszSearch) return NULL;
593     return strstrW( lpszStr, lpszSearch );
594 }
595
596 /*************************************************************************
597  * StrChrIA     [COMCTL32.366]
598  *
599  * Find a given character in a string, ignoring case.
600  *
601  * PARAMS
602  *  lpszStr [I] String to search in.
603  *  ch      [I] Character to search for.
604  *
605  * RETURNS
606  *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if
607  *           not found.
608  *  Failure: NULL, if any arguments are invalid.
609  */
610 LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch)
611 {
612   TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch);
613
614   if (lpszStr)
615   {
616     while (*lpszStr)
617     {
618       if (!COMCTL32_ChrCmpIA(*lpszStr, ch))
619         return (LPSTR)lpszStr;
620       lpszStr = CharNextA(lpszStr);
621     }
622   }
623   return NULL;
624 }
625
626 /*************************************************************************
627  * StrChrIW     [COMCTL32.367]
628  *
629  * See StrChrA.
630  */
631 LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
632 {
633   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
634
635   if (lpszStr)
636   {
637     ch = toupperW(ch);
638     while (*lpszStr)
639     {
640       if (toupperW(*lpszStr) == ch)
641         return (LPWSTR)lpszStr;
642       lpszStr++;
643     }
644     lpszStr = NULL;
645   }
646   return (LPWSTR)lpszStr;
647 }
648
649 /*************************************************************************
650  * StrRStrIA    [COMCTL32.372]
651  *
652  * Find the last occurrence of a substring within a string.
653  *
654  * PARAMS
655  *  lpszStr    [I] String to search in
656  *  lpszEnd    [I] End of lpszStr
657  *  lpszSearch [I] String to look for
658  *
659  * RETURNS
660  *  The last occurrence lpszSearch within lpszStr, or NULL if not found.
661  */
662 LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch)
663 {
664   LPSTR lpszRet = NULL;
665   WORD ch1, ch2;
666   INT iLen;
667  
668   TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch));
669  
670   if (!lpszStr || !lpszSearch || !*lpszSearch)
671     return NULL;
672
673   if (!lpszEnd)
674     lpszEnd = lpszStr + lstrlenA(lpszStr);
675
676   if (IsDBCSLeadByte(*lpszSearch))
677     ch1 = *lpszSearch << 8 | lpszSearch[1];
678   else
679     ch1 = *lpszSearch;
680   iLen = lstrlenA(lpszSearch);
681
682   while (lpszStr <= lpszEnd  && *lpszStr)
683   {
684     ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
685     if (!COMCTL32_ChrCmpIA(ch1, ch2))
686     {
687       if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
688         lpszRet = (LPSTR)lpszStr;
689     }
690     lpszStr = CharNextA(lpszStr);
691   }
692   return lpszRet;
693 }
694
695 /*************************************************************************
696  * StrRStrIW    [COMCTL32.373]
697  *
698  * See StrRStrIA.
699  */
700 LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
701 {
702   LPWSTR lpszRet = NULL;
703   INT iLen;
704
705   TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
706
707   if (!lpszStr || !lpszSearch || !*lpszSearch)
708     return NULL;
709
710   if (!lpszEnd)
711     lpszEnd = lpszStr + strlenW(lpszStr);
712
713   iLen = strlenW(lpszSearch);
714
715   while (lpszStr <= lpszEnd  && *lpszStr)
716   {
717     if (!COMCTL32_ChrCmpIW(*lpszSearch, *lpszStr))
718     {
719       if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
720         lpszRet = (LPWSTR)lpszStr;
721     }
722     lpszStr++;
723   }
724   return lpszRet;
725 }
726
727 /*************************************************************************
728  * StrCSpnIA    [COMCTL32.374]
729  *
730  * Find the length of the start of a string that does not contain certain
731  * characters, ignoring case.
732  *
733  * PARAMS
734  *  lpszStr   [I] String to search
735  *  lpszMatch [I] Characters that cannot be in the substring
736  *
737  * RETURNS
738  *  The length of the part of lpszStr containing only chars not in lpszMatch,
739  *  or 0 if any parameter is invalid.
740  */
741 int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch)
742 {
743   TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
744
745   return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
746 }
747
748 /*************************************************************************
749  * StrCSpnIW    [COMCTL32.375]
750  *
751  * See StrCSpnIA.
752  */
753 int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
754 {
755   LPCWSTR lpszRead = lpszStr;
756
757   TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
758
759   if (lpszStr && *lpszStr && lpszMatch)
760   {
761     while (*lpszRead)
762     {
763       if (StrChrIW(lpszMatch, *lpszRead)) break;
764       lpszRead++;
765     }
766   }
767   return lpszRead - lpszStr;
768 }
769
770 /**************************************************************************
771  * StrRChrIA    [COMCTL32.368]
772  *
773  * Find the last occurrence of a character in string, ignoring case.
774  *
775  * PARAMS
776  *  lpszStr [I] String to search in
777  *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr
778  *  ch      [I] Character to search for.
779  *
780  * RETURNS
781  *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd,
782  *           or NULL if not found.
783  *  Failure: NULL, if any arguments are invalid.
784  */
785 LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch)
786 {
787   LPCSTR lpszRet = NULL;
788
789   TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
790
791   if (lpszStr)
792   {
793     WORD ch2;
794
795     if (!lpszEnd)
796       lpszEnd = lpszStr + lstrlenA(lpszStr);
797
798     while (*lpszStr && lpszStr <= lpszEnd)
799     {
800       ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
801
802       if (ch == ch2)
803         lpszRet = lpszStr;
804       lpszStr = CharNextA(lpszStr);
805     }
806   }
807   return (LPSTR)lpszRet;
808 }
809
810 /**************************************************************************
811  * StrRChrIW    [COMCTL32.369]
812  *
813  * See StrRChrIA.
814  */
815 LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
816 {
817     WCHAR *ret = NULL;
818
819     if (!str) return NULL;
820     if (!end) end = str + strlenW(str);
821     while (str < end)
822     {
823         if (!COMCTL32_ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
824         str++;
825     }
826     return ret;
827 }
828
829 /*************************************************************************
830  * StrCSpnW     [COMCTL32.364]
831  *
832  * See StrCSpnA.
833  */
834 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
835 {
836     if (!lpszStr || !lpszMatch) return 0;
837     return strcspnW( lpszStr, lpszMatch );
838 }
839
840 /*************************************************************************
841  * IntlStrEqWorkerA     [COMCTL32.376]
842  *
843  * Compare two strings.
844  *
845  * PARAMS
846  *  bCase    [I] Whether to compare case sensitively
847  *  lpszStr  [I] First string to compare
848  *  lpszComp [I] Second string to compare
849  *  iLen     [I] Length to compare
850  *
851  * RETURNS
852  *  TRUE  If the strings are equal.
853  *  FALSE Otherwise.
854  */
855 BOOL WINAPI IntlStrEqWorkerA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp,
856                              int iLen)
857 {
858   DWORD dwFlags = LOCALE_USE_CP_ACP;
859   int iRet;
860
861   TRACE("(%d,%s,%s,%d)\n", bCase,
862         debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
863
864   /* FIXME: This flag is undocumented and unknown by our CompareString.
865    *        We need a define for it.
866    */
867   dwFlags = 0x10000000;
868   if (!bCase) dwFlags |= NORM_IGNORECASE;
869
870   iRet = CompareStringA(GetThreadLocale(),
871                         dwFlags, lpszStr, iLen, lpszComp, iLen);
872
873   if (!iRet)
874     iRet = CompareStringA(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
875
876   return iRet == CSTR_EQUAL;
877 }
878
879 /*************************************************************************
880  * IntlStrEqWorkerW     [COMCTL32.377]
881  *
882  * See IntlStrEqWorkerA.
883  */
884 BOOL WINAPI IntlStrEqWorkerW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp,
885                              int iLen)
886 {
887   DWORD dwFlags;
888   int iRet;
889
890   TRACE("(%d,%s,%s,%d)\n", bCase,
891         debugstr_w(lpszStr),debugstr_w(lpszComp), iLen);
892
893   /* FIXME: This flag is undocumented and unknown by our CompareString.
894    *        We need a define for it.
895    */
896   dwFlags = 0x10000000;
897   if (!bCase) dwFlags |= NORM_IGNORECASE;
898
899   iRet = CompareStringW(GetThreadLocale(),
900                         dwFlags, lpszStr, iLen, lpszComp, iLen);
901
902   if (!iRet)
903     iRet = CompareStringW(2048, dwFlags, lpszStr, iLen, lpszComp, iLen);
904
905   return iRet == CSTR_EQUAL;
906 }