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