2307 lines
57 KiB
C
2307 lines
57 KiB
C
/*
|
|
* Shlwapi string functions
|
|
*
|
|
* Copyright 1998 Juergen Schmied
|
|
* 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
|
|
*/
|
|
|
|
#define COM_NO_WINDOWS_H
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
#include "winerror.h"
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
#include "winreg.h"
|
|
#define NO_SHLWAPI_STREAM
|
|
#include "shlwapi.h"
|
|
#include "shlobj.h"
|
|
#include "wine/unicode.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(shell);
|
|
|
|
static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest);
|
|
static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest);
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_ChrCmpHelperA
|
|
*
|
|
* Internal helper for SHLWAPI_ChrCmpA/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 WINAPI SHLWAPI_ChrCmpHelperA(WORD ch1, WORD ch2, DWORD dwFlags)
|
|
{
|
|
char str1[3], str2[3];
|
|
|
|
str1[0] = LOBYTE(ch1);
|
|
if (IsDBCSLeadByte(ch1))
|
|
{
|
|
str1[1] = HIBYTE(ch1);
|
|
str1[2] = '\0';
|
|
}
|
|
else
|
|
str1[1] = '\0';
|
|
|
|
str2[0] = LOBYTE(ch2);
|
|
if (IsDBCSLeadByte(ch2))
|
|
{
|
|
str2[1] = HIBYTE(ch2);
|
|
str2[2] = '\0';
|
|
}
|
|
else
|
|
str2[1] = '\0';
|
|
|
|
return CompareStringA(GetThreadLocale(), dwFlags, str1, -1, str2, -1) - 2;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_ChrCmpHelperW
|
|
*
|
|
* Internal helper for SHLWAPI_ChrCmpW/ChrCmpIW.
|
|
*/
|
|
static BOOL WINAPI SHLWAPI_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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_ChrCmpA
|
|
*
|
|
* Internal helper function.
|
|
*/
|
|
static BOOL WINAPI SHLWAPI_ChrCmpA(WORD ch1, WORD ch2)
|
|
{
|
|
return SHLWAPI_ChrCmpHelperA(ch1, ch2, 0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* ChrCmpIA (SHLWAPI.385)
|
|
*
|
|
* 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.
|
|
*/
|
|
BOOL WINAPI ChrCmpIA(WORD ch1, WORD ch2)
|
|
{
|
|
TRACE("(%d,%d)\n", ch1, ch2);
|
|
|
|
return SHLWAPI_ChrCmpHelperA(ch1, ch2, NORM_IGNORECASE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_ChrCmpW
|
|
*
|
|
* Internal helper function.
|
|
*/
|
|
static BOOL WINAPI SHLWAPI_ChrCmpW(WCHAR ch1, WCHAR ch2)
|
|
{
|
|
return SHLWAPI_ChrCmpHelperW(ch1, ch2, 0);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* ChrCmpIW [SHLWAPI.386]
|
|
*
|
|
* See ChrCmpIA.
|
|
*/
|
|
BOOL WINAPI ChrCmpIW(WCHAR ch1, WCHAR ch2)
|
|
{
|
|
return SHLWAPI_ChrCmpHelperW(ch1, ch2, NORM_IGNORECASE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrChrA [SHLWAPI.@]
|
|
*
|
|
* 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 (!SHLWAPI_ChrCmpA(*lpszStr, ch))
|
|
return (LPSTR)lpszStr;
|
|
lpszStr = CharNextA(lpszStr);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrChrW [SHLWAPI.@]
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrChrIA [SHLWAPI.@]
|
|
*
|
|
* 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 (!ChrCmpIA(*lpszStr, ch))
|
|
return (LPSTR)lpszStr;
|
|
lpszStr = CharNextA(lpszStr);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrChrIW [SHLWAPI.@]
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCmpIW [SHLWAPI.@]
|
|
*
|
|
* Compare two strings, ignoring case.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] First string to compare
|
|
* lpszComp [I] Second string 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 StrCmpIW(LPCWSTR lpszStr, LPCWSTR lpszComp)
|
|
{
|
|
INT iRet;
|
|
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszStr),debugstr_w(lpszComp));
|
|
|
|
iRet = strcmpiW(lpszStr, lpszComp);
|
|
return iRet < 0 ? -1 : iRet ? 1 : 0;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_StrCmpNHelperA
|
|
*
|
|
* Internal helper for StrCmpNA/StrCmpNIA.
|
|
*/
|
|
static INT WINAPI SHLWAPI_StrCmpNHelperA(LPCSTR lpszStr, LPCSTR lpszComp,
|
|
INT iLen,
|
|
BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
|
|
{
|
|
if (!lpszStr)
|
|
{
|
|
if (!lpszComp)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
else if (!lpszComp)
|
|
return -1;
|
|
|
|
while (iLen-- > 0)
|
|
{
|
|
int iDiff;
|
|
WORD ch1, ch2;
|
|
|
|
ch1 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
|
|
ch2 = IsDBCSLeadByte(*lpszComp)? *lpszComp << 8 | lpszComp[1] : *lpszComp;
|
|
|
|
if ((iDiff = pChrCmpFn(ch1, ch2)) < 0)
|
|
return -1;
|
|
else if (iDiff > 0)
|
|
return 1;
|
|
else if (!*lpszStr && !*lpszComp)
|
|
return 0;
|
|
|
|
lpszStr = CharNextA(lpszStr);
|
|
lpszComp = CharNextA(lpszComp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCmpNA [SHLWAPI.@]
|
|
*
|
|
* 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)
|
|
{
|
|
TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
|
|
|
|
return SHLWAPI_StrCmpNHelperA(lpszStr, lpszComp, iLen, SHLWAPI_ChrCmpA);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCmpNW [SHLWAPI.@]
|
|
*
|
|
* 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 = strncmpW(lpszStr, lpszComp, iLen);
|
|
return iRet < 0 ? -1 : iRet ? 1 : 0;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCmpNIA [SHLWAPI.@]
|
|
*
|
|
* 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.
|
|
*
|
|
* NOTES
|
|
* The Win32 version of this function is _completely_ broken for cases
|
|
* where iLen is greater than the length of lpszComp. Examples:
|
|
*
|
|
*| StrCmpNIA("foo.gif", "foo", 5) is -1 under Win32; Should return 1.
|
|
*| StrCmpNIA("\", "\\", 3) is 0 under Win32; Should return -1.
|
|
*| StrCmpNIA("\", "\..\foo\", 3) is 1 under Win32; Should return -1.
|
|
*
|
|
* This implementation behaves correctly, since it is unlikely any
|
|
* applications actually rely on this function being broken.
|
|
*/
|
|
int WINAPI StrCmpNIA(LPCSTR lpszStr, LPCSTR lpszComp, int iLen)
|
|
{
|
|
TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszComp), iLen);
|
|
|
|
return SHLWAPI_StrCmpNHelperA(lpszStr, lpszComp, iLen, ChrCmpIA);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCmpNIW [SHLWAPI.@]
|
|
*
|
|
* 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 = strncmpiW(lpszStr, lpszComp, iLen);
|
|
return iRet < 0 ? -1 : iRet ? 1 : 0;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCmpW [SHLWAPI.@]
|
|
*
|
|
* Compare two strings.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] First string to compare
|
|
* lpszComp [I] Second string 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 StrCmpW(LPCWSTR lpszStr, LPCWSTR lpszComp)
|
|
{
|
|
INT iRet;
|
|
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
|
|
|
|
iRet = strcmpW(lpszStr, lpszComp);
|
|
return iRet < 0 ? -1 : iRet ? 1 : 0;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCatW [SHLWAPI.@]
|
|
*
|
|
* Concatanate two strings.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [O] Initial string
|
|
* lpszSrc [I] String to concatanate
|
|
*
|
|
* RETURNS
|
|
* lpszStr.
|
|
*/
|
|
LPWSTR WINAPI StrCatW(LPWSTR lpszStr, LPCWSTR lpszSrc)
|
|
{
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSrc));
|
|
|
|
strcatW(lpszStr, lpszSrc);
|
|
return lpszStr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCpyW [SHLWAPI.@]
|
|
*
|
|
* Copy a string to another string.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [O] Destination string
|
|
* lpszSrc [I] Source string
|
|
*
|
|
* RETURNS
|
|
* lpszStr.
|
|
*/
|
|
LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
|
|
{
|
|
TRACE("(%p,%s)\n", lpszStr, debugstr_w(lpszSrc));
|
|
|
|
strcpyW(lpszStr, lpszSrc);
|
|
return lpszStr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCpyNW [SHLWAPI.@]
|
|
*
|
|
* Copy a string to another string, up to a maximum number of characters.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [O] Destination string
|
|
* lpszSrc [I] Source string
|
|
* iLen [I] Maximum number of chars to copy
|
|
*
|
|
* RETURNS
|
|
* lpszStr.
|
|
*/
|
|
LPWSTR WINAPI StrCpyNW(LPWSTR lpszStr, LPCWSTR lpszSrc, int iLen)
|
|
{
|
|
TRACE("(%p,%s,%i)\n", lpszStr, debugstr_w(lpszSrc), iLen);
|
|
|
|
lstrcpynW(lpszStr, lpszSrc, iLen);
|
|
return lpszStr;
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_StrStrHelperA
|
|
*
|
|
* Internal implementation of StrStrA/StrStrIA
|
|
*/
|
|
static LPSTR WINAPI SHLWAPI_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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_StrStrHelperW
|
|
*
|
|
* Internal implementation of StrStrW/StrStrIW
|
|
*/
|
|
static LPWSTR WINAPI SHLWAPI_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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrStrA [SHLWAPI.@]
|
|
*
|
|
* 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 SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncmp);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrStrW [SHLWAPI.@]
|
|
*
|
|
* See StrStrA.
|
|
*/
|
|
LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
|
|
{
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
|
|
|
|
return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpW);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrRStrIA [SHLWAPI.@]
|
|
*
|
|
* Find the last occurence 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 occurence 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 (!ChrCmpIA(ch1, ch2))
|
|
{
|
|
if (!StrCmpNIA(lpszStr, lpszSearch, iLen))
|
|
lpszRet = (LPSTR)lpszStr;
|
|
}
|
|
lpszStr = CharNextA(lpszStr);
|
|
}
|
|
return lpszRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrRStrIW [SHLWAPI.@]
|
|
*
|
|
* 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 (!ChrCmpIA(*lpszSearch, *lpszStr))
|
|
{
|
|
if (!StrCmpNIW(lpszStr, lpszSearch, iLen))
|
|
lpszRet = (LPWSTR)lpszStr;
|
|
}
|
|
lpszStr = CharNextW(lpszStr);
|
|
}
|
|
return lpszRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrStrIA [SHLWAPI.@]
|
|
*
|
|
* 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 SHLWAPI_StrStrHelperA(lpszStr, lpszSearch, strncasecmp);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrStrIW [SHLWAPI.@]
|
|
*
|
|
* See StrStrIA.
|
|
*/
|
|
LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
|
|
{
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszSearch));
|
|
|
|
return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpiW);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrToIntA [SHLWAPI.@]
|
|
*
|
|
* Read an integer from a string.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] String to read integer from
|
|
*
|
|
* RETURNS
|
|
* The integer value represented by the string, or 0 if no integer is
|
|
* present.
|
|
*
|
|
* NOTES
|
|
* No leading space is allowed before the number, although a leading '-' is.
|
|
*/
|
|
int WINAPI StrToIntA(LPCSTR lpszStr)
|
|
{
|
|
int iRet = 0;
|
|
|
|
TRACE("(%s)\n", debugstr_a(lpszStr));
|
|
|
|
if (!lpszStr)
|
|
{
|
|
WARN("Invalid lpszStr would crash under Win32!\n");
|
|
return 0;
|
|
}
|
|
|
|
if (*lpszStr == '-' || isdigit(*lpszStr))
|
|
StrToIntExA(lpszStr, 0, &iRet);
|
|
return iRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrToIntW [SHLWAPI.@]
|
|
*
|
|
* See StrToIntA.
|
|
*/
|
|
int WINAPI StrToIntW(LPCWSTR lpszStr)
|
|
{
|
|
int iRet = 0;
|
|
|
|
TRACE("(%s)\n", debugstr_w(lpszStr));
|
|
|
|
if (!lpszStr)
|
|
{
|
|
WARN("Invalid lpszStr would crash under Win32!\n");
|
|
return 0;
|
|
}
|
|
|
|
if (*lpszStr == '-' || isdigitW(*lpszStr))
|
|
StrToIntExW(lpszStr, 0, &iRet);
|
|
return iRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrToIntExA [SHLWAPI.@]
|
|
*
|
|
* Read an integer from a string.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] String to read integer from
|
|
* dwFlags [I] Flags controlling the conversion
|
|
* lpiRet [O] Destination for read integer.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE. lpiRet contains the integer value represented by the string.
|
|
* Failure: FALSE, if the string is invalid, or no number is present.
|
|
*
|
|
* NOTES
|
|
* Leading whitespace, '-' and '+' are allowed before the number. If
|
|
* dwFlags includes STIF_SUPPORT_HEX, hexidecimal numbers are allowed, if
|
|
* preceeded by '0x'. If this flag is not set, or there is no '0x' prefix,
|
|
* the string is treated as a decimal string. A leading '-' is ignored for
|
|
* hexidecimal numbers.
|
|
*/
|
|
BOOL WINAPI StrToIntExA(LPCSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
|
|
{
|
|
BOOL bNegative = FALSE;
|
|
int iRet = 0;
|
|
|
|
TRACE("(%s,%08lX,%p)\n", debugstr_a(lpszStr), dwFlags, lpiRet);
|
|
|
|
if (!lpszStr || !lpiRet)
|
|
{
|
|
WARN("Invalid parameter would crash under Win32!\n");
|
|
return FALSE;
|
|
}
|
|
if (dwFlags > STIF_SUPPORT_HEX)
|
|
{
|
|
WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
|
|
}
|
|
|
|
/* Skip leading space, '+', '-' */
|
|
while (isspace(*lpszStr))
|
|
lpszStr = CharNextA(lpszStr);
|
|
|
|
if (*lpszStr == '-')
|
|
{
|
|
bNegative = TRUE;
|
|
lpszStr++;
|
|
}
|
|
else if (*lpszStr == '+')
|
|
lpszStr++;
|
|
|
|
if (dwFlags & STIF_SUPPORT_HEX &&
|
|
*lpszStr == '0' && tolower(lpszStr[1]) == 'x')
|
|
{
|
|
/* Read hex number */
|
|
lpszStr += 2;
|
|
|
|
if (!isxdigit(*lpszStr))
|
|
return FALSE;
|
|
|
|
while (isxdigit(*lpszStr))
|
|
{
|
|
iRet = iRet * 16;
|
|
if (isdigit(*lpszStr))
|
|
iRet += (*lpszStr - '0');
|
|
else
|
|
iRet += 10 + (tolower(*lpszStr) - 'a');
|
|
lpszStr++;
|
|
}
|
|
*lpiRet = iRet;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Read decimal number */
|
|
if (!isdigit(*lpszStr))
|
|
return FALSE;
|
|
|
|
while (isdigit(*lpszStr))
|
|
{
|
|
iRet = iRet * 10;
|
|
iRet += (*lpszStr - '0');
|
|
lpszStr++;
|
|
}
|
|
*lpiRet = bNegative ? -iRet : iRet;
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrToIntExW [SHLWAPI.@]
|
|
*
|
|
* See StrToIntExA.
|
|
*/
|
|
BOOL WINAPI StrToIntExW(LPCWSTR lpszStr, DWORD dwFlags, LPINT lpiRet)
|
|
{
|
|
BOOL bNegative = FALSE;
|
|
int iRet = 0;
|
|
|
|
TRACE("(%s,%08lX,%p)\n", debugstr_w(lpszStr), dwFlags, lpiRet);
|
|
|
|
if (!lpszStr || !lpiRet)
|
|
{
|
|
WARN("Invalid parameter would crash under Win32!\n");
|
|
return FALSE;
|
|
}
|
|
if (dwFlags > STIF_SUPPORT_HEX)
|
|
{
|
|
WARN("Unknown flags (%08lX)!\n", dwFlags & ~STIF_SUPPORT_HEX);
|
|
}
|
|
|
|
/* Skip leading space, '+', '-' */
|
|
while (isspaceW(*lpszStr))
|
|
lpszStr = CharNextW(lpszStr);
|
|
|
|
if (*lpszStr == '-')
|
|
{
|
|
bNegative = TRUE;
|
|
lpszStr++;
|
|
}
|
|
else if (*lpszStr == '+')
|
|
lpszStr++;
|
|
|
|
if (dwFlags & STIF_SUPPORT_HEX &&
|
|
*lpszStr == '0' && tolowerW(lpszStr[1]) == 'x')
|
|
{
|
|
/* Read hex number */
|
|
lpszStr += 2;
|
|
|
|
if (!isxdigitW(*lpszStr))
|
|
return FALSE;
|
|
|
|
while (isxdigitW(*lpszStr))
|
|
{
|
|
iRet = iRet * 16;
|
|
if (isdigitW(*lpszStr))
|
|
iRet += (*lpszStr - '0');
|
|
else
|
|
iRet += 10 + (tolowerW(*lpszStr) - 'a');
|
|
lpszStr++;
|
|
}
|
|
*lpiRet = iRet;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Read decimal number */
|
|
if (!isdigitW(*lpszStr))
|
|
return FALSE;
|
|
|
|
while (isdigitW(*lpszStr))
|
|
{
|
|
iRet = iRet * 10;
|
|
iRet += (*lpszStr - '0');
|
|
lpszStr++;
|
|
}
|
|
*lpiRet = bNegative ? -iRet : iRet;
|
|
return TRUE;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrDupA [SHLWAPI.@]
|
|
*
|
|
* Duplicate a string.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] String to duplicate.
|
|
*
|
|
* RETURNS
|
|
* Success: A pointer to a new string containing the contents of lpszStr
|
|
* Failure: NULL, if memory cannot be allocated
|
|
*
|
|
* NOTES
|
|
* The string memory is allocated with LocalAlloc(), and so should be released
|
|
* by calling LocalFree().
|
|
*/
|
|
LPSTR WINAPI StrDupA(LPCSTR lpszStr)
|
|
{
|
|
int iLen;
|
|
LPSTR lpszRet;
|
|
|
|
TRACE("(%s)\n",debugstr_a(lpszStr));
|
|
|
|
iLen = lpszStr ? strlen(lpszStr) + 1 : 1;
|
|
lpszRet = (LPSTR)LocalAlloc(LMEM_FIXED, iLen);
|
|
|
|
if (lpszRet)
|
|
{
|
|
if (lpszStr)
|
|
memcpy(lpszRet, lpszStr, iLen);
|
|
else
|
|
*lpszRet = '\0';
|
|
}
|
|
return lpszRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrDupW [SHLWAPI.@]
|
|
*
|
|
* See StrDupA.
|
|
*/
|
|
LPWSTR WINAPI StrDupW(LPCWSTR lpszStr)
|
|
{
|
|
int iLen;
|
|
LPWSTR lpszRet;
|
|
|
|
TRACE("(%s)\n",debugstr_w(lpszStr));
|
|
|
|
iLen = (lpszStr ? strlenW(lpszStr) + 1 : 1) * sizeof(WCHAR);
|
|
lpszRet = (LPWSTR)LocalAlloc(LMEM_FIXED, iLen);
|
|
|
|
if (lpszRet)
|
|
{
|
|
if (lpszStr)
|
|
memcpy(lpszRet, lpszStr, iLen);
|
|
else
|
|
*lpszRet = '\0';
|
|
}
|
|
return lpszRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_StrSpnHelperA
|
|
*
|
|
* Internal implementation of StrSpnA/StrCSpnA/StrCSpnIA
|
|
*/
|
|
static int WINAPI SHLWAPI_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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_StrSpnHelperW
|
|
*
|
|
* Internal implementation of StrSpnW/StrCSpnW/StrCSpnIW
|
|
*/
|
|
static int WINAPI SHLWAPI_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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrSpnA [SHLWAPI.@]
|
|
*
|
|
* Find the length of the start of a string that contains only certain
|
|
* characters.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] String to search
|
|
* lpszMatch [I] Characters that can be in the substring
|
|
*
|
|
* RETURNS
|
|
* The length of the part of lpszStr containing only chars from lpszMatch,
|
|
* or 0 if any parameter is invalid.
|
|
*/
|
|
int WINAPI StrSpnA(LPCSTR lpszStr, LPCSTR lpszMatch)
|
|
{
|
|
TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
|
|
|
|
return SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, FALSE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrSpnW [SHLWAPI.@]
|
|
*
|
|
* See StrSpnA.
|
|
*/
|
|
int WINAPI StrSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
|
|
{
|
|
TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
|
|
|
|
return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, FALSE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCSpnA [SHLWAPI.@]
|
|
*
|
|
* 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 SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrA, TRUE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCSpnW [SHLWAPI.@]
|
|
*
|
|
* See StrCSpnA.
|
|
*/
|
|
int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
|
|
{
|
|
TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
|
|
|
|
return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrW, TRUE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCSpnIA [SHLWAPI.@]
|
|
*
|
|
* 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 SHLWAPI_StrSpnHelperA(lpszStr, lpszMatch, StrChrIA, TRUE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCSpnIW [SHLWAPI.@]
|
|
*
|
|
* See StrCSpnIA.
|
|
*/
|
|
int WINAPI StrCSpnIW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
|
|
{
|
|
TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
|
|
|
|
return SHLWAPI_StrSpnHelperW(lpszStr, lpszMatch, StrChrIW, TRUE);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrPBrkA [SHLWAPI.@]
|
|
*
|
|
* Search a string for any of a group of characters.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] String to search
|
|
* lpszMatch [I] Characters to match
|
|
*
|
|
* RETURNS
|
|
* A pointer to the first matching character in lpszStr, or NULL if no
|
|
* match was found.
|
|
*/
|
|
LPSTR WINAPI StrPBrkA(LPCSTR lpszStr, LPCSTR lpszMatch)
|
|
{
|
|
TRACE("(%s,%s)\n",debugstr_a(lpszStr), debugstr_a(lpszMatch));
|
|
|
|
if (lpszStr && lpszMatch && *lpszMatch)
|
|
{
|
|
while (*lpszStr)
|
|
{
|
|
if (StrChrA(lpszMatch, *lpszStr))
|
|
return (LPSTR)lpszStr;
|
|
lpszStr = CharNextA(lpszStr);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrPBrkW [SHLWAPI.@]
|
|
*
|
|
* See StrPBrkA.
|
|
*/
|
|
LPWSTR WINAPI StrPBrkW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
|
|
{
|
|
TRACE("(%s,%s)\n",debugstr_w(lpszStr), debugstr_w(lpszMatch));
|
|
|
|
if (lpszStr && lpszMatch && *lpszMatch)
|
|
{
|
|
while (*lpszStr)
|
|
{
|
|
if (StrChrW(lpszMatch, *lpszStr))
|
|
return (LPWSTR)lpszStr;
|
|
lpszStr = CharNextW(lpszStr);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_StrRChrHelperA
|
|
*
|
|
* Internal implementation of StrRChrA/StrRChrIA.
|
|
*/
|
|
static LPSTR WINAPI SHLWAPI_StrRChrHelperA(LPCSTR lpszStr,
|
|
LPCSTR lpszEnd, WORD ch,
|
|
BOOL (WINAPI *pChrCmpFn)(WORD,WORD))
|
|
{
|
|
LPCSTR lpszRet = NULL;
|
|
|
|
if (lpszStr)
|
|
{
|
|
WORD ch2;
|
|
|
|
if (!lpszEnd)
|
|
lpszEnd = lpszStr + lstrlenA(lpszStr);
|
|
|
|
while (*lpszStr && lpszStr <= lpszEnd)
|
|
{
|
|
ch2 = IsDBCSLeadByte(*lpszStr)? *lpszStr << 8 | lpszStr[1] : *lpszStr;
|
|
|
|
if (!pChrCmpFn(ch, ch2))
|
|
lpszRet = lpszStr;
|
|
lpszStr = CharNextA(lpszStr);
|
|
}
|
|
}
|
|
return (LPSTR)lpszRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_StrRChrHelperW
|
|
*
|
|
* Internal implementation of StrRChrW/StrRChrIW.
|
|
*/
|
|
static LPWSTR WINAPI SHLWAPI_StrRChrHelperW(LPCWSTR lpszStr,
|
|
LPCWSTR lpszEnd, WCHAR ch,
|
|
BOOL (WINAPI *pChrCmpFn)(WCHAR,WCHAR))
|
|
{
|
|
LPCWSTR lpszRet = NULL;
|
|
|
|
if (lpszStr)
|
|
{
|
|
if (!lpszEnd)
|
|
lpszEnd = lpszStr + strlenW(lpszStr);
|
|
|
|
while (*lpszStr && lpszStr <= lpszEnd)
|
|
{
|
|
if (!pChrCmpFn(ch, *lpszStr))
|
|
lpszRet = lpszStr;
|
|
lpszStr = CharNextW(lpszStr);
|
|
}
|
|
}
|
|
return (LPWSTR)lpszRet;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* StrRChrA [SHLWAPI.@]
|
|
*
|
|
* Find the last occurence 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)
|
|
{
|
|
TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
|
|
|
|
return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpA);
|
|
}
|
|
|
|
/**************************************************************************
|
|
* StrRChrW [SHLWAPI.@]
|
|
*
|
|
* See StrRChrA.
|
|
*/
|
|
LPWSTR WINAPI StrRChrW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
|
|
{
|
|
TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
|
|
|
|
return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, SHLWAPI_ChrCmpW);
|
|
}
|
|
|
|
/**************************************************************************
|
|
* StrRChrIA [SHLWAPI.@]
|
|
*
|
|
* Find the last occurence 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)
|
|
{
|
|
TRACE("(%s,%s,%x)\n", debugstr_a(lpszStr), debugstr_a(lpszEnd), ch);
|
|
|
|
return SHLWAPI_StrRChrHelperA(lpszStr, lpszEnd, ch, ChrCmpIA);
|
|
}
|
|
|
|
/**************************************************************************
|
|
* StrRChrIW [SHLWAPI.@]
|
|
*
|
|
* See StrRChrIA.
|
|
*/
|
|
LPWSTR WINAPI StrRChrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, WORD ch)
|
|
{
|
|
TRACE("(%s,%s,%x)\n", debugstr_w(lpszStr), debugstr_w(lpszEnd), ch);
|
|
|
|
return SHLWAPI_StrRChrHelperW(lpszStr, lpszEnd, ch, ChrCmpIW);
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCatBuffA [SHLWAPI.@]
|
|
*
|
|
* Concatenate two strings together.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [O] String to concatenate to
|
|
* lpszCat [I] String to add to lpszCat
|
|
* cchMax [I] Maximum number of characters for the whole string
|
|
*
|
|
* RETURNS
|
|
* lpszStr.
|
|
*
|
|
* NOTES
|
|
* cchMax dtermines the number of characters in the final length of the
|
|
* string, not the number appended to lpszStr from lpszCat.
|
|
*/
|
|
LPSTR WINAPI StrCatBuffA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
|
|
{
|
|
INT iLen;
|
|
|
|
TRACE("(%p,%s,%d)\n", lpszStr, debugstr_a(lpszCat), cchMax);
|
|
|
|
if (!lpszStr)
|
|
{
|
|
WARN("Invalid lpszStr would crash under Win32!\n");
|
|
return NULL;
|
|
}
|
|
|
|
iLen = strlen(lpszStr);
|
|
cchMax -= iLen;
|
|
|
|
if (cchMax > 0)
|
|
StrCpyNA(lpszStr + iLen, lpszCat, cchMax);
|
|
return lpszStr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCatBuffW [SHLWAPI.@]
|
|
*
|
|
* See StrCatBuffA.
|
|
*/
|
|
LPWSTR WINAPI StrCatBuffW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
|
|
{
|
|
INT iLen;
|
|
|
|
TRACE("(%p,%s,%d)\n", lpszStr, debugstr_w(lpszCat), cchMax);
|
|
|
|
if (!lpszStr)
|
|
{
|
|
WARN("Invalid lpszStr would crash under Win32!\n");
|
|
return NULL;
|
|
}
|
|
|
|
iLen = strlenW(lpszStr);
|
|
cchMax -= iLen;
|
|
|
|
if (cchMax > 0)
|
|
StrCpyNW(lpszStr + iLen, lpszCat, cchMax);
|
|
return lpszStr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrRetToBufA [SHLWAPI.@]
|
|
*
|
|
* Convert a STRRET to a normal string.
|
|
*
|
|
* PARAMS
|
|
* lpStrRet [O] STRRET to convert
|
|
* pIdl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSETA
|
|
* lpszDest [O] Destination for normal string
|
|
* dwLen [I] Length of lpszDest
|
|
*
|
|
* RETURNS
|
|
* Success: S_OK. lpszDest contains up to dwLen characters of the string.
|
|
* If lpStrRet is of type STRRET_WSTR, its memory is freed with
|
|
* CoTaskMemFree() and its type set to STRRET_CSTRA.
|
|
* Failure: E_FAIL, if any parameters are invalid.
|
|
*/
|
|
HRESULT WINAPI StrRetToBufA (LPSTRRET src, const ITEMIDLIST *pidl, LPSTR dest, DWORD len)
|
|
{
|
|
/* NOTE:
|
|
* This routine is identical to that in dlls/shell32/shellstring.c.
|
|
* It was duplicated because not every version of Shlwapi.dll exports
|
|
* StrRetToBufA. If you change one routine, change them both.
|
|
*/
|
|
TRACE("dest=%p len=0x%lx strret=%p pidl=%p stub\n",dest,len,src,pidl);
|
|
|
|
if (!src)
|
|
{
|
|
WARN("Invalid lpStrRet would crash under Win32!\n");
|
|
if (dest)
|
|
*dest = '\0';
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (!dest || !len)
|
|
return E_FAIL;
|
|
|
|
*dest = '\0';
|
|
|
|
switch (src->uType)
|
|
{
|
|
case STRRET_WSTR:
|
|
WideCharToMultiByte(CP_ACP, 0, src->u.pOleStr, -1, dest, len, NULL, NULL);
|
|
CoTaskMemFree(src->u.pOleStr);
|
|
break;
|
|
|
|
case STRRET_CSTR:
|
|
lstrcpynA((LPSTR)dest, src->u.cStr, len);
|
|
break;
|
|
|
|
case STRRET_OFFSET:
|
|
lstrcpynA((LPSTR)dest, ((LPCSTR)&pidl->mkid)+src->u.uOffset, len);
|
|
break;
|
|
|
|
default:
|
|
FIXME("unknown type!\n");
|
|
return FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrRetToBufW [SHLWAPI.@]
|
|
*
|
|
* See StrRetToBufA.
|
|
*/
|
|
HRESULT WINAPI StrRetToBufW (LPSTRRET src, const ITEMIDLIST *pidl, LPWSTR dest, DWORD len)
|
|
{
|
|
TRACE("dest=%p len=0x%lx strret=%p pidl=%p stub\n",dest,len,src,pidl);
|
|
|
|
if (!src)
|
|
{
|
|
WARN("Invalid lpStrRet would crash under Win32!\n");
|
|
if (dest)
|
|
*dest = '\0';
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (!dest || !len)
|
|
return E_FAIL;
|
|
|
|
*dest = '\0';
|
|
|
|
switch (src->uType)
|
|
{
|
|
case STRRET_WSTR:
|
|
lstrcpynW((LPWSTR)dest, src->u.pOleStr, len);
|
|
CoTaskMemFree(src->u.pOleStr);
|
|
break;
|
|
|
|
case STRRET_CSTR:
|
|
if (!MultiByteToWideChar( CP_ACP, 0, src->u.cStr, -1, dest, len ) && len)
|
|
dest[len-1] = 0;
|
|
break;
|
|
|
|
case STRRET_OFFSET:
|
|
if (pidl)
|
|
{
|
|
if (!MultiByteToWideChar( CP_ACP, 0, ((LPCSTR)&pidl->mkid)+src->u.uOffset, -1,
|
|
dest, len ) && len)
|
|
dest[len-1] = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
FIXME("unknown type!\n");
|
|
return FALSE;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrRetToStrA [SHLWAPI.@]
|
|
*
|
|
* converts a STRRET to a normal string
|
|
*/
|
|
HRESULT WINAPI StrRetToStrA(LPSTRRET pstr, const ITEMIDLIST * pidl, LPSTR* ppszName)
|
|
{
|
|
HRESULT ret = E_FAIL;
|
|
|
|
switch (pstr->uType) {
|
|
case STRRET_WSTR:
|
|
ret = _SHStrDupAW(pstr->u.pOleStr, ppszName);
|
|
CoTaskMemFree(pstr->u.pOleStr);
|
|
break;
|
|
|
|
case STRRET_CSTR:
|
|
ret = _SHStrDupAA(pstr->u.cStr, ppszName);
|
|
break;
|
|
|
|
case STRRET_OFFSET:
|
|
ret = _SHStrDupAA(((LPCSTR)&pidl->mkid)+pstr->u.uOffset, ppszName);
|
|
break;
|
|
|
|
default:
|
|
*ppszName = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrRetToStrW [SHLWAPI.@]
|
|
*
|
|
* converts a STRRET to a normal string
|
|
*/
|
|
HRESULT WINAPI StrRetToStrW(LPSTRRET pstr, const ITEMIDLIST * pidl, LPWSTR* ppszName)
|
|
{
|
|
HRESULT ret = E_FAIL;
|
|
|
|
switch (pstr->uType) {
|
|
case STRRET_WSTR:
|
|
ret = SHStrDupW(pstr->u.pOleStr, ppszName);
|
|
CoTaskMemFree(pstr->u.pOleStr);
|
|
break;
|
|
|
|
case STRRET_CSTR:
|
|
ret = SHStrDupA(pstr->u.cStr, ppszName);
|
|
break;
|
|
|
|
case STRRET_OFFSET:
|
|
ret = SHStrDupA(((LPCSTR)&pidl->mkid)+pstr->u.uOffset, ppszName);
|
|
break;
|
|
|
|
default:
|
|
*ppszName = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrFormatKBSizeA [SHLWAPI.@]
|
|
*
|
|
* Create a formatted string containing a byte count in Kilobytes.
|
|
*
|
|
* PARAMS
|
|
* llBytes [I] Byte size to format
|
|
* lpszDest [I] Destination for formatted string
|
|
* cchMax [I] Size of lpszDest
|
|
*
|
|
* RETURNS
|
|
* lpszDest.
|
|
*/
|
|
LPSTR WINAPI StrFormatKBSizeA(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
|
|
{
|
|
char szBuff[256], *szOut = szBuff + sizeof(szBuff) - 1;
|
|
LONGLONG ulKB = (llBytes + 1023) >> 10;
|
|
|
|
TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
|
|
|
|
*szOut-- = '\0';
|
|
*szOut-- = 'B';
|
|
*szOut-- = 'K';
|
|
*szOut-- = ' ';
|
|
|
|
do
|
|
{
|
|
LONGLONG ulNextDigit = ulKB % 10;
|
|
*szOut-- = '0' + ulNextDigit;
|
|
ulKB = (ulKB - ulNextDigit) / 10;
|
|
} while (ulKB > 0);
|
|
|
|
strncpy(lpszDest, szOut + 1, cchMax);
|
|
return lpszDest;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrFormatKBSizeW [SHLWAPI.@]
|
|
*
|
|
* See StrFormatKBSizeA.
|
|
*/
|
|
LPWSTR WINAPI StrFormatKBSizeW(LONGLONG llBytes, LPWSTR lpszDest, UINT cchMax)
|
|
{
|
|
WCHAR szBuff[256], *szOut = szBuff + sizeof(szBuff) - 1;
|
|
LONGLONG ulKB = (llBytes + 1023) >> 10;
|
|
|
|
TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
|
|
|
|
*szOut-- = '\0';
|
|
*szOut-- = 'B';
|
|
*szOut-- = 'K';
|
|
*szOut-- = ' ';
|
|
|
|
do
|
|
{
|
|
LONGLONG ulNextDigit = ulKB % 10;
|
|
*szOut-- = '0' + ulNextDigit;
|
|
ulKB = (ulKB - ulNextDigit) / 10;
|
|
} while (ulKB > 0);
|
|
|
|
strncpyW(lpszDest, szOut + 1, cchMax);
|
|
return lpszDest;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrNCatA [SHLWAPI.@]
|
|
*
|
|
* Concatenate two strings together.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [O] String to concatenate to
|
|
* lpszCat [I] String to add to lpszCat
|
|
* cchMax [I] Maximum number of characters to concatenate
|
|
*
|
|
* RETURNS
|
|
* lpszStr.
|
|
*
|
|
* NOTES
|
|
* cchMax dtermines the number of characters that are appended to lpszStr,
|
|
* not the total length of the string.
|
|
*/
|
|
LPSTR WINAPI StrNCatA(LPSTR lpszStr, LPCSTR lpszCat, INT cchMax)
|
|
{
|
|
LPSTR lpszRet = lpszStr;
|
|
|
|
TRACE("(%s,%s,%i)\n", debugstr_a(lpszStr), debugstr_a(lpszCat), cchMax);
|
|
|
|
if (!lpszStr)
|
|
{
|
|
WARN("Invalid lpszStr would crash under Win32!\n");
|
|
return NULL;
|
|
}
|
|
|
|
StrCpyNA(lpszStr + strlen(lpszStr), lpszCat, cchMax);
|
|
return lpszRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrNCatW [SHLWAPI.@]
|
|
*
|
|
* See StrNCatA.
|
|
*/
|
|
LPWSTR WINAPI StrNCatW(LPWSTR lpszStr, LPCWSTR lpszCat, INT cchMax)
|
|
{
|
|
LPWSTR lpszRet = lpszStr;
|
|
|
|
TRACE("(%s,%s,%i)\n", debugstr_w(lpszStr), debugstr_w(lpszCat), cchMax);
|
|
|
|
if (!lpszStr)
|
|
{
|
|
WARN("Invalid lpszStr would crash under Win32\n");
|
|
return NULL;
|
|
}
|
|
|
|
StrCpyNW(lpszStr + strlenW(lpszStr), lpszCat, cchMax);
|
|
return lpszRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrTrimA [SHLWAPI.@]
|
|
*
|
|
* Remove characters from the start and end of a string.
|
|
*
|
|
* PARAMS
|
|
* lpszStr [O] String to remove characters from
|
|
* lpszTrim [I] Characters to remove from lpszStr
|
|
*
|
|
* RETURNS
|
|
* TRUE If lpszStr was valid and modified
|
|
* FALSE Otherwise
|
|
*/
|
|
BOOL WINAPI StrTrimA(LPSTR lpszStr, LPCSTR lpszTrim)
|
|
{
|
|
DWORD dwLen;
|
|
LPSTR lpszRead = lpszStr;
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("(%s,%s)\n", debugstr_a(lpszStr), debugstr_a(lpszTrim));
|
|
|
|
if (lpszRead && *lpszRead)
|
|
{
|
|
while (*lpszRead && StrChrA(lpszTrim, *lpszRead))
|
|
lpszRead = CharNextA(lpszRead); /* Skip leading matches */
|
|
|
|
dwLen = strlen(lpszRead);
|
|
|
|
if (lpszRead != lpszStr)
|
|
{
|
|
memmove(lpszStr, lpszRead, dwLen + 1);
|
|
bRet = TRUE;
|
|
}
|
|
if (dwLen > 0)
|
|
{
|
|
lpszRead = lpszStr + dwLen;
|
|
while (StrChrA(lpszTrim, lpszRead[-1]))
|
|
lpszRead = CharPrevA(lpszStr, lpszRead); /* Skip trailing matches */
|
|
|
|
if (lpszRead != lpszStr + dwLen)
|
|
{
|
|
*lpszRead = '\0';
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrTrimW [SHLWAPI.@]
|
|
*
|
|
* See StrTrimA.
|
|
*/
|
|
BOOL WINAPI StrTrimW(LPWSTR lpszStr, LPCWSTR lpszTrim)
|
|
{
|
|
DWORD dwLen;
|
|
LPWSTR lpszRead = lpszStr;
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszTrim));
|
|
|
|
if (lpszRead && *lpszRead)
|
|
{
|
|
while (*lpszRead && StrChrW(lpszTrim, *lpszRead))
|
|
lpszRead = CharNextW(lpszRead); /* Skip leading matches */
|
|
|
|
dwLen = strlenW(lpszRead);
|
|
|
|
if (lpszRead != lpszStr)
|
|
{
|
|
memmove(lpszStr, lpszRead, (dwLen + 1) * sizeof(WCHAR));
|
|
bRet = TRUE;
|
|
}
|
|
if (dwLen > 0)
|
|
{
|
|
lpszRead = lpszStr + dwLen;
|
|
while (StrChrW(lpszTrim, lpszRead[-1]))
|
|
lpszRead = CharPrevW(lpszStr, lpszRead); /* Skip trailing matches */
|
|
|
|
if (lpszRead != lpszStr + dwLen)
|
|
{
|
|
*lpszRead = '\0';
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* _SHStrDupA [INTERNAL]
|
|
*
|
|
* Duplicates a ASCII string to ASCII. The destination buffer is allocated.
|
|
*/
|
|
static HRESULT WINAPI _SHStrDupAA(LPCSTR src, LPSTR * dest)
|
|
{
|
|
HRESULT hr;
|
|
int len = 0;
|
|
|
|
if (src) {
|
|
len = lstrlenA(src) + 1;
|
|
*dest = CoTaskMemAlloc(len);
|
|
} else {
|
|
*dest = NULL;
|
|
}
|
|
|
|
if (*dest) {
|
|
lstrcpynA(*dest,src, len);
|
|
hr = S_OK;
|
|
} else {
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
TRACE("%s->(%p)\n", debugstr_a(src), *dest);
|
|
return hr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHStrDupA
|
|
*
|
|
* Return a Unicode copy of a string, in memory allocated by CoTaskMemAlloc().
|
|
*
|
|
* PARAMS
|
|
* lpszStr [I] String to copy
|
|
* lppszDest [O] Destination for the new string copy
|
|
*
|
|
* RETURNS
|
|
* Success: S_OK. lppszDest contains the new string in Unicode format.
|
|
* Failure: E_OUTOFMEMORY, If any arguments are invalid or memory allocation
|
|
* fails.
|
|
*/
|
|
HRESULT WINAPI SHStrDupA(LPCSTR src, LPWSTR * dest)
|
|
{
|
|
HRESULT hr;
|
|
int len = 0;
|
|
|
|
if (src) {
|
|
len = MultiByteToWideChar(0,0,src,-1,0,0)* sizeof(WCHAR);
|
|
*dest = CoTaskMemAlloc(len);
|
|
} else {
|
|
*dest = NULL;
|
|
}
|
|
|
|
if (*dest) {
|
|
MultiByteToWideChar(0,0,src,-1,*dest,len);
|
|
hr = S_OK;
|
|
} else {
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
TRACE("%s->(%p)\n", debugstr_a(src), *dest);
|
|
return hr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* _SHStrDupAW [INTERNAL]
|
|
*
|
|
* Duplicates a UNICODE to a ASCII string. The destination buffer is allocated.
|
|
*/
|
|
static HRESULT WINAPI _SHStrDupAW(LPCWSTR src, LPSTR * dest)
|
|
{
|
|
HRESULT hr;
|
|
int len = 0;
|
|
|
|
if (src) {
|
|
len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
|
|
*dest = CoTaskMemAlloc(len);
|
|
} else {
|
|
*dest = NULL;
|
|
}
|
|
|
|
if (*dest) {
|
|
WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len, NULL, NULL);
|
|
hr = S_OK;
|
|
} else {
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
TRACE("%s->(%p)\n", debugstr_w(src), *dest);
|
|
return hr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHStrDupW
|
|
*
|
|
* See SHStrDupA.
|
|
*/
|
|
HRESULT WINAPI SHStrDupW(LPCWSTR src, LPWSTR * dest)
|
|
{
|
|
HRESULT hr;
|
|
int len = 0;
|
|
|
|
if (src) {
|
|
len = (lstrlenW(src) + 1) * sizeof(WCHAR);
|
|
*dest = CoTaskMemAlloc(len);
|
|
} else {
|
|
*dest = NULL;
|
|
}
|
|
|
|
if (*dest) {
|
|
memcpy(*dest, src, len);
|
|
hr = S_OK;
|
|
} else {
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
TRACE("%s->(%p)\n", debugstr_w(src), *dest);
|
|
return hr;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_WriteReverseNum
|
|
*
|
|
* Internal helper for SHLWAPI_WriteTimeClass.
|
|
*/
|
|
inline static LPWSTR SHLWAPI_WriteReverseNum(LPWSTR lpszOut, DWORD dwNum)
|
|
{
|
|
*lpszOut-- = '\0';
|
|
|
|
/* Write a decimal number to a string, backwards */
|
|
do
|
|
{
|
|
DWORD dwNextDigit = dwNum % 10;
|
|
*lpszOut-- = '0' + dwNextDigit;
|
|
dwNum = (dwNum - dwNextDigit) / 10;
|
|
} while (dwNum > 0);
|
|
|
|
return lpszOut;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_FormatSignificant
|
|
*
|
|
* Internal helper for SHLWAPI_WriteTimeClass.
|
|
*/
|
|
inline static int SHLWAPI_FormatSignificant(LPWSTR lpszNum, int dwDigits)
|
|
{
|
|
/* Zero non significant digits, return remaining significant digits */
|
|
while (*lpszNum)
|
|
{
|
|
lpszNum++;
|
|
if (--dwDigits == 0)
|
|
{
|
|
while (*lpszNum)
|
|
*lpszNum++ = '0';
|
|
return 0;
|
|
}
|
|
}
|
|
return dwDigits;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHLWAPI_WriteTimeClass
|
|
*
|
|
* Internal helper for StrFromTimeIntervalW.
|
|
*/
|
|
static int SHLWAPI_WriteTimeClass(LPWSTR lpszOut, DWORD dwValue,
|
|
LPCWSTR lpszClass, int iDigits)
|
|
{
|
|
WCHAR szBuff[64], *szOut = szBuff + 32;
|
|
|
|
szOut = SHLWAPI_WriteReverseNum(szOut, dwValue);
|
|
iDigits = SHLWAPI_FormatSignificant(szOut + 1, iDigits);
|
|
*szOut = ' ';
|
|
strcpyW(szBuff + 32, lpszClass);
|
|
strcatW(lpszOut, szOut);
|
|
return iDigits;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrFromTimeIntervalA [SHLWAPI.@]
|
|
*
|
|
* Format a millisecond time interval into a string
|
|
*
|
|
* PARAMS
|
|
* lpszStr [O] Output buffer for formatted time interval
|
|
* cchMax [I] Size of lpszStr
|
|
* dwMS [I] Number of milliseconds
|
|
* iDigits [I] Number of digits to print
|
|
*
|
|
* RETURNS
|
|
* The length of the formatted string, or 0 if any parameter is invalid.
|
|
*
|
|
* NOTES
|
|
* This implementation mimics the Win32 behaviour of always writing a leading
|
|
* space before the time interval begins.
|
|
*
|
|
* iDigits is used to provide approximate times if accuracy is not important.
|
|
* This number of digits will be written of the first non-zero time class
|
|
* (hours/minutes/seconds). If this does not complete the time classification,
|
|
* the remaining digits are changed to zeros (i.e. The time is _not_ rounded).
|
|
* If there are digits remaining following the writing of a time class, the
|
|
* next time class will be written.
|
|
*
|
|
* For example, given dwMS represents 138 hours,43 minutes and 15 seconds, the
|
|
* following will result from the given values of iDigits:
|
|
*
|
|
*| iDigits 1 2 3 4 5 ...
|
|
*| lpszStr "100 hr" "130 hr" "138 hr" "138 hr 40 min" "138 hr 43 min" ...
|
|
*/
|
|
INT WINAPI StrFromTimeIntervalA(LPSTR lpszStr, UINT cchMax, DWORD dwMS,
|
|
int iDigits)
|
|
{
|
|
INT iRet = 0;
|
|
|
|
TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
|
|
|
|
if (lpszStr && cchMax)
|
|
{
|
|
WCHAR szBuff[128];
|
|
StrFromTimeIntervalW(szBuff, sizeof(szBuff)/sizeof(WCHAR), dwMS, iDigits);
|
|
WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszStr,cchMax,0,0);
|
|
}
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
* StrFromTimeIntervalW [SHLWAPI.@]
|
|
*
|
|
* See StrFromTimeIntervalA.
|
|
*/
|
|
INT WINAPI StrFromTimeIntervalW(LPWSTR lpszStr, UINT cchMax, DWORD dwMS,
|
|
int iDigits)
|
|
{
|
|
static const WCHAR szHr[] = {' ','h','r','\0'};
|
|
static const WCHAR szMin[] = {' ','m','i','n','\0'};
|
|
static const WCHAR szSec[] = {' ','s','e','c','\0'};
|
|
INT iRet = 0;
|
|
|
|
TRACE("(%p,%d,%ld,%d)\n", lpszStr, cchMax, dwMS, iDigits);
|
|
|
|
if (lpszStr && cchMax)
|
|
{
|
|
WCHAR szCopy[128];
|
|
DWORD dwHours, dwMinutes;
|
|
|
|
if (!iDigits || cchMax == 1)
|
|
{
|
|
*lpszStr = '\0';
|
|
return 0;
|
|
}
|
|
|
|
/* Calculate the time classes */
|
|
dwMS = (dwMS + 500) / 1000;
|
|
dwHours = dwMS / 3600;
|
|
dwMS -= dwHours * 3600;
|
|
dwMinutes = dwMS / 60;
|
|
dwMS -= dwMinutes * 60;
|
|
|
|
szCopy[0] = '\0';
|
|
|
|
if (dwHours)
|
|
iDigits = SHLWAPI_WriteTimeClass(szCopy, dwHours, szHr, iDigits);
|
|
|
|
if (dwMinutes && iDigits)
|
|
iDigits = SHLWAPI_WriteTimeClass(szCopy, dwMinutes, szMin, iDigits);
|
|
|
|
if (iDigits) /* Always write seconds if we have significant digits */
|
|
SHLWAPI_WriteTimeClass(szCopy, dwMS, szSec, iDigits);
|
|
|
|
strncpyW(lpszStr, szCopy, cchMax);
|
|
iRet = strlenW(lpszStr);
|
|
}
|
|
return iRet;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrIsIntlEqualA [SHLWAPI.@]
|
|
*
|
|
* 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 StrIsIntlEqualA(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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrIsIntlEqualW [SHLWAPI.@]
|
|
*
|
|
* See StrIsIntlEqualA.
|
|
*/
|
|
BOOL WINAPI StrIsIntlEqualW(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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.399]
|
|
*
|
|
* Copy a string to another string, up to a maximum number of characters.
|
|
*
|
|
* PARAMS
|
|
* lpszDest [O] Destination string
|
|
* lpszSrc [I] Source string
|
|
* iLen [I] Maximum number of chars to copy
|
|
*
|
|
* RETURNS
|
|
* Success: A pointer to the last character written.
|
|
* Failure: lpszDest, if any arguments are invalid.
|
|
*/
|
|
LPSTR WINAPI SHLWAPI_399(LPSTR lpszDest, LPCSTR lpszSrc, int iLen)
|
|
{
|
|
TRACE("(%p,%s,%i)\n", lpszDest, debugstr_a(lpszSrc), iLen);
|
|
|
|
if (lpszDest && lpszSrc && iLen > 0)
|
|
{
|
|
while ((iLen-- > 1) && *lpszSrc)
|
|
*lpszDest++ = *lpszSrc++;
|
|
if (iLen >= 0)
|
|
*lpszDest = '\0';
|
|
}
|
|
return lpszDest;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* @ [SHLWAPI.400]
|
|
*
|
|
* Unicode version of SHLWAPI_399.
|
|
*/
|
|
LPWSTR WINAPI SHLWAPI_400(LPWSTR lpszDest, LPCWSTR lpszSrc, int iLen)
|
|
{
|
|
TRACE("(%p,%s,%i)\n", lpszDest, debugstr_w(lpszSrc), iLen);
|
|
|
|
if (lpszDest && lpszSrc && iLen > 0)
|
|
{
|
|
while ((iLen-- > 1) && *lpszSrc)
|
|
*lpszDest++ = *lpszSrc++;
|
|
if (iLen >= 0)
|
|
*lpszDest = '\0';
|
|
}
|
|
return lpszDest;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrCmpLogicalW [SHLWAPI.@]
|
|
*
|
|
* Compare two strings, ignoring case and comparing digits as numbers.
|
|
*
|
|
* PARAMS
|
|
* 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.
|
|
*/
|
|
INT WINAPI StrCmpLogicalW(LPCWSTR lpszStr, LPCWSTR lpszComp)
|
|
{
|
|
INT iDiff;
|
|
|
|
TRACE("(%s,%s)\n", debugstr_w(lpszStr), debugstr_w(lpszComp));
|
|
|
|
if (lpszStr && lpszComp)
|
|
{
|
|
while (*lpszStr)
|
|
{
|
|
if (!*lpszComp)
|
|
return 1;
|
|
else if (isdigitW(*lpszStr))
|
|
{
|
|
int iStr, iComp;
|
|
|
|
if (!isdigitW(*lpszComp))
|
|
return -1;
|
|
|
|
/* Compare the numbers */
|
|
StrToIntExW(lpszStr, 0, &iStr);
|
|
StrToIntExW(lpszComp, 0, &iComp);
|
|
|
|
if (iStr < iComp)
|
|
return -1;
|
|
else if (iStr > iComp)
|
|
return 1;
|
|
|
|
/* Skip */
|
|
while (isdigitW(*lpszStr))
|
|
lpszStr++;
|
|
while (isdigitW(*lpszComp))
|
|
lpszComp++;
|
|
}
|
|
else if (isdigitW(*lpszComp))
|
|
return 1;
|
|
else
|
|
{
|
|
iDiff = SHLWAPI_ChrCmpHelperA(*lpszStr,*lpszComp,NORM_IGNORECASE);
|
|
if (iDiff > 0)
|
|
return 1;
|
|
else if (iDiff < 0)
|
|
return -1;
|
|
|
|
lpszStr++;
|
|
lpszComp++;
|
|
}
|
|
}
|
|
if (*lpszComp)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Structure for formatting byte strings */
|
|
typedef struct tagSHLWAPI_BYTEFORMATS
|
|
{
|
|
LONGLONG dLimit;
|
|
double dDivisor;
|
|
double dNormaliser;
|
|
LPCSTR lpszFormat;
|
|
CHAR wPrefix;
|
|
} SHLWAPI_BYTEFORMATS;
|
|
|
|
/*************************************************************************
|
|
* StrFormatByteSize64A [SHLWAPI.@]
|
|
*
|
|
* Create a string containing an abbreviated byte count of up to 2^63-1.
|
|
*
|
|
* PARAMS
|
|
* llBytes [I] Byte size to format
|
|
* lpszDest [I] Destination for formatted string
|
|
* cchMax [I] Size of lpszDest
|
|
*
|
|
* RETURNS
|
|
* lpszDest.
|
|
*
|
|
* NOTES
|
|
* There is no StrFormatByteSize64W function, it is called StrFormatByteSizeW().
|
|
*/
|
|
LPSTR WINAPI StrFormatByteSize64A(LONGLONG llBytes, LPSTR lpszDest, UINT cchMax)
|
|
{
|
|
static const char szBytes[] = "%ld bytes";
|
|
static const char sz3_0[] = "%3.0f";
|
|
static const char sz3_1[] = "%3.1f";
|
|
static const char sz3_2[] = "%3.2f";
|
|
|
|
static const SHLWAPI_BYTEFORMATS bfFormats[] =
|
|
{
|
|
{ 10240, 10.24, 100.0, sz3_2, 'K' }, /* 10 KB */
|
|
{ 102400, 102.4, 10.0, sz3_1, 'K' }, /* 100 KB */
|
|
{ 1024000, 1024.0, 1.0, sz3_0, 'K' }, /* 1000 KB */
|
|
{ 10485760, 10485.76, 100.0, sz3_2, 'M' }, /* 10 MB */
|
|
{ 104857600, 104857.6, 10.0, sz3_1, 'M' }, /* 100 MB */
|
|
{ 1048576000, 1048576.0, 1.0, sz3_0, 'M' }, /* 1000 MB */
|
|
{ 10737418240, 10737418.24, 100.0, sz3_2, 'G' }, /* 10 GB */
|
|
{ 107374182400, 107374182.4, 10.0, sz3_1, 'G' }, /* 100 GB */
|
|
{ 1073741824000, 1073741824.0, 1.0, sz3_0, 'G' }, /* 1000 GB */
|
|
{ 10995116277760, 10485.76, 100.0, sz3_2, 'T' }, /* 10 TB */
|
|
{ 109951162777600, 104857.6, 10.0, sz3_1, 'T' }, /* 100 TB */
|
|
{ 1099511627776000, 1048576.0, 1.0, sz3_0, 'T' }, /* 1000 TB */
|
|
{ 11258999068426240, 10737418.24, 100.00, sz3_2, 'P' }, /* 10 PB */
|
|
{ 112589990684262400, 107374182.4, 10.00, sz3_1, 'P' }, /* 100 PB */
|
|
{ 1125899906842624000, 1073741824.0, 1.00, sz3_0, 'P' }, /* 1000 PB */
|
|
{ 0, 10995116277.76, 100.00, sz3_2, 'E' } /* EB's, catch all */
|
|
};
|
|
char szBuff[32];
|
|
char szAdd[4];
|
|
double dBytes;
|
|
UINT i = 0;
|
|
|
|
TRACE("(%lld,%p,%d)\n", llBytes, lpszDest, cchMax);
|
|
|
|
if (!lpszDest || !cchMax)
|
|
return lpszDest;
|
|
|
|
if (llBytes < 1024) /* 1K */
|
|
{
|
|
snprintf (lpszDest, cchMax, szBytes, (long)llBytes);
|
|
return lpszDest;
|
|
}
|
|
|
|
/* Note that if this loop completes without finding a match, i will be
|
|
* pointing at the last entry, which is a catch all for > 1000 PB
|
|
*/
|
|
while (i < sizeof(bfFormats) / sizeof(SHLWAPI_BYTEFORMATS) - 1)
|
|
{
|
|
if (llBytes < bfFormats[i].dLimit)
|
|
break;
|
|
i++;
|
|
}
|
|
/* Above 1 TB we encounter problems with FP accuracy. So for amounts above
|
|
* this number we integer shift down by 1 MB first. The table above has
|
|
* the divisors scaled down from the '< 10 TB' entry onwards, to account
|
|
* for this. We also add a small fudge factor to get the correct result for
|
|
* counts that lie exactly on a 1024 byte boundary.
|
|
*/
|
|
if (i > 8)
|
|
dBytes = (double)(llBytes >> 20) + 0.001; /* Scale down by I MB */
|
|
else
|
|
dBytes = (double)llBytes + 0.00001;
|
|
|
|
dBytes = floor(dBytes / bfFormats[i].dDivisor) / bfFormats[i].dNormaliser;
|
|
|
|
sprintf(szBuff, bfFormats[i].lpszFormat, dBytes);
|
|
szAdd[0] = ' ';
|
|
szAdd[1] = bfFormats[i].wPrefix;
|
|
szAdd[2] = 'B';
|
|
szAdd[3] = '\0';
|
|
strcat(szBuff, szAdd);
|
|
strncpy(lpszDest, szBuff, cchMax);
|
|
return lpszDest;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrFormatByteSizeW [SHLWAPI.@]
|
|
*
|
|
* See StrFormatByteSize64A.
|
|
*/
|
|
LPWSTR WINAPI StrFormatByteSizeW(LONGLONG llBytes, LPWSTR lpszDest,
|
|
UINT cchMax)
|
|
{
|
|
char szBuff[32];
|
|
|
|
StrFormatByteSize64A(llBytes, szBuff, sizeof(szBuff));
|
|
|
|
if (lpszDest)
|
|
MultiByteToWideChar(CP_ACP, 0, szBuff, -1, lpszDest, cchMax);
|
|
return lpszDest;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* StrFormatByteSizeA [SHLWAPI.@]
|
|
*
|
|
* Create a string containing an abbreviated byte count of up to 2^31-1.
|
|
*
|
|
* PARAMS
|
|
* dwBytes [I] Byte size to format
|
|
* lpszDest [I] Destination for formatted string
|
|
* cchMax [I] Size of lpszDest
|
|
*
|
|
* RETURNS
|
|
* lpszDest.
|
|
*
|
|
* NOTES
|
|
* The Ascii and Unicode versions of this function accept a different
|
|
* integer type for dwBytes. See StrFormatByteSize64A().
|
|
*/
|
|
LPSTR WINAPI StrFormatByteSizeA(DWORD dwBytes, LPSTR lpszDest, UINT cchMax)
|
|
{
|
|
TRACE("(%ld,%p,%d)\n", dwBytes, lpszDest, cchMax);
|
|
|
|
return StrFormatByteSize64A(dwBytes, lpszDest, cchMax);
|
|
}
|