set svn:eol-style to native
[reactos.git] / reactos / lib / comctl32 / string.c
index b9aa285..7e7afe0 100644 (file)
-/*\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;
+}