/* * 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, strncmpiW); } /************************************************************************** * 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, strncmpW); } /************************************************************************* * 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; }