|  | /* | 
|  | * String manipulation functions | 
|  | * | 
|  | * Copyright 1998 Eric Kohl | 
|  | *           1998 Juergen Schmied <j.schmied@metronet.de> | 
|  | *           2000 Eric Kohl for CodeWeavers | 
|  | * Copyright 2002 Jon Griffiths | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.h" | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> /* atoi */ | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winuser.h" | 
|  | #include "winnls.h" | 
|  |  | 
|  | #include "comctl32.h" | 
|  |  | 
|  | #include "wine/unicode.h" | 
|  |  | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(commctrl); | 
|  |  | 
|  | /************************************************************************* | 
|  | * COMCTL32_ChrCmpHelperA | 
|  | * | 
|  | * Internal helper for ChrCmpA/COMCTL32_ChrCmpIA. | 
|  | * | 
|  | * NOTES | 
|  | *  Both this function and its Unicode counterpart are very inefficient. To | 
|  | *  fix this, CompareString must be completely implemented and optimised | 
|  | *  first. Then the core character test can be taken out of that function and | 
|  | *  placed here, so that it need never be called at all. Until then, do not | 
|  | *  attempt to optimise this code unless you are willing to test that it | 
|  | *  still performs correctly. | 
|  | */ | 
|  | static BOOL COMCTL32_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags) | 
|  | { | 
|  | char str1[3], str2[3]; | 
|  |  | 
|  | str1[0] = LOBYTE(ch1); | 
|  | if (IsDBCSLeadByte(str1[0])) | 
|  | { | 
|  | str1[1] = HIBYTE(ch1); | 
|  | str1[2] = '\0'; | 
|  | } | 
|  | else | 
|  | str1[1] = '\0'; | 
|  |  | 
|  | str2[0] = LOBYTE(ch2); | 
|  | if (IsDBCSLeadByte(str2[0])) | 
|  | { | 
|  | str2[1] = HIBYTE(ch2); | 
|  | str2[2] = '\0'; | 
|  | } | 
|  | else | 
|  | str2[1] = '\0'; | 
|  |  | 
|  | return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * COMCTL32_ChrCmpA (internal) | 
|  | * | 
|  | * Internal helper function. | 
|  | */ | 
|  | static BOOL COMCTL32_ChrCmpA(WORD ch1, WORD ch2) | 
|  | { | 
|  | return COMCTL32_ChrCmpHelperA(ch1, ch2, 0); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * COMCTL32_ChrCmpIA	(internal) | 
|  | * | 
|  | * Compare two characters, ignoring case. | 
|  | * | 
|  | * PARAMS | 
|  | *  ch1 [I] First character to compare | 
|  | *  ch2 [I] Second character to compare | 
|  | * | 
|  | * RETURNS | 
|  | *  FALSE, if the characters are equal. | 
|  | *  Non-zero otherwise. | 
|  | */ | 
|  | static BOOL COMCTL32_ChrCmpIA(WORD ch1, WORD ch2) | 
|  | { | 
|  | TRACE("(%d,%d)\n", ch1, ch2); | 
|  |  | 
|  | return COMCTL32_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * COMCTL32_ChrCmpIW | 
|  | * | 
|  | * Internal helper function. | 
|  | */ | 
|  | static inline BOOL COMCTL32_ChrCmpIW(WCHAR ch1, WCHAR ch2) | 
|  | { | 
|  | return CompareStringW(GetThreadLocale(), NORM_IGNORECASE, &ch1, 1, &ch2, 1) - 2; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * Str_GetPtrA [COMCTL32.233] | 
|  | * | 
|  | * Copies a string into a destination buffer. | 
|  | * | 
|  | * PARAMS | 
|  | *     lpSrc   [I] Source string | 
|  | *     lpDest  [O] Destination buffer | 
|  | *     nMaxLen [I] Size of buffer in characters | 
|  | * | 
|  | * RETURNS | 
|  | *     The number of characters copied. | 
|  | */ | 
|  | INT WINAPI Str_GetPtrA (LPCSTR lpSrc, LPSTR lpDest, INT nMaxLen) | 
|  | { | 
|  | INT len; | 
|  |  | 
|  | TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen); | 
|  |  | 
|  | if ((!lpDest || nMaxLen == 0) && lpSrc) | 
|  | return (strlen(lpSrc) + 1); | 
|  |  | 
|  | if (nMaxLen == 0) | 
|  | return 0; | 
|  |  | 
|  | if (lpSrc == NULL) { | 
|  | lpDest[0] = '\0'; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | len = strlen(lpSrc) + 1; | 
|  | if (len >= nMaxLen) | 
|  | len = nMaxLen; | 
|  |  | 
|  | RtlMoveMemory (lpDest, lpSrc, len - 1); | 
|  | lpDest[len - 1] = '\0'; | 
|  |  | 
|  | return len; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * Str_SetPtrA [COMCTL32.234] | 
|  | * | 
|  | * Makes a copy of a string, allocating memory if necessary. | 
|  | * | 
|  | * PARAMS | 
|  | *     lppDest [O] Pointer to destination string | 
|  | *     lpSrc   [I] Source string | 
|  | * | 
|  | * RETURNS | 
|  | *     Success: TRUE | 
|  | *     Failure: FALSE | 
|  | * | 
|  | * NOTES | 
|  | *     Set lpSrc to NULL to free the memory allocated by a previous call | 
|  | *     to this function. | 
|  | */ | 
|  | BOOL WINAPI Str_SetPtrA (LPSTR *lppDest, LPCSTR lpSrc) | 
|  | { | 
|  | TRACE("(%p %p)\n", lppDest, lpSrc); | 
|  |  | 
|  | if (lpSrc) { | 
|  | LPSTR ptr = ReAlloc (*lppDest, strlen (lpSrc) + 1); | 
|  | if (!ptr) | 
|  | return FALSE; | 
|  | strcpy (ptr, lpSrc); | 
|  | *lppDest = ptr; | 
|  | } | 
|  | else { | 
|  | Free (*lppDest); | 
|  | *lppDest = NULL; | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * Str_GetPtrW [COMCTL32.235] | 
|  | * | 
|  | * See Str_GetPtrA. | 
|  | */ | 
|  | INT WINAPI Str_GetPtrW (LPCWSTR lpSrc, LPWSTR lpDest, INT nMaxLen) | 
|  | { | 
|  | INT len; | 
|  |  | 
|  | TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen); | 
|  |  | 
|  | if (!lpDest && lpSrc) | 
|  | return strlenW (lpSrc); | 
|  |  | 
|  | if (nMaxLen == 0) | 
|  | return 0; | 
|  |  | 
|  | if (lpSrc == NULL) { | 
|  | lpDest[0] = '\0'; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | len = strlenW (lpSrc); | 
|  | if (len >= nMaxLen) | 
|  | len = nMaxLen - 1; | 
|  |  | 
|  | RtlMoveMemory (lpDest, lpSrc, len*sizeof(WCHAR)); | 
|  | lpDest[len] = '\0'; | 
|  |  | 
|  | return len; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * Str_SetPtrW [COMCTL32.236] | 
|  | * | 
|  | * See Str_SetPtrA. | 
|  | */ | 
|  | BOOL WINAPI Str_SetPtrW (LPWSTR *lppDest, LPCWSTR lpSrc) | 
|  | { | 
|  | TRACE("(%p %p)\n", lppDest, lpSrc); | 
|  |  | 
|  | if (lpSrc) { | 
|  | INT len = strlenW (lpSrc) + 1; | 
|  | LPWSTR ptr = ReAlloc (*lppDest, len * sizeof(WCHAR)); | 
|  | if (!ptr) | 
|  | return FALSE; | 
|  | strcpyW (ptr, lpSrc); | 
|  | *lppDest = ptr; | 
|  | } | 
|  | else { | 
|  | Free (*lppDest); | 
|  | *lppDest = NULL; | 
|  | } | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrChrA [COMCTL32.350] | 
|  | * | 
|  | * Find a given character in a string. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr [I] String to search in. | 
|  | *  ch      [I] Character to search for. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if | 
|  | *           not found. | 
|  | *  Failure: NULL, if any arguments are invalid. | 
|  | */ | 
|  | LPSTR WINAPI StrChrA(LPCSTR lpszStr, WORD ch) | 
|  | { | 
|  | TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch); | 
|  |  | 
|  | if (lpszStr) | 
|  | { | 
|  | while (*lpszStr) | 
|  | { | 
|  | if (!COMCTL32_ChrCmpA(*lpszStr, ch)) | 
|  | return (LPSTR)lpszStr; | 
|  | lpszStr = CharNextA(lpszStr); | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrCmpNIA [COMCTL32.353] | 
|  | * | 
|  | * Compare two strings, up to a maximum length, ignoring case. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr  [I] First string to compare | 
|  | *  lpszComp [I] Second string to compare | 
|  | *  iLen     [I] Maximum number of chars to compare. | 
|  | * | 
|  | * RETURNS | 
|  | *  An integer less than, equal to or greater than 0, indicating that | 
|  | *  lpszStr is less than, the same, or greater than lpszComp. | 
|  | */ | 
|  | INT WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen) | 
|  | { | 
|  | INT iRet; | 
|  |  | 
|  | TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen); | 
|  |  | 
|  | iRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen); | 
|  | return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrCmpNIW	[COMCTL32.361] | 
|  | * | 
|  | * See StrCmpNIA. | 
|  | */ | 
|  | INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen) | 
|  | { | 
|  | INT iRet; | 
|  |  | 
|  | TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen); | 
|  |  | 
|  | iRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, lpszStr, iLen, lpszComp, iLen); | 
|  | return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * COMCTL32_StrStrHelperA | 
|  | * | 
|  | * Internal implementation of StrStrA/StrStrIA | 
|  | */ | 
|  | static LPSTR COMCTL32_StrStrHelperA(LPCSTR lpszStr, LPCSTR lpszSearch, | 
|  | INT (WINAPI *pStrCmpFn)(LPCSTR,LPCSTR,INT)) | 
|  | { | 
|  | size_t iLen; | 
|  |  | 
|  | if (!lpszStr || !lpszSearch || !*lpszSearch) | 
|  | return NULL; | 
|  |  | 
|  | iLen = strlen(lpszSearch); | 
|  |  | 
|  | while (*lpszStr) | 
|  | { | 
|  | if (!pStrCmpFn(lpszStr, lpszSearch, iLen)) | 
|  | return (LPSTR)lpszStr; | 
|  | lpszStr = CharNextA(lpszStr); | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrStrIA [COMCTL32.355] | 
|  | * | 
|  | * Find a substring within a string, ignoring case. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr    [I] String to search in | 
|  | *  lpszSearch [I] String to look for | 
|  | * | 
|  | * RETURNS | 
|  | *  The start of lpszSearch within lpszStr, or NULL if not found. | 
|  | */ | 
|  | LPSTR WINAPI StrStrIA(LPCSTR lpszStr, LPCSTR lpszSearch) | 
|  | { | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch)); | 
|  |  | 
|  | return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, StrCmpNIA); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrToIntA [COMCTL32.357] | 
|  | * | 
|  | * Read a signed integer from a string. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr [I] String to read integer from | 
|  | * | 
|  | * RETURNS | 
|  | *   The signed integer value represented by the string, or 0 if no integer is | 
|  | *   present. | 
|  | */ | 
|  | INT WINAPI StrToIntA (LPCSTR lpszStr) | 
|  | { | 
|  | return atoi(lpszStr); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrStrIW [COMCTL32.363] | 
|  | * | 
|  | * See StrStrIA. | 
|  | */ | 
|  | LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch) | 
|  | { | 
|  | int iLen; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch)); | 
|  |  | 
|  | if (!lpszStr || !lpszSearch || !*lpszSearch) | 
|  | return NULL; | 
|  |  | 
|  | iLen = strlenW(lpszSearch); | 
|  |  | 
|  | while (*lpszStr) | 
|  | { | 
|  | if (!StrCmpNIW(lpszStr, lpszSearch, iLen)) | 
|  | return (LPWSTR)lpszStr; | 
|  | lpszStr++; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrToIntW [COMCTL32.365] | 
|  | * | 
|  | * See StrToIntA. | 
|  | */ | 
|  | INT WINAPI StrToIntW (LPCWSTR lpString) | 
|  | { | 
|  | return atoiW(lpString); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * COMCTL32_StrSpnHelperA (internal) | 
|  | * | 
|  | * Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA | 
|  | */ | 
|  | static int COMCTL32_StrSpnHelperA(LPCSTR lpszStr, LPCSTR lpszMatch, | 
|  | LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD), | 
|  | BOOL bInvert) | 
|  | { | 
|  | LPCSTR lpszRead = lpszStr; | 
|  | if (lpszStr && *lpszStr && lpszMatch) | 
|  | { | 
|  | while (*lpszRead) | 
|  | { | 
|  | LPCSTR lpszTest = pStrChrFn(lpszMatch, *lpszRead); | 
|  |  | 
|  | if (!bInvert && !lpszTest) | 
|  | break; | 
|  | if (bInvert && lpszTest) | 
|  | break; | 
|  | lpszRead = CharNextA(lpszRead); | 
|  | }; | 
|  | } | 
|  | return lpszRead - lpszStr; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrCSpnA [COMCTL32.356] | 
|  | * | 
|  | * Find the length of the start of a string that does not contain certain | 
|  | * characters. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr   [I] String to search | 
|  | *  lpszMatch [I] Characters that cannot be in the substring | 
|  | * | 
|  | * RETURNS | 
|  | *  The length of the part of lpszStr containing only chars not in lpszMatch, | 
|  | *  or 0 if any parameter is invalid. | 
|  | */ | 
|  | int WINAPI StrCSpnA(LPCSTR lpszStr, LPCSTR lpszMatch) | 
|  | { | 
|  | TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch)); | 
|  |  | 
|  | return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrChrW [COMCTL32.358] | 
|  | * | 
|  | * See StrChrA. | 
|  | */ | 
|  | LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch) | 
|  | { | 
|  | LPWSTR lpszRet = NULL; | 
|  |  | 
|  | TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch); | 
|  |  | 
|  | if (lpszStr) | 
|  | lpszRet = strchrW(lpszStr, ch); | 
|  | return lpszRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrCmpNA [COMCTL32.352] | 
|  | * | 
|  | * Compare two strings, up to a maximum length. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr  [I] First string to compare | 
|  | *  lpszComp [I] Second string to compare | 
|  | *  iLen     [I] Maximum number of chars to compare. | 
|  | * | 
|  | * RETURNS | 
|  | *  An integer less than, equal to or greater than 0, indicating that | 
|  | *  lpszStr is less than, the same, or greater than lpszComp. | 
|  | */ | 
|  | INT WINAPI StrCmpNA(LPCSTR lpszStr, LPCSTR lpszComp, INT iLen) | 
|  | { | 
|  | INT iRet; | 
|  |  | 
|  | TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen); | 
|  |  | 
|  | iRet = CompareStringA(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen); | 
|  | return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrCmpNW [COMCTL32.360] | 
|  | * | 
|  | * See StrCmpNA. | 
|  | */ | 
|  | INT WINAPI StrCmpNW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen) | 
|  | { | 
|  | INT iRet; | 
|  |  | 
|  | TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszComp), iLen); | 
|  |  | 
|  | iRet = CompareStringW(GetThreadLocale(), 0, lpszStr, iLen, lpszComp, iLen); | 
|  | return iRet == CSTR_LESS_THAN ? -1 : iRet == CSTR_GREATER_THAN ? 1 : 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrRChrA [COMCTL32.351] | 
|  | * | 
|  | * Find the last occurrence of a character in string. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr [I] String to search in | 
|  | *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr | 
|  | *  ch      [I] Character to search for. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd, | 
|  | *           or NULL if not found. | 
|  | *  Failure: NULL, if any arguments are invalid. | 
|  | */ | 
|  | LPSTR WINAPI StrRChrA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch) | 
|  | { | 
|  | LPCSTR lpszRet = NULL; | 
|  |  | 
|  | TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch); | 
|  |  | 
|  | if (lpszStr) | 
|  | { | 
|  | WORD ch2; | 
|  |  | 
|  | if (!lpszEnd) | 
|  | lpszEnd = lpszStr + lstrlenA(lpszStr); | 
|  |  | 
|  | while (*lpszStr && lpszStr <= lpszEnd) | 
|  | { | 
|  | ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr; | 
|  |  | 
|  | if (!COMCTL32_ChrCmpA(ch, ch2)) | 
|  | lpszRet = lpszStr; | 
|  | lpszStr = CharNextA(lpszStr); | 
|  | } | 
|  | } | 
|  | return (LPSTR)lpszRet; | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrRChrW [COMCTL32.359] | 
|  | * | 
|  | * See StrRChrA. | 
|  | */ | 
|  | LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch) | 
|  | { | 
|  | WCHAR *ret = NULL; | 
|  |  | 
|  | if (!str) return NULL; | 
|  | if (!end) end = str + strlenW(str); | 
|  | while (str < end) | 
|  | { | 
|  | if (*str == ch) ret = (WCHAR *)str; | 
|  | str++; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrStrA [COMCTL32.354] | 
|  | * | 
|  | * Find a substring within a string. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr    [I] String to search in | 
|  | *  lpszSearch [I] String to look for | 
|  | * | 
|  | * RETURNS | 
|  | *  The start of lpszSearch within lpszStr, or NULL if not found. | 
|  | */ | 
|  | LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch) | 
|  | { | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch)); | 
|  |  | 
|  | return COMCTL32_StrStrHelperA(lpszStr, lpszSearch, StrCmpNA); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrStrW [COMCTL32.362] | 
|  | * | 
|  | * See StrStrA. | 
|  | */ | 
|  | LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch) | 
|  | { | 
|  | if (!lpszStr || !lpszSearch) return NULL; | 
|  | return strstrW( lpszStr, lpszSearch ); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrChrIA	[COMCTL32.366] | 
|  | * | 
|  | * Find a given character in a string, ignoring case. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr [I] String to search in. | 
|  | *  ch      [I] Character to search for. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to the first occurrence of ch in lpszStr, or NULL if | 
|  | *           not found. | 
|  | *  Failure: NULL, if any arguments are invalid. | 
|  | */ | 
|  | LPSTR WINAPI StrChrIA(LPCSTR lpszStr, WORD ch) | 
|  | { | 
|  | TRACE("(%s,%i)\n", debugstr_a(lpszStr), ch); | 
|  |  | 
|  | if (lpszStr) | 
|  | { | 
|  | while (*lpszStr) | 
|  | { | 
|  | if (!COMCTL32_ChrCmpIA(*lpszStr, ch)) | 
|  | return (LPSTR)lpszStr; | 
|  | lpszStr = CharNextA(lpszStr); | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrChrIW	[COMCTL32.367] | 
|  | * | 
|  | * See StrChrA. | 
|  | */ | 
|  | LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch) | 
|  | { | 
|  | TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch); | 
|  |  | 
|  | if (lpszStr) | 
|  | { | 
|  | ch = toupperW(ch); | 
|  | while (*lpszStr) | 
|  | { | 
|  | if (toupperW(*lpszStr) == ch) | 
|  | return (LPWSTR)lpszStr; | 
|  | lpszStr++; | 
|  | } | 
|  | lpszStr = NULL; | 
|  | } | 
|  | return (LPWSTR)lpszStr; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrRStrIA	[COMCTL32.372] | 
|  | * | 
|  | * Find the last occurrence of a substring within a string. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr    [I] String to search in | 
|  | *  lpszEnd    [I] End of lpszStr | 
|  | *  lpszSearch [I] String to look for | 
|  | * | 
|  | * RETURNS | 
|  | *  The last occurrence lpszSearch within lpszStr, or NULL if not found. | 
|  | */ | 
|  | LPSTR WINAPI StrRStrIA(LPCSTR lpszStr, LPCSTR lpszEnd, LPCSTR lpszSearch) | 
|  | { | 
|  | LPSTR lpszRet = NULL; | 
|  | WORD ch1, ch2; | 
|  | INT iLen; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszSearch)); | 
|  |  | 
|  | if (!lpszStr || !lpszSearch || !*lpszSearch) | 
|  | return NULL; | 
|  |  | 
|  | if (!lpszEnd) | 
|  | lpszEnd = lpszStr + lstrlenA(lpszStr); | 
|  |  | 
|  | if (IsDBCSLeadByte(*lpszSearch)) | 
|  | ch1 = *lpszSearch << 8 | lpszSearch[1]; | 
|  | else | 
|  | ch1 = *lpszSearch; | 
|  | iLen = lstrlenA(lpszSearch); | 
|  |  | 
|  | while (lpszStr <= lpszEnd  && *lpszStr) | 
|  | { | 
|  | ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr; | 
|  | if (!COMCTL32_ChrCmpIA(ch1, ch2)) | 
|  | { | 
|  | if (!StrCmpNIA(lpszStr, lpszSearch, iLen)) | 
|  | lpszRet = (LPSTR)lpszStr; | 
|  | } | 
|  | lpszStr = CharNextA(lpszStr); | 
|  | } | 
|  | return lpszRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrRStrIW	[COMCTL32.373] | 
|  | * | 
|  | * See StrRStrIA. | 
|  | */ | 
|  | LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch) | 
|  | { | 
|  | LPWSTR lpszRet = NULL; | 
|  | INT iLen; | 
|  |  | 
|  | TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch)); | 
|  |  | 
|  | if (!lpszStr || !lpszSearch || !*lpszSearch) | 
|  | return NULL; | 
|  |  | 
|  | if (!lpszEnd) | 
|  | lpszEnd = lpszStr + strlenW(lpszStr); | 
|  |  | 
|  | iLen = strlenW(lpszSearch); | 
|  |  | 
|  | while (lpszStr <= lpszEnd  && *lpszStr) | 
|  | { | 
|  | if (!COMCTL32_ChrCmpIW(*lpszSearch, *lpszStr)) | 
|  | { | 
|  | if (!StrCmpNIW(lpszStr, lpszSearch, iLen)) | 
|  | lpszRet = (LPWSTR)lpszStr; | 
|  | } | 
|  | lpszStr++; | 
|  | } | 
|  | return lpszRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrCSpnIA	[COMCTL32.374] | 
|  | * | 
|  | * Find the length of the start of a string that does not contain certain | 
|  | * characters, ignoring case. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr   [I] String to search | 
|  | *  lpszMatch [I] Characters that cannot be in the substring | 
|  | * | 
|  | * RETURNS | 
|  | *  The length of the part of lpszStr containing only chars not in lpszMatch, | 
|  | *  or 0 if any parameter is invalid. | 
|  | */ | 
|  | int WINAPI StrCSpnIA(LPCSTR lpszStr, LPCSTR lpszMatch) | 
|  | { | 
|  | TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch)); | 
|  |  | 
|  | return COMCTL32_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrCSpnIW	[COMCTL32.375] | 
|  | * | 
|  | * See StrCSpnIA. | 
|  | */ | 
|  | int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch) | 
|  | { | 
|  | LPCWSTR lpszRead = lpszStr; | 
|  |  | 
|  | TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch)); | 
|  |  | 
|  | if (lpszStr && *lpszStr && lpszMatch) | 
|  | { | 
|  | while (*lpszRead) | 
|  | { | 
|  | if (StrChrIW(lpszMatch, *lpszRead)) break; | 
|  | lpszRead++; | 
|  | } | 
|  | } | 
|  | return lpszRead - lpszStr; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrRChrIA	[COMCTL32.368] | 
|  | * | 
|  | * Find the last occurrence of a character in string, ignoring case. | 
|  | * | 
|  | * PARAMS | 
|  | *  lpszStr [I] String to search in | 
|  | *  lpszEnd [I] Place to end search, or NULL to search until the end of lpszStr | 
|  | *  ch      [I] Character to search for. | 
|  | * | 
|  | * RETURNS | 
|  | *  Success: A pointer to the last occurrence of ch in lpszStr before lpszEnd, | 
|  | *           or NULL if not found. | 
|  | *  Failure: NULL, if any arguments are invalid. | 
|  | */ | 
|  | LPSTR WINAPI StrRChrIA(LPCSTR lpszStr, LPCSTR lpszEnd, WORD ch) | 
|  | { | 
|  | LPCSTR lpszRet = NULL; | 
|  |  | 
|  | TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch); | 
|  |  | 
|  | if (lpszStr) | 
|  | { | 
|  | WORD ch2; | 
|  |  | 
|  | if (!lpszEnd) | 
|  | lpszEnd = lpszStr + lstrlenA(lpszStr); | 
|  |  | 
|  | while (*lpszStr && lpszStr <= lpszEnd) | 
|  | { | 
|  | ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr; | 
|  |  | 
|  | if (ch == ch2) | 
|  | lpszRet = lpszStr; | 
|  | lpszStr = CharNextA(lpszStr); | 
|  | } | 
|  | } | 
|  | return (LPSTR)lpszRet; | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | * StrRChrIW	[COMCTL32.369] | 
|  | * | 
|  | * See StrRChrIA. | 
|  | */ | 
|  | LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch) | 
|  | { | 
|  | WCHAR *ret = NULL; | 
|  |  | 
|  | if (!str) return NULL; | 
|  | if (!end) end = str + strlenW(str); | 
|  | while (str < end) | 
|  | { | 
|  | if (!COMCTL32_ChrCmpIW(*str, ch)) ret = (WCHAR *)str; | 
|  | str++; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * StrCSpnW	[COMCTL32.364] | 
|  | * | 
|  | * See StrCSpnA. | 
|  | */ | 
|  | int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch) | 
|  | { | 
|  | if (!lpszStr || !lpszMatch) return 0; | 
|  | return strcspnW( lpszStr, lpszMatch ); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * IntlStrEqWorkerA	[COMCTL32.376] | 
|  | * | 
|  | * Compare two strings. | 
|  | * | 
|  | * PARAMS | 
|  | *  bCase    [I] Whether to compare case sensitively | 
|  | *  lpszStr  [I] First string to compare | 
|  | *  lpszComp [I] Second string to compare | 
|  | *  iLen     [I] Length to compare | 
|  | * | 
|  | * RETURNS | 
|  | *  TRUE  If the strings are equal. | 
|  | *  FALSE Otherwise. | 
|  | */ | 
|  | BOOL WINAPI IntlStrEqWorkerA(BOOL bCase, LPCSTR lpszStr, LPCSTR lpszComp, | 
|  | int iLen) | 
|  | { | 
|  | DWORD dwFlags = LOCALE_USE_CP_ACP; | 
|  | int iRet; | 
|  |  | 
|  | TRACE("(%d,%s,%s,%d)\n", bCase, | 
|  | debugstr_a(lpszStr), debugstr_a(lpszComp), iLen); | 
|  |  | 
|  | /* FIXME: These flags are undocumented and unknown by our CompareString. | 
|  | *        We need defines for them. | 
|  | */ | 
|  | dwFlags |= bCase ? 0x10000000 : 0x10000001; | 
|  |  | 
|  | iRet = CompareStringA(GetThreadLocale(), | 
|  | dwFlags, lpszStr, iLen, lpszComp, iLen); | 
|  |  | 
|  | if (!iRet) | 
|  | iRet = CompareStringA(2048, dwFlags, lpszStr, iLen, lpszComp, iLen); | 
|  |  | 
|  | return iRet == 2 ? TRUE : FALSE; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * IntlStrEqWorkerW	[COMCTL32.377] | 
|  | * | 
|  | * See IntlStrEqWorkerA. | 
|  | */ | 
|  | BOOL WINAPI IntlStrEqWorkerW(BOOL bCase, LPCWSTR lpszStr, LPCWSTR lpszComp, | 
|  | int iLen) | 
|  | { | 
|  | DWORD dwFlags; | 
|  | int iRet; | 
|  |  | 
|  | TRACE("(%d,%s,%s,%d)\n", bCase, | 
|  | debugstr_w(lpszStr),debugstr_w(lpszComp), iLen); | 
|  |  | 
|  | /* FIXME: These flags are undocumented and unknown by our CompareString. | 
|  | *        We need defines for them. | 
|  | */ | 
|  | dwFlags = bCase ? 0x10000000 : 0x10000001; | 
|  |  | 
|  | iRet = CompareStringW(GetThreadLocale(), | 
|  | dwFlags, lpszStr, iLen, lpszComp, iLen); | 
|  |  | 
|  | if (!iRet) | 
|  | iRet = CompareStringW(2048, dwFlags, lpszStr, iLen, lpszComp, iLen); | 
|  |  | 
|  | return iRet == 2 ? TRUE : FALSE; | 
|  | } |