diff --git a/reactos/lib/shlwapi/assoc.c b/reactos/lib/shlwapi/assoc.c new file mode 100644 index 00000000000..f69638bb07e --- /dev/null +++ b/reactos/lib/shlwapi/assoc.c @@ -0,0 +1,699 @@ +/* + * IQueryAssociations object and helper functions + * + * 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 + +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winreg.h" +#include "objbase.h" +#include "shlguid.h" +#include "shlwapi.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/************************************************************************** + * IQueryAssociations {SHLWAPI} + * + * DESCRIPTION + * This object provides a layer of abstraction over the system registry in + * order to simplify the process of parsing associations between files. + * Associations in this context means the registry entries that link (for + * example) the extension of a file with its description, list of + * applications to open the file with, and actions that can be performed on it + * (the shell displays such information in the context menu of explorer + * when you right-click on a file). + * + * HELPERS + * You can use this object tranparently by calling the helper functions + * AssocQueryKeyA(), AssocQueryStringA() and AssocQueryStringByKeyA(). These + * create an IQueryAssociations object, perform the requested actions + * and then dispose of the object. Alternatively, you can create an instance + * of the object using AssocCreate() and call the following methods on it: + * + * METHODS + */ + +/* Default IQueryAssociations::Init() flags */ +#define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME|ASSOCF_INIT_DEFAULTTOSTAR| \ + ASSOCF_INIT_DEFAULTTOFOLDER) + +typedef struct +{ + ICOM_VFIELD(IQueryAssociations); + LONG ref; + HKEY hkeySource; + HKEY hkeyProgID; +} IQueryAssociationsImpl; + +static struct ICOM_VTABLE(IQueryAssociations) IQueryAssociations_vtbl; + +/************************************************************************** + * IQueryAssociations_Constructor [internal] + * + * Construct a new IQueryAssociations object. + */ +static IQueryAssociations* IQueryAssociations_Constructor(void) +{ + IQueryAssociationsImpl* iface; + + iface =(IQueryAssociationsImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IQueryAssociationsImpl)); + iface->lpVtbl = &IQueryAssociations_vtbl; + iface->ref = 1; + iface->hkeySource = NULL; + iface->hkeyProgID = NULL; + + TRACE("Returning IQueryAssociations* %p\n", iface); + return (IQueryAssociations*)iface; +} + +/************************************************************************* + * SHLWAPI_ParamAToW + * + * Internal helper function: Convert ASCII parameter to Unicode. + */ +static BOOL SHLWAPI_ParamAToW(LPCSTR lpszParam, LPWSTR lpszBuff, DWORD dwLen, + LPWSTR* lpszOut) +{ + if (lpszParam) + { + DWORD dwStrLen = lstrlenA(lpszParam); + + if (dwStrLen < dwLen) + { + *lpszOut = lpszBuff; /* Use Buffer, it is big enough */ + } + else + { + /* Create a new buffer big enough for the string */ + *lpszOut = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, + (dwStrLen + 1) * sizeof(WCHAR)); + if (!*lpszOut) + return FALSE; + } + MultiByteToWideChar(0, 0, lpszParam, -1, *lpszOut, -1); + } + else + *lpszOut = NULL; + return TRUE; +} + +/************************************************************************* + * AssocCreate [SHLWAPI.@] + * + * Create a new IQueryAssociations object. + * + * PARAMS + * clsid [I] CLSID of object + * refiid [I] REFIID of interface + * lpInterface [O] Destination for the created IQueryAssociations object + * + * RETURNS + * Success: S_OK. lpInterface contains the new object. + * Failure: An HRESULT error code indicating the error. + * + * NOTES + * refiid must be equal to IID_IQueryAssociations, or this function will fail. + */ +HRESULT WINAPI AssocCreate(CLSID clsid, REFIID refiid, void **lpInterface) +{ + HRESULT hRet; + IQueryAssociations* lpAssoc; + + TRACE("(%s,%s,%p)\n", debugstr_guid(&clsid), debugstr_guid(refiid), + lpInterface); + + if (!lpInterface) + return E_INVALIDARG; + + *(DWORD*)lpInterface = 0; + + if (!IsEqualGUID(&clsid, &IID_IQueryAssociations)) + return E_NOTIMPL; + + lpAssoc = IQueryAssociations_Constructor(); + + if (!lpAssoc) + return E_OUTOFMEMORY; + + hRet = IQueryAssociations_QueryInterface(lpAssoc, refiid, lpInterface); + IQueryAssociations_Release(lpAssoc); + return hRet; +} + +/************************************************************************* + * AssocQueryKeyW [SHLWAPI.@] + * + * See AssocQueryKeyA. + */ +HRESULT WINAPI AssocQueryKeyW(ASSOCF cfFlags, ASSOCKEY assockey, LPCWSTR pszAssoc, + LPCWSTR pszExtra, HKEY *phkeyOut) +{ + HRESULT hRet; + IQueryAssociations* lpAssoc; + + TRACE("(0x%8lx,0x%8x,%s,%s,%p)\n", cfFlags, assockey, debugstr_w(pszAssoc), + debugstr_w(pszExtra), phkeyOut); + + lpAssoc = IQueryAssociations_Constructor(); + + if (!lpAssoc) + return E_OUTOFMEMORY; + + cfFlags &= SHLWAPI_DEF_ASSOCF; + hRet = IQueryAssociations_Init(lpAssoc, cfFlags, pszAssoc, NULL, NULL); + + if (SUCCEEDED(hRet)) + hRet = IQueryAssociations_GetKey(lpAssoc, cfFlags, assockey, pszExtra, phkeyOut); + + IQueryAssociations_Release(lpAssoc); + return hRet; +} + +/************************************************************************* + * AssocQueryKeyA [SHLWAPI.@] + * + * Get a file association key from the registry. + * + * PARAMS + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * assockey [I] Type of key to get + * pszAssoc [I] Key name to search below + * pszExtra [I] Extra information about the key location + * phkeyOut [O] Destination for the association key + * + * RETURNS + * Success: S_OK. phkeyOut contains the key. + * Failure: An HRESULT error code indicating the error. + */ +HRESULT WINAPI AssocQueryKeyA(ASSOCF cfFlags, ASSOCKEY assockey, LPCSTR pszAssoc, + LPCSTR pszExtra, HKEY *phkeyOut) +{ + WCHAR szAssocW[MAX_PATH], *lpszAssocW = NULL; + WCHAR szExtraW[MAX_PATH], *lpszExtraW = NULL; + HRESULT hRet = E_OUTOFMEMORY; + + TRACE("(0x%8lx,0x%8x,%s,%s,%p)\n", cfFlags, assockey, debugstr_a(pszAssoc), + debugstr_a(pszExtra), phkeyOut); + + if (SHLWAPI_ParamAToW(pszAssoc, szAssocW, MAX_PATH, &lpszAssocW) && + SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW)) + { + hRet = AssocQueryKeyW(cfFlags, assockey, lpszAssocW, lpszExtraW, phkeyOut); + } + + if (lpszAssocW && lpszAssocW != szAssocW) + HeapFree(GetProcessHeap(), 0, lpszAssocW); + + if (lpszExtraW && lpszExtraW != szExtraW) + HeapFree(GetProcessHeap(), 0, lpszExtraW); + + return hRet; +} + +/************************************************************************* + * AssocQueryStringW [SHLWAPI.@] + * + * See AssocQueryStringA. + */ +HRESULT WINAPI AssocQueryStringW(ASSOCF cfFlags, ASSOCSTR str, LPCWSTR pszAssoc, + LPCWSTR pszExtra, LPWSTR pszOut, DWORD *pcchOut) +{ + HRESULT hRet; + IQueryAssociations* lpAssoc; + + TRACE("(0x%8lx,0x%8x,%s,%s,%p,%p)\n", cfFlags, str, debugstr_w(pszAssoc), + debugstr_w(pszExtra), pszOut, pcchOut); + + if (!pcchOut) + return E_INVALIDARG; + + lpAssoc = IQueryAssociations_Constructor(); + + if (!lpAssoc) + return E_OUTOFMEMORY; + + hRet = IQueryAssociations_Init(lpAssoc, cfFlags & SHLWAPI_DEF_ASSOCF, + pszAssoc, NULL, NULL); + + if (SUCCEEDED(hRet)) + hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra, + pszOut, pcchOut); + + IQueryAssociations_Release(lpAssoc); + return hRet; +} + +/************************************************************************* + * AssocQueryStringA [SHLWAPI.@] + * + * Get a file association string from the registry. + * + * PARAMS + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * str [I] Type of string to get (ASSOCSTR enum from "shlwapi.h") + * pszAssoc [I] Key name to search below + * pszExtra [I] Extra information about the string location + * pszOut [O] Destination for the association string + * pcchOut [O] Length of pszOut + * + * RETURNS + * Success: S_OK. pszOut contains the string, pcchOut contains its length. + * Failure: An HRESULT error code indicating the error. + */ +HRESULT WINAPI AssocQueryStringA(ASSOCF cfFlags, ASSOCSTR str, LPCSTR pszAssoc, + LPCSTR pszExtra, LPSTR pszOut, DWORD *pcchOut) +{ + WCHAR szAssocW[MAX_PATH], *lpszAssocW = NULL; + WCHAR szExtraW[MAX_PATH], *lpszExtraW = NULL; + HRESULT hRet = E_OUTOFMEMORY; + + TRACE("(0x%8lx,0x%8x,%s,%s,%p,%p)\n", cfFlags, str, debugstr_a(pszAssoc), + debugstr_a(pszExtra), pszOut, pcchOut); + + if (!pcchOut) + hRet = E_INVALIDARG; + else if (SHLWAPI_ParamAToW(pszAssoc, szAssocW, MAX_PATH, &lpszAssocW) && + SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW)) + { + WCHAR szReturnW[MAX_PATH], *lpszReturnW = szReturnW; + DWORD dwLenOut = *pcchOut; + + if (dwLenOut >= MAX_PATH) + lpszReturnW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, + (dwLenOut + 1) * sizeof(WCHAR)); + + if (!lpszReturnW) + hRet = E_OUTOFMEMORY; + else + { + hRet = AssocQueryStringW(cfFlags, str, lpszAssocW, lpszExtraW, + lpszReturnW, &dwLenOut); + + if (SUCCEEDED(hRet)) + WideCharToMultiByte(CP_ACP,0,szReturnW,-1,pszOut,dwLenOut,0,0); + *pcchOut = dwLenOut; + + if (lpszReturnW && lpszReturnW != szReturnW) + HeapFree(GetProcessHeap(), 0, lpszReturnW); + } + } + + if (lpszAssocW && lpszAssocW != szAssocW) + HeapFree(GetProcessHeap(), 0, lpszAssocW); + if (lpszExtraW && lpszExtraW != szExtraW) + HeapFree(GetProcessHeap(), 0, lpszExtraW); + return hRet; +} + +/************************************************************************* + * AssocQueryStringByKeyW [SHLWAPI.@] + * + * See AssocQueryStringByKeyA. + */ +HRESULT WINAPI AssocQueryStringByKeyW(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc, + LPCWSTR pszExtra, LPWSTR pszOut, + DWORD *pcchOut) +{ + HRESULT hRet; + IQueryAssociations* lpAssoc; + + TRACE("(0x%8lx,0x%8x,%p,%s,%p,%p)\n", cfFlags, str, hkAssoc, + debugstr_w(pszExtra), pszOut, pcchOut); + + lpAssoc = IQueryAssociations_Constructor(); + + if (!lpAssoc) + return E_OUTOFMEMORY; + + cfFlags &= SHLWAPI_DEF_ASSOCF; + hRet = IQueryAssociations_Init(lpAssoc, cfFlags, 0, hkAssoc, NULL); + + if (SUCCEEDED(hRet)) + hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra, + pszOut, pcchOut); + + IQueryAssociations_Release(lpAssoc); + return hRet; +} + +/************************************************************************* + * AssocQueryStringByKeyA [SHLWAPI.@] + * + * Get a file association string from the registry, given a starting key. + * + * PARAMS + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * str [I] Type of string to get + * hkAssoc [I] Key to search below + * pszExtra [I] Extra information about the string location + * pszOut [O] Destination for the association string + * pcchOut [O] Length of pszOut + * + * RETURNS + * Success: S_OK. pszOut contains the string, pcchOut contains its length. + * Failure: An HRESULT error code indicating the error. + */ +HRESULT WINAPI AssocQueryStringByKeyA(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc, + LPCSTR pszExtra, LPSTR pszOut, + DWORD *pcchOut) +{ + WCHAR szExtraW[MAX_PATH], *lpszExtraW; + WCHAR szReturnW[MAX_PATH], *lpszReturnW = szReturnW; + HRESULT hRet = E_OUTOFMEMORY; + + TRACE("(0x%8lx,0x%8x,%p,%s,%p,%p)\n", cfFlags, str, hkAssoc, + debugstr_a(pszExtra), pszOut, pcchOut); + + if (!pcchOut) + hRet = E_INVALIDARG; + else if (SHLWAPI_ParamAToW(pszExtra, szExtraW, MAX_PATH, &lpszExtraW)) + { + DWORD dwLenOut = *pcchOut; + if (dwLenOut >= MAX_PATH) + lpszReturnW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, + (dwLenOut + 1) * sizeof(WCHAR)); + + if (lpszReturnW) + { + hRet = AssocQueryStringByKeyW(cfFlags, str, hkAssoc, lpszExtraW, + lpszReturnW, &dwLenOut); + + if (SUCCEEDED(hRet)) + WideCharToMultiByte(CP_ACP,0,szReturnW,-1,pszOut,dwLenOut,0,0); + *pcchOut = dwLenOut; + + if (lpszReturnW != szReturnW) + HeapFree(GetProcessHeap(), 0, lpszReturnW); + } + } + + if (lpszExtraW && lpszExtraW != szExtraW) + HeapFree(GetProcessHeap(), 0, lpszExtraW); + return hRet; +} + + +/************************************************************************** + * AssocIsDangerous (SHLWAPI.@) + */ +HRESULT WINAPI AssocIsDangerous( ASSOCSTR str ) +{ + FIXME("%08x\n", str); + return S_FALSE; +} + +/************************************************************************** + * IQueryAssociations_QueryInterface {SHLWAPI} + * + * See IUnknown_QueryInterface. + */ +static HRESULT WINAPI IQueryAssociations_fnQueryInterface( + IQueryAssociations* iface, + REFIID riid, + LPVOID *ppvObj) +{ + ICOM_THIS(IQueryAssociationsImpl, iface); + + TRACE("(%p,%s,%p)\n",This, debugstr_guid(riid), ppvObj); + + *ppvObj = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IQueryAssociations)) + { + *ppvObj = (IQueryAssociations*)This; + + IQueryAssociations_AddRef((IQueryAssociations*)*ppvObj); + TRACE("Returning IQueryAssociations (%p)\n", *ppvObj); + return S_OK; + } + TRACE("Returning E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +/************************************************************************** + * IQueryAssociations_AddRef {SHLWAPI} + * + * See IUnknown_AddRef. + */ +static ULONG WINAPI IQueryAssociations_fnAddRef(IQueryAssociations *iface) +{ + ICOM_THIS(IQueryAssociationsImpl,iface); + + TRACE("(%p)->(ref before=%lu)\n",This, This->ref); + + return InterlockedIncrement(&This->ref); +} + +/************************************************************************** + * IQueryAssociations_Release {SHLWAPI} + * + * See IUnknown_Release. + */ +static ULONG WINAPI IQueryAssociations_fnRelease(IQueryAssociations *iface) +{ + ICOM_THIS(IQueryAssociationsImpl,iface); + ULONG ulRet; + + TRACE("(%p)->(ref before=%lu)\n",This, This->ref); + + if (!(ulRet = InterlockedDecrement(&This->ref))) + { + TRACE("Destroying IQueryAssociations (%p)\n", This); + HeapFree(GetProcessHeap(), 0, This); + } + return ulRet; +} + +/************************************************************************** + * IQueryAssociations_Init {SHLWAPI} + * + * Initialise an IQueryAssociations object. + * + * PARAMS + * iface [I] IQueryAssociations interface to initialise + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * pszAssoc [I] String for the root key name, or NULL if hkProgid is given + * hkeyProgid [I] Handle for the root key, or NULL if pszAssoc is given + * hWnd [I] Reserved, must be NULL. + * + * RETURNS + * Success: S_OK. iface is initialised with the parameters given. + * Failure: An HRESULT error code indicating the error. + */ +static HRESULT WINAPI IQueryAssociations_fnInit( + IQueryAssociations *iface, + ASSOCF cfFlags, + LPCWSTR pszAssoc, + HKEY hkeyProgid, + HWND hWnd) +{ + static const WCHAR szProgID[] = {'P','r','o','g','I','D',0}; + ICOM_THIS(IQueryAssociationsImpl,iface); + HRESULT hr; + + TRACE("(%p)->(%ld,%s,%p,%p)\n", iface, + cfFlags, + debugstr_w(pszAssoc), + hkeyProgid, + hWnd); + if (hWnd != NULL) + FIXME("hwnd != NULL not supported\n"); + if (cfFlags != 0) + FIXME("unsupported flags: %lx\n", cfFlags); + if (pszAssoc != NULL) + { + hr = RegOpenKeyExW(HKEY_CLASSES_ROOT, + pszAssoc, + 0, + KEY_READ, + &This->hkeySource); + if (FAILED(hr)) + return HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION); + /* if this is not a prog id */ + if ((*pszAssoc == '.') || (*pszAssoc == '{')) + { + hr = RegOpenKeyExW(This->hkeySource, + szProgID, + 0, + KEY_READ, + &This->hkeyProgID); + if (FAILED(hr)) + FIXME("Don't know what to return\n"); + } + else + This->hkeyProgID = This->hkeySource; + return S_OK; + } + else if (hkeyProgid != NULL) + { + This->hkeyProgID = hkeyProgid; + return S_OK; + } + else + return E_FAIL; +} + +/************************************************************************** + * IQueryAssociations_GetString {SHLWAPI} + * + * Get a file association string from the registry. + * + * PARAMS + * iface [I] IQueryAssociations interface to query + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * str [I] Type of string to get (ASSOCSTR enum from "shlwapi.h") + * pszExtra [I] Extra information about the string location + * pszOut [O] Destination for the association string + * pcchOut [I/O] Length of pszOut + * + * RETURNS + * Success: S_OK. pszOut contains the string, pcchOut contains its length. + * Failure: An HRESULT error code indicating the error. + */ +static HRESULT WINAPI IQueryAssociations_fnGetString( + IQueryAssociations *iface, + ASSOCF cfFlags, + ASSOCSTR str, + LPCWSTR pszExtra, + LPWSTR pszOut, + DWORD *pcchOut) +{ + ICOM_THIS(IQueryAssociationsImpl, iface); + + FIXME("(%p,0x%8lx,0x%8x,%s,%p,%p)-stub!\n", This, cfFlags, str, + debugstr_w(pszExtra), pszOut, pcchOut); + return E_NOTIMPL; +} + +/************************************************************************** + * IQueryAssociations_GetKey {SHLWAPI} + * + * Get a file association key from the registry. + * + * PARAMS + * iface [I] IQueryAssociations interface to query + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * assockey [I] Type of key to get (ASSOCKEY enum from "shlwapi.h") + * pszExtra [I] Extra information about the key location + * phkeyOut [O] Destination for the association key + * + * RETURNS + * Success: S_OK. phkeyOut contains a handle to the key. + * Failure: An HRESULT error code indicating the error. + */ +static HRESULT WINAPI IQueryAssociations_fnGetKey( + IQueryAssociations *iface, + ASSOCF cfFlags, + ASSOCKEY assockey, + LPCWSTR pszExtra, + HKEY *phkeyOut) +{ + ICOM_THIS(IQueryAssociationsImpl, iface); + + FIXME("(%p,0x%8lx,0x%8x,%s,%p)-stub!\n", This, cfFlags, assockey, + debugstr_w(pszExtra), phkeyOut); + return E_NOTIMPL; +} + +/************************************************************************** + * IQueryAssociations_GetData {SHLWAPI} + * + * Get the data for a file association key from the registry. + * + * PARAMS + * iface [I] IQueryAssociations interface to query + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * assocdata [I] Type of data to get (ASSOCDATA enum from "shlwapi.h") + * pszExtra [I] Extra information about the data location + * pvOut [O] Destination for the association key + * pcbOut [I/O] Size of pvOut + * + * RETURNS + * Success: S_OK. pszOut contains the data, pcbOut contains its length. + * Failure: An HRESULT error code indicating the error. + */ +static HRESULT WINAPI IQueryAssociations_fnGetData( + IQueryAssociations *iface, + ASSOCF cfFlags, + ASSOCDATA assocdata, + LPCWSTR pszExtra, + LPVOID pvOut, + DWORD *pcbOut) +{ + ICOM_THIS(IQueryAssociationsImpl, iface); + + FIXME("(%p,0x%8lx,0x%8x,%s,%p,%p)-stub!\n", This, cfFlags, assocdata, + debugstr_w(pszExtra), pvOut, pcbOut); + return E_NOTIMPL; +} + +/************************************************************************** + * IQueryAssociations_GetEnum {SHLWAPI} + * + * Not yet implemented in native Win32. + * + * PARAMS + * iface [I] IQueryAssociations interface to query + * cfFlags [I] ASSOCF_ flags from "shlwapi.h" + * assocenum [I] Type of enum to get (ASSOCENUM enum from "shlwapi.h") + * pszExtra [I] Extra information about the enum location + * riid [I] REFIID to look for + * ppvOut [O] Destination for the interface. + * + * RETURNS + * Success: S_OK. + * Failure: An HRESULT error code indicating the error. + * + * NOTES + * Presumably this function returns an enumerator object. + */ +static HRESULT WINAPI IQueryAssociations_fnGetEnum( + IQueryAssociations *iface, + ASSOCF cfFlags, + ASSOCENUM assocenum, + LPCWSTR pszExtra, + REFIID riid, + LPVOID *ppvOut) +{ + ICOM_THIS(IQueryAssociationsImpl, iface); + + FIXME("(%p,0x%8lx,0x%8x,%s,%s,%p)-stub!\n", This, cfFlags, assocenum, + debugstr_w(pszExtra), debugstr_guid(riid), ppvOut); + return E_NOTIMPL; +} + +static struct ICOM_VTABLE(IQueryAssociations) IQueryAssociations_vtbl = +{ + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IQueryAssociations_fnQueryInterface, + IQueryAssociations_fnAddRef, + IQueryAssociations_fnRelease, + IQueryAssociations_fnInit, + IQueryAssociations_fnGetString, + IQueryAssociations_fnGetKey, + IQueryAssociations_fnGetData, + IQueryAssociations_fnGetEnum +}; diff --git a/reactos/lib/shlwapi/clist.c b/reactos/lib/shlwapi/clist.c new file mode 100644 index 00000000000..9ea35584f8c --- /dev/null +++ b/reactos/lib/shlwapi/clist.c @@ -0,0 +1,453 @@ +/* + * SHLWAPI DataBlock List functions + * + * 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 +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* DataBlock list element (ordinals 17-22) */ +typedef struct tagSHLWAPI_CLIST +{ + ULONG ulSize; /* Size of this list element and its data */ + ULONG ulId; /* If 0xFFFFFFFF, The real element follows */ + /* Item data (or a contained SHLWAPI_CLIST) follows... */ +} SHLWAPI_CLIST, *LPSHLWAPI_CLIST; + +typedef const SHLWAPI_CLIST* LPCSHLWAPI_CLIST; + +/* ulId for contained SHLWAPI_CLIST items */ +#define CLIST_ID_CONTAINER (~0UL) + +HRESULT WINAPI SHAddDataBlock(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST); + +/************************************************************************* + * NextItem + * + * Internal helper: move a DataBlock pointer to the next item. + */ +inline static LPSHLWAPI_CLIST NextItem(LPCSHLWAPI_CLIST lpList) +{ + const char* address = (char*)lpList; + address += lpList->ulSize; + return (LPSHLWAPI_CLIST)address; +} + +/************************************************************************* + * @ [SHLWAPI.17] + * + * Write a DataBlock list to an IStream object. + * + * PARAMS + * lpStream [I] IStream object to write the list to + * lpList [I] List of items to write + * + * RETURNS + * Success: S_OK. The object is written to the stream. + * Failure: An HRESULT error code + * + * NOTES + * Ordinals 17,18,19,20,21 and 22 are related and together provide a compact + * list structure (a "DataBlock List"), which may be stored and retrieved from + * an IStream object. + * + * The exposed API consists of: + * + * - SHWriteDataBlockList() - Write a DataBlock list to a stream, + * - SHReadDataBlockList() - Read and create a list from a stream, + * - SHFreeDataBlockList() - Free a list, + * - SHAddDataBlock() - Insert a new item into a list, + * - SHRemoveDataBlock() - Remove an item from a list, + * - SHFindDataBlock() - Find an item in a list. + * + * The DataBlock list is stored packed into a memory array. Each element has a + * size and an associated ID. Elements must be less than 64k if the list is + * to be subsequently read from a stream. + * + * Elements are aligned on DWORD boundaries. If an elements data size is not + * a DWORD size multiple, the element is wrapped by inserting a surrounding + * element with an Id of 0xFFFFFFFF, and size sufficient to pad to a DWORD boundary. + * + * These functions are slow for large objects and long lists. + */ +HRESULT WINAPI SHWriteDataBlockList(IStream* lpStream, LPSHLWAPI_CLIST lpList) +{ + ULONG ulSize; + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p)\n", lpStream, lpList); + + if(lpList) + { + while (lpList->ulSize) + { + LPSHLWAPI_CLIST lpItem = lpList; + + if(lpList->ulId == CLIST_ID_CONTAINER) + lpItem++; + + hRet = IStream_Write(lpStream,lpItem,lpItem->ulSize,&ulSize); + if (FAILED(hRet)) + return hRet; + + if(lpItem->ulSize != ulSize) + return STG_E_MEDIUMFULL; + + lpList = NextItem(lpList); + } + } + + if(SUCCEEDED(hRet)) + { + ULONG ulDummy; + ulSize = 0; + + /* Write a terminating list entry with zero size */ + hRet = IStream_Write(lpStream, &ulSize,sizeof(ulSize),&ulDummy); + } + + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.18] + * + * Read and create a DataBlock list from an IStream object. + * + * PARAMS + * lpStream [I] Stream to read the list from + * lppList [0] Pointer to receive the new List + * + * RETURNS + * Success: S_OK + * Failure: An HRESULT error code + * + * NOTES + * When read from a file, list objects are limited in size to 64k. + * See SHWriteDataBlockList. + */ +HRESULT WINAPI SHReadDataBlockList(IStream* lpStream, LPSHLWAPI_CLIST* lppList) +{ + SHLWAPI_CLIST bBuff[128]; /* Temporary storage for new list item */ + ULONG ulBuffSize = sizeof(bBuff); + LPSHLWAPI_CLIST pItem = bBuff; + ULONG ulRead, ulSize; + HRESULT hRet = S_OK; + + TRACE("(%p,%p)\n", lpStream, lppList); + + if(*lppList) + { + /* Free any existing list */ + LocalFree((HLOCAL)*lppList); + *lppList = NULL; + } + + do + { + /* Read the size of the next item */ + hRet = IStream_Read(lpStream, &ulSize,sizeof(ulSize),&ulRead); + + if(FAILED(hRet) || ulRead != sizeof(ulSize) || !ulSize) + break; /* Read failed or read zero size (the end of the list) */ + + if(ulSize > 0xFFFF) + { + LARGE_INTEGER liZero; + ULARGE_INTEGER ulPos; + + liZero.QuadPart = 0; + + /* Back the stream up; this object is too big for the list */ + if(SUCCEEDED(IStream_Seek(lpStream, liZero, STREAM_SEEK_CUR, &ulPos))) + { + liZero.QuadPart = ulPos.QuadPart - sizeof(ULONG); + IStream_Seek(lpStream, liZero, STREAM_SEEK_SET, NULL); + } + break; + } + else if (ulSize >= sizeof(SHLWAPI_CLIST)) + { + /* Add this new item to the list */ + if(ulSize > ulBuffSize) + { + /* We need more buffer space, allocate it */ + LPSHLWAPI_CLIST lpTemp; + + if (pItem == bBuff) + lpTemp = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT, ulSize); + else + lpTemp = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)pItem, ulSize, + LMEM_ZEROINIT|LMEM_MOVEABLE); + + if(!lpTemp) + { + hRet = E_OUTOFMEMORY; + break; + } + ulBuffSize = ulSize; + pItem = lpTemp; + } + + pItem->ulSize = ulSize; + ulSize -= sizeof(pItem->ulSize); /* already read this member */ + + /* Read the item Id and data */ + hRet = IStream_Read(lpStream, &pItem->ulId, ulSize, &ulRead); + + if(FAILED(hRet) || ulRead != ulSize) + break; + + SHAddDataBlock(lppList, pItem); /* Insert Item */ + } + } while(1); + + /* If we allocated space, free it */ + if(pItem != bBuff) + LocalFree((HLOCAL)pItem); + + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.19] + * + * Free a DataBlock list. + * + * PARAMS + * lpList [I] List to free + * + * RETURNS + * Nothing. + * + * NOTES + * See SHWriteDataBlockList. + */ +VOID WINAPI SHFreeDataBlockList(LPSHLWAPI_CLIST lpList) +{ + TRACE("(%p)\n", lpList); + + if (lpList) + LocalFree((HLOCAL)lpList); +} + +/************************************************************************* + * @ [SHLWAPI.20] + * + * Insert a new item into a DataBlock list. + * + * PARAMS + * lppList [0] Pointer to the List + * lpNewItem [I] The new item to add to the list + * + * RETURNS + * Success: S_OK. The item is added to the list. + * Failure: An HRESULT error code. + * + * NOTES + * If the size of the element to be inserted is less than the size of a + * SHLWAPI_CLIST node, or the Id for the item is CLIST_ID_CONTAINER, + * the call returns S_OK but does not actually add the element. + * See SHWriteDataBlockList. + */ +HRESULT WINAPI SHAddDataBlock(LPSHLWAPI_CLIST* lppList, LPCSHLWAPI_CLIST lpNewItem) +{ + LPSHLWAPI_CLIST lpInsertAt = NULL; + ULONG ulSize; + + TRACE("(%p,%p)\n", lppList, lpNewItem); + + if(!lppList || !lpNewItem ) + return E_INVALIDARG; + + if (lpNewItem->ulSize < sizeof(SHLWAPI_CLIST) || + lpNewItem->ulId == CLIST_ID_CONTAINER) + return S_OK; + + ulSize = lpNewItem->ulSize; + + if(ulSize & 0x3) + { + /* Tune size to a ULONG boundary, add space for container element */ + ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(SHLWAPI_CLIST); + TRACE("Creating container item, new size = %ld\n", ulSize); + } + + if(!*lppList) + { + /* An empty list. Allocate space for terminal ulSize also */ + *lppList = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT, + ulSize + sizeof(ULONG)); + lpInsertAt = *lppList; + } + else + { + /* Append to the end of the list */ + ULONG ulTotalSize = 0; + LPSHLWAPI_CLIST lpIter = *lppList; + + /* Iterate to the end of the list, calculating the total size */ + while (lpIter->ulSize) + { + ulTotalSize += lpIter->ulSize; + lpIter = NextItem(lpIter); + } + + /* Increase the size of the list */ + lpIter = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList, + ulTotalSize + ulSize+sizeof(ULONG), + LMEM_ZEROINIT | LMEM_MOVEABLE); + if(lpIter) + { + *lppList = lpIter; + lpInsertAt = (LPSHLWAPI_CLIST)((char*)lpIter + ulTotalSize); /* At end */ + } + } + + if(lpInsertAt) + { + /* Copy in the new item */ + LPSHLWAPI_CLIST lpDest = lpInsertAt; + + if(ulSize != lpNewItem->ulSize) + { + lpInsertAt->ulSize = ulSize; + lpInsertAt->ulId = CLIST_ID_CONTAINER; + lpDest++; + } + memcpy(lpDest, lpNewItem, lpNewItem->ulSize); + + /* Terminate the list */ + lpInsertAt = NextItem(lpInsertAt); + lpInsertAt->ulSize = 0; + + return lpNewItem->ulSize; + } + return S_OK; +} + +/************************************************************************* + * @ [SHLWAPI.21] + * + * Remove an item from a DataBlock list. + * + * PARAMS + * lppList [O] List to remove the item from + * ulId [I] Id of item to remove + * + * RETURNS + * Success: TRUE. + * Failure: FALSE, If any parameters are invalid, or the item was not found. + * + * NOTES + * See SHWriteDataBlockList. + */ +BOOL WINAPI SHRemoveDataBlock(LPSHLWAPI_CLIST* lppList, ULONG ulId) +{ + LPSHLWAPI_CLIST lpList = 0; + LPSHLWAPI_CLIST lpItem = NULL; + LPSHLWAPI_CLIST lpNext; + ULONG ulNewSize; + + TRACE("(%p,%ld)\n", lppList, ulId); + + if(lppList && (lpList = *lppList)) + { + /* Search for item in list */ + while (lpList->ulSize) + { + if(lpList->ulId == ulId || + (lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId)) + { + lpItem = lpList; /* Found */ + break; + } + lpList = NextItem(lpList); + } + } + + if(!lpItem) + return FALSE; + + lpList = lpNext = NextItem(lpItem); + + /* Locate the end of the list */ + while (lpList->ulSize) + lpList = NextItem(lpList); + + /* Resize the list */ + ulNewSize = LocalSize((HLOCAL)*lppList) - lpItem->ulSize; + + /* Copy following elements over lpItem */ + memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG)); + + if(ulNewSize <= sizeof(ULONG)) + { + LocalFree((HLOCAL)*lppList); + *lppList = NULL; /* Removed the last element */ + } + else + { + lpList = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList, ulNewSize, + LMEM_ZEROINIT|LMEM_MOVEABLE); + if(lpList) + *lppList = lpList; + } + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.22] + * + * Find an item in a DataBlock list. + * + * PARAMS + * lpList [I] List to search + * ulId [I] Id of item to find + * + * RETURNS + * Success: A pointer to the list item found + * Failure: NULL + * + * NOTES + * See SHWriteDataBlockList. + */ +LPSHLWAPI_CLIST WINAPI SHFindDataBlock(LPSHLWAPI_CLIST lpList, ULONG ulId) +{ + TRACE("(%p,%ld)\n", lpList, ulId); + + if(lpList) + { + while(lpList->ulSize) + { + if(lpList->ulId == ulId) + return lpList; /* Matched */ + else if(lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId) + return lpList + 1; /* Contained item matches */ + + lpList = NextItem(lpList); + } + } + return NULL; +} diff --git a/reactos/lib/shlwapi/istream.c b/reactos/lib/shlwapi/istream.c new file mode 100644 index 00000000000..cea294e30b0 --- /dev/null +++ b/reactos/lib/shlwapi/istream.c @@ -0,0 +1,683 @@ +/* + * SHLWAPI IStream functions + * + * 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 +#include + +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winnls.h" +#define NO_SHLWAPI_REG +#define NO_SHLWAPI_PATH +#include "shlwapi.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* Layout of ISHFileStream object */ +typedef struct +{ + ICOM_VFIELD(IStream); + ULONG ref; + HANDLE hFile; + DWORD dwMode; + LPOLESTR lpszPath; + DWORD type; + DWORD grfStateBits; +} ISHFileStream; + +static HRESULT WINAPI IStream_fnCommit(IStream*,DWORD); + + +/************************************************************************** +* IStream_fnQueryInterface +*/ +static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, REFIID riid, LPVOID *ppvObj) +{ + ICOM_THIS(ISHFileStream, iface); + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj); + + *ppvObj = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IStream)) + { + *ppvObj = This; + + IStream_AddRef((IStream*)*ppvObj); + return S_OK; + } + return E_NOINTERFACE; +} + +/************************************************************************** +* IStream_fnAddRef +*/ +static ULONG WINAPI IStream_fnAddRef(IStream *iface) +{ + ICOM_THIS(ISHFileStream, iface); + + TRACE("(%p)\n", This); + return InterlockedIncrement(&This->ref); +} + +/************************************************************************** +* IStream_fnRelease +*/ +static ULONG WINAPI IStream_fnRelease(IStream *iface) +{ + ICOM_THIS(ISHFileStream, iface); + ULONG ulRet; + + TRACE("(%p)\n", This); + + if (!(ulRet = InterlockedDecrement(&This->ref))) + { + IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ + LocalFree((HLOCAL)This->lpszPath); + CloseHandle(This->hFile); + HeapFree(GetProcessHeap(), 0, This); + } + return ulRet; +} + +/************************************************************************** + * IStream_fnRead + */ +static HRESULT WINAPI IStream_fnRead(IStream *iface, void* pv, ULONG cb, ULONG* pcbRead) +{ + ICOM_THIS(ISHFileStream, iface); + HRESULT hRet = S_OK; + DWORD dwRead = 0; + + TRACE("(%p,%p,0x%08lx,%p)\n", This, pv, cb, pcbRead); + + if (!pv) + hRet = STG_E_INVALIDPOINTER; + else if (!ReadFile(This->hFile, pv, cb, &dwRead, NULL)) + { + hRet = (HRESULT)GetLastError(); + if(hRet > 0) + hRet = HRESULT_FROM_WIN32(hRet); + } + if (pcbRead) + *pcbRead = dwRead; + return hRet; +} + +/************************************************************************** + * IStream_fnWrite + */ +static HRESULT WINAPI IStream_fnWrite(IStream *iface, const void* pv, ULONG cb, ULONG* pcbWritten) +{ + ICOM_THIS(ISHFileStream, iface); + HRESULT hRet = S_OK; + DWORD dwWritten = 0; + + TRACE("(%p,%p,0x%08lx,%p)\n", This, pv, cb, pcbWritten); + + if (!pv) + hRet = STG_E_INVALIDPOINTER; + else if (!(This->dwMode & STGM_WRITE)) + hRet = E_FAIL; + else if (!WriteFile(This->hFile, pv, cb, &dwWritten, NULL)) + { + hRet = (HRESULT)GetLastError(); + if(hRet > 0) + hRet = HRESULT_FROM_WIN32(hRet); + } + if (pcbWritten) + *pcbWritten = dwWritten; + return hRet; +} + +/************************************************************************** + * IStream_fnSeek + */ +static HRESULT WINAPI IStream_fnSeek(IStream *iface, LARGE_INTEGER dlibMove, + DWORD dwOrigin, ULARGE_INTEGER* pNewPos) +{ + ICOM_THIS(ISHFileStream, iface); + DWORD dwPos; + + TRACE("(%p,%ld,%ld,%p)\n", This, dlibMove.u.LowPart, dwOrigin, pNewPos); + + IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ + dwPos = SetFilePointer(This->hFile, dlibMove.u.LowPart, NULL, dwOrigin); + + if (pNewPos) + { + pNewPos->u.HighPart = 0; + pNewPos->u.LowPart = dwPos; + } + return S_OK; +} + +/************************************************************************** + * IStream_fnSetSize + */ +static HRESULT WINAPI IStream_fnSetSize(IStream *iface, ULARGE_INTEGER libNewSize) +{ +#ifndef NDEBUG + ICOM_THIS(ISHFileStream, iface); +#endif + + TRACE("(%p,%ld)\n", This, libNewSize.s.LowPart); + IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnCopyTo + */ +static HRESULT WINAPI IStream_fnCopyTo(IStream *iface, IStream* pstm, ULARGE_INTEGER cb, + ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) +{ +#ifndef NDEBUG + ICOM_THIS(ISHFileStream, iface); +#endif + char copyBuff[1024]; + ULONGLONG ulSize; + HRESULT hRet = S_OK; + + TRACE("(%p,%p,%ld,%p,%p)\n", This, pstm, cb.s.LowPart, pcbRead, pcbWritten); + + if (pcbRead) + pcbRead->QuadPart = 0; + if (pcbWritten) + pcbWritten->QuadPart = 0; + + if (!pstm) + return STG_E_INVALIDPOINTER; + + IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ + + /* Copy data */ + ulSize = cb.QuadPart; + while (ulSize) + { + ULONG ulLeft, ulAmt; + + ulLeft = ulSize > sizeof(copyBuff) ? sizeof(copyBuff) : ulSize; + + /* Read */ + hRet = IStream_fnRead(iface, copyBuff, ulLeft, &ulAmt); + if (pcbRead) + pcbRead->QuadPart += ulAmt; + if (FAILED(hRet) || ulAmt != ulLeft) + break; + + /* Write */ + hRet = IStream_fnWrite(pstm, copyBuff, ulLeft, &ulAmt); + if (pcbWritten) + pcbWritten->QuadPart += ulAmt; + if (FAILED(hRet) || ulAmt != ulLeft) + break; + + ulSize -= ulLeft; + } + return hRet; +} + +/************************************************************************** + * IStream_fnCommit + */ +static HRESULT WINAPI IStream_fnCommit(IStream *iface, DWORD grfCommitFlags) +{ +#ifndef NDEBUG + ICOM_THIS(ISHFileStream, iface); +#endif + + TRACE("(%p,%ld)\n", This, grfCommitFlags); + /* Currently unbuffered: This function is not needed */ + return S_OK; +} + +/************************************************************************** + * IStream_fnRevert + */ +static HRESULT WINAPI IStream_fnRevert(IStream *iface) +{ +#ifndef NDEBUG + ICOM_THIS(ISHFileStream, iface); +#endif + + TRACE("(%p)\n", This); + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnLockUnlockRegion + */ +static HRESULT WINAPI IStream_fnLockUnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, DWORD dwLockType) +{ +#ifndef NDEBUG + ICOM_THIS(ISHFileStream, iface); +#endif + TRACE("(%p,%ld,%ld,%ld)\n", This, libOffset.s.LowPart, cb.s.LowPart, dwLockType); + return E_NOTIMPL; +} + +/************************************************************************* + * IStream_fnStat + */ +static HRESULT WINAPI IStream_fnStat(IStream *iface, STATSTG* lpStat, + DWORD grfStatFlag) +{ + ICOM_THIS(ISHFileStream, iface); + BY_HANDLE_FILE_INFORMATION fi; + HRESULT hRet = S_OK; + + TRACE("(%p,%p,%ld)\n", This, lpStat, grfStatFlag); + + if (!grfStatFlag) + hRet = STG_E_INVALIDPOINTER; + else + { + memset(&fi, 0, sizeof(fi)); + GetFileInformationByHandle(This->hFile, &fi); + + if (grfStatFlag & STATFLAG_NONAME) + lpStat->pwcsName = NULL; + else + lpStat->pwcsName = StrDupW(This->lpszPath); + lpStat->type = This->type; + lpStat->cbSize.u.LowPart = fi.nFileSizeLow; + lpStat->cbSize.u.HighPart = fi.nFileSizeHigh; + lpStat->mtime = fi.ftLastWriteTime; + lpStat->ctime = fi.ftCreationTime; + lpStat->atime = fi.ftLastAccessTime; + lpStat->grfMode = This->dwMode; + lpStat->grfLocksSupported = 0; + memcpy(&lpStat->clsid, &IID_IStream, sizeof(CLSID)); + lpStat->grfStateBits = This->grfStateBits; + lpStat->reserved = 0; + } + return hRet; +} + +/************************************************************************* + * IStream_fnClone + */ +static HRESULT WINAPI IStream_fnClone(IStream *iface, IStream** ppstm) +{ +#ifndef NDEBUG + ICOM_THIS(ISHFileStream, iface); +#endif + + TRACE("(%p)\n",This); + if (ppstm) + *ppstm = NULL; + return E_NOTIMPL; +} + +static struct ICOM_VTABLE(IStream) SHLWAPI_fsVTable = +{ + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IStream_fnQueryInterface, + IStream_fnAddRef, + IStream_fnRelease, + IStream_fnRead, + IStream_fnWrite, + IStream_fnSeek, + IStream_fnSetSize, + IStream_fnCopyTo, + IStream_fnCommit, + IStream_fnRevert, + IStream_fnLockUnlockRegion, + IStream_fnLockUnlockRegion, + IStream_fnStat, + IStream_fnClone +}; + +/************************************************************************** + * IStream_Create + * + * Internal helper: Create and initialise a new file stream object. + */ +static IStream *IStream_Create(LPCWSTR lpszPath, HANDLE hFile, DWORD dwMode) +{ + ISHFileStream* fileStream; + + fileStream = (ISHFileStream*)HeapAlloc(GetProcessHeap(), 0, sizeof(ISHFileStream)); + + if (fileStream) + { + fileStream->lpVtbl = &SHLWAPI_fsVTable; + fileStream->ref = 1; + fileStream->hFile = hFile; + fileStream->dwMode = dwMode; + fileStream->lpszPath = StrDupW(lpszPath); + fileStream->type = 0; /* FIXME */ + fileStream->grfStateBits = 0; /* FIXME */ + } + TRACE ("Returning %p\n", fileStream); + return (IStream *)fileStream; +} + +/************************************************************************* + * SHCreateStreamOnFileEx [SHLWAPI.@] + * + * Create a stream on a file. + * + * PARAMS + * lpszPath [I] Path of file to create stream on + * dwMode [I] Mode to create stream in + * dwAttributes [I] Attributes of the file + * bCreate [I] Whether to create the file if it doesn't exist + * lpTemplate [I] Reserved, must be NULL + * lppStream [O] Destination for created stream + * + * RETURNS + * Success: S_OK. lppStream contains the new stream object + * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code + * + * NOTES + * This function is available in Unicode only. + */ +HRESULT WINAPI SHCreateStreamOnFileEx(LPCWSTR lpszPath, DWORD dwMode, + DWORD dwAttributes, BOOL bCreate, + IStream *lpTemplate, IStream **lppStream) +{ + DWORD dwAccess, dwShare, dwCreate; + HANDLE hFile; + + TRACE("(%s,%ld,0x%08lX,%d,%p,%p)\n", debugstr_w(lpszPath), dwMode, + dwAttributes, bCreate, lpTemplate, lppStream); + + if (!lpszPath || !lppStream || lpTemplate) + return E_INVALIDARG; + + *lppStream = NULL; + + if (dwMode & ~(STGM_WRITE|STGM_READWRITE|STGM_SHARE_DENY_NONE|STGM_SHARE_DENY_READ|STGM_CREATE)) + { + WARN("Invalid mode 0x%08lX\n", dwMode); + return E_INVALIDARG; + } + + /* Access */ + switch (dwMode & (STGM_WRITE|STGM_READWRITE)) + { + case STGM_READWRITE|STGM_WRITE: + case STGM_READWRITE: + dwAccess = GENERIC_READ|GENERIC_WRITE; + break; + case STGM_WRITE: + dwAccess = GENERIC_WRITE; + break; + default: + dwAccess = GENERIC_READ; + break; + } + + /* Sharing */ + switch (dwMode & STGM_SHARE_DENY_READ) + { + case STGM_SHARE_DENY_READ: + dwShare = FILE_SHARE_WRITE; + break; + case STGM_SHARE_DENY_WRITE: + dwShare = FILE_SHARE_READ; + break; + case STGM_SHARE_EXCLUSIVE: + dwShare = 0; + break; + default: + dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE; + } + + /* FIXME: Creation Flags, MSDN is fuzzy on the mapping... */ + if (dwMode == STGM_FAILIFTHERE) + dwCreate = bCreate ? CREATE_NEW : OPEN_EXISTING; + else if (dwMode & STGM_CREATE) + dwCreate = CREATE_ALWAYS; + else + dwCreate = OPEN_ALWAYS; + + /* Open HANDLE to file */ + hFile = CreateFileW(lpszPath, dwAccess, dwShare, NULL, dwCreate, + dwAttributes, 0); + + if(hFile == INVALID_HANDLE_VALUE) + { + HRESULT hRet = (HRESULT)GetLastError(); + if(hRet > 0) + hRet = HRESULT_FROM_WIN32(hRet); + return hRet; + } + + *lppStream = IStream_Create(lpszPath, hFile, dwMode); + + if(!*lppStream) + { + CloseHandle(hFile); + return E_OUTOFMEMORY; + } + return S_OK; +} + +/************************************************************************* + * SHCreateStreamOnFileW [SHLWAPI.@] + * + * See SHCreateStreamOnFileA. + */ +HRESULT WINAPI SHCreateStreamOnFileW(LPCWSTR lpszPath, DWORD dwMode, + IStream **lppStream) +{ + DWORD dwAttr; + + TRACE("(%s,%ld,%p)\n", debugstr_w(lpszPath), dwMode, lppStream); + + if (!lpszPath || !lppStream) + return E_INVALIDARG; + + dwAttr = GetFileAttributesW(lpszPath); + if (dwAttr == INVALID_FILE_ATTRIBUTES) + dwAttr = 0; + + return SHCreateStreamOnFileEx(lpszPath, dwMode|STGM_WRITE, dwAttr, + TRUE, NULL, lppStream); +} + +/************************************************************************* + * SHCreateStreamOnFileA [SHLWAPI.@] + * + * Create a stream on a file. + * + * PARAMS + * lpszPath [I] Path of file to create stream on + * dwMode [I] Mode to create stream in + * lppStream [O] Destination for created IStream object + * + * RETURNS + * Success: S_OK. lppStream contains the new IStream object + * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code + */ +HRESULT WINAPI SHCreateStreamOnFileA(LPCSTR lpszPath, DWORD dwMode, + IStream **lppStream) +{ + WCHAR szPath[MAX_PATH]; + + TRACE("(%s,%ld,%p)\n", debugstr_a(lpszPath), dwMode, lppStream); + + if (!lpszPath) + return E_INVALIDARG; + MultiByteToWideChar(0, 0, lpszPath, -1, szPath, MAX_PATH); + return SHCreateStreamOnFileW(szPath, dwMode, lppStream); +} + +/************************************************************************* + * @ [SHLWAPI.184] + * + * Call IStream_Read() on a stream. + * + * PARAMS + * lpStream [I] IStream object + * lpvDest [O] Destination for data read + * ulSize [I] Size of data to read + * + * RETURNS + * Success: S_OK. ulSize bytes have been read from the stream into lpvDest. + * Failure: An HRESULT error code, or E_FAIL if the read succeeded but the + * number of bytes read does not match. + */ +HRESULT WINAPI SHLWAPI_184(IStream *lpStream, LPVOID lpvDest, ULONG ulSize) +{ + ULONG ulRead; + HRESULT hRet; + + TRACE("(%p,%p,%ld)\n", lpStream, lpvDest, ulSize); + + hRet = IStream_Read(lpStream, lpvDest, ulSize, &ulRead); + + if (SUCCEEDED(hRet) && ulRead != ulSize) + hRet = E_FAIL; + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.166] + * + * Determine if a stream has 0 length. + * + * PARAMS + * lpStream [I] IStream object + * + * RETURNS + * TRUE: If the stream has 0 length + * FALSE: Otherwise. + */ +BOOL WINAPI SHIsEmptyStream(IStream *lpStream) +{ + STATSTG statstg; + BOOL bRet = TRUE; + + TRACE("(%p)\n", lpStream); + + memset(&statstg, 0, sizeof(statstg)); + + if(SUCCEEDED(IStream_Stat(lpStream, &statstg, 1))) + { + if(statstg.cbSize.QuadPart) + bRet = FALSE; /* Non-Zero */ + } + else + { + DWORD dwDummy; + + /* Try to read from the stream */ + if(SUCCEEDED(SHLWAPI_184(lpStream, &dwDummy, sizeof(dwDummy)))) + { + LARGE_INTEGER zero; + zero.QuadPart = 0; + + IStream_Seek(lpStream, zero, 0, NULL); + bRet = FALSE; /* Non-Zero */ + } + } + return bRet; +} + +/************************************************************************* + * @ [SHLWAPI.212] + * + * Call IStream_Write() on a stream. + * + * PARAMS + * lpStream [I] IStream object + * lpvSrc [I] Source for data to write + * ulSize [I] Size of data + * + * RETURNS + * Success: S_OK. ulSize bytes have been written to the stream from lpvSrc. + * Failure: An HRESULT error code, or E_FAIL if the write succeeded but the + * number of bytes written does not match. + */ +HRESULT WINAPI SHLWAPI_212(IStream *lpStream, LPCVOID lpvSrc, ULONG ulSize) +{ + ULONG ulWritten; + HRESULT hRet; + + TRACE("(%p,%p,%ld)\n", lpStream, lpvSrc, ulSize); + + hRet = IStream_Write(lpStream, lpvSrc, ulSize, &ulWritten); + + if (SUCCEEDED(hRet) && ulWritten != ulSize) + hRet = E_FAIL; + + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.213] + * + * Seek to the start of a stream. + * + * PARAMS + * lpStream [I] IStream object + * + * RETURNS + * Success: S_OK. The current position within the stream is updated + * Failure: An HRESULT error code. + */ +HRESULT WINAPI IStream_Reset(IStream *lpStream) +{ + LARGE_INTEGER zero; + TRACE("(%p)\n", lpStream); + zero.QuadPart = 0; + return IStream_Seek(lpStream, zero, 0, NULL); +} + +/************************************************************************* + * @ [SHLWAPI.214] + * + * Get the size of a stream. + * + * PARAMS + * lpStream [I] IStream object + * lpulSize [O] Destination for size + * + * RETURNS + * Success: S_OK. lpulSize contains the size of the stream. + * Failure: An HRESULT error code. + */ +HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize) +{ + STATSTG statstg; + HRESULT hRet; + + TRACE("(%p,%p)\n", lpStream, lpulSize); + + memset(&statstg, 0, sizeof(statstg)); + + hRet = IStream_Stat(lpStream, &statstg, 1); + + if (SUCCEEDED(hRet) && lpulSize) + *lpulSize = statstg.cbSize; + return hRet; +} diff --git a/reactos/lib/shlwapi/makefile b/reactos/lib/shlwapi/makefile new file mode 100644 index 00000000000..fb35afcff74 --- /dev/null +++ b/reactos/lib/shlwapi/makefile @@ -0,0 +1,66 @@ +PATH_TO_TOP = ../.. + +TARGET_TYPE = dynlink + +TARGET_NAME = shlwapi + +TARGET_CFLAGS = \ + -Wall \ + -fno-builtin + +# Compile definitions usage: +# __USE_W32API - Compilation with w32api headers +# __REACTOS__ - Compilation of Wine sources for ReactOS +# _WIN32_IE=0x600 - Internet Explorer 6 compatible defintions +# WINVER=0x501 - Windows XP definitions +# __need_offsetof - Force definition of macro offsetof in stddef.h +# +DEFINES = \ + -D_DISABLE_TIDENTS \ + -D__USE_W32API \ + -D__REACTOS__ \ + -D_WIN32_IE=0x600 \ + -D_WIN32_WINNT=0x501 \ + -DWINVER=0x501 \ + -DCOBJMACROS \ + -D__need_offsetof + +TARGET_CFLAGS += \ + $(DEFINES) \ + -I$(PATH_TO_TOP)/include/wine + +TARGET_RCFLAGS += -D__REACTOS__ -D_WIN32_IE=0x600 -D_WIN32_WINNT=0x501 + +TARGET_LFLAGS = -nostartfiles -nostdlib + +TARGET_SDKLIBS = libwine.a + +TARGET_BASE = 0x76120000 + +TARGET_NORC = yes + +TARGET_DEFONLY = yes + +TARGET_OBJECTS = \ + assoc.o \ + clist.o \ + istream.o \ + path.o \ + reg.o \ + regstream.o \ + shlwapi_main.o \ + string.o \ + thread.o \ + url.o \ + wsprintf.o \ + ordinal.o + +DEP_OBJECTS = $(TARGET_OBJECTS) + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk + +include $(TOOLS_PATH)/config.mk + +# include $(TOOLS_PATH)/depend.mk diff --git a/reactos/lib/shlwapi/ordinal.c b/reactos/lib/shlwapi/ordinal.c new file mode 100644 index 00000000000..d498d80eac5 --- /dev/null +++ b/reactos/lib/shlwapi/ordinal.c @@ -0,0 +1,4065 @@ +/* + * SHLWAPI ordinal functions + * + * Copyright 1997 Marcus Meissner + * 1998 Jürgen Schmied + * 2001-2003 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 +#include +#include + +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "windef.h" +#include "winbase.h" +#ifdef __REACTOS__ +#include "wingdi.h" +#endif +#include "winuser.h" +#include "winnls.h" +#include "ddeml.h" +#include "docobj.h" +#include "exdisp.h" +#include "shlguid.h" +#include "wingdi.h" +#include "shlobj.h" +#include "olectl.h" +#include "shellapi.h" +#include "commdlg.h" +#include "wine/unicode.h" +#include "servprov.h" +#include "winreg.h" +#include "winuser.h" +#include "wine/debug.h" +#include "shlwapi.h" +#ifdef __REACTOS__ +#include "shlobj.h" +#endif + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* Get a function pointer from a DLL handle */ +#define GET_FUNC(func, module, name, fail) \ + do { \ + if (!func) { \ + if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \ + func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \ + if (!func) return fail; \ + } \ + } while (0) + +/* DLL handles for late bound calls */ +extern HINSTANCE shlwapi_hInstance; +extern HMODULE SHLWAPI_hshell32; +extern HMODULE SHLWAPI_hwinmm; +extern HMODULE SHLWAPI_hcomdlg32; +extern HMODULE SHLWAPI_hcomctl32; +extern HMODULE SHLWAPI_hmpr; +extern HMODULE SHLWAPI_hmlang; +extern HMODULE SHLWAPI_hurlmon; +extern HMODULE SHLWAPI_hversion; + +extern DWORD SHLWAPI_ThreadRef_index; + +typedef HANDLE HSHARED; /* Shared memory */ + +/* following is GUID for IObjectWithSite::SetSite -- see _174 */ +static DWORD id1[4] = {0xfc4801a3, 0x11cf2ba9, 0xaa0029a2, 0x52733d00}; +/* following is GUID for IPersistMoniker::GetClassID -- see _174 */ +static DWORD id2[4] = {0x79eac9ee, 0x11cebaf9, 0xaa00828c, 0x0ba94b00}; + +/* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */ +typedef LPITEMIDLIST (WINAPI *fnpSHBrowseForFolderW)(LPBROWSEINFOW); +static fnpSHBrowseForFolderW pSHBrowseForFolderW; +typedef HRESULT (WINAPI *fnpConvertINetUnicodeToMultiByte)(LPDWORD,DWORD,LPCWSTR,LPINT,LPSTR,LPINT); +static fnpConvertINetUnicodeToMultiByte pConvertINetUnicodeToMultiByte; +typedef BOOL (WINAPI *fnpPlaySoundW)(LPCWSTR, HMODULE, DWORD); +static fnpPlaySoundW pPlaySoundW; +typedef DWORD (WINAPI *fnpSHGetFileInfoW)(LPCWSTR,DWORD,SHFILEINFOW*,UINT,UINT); +static fnpSHGetFileInfoW pSHGetFileInfoW; +typedef UINT (WINAPI *fnpDragQueryFileW)(HDROP, UINT, LPWSTR, UINT); +static fnpDragQueryFileW pDragQueryFileW; +typedef BOOL (WINAPI *fnpSHGetPathFromIDListW)(LPCITEMIDLIST, LPWSTR); +static fnpSHGetPathFromIDListW pSHGetPathFromIDListW; +typedef BOOL (WINAPI *fnpShellExecuteExW)(LPSHELLEXECUTEINFOW); +static fnpShellExecuteExW pShellExecuteExW; +typedef HICON (WINAPI *fnpSHFileOperationW)(LPSHFILEOPSTRUCTW); +static fnpSHFileOperationW pSHFileOperationW; +typedef UINT (WINAPI *fnpExtractIconExW)(LPCWSTR, INT,HICON *,HICON *, UINT); +static fnpExtractIconExW pExtractIconExW; +typedef BOOL (WINAPI *fnpSHGetNewLinkInfoW)(LPCWSTR, LPCWSTR, LPCWSTR, BOOL*, UINT); +static fnpSHGetNewLinkInfoW pSHGetNewLinkInfoW; +typedef HRESULT (WINAPI *fnpSHDefExtractIconW)(LPCWSTR, int, UINT, HICON*, HICON*, UINT); +static fnpSHDefExtractIconW pSHDefExtractIconW; +typedef HICON (WINAPI *fnpExtractIconW)(HINSTANCE, LPCWSTR, UINT); +static fnpExtractIconW pExtractIconW; +typedef BOOL (WINAPI *fnpGetSaveFileNameW)(LPOPENFILENAMEW); +static fnpGetSaveFileNameW pGetSaveFileNameW; +typedef DWORD (WINAPI *fnpWNetRestoreConnectionW)(HWND, LPWSTR); +static fnpWNetRestoreConnectionW pWNetRestoreConnectionW; +typedef DWORD (WINAPI *fnpWNetGetLastErrorW)(LPDWORD, LPWSTR, DWORD, LPWSTR, DWORD); +static fnpWNetGetLastErrorW pWNetGetLastErrorW; +typedef BOOL (WINAPI *fnpPageSetupDlgW)(LPPAGESETUPDLGW); +static fnpPageSetupDlgW pPageSetupDlgW; +typedef BOOL (WINAPI *fnpPrintDlgW)(LPPRINTDLGW); +static fnpPrintDlgW pPrintDlgW; +typedef BOOL (WINAPI *fnpGetOpenFileNameW)(LPOPENFILENAMEW); +static fnpGetOpenFileNameW pGetOpenFileNameW; +typedef DWORD (WINAPI *fnpGetFileVersionInfoSizeW)(LPCWSTR,LPDWORD); +static fnpGetFileVersionInfoSizeW pGetFileVersionInfoSizeW; +typedef BOOL (WINAPI *fnpGetFileVersionInfoW)(LPCWSTR,DWORD,DWORD,LPVOID); +static fnpGetFileVersionInfoW pGetFileVersionInfoW; +typedef WORD (WINAPI *fnpVerQueryValueW)(LPVOID,LPCWSTR,LPVOID*,UINT*); +static fnpVerQueryValueW pVerQueryValueW; +typedef BOOL (WINAPI *fnpCOMCTL32_417)(HDC,INT,INT,UINT,const RECT*,LPCWSTR,UINT,const INT*); +static fnpCOMCTL32_417 pCOMCTL32_417; +typedef HRESULT (WINAPI *fnpDllGetVersion)(DLLVERSIONINFO*); +static fnpDllGetVersion pDllGetVersion; +typedef HRESULT (WINAPI *fnpCreateFormatEnumerator)(UINT,FORMATETC*,IEnumFORMATETC**); +static fnpCreateFormatEnumerator pCreateFormatEnumerator; +typedef HRESULT (WINAPI *fnpRegisterFormatEnumerator)(LPBC,IEnumFORMATETC*,DWORD); +static fnpRegisterFormatEnumerator pRegisterFormatEnumerator; + +HRESULT WINAPI IUnknown_QueryService(IUnknown*,REFGUID,REFIID,LPVOID*); +HRESULT WINAPI SHInvokeCommand(HWND,IShellFolder*,LPCITEMIDLIST,BOOL); +HRESULT WINAPI CLSIDFromStringWrap(LPCWSTR,CLSID*); +BOOL WINAPI SHAboutInfoW(LPWSTR,DWORD); + +/* + NOTES: Most functions exported by ordinal seem to be superflous. + The reason for these functions to be there is to provide a wrapper + for unicode functions to provide these functions on systems without + unicode functions eg. win95/win98. Since we have such functions we just + call these. If running Wine with native DLL's, some late bound calls may + fail. However, its better to implement the functions in the forward DLL + and recommend the builtin rather than reimplementing the calls here! +*/ + +/************************************************************************* + * SHLWAPI_DupSharedHandle + * + * Internal implemetation of SHLWAPI_11. + */ +static +HSHARED WINAPI SHLWAPI_DupSharedHandle(HSHARED hShared, DWORD dwDstProcId, + DWORD dwSrcProcId, DWORD dwAccess, + DWORD dwOptions) +{ + HANDLE hDst, hSrc; + DWORD dwMyProcId = GetCurrentProcessId(); + HSHARED hRet = (HSHARED)NULL; + + TRACE("(%p,%ld,%ld,%08lx,%08lx)\n", (PVOID)hShared, dwDstProcId, dwSrcProcId, + dwAccess, dwOptions); + + /* Get dest process handle */ + if (dwDstProcId == dwMyProcId) + hDst = GetCurrentProcess(); + else + hDst = OpenProcess(PROCESS_DUP_HANDLE, 0, dwDstProcId); + + if (hDst) + { + /* Get src process handle */ + if (dwSrcProcId == dwMyProcId) + hSrc = GetCurrentProcess(); + else + hSrc = OpenProcess(PROCESS_DUP_HANDLE, 0, dwSrcProcId); + + if (hSrc) + { + /* Make handle available to dest process */ + if (!DuplicateHandle(hDst, (HANDLE)hShared, hSrc, &hRet, + dwAccess, 0, dwOptions | DUPLICATE_SAME_ACCESS)) + hRet = (HSHARED)NULL; + + if (dwSrcProcId != dwMyProcId) + CloseHandle(hSrc); + } + + if (dwDstProcId != dwMyProcId) + CloseHandle(hDst); + } + + TRACE("Returning handle %p\n", (PVOID)hRet); + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.7] + * + * Create a block of sharable memory and initialise it with data. + * + * PARAMS + * dwProcId [I] ID of process owning data + * lpvData [I] Pointer to data to write + * dwSize [I] Size of data + * + * RETURNS + * Success: A shared memory handle + * Failure: NULL + * + * NOTES + * Ordinals 7-11 provide a set of calls to create shared memory between a + * group of processes. The shared memory is treated opaquely in that its size + * is not exposed to clients who map it. This is accomplished by storing + * the size of the map as the first DWORD of mapped data, and then offsetting + * the view pointer returned by this size. + * + */ +HSHARED WINAPI SHAllocShared(DWORD dwProcId, DWORD dwSize, LPCVOID lpvData) +{ + HANDLE hMap; + LPVOID pMapped; + HSHARED hRet = (HSHARED)NULL; + + TRACE("(%ld,%p,%ld)\n", dwProcId, lpvData, dwSize); + + /* Create file mapping of the correct length */ + hMap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, FILE_MAP_READ, 0, + dwSize + sizeof(dwSize), NULL); + if (!hMap) + return hRet; + + /* Get a view in our process address space */ + pMapped = MapViewOfFile(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + + if (pMapped) + { + /* Write size of data, followed by the data, to the view */ + *((DWORD*)pMapped) = dwSize; + if (dwSize) + memcpy((char *) pMapped + sizeof(dwSize), lpvData, dwSize); + + /* Release view. All further views mapped will be opaque */ + UnmapViewOfFile(pMapped); + hRet = SHLWAPI_DupSharedHandle((HSHARED)hMap, dwProcId, + GetCurrentProcessId(), FILE_MAP_ALL_ACCESS, + DUPLICATE_SAME_ACCESS); + } + + CloseHandle(hMap); + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.8] + * + * Get a pointer to a block of shared memory from a shared memory handle. + * + * PARAMS + * hShared [I] Shared memory handle + * dwProcId [I] ID of process owning hShared + * + * RETURNS + * Success: A pointer to the shared memory + * Failure: NULL + * + */ +PVOID WINAPI SHLockShared(HSHARED hShared, DWORD dwProcId) +{ + HSHARED hDup; + LPVOID pMapped; + + TRACE("(%p %ld)\n", (PVOID)hShared, dwProcId); + + /* Get handle to shared memory for current process */ + hDup = SHLWAPI_DupSharedHandle(hShared, dwProcId, GetCurrentProcessId(), + FILE_MAP_ALL_ACCESS, 0); + /* Get View */ + pMapped = MapViewOfFile((HANDLE)hDup, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + CloseHandle(hDup); + + if (pMapped) + return (char *) pMapped + sizeof(DWORD); /* Hide size */ + return NULL; +} + +/************************************************************************* + * @ [SHLWAPI.9] + * + * Release a pointer to a block of shared memory. + * + * PARAMS + * lpView [I] Shared memory pointer + * + * RETURNS + * Success: TRUE + * Failure: FALSE + * + */ +BOOL WINAPI SHUnlockShared(LPVOID lpView) +{ + TRACE("(%p)\n", lpView); + return UnmapViewOfFile((char *) lpView - sizeof(DWORD)); /* Include size */ +} + +/************************************************************************* + * @ [SHLWAPI.10] + * + * Destroy a block of sharable memory. + * + * PARAMS + * hShared [I] Shared memory handle + * dwProcId [I] ID of process owning hShared + * + * RETURNS + * Success: TRUE + * Failure: FALSE + * + */ +BOOL WINAPI SHFreeShared(HSHARED hShared, DWORD dwProcId) +{ + HSHARED hClose; + + TRACE("(%p %ld)\n", (PVOID)hShared, dwProcId); + + /* Get a copy of the handle for our process, closing the source handle */ + hClose = SHLWAPI_DupSharedHandle(hShared, dwProcId, GetCurrentProcessId(), + FILE_MAP_ALL_ACCESS,DUPLICATE_CLOSE_SOURCE); + /* Close local copy */ + return CloseHandle((HANDLE)hClose); +} + +/************************************************************************* + * @ [SHLWAPI.11] + * + * Copy a sharable memory handle from one process to another. + * + * PARAMS + * hShared [I] Shared memory handle to duplicate + * dwDstProcId [I] ID of the process wanting the duplicated handle + * dwSrcProcId [I] ID of the process owning hShared + * dwAccess [I] Desired DuplicateHandle() access + * dwOptions [I] Desired DuplicateHandle() options + * + * RETURNS + * Success: A handle suitable for use by the dwDstProcId process. + * Failure: A NULL handle. + * + */ +HSHARED WINAPI SHMapHandle(HSHARED hShared, DWORD dwDstProcId, DWORD dwSrcProcId, + DWORD dwAccess, DWORD dwOptions) +{ + HSHARED hRet; + + hRet = SHLWAPI_DupSharedHandle(hShared, dwDstProcId, dwSrcProcId, + dwAccess, dwOptions); + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.13] + * + * Create and register a clipboard enumerator for a web browser. + * + * PARAMS + * lpBC [I] Binding context + * lpUnknown [I] An object exposing the IWebBrowserApp interface + * + * RETURNS + * Success: S_OK. + * Failure: An HRESULT error code. + * + * NOTES + * The enumerator is stored as a property of the web browser. If it does not + * yet exist, it is created and set before being registered. + */ +HRESULT WINAPI RegisterDefaultAcceptHeaders(LPBC lpBC, IUnknown *lpUnknown) +{ + static const WCHAR szProperty[] = { '{','D','0','F','C','A','4','2','0', + '-','D','3','F','5','-','1','1','C','F', '-','B','2','1','1','-','0', + '0','A','A','0','0','4','A','E','8','3','7','}','\0' }; + IEnumFORMATETC* pIEnumFormatEtc = NULL; + VARIANTARG var; + HRESULT hRet; + IWebBrowserApp* pBrowser = NULL; + + TRACE("(%p, %p)\n", lpBC, lpUnknown); + + /* Get An IWebBrowserApp interface from lpUnknown */ + hRet = IUnknown_QueryService(lpUnknown, &IID_IWebBrowserApp, &IID_IWebBrowserApp, (PVOID)&pBrowser); + if (FAILED(hRet) || !pBrowser) + return E_NOINTERFACE; + + V_VT(&var) = VT_EMPTY; + + /* The property we get is the browsers clipboard enumerator */ + hRet = IWebBrowserApp_GetProperty(pBrowser, (BSTR)szProperty, &var); + if (FAILED(hRet)) + return hRet; + + if (V_VT(&var) == VT_EMPTY) + { + /* Iterate through accepted documents and RegisterClipBoardFormatA() them */ + char szKeyBuff[128], szValueBuff[128]; + DWORD dwKeySize, dwValueSize, dwRet = 0, dwCount = 0, dwNumValues, dwType; + FORMATETC* formatList, *format; + HKEY hDocs; + + TRACE("Registering formats and creating IEnumFORMATETC instance\n"); + + if (!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\Current" + "Version\\Internet Settings\\Accepted Documents", &hDocs)) + return E_FAIL; + + /* Get count of values in key */ + while (!dwRet) + { + dwKeySize = sizeof(szKeyBuff); + dwRet = RegEnumValueA(hDocs,dwCount,szKeyBuff,&dwKeySize,0,&dwType,0,0); + dwCount++; + } + + dwNumValues = dwCount; + + /* Note: dwCount = number of items + 1; The extra item is the end node */ + format = formatList = HeapAlloc(GetProcessHeap(), 0, dwCount * sizeof(FORMATETC)); + if (!formatList) + return E_OUTOFMEMORY; + + if (dwNumValues > 1) + { + dwRet = 0; + dwCount = 0; + + dwNumValues--; + + /* Register clipboard formats for the values and populate format list */ + while(!dwRet && dwCount < dwNumValues) + { + dwKeySize = sizeof(szKeyBuff); + dwValueSize = sizeof(szValueBuff); + dwRet = RegEnumValueA(hDocs, dwCount, szKeyBuff, &dwKeySize, 0, &dwType, + (PBYTE)szValueBuff, &dwValueSize); + if (!dwRet) + return E_FAIL; + + format->cfFormat = RegisterClipboardFormatA(szValueBuff); + format->ptd = NULL; + format->dwAspect = 1; + format->lindex = 4; + format->tymed = -1; + + format++; + dwCount++; + } + } + + /* Terminate the (maybe empty) list, last entry has a cfFormat of 0 */ + format->cfFormat = 0; + format->ptd = NULL; + format->dwAspect = 1; + format->lindex = 4; + format->tymed = -1; + + /* Create a clipboard enumerator */ + GET_FUNC(pCreateFormatEnumerator, urlmon, "CreateFormatEnumerator", E_FAIL); + hRet = pCreateFormatEnumerator(dwNumValues, formatList, &pIEnumFormatEtc); + + if (FAILED(hRet) || !pIEnumFormatEtc) + return hRet; + + /* Set our enumerator as the browsers property */ + V_VT(&var) = VT_UNKNOWN; + V_UNKNOWN(&var) = (IUnknown*)pIEnumFormatEtc; + + hRet = IWebBrowserApp_PutProperty(pBrowser, (BSTR)szProperty, var); + if (FAILED(hRet)) + { + IEnumFORMATETC_Release(pIEnumFormatEtc); + goto RegisterDefaultAcceptHeaders_Exit; + } + } + + if (V_VT(&var) == VT_UNKNOWN) + { + /* Our variant is holding the clipboard enumerator */ + IUnknown* pIUnknown = V_UNKNOWN(&var); + IEnumFORMATETC* pClone = NULL; + + TRACE("Retrieved IEnumFORMATETC property\n"); + + /* Get an IEnumFormatEtc interface from the variants value */ + pIEnumFormatEtc = NULL; + hRet = IUnknown_QueryInterface(pIUnknown, &IID_IEnumFORMATETC, + (PVOID)&pIEnumFormatEtc); + if (!hRet && pIEnumFormatEtc) + { + /* Clone and register the enumerator */ + hRet = IEnumFORMATETC_Clone(pIEnumFormatEtc, &pClone); + if (!hRet && pClone) + { + GET_FUNC(pRegisterFormatEnumerator, urlmon, "RegisterFormatEnumerator", E_FAIL); + pRegisterFormatEnumerator(lpBC, pClone, 0); + + IEnumFORMATETC_Release(pClone); + } + + /* Release the IEnumFormatEtc interface */ + IEnumFORMATETC_Release(pIUnknown); + } + IUnknown_Release(V_UNKNOWN(&var)); + } + +RegisterDefaultAcceptHeaders_Exit: + IWebBrowserApp_Release(pBrowser); + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.14] + * + * Get Explorers "AcceptLanguage" setting. + * + * PARAMS + * langbuf [O] Destination for language string + * buflen [I] Length of langbuf + * + * RETURNS + * Success: S_OK. langbuf is set to the language string found. + * Failure: E_FAIL, If any arguments are invalid, error occurred, or Explorer + * does not contain the setting. + */ +HRESULT WINAPI GetAcceptLanguagesA( + LPSTR langbuf, + LPDWORD buflen) +{ + CHAR *mystr; + DWORD mystrlen, mytype; + HKEY mykey; + LCID mylcid; + + mystrlen = (*buflen > 6) ? *buflen : 6; + mystr = (CHAR*)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, mystrlen); + RegOpenKeyA(HKEY_CURRENT_USER, + "Software\\Microsoft\\Internet Explorer\\International", + &mykey); + if (RegQueryValueExA(mykey, "AcceptLanguage", + 0, &mytype, (PBYTE)mystr, &mystrlen)) { + /* Did not find value */ + mylcid = GetUserDefaultLCID(); + /* somehow the mylcid translates into "en-us" + * this is similar to "LOCALE_SABBREVLANGNAME" + * which could be gotten via GetLocaleInfo. + * The only problem is LOCALE_SABBREVLANGUAGE" is + * a 3 char string (first 2 are country code and third is + * letter for "sublanguage", which does not come close to + * "en-us" + */ + lstrcpyA(mystr, "en-us"); + mystrlen = lstrlenA(mystr); + } + else { + /* handle returned string */ + FIXME("missing code\n"); + } + if (mystrlen > *buflen) + lstrcpynA(langbuf, mystr, *buflen); + else { + lstrcpyA(langbuf, mystr); + *buflen = lstrlenA(langbuf); + } + RegCloseKey(mykey); + HeapFree(GetProcessHeap(), 0, mystr); + TRACE("language is %s\n", debugstr_a(langbuf)); + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.15] + * + * Unicode version of SHLWAPI_14. + */ +HRESULT WINAPI GetAcceptLanguagesW( + LPWSTR langbuf, + LPDWORD buflen) +{ + CHAR *mystr; + DWORD mystrlen, mytype; + HKEY mykey; + LCID mylcid; + + mystrlen = (*buflen > 6) ? *buflen : 6; + mystr = (CHAR*)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, mystrlen); + RegOpenKeyA(HKEY_CURRENT_USER, + "Software\\Microsoft\\Internet Explorer\\International", + &mykey); + if (RegQueryValueExA(mykey, "AcceptLanguage", + 0, &mytype, (PBYTE)mystr, &mystrlen)) { + /* Did not find value */ + mylcid = GetUserDefaultLCID(); + /* somehow the mylcid translates into "en-us" + * this is similar to "LOCALE_SABBREVLANGNAME" + * which could be gotten via GetLocaleInfo. + * The only problem is LOCALE_SABBREVLANGUAGE" is + * a 3 char string (first 2 are country code and third is + * letter for "sublanguage", which does not come close to + * "en-us" + */ + lstrcpyA(mystr, "en-us"); + mystrlen = lstrlenA(mystr); + } + else { + /* handle returned string */ + FIXME("missing code\n"); + } + RegCloseKey(mykey); + *buflen = MultiByteToWideChar(0, 0, mystr, -1, langbuf, (*buflen)-1); + HeapFree(GetProcessHeap(), 0, mystr); + TRACE("language is %s\n", debugstr_w(langbuf)); + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.23] + * + * Convert a GUID to a string. + * + * PARAMS + * guid [I] GUID to convert + * str [O] Destination for string + * cmax [I] Length of output buffer + * + * RETURNS + * The length of the string created. + */ +INT WINAPI SHStringFromGUIDA(REFGUID guid, LPSTR lpszDest, INT cchMax) +{ + char xguid[40]; + INT iLen; + + TRACE("(%s,%p,%d)\n", debugstr_guid(guid), lpszDest, cchMax); + + sprintf(xguid, "{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + + iLen = strlen(xguid) + 1; + + if (iLen > cchMax) + return 0; + memcpy(lpszDest, xguid, iLen); + return iLen; +} + +/************************************************************************* + * @ [SHLWAPI.24] + * + * Unicode version of SHStringFromGUIDA. + */ +INT WINAPI SHStringFromGUIDW(REFGUID guid, LPWSTR lpszDest, INT cchMax) +{ + char xguid[40]; + INT iLen = SHStringFromGUIDA(guid, xguid, cchMax); + + if (iLen) + MultiByteToWideChar(CP_ACP, 0, xguid, -1, lpszDest, cchMax); + return iLen; +} + +/************************************************************************* + * @ [SHLWAPI.25] + * + * Determine if a Unicode character is alphabetic. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is alphabetic, + * FALSE otherwise. + */ +BOOL WINAPI IsCharAlphaWrapW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_ALPHA) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.26] + * + * Determine if a Unicode character is upper-case. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is upper-case, + * FALSE otherwise. + */ +BOOL WINAPI IsCharUpperWrapW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_UPPER) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.27] + * + * Determine if a Unicode character is lower-case. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is lower-case, + * FALSE otherwise. + */ +BOOL WINAPI IsCharLowerWrapW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_LOWER) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.28] + * + * Determine if a Unicode character is alphabetic or a digit. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is alphabetic or a digit, + * FALSE otherwise. + */ +BOOL WINAPI IsCharAlphaNumericWrapW(WCHAR wc) +{ + return (get_char_typeW(wc) & (C1_ALPHA|C1_DIGIT)) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.29] + * + * Determine if a Unicode character is a space. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is a space, + * FALSE otherwise. + */ +BOOL WINAPI IsCharSpaceW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_SPACE) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.30] + * + * Determine if a Unicode character is a blank. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is a blank, + * FALSE otherwise. + * + */ +BOOL WINAPI IsCharBlankW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_BLANK) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.31] + * + * Determine if a Unicode character is punctuation. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is punctuation, + * FALSE otherwise. + */ +BOOL WINAPI IsCharPunctW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_PUNCT) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.32] + * + * Determine if a Unicode character is a control character. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is a control character, + * FALSE otherwise. + */ +BOOL WINAPI IsCharCntrlW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_CNTRL) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.33] + * + * Determine if a Unicode character is a digit. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is a digit, + * FALSE otherwise. + */ +BOOL WINAPI IsCharDigitW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_DIGIT) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.34] + * + * Determine if a Unicode character is a hex digit. + * + * PARAMS + * wc [I] Character to check. + * + * RETURNS + * TRUE, if wc is a hex digit, + * FALSE otherwise. + */ +BOOL WINAPI IsCharXDigitW(WCHAR wc) +{ + return (get_char_typeW(wc) & C1_XDIGIT) != 0; +} + +/************************************************************************* + * @ [SHLWAPI.35] + * + */ +BOOL WINAPI GetStringType3ExW(LPWSTR lpszStr, DWORD dwLen, LPVOID p3) +{ + FIXME("(%s,0x%08lx,%p): stub\n", debugstr_w(lpszStr), dwLen, p3); + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.36] + * + * Insert a bitmap menu item at the bottom of a menu. + * + * PARAMS + * hMenu [I] Menu to insert into + * flags [I] Flags for insertion + * id [I] Menu ID of the item + * str [I] Menu text for the item + * + * RETURNS + * Success: TRUE, the item is inserted into the menu + * Failure: FALSE, if any parameter is invalid + */ +BOOL WINAPI AppendMenuWrapW(HMENU hMenu, UINT flags, UINT id, LPCWSTR str) +{ + TRACE("(%p,0x%08x,0x%08x,%s)\n",hMenu, flags, id, debugstr_w(str)); + return InsertMenuW(hMenu, -1, flags | MF_BITMAP, id, str); +} + +/************************************************************************* + * @ [SHLWAPI.74] + * + * Get the text from a given dialog item. + * + * PARAMS + * hWnd [I] Handle of dialog + * nItem [I] Index of item + * lpsDest [O] Buffer for receiving window text + * nDestLen [I] Length of buffer. + * + * RETURNS + * Success: The length of the returned text. + * Failure: 0. + */ +INT WINAPI GetDlgItemTextWrapW(HWND hWnd, INT nItem, LPWSTR lpsDest,INT nDestLen) +{ + HWND hItem = GetDlgItem(hWnd, nItem); + + if (hItem) + return GetWindowTextW(hItem, lpsDest, nDestLen); + if (nDestLen) + *lpsDest = (WCHAR)'\0'; + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.138] + * + * Set the text of a given dialog item. + * + * PARAMS + * hWnd [I] Handle of dialog + * iItem [I] Index of item + * lpszText [O] Text to set + * + * RETURNS + * Success: TRUE. The text of the dialog is set to lpszText. + * Failure: FALSE, Otherwise. + */ +BOOL WINAPI SetDlgItemTextWrapW(HWND hWnd, INT iItem, LPCWSTR lpszText) +{ + HWND hWndItem = GetDlgItem(hWnd, iItem); + if (hWndItem) + return SetWindowTextW(hWndItem, lpszText); + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.151] + * + * Compare two Ascii strings up to a given length. + * + * PARAMS + * lpszSrc [I] Source string + * lpszCmp [I] String to compare to lpszSrc + * len [I] Maximum length + * + * RETURNS + * A number greater than, less than or equal to 0 depending on whether + * lpszSrc is greater than, less than or equal to lpszCmp. + */ +DWORD WINAPI StrCmpNCA(LPCSTR lpszSrc, LPCSTR lpszCmp, INT len) +{ + return strncmp(lpszSrc, lpszCmp, len); +} + +/************************************************************************* + * @ [SHLWAPI.152] + * + * Unicode version of StrCmpNCA. + */ +DWORD WINAPI StrCmpNCW(LPCWSTR lpszSrc, LPCWSTR lpszCmp, INT len) +{ + return strncmpW(lpszSrc, lpszCmp, len); +} + +/************************************************************************* + * @ [SHLWAPI.153] + * + * Compare two Ascii strings up to a given length, ignoring case. + * + * PARAMS + * lpszSrc [I] Source string + * lpszCmp [I] String to compare to lpszSrc + * len [I] Maximum length + * + * RETURNS + * A number greater than, less than or equal to 0 depending on whether + * lpszSrc is greater than, less than or equal to lpszCmp. + */ +DWORD WINAPI StrCmpNICA(LPCSTR lpszSrc, LPCSTR lpszCmp, DWORD len) +{ + return strncasecmp(lpszSrc, lpszCmp, len); +} + +/************************************************************************* + * @ [SHLWAPI.154] + * + * Unicode version of StrCmpNICA. + */ +DWORD WINAPI StrCmpNICW(LPCWSTR lpszSrc, LPCWSTR lpszCmp, DWORD len) +{ + return strncmpiW(lpszSrc, lpszCmp, len); +} + +/************************************************************************* + * @ [SHLWAPI.155] + * + * Compare two Ascii strings. + * + * PARAMS + * lpszSrc [I] Source string + * lpszCmp [I] String to compare to lpszSrc + * + * RETURNS + * A number greater than, less than or equal to 0 depending on whether + * lpszSrc is greater than, less than or equal to lpszCmp. + */ +DWORD WINAPI StrCmpCA(LPCSTR lpszSrc, LPCSTR lpszCmp) +{ + return strcmp(lpszSrc, lpszCmp); +} + +/************************************************************************* + * @ [SHLWAPI.156] + * + * Unicode version of StrCmpCA. + */ +DWORD WINAPI StrCmpCW(LPCWSTR lpszSrc, LPCWSTR lpszCmp) +{ + return strcmpW(lpszSrc, lpszCmp); +} + +/************************************************************************* + * @ [SHLWAPI.157] + * + * Compare two Ascii strings, ignoring case. + * + * PARAMS + * lpszSrc [I] Source string + * lpszCmp [I] String to compare to lpszSrc + * + * RETURNS + * A number greater than, less than or equal to 0 depending on whether + * lpszSrc is greater than, less than or equal to lpszCmp. + */ +DWORD WINAPI StrCmpICA(LPCSTR lpszSrc, LPCSTR lpszCmp) +{ + return strcasecmp(lpszSrc, lpszCmp); +} + +/************************************************************************* + * @ [SHLWAPI.158] + * + * Unicode version of StrCmpICA. + */ +DWORD WINAPI StrCmpICW(LPCWSTR lpszSrc, LPCWSTR lpszCmp) +{ + return strcmpiW(lpszSrc, lpszCmp); +} + +/************************************************************************* + * @ [SHLWAPI.160] + * + * Get an identification string for the OS and explorer. + * + * PARAMS + * lpszDest [O] Destination for Id string + * dwDestLen [I] Length of lpszDest + * + * RETURNS + * TRUE, If the string was created successfully + * FALSE, Otherwise + */ +BOOL WINAPI SHAboutInfoA(LPSTR lpszDest, DWORD dwDestLen) +{ + WCHAR buff[2084]; + + TRACE("(%p,%ld)", lpszDest, dwDestLen); + + if (lpszDest && SHAboutInfoW(buff, dwDestLen)) + { + WideCharToMultiByte(CP_ACP, 0, buff, -1, lpszDest, dwDestLen, NULL, NULL); + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.161] + * + * Unicode version of SHAboutInfoA. + */ +BOOL WINAPI SHAboutInfoW(LPWSTR lpszDest, DWORD dwDestLen) +{ + static const WCHAR szIEKey[] = { 'S','O','F','T','W','A','R','E','\\', + 'M','i','c','r','o','s','o','f','t','\\','I','n','t','e','r','n','e','t', + ' ','E','x','p','l','o','r','e','r','\0' }; + static const WCHAR szWinNtKey[] = { 'S','O','F','T','W','A','R','E','\\', + 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s',' ', + 'N','T','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0' }; + static const WCHAR szWinKey[] = { 'S','O','F','T','W','A','R','E','\\', + 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\0' }; + static const WCHAR szRegKey[] = { 'S','O','F','T','W','A','R','E','\\', + 'M','i','c','r','o','s','o','f','t','\\','I','n','t','e','r','n','e','t', + ' ','E','x','p','l','o','r','e','r','\\', + 'R','e','g','i','s','t','r','a','t','i','o','n','\0' }; + static const WCHAR szVersion[] = { 'V','e','r','s','i','o','n','\0' }; + static const WCHAR szCustomized[] = { 'C','u','s','t','o','m','i','z','e','d', + 'V','e','r','s','i','o','n','\0' }; + static const WCHAR szOwner[] = { 'R','e','g','i','s','t','e','r','e','d', + 'O','w','n','e','r','\0' }; + static const WCHAR szOrg[] = { 'R','e','g','i','s','t','e','r','e','d', + 'O','r','g','a','n','i','z','a','t','i','o','n','\0' }; + static const WCHAR szProduct[] = { 'P','r','o','d','u','c','t','I','d','\0' }; + static const WCHAR szUpdate[] = { 'I','E','A','K', + 'U','p','d','a','t','e','U','r','l','\0' }; + static const WCHAR szHelp[] = { 'I','E','A','K', + 'H','e','l','p','S','t','r','i','n','g','\0' }; + WCHAR buff[2084]; + HKEY hReg; + DWORD dwType, dwLen; + + TRACE("(%p,%ld)", lpszDest, dwDestLen); + + if (!lpszDest) + return FALSE; + + *lpszDest = '\0'; + + /* Try the NT key first, followed by 95/98 key */ + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szWinNtKey, 0, KEY_READ, &hReg) && + RegOpenKeyExW(HKEY_LOCAL_MACHINE, szWinKey, 0, KEY_READ, &hReg)) + return FALSE; + + /* OS Version */ + buff[0] = '\0'; + dwLen = 30; + if (!SHGetValueW(HKEY_LOCAL_MACHINE, szIEKey, szVersion, &dwType, buff, &dwLen)) + { + DWORD dwStrLen = strlenW(buff); + dwLen = 30 - dwStrLen; + SHGetValueW(HKEY_LOCAL_MACHINE, szIEKey, + szCustomized, &dwType, buff+dwStrLen, &dwLen); + } + StrCatBuffW(lpszDest, buff, dwDestLen); + + /* ~Registered Owner */ + buff[0] = '~'; + dwLen = 256; + if (SHGetValueW(hReg, szOwner, 0, &dwType, buff+1, &dwLen)) + buff[1] = '\0'; + StrCatBuffW(lpszDest, buff, dwDestLen); + + /* ~Registered Organization */ + dwLen = 256; + if (SHGetValueW(hReg, szOrg, 0, &dwType, buff+1, &dwLen)) + buff[1] = '\0'; + StrCatBuffW(lpszDest, buff, dwDestLen); + + /* FIXME: Not sure where this number comes from */ + buff[0] = '~'; + buff[1] = '0'; + buff[2] = '\0'; + StrCatBuffW(lpszDest, buff, dwDestLen); + + /* ~Product Id */ + dwLen = 256; + if (SHGetValueW(HKEY_LOCAL_MACHINE, szRegKey, szProduct, &dwType, buff+1, &dwLen)) + buff[1] = '\0'; + StrCatBuffW(lpszDest, buff, dwDestLen); + + /* ~IE Update Url */ + dwLen = 2048; + if(SHGetValueW(HKEY_LOCAL_MACHINE, szWinKey, szUpdate, &dwType, buff+1, &dwLen)) + buff[1] = '\0'; + StrCatBuffW(lpszDest, buff, dwDestLen); + + /* ~IE Help String */ + dwLen = 256; + if(SHGetValueW(hReg, szHelp, 0, &dwType, buff+1, &dwLen)) + buff[1] = '\0'; + StrCatBuffW(lpszDest, buff, dwDestLen); + + RegCloseKey(hReg); + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.162] + * + * Remove a hanging lead byte from the end of a string, if present. + * + * PARAMS + * lpStr [I] String to check for a hanging lead byte + * size [I] Length of lpStr + * + * RETURNS + * Success: The new length of the string. Any hanging lead bytes are removed. + * Failure: 0, if any parameters are invalid. + */ +DWORD WINAPI SHTruncateString(LPSTR lpStr, DWORD size) +{ + if (lpStr && size) + { + LPSTR lastByte = lpStr + size - 1; + + while(lpStr < lastByte) + lpStr += IsDBCSLeadByte(*lpStr) ? 2 : 1; + + if(lpStr == lastByte && IsDBCSLeadByte(*lpStr)) + { + *lpStr = '\0'; + size--; + } + return size; + } + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.163] + * + * Call IOleCommandTarget_QueryStatus() on an object. + * + * PARAMS + * lpUnknown [I] Object supporting the IOleCommandTarget interface + * pguidCmdGroup [I] GUID for the command group + * cCmds [I] + * prgCmds [O] Commands + * pCmdText [O] Command text + * + * RETURNS + * Success: S_OK. + * Failure: E_FAIL, if lpUnknown is NULL. + * E_NOINTERFACE, if lpUnknown does not support IOleCommandTarget. + * Otherwise, an error code from IOleCommandTarget_QueryStatus(). + */ +HRESULT WINAPI IUnknown_QueryStatus(IUnknown* lpUnknown, REFGUID pguidCmdGroup, + ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT* pCmdText) +{ + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p,%ld,%p,%p)\n",lpUnknown, pguidCmdGroup, cCmds, prgCmds, pCmdText); + + if (lpUnknown) + { + IOleCommandTarget* lpOle; + + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleCommandTarget, + (void**)&lpOle); + + if (SUCCEEDED(hRet) && lpOle) + { + hRet = IOleCommandTarget_QueryStatus(lpOle, pguidCmdGroup, cCmds, + prgCmds, pCmdText); + IOleCommandTarget_Release(lpOle); + } + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.164] + * + * Call IOleCommandTarget_Exec() on an object. + * + * PARAMS + * lpUnknown [I] Object supporting the IOleCommandTarget interface + * pguidCmdGroup [I] GUID for the command group + * + * RETURNS + * Success: S_OK. + * Failure: E_FAIL, if lpUnknown is NULL. + * E_NOINTERFACE, if lpUnknown does not support IOleCommandTarget. + * Otherwise, an error code from IOleCommandTarget_Exec(). + */ +HRESULT WINAPI IUnknown_Exec(IUnknown* lpUnknown, REFGUID pguidCmdGroup, + DWORD nCmdID, DWORD nCmdexecopt, VARIANT* pvaIn, + VARIANT* pvaOut) +{ + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p,%ld,%ld,%p,%p)\n",lpUnknown, pguidCmdGroup, nCmdID, + nCmdexecopt, pvaIn, pvaOut); + + if (lpUnknown) + { + IOleCommandTarget* lpOle; + + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleCommandTarget, + (void**)&lpOle); + if (SUCCEEDED(hRet) && lpOle) + { + hRet = IOleCommandTarget_Exec(lpOle, pguidCmdGroup, nCmdID, + nCmdexecopt, pvaIn, pvaOut); + IOleCommandTarget_Release(lpOle); + } + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.165] + * + * Retrieve, modify, and re-set a value from a window. + * + * PARAMS + * hWnd [I] Window to get value from + * offset [I] Offset of value + * wMask [I] Mask for uiFlags + * wFlags [I] Bits to set in window value + * + * RETURNS + * The new value as it was set, or 0 if any parameter is invalid. + * + * NOTES + * Any bits set in uiMask are cleared from the value, then any bits set in + * uiFlags are set in the value. + */ +LONG WINAPI SHSetWindowBits(HWND hwnd, INT offset, UINT wMask, UINT wFlags) +{ + LONG ret = GetWindowLongA(hwnd, offset); + LONG newFlags = (wFlags & wMask) | (ret & ~wFlags); + + if (newFlags != ret) + ret = SetWindowLongA(hwnd, offset, newFlags); + return ret; +} + +/************************************************************************* + * @ [SHLWAPI.167] + * + * Change a window's parent. + * + * PARAMS + * hWnd [I] Window to change parent of + * hWndParent [I] New parent window + * + * RETURNS + * The old parent of hWnd. + * + * NOTES + * If hWndParent is NULL (desktop), the window style is changed to WS_POPUP. + * If hWndParent is NOT NULL then we set the WS_CHILD style. + */ +HWND WINAPI SHSetParentHwnd(HWND hWnd, HWND hWndParent) +{ + TRACE("%p, %p\n", hWnd, hWndParent); + + if(GetParent(hWnd) == hWndParent) + return 0; + + if(hWndParent) + SHSetWindowBits(hWnd, GWL_STYLE, WS_CHILD, WS_CHILD); + else + SHSetWindowBits(hWnd, GWL_STYLE, WS_POPUP, WS_POPUP); + + return SetParent(hWnd, hWndParent); +} + +/************************************************************************* + * @ [SHLWAPI.168] + * + * Locate and advise a connection point in an IConnectionPointContainer object. + * + * PARAMS + * lpUnkSink [I] Sink for the connection point advise call + * riid [I] REFIID of connection point to advise + * bAdviseOnly [I] TRUE = Advise only, FALSE = Unadvise first + * lpUnknown [I] Object supporting the IConnectionPointContainer interface + * lpCookie [O] Pointer to connection point cookie + * lppCP [O] Destination for the IConnectionPoint found + * + * RETURNS + * Success: S_OK. If lppCP is non-NULL, it is filled with the IConnectionPoint + * that was advised. The caller is responsable for releasing it. + * Failure: E_FAIL, if any arguments are invalid. + * E_NOINTERFACE, if lpUnknown isn't an IConnectionPointContainer, + * Or an HRESULT error code if any call fails. + */ +HRESULT WINAPI ConnectToConnectionPoint(IUnknown* lpUnkSink, REFIID riid, BOOL bAdviseOnly, + IUnknown* lpUnknown, LPDWORD lpCookie, + IConnectionPoint **lppCP) +{ + HRESULT hRet; + IConnectionPointContainer* lpContainer; + IConnectionPoint *lpCP; + + if(!lpUnknown || (bAdviseOnly && !lpUnkSink)) + return E_FAIL; + + if(lppCP) + *lppCP = NULL; + + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IConnectionPointContainer, + (void**)&lpContainer); + if (SUCCEEDED(hRet)) + { + hRet = IConnectionPointContainer_FindConnectionPoint(lpContainer, riid, &lpCP); + + if (SUCCEEDED(hRet)) + { + if(!bAdviseOnly) + hRet = IConnectionPoint_Unadvise(lpCP, *lpCookie); + hRet = IConnectionPoint_Advise(lpCP, lpUnkSink, lpCookie); + + if (FAILED(hRet)) + *lpCookie = 0; + + if (lppCP && SUCCEEDED(hRet)) + *lppCP = lpCP; /* Caller keeps the interface */ + else + IConnectionPoint_Release(lpCP); /* Release it */ + } + + IUnknown_Release(lpContainer); + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.169] + * + * Release an interface. + * + * PARAMS + * lpUnknown [I] Object to release + * + * RETURNS + * Nothing. + */ +DWORD WINAPI IUnknown_AtomicRelease(IUnknown ** lpUnknown) +{ + IUnknown *temp; + + TRACE("(%p)\n",lpUnknown); + + if(!lpUnknown || !*((LPDWORD)lpUnknown)) return 0; + temp = *lpUnknown; + *lpUnknown = NULL; + + TRACE("doing Release\n"); + + return IUnknown_Release(temp); +} + +/************************************************************************* + * @ [SHLWAPI.170] + * + * Skip '//' if present in a string. + * + * PARAMS + * lpszSrc [I] String to check for '//' + * + * RETURNS + * Success: The next character after the '//' or the string if not present + * Failure: NULL, if lpszStr is NULL. + */ +LPCSTR WINAPI PathSkipLeadingSlashesA(LPCSTR lpszSrc) +{ + if (lpszSrc && lpszSrc[0] == '/' && lpszSrc[1] == '/') + lpszSrc += 2; + return lpszSrc; +} + +/************************************************************************* + * @ [SHLWAPI.171] + * + * Check if two interfaces come from the same object. + * + * PARAMS + * lpInt1 [I] Interface to check against lpInt2. + * lpInt2 [I] Interface to check against lpInt1. + * + * RETURNS + * TRUE, If the interfaces come from the same object. + * FALSE Otherwise. + */ +BOOL WINAPI SHIsSameObject(IUnknown* lpInt1, IUnknown* lpInt2) +{ + LPVOID lpUnknown1, lpUnknown2; + + TRACE("%p %p\n", lpInt1, lpInt2); + + if (!lpInt1 || !lpInt2) + return FALSE; + + if (lpInt1 == lpInt2) + return TRUE; + + if (!SUCCEEDED(IUnknown_QueryInterface(lpInt1, &IID_IUnknown, + (LPVOID *)&lpUnknown1))) + return FALSE; + + if (!SUCCEEDED(IUnknown_QueryInterface(lpInt2, &IID_IUnknown, + (LPVOID *)&lpUnknown2))) + return FALSE; + + if (lpUnknown1 == lpUnknown2) + return TRUE; + + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.172] + * + * Get the window handle of an object. + * + * PARAMS + * lpUnknown [I] Object to get the window handle of + * lphWnd [O] Destination for window handle + * + * RETURNS + * Success: S_OK. lphWnd contains the objects window handle. + * Failure: An HRESULT error code. + * + * NOTES + * lpUnknown is expected to support one of the following interfaces: + * IOleWindow(), IInternetSecurityMgrSite(), or IShellView(). + */ +HRESULT WINAPI IUnknown_GetWindow(IUnknown *lpUnknown, HWND *lphWnd) +{ + /* FIXME: Wine has no header for this object */ + static const GUID IID_IInternetSecurityMgrSite = { 0x79eac9ed, + 0xbaf9, 0x11ce, { 0x8c, 0x82, 0x00, 0xaa, 0x00, 0x4b, 0xa9, 0x0b }}; + IUnknown *lpOle; + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p)\n", lpUnknown, lphWnd); + + if (!lpUnknown) + return hRet; + + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleWindow, (void**)&lpOle); + + if (FAILED(hRet)) + { + hRet = IUnknown_QueryInterface(lpUnknown,&IID_IShellView, (void**)&lpOle); + + if (FAILED(hRet)) + { + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IInternetSecurityMgrSite, + (void**)&lpOle); + } + } + + if (SUCCEEDED(hRet)) + { + /* Lazyness here - Since GetWindow() is the first method for the above 3 + * interfaces, we use the same call for them all. + */ + hRet = IOleWindow_GetWindow((IOleWindow*)lpOle, lphWnd); + IUnknown_Release(lpOle); + if (lphWnd) + TRACE("Returning HWND=%p\n", *lphWnd); + } + + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.173] + * + * Call a method on as as yet unidentified object. + * + * PARAMS + * pUnk [I] Object supporting the unidentified interface, + * arg [I] Argument for the call on the object. + * + * RETURNS + * S_OK. + */ +HRESULT WINAPI IUnknown_SetOwner(IUnknown *pUnk, ULONG arg) +{ + static const GUID guid_173 = { + 0x5836fb00, 0x8187, 0x11cf, { 0xa1,0x2b,0x00,0xaa,0x00,0x4a,0xe8,0x37 } + }; + IMalloc *pUnk2; + + TRACE("(%p,%ld)\n", pUnk, arg); + + /* Note: arg may not be a ULONG and pUnk2 is for sure not an IMalloc - + * We use this interface as its vtable entry is compatible with the + * object in question. + * FIXME: Find out what this object is and where it should be defined. + */ + if (pUnk && + SUCCEEDED(IUnknown_QueryInterface(pUnk, &guid_173, (void**)&pUnk2))) + { + IMalloc_Alloc(pUnk2, arg); /* Faked call!! */ + IMalloc_Release(pUnk2); + } + return S_OK; +} + +/************************************************************************* + * @ [SHLWAPI.174] + * + * Call either IObjectWithSite_SetSite() or IPersistMoniker_GetClassID() on + * an interface. + * + * RETURNS + * Success: S_OK. + * Failure: E_FAIL, if p1 is NULL. + * E_NOINTERFACE If p1 does not support the IPersist interface, + * Or an HRESULT error code. + */ +DWORD WINAPI IUnknown_SetSite( + IUnknown *p1, /* [in] OLE object */ + LPVOID *p2) /* [out] ptr for call results */ +{ + DWORD ret, aa; + + if (!p1) return E_FAIL; + + /* see if SetSite interface exists for IObjectWithSite object */ + ret = IUnknown_QueryInterface((IUnknown *)p1, (REFIID)id1, (LPVOID *)&p1); + TRACE("first IU_QI ret=%08lx, p1=%p\n", ret, p1); + if (ret) { + + /* see if GetClassId interface exists for IPersistMoniker object */ + ret = IUnknown_QueryInterface((IUnknown *)p1, (REFIID)id2, (LPVOID *)&aa); + TRACE("second IU_QI ret=%08lx, aa=%08lx\n", ret, aa); + if (ret) return ret; + + /* fake a GetClassId call */ + ret = IOleWindow_GetWindow((IOleWindow *)aa, (HWND*)p2); + TRACE("second IU_QI doing 0x0c ret=%08lx, *p2=%08lx\n", ret, + *(LPDWORD)p2); + IUnknown_Release((IUnknown *)aa); + } + else { + /* fake a SetSite call */ + ret = IOleWindow_GetWindow((IOleWindow *)p1, (HWND*)p2); + TRACE("first IU_QI doing 0x0c ret=%08lx, *p2=%08lx\n", ret, + *(LPDWORD)p2); + IUnknown_Release((IUnknown *)p1); + } + return ret; +} + +/************************************************************************* + * @ [SHLWAPI.175] + * + * Call IPersist_GetClassID() on an object. + * + * PARAMS + * lpUnknown [I] Object supporting the IPersist interface + * lpClassId [O] Destination for Class Id + * + * RETURNS + * Success: S_OK. lpClassId contains the Class Id requested. + * Failure: E_FAIL, If lpUnknown is NULL, + * E_NOINTERFACE If lpUnknown does not support IPersist, + * Or an HRESULT error code. + */ +HRESULT WINAPI IUnknown_GetClassID(IUnknown *lpUnknown, CLSID* lpClassId) +{ + IPersist* lpPersist; + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p)\n", lpUnknown, debugstr_guid(lpClassId)); + + if (lpUnknown) + { + hRet = IUnknown_QueryInterface(lpUnknown,&IID_IPersist,(void**)&lpPersist); + if (SUCCEEDED(hRet)) + { + IPersist_GetClassID(lpPersist, lpClassId); + IPersist_Release(lpPersist); + } + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.176] + * + * Retrieve a Service Interface from an object. + * + * PARAMS + * lpUnknown [I] Object to get an IServiceProvider interface from + * sid [I] Service ID for IServiceProvider_QueryService() call + * riid [I] Function requested for QueryService call + * lppOut [O] Destination for the service interface pointer + * + * RETURNS + * Success: S_OK. lppOut contains an object providing the requested service + * Failure: An HRESULT error code + * + * NOTES + * lpUnknown is expected to support the IServiceProvider interface. + */ +HRESULT WINAPI IUnknown_QueryService(IUnknown* lpUnknown, REFGUID sid, REFIID riid, + LPVOID *lppOut) +{ + IServiceProvider* pService = NULL; + HRESULT hRet; + + if (!lppOut) + return E_FAIL; + + *lppOut = NULL; + + if (!lpUnknown) + return E_FAIL; + + /* Get an IServiceProvider interface from the object */ + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IServiceProvider, + (LPVOID*)&pService); + + if (!hRet && pService) + { + TRACE("QueryInterface returned (IServiceProvider*)%p\n", pService); + + /* Get a Service interface from the object */ + hRet = IServiceProvider_QueryService(pService, sid, riid, lppOut); + + TRACE("(IServiceProvider*)%p returned (IUnknown*)%p\n", pService, *lppOut); + + /* Release the IServiceProvider interface */ + IUnknown_Release(pService); + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.177] + * + * Loads a popup menu. + * + * PARAMS + * hInst [I] Instance handle + * szName [I] Menu name + * + * RETURNS + * Success: TRUE. + * Failure: FALSE. + */ +BOOL WINAPI SHLoadMenuPopup(HINSTANCE hInst, LPCWSTR szName) +{ + HMENU hMenu, hSubMenu; + + if ((hMenu = LoadMenuW(hInst, szName))) + { + if ((hSubMenu = GetSubMenu(hMenu, 0))) + RemoveMenu(hMenu, 0, MF_BYPOSITION); + + DestroyMenu(hMenu); + return TRUE; + } + return FALSE; +} + +typedef struct _enumWndData +{ + UINT uiMsgId; + WPARAM wParam; + LPARAM lParam; + LRESULT (WINAPI *pfnPost)(HWND,UINT,WPARAM,LPARAM); +} enumWndData; + +/* Callback for SHLWAPI_178 */ +static BOOL CALLBACK SHLWAPI_EnumChildProc(HWND hWnd, LPARAM lParam) +{ + enumWndData *data = (enumWndData *)lParam; + + TRACE("(%p,%p)\n", hWnd, data); + data->pfnPost(hWnd, data->uiMsgId, data->wParam, data->lParam); + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.178] + * + * Send or post a message to every child of a window. + * + * PARAMS + * hWnd [I] Window whose children will get the messages + * uiMsgId [I] Message Id + * wParam [I] WPARAM of message + * lParam [I] LPARAM of message + * bSend [I] TRUE = Use SendMessageA(), FALSE = Use PostMessageA() + * + * RETURNS + * Nothing. + * + * NOTES + * The appropriate ASCII or Unicode function is called for the window. + */ +void WINAPI SHPropagateMessage(HWND hWnd, UINT uiMsgId, WPARAM wParam, LPARAM lParam, BOOL bSend) +{ + enumWndData data; + + TRACE("(%p,%u,%d,%ld,%d)\n", hWnd, uiMsgId, wParam, lParam, bSend); + + if(hWnd) + { + data.uiMsgId = uiMsgId; + data.wParam = wParam; + data.lParam = lParam; + + if (bSend) + data.pfnPost = IsWindowUnicode(hWnd) ? (void*)SendMessageW : (void*)SendMessageA; + else + data.pfnPost = IsWindowUnicode(hWnd) ? (void*)PostMessageW : (void*)PostMessageA; + + EnumChildWindows(hWnd, SHLWAPI_EnumChildProc, (LPARAM)&data); + } +} + +/************************************************************************* + * @ [SHLWAPI.180] + * + * Remove all sub-menus from a menu. + * + * PARAMS + * hMenu [I] Menu to remove sub-menus from + * + * RETURNS + * Success: 0. All sub-menus under hMenu are removed + * Failure: -1, if any parameter is invalid + */ +DWORD WINAPI SHRemoveAllSubMenus(HMENU hMenu) +{ + int iItemCount = GetMenuItemCount(hMenu) - 1; + while (iItemCount >= 0) + { + HMENU hSubMenu = GetSubMenu(hMenu, iItemCount); + if (hSubMenu) + RemoveMenu(hMenu, iItemCount, MF_BYPOSITION); + iItemCount--; + } + return iItemCount; +} + +/************************************************************************* + * @ [SHLWAPI.181] + * + * Enable or disable a menu item. + * + * PARAMS + * hMenu [I] Menu holding menu item + * uID [I] ID of menu item to enable/disable + * bEnable [I] Whether to enable (TRUE) or disable (FALSE) the item. + * + * RETURNS + * The return code from EnableMenuItem. + */ +UINT WINAPI SHEnableMenuItem(HMENU hMenu, UINT wItemID, BOOL bEnable) +{ + return EnableMenuItem(hMenu, wItemID, bEnable ? MF_ENABLED : MF_GRAYED); +} + +/************************************************************************* + * @ [SHLWAPI.182] + * + * Check or uncheck a menu item. + * + * PARAMS + * hMenu [I] Menu holding menu item + * uID [I] ID of menu item to check/uncheck + * bCheck [I] Whether to check (TRUE) or uncheck (FALSE) the item. + * + * RETURNS + * The return code from CheckMenuItem. + */ +DWORD WINAPI SHCheckMenuItem(HMENU hMenu, UINT uID, BOOL bCheck) +{ + return CheckMenuItem(hMenu, uID, bCheck ? MF_CHECKED : MF_UNCHECKED); +} + +/************************************************************************* + * @ [SHLWAPI.183] + * + * Register a window class if it isn't already. + * + * PARAMS + * lpWndClass [I] Window class to register + * + * RETURNS + * The result of the RegisterClassA call. + */ +DWORD WINAPI SHRegisterClassA(WNDCLASSA *wndclass) +{ + WNDCLASSA wca; + if (GetClassInfoA(wndclass->hInstance, wndclass->lpszClassName, &wca)) + return TRUE; + return (DWORD)RegisterClassA(wndclass); +} + +/************************************************************************* + * @ [SHLWAPI.187] + * + * Call IPersistPropertyBag_Load() on an object. + * + * PARAMS + * lpUnknown [I] Object supporting the IPersistPropertyBag interface + * lpPropBag [O] Destination for loaded IPropertyBag + * + * RETURNS + * Success: S_OK. + * Failure: An HRESULT error code, or E_FAIL if lpUnknown is NULL. + */ +DWORD WINAPI SHLoadFromPropertyBag(IUnknown *lpUnknown, IPropertyBag* lpPropBag) +{ + IPersistPropertyBag* lpPPBag; + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p)\n", lpUnknown, lpPropBag); + + if (lpUnknown) + { + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IPersistPropertyBag, + (void**)&lpPPBag); + if (SUCCEEDED(hRet) && lpPPBag) + { + hRet = IPersistPropertyBag_Load(lpPPBag, lpPropBag, NULL); + IPersistPropertyBag_Release(lpPPBag); + } + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.189] + * + * Call IOleControlSite_GetExtendedControl() on an object. + * + * PARAMS + * lpUnknown [I] Object supporting the IOleControlSite interface + * lppDisp [O] Destination for resulting IDispatch. + * + * RETURNS + * Success: S_OK. + * Failure: An HRESULT error code, or E_FAIL if lpUnknown is NULL. + */ +DWORD WINAPI IUnknown_OnFocusOCS(IUnknown *lpUnknown, IDispatch** lppDisp) +{ + IOleControlSite* lpCSite; + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p)\n", lpUnknown, lppDisp); + if (lpUnknown) + { + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IOleControlSite, + (void**)&lpCSite); + if (SUCCEEDED(hRet) && lpCSite) + { + hRet = IOleControlSite_GetExtendedControl(lpCSite, lppDisp); + IOleControlSite_Release(lpCSite); + } + } + return hRet; +} + +static const WCHAR szDontShowKey[] = { 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'E','x','p','l','o','r','e','r','\\','D','o','n','t','S','h','o','w', + 'M','e','T','h','i','s','D','i','a','l','o','g','A','g','a','i','n','\0' +}; + +/************************************************************************* + * @ [SHLWAPI.191] + * + * Pop up a 'Don't show this message again' error dialog box. + * + * PARAMS + * hWnd [I] Window to own the dialog box + * arg2 [I] Unknown + * arg3 [I] Unknown + * arg4 [I] Unknown + * arg5 [I] Unknown + * lpszValue [I] Registry value holding boolean show/don't show. + * + * RETURNS + * Nothing. + */ +void WINAPI SHMessageBoxCheckW(HWND hWnd, PVOID arg2, PVOID arg3, PVOID arg4, PVOID arg5, LPCWSTR lpszValue) +{ + FIXME("(%p,%p,%p,%p,%p,%s) - stub!\n", hWnd, arg2, arg3, arg4, arg5, debugstr_w(lpszValue)); + + if (SHRegGetBoolUSValueW(szDontShowKey, lpszValue, FALSE, TRUE)) + { + /* FIXME: Should use DialogBoxParamW to load a dialog box; its dlgproc + * should accept clicks on 'Don't show' and set the reg value appropriately. + */ + } +} + +/************************************************************************* + * @ [SHLWAPI.192] + * + * Get a sub-menu from a menu item. + * + * PARAMS + * hMenu [I] Menu to get sub-menu from + * uID [I] ID of menu item containing sub-menu + * + * RETURNS + * The sub-menu of the item, or a NULL handle if any parameters are invalid. + */ +HMENU WINAPI SHGetMenuFromID(HMENU hMenu, UINT uID) +{ + MENUITEMINFOA mi; + + TRACE("(%p,%uld)\n", hMenu, uID); + + mi.cbSize = sizeof(MENUITEMINFOA); + mi.fMask = MIIM_SUBMENU; + + if (!GetMenuItemInfoA(hMenu, uID, 0, &mi)) + return (HMENU)NULL; + + return mi.hSubMenu; +} + +/************************************************************************* + * @ [SHLWAPI.193] + * + * Get the color depth of the primary display. + * + * PARAMS + * None. + * + * RETURNS + * The color depth of the primary display. + */ +DWORD WINAPI SHGetCurColorRes() +{ + HDC hdc; + DWORD ret; + + TRACE("()\n"); + + hdc = GetDC(0); + ret = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES); + ReleaseDC(0, hdc); + return ret; +} + +/************************************************************************* + * @ [SHLWAPI.197] + * + * Blank out a region of text by drawing the background only. + * + * PARAMS + * hDC [I] Device context to draw in + * pRect [I] Area to draw in + * cRef [I] Color to draw in + * + * RETURNS + * Nothing. + */ +DWORD WINAPI SHFillRectClr(HDC hDC, LPCRECT pRect, COLORREF cRef) +{ + COLORREF cOldColor = SetBkColor(hDC, cRef); + ExtTextOutA(hDC, 0, 0, ETO_OPAQUE, pRect, 0, 0, 0); + SetBkColor(hDC, cOldColor); + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.199] + * + * Copy an interface pointer + * + * PARAMS + * lppDest [O] Destination for copy + * lpUnknown [I] Source for copy + * + * RETURNS + * Nothing. + */ +VOID WINAPI IUnknown_Set(IUnknown **lppDest, IUnknown *lpUnknown) +{ + TRACE("(%p,%p)\n", lppDest, lpUnknown); + + if (lppDest) + IUnknown_AtomicRelease(lppDest); /* Release existing interface */ + + if (lpUnknown) + { + /* Copy */ + IUnknown_AddRef(lpUnknown); + *lppDest = lpUnknown; + } +} + +/************************************************************************* + * @ [SHLWAPI.201] + * + */ +HRESULT WINAPI MayExecForward(IUnknown* lpUnknown, INT iUnk, REFGUID pguidCmdGroup, + DWORD nCmdID, DWORD nCmdexecopt, VARIANT* pvaIn, + VARIANT* pvaOut) +{ + FIXME("(%p,%d,%p,%ld,%ld,%p,%p) - stub!\n", lpUnknown, iUnk, pguidCmdGroup, + nCmdID, nCmdexecopt, pvaIn, pvaOut); + return DRAGDROP_E_NOTREGISTERED; +} + +/************************************************************************* + * @ [SHLWAPI.202] + * + */ +HRESULT WINAPI IsQSForward(REFGUID pguidCmdGroup,ULONG cCmds, OLECMD *prgCmds) +{ + FIXME("(%p,%ld,%p) - stub!\n", pguidCmdGroup, cCmds, prgCmds); + return DRAGDROP_E_NOTREGISTERED; +} + +/************************************************************************* + * @ [SHLWAPI.204] + * + * Determine if a window is not a child of another window. + * + * PARAMS + * hParent [I] Suspected parent window + * hChild [I] Suspected child window + * + * RETURNS + * TRUE: If hChild is a child window of hParent + * FALSE: If hChild is not a child window of hParent, or they are equal + */ +BOOL WINAPI SHIsChildOrSelf(HWND hParent, HWND hChild) +{ + TRACE("(%p,%p)\n", hParent, hChild); + + if (!hParent || !hChild) + return TRUE; + else if(hParent == hChild) + return FALSE; + return !IsChild(hParent, hChild); +} + +/************************************************************************* + * @ [SHLWAPI.208] + * + * Some sort of memory management process. + */ +DWORD WINAPI FDSA_Initialize( + DWORD a, + DWORD b, + LPVOID c, + LPVOID d, + DWORD e) +{ + FIXME("(0x%08lx 0x%08lx %p %p 0x%08lx) stub\n", + a, b, c, d, e); + return 1; +} + +/************************************************************************* + * @ [SHLWAPI.209] + * + * Some sort of memory management process. + */ +DWORD WINAPI FDSA_Destroy( + LPVOID a) +{ + FIXME("(%p) stub\n", + a); + return 1; +} + +/************************************************************************* + * @ [SHLWAPI.210] + * + * Some sort of memory management process. + */ +DWORD WINAPI FDSA_InsertItem( + LPVOID a, + DWORD b, + LPVOID c) +{ + FIXME("(%p 0x%08lx %p) stub\n", + a, b, c); + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.211] + */ +DWORD WINAPI FDSA_DeleteItem( + LPVOID a, + DWORD b) +{ + FIXME("(%p 0x%08lx) stub\n", + a, b); + return 1; +} + +/************************************************************************* + * @ [SHLWAPI.215] + * + * NOTES + * check me! + */ +DWORD WINAPI SHAnsiToUnicode( + LPCSTR lpStrSrc, + LPWSTR lpwStrDest, + int len) +{ + INT len_a, ret; + + len_a = lstrlenA(lpStrSrc); + ret = MultiByteToWideChar(0, 0, lpStrSrc, len_a, lpwStrDest, len); + TRACE("%s %s %d, ret=%d\n", + debugstr_a(lpStrSrc), debugstr_w(lpwStrDest), len, ret); + return ret; +} + +/************************************************************************* + * @ [SHLWAPI.218] + * + * WideCharToMultiByte with support for multiple codepages. + * + * PARAMS + * CodePage [I] Code page to use for the conversion + * lpSrcStr [I] Source Unicode string to convert + * lpDstStr [O] Destination for converted Ascii string + * lpnMultiCharCount [O] Input length of lpDstStr/destination for length of lpDstStr + * + * RETURNS + * Success: The number of characters that result from the conversion. + * Failure: 0. + */ +INT WINAPI SHUnicodeToAnsiCP(UINT CodePage, LPCWSTR lpSrcStr, LPSTR lpDstStr, + LPINT lpnMultiCharCount) +{ + WCHAR emptyW[] = { '\0' }; + int len , reqLen; + LPSTR mem; + + if (!lpDstStr || !lpnMultiCharCount) + return 0; + + if (!lpSrcStr) + lpSrcStr = emptyW; + + *lpDstStr = '\0'; + + len = strlenW(lpSrcStr) + 1; + + switch (CodePage) + { + case CP_WINUNICODE: + CodePage = CP_UTF8; /* Fall through... */ + case 0x0000C350: /* FIXME: CP_ #define */ + case CP_UTF7: + case CP_UTF8: + { + DWORD dwMode = 0; + INT nWideCharCount = len - 1; + + GET_FUNC(pConvertINetUnicodeToMultiByte, mlang, "ConvertINetUnicodeToMultiByte", 0); + if (!pConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &nWideCharCount, lpDstStr, + lpnMultiCharCount)) + return 0; + + if (nWideCharCount < len - 1) + { + mem = (LPSTR)HeapAlloc(GetProcessHeap(), 0, *lpnMultiCharCount); + if (!mem) + return 0; + + *lpnMultiCharCount = 0; + + if (pConvertINetUnicodeToMultiByte(&dwMode, CodePage, lpSrcStr, &len, mem, lpnMultiCharCount)) + { + SHTruncateString(mem, *lpnMultiCharCount); + lstrcpynA(lpDstStr, mem, *lpnMultiCharCount + 1); + return *lpnMultiCharCount + 1; + } + HeapFree(GetProcessHeap(), 0, mem); + return *lpnMultiCharCount; + } + lpDstStr[*lpnMultiCharCount] = '\0'; + return *lpnMultiCharCount; + } + break; + default: + break; + } + + reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, lpDstStr, + *lpnMultiCharCount, NULL, NULL); + + if (!reqLen && GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, NULL, 0, NULL, NULL); + if (reqLen) + { + mem = (LPSTR)HeapAlloc(GetProcessHeap(), 0, reqLen); + if (mem) + { + reqLen = WideCharToMultiByte(CodePage, 0, lpSrcStr, len, mem, + reqLen, NULL, NULL); + + reqLen = SHTruncateString(mem, *lpnMultiCharCount); + reqLen++; + + lstrcpynA(lpDstStr, mem, *lpnMultiCharCount); + + HeapFree(GetProcessHeap(), 0, mem); + } + } + } + return reqLen; +} + +/************************************************************************* + * @ [SHLWAPI.217] + * + * WideCharToMultiByte with support for multiple codepages. + * + * PARAMS + * lpSrcStr [I] Source Unicode string to convert + * lpDstStr [O] Destination for converted Ascii string + * lpnMultiCharCount [O] Input length of lpDstStr/destination for length of lpDstStr + * + * RETURNS + * See SHUnicodeToAnsiCP + + * NOTES + * This function simply calls SHUnicodeToAnsiCP with CodePage = CP_ACP. + */ +INT WINAPI SHUnicodeToAnsi(LPCWSTR lpSrcStr, LPSTR lpDstStr, INT MultiCharCount) +{ + INT myint = MultiCharCount; + + return SHUnicodeToAnsiCP(CP_ACP, lpSrcStr, lpDstStr, &myint); +} + +typedef struct { + REFIID refid; + DWORD indx; +} IFACE_INDEX_TBL; + +/************************************************************************* + * @ [SHLWAPI.219] + * + * Call IUnknown_QueryInterface() on a table of objects. + * + * RETURNS + * Success: S_OK. + * Failure: E_POINTER or E_NOINTERFACE. + */ +HRESULT WINAPI QISearch( + LPVOID w, /* [in] Table of interfaces */ + IFACE_INDEX_TBL *x, /* [in] Array of REFIIDs and indexes into the table */ + REFIID riid, /* [in] REFIID to get interface for */ + LPVOID *ppv) /* [out] Destination for interface pointer */ +{ + HRESULT ret; + IUnknown *a_vtbl; + IFACE_INDEX_TBL *xmove; + + TRACE("(%p %p %s %p)\n", w,x,debugstr_guid(riid),ppv); + if (ppv) { + xmove = x; + while (xmove->refid) { + TRACE("trying (indx %ld) %s\n", xmove->indx, debugstr_guid(xmove->refid)); + if (IsEqualIID(riid, xmove->refid)) { + a_vtbl = (IUnknown*)(xmove->indx + (LPBYTE)w); + TRACE("matched, returning (%p)\n", a_vtbl); + *ppv = (LPVOID)a_vtbl; + IUnknown_AddRef(a_vtbl); + return S_OK; + } + xmove++; + } + + if (IsEqualIID(riid, &IID_IUnknown)) { + a_vtbl = (IUnknown*)(x->indx + (LPBYTE)w); + TRACE("returning first for IUnknown (%p)\n", a_vtbl); + *ppv = (LPVOID)a_vtbl; + IUnknown_AddRef(a_vtbl); + return S_OK; + } + *ppv = 0; + ret = E_NOINTERFACE; + } else + ret = E_POINTER; + + TRACE("-- 0x%08lx\n", ret); + return ret; +} + +/************************************************************************* + * @ [SHLWAPI.221] + * + * Remove the "PropDlgFont" property from a window. + * + * PARAMS + * hWnd [I] Window to remove the property from + * + * RETURNS + * A handle to the removed property, or NULL if it did not exist. + */ +HANDLE WINAPI SHRemoveDefaultDialogFont(HWND hWnd) +{ + HANDLE hProp; + + TRACE("(%p)\n", hWnd); + + hProp = GetPropA(hWnd, "PropDlgFont"); + + if(hProp) + { + DeleteObject(hProp); + hProp = RemovePropA(hWnd, "PropDlgFont"); + } + return hProp; +} + +/************************************************************************* + * @ [SHLWAPI.236] + * + * Load the in-process server of a given GUID. + * + * PARAMS + * refiid [I] GUID of the server to load. + * + * RETURNS + * Success: A handle to the loaded server dll. + * Failure: A NULL handle. + */ +HMODULE WINAPI SHPinDllOfCLSID(REFIID refiid) +{ + HKEY newkey; + DWORD type, count; + CHAR value[MAX_PATH], string[MAX_PATH]; + + strcpy(string, "CLSID\\"); + SHStringFromGUIDA(refiid, string + 6, sizeof(string)/sizeof(char) - 6); + strcat(string, "\\InProcServer32"); + + count = MAX_PATH; + RegOpenKeyExA(HKEY_CLASSES_ROOT, string, 0, 1, &newkey); + RegQueryValueExA(newkey, 0, 0, &type, (PBYTE)value, &count); + RegCloseKey(newkey); + return LoadLibraryExA(value, 0, 0); +} + +/************************************************************************* + * @ [SHLWAPI.237] + * + * Unicode version of SHLWAPI_183. + */ +DWORD WINAPI SHRegisterClassW(WNDCLASSW * lpWndClass) +{ + WNDCLASSW WndClass; + + TRACE("(%p %s)\n",lpWndClass->hInstance, debugstr_w(lpWndClass->lpszClassName)); + + if (GetClassInfoW(lpWndClass->hInstance, lpWndClass->lpszClassName, &WndClass)) + return TRUE; + return RegisterClassW(lpWndClass); +} + +/************************************************************************* + * @ [SHLWAPI.238] + * + * Unregister a list of classes. + * + * PARAMS + * hInst [I] Application instance that registered the classes + * lppClasses [I] List of class names + * iCount [I] Number of names in lppClasses + * + * RETURNS + * Nothing. + */ +void WINAPI SHUnregisterClassesA(HINSTANCE hInst, LPCSTR *lppClasses, INT iCount) +{ + WNDCLASSA WndClass; + + TRACE("(%p,%p,%d)\n", hInst, lppClasses, iCount); + + while (iCount > 0) + { + if (GetClassInfoA(hInst, *lppClasses, &WndClass)) + UnregisterClassA(*lppClasses, hInst); + lppClasses++; + iCount--; + } +} + +/************************************************************************* + * @ [SHLWAPI.239] + * + * Unicode version of SHUnregisterClassesA. + */ +void WINAPI SHUnregisterClassesW(HINSTANCE hInst, LPCWSTR *lppClasses, INT iCount) +{ + WNDCLASSW WndClass; + + TRACE("(%p,%p,%d)\n", hInst, lppClasses, iCount); + + while (iCount > 0) + { + if (GetClassInfoW(hInst, *lppClasses, &WndClass)) + UnregisterClassW(*lppClasses, hInst); + lppClasses++; + iCount--; + } +} + +/************************************************************************* + * @ [SHLWAPI.240] + * + * Call The correct (Ascii/Unicode) default window procedure for a window. + * + * PARAMS + * hWnd [I] Window to call the default procedure for + * uMessage [I] Message ID + * wParam [I] WPARAM of message + * lParam [I] LPARAM of message + * + * RETURNS + * The result of calling DefWindowProcA() or DefWindowProcW(). + */ +LRESULT CALLBACK SHDefWindowProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) +{ + if (IsWindowUnicode(hWnd)) + return DefWindowProcW(hWnd, uMessage, wParam, lParam); + return DefWindowProcA(hWnd, uMessage, wParam, lParam); +} + +/************************************************************************* + * @ [SHLWAPI.241] + * + */ +DWORD WINAPI StopWatchMode() +{ + FIXME("()stub\n"); + return /* 0xabba1243 */ 0; +} + +/************************************************************************* + * @ [SHLWAPI.257] + * + * Create a worker window using CreateWindowExA(). + * + * PARAMS + * wndProc [I] Window procedure + * hWndParent [I] Parent window + * dwExStyle [I] Extra style flags + * dwStyle [I] Style flags + * hMenu [I] Window menu + * z [I] Unknown + * + * RETURNS + * Success: The window handle of the newly created window. + * Failure: 0. + */ +HWND WINAPI SHCreateWorkerWindowA(LONG wndProc, HWND hWndParent, DWORD dwExStyle, + DWORD dwStyle, HMENU hMenu, LONG z) +{ + static const char* szClass = "WorkerA"; + WNDCLASSA wc; + HWND hWnd; + + TRACE("(0x%08lx,%p,0x%08lx,0x%08lx,%p,0x%08lx)\n", + wndProc, hWndParent, dwExStyle, dwStyle, hMenu, z); + + /* Create Window class */ + wc.style = 0; + wc.lpfnWndProc = DefWindowProcA; + wc.cbClsExtra = 0; + wc.cbWndExtra = 4; + wc.hInstance = shlwapi_hInstance; + wc.hIcon = (HICON)0; + wc.hCursor = LoadCursorA((HINSTANCE)0, (LPSTR)IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW; + wc.lpszMenuName = NULL; + wc.lpszClassName = szClass; + + SHRegisterClassA(&wc); /* Register class */ + + /* FIXME: Set extra bits in dwExStyle */ + + hWnd = CreateWindowExA(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0, + hWndParent, hMenu, shlwapi_hInstance, 0); + if (hWnd) + { + SetWindowLongA(hWnd, DWL_MSGRESULT, z); + + if (wndProc) + SetWindowLongA(hWnd, GWL_WNDPROC, wndProc); + } + return hWnd; +} + +typedef struct tagPOLICYDATA +{ + DWORD policy; /* flags value passed to SHRestricted */ + LPCWSTR appstr; /* application str such as "Explorer" */ + LPCWSTR keystr; /* name of the actual registry key / policy */ +} POLICYDATA, *LPPOLICYDATA; + +#define SHELL_NO_POLICY 0xffffffff + +/* default shell policy registry key */ +static WCHAR strRegistryPolicyW[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o', + 's','o','f','t','\\','W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n', + '\\','P','o','l','i','c','i','e','s',0}; + +/************************************************************************* + * @ [SHLWAPI.271] + * + * Retrieve a policy value from the registry. + * + * PARAMS + * lpSubKey [I] registry key name + * lpSubName [I] subname of registry key + * lpValue [I] value name of registry value + * + * RETURNS + * the value associated with the registry key or 0 if not found + */ +DWORD WINAPI SHGetRestriction(LPCWSTR lpSubKey, LPCWSTR lpSubName, LPCWSTR lpValue) +{ + DWORD retval, datsize = 4; + HKEY hKey; + + if (!lpSubKey) + lpSubKey = (LPCWSTR)strRegistryPolicyW; + + retval = RegOpenKeyW(HKEY_LOCAL_MACHINE, lpSubKey, &hKey); + if (retval != ERROR_SUCCESS) + retval = RegOpenKeyW(HKEY_CURRENT_USER, lpSubKey, &hKey); + if (retval != ERROR_SUCCESS) + return 0; + + SHGetValueW(hKey, lpSubName, lpValue, NULL, (LPBYTE)&retval, &datsize); + RegCloseKey(hKey); + return retval; +} + +/************************************************************************* + * @ [SHLWAPI.266] + * + * Helper function to retrieve the possibly cached value for a specific policy + * + * PARAMS + * policy [I] The policy to look for + * initial [I] Main registry key to open, if NULL use default + * polTable [I] Table of known policies, 0 terminated + * polArr [I] Cache array of policy values + * + * RETURNS + * The retrieved policy value or 0 if not successful + * + * NOTES + * This function is used by the native SHRestricted function to search for the + * policy and cache it once retrieved. The current Wine implementation uses a + * different POLICYDATA structure and implements a similar algorithme adapted to + * that structure. + */ +DWORD WINAPI SHRestrictionLookup( + DWORD policy, + LPCWSTR initial, + LPPOLICYDATA polTable, + LPDWORD polArr) +{ + TRACE("(0x%08lx %s %p %p)\n", policy, debugstr_w(initial), polTable, polArr); + + if (!polTable || !polArr) + return 0; + + for (;polTable->policy; polTable++, polArr++) + { + if (policy == polTable->policy) + { + /* we have a known policy */ + + /* check if this policy has been cached */ + if (*polArr == SHELL_NO_POLICY) + *polArr = SHGetRestriction(initial, polTable->appstr, polTable->keystr); + return *polArr; + } + } + /* we don't know this policy, return 0 */ + TRACE("unknown policy: (%08lx)\n", policy); + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.267] + * + * Get an interface from an object. + * + * RETURNS + * Success: S_OK. ppv contains the requested interface. + * Failure: An HRESULT error code. + * + * NOTES + * This QueryInterface asks the inner object for a interface. In case + * of aggregation this request would be forwarded by the inner to the + * outer object. This function asks the inner object directly for the + * interface circumventing the forwarding to the outer object. + */ +HRESULT WINAPI SHWeakQueryInterface( + IUnknown * pUnk, /* [in] Outer object */ + IUnknown * pInner, /* [in] Inner object */ + IID * riid, /* [in] Interface GUID to query for */ + LPVOID* ppv) /* [out] Destination for queried interface */ +{ + HRESULT hret = E_NOINTERFACE; + TRACE("(pUnk=%p pInner=%p\n\tIID: %s %p)\n",pUnk,pInner,debugstr_guid(riid), ppv); + + *ppv = NULL; + if(pUnk && pInner) { + hret = IUnknown_QueryInterface(pInner, riid, (LPVOID*)ppv); + if (SUCCEEDED(hret)) IUnknown_Release(pUnk); + } + TRACE("-- 0x%08lx\n", hret); + return hret; +} + +/************************************************************************* + * @ [SHLWAPI.268] + * + * Move a reference from one interface to another. + * + * PARAMS + * lpDest [O] Destination to receive the reference + * lppUnknown [O] Source to give up the reference to lpDest + * + * RETURNS + * Nothing. + */ +VOID WINAPI SHWeakReleaseInterface(IUnknown *lpDest, IUnknown **lppUnknown) +{ + TRACE("(%p,%p)\n", lpDest, lppUnknown); + + if (*lppUnknown) + { + /* Copy Reference*/ + IUnknown_AddRef(lpDest); + IUnknown_AtomicRelease(lppUnknown); /* Release existing interface */ + } +} + +/************************************************************************* + * @ [SHLWAPI.269] + * + * Convert an ASCII string of a CLSID into a CLSID. + * + * PARAMS + * idstr [I] String representing a CLSID in registry format + * id [O] Destination for the converted CLSID + * + * RETURNS + * Success: TRUE. id contains the converted CLSID. + * Failure: FALSE. + */ +BOOL WINAPI GUIDFromStringA(LPCSTR idstr, CLSID *id) +{ + WCHAR wClsid[40]; + MultiByteToWideChar(CP_ACP, 0, idstr, -1, wClsid, sizeof(wClsid)/sizeof(WCHAR)); + return SUCCEEDED(CLSIDFromStringWrap(wClsid, id)); +} + +/************************************************************************* + * @ [SHLWAPI.270] + * + * Unicode version of GUIDFromStringA. + */ +BOOL WINAPI GUIDFromStringW(LPCWSTR idstr, CLSID *id) +{ + return SUCCEEDED(CLSIDFromStringWrap(idstr, id)); +} + +/************************************************************************* + * @ [SHLWAPI.276] + * + * Determine if the browser is integrated into the shell, and set a registry + * key accordingly. + * + * PARAMS + * None. + * + * RETURNS + * 1, If the browser is not integrated. + * 2, If the browser is integrated. + * + * NOTES + * The key "HKLM\Software\Microsoft\Internet Explorer\IntegratedBrowser" is + * either set to TRUE, or removed depending on whether the browser is deemed + * to be integrated. + */ +DWORD WINAPI WhichPlatform() +{ + static LPCSTR szIntegratedBrowser = "IntegratedBrowser"; + static DWORD dwState = 0; + HKEY hKey; + DWORD dwRet, dwData, dwSize; + + if (dwState) + return dwState; + + /* If shell32 exports DllGetVersion(), the browser is integrated */ + GET_FUNC(pDllGetVersion, shell32, "DllGetVersion", 1); + dwState = pDllGetVersion ? 2 : 1; + + /* Set or delete the key accordinly */ + dwRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Internet Explorer", 0, + KEY_ALL_ACCESS, &hKey); + if (!dwRet) + { + dwRet = RegQueryValueExA(hKey, szIntegratedBrowser, 0, 0, + (LPBYTE)&dwData, &dwSize); + + if (!dwRet && dwState == 1) + { + /* Value exists but browser is not integrated */ + RegDeleteValueA(hKey, szIntegratedBrowser); + } + else if (dwRet && dwState == 2) + { + /* Browser is integrated but value does not exist */ + dwData = TRUE; + RegSetValueExA(hKey, szIntegratedBrowser, 0, REG_DWORD, + (LPBYTE)&dwData, sizeof(dwData)); + } + RegCloseKey(hKey); + } + return dwState; +} + +/************************************************************************* + * @ [SHLWAPI.278] + * + * Unicode version of SHCreateWorkerWindowA. + */ +HWND WINAPI SHCreateWorkerWindowW(LONG wndProc, HWND hWndParent, DWORD dwExStyle, + DWORD dwStyle, HMENU hMenu, LONG z) +{ + static const WCHAR szClass[] = { 'W', 'o', 'r', 'k', 'e', 'r', 'W', '\0' }; + WNDCLASSW wc; + HWND hWnd; + + TRACE("(0x%08lx,%p,0x%08lx,0x%08lx,%p,0x%08lx)\n", + wndProc, hWndParent, dwExStyle, dwStyle, hMenu, z); + + /* If our OS is natively ASCII, use the ASCII version */ + if (!(GetVersion() & 0x80000000)) /* NT */ + return SHCreateWorkerWindowA(wndProc, hWndParent, dwExStyle, dwStyle, hMenu, z); + + /* Create Window class */ + wc.style = 0; + wc.lpfnWndProc = DefWindowProcW; + wc.cbClsExtra = 0; + wc.cbWndExtra = 4; + wc.hInstance = shlwapi_hInstance; + wc.hIcon = (HICON)0; + wc.hCursor = LoadCursorA((HINSTANCE)0, (LPSTR)IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW; + wc.lpszMenuName = NULL; + wc.lpszClassName = szClass; + + SHRegisterClassW(&wc); /* Register class */ + + /* FIXME: Set extra bits in dwExStyle */ + + hWnd = CreateWindowExW(dwExStyle, szClass, 0, dwStyle, 0, 0, 0, 0, + hWndParent, hMenu, shlwapi_hInstance, 0); + if (hWnd) + { + SetWindowLongA(hWnd, DWL_MSGRESULT, z); + + if (wndProc) + SetWindowLongA(hWnd, GWL_WNDPROC, wndProc); + } + return hWnd; +} + +/************************************************************************* + * @ [SHLWAPI.279] + * + * Get and show a context menu from a shell folder. + * + * PARAMS + * hWnd [I] Window displaying the shell folder + * lpFolder [I] IShellFolder interface + * lpApidl [I] Id for the particular folder desired + * + * RETURNS + * Success: S_OK. + * Failure: An HRESULT error code indicating the error. + */ +HRESULT WINAPI SHInvokeDefaultCommand(HWND hWnd, IShellFolder* lpFolder, LPCITEMIDLIST lpApidl) +{ + return SHInvokeCommand(hWnd, lpFolder, lpApidl, FALSE); +} + +/************************************************************************* + * @ [SHLWAPI.281] + * + * _SHPackDispParamsV + */ +HRESULT WINAPI SHPackDispParamsV(LPVOID w, LPVOID x, LPVOID y, LPVOID z) +{ + FIXME("%p %p %p %p\n",w,x,y,z); + return E_FAIL; +} + +/************************************************************************* + * @ [SHLWAPI.282] + * + * This function seems to be a forward to SHPackDispParamsV (whatever THAT + * function does...). + */ +HRESULT WINAPI SHPackDispParams(LPVOID w, LPVOID x, LPVOID y, LPVOID z) +{ + FIXME("%p %p %p %p\n", w, x, y, z); + return E_FAIL; +} + +/************************************************************************* + * @ [SHLWAPI.284] + * + * _IConnectionPoint_SimpleInvoke + */ +DWORD WINAPI IConnectionPoint_SimpleInvoke( + LPVOID x, + LPVOID y, + LPVOID z) +{ + FIXME("(%p %p %p) stub\n",x,y,z); + return 0; +} + +/************************************************************************* + * @ [SHLWAPI.285] + * + * Notify an IConnectionPoint object of changes. + * + * PARAMS + * lpCP [I] Object to notify + * dispID [I] + * + * RETURNS + * Success: S_OK. + * Failure: E_NOINTERFACE, if lpCP is NULL or does not support the + * IConnectionPoint interface. + */ +HRESULT WINAPI IConnectionPoint_OnChanged(IConnectionPoint* lpCP, DISPID dispID) +{ + IEnumConnections *lpEnum; + HRESULT hRet = E_NOINTERFACE; + + TRACE("(%p,0x%8lX)\n", lpCP, dispID); + + /* Get an enumerator for the connections */ + if (lpCP) + hRet = IConnectionPoint_EnumConnections(lpCP, &lpEnum); + + if (SUCCEEDED(hRet)) + { + IPropertyNotifySink *lpSink; + CONNECTDATA connData; + ULONG ulFetched; + + /* Call OnChanged() for every notify sink in the connection point */ + while (IEnumConnections_Next(lpEnum, 1, &connData, &ulFetched) == S_OK) + { + if (SUCCEEDED(IUnknown_QueryInterface(connData.pUnk, &IID_IPropertyNotifySink, (void**)&lpSink)) && + lpSink) + { + IPropertyNotifySink_OnChanged(lpSink, dispID); + IPropertyNotifySink_Release(lpSink); + } + IUnknown_Release(connData.pUnk); + } + + IEnumConnections_Release(lpEnum); + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.287] + * + * Notify an IConnectionPointContainer object of changes. + * + * PARAMS + * lpUnknown [I] Object to notify + * dispID [I] + * + * RETURNS + * Success: S_OK. + * Failure: E_NOINTERFACE, if lpUnknown is NULL or does not support the + * IConnectionPointContainer interface. + */ +HRESULT WINAPI IUnknown_CPContainerOnChanged(IUnknown *lpUnknown, DISPID dispID) +{ + IConnectionPointContainer* lpCPC = NULL; + HRESULT hRet = E_NOINTERFACE; + + TRACE("(%p,0x%8lX)\n", lpUnknown, dispID); + + if (lpUnknown) + hRet = IUnknown_QueryInterface(lpUnknown, &IID_IConnectionPointContainer, (void**)&lpCPC); + + if (SUCCEEDED(hRet)) + { + IConnectionPoint* lpCP; + + hRet = IConnectionPointContainer_FindConnectionPoint(lpCPC, &IID_IPropertyNotifySink, &lpCP); + IConnectionPointContainer_Release(lpCPC); + + hRet = IConnectionPoint_OnChanged(lpCP, dispID); + IConnectionPoint_Release(lpCP); + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.289] + * + * See PlaySoundW. + */ +BOOL WINAPI PlaySoundWrapW(LPCWSTR pszSound, HMODULE hmod, DWORD fdwSound) +{ + GET_FUNC(pPlaySoundW, winmm, "PlaySoundW", FALSE); + return pPlaySoundW(pszSound, hmod, fdwSound); +} + +/************************************************************************* + * @ [SHLWAPI.294] + */ +BOOL WINAPI SHGetIniStringW(LPSTR str1, LPSTR str2, LPSTR pStr, DWORD some_len, LPCSTR lpStr2) +{ + /* + * str1: "I" "I" pushl esp+0x20 + * str2: "U" "I" pushl 0x77c93810 + * (is "I" and "U" "integer" and "unsigned" ??) + * + * pStr: "" "" pushl eax + * some_len: 0x824 0x104 pushl 0x824 + * lpStr2: "%l" "%l" pushl esp+0xc + * + * shlwapi. StrCpyNW(lpStr2, irrelevant_var, 0x104); + * LocalAlloc(0x00, some_len) -> irrelevant_var + * LocalAlloc(0x40, irrelevant_len) -> pStr + * shlwapi.294(str1, str2, pStr, some_len, lpStr2); + * shlwapi.PathRemoveBlanksW(pStr); + */ + FIXME("('%s', '%s', '%s', %08lx, '%s'): stub!\n", str1, str2, pStr, some_len, lpStr2); + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.295] + * + * Called by ICQ2000b install via SHDOCVW: + * str1: "InternetShortcut" + * x: some unknown pointer + * str2: "http://free.aol.com/tryaolfree/index.adp?139269" + * str3: "C:\\WINDOWS\\Desktop.new2\\Free AOL & Unlimited Internet.url" + * + * In short: this one maybe creates a desktop link :-) + */ +BOOL WINAPI SHSetIniStringW(LPWSTR str1, LPVOID x, LPWSTR str2, LPWSTR str3) +{ + FIXME("('%s', %p, '%s', '%s'), stub.\n", debugstr_w(str1), x, debugstr_w(str2), debugstr_w(str3)); + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.299] + * + * See COMCTL32_417. + */ +BOOL WINAPI ExtTextOutWrapW(HDC hdc, INT x, INT y, UINT flags, const RECT *lprect, + LPCWSTR str, UINT count, const INT *lpDx) +{ + GET_FUNC(pCOMCTL32_417, comctl32, (LPCSTR)417, FALSE); + return pCOMCTL32_417(hdc, x, y, flags, lprect, str, count, lpDx); +} + +/************************************************************************* + * @ [SHLWAPI.313] + * + * See SHGetFileInfoW. + */ +DWORD WINAPI SHGetFileInfoWrapW(LPCWSTR path, DWORD dwFileAttributes, + SHFILEINFOW *psfi, UINT sizeofpsfi, UINT flags) +{ + GET_FUNC(pSHGetFileInfoW, shell32, "SHGetFileInfoW", 0); + return pSHGetFileInfoW(path, dwFileAttributes, psfi, sizeofpsfi, flags); +} + +/************************************************************************* + * @ [SHLWAPI.318] + * + * See DragQueryFileW. + */ +UINT WINAPI DragQueryFileWrapW(HDROP hDrop, UINT lFile, LPWSTR lpszFile, UINT lLength) +{ + GET_FUNC(pDragQueryFileW, shell32, "DragQueryFileW", 0); + return pDragQueryFileW(hDrop, lFile, lpszFile, lLength); +} + +/************************************************************************* + * @ [SHLWAPI.333] + * + * See SHBrowseForFolderW. + */ +LPITEMIDLIST WINAPI SHBrowseForFolderWrapW(LPBROWSEINFOW lpBi) +{ + GET_FUNC(pSHBrowseForFolderW, shell32, "SHBrowseForFolderW", NULL); + return pSHBrowseForFolderW(lpBi); +} + +/************************************************************************* + * @ [SHLWAPI.334] + * + * See SHGetPathFromIDListW. + */ +BOOL WINAPI SHGetPathFromIDListWrapW(LPCITEMIDLIST pidl,LPWSTR pszPath) +{ + GET_FUNC(pSHGetPathFromIDListW, shell32, "SHGetPathFromIDListW", 0); + return pSHGetPathFromIDListW(pidl, pszPath); +} + +/************************************************************************* + * @ [SHLWAPI.335] + * + * See ShellExecuteExW. + */ +BOOL WINAPI ShellExecuteExWrapW(LPSHELLEXECUTEINFOW lpExecInfo) +{ + GET_FUNC(pShellExecuteExW, shell32, "ShellExecuteExW", FALSE); + return pShellExecuteExW(lpExecInfo); +} + +/************************************************************************* + * @ [SHLWAPI.336] + * + * See SHFileOperationW. + */ +HICON WINAPI SHFileOperationWrapW(LPSHFILEOPSTRUCTW lpFileOp) +{ + GET_FUNC(pSHFileOperationW, shell32, "SHFileOperationW", 0); + return pSHFileOperationW(lpFileOp); +} + +/************************************************************************* + * @ [SHLWAPI.337] + * + * See ExtractIconExW. + */ +UINT WINAPI ExtractIconExWrapW(LPCWSTR lpszFile, INT nIconIndex, HICON *phiconLarge, + HICON *phiconSmall, UINT nIcons) +{ + GET_FUNC(pExtractIconExW, shell32, "ExtractIconExW", 0); + return pExtractIconExW(lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons); +} + +/************************************************************************* + * @ [SHLWAPI.342] + * + */ +LONG WINAPI SHInterlockedCompareExchange( PLONG dest, LONG xchg, LONG compare) +{ + return InterlockedCompareExchange(dest, xchg, compare); +} + +/************************************************************************* + * @ [SHLWAPI.346] + */ +DWORD WINAPI SHUnicodeToUnicode( + LPCWSTR src, + LPWSTR dest, + int len) +{ + FIXME("(%s %p 0x%08x)stub\n",debugstr_w(src),dest,len); + lstrcpynW(dest, src, len); + return lstrlenW(dest)+1; +} + +/************************************************************************* + * @ [SHLWAPI.350] + * + * See GetFileVersionInfoSizeW. + */ +DWORD WINAPI GetFileVersionInfoSizeWrapW( + LPWSTR x, + LPVOID y) +{ + DWORD ret; + + GET_FUNC(pGetFileVersionInfoSizeW, version, "GetFileVersionInfoSizeW", 0); + ret = pGetFileVersionInfoSizeW(x, y); + return 0x208 + ret; +} + +/************************************************************************* + * @ [SHLWAPI.351] + * + * See GetFileVersionInfoW. + */ +BOOL WINAPI GetFileVersionInfoWrapW( + LPWSTR w, /* [in] path to dll */ + DWORD x, /* [in] parm 2 to GetFileVersionInfoA */ + DWORD y, /* [in] return value from SHLWAPI_350() - assume length */ + LPVOID z) /* [in/out] buffer (+0x208 sent to GetFileVersionInfoA()) */ +{ + GET_FUNC(pGetFileVersionInfoW, version, "GetFileVersionInfoW", 0); + return pGetFileVersionInfoW(w, x, y-0x208, (char*)z+0x208); +} + +/************************************************************************* + * @ [SHLWAPI.352] + * + * See VerQueryValueW. + */ +WORD WINAPI VerQueryValueWrapW( + LPVOID w, /* [in] Buffer from SHLWAPI_351() */ + LPWSTR x, /* [in] Value to retrieve - converted and passed to VerQueryValueA() as #2 */ + LPVOID y, /* [out] Ver buffer - passed to VerQueryValueA as #3 */ + UINT* z) /* [in] Ver length - passed to VerQueryValueA as #4 */ +{ + GET_FUNC(pVerQueryValueW, version, "VerQueryValueW", 0); + return pVerQueryValueW((char*)w+0x208, x, y, z); +} + +#define IsIface(type) SUCCEEDED((hRet = IUnknown_QueryInterface(lpUnknown, &IID_##type, (void**)&lpObj))) +#define IShellBrowser_EnableModeless IShellBrowser_EnableModelessSB +#define EnableModeless(type) type##_EnableModeless((type*)lpObj, bModeless) + +/************************************************************************* + * @ [SHLWAPI.355] + * + * Change the modality of a shell object. + * + * PARAMS + * lpUnknown [I] Object to make modeless + * bModeless [I] TRUE=Make modeless, FALSE=Make modal + * + * RETURNS + * Success: S_OK. The modality lpUnknown is changed. + * Failure: An HRESULT error code indicating the error. + * + * NOTES + * lpUnknown must support the IOleInPlaceFrame interface, the + * IInternetSecurityMgrSite interface, the IShellBrowser interface + * or the IDocHostUIHandler interface, or this call fails. + */ +HRESULT WINAPI IUnknown_EnableModeless(IUnknown *lpUnknown, BOOL bModeless) +{ + IUnknown *lpObj; + HRESULT hRet; + + TRACE("(%p,%d)\n", lpUnknown, bModeless); + + if (!lpUnknown) + return E_FAIL; + + if (IsIface(IOleInPlaceFrame)) + EnableModeless(IOleInPlaceFrame); + else if (IsIface(IShellBrowser)) + EnableModeless(IShellBrowser); +#if 0 + /* FIXME: Wine has no headers for these objects yet */ + else if (IsIface(IInternetSecurityMgrSite)) + EnableModeless(IInternetSecurityMgrSite); + else if (IsIface(IDocHostUIHandler)) + EnableModeless(IDocHostUIHandler); +#endif + else + return hRet; + + IUnknown_Release(lpObj); + return S_OK; +} + +/************************************************************************* + * @ [SHLWAPI.357] + * + * See SHGetNewLinkInfoW. + */ +BOOL WINAPI SHGetNewLinkInfoWrapW(LPCWSTR pszLinkTo, LPCWSTR pszDir, LPWSTR pszName, + BOOL *pfMustCopy, UINT uFlags) +{ + GET_FUNC(pSHGetNewLinkInfoW, shell32, "SHGetNewLinkInfoW", FALSE); + return pSHGetNewLinkInfoW(pszLinkTo, pszDir, pszName, pfMustCopy, uFlags); +} + +/************************************************************************* + * @ [SHLWAPI.358] + * + * See SHDefExtractIconW. + */ +UINT WINAPI SHDefExtractIconWrapW(LPCWSTR pszIconFile, int iIndex, UINT uFlags, HICON* phiconLarge, + HICON* phiconSmall, UINT nIconSize) +{ + GET_FUNC(pSHDefExtractIconW, shell32, "SHDefExtractIconW", 0); + return pSHDefExtractIconW(pszIconFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize); +} + +/************************************************************************* + * @ [SHLWAPI.363] + * + * Get and show a context menu from a shell folder. + * + * PARAMS + * hWnd [I] Window displaying the shell folder + * lpFolder [I] IShellFolder interface + * lpApidl [I] Id for the particular folder desired + * bInvokeDefault [I] Whether to invoke the default menu item + * + * RETURNS + * Success: S_OK. If bInvokeDefault is TRUE, the default menu action was + * executed. + * Failure: An HRESULT error code indicating the error. + */ +HRESULT WINAPI SHInvokeCommand(HWND hWnd, IShellFolder* lpFolder, LPCITEMIDLIST lpApidl, BOOL bInvokeDefault) +{ + IContextMenu *iContext; + HRESULT hRet = E_FAIL; + + TRACE("(%p,%p,%p,%d)\n", hWnd, lpFolder, lpApidl, bInvokeDefault); + + if (!lpFolder) + return hRet; + + /* Get the context menu from the shell folder */ + hRet = IShellFolder_GetUIObjectOf(lpFolder, hWnd, 1, &lpApidl, + &IID_IContextMenu, 0, (void**)&iContext); + if (SUCCEEDED(hRet)) + { + HMENU hMenu; + if ((hMenu = CreatePopupMenu())) + { + HRESULT hQuery; + DWORD dwDefaultId = 0; + + /* Add the context menu entries to the popup */ + hQuery = IContextMenu_QueryContextMenu(iContext, hMenu, 0, 1, 0x7FFF, + bInvokeDefault ? CMF_NORMAL : CMF_DEFAULTONLY); + + if (SUCCEEDED(hQuery)) + { + if (bInvokeDefault && + (dwDefaultId = GetMenuDefaultItem(hMenu, 0, 0)) != 0xFFFFFFFF) + { + CMINVOKECOMMANDINFO cmIci; + /* Invoke the default item */ + memset(&cmIci,0,sizeof(cmIci)); + cmIci.cbSize = sizeof(cmIci); + cmIci.fMask = CMIC_MASK_ASYNCOK; + cmIci.hwnd = hWnd; + cmIci.lpVerb = MAKEINTRESOURCEA(dwDefaultId); + cmIci.nShow = SW_SCROLLCHILDREN; + + hRet = IContextMenu_InvokeCommand(iContext, &cmIci); + } + } + DestroyMenu(hMenu); + } + IContextMenu_Release(iContext); + } + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.364] + * + * Copy one string to another, up to a given length. + * + * PARAMS + * lpszSrc [I] Source string to copy + * lpszDst [O] Destination for copied string + * iLen [I] Number of characters to copy + * + * RETURNS + * TRUE. + */ +DWORD WINAPI DoesStringRoundTripA(LPCSTR lpszSrc, LPSTR lpszDst, INT iLen) +{ + lstrcpynA(lpszDst, lpszSrc, iLen); + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.370] + * + * See ExtractIconW. + */ +HICON WINAPI ExtractIconWrapW(HINSTANCE hInstance, LPCWSTR lpszExeFileName, + UINT nIconIndex) +{ + GET_FUNC(pExtractIconW, shell32, "ExtractIconW", NULL); + return pExtractIconW(hInstance, lpszExeFileName, nIconIndex); +} + +/************************************************************************* + * @ [SHLWAPI.376] + */ +LANGID WINAPI MLGetUILanguage() +{ + FIXME("() stub\n"); + /* FIXME: This should be a forward in the .spec file to the win2k function + * kernel32.GetUserDefaultUILanguage, however that function isn't there yet. + */ + return GetUserDefaultLangID(); +} + +/************************************************************************* + * @ [SHLWAPI.377] + * + * Load a library from the directory of a particular process. + * + * PARAMS + * new_mod [I] Library name + * inst_hwnd [I] Module whose directory is to be used + * dwFlags [I] Flags controlling the load + * + * RETURNS + * Success: A handle to the loaded module + * Failure: A NULL handle. + */ +HMODULE WINAPI MLLoadLibraryA(LPCSTR new_mod, HMODULE inst_hwnd, DWORD dwFlags) +{ + /* FIXME: Native appears to do DPA_Create and a DPA_InsertPtr for + * each call here. + * FIXME: Native shows calls to: + * SHRegGetUSValue for "Software\Microsoft\Internet Explorer\International" + * CheckVersion + * RegOpenKeyExA for "HKLM\Software\Microsoft\Internet Explorer" + * RegQueryValueExA for "LPKInstalled" + * RegCloseKey + * RegOpenKeyExA for "HKCU\Software\Microsoft\Internet Explorer\International" + * RegQueryValueExA for "ResourceLocale" + * RegCloseKey + * RegOpenKeyExA for "HKLM\Software\Microsoft\Active Setup\Installed Components\{guid}" + * RegQueryValueExA for "Locale" + * RegCloseKey + * and then tests the Locale ("en" for me). + * code below + * after the code then a DPA_Create (first time) and DPA_InsertPtr are done. + */ + CHAR mod_path[2*MAX_PATH]; + LPSTR ptr; + + FIXME("(%s,%p,0x%08lx) semi-stub!\n", debugstr_a(new_mod), inst_hwnd, dwFlags); + GetModuleFileNameA(inst_hwnd, mod_path, 2*MAX_PATH); + ptr = strrchr(mod_path, '\\'); + if (ptr) { + strcpy(ptr+1, new_mod); + TRACE("loading %s\n", debugstr_a(mod_path)); + return LoadLibraryA(mod_path); + } + return NULL; +} + +/************************************************************************* + * @ [SHLWAPI.378] + * + * Unicode version of MLLoadLibraryA. + */ +HMODULE WINAPI MLLoadLibraryW(LPCWSTR new_mod, HMODULE inst_hwnd, DWORD dwFlags) +{ + WCHAR mod_path[2*MAX_PATH]; + LPWSTR ptr; + + FIXME("(%s,%p,0x%08lx) semi-stub!\n", debugstr_w(new_mod), inst_hwnd, dwFlags); + GetModuleFileNameW(inst_hwnd, mod_path, 2*MAX_PATH); + ptr = strrchrW(mod_path, '\\'); + if (ptr) { + strcpyW(ptr+1, new_mod); + TRACE("loading %s\n", debugstr_w(mod_path)); + return LoadLibraryW(mod_path); + } + return NULL; +} + +/************************************************************************* + * ColorAdjustLuma [SHLWAPI.@] + * + * Adjust the luminosity of a color + * + * PARAMS + * cRGB [I] RGB value to convert + * dwLuma [I] Luma adjustment + * bUnknown [I] Unknown + * + * RETURNS + * The adjusted RGB color. + */ +COLORREF WINAPI ColorAdjustLuma(COLORREF cRGB, int dwLuma, BOOL bUnknown) +{ + TRACE("(0x%8lx,%d,%d)\n", cRGB, dwLuma, bUnknown); + + if (dwLuma) + { + WORD wH, wL, wS; + + ColorRGBToHLS(cRGB, &wH, &wL, &wS); + + FIXME("Ignoring luma adjustment\n"); + + /* FIXME: The ajdustment is not linear */ + + cRGB = ColorHLSToRGB(wH, wL, wS); + } + return cRGB; +} + +/************************************************************************* + * @ [SHLWAPI.389] + * + * See GetSaveFileNameW. + */ +BOOL WINAPI GetSaveFileNameWrapW(LPOPENFILENAMEW ofn) +{ + GET_FUNC(pGetSaveFileNameW, comdlg32, "GetSaveFileNameW", FALSE); + return pGetSaveFileNameW(ofn); +} + +/************************************************************************* + * @ [SHLWAPI.390] + * + * See WNetRestoreConnectionW. + */ +DWORD WINAPI WNetRestoreConnectionWrapW(HWND hwndOwner, LPWSTR lpszDevice) +{ + GET_FUNC(pWNetRestoreConnectionW, mpr, "WNetRestoreConnectionW", 0); + return pWNetRestoreConnectionW(hwndOwner, lpszDevice); +} + +/************************************************************************* + * @ [SHLWAPI.391] + * + * See WNetGetLastErrorW. + */ +DWORD WINAPI WNetGetLastErrorWrapW(LPDWORD lpError, LPWSTR lpErrorBuf, DWORD nErrorBufSize, + LPWSTR lpNameBuf, DWORD nNameBufSize) +{ + GET_FUNC(pWNetGetLastErrorW, mpr, "WNetGetLastErrorW", 0); + return pWNetGetLastErrorW(lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize); +} + +/************************************************************************* + * @ [SHLWAPI.401] + * + * See PageSetupDlgW. + */ +BOOL WINAPI PageSetupDlgWrapW(LPPAGESETUPDLGW pagedlg) +{ + GET_FUNC(pPageSetupDlgW, comdlg32, "PageSetupDlgW", FALSE); + return pPageSetupDlgW(pagedlg); +} + +/************************************************************************* + * @ [SHLWAPI.402] + * + * See PrintDlgW. + */ +BOOL WINAPI PrintDlgWrapW(LPPRINTDLGW printdlg) +{ + GET_FUNC(pPrintDlgW, comdlg32, "PrintDlgW", FALSE); + return pPrintDlgW(printdlg); +} + +/************************************************************************* + * @ [SHLWAPI.403] + * + * See GetOpenFileNameW. + */ +BOOL WINAPI GetOpenFileNameWrapW(LPOPENFILENAMEW ofn) +{ + GET_FUNC(pGetOpenFileNameW, comdlg32, "GetOpenFileNameW", FALSE); + return pGetOpenFileNameW(ofn); +} + +/* INTERNAL: Map from HLS color space to RGB */ +static WORD WINAPI ConvertHue(int wHue, WORD wMid1, WORD wMid2) +{ + wHue = wHue > 240 ? wHue - 240 : wHue < 0 ? wHue + 240 : wHue; + + if (wHue > 160) + return wMid1; + else if (wHue > 120) + wHue = 160 - wHue; + else if (wHue > 40) + return wMid2; + + return ((wHue * (wMid2 - wMid1) + 20) / 40) + wMid1; +} + +/* Convert to RGB and scale into RGB range (0..255) */ +#define GET_RGB(h) (ConvertHue(h, wMid1, wMid2) * 255 + 120) / 240 + +/************************************************************************* + * ColorHLSToRGB [SHLWAPI.@] + * + * Convert from hls color space into an rgb COLORREF. + * + * PARAMS + * wHue [I] Hue amount + * wLuminosity [I] Luminosity amount + * wSaturation [I] Saturation amount + * + * RETURNS + * A COLORREF representing the converted color. + * + * NOTES + * Input hls values are constrained to the range (0..240). + */ +COLORREF WINAPI ColorHLSToRGB(WORD wHue, WORD wLuminosity, WORD wSaturation) +{ + WORD wRed; + + if (wSaturation) + { + WORD wGreen, wBlue, wMid1, wMid2; + + if (wLuminosity > 120) + wMid2 = wSaturation + wLuminosity - (wSaturation * wLuminosity + 120) / 240; + else + wMid2 = ((wSaturation + 240) * wLuminosity + 120) / 240; + + wMid1 = wLuminosity * 2 - wMid2; + + wRed = GET_RGB(wHue + 80); + wGreen = GET_RGB(wHue); + wBlue = GET_RGB(wHue - 80); + + return RGB(wRed, wGreen, wBlue); + } + + wRed = wLuminosity * 255 / 240; + return RGB(wRed, wRed, wRed); +} + +/************************************************************************* + * @ [SHLWAPI.413] + * + * Get the current docking status of the system. + * + * PARAMS + * dwFlags [I] DOCKINFO_ flags from "winbase.h", unused + * + * RETURNS + * One of DOCKINFO_UNDOCKED, DOCKINFO_UNDOCKED, or 0 if the system is not + * a notebook. + */ +DWORD WINAPI SHGetMachineInfo(DWORD dwFlags) +{ + HW_PROFILE_INFOA hwInfo; + + TRACE("(0x%08lx)\n", dwFlags); + + GetCurrentHwProfileA(&hwInfo); + switch (hwInfo.dwDockInfo & (DOCKINFO_DOCKED|DOCKINFO_UNDOCKED)) + { + case DOCKINFO_DOCKED: + case DOCKINFO_UNDOCKED: + return hwInfo.dwDockInfo & (DOCKINFO_DOCKED|DOCKINFO_UNDOCKED); + default: + return 0; + } +} + +/************************************************************************* + * @ [SHLWAPI.418] + * + * Function seems to do FreeLibrary plus other things. + * + * FIXME native shows the following calls: + * RtlEnterCriticalSection + * LocalFree + * GetProcAddress(Comctl32??, 150L) + * DPA_DeletePtr + * RtlLeaveCriticalSection + * followed by the FreeLibrary. + * The above code may be related to .377 above. + */ +BOOL WINAPI MLFreeLibrary(HMODULE hModule) +{ + FIXME("(%p) semi-stub\n", hModule); + return FreeLibrary(hModule); +} + +/************************************************************************* + * @ [SHLWAPI.430] + */ +DWORD WINAPI MLSetMLHInstance(HINSTANCE hInst, HANDLE hHeap) +{ + FIXME("(%p,%p) stub\n", hInst, hHeap); + return E_FAIL; /* This is what is used if shlwapi not loaded */ +} + +/************************************************************************* + * @ [SHLWAPI.431] + */ +DWORD WINAPI MLClearMLHInstance(DWORD x) +{ + FIXME("(0x%08lx)stub\n", x); + return 0xabba1247; +} + +/************************************************************************* + * @ [SHLWAPI.436] + * + * Convert an Unicode string CLSID into a CLSID. + * + * PARAMS + * idstr [I] string containing a CLSID in text form + * id [O] CLSID extracted from the string + * + * RETURNS + * S_OK on success or E_INVALIDARG on failure + * + * NOTES + * This is really CLSIDFromString() which is exported by ole32.dll, + * however the native shlwapi.dll does *not* import ole32. Nor does + * ole32.dll import this ordinal from shlwapi. Therefore we must conclude + * that MS duplicated the code for CLSIDFromString(), and yes they did, only + * it returns an E_INVALIDARG error code on failure. + * This is a duplicate (with changes for Unicode) of CLSIDFromString16() + * in "dlls/ole32/compobj.c". + */ +HRESULT WINAPI CLSIDFromStringWrap(LPCWSTR idstr, CLSID *id) +{ + LPCWSTR s = idstr; + BYTE *p; + INT i; + WCHAR table[256]; + + if (!s) { + memset(id, 0, sizeof(CLSID)); + return S_OK; + } + else { /* validate the CLSID string */ + + if (strlenW(s) != 38) + return E_INVALIDARG; + + if ((s[0]!=L'{') || (s[9]!=L'-') || (s[14]!=L'-') || (s[19]!=L'-') || (s[24]!=L'-') || (s[37]!=L'}')) + return E_INVALIDARG; + + for (i=1; i<37; i++) + { + if ((i == 9)||(i == 14)||(i == 19)||(i == 24)) + continue; + if (!(((s[i] >= L'0') && (s[i] <= L'9')) || + ((s[i] >= L'a') && (s[i] <= L'f')) || + ((s[i] >= L'A') && (s[i] <= L'F'))) + ) + return E_INVALIDARG; + } + } + + TRACE("%s -> %p\n", debugstr_w(s), id); + + /* quick lookup table */ + memset(table, 0, 256*sizeof(WCHAR)); + + for (i = 0; i < 10; i++) { + table['0' + i] = i; + } + for (i = 0; i < 6; i++) { + table['A' + i] = i+10; + table['a' + i] = i+10; + } + + /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */ + + p = (BYTE *) id; + + s++; /* skip leading brace */ + for (i = 0; i < 4; i++) { + p[3 - i] = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + p += 4; + s++; /* skip - */ + + for (i = 0; i < 2; i++) { + p[1-i] = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + p += 2; + s++; /* skip - */ + + for (i = 0; i < 2; i++) { + p[1-i] = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + p += 2; + s++; /* skip - */ + + /* these are just sequential bytes */ + for (i = 0; i < 2; i++) { + *p++ = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + s++; /* skip - */ + + for (i = 0; i < 6; i++) { + *p++ = table[*s]<<4 | table[*(s+1)]; + s += 2; + } + + return S_OK; +} + +/************************************************************************* + * @ [SHLWAPI.437] + * + * Determine if the OS supports a given feature. + * + * PARAMS + * dwFeature [I] Feature requested (undocumented) + * + * RETURNS + * TRUE If the feature is available. + * FALSE If the feature is not available. + */ +DWORD WINAPI IsOS(DWORD feature) +{ + FIXME("(0x%08lx) stub\n", feature); + if (feature == 4) + return TRUE; + return FALSE; +} + +/************************************************************************* + * ColorRGBToHLS [SHLWAPI.@] + * + * Convert an rgb COLORREF into the hls color space. + * + * PARAMS + * cRGB [I] Source rgb value + * pwHue [O] Destination for converted hue + * pwLuminance [O] Destination for converted luminance + * pwSaturation [O] Destination for converted saturation + * + * RETURNS + * Nothing. pwHue, pwLuminance and pwSaturation are set to the converted + * values. + * + * NOTES + * Output HLS values are constrained to the range (0..240). + * For Achromatic conversions, Hue is set to 160. + */ +VOID WINAPI ColorRGBToHLS(COLORREF cRGB, LPWORD pwHue, + LPWORD pwLuminance, LPWORD pwSaturation) +{ + int wR, wG, wB, wMax, wMin, wHue, wLuminosity, wSaturation; + + TRACE("(%08lx,%p,%p,%p)\n", cRGB, pwHue, pwLuminance, pwSaturation); + + wR = GetRValue(cRGB); + wG = GetGValue(cRGB); + wB = GetBValue(cRGB); + + wMax = max(wR, max(wG, wB)); + wMin = min(wR, min(wG, wB)); + + /* Luminosity */ + wLuminosity = ((wMax + wMin) * 240 + 255) / 510; + + if (wMax == wMin) + { + /* Achromatic case */ + wSaturation = 0; + /* Hue is now unrepresentable, but this is what native returns... */ + wHue = 160; + } + else + { + /* Chromatic case */ + int wDelta = wMax - wMin, wRNorm, wGNorm, wBNorm; + + /* Saturation */ + if (wLuminosity <= 120) + wSaturation = ((wMax + wMin)/2 + wDelta * 240) / (wMax + wMin); + else + wSaturation = ((510 - wMax - wMin)/2 + wDelta * 240) / (510 - wMax - wMin); + + /* Hue */ + wRNorm = (wDelta/2 + wMax * 40 - wR * 40) / wDelta; + wGNorm = (wDelta/2 + wMax * 40 - wG * 40) / wDelta; + wBNorm = (wDelta/2 + wMax * 40 - wB * 40) / wDelta; + + if (wR == wMax) + wHue = wBNorm - wGNorm; + else if (wG == wMax) + wHue = 80 + wRNorm - wBNorm; + else + wHue = 160 + wGNorm - wRNorm; + if (wHue < 0) + wHue += 240; + else if (wHue > 240) + wHue -= 240; + } + if (pwHue) + *pwHue = wHue; + if (pwLuminance) + *pwLuminance = wLuminosity; + if (pwSaturation) + *pwSaturation = wSaturation; +} + +/************************************************************************* + * SHCreateShellPalette [SHLWAPI.@] + */ +HPALETTE WINAPI SHCreateShellPalette(HDC hdc) +{ + FIXME("stub\n"); + return CreateHalftonePalette(hdc); +} + +/************************************************************************* + * SHGetInverseCMAP (SHLWAPI.@) + * + * Get an inverse color map table. + * + * PARAMS + * lpCmap [O] Destination for color map + * dwSize [I] Size of memory pointed to by lpCmap + * + * RETURNS + * Success: S_OK. + * Failure: E_POINTER, If lpCmap is invalid. + * E_INVALIDARG, If dwFlags is invalid + * E_OUTOFMEMORY, If there is no memory available + * + * NOTES + * dwSize may only be CMAP_PTR_SIZE (4) or CMAP_SIZE (8192). + * If dwSize = CMAP_PTR_SIZE, *lpCmap is set to the address of this DLL's + * internal CMap. + * If dwSize = CMAP_SIZE, lpCmap is filled with a copy of the data from + * this DLL's internal CMap. + */ +HRESULT WINAPI SHGetInverseCMAP(LPDWORD dest, DWORD dwSize) +{ + if (dwSize == 4) { + FIXME(" - returning bogus address for SHGetInverseCMAP\n"); + *dest = (DWORD)0xabba1249; + return 0; + } + FIXME("(%p, %#lx) stub\n", dest, dwSize); + return 0; +} + +/************************************************************************* + * SHIsLowMemoryMachine [SHLWAPI.@] + * + * Determine if the current computer has low memory. + * + * PARAMS + * x [I] FIXME + * + * RETURNS + * TRUE if the users machine has 16 Megabytes of memory or less, + * FALSE otherwise. + */ +BOOL WINAPI SHIsLowMemoryMachine (DWORD x) +{ + FIXME("(0x%08lx) stub\n", x); + return FALSE; +} + +/************************************************************************* + * GetMenuPosFromID [SHLWAPI.@] + * + * Return the position of a menu item from its Id. + * + * PARAMS + * hMenu [I] Menu containing the item + * wID [I] Id of the menu item + * + * RETURNS + * Success: The index of the menu item in hMenu. + * Failure: -1, If the item is not found. + */ +INT WINAPI GetMenuPosFromID(HMENU hMenu, UINT wID) +{ + MENUITEMINFOA mi; + INT nCount = GetMenuItemCount(hMenu), nIter = 0; + + while (nIter < nCount) + { + mi.wID = 0; + if (!GetMenuItemInfoA(hMenu, nIter, TRUE, &mi) && mi.wID == wID) + return nIter; + nIter++; + } + return -1; +} + +/************************************************************************* + * SHSkipJunction [SHLWAPI.@] + * + * Determine if a bind context can be bound to an object + * + * PARAMS + * pbc [I] Bind context to check + * pclsid [I] CLSID of object to be bound to + * + * RETURNS + * TRUE: If it is safe to bind + * FALSE: If pbc is invalid or binding would not be safe + * + */ +BOOL WINAPI SHSkipJunction(IBindCtx *pbc, const CLSID *pclsid) +{ + static WCHAR szSkipBinding[] = { 'S','k','i','p',' ', + 'B','i','n','d','i','n','g',' ','C','L','S','I','D','\0' }; + BOOL bRet = FALSE; + + if (pbc) + { + IUnknown* lpUnk; + + if (SUCCEEDED(IBindCtx_GetObjectParam(pbc, szSkipBinding, &lpUnk))) + { + CLSID clsid; + + if (SUCCEEDED(IUnknown_GetClassID(lpUnk, &clsid)) && + IsEqualGUID(pclsid, &clsid)) + bRet = TRUE; + + IUnknown_Release(lpUnk); + } + } + return bRet; +} + +DWORD WINAPI SHGetShellKey(DWORD a, DWORD b, DWORD c) +{ + FIXME("(%lx, %lx, %lx): stub\n", a, b, c); + return 0x50; +} + +HRESULT WINAPI SHQueueUserWorkItem(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f, DWORD g) +{ + FIXME("(%lx, %lx, %lx, %lx, %lx, %lx, %lx): stub\n", a, b, c, d, e, f, g); + return E_FAIL; +} + +DWORD WINAPI IUnknown_OnFocusChangeIS(IUnknown * pUnk, IUnknown * pFocusObject, BOOL bChange) +{ + FIXME("(%p, %p, %s)\n", pUnk, pFocusObject, bChange ? "TRUE" : "FALSE"); + +/* + IInputObjectSite * pIOS = NULL; + if (SUCCEEDED(IUnknown_QueryInterface(pUnk, &IID_IInputObjectSite, (void **)&pIOS)) + IInputObjectSite_OnFocusChangeIS(pIOS, pFocusObject, bChange); +*/ + + return 0; +} + +HRESULT WINAPI SKGetValueW(DWORD a, LPWSTR b, LPWSTR c, DWORD d, DWORD e, DWORD f) +{ + FIXME("(%lx, %s, %s, %lx, %lx, %lx): stub\n", a, debugstr_w(b), debugstr_w(c), d, e, f); + return E_FAIL; +} diff --git a/reactos/lib/shlwapi/path.c b/reactos/lib/shlwapi/path.c new file mode 100644 index 00000000000..5260cf7bc91 --- /dev/null +++ b/reactos/lib/shlwapi/path.c @@ -0,0 +1,3902 @@ +/* + * Path Functions + * + * Copyright 1999, 2000 Juergen Schmied + * Copyright 2001, 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 "config.h" +#include "wine/port.h" + +#include +#include +#include + +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#include "wine/unicode.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winreg.h" +#define NO_SHLWAPI_STREAM +#include "shlwapi.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* Get a function pointer from a DLL handle */ +#define GET_FUNC(func, module, name, fail) \ + do { \ + if (!func) { \ + if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \ + func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \ + if (!func) return fail; \ + } \ + } while (0) + +/* DLL handles for late bound calls */ +extern HMODULE SHLWAPI_hshell32; + +/* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */ +typedef BOOL (WINAPI *fnpIsNetDrive)(int); +static fnpIsNetDrive pIsNetDrive; + +HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD); + +/************************************************************************* + * PathAppendA [SHLWAPI.@] + * + * Append one path to another. + * + * PARAMS + * lpszPath [I/O] Initial part of path, and destination for output + * lpszAppend [I] Path to append + * + * RETURNS + * Success: TRUE. lpszPath contains the newly created path. + * Failure: FALSE, if either path is NULL, or PathCombineA() fails. + * + * NOTES + * lpszAppend must contain at least one backslash ('\') if not NULL. + * Because PathCombineA() is used to join the paths, the resulting + * path is also canonicalized. + */ +BOOL WINAPI PathAppendA (LPSTR lpszPath, LPCSTR lpszAppend) +{ + TRACE("(%s,%s)\n",debugstr_a(lpszPath), debugstr_a(lpszAppend)); + + if (lpszPath && lpszAppend) + { + if (!PathIsUNCA(lpszAppend)) + while (*lpszAppend == '\\') + lpszAppend++; + if (PathCombineA(lpszPath, lpszPath, lpszAppend)) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathAppendW [SHLWAPI.@] + * + * See PathAppendA. + */ +BOOL WINAPI PathAppendW(LPWSTR lpszPath, LPCWSTR lpszAppend) +{ + TRACE("(%s,%s)\n",debugstr_w(lpszPath), debugstr_w(lpszAppend)); + + if (lpszPath && lpszAppend) + { + if (!PathIsUNCW(lpszAppend)) + while (*lpszAppend == '\\') + lpszAppend++; + if (PathCombineW(lpszPath, lpszPath, lpszAppend)) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathCombineA [SHLWAPI.@] + * + * Combine two paths together. + * + * PARAMS + * lpszDest [O] Destination for combined path + * lpszDir [I] Directory path + * lpszFile [I] File path + * + * RETURNS + * Success: The output path + * Failure: NULL, if inputs are invalid. + * + * NOTES + * lpszDest should be at least MAX_PATH in size, and may point to the same + * memory location as lpszDir. The combined path is canonicalised. + */ +LPSTR WINAPI PathCombineA(LPSTR lpszDest, LPCSTR lpszDir, LPCSTR lpszFile) +{ + TRACE("(%p,%s,%s)\n", lpszDest, debugstr_a(lpszDir), debugstr_a(lpszFile)); + + if (!lpszDest || (!lpszDir && !lpszFile)) + return NULL; /* Invalid parameters */ + else + { + WCHAR szDest[MAX_PATH]; + WCHAR szDir[MAX_PATH]; + WCHAR szFile[MAX_PATH]; + if (lpszDir) + MultiByteToWideChar(0,0,lpszDir,-1,szDir,MAX_PATH); + if (lpszFile) + MultiByteToWideChar(0,0,lpszFile,-1,szFile,MAX_PATH); + PathCombineW(szDest, lpszDir ? szDir : NULL, lpszFile ? szFile : NULL); + WideCharToMultiByte(0,0,szDest,-1,lpszDest,MAX_PATH,0,0); + } + return lpszDest; +} + +/************************************************************************* + * PathCombineW [SHLWAPI.@] + * + * See PathCombineA. + */ +LPWSTR WINAPI PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile) +{ + WCHAR szTemp[MAX_PATH]; + BOOL bUseBoth = FALSE, bStrip = FALSE; + + TRACE("(%p,%s,%s)\n", lpszDest, debugstr_w(lpszDir), debugstr_w(lpszFile)); + + if (!lpszDest || (!lpszDir && !lpszFile)) + return lpszDest; /* Invalid parameters */ + + if (!lpszFile || !*lpszFile) + { + /* Use dir only */ + strncpyW(szTemp, lpszDir, MAX_PATH); + } + else if (!lpszDir || !*lpszDir || !PathIsRelativeW(lpszFile)) + { + if (!lpszDir || !*lpszDir || *lpszFile != '\\' || PathIsUNCW(lpszFile)) + { + /* Use file only */ + strncpyW(szTemp, lpszFile, MAX_PATH); + } + else + { + bUseBoth = TRUE; + bStrip = TRUE; + } + } + else + bUseBoth = TRUE; + + if (bUseBoth) + { + strncpyW(szTemp, lpszDir, MAX_PATH); + if (bStrip) + { + PathStripToRootW(szTemp); + lpszFile++; /* Skip '\' */ + } + if (!PathAddBackslashW(szTemp)) + return NULL; + if (strlenW(szTemp) + strlenW(lpszFile) >= MAX_PATH) + return NULL; + strcatW(szTemp, lpszFile); + } + + PathCanonicalizeW(lpszDest, szTemp); + return lpszDest; +} + +/************************************************************************* + * PathAddBackslashA [SHLWAPI.@] + * + * Append a backslash ('\') to a path if one doesn't exist. + * + * PARAMS + * lpszPath [I/O] The path to append a backslash to. + * + * RETURNS + * Success: The position of the last backslash in the path. + * Failure: NULL, if lpszPath is NULL or the path is too large. + */ +LPSTR WINAPI PathAddBackslashA(LPSTR lpszPath) +{ + size_t iLen; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath || (iLen = strlen(lpszPath)) >= MAX_PATH) + return NULL; + + if (iLen) + { + lpszPath += iLen; + if (lpszPath[-1] != '\\') + { + *lpszPath++ = '\\'; + *lpszPath = '\0'; + } + } + return lpszPath; +} + +/************************************************************************* + * PathAddBackslashW [SHLWAPI.@] + * + * See PathAddBackslashA. + */ +LPWSTR WINAPI PathAddBackslashW( LPWSTR lpszPath ) +{ + size_t iLen; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath || (iLen = strlenW(lpszPath)) >= MAX_PATH) + return NULL; + + if (iLen) + { + lpszPath += iLen; + if (lpszPath[-1] != '\\') + { + *lpszPath++ = '\\'; + *lpszPath = '\0'; + } + } + return lpszPath; +} + +/************************************************************************* + * PathBuildRootA [SHLWAPI.@] + * + * Create a root drive string (e.g. "A:\") from a drive number. + * + * PARAMS + * lpszPath [O] Destination for the drive string + * + * RETURNS + * lpszPath + * + * NOTES + * If lpszPath is NULL or drive is invalid, nothing is written to lpszPath. + */ +LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive) +{ + TRACE("(%p,%d)\n", lpszPath, drive); + + if (lpszPath && drive >= 0 && drive < 26) + { + lpszPath[0] = 'A' + drive; + lpszPath[1] = ':'; + lpszPath[2] = '\\'; + lpszPath[3] = '\0'; + } + return lpszPath; +} + +/************************************************************************* + * PathBuildRootW [SHLWAPI.@] + * + * See PathBuildRootA. + */ +LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive) +{ + TRACE("(%p,%d)\n", lpszPath, drive); + + if (lpszPath && drive >= 0 && drive < 26) + { + lpszPath[0] = 'A' + drive; + lpszPath[1] = ':'; + lpszPath[2] = '\\'; + lpszPath[3] = '\0'; + } + return lpszPath; +} + +/************************************************************************* + * PathFindFileNameA [SHLWAPI.@] + * + * Locate the start of the file name in a path + * + * PARAMS + * lpszPath [I] Path to search + * + * RETURNS + * A pointer to the first character of the file name + */ +LPSTR WINAPI PathFindFileNameA(LPCSTR lpszPath) +{ + LPCSTR lastSlash = lpszPath; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + while (lpszPath && *lpszPath) + { + if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && + lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') + lastSlash = lpszPath + 1; + lpszPath = CharNextA(lpszPath); + } + return (LPSTR)lastSlash; +} + +/************************************************************************* + * PathFindFileNameW [SHLWAPI.@] + * + * See PathFindFileNameA. + */ +LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath) +{ + LPCWSTR lastSlash = lpszPath; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + while (lpszPath && *lpszPath) + { + if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && + lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') + lastSlash = lpszPath + 1; + lpszPath = CharNextW(lpszPath); + } + return (LPWSTR)lastSlash; +} + +/************************************************************************* + * PathFindExtensionA [SHLWAPI.@] + * + * Locate the start of the file extension in a path + * + * PARAMS + * lpszPath [I] The path to search + * + * RETURNS + * A pointer to the first character of the extension, the end of + * the string if the path has no extension, or NULL If lpszPath is NULL + */ +LPSTR WINAPI PathFindExtensionA( LPCSTR lpszPath ) +{ + LPCSTR lastpoint = NULL; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath==' ') + lastpoint = NULL; + else if (*lpszPath == '.') + lastpoint = lpszPath; + lpszPath = CharNextA(lpszPath); + } + } + return (LPSTR)(lastpoint ? lastpoint : lpszPath); +} + +/************************************************************************* + * PathFindExtensionW [SHLWAPI.@] + * + * See PathFindExtensionA. + */ +LPWSTR WINAPI PathFindExtensionW( LPCWSTR lpszPath ) +{ + LPCWSTR lastpoint = NULL; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath==' ') + lastpoint = NULL; + else if (*lpszPath == '.') + lastpoint = lpszPath; + lpszPath = CharNextW(lpszPath); + } + } + return (LPWSTR)(lastpoint ? lastpoint : lpszPath); +} + +/************************************************************************* + * PathGetArgsA [SHLWAPI.@] + * + * Find the next argument in a string delimited by spaces. + * + * PARAMS + * lpszPath [I] The string to search for arguments in + * + * RETURNS + * The start of the next argument in lpszPath, or NULL if lpszPath is NULL + * + * NOTES + * Spaces in quoted strings are ignored as delimiters. + */ +LPSTR WINAPI PathGetArgsA(LPCSTR lpszPath) +{ + BOOL bSeenQuote = FALSE; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if ((*lpszPath==' ') && !bSeenQuote) + return (LPSTR)lpszPath + 1; + if (*lpszPath == '"') + bSeenQuote = !bSeenQuote; + lpszPath = CharNextA(lpszPath); + } + } + return (LPSTR)lpszPath; +} + +/************************************************************************* + * PathGetArgsW [SHLWAPI.@] + * + * See PathGetArgsA. + */ +LPWSTR WINAPI PathGetArgsW(LPCWSTR lpszPath) +{ + BOOL bSeenQuote = FALSE; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if ((*lpszPath==' ') && !bSeenQuote) + return (LPWSTR)lpszPath + 1; + if (*lpszPath == '"') + bSeenQuote = !bSeenQuote; + lpszPath = CharNextW(lpszPath); + } + } + return (LPWSTR)lpszPath; +} + +/************************************************************************* + * PathGetDriveNumberA [SHLWAPI.@] + * + * Return the drive number from a path + * + * PARAMS + * lpszPath [I] Path to get the drive number from + * + * RETURNS + * Success: The drive number corresponding to the drive in the path + * Failure: -1, if lpszPath contains no valid drive + */ +int WINAPI PathGetDriveNumberA(LPCSTR lpszPath) +{ + TRACE ("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath && !IsDBCSLeadByte(*lpszPath) && lpszPath[1] == ':' && + tolower(*lpszPath) >= 'a' && tolower(*lpszPath) <= 'z') + return tolower(*lpszPath) - 'a'; + return -1; +} + +/************************************************************************* + * PathGetDriveNumberW [SHLWAPI.@] + * + * See PathGetDriveNumberA. + */ +int WINAPI PathGetDriveNumberW(LPCWSTR lpszPath) +{ + TRACE ("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath && lpszPath[1] == ':' && + tolowerW(*lpszPath) >= 'a' && tolowerW(*lpszPath) <= 'z') + return tolowerW(*lpszPath) - 'a'; + return -1; +} + +/************************************************************************* + * PathRemoveFileSpecA [SHLWAPI.@] + * + * Remove the file specification from a path. + * + * PARAMS + * lpszPath [I/O] Path to remove the file spec from + * + * RETURNS + * TRUE If the path was valid and modified + * FALSE Otherwise + */ +BOOL WINAPI PathRemoveFileSpecA(LPSTR lpszPath) +{ + LPSTR lpszFileSpec = lpszPath; + BOOL bModified = FALSE; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if(lpszPath) + { + /* Skip directory or UNC path */ + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + + while (*lpszPath) + { + if(*lpszPath == '\\') + lpszFileSpec = lpszPath; /* Skip dir */ + else if(*lpszPath == ':') + { + lpszFileSpec = ++lpszPath; /* Skip drive */ + if (*lpszPath == '\\') + lpszFileSpec++; + } + if (!(lpszPath = CharNextA(lpszPath))) + break; + } + + if (*lpszFileSpec) + { + *lpszFileSpec = '\0'; + bModified = TRUE; + } + } + return bModified; +} + +/************************************************************************* + * PathRemoveFileSpecW [SHLWAPI.@] + * + * See PathRemoveFileSpecA. + */ +BOOL WINAPI PathRemoveFileSpecW(LPWSTR lpszPath) +{ + LPWSTR lpszFileSpec = lpszPath; + BOOL bModified = FALSE; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if(lpszPath) + { + /* Skip directory or UNC path */ + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + + while (*lpszPath) + { + if(*lpszPath == '\\') + lpszFileSpec = lpszPath; /* Skip dir */ + else if(*lpszPath == ':') + { + lpszFileSpec = ++lpszPath; /* Skip drive */ + if (*lpszPath == '\\') + lpszFileSpec++; + } + if (!(lpszPath = CharNextW(lpszPath))) + break; + } + + if (*lpszFileSpec) + { + *lpszFileSpec = '\0'; + bModified = TRUE; + } + } + return bModified; +} + +/************************************************************************* + * PathStripPathA [SHLWAPI.@] + * + * Remove the initial path from the beginning of a filename + * + * PARAMS + * lpszPath [I/O] Path to remove the initial path from + * + * RETURNS + * Nothing. + */ +void WINAPI PathStripPathA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + LPSTR lpszFileName = PathFindFileNameA(lpszPath); + if(lpszFileName) + RtlMoveMemory(lpszPath, lpszFileName, strlen(lpszFileName)+1); + } +} + +/************************************************************************* + * PathStripPathW [SHLWAPI.@] + * + * See PathStripPathA. + */ +void WINAPI PathStripPathW(LPWSTR lpszPath) +{ + LPWSTR lpszFileName; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + lpszFileName = PathFindFileNameW(lpszPath); + if(lpszFileName) + RtlMoveMemory(lpszPath, lpszFileName, (strlenW(lpszFileName)+1)*sizeof(WCHAR)); +} + +/************************************************************************* + * PathStripToRootA [SHLWAPI.@] + * + * Reduce a path to its root. + * + * PARAMS + * lpszPath [I/O] the path to reduce + * + * RETURNS + * Success: TRUE if the stripped path is a root path + * Failure: FALSE if the path cannot be stripped or is NULL + */ +BOOL WINAPI PathStripToRootA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + while(!PathIsRootA(lpszPath)) + if (!PathRemoveFileSpecA(lpszPath)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathStripToRootW [SHLWAPI.@] + * + * See PathStripToRootA. + */ +BOOL WINAPI PathStripToRootW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + while(!PathIsRootW(lpszPath)) + if (!PathRemoveFileSpecW(lpszPath)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathRemoveArgsA [SHLWAPI.@] + * + * Strip space separated arguments from a path. + * + * PARAMS + * lpszPath [I/O] Path to remove arguments from + * + * RETURNS + * Nothing. + */ +void WINAPI PathRemoveArgsA(LPSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if(lpszPath) + { + LPSTR lpszArgs = PathGetArgsA(lpszPath); + if (*lpszArgs) + lpszArgs[-1] = '\0'; + else + { + LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs); + if(*lpszLastChar == ' ') + *lpszLastChar = '\0'; + } + } +} + +/************************************************************************* + * PathRemoveArgsW [SHLWAPI.@] + * + * See PathRemoveArgsA. + */ +void WINAPI PathRemoveArgsW(LPWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if(lpszPath) + { + LPWSTR lpszArgs = PathGetArgsW(lpszPath); + if (*lpszArgs) + lpszArgs[-1] = '\0'; + else + { + LPWSTR lpszLastChar = CharPrevW(lpszPath, lpszArgs); + if(*lpszLastChar == ' ') + *lpszLastChar = '\0'; + } + } +} + +/************************************************************************* + * PathRemoveExtensionA [SHLWAPI.@] + * + * Remove the file extension from a path + * + * PARAMS + * lpszPath [I/O] Path to remove the extension from + * + * RETURNS + * Nothing. + */ +void WINAPI PathRemoveExtensionA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + lpszPath = PathFindExtensionA(lpszPath); + *lpszPath = '\0'; + } +} + +/************************************************************************* + * PathRemoveExtensionW [SHLWAPI.@] + * + * See PathRemoveExtensionA. +*/ +void WINAPI PathRemoveExtensionW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath) + { + lpszPath = PathFindExtensionW(lpszPath); + *lpszPath = '\0'; + } +} + +/************************************************************************* + * PathRemoveBackslashA [SHLWAPI.@] + * + * Remove a trailing backslash from a path. + * + * PARAMS + * lpszPath [I/O] Path to remove backslash from + * + * RETURNS + * Success: A pointer to the end of the path + * Failure: NULL, if lpszPath is NULL + */ +LPSTR WINAPI PathRemoveBackslashA( LPSTR lpszPath ) +{ + LPSTR szTemp = NULL; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(lpszPath) + { + szTemp = CharPrevA(lpszPath, lpszPath + strlen(lpszPath)); + if (!PathIsRootA(lpszPath) && *szTemp == '\\') + *szTemp = '\0'; + } + return szTemp; +} + +/************************************************************************* + * PathRemoveBackslashW [SHLWAPI.@] + * + * See PathRemoveBackslashA. + */ +LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpszPath ) +{ + LPWSTR szTemp = NULL; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(lpszPath) + { + szTemp = CharPrevW(lpszPath, lpszPath + strlenW(lpszPath)); + if (!PathIsRootW(lpszPath) && *szTemp == '\\') + *szTemp = '\0'; + } + return szTemp; +} + +/************************************************************************* + * PathRemoveBlanksA [SHLWAPI.@] + * + * Remove Spaces from the start and end of a path. + * + * PARAMS + * lpszPath [I/O] Path to strip blanks from + * + * RETURNS + * Nothing. + */ +VOID WINAPI PathRemoveBlanksA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(lpszPath && *lpszPath) + { + LPSTR start = lpszPath; + + while (*lpszPath == ' ') + lpszPath = CharNextA(lpszPath); + + while(*lpszPath) + *start++ = *lpszPath++; + + if (start != lpszPath) + while (start[-1] == ' ') + start--; + *start = '\0'; + } +} + +/************************************************************************* + * PathRemoveBlanksW [SHLWAPI.@] + * + * See PathRemoveBlanksA. + */ +VOID WINAPI PathRemoveBlanksW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(lpszPath && *lpszPath) + { + LPWSTR start = lpszPath; + + while (*lpszPath == ' ') + lpszPath++; + + while(*lpszPath) + *start++ = *lpszPath++; + + if (start != lpszPath) + while (start[-1] == ' ') + start--; + *start = '\0'; + } +} + +/************************************************************************* + * PathQuoteSpacesA [SHLWAPI.@] + * + * Surround a path containg spaces in quotes. + * + * PARAMS + * lpszPath [I/O] Path to quote + * + * RETURNS + * Nothing. + * + * NOTES + * The path is not changed if it is invalid or has no spaces. + */ +VOID WINAPI PathQuoteSpacesA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(lpszPath && StrChrA(lpszPath,' ')) + { + size_t iLen = strlen(lpszPath) + 1; + + if (iLen + 2 < MAX_PATH) + { + memmove(lpszPath + 1, lpszPath, iLen); + lpszPath[0] = '"'; + lpszPath[iLen] = '"'; + lpszPath[iLen + 1] = '\0'; + } + } +} + +/************************************************************************* + * PathQuoteSpacesW [SHLWAPI.@] + * + * See PathQuoteSpacesA. + */ +VOID WINAPI PathQuoteSpacesW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(lpszPath && StrChrW(lpszPath,' ')) + { + int iLen = strlenW(lpszPath) + 1; + + if (iLen + 2 < MAX_PATH) + { + memmove(lpszPath + 1, lpszPath, iLen * sizeof(WCHAR)); + lpszPath[0] = '"'; + lpszPath[iLen] = '"'; + lpszPath[iLen + 1] = '\0'; + } + } +} + +/************************************************************************* + * PathUnquoteSpacesA [SHLWAPI.@] + * + * Remove quotes ("") from around a path, if present. + * + * PARAMS + * lpszPath [I/O] Path to strip quotes from + * + * RETURNS + * Nothing + * + * NOTES + * If the path contains a single quote only, an empty string will result. + * Otherwise quotes are only removed if they appear at the start and end + * of the path. + */ +VOID WINAPI PathUnquoteSpacesA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath == '"') + { + DWORD dwLen = strlen(lpszPath) - 1; + + if (lpszPath[dwLen] == '"') + { + lpszPath[dwLen] = '\0'; + for (; *lpszPath; lpszPath++) + *lpszPath = lpszPath[1]; + } + } +} + +/************************************************************************* + * PathUnquoteSpacesW [SHLWAPI.@] + * + * See PathUnquoteSpacesA. + */ +VOID WINAPI PathUnquoteSpacesW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && *lpszPath == '"') + { + DWORD dwLen = strlenW(lpszPath) - 1; + + if (lpszPath[dwLen] == '"') + { + lpszPath[dwLen] = '\0'; + for (; *lpszPath; lpszPath++) + *lpszPath = lpszPath[1]; + } + } +} + +/************************************************************************* + * PathParseIconLocationA [SHLWAPI.@] + * + * Parse the location of an icon from a path. + * + * PARAMS + * lpszPath [I/O] The path to parse the icon location from. + * + * RETURNS + * Success: The number of the icon + * Failure: 0 if the path does not contain an icon location or is NULL + * + * NOTES + * The path has surrounding quotes and spaces removed regardless + * of whether the call succeeds or not. + */ +int WINAPI PathParseIconLocationA(LPSTR lpszPath) +{ + int iRet = 0; + LPSTR lpszComma; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + if ((lpszComma = strchr(lpszPath, ','))) + { + *lpszComma++ = '\0'; + iRet = StrToIntA(lpszComma); + } + PathUnquoteSpacesA(lpszPath); + PathRemoveBlanksA(lpszPath); + } + return iRet; +} + +/************************************************************************* + * PathParseIconLocationW [SHLWAPI.@] + * + * See PathParseIconLocationA. + */ +int WINAPI PathParseIconLocationW(LPWSTR lpszPath) +{ + int iRet = 0; + LPWSTR lpszComma; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath) + { + if ((lpszComma = StrChrW(lpszPath, ','))) + { + *lpszComma++ = '\0'; + iRet = StrToIntW(lpszComma); + } + PathUnquoteSpacesW(lpszPath); + PathRemoveBlanksW(lpszPath); + } + return iRet; +} + +/************************************************************************* + * @ [SHLWAPI.4] + * + * Unicode version of PathFileExistsDefExtA. + */ +BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich) +{ + static const WCHAR pszExts[7][5] = { { '.', 'p', 'i', 'f', 0}, + { '.', 'c', 'o', 'm', 0}, + { '.', 'e', 'x', 'e', 0}, + { '.', 'b', 'a', 't', 0}, + { '.', 'l', 'n', 'k', 0}, + { '.', 'c', 'm', 'd', 0}, + { 0, 0, 0, 0, 0} }; + + TRACE("(%s,%ld)\n", debugstr_w(lpszPath), dwWhich); + + if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath)) + return FALSE; + + if (dwWhich) + { + LPCWSTR szExt = PathFindExtensionW(lpszPath); + if (!*szExt || dwWhich & 0x40) + { + size_t iChoose = 0; + int iLen = lstrlenW(lpszPath); + if (iLen > (MAX_PATH - 5)) + return FALSE; + while (dwWhich & 0x1 && iChoose < sizeof(pszExts)) + { + lstrcpyW(lpszPath + iLen, pszExts[iChoose]); + if (PathFileExistsW(lpszPath)) + return TRUE; + iChoose++; + dwWhich >>= 1; + } + *(lpszPath + iLen) = (WCHAR)'\0'; + return FALSE; + } + } + return PathFileExistsW(lpszPath); +} + +/************************************************************************* + * @ [SHLWAPI.3] + * + * Determine if a file exists locally and is of an executable type. + * + * PARAMS + * lpszPath [I/O] File to search for + * dwWhich [I] Type of executable to search for + * + * RETURNS + * TRUE If the file was found. lpszPath contains the file name. + * FALSE Otherwise. + * + * NOTES + * lpszPath is modified in place and must be at least MAX_PATH in length. + * If the function returns FALSE, the path is modified to its orginal state. + * If the given path contains an extension or dwWhich is 0, executable + * extensions are not checked. + * + * Ordinals 3-6 are a classic case of MS exposing limited functionality to + * users (here through PathFindOnPathA()) and keeping advanced functionality for + * their own developers exclusive use. Monopoly, anyone? + */ +BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich) +{ + BOOL bRet = FALSE; + + TRACE("(%s,%ld)\n", debugstr_a(lpszPath), dwWhich); + + if (lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathFileExistsDefExtW(szPath, dwWhich); + if (bRet) + WideCharToMultiByte(0,0,szPath,-1,lpszPath,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * SHLWAPI_PathFindInOtherDirs + * + * Internal helper for SHLWAPI_PathFindOnPathExA/W. + */ +static BOOL WINAPI SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich) +{ + static WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'}; + static WCHAR szPath[] = { 'P','A','T','H','\0'}; + DWORD dwLenPATH; + LPCWSTR lpszCurr; + WCHAR *lpszPATH; + WCHAR buff[MAX_PATH]; + + TRACE("(%s,%08lx)\n", debugstr_w(lpszFile), dwWhich); + + /* Try system directories */ + GetSystemDirectoryW(buff, MAX_PATH); + if (!PathAppendW(buff, lpszFile)) + return FALSE; + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + GetWindowsDirectoryW(buff, MAX_PATH); + if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile)) + return FALSE; + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + GetWindowsDirectoryW(buff, MAX_PATH); + if (!PathAppendW(buff, lpszFile)) + return FALSE; + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + /* Try dirs listed in %PATH% */ + dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH); + + if (!dwLenPATH || !(lpszPATH = malloc((dwLenPATH + 1) * sizeof (WCHAR)))) + return FALSE; + + GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1); + lpszCurr = lpszPATH; + while (lpszCurr) + { + LPCWSTR lpszEnd = lpszCurr; + LPWSTR pBuff = buff; + + while (*lpszEnd == ' ') + lpszEnd++; + while (*lpszEnd && *lpszEnd != ';') + *pBuff++ = *lpszEnd++; + *pBuff = '\0'; + + if (*lpszEnd) + lpszCurr = lpszEnd + 1; + else + lpszCurr = NULL; /* Last Path, terminate after this */ + + if (!PathAppendW(buff, lpszFile)) + { + free(lpszPATH); + return FALSE; + } + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + free(lpszPATH); + return TRUE; + } + } + free(lpszPATH); + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.5] + * + * Search a range of paths for a specific type of executable. + * + * PARAMS + * lpszFile [I/O] File to search for + * lppszOtherDirs [I] Other directories to look in + * dwWhich [I] Type of executable to search for + * + * RETURNS + * Success: TRUE. The path to the executable is stored in lpszFile. + * Failure: FALSE. The path to the executable is unchanged. + */ +BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich) +{ + WCHAR szFile[MAX_PATH]; + WCHAR buff[MAX_PATH]; + + TRACE("(%s,%p,%08lx)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich); + + if (!lpszFile || !PathIsFileSpecA(lpszFile)) + return FALSE; + + MultiByteToWideChar(0,0,lpszFile,-1,szFile,MAX_PATH); + + /* Search provided directories first */ + if (lppszOtherDirs && *lppszOtherDirs) + { + WCHAR szOther[MAX_PATH]; + LPCSTR *lpszOtherPath = lppszOtherDirs; + + while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) + { + MultiByteToWideChar(0,0,*lpszOtherPath,-1,szOther,MAX_PATH); + PathCombineW(buff, szOther, szFile); + if (PathFileExistsDefExtW(buff, dwWhich)) + { + WideCharToMultiByte(0,0,buff,-1,lpszFile,MAX_PATH,0,0); + return TRUE; + } + lpszOtherPath++; + } + } + /* Not found, try system and path dirs */ + if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich)) + { + WideCharToMultiByte(0,0,szFile,-1,lpszFile,MAX_PATH,0,0); + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.6] + * + * Unicode version of PathFindOnPathExA. + */ +BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich) +{ + WCHAR buff[MAX_PATH]; + + TRACE("(%s,%p,%08lx)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich); + + if (!lpszFile || !PathIsFileSpecW(lpszFile)) + return FALSE; + + /* Search provided directories first */ + if (lppszOtherDirs && *lppszOtherDirs) + { + LPCWSTR *lpszOtherPath = lppszOtherDirs; + while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) + { + PathCombineW(buff, *lpszOtherPath, lpszFile); + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + lpszOtherPath++; + } + } + /* Not found, try system and path dirs */ + return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich); +} + +/************************************************************************* + * PathFindOnPathA [SHLWAPI.@] + * + * Search a range of paths for an executable. + * + * PARAMS + * lpszFile [I/O] File to search for + * lppszOtherDirs [I] Other directories to look in + * + * RETURNS + * Success: TRUE. The path to the executable is stored in lpszFile. + * Failure: FALSE. The path to the executable is unchanged. + */ +BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs) +{ + TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs); + return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0); + } + +/************************************************************************* + * PathFindOnPathW [SHLWAPI.@] + * + * See PathFindOnPathA. + */ +BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs) +{ + TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs); + return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0); +} + +/************************************************************************* + * PathCompactPathExA [SHLWAPI.@] + * + * Compact a path into a given number of characters. + * + * PARAMS + * lpszDest [O] Destination for compacted path + * lpszPath [I] Source path + * cchMax [I] Maximum size of compacted path + * dwFlags [I] Reserved + * + * RETURNS + * Success: TRUE. The compacted path is written to lpszDest. + * Failure: FALSE. lpszPath is undefined. + * + * NOTES + * If cchMax is given as 0, lpszDest will still be NUL terminated. + * + * The Win32 version of this function contains a bug: When cchMax == 7, + * 8 bytes will be written to lpszDest. This bug is fixed in the Wine + * implementation. + * + * Some relative paths will be different when cchMax == 5 or 6. This occurs + * because Win32 will insert a "\" in lpszDest, even if one is + * not present in the original path. + */ +BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath, + UINT cchMax, DWORD dwFlags) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags); + + if (lpszPath && lpszDest) + { + WCHAR szPath[MAX_PATH]; + WCHAR szDest[MAX_PATH]; + + MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH); + szDest[0] = '\0'; + bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags); + WideCharToMultiByte(0,0,szDest,-1,lpszDest,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * PathCompactPathExW [SHLWAPI.@] + * + * See PathCompactPathExA. + */ +BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath, + UINT cchMax, DWORD dwFlags) +{ + static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; + LPCWSTR lpszFile; + DWORD dwLen, dwFileLen = 0; + + TRACE("(%p,%s,%d,0x%08lx)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags); + + if (!lpszPath) + return FALSE; + + if (!lpszDest) + { + WARN("Invalid lpszDest would crash under Win32!\n"); + return FALSE; + } + + *lpszDest = '\0'; + + if (cchMax < 2) + return TRUE; + + dwLen = strlenW(lpszPath) + 1; + + if (dwLen < cchMax) + { + /* Don't need to compact */ + memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); + return TRUE; + } + + /* Path must be compacted to fit into lpszDest */ + lpszFile = PathFindFileNameW(lpszPath); + dwFileLen = lpszPath + dwLen - lpszFile; + + if (dwFileLen == dwLen) + { + /* No root in psth */ + if (cchMax <= 4) + { + while (--cchMax > 0) /* No room left for anything but ellipses */ + *lpszDest++ = '.'; + *lpszDest = '\0'; + return TRUE; + } + /* Compact the file name with ellipses at the end */ + cchMax -= 4; + memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); + strcpyW(lpszDest + cchMax, szEllipses); + return TRUE; + } + /* We have a root in the path */ + lpszFile--; /* Start compacted filename with the path separator */ + dwFileLen++; + + if (dwFileLen + 3 > cchMax) + { + /* Compact the file name */ + if (cchMax <= 4) + { + while (--cchMax > 0) /* No room left for anything but ellipses */ + *lpszDest++ = '.'; + *lpszDest = '\0'; + return TRUE; + } + strcpyW(lpszDest, szEllipses); + lpszDest += 3; + cchMax -= 4; + *lpszDest++ = *lpszFile++; + if (cchMax <= 4) + { + while (--cchMax > 0) /* No room left for anything but ellipses */ + *lpszDest++ = '.'; + *lpszDest = '\0'; + return TRUE; + } + cchMax -= 4; + memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); + strcpyW(lpszDest + cchMax, szEllipses); + return TRUE; + } + + /* Only the root needs to be Compacted */ + dwLen = cchMax - dwFileLen - 3; + memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); + strcpyW(lpszDest + dwLen, szEllipses); + strcpyW(lpszDest + dwLen + 3, lpszFile); + return TRUE; +} + +/************************************************************************* + * PathIsRelativeA [SHLWAPI.@] + * + * Determine if a path is a relative path. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE: The path is relative, or is invalid. + * FALSE: The path is not relative. + */ +BOOL WINAPI PathIsRelativeA (LPCSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath || !*lpszPath || IsDBCSLeadByte(*lpszPath)) + return TRUE; + if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsRelativeW [SHLWAPI.@] + * + * See PathIsRelativeA. + */ +BOOL WINAPI PathIsRelativeW (LPCWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath) + return TRUE; + if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsRootA [SHLWAPI.@] + * + * Determine if a path is a root path. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If lpszPath is valid and a root path, + * FALSE Otherwise + */ +BOOL WINAPI PathIsRootA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath) + { + if (*lpszPath == '\\') + { + if (!lpszPath[1]) + return TRUE; /* \ */ + else if (lpszPath[1]=='\\') + { + BOOL bSeenSlash = FALSE; + lpszPath += 2; + + /* Check for UNC root path */ + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath = CharNextA(lpszPath); + } + return TRUE; + } + } + else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') + return TRUE; /* X:\ */ + } + return FALSE; +} + +/************************************************************************* + * PathIsRootW [SHLWAPI.@] + * + * See PathIsRootA. + */ +BOOL WINAPI PathIsRootW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && *lpszPath) + { + if (*lpszPath == '\\') + { + if (!lpszPath[1]) + return TRUE; /* \ */ + else if (lpszPath[1]=='\\') + { + BOOL bSeenSlash = FALSE; + lpszPath += 2; + + /* Check for UNC root path */ + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath = CharNextW(lpszPath); + } + return TRUE; + } + } + else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') + return TRUE; /* X:\ */ + } + return FALSE; +} + +/************************************************************************* + * PathIsDirectoryA [SHLWAPI.@] + * + * Determine if a path is a valid directory + * + * PARAMS + * lpszPath [I] Path to check. + * + * RETURNS + * FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes) + * FALSE if lpszPath is invalid or not a directory. + * + * NOTES + * Although this function is prototyped as returning a BOOL, it returns + * FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as: + * + *| if (PathIsDirectoryA("c:\\windows\\") == TRUE) + *| ... + * + * will always fail. + */ +BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath || PathIsUNCServerA(lpszPath)) + return FALSE; + + if (PathIsUNCServerShareA(lpszPath)) + { + FIXME("UNC Server Share not yet supported - FAILING\n"); + return FALSE; + } + + if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES) + return FALSE; + return dwAttr & FILE_ATTRIBUTE_DIRECTORY; +} + +/************************************************************************* + * PathIsDirectoryW [SHLWAPI.@] + * + * See PathIsDirectoryA. + */ +BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || PathIsUNCServerW(lpszPath)) + return FALSE; + + if (PathIsUNCServerShareW(lpszPath)) + { + FIXME("UNC Server Share not yet supported - FAILING\n"); + return FALSE; + } + + if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) + return FALSE; + return dwAttr & FILE_ATTRIBUTE_DIRECTORY; +} + +/************************************************************************* + * PathFileExistsA [SHLWAPI.@] + * + * Determine if a file exists. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If the file exists and is readable + * FALSE Otherwise + */ +BOOL WINAPI PathFileExistsA(LPCSTR lpszPath) +{ + UINT iPrevErrMode; + DWORD dwAttr; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + + iPrevErrMode = SetErrorMode(1); + dwAttr = GetFileAttributesA(lpszPath); + SetErrorMode(iPrevErrMode); + return dwAttr == INVALID_FILE_ATTRIBUTES ? FALSE : TRUE; +} + +/************************************************************************* + * PathFileExistsW [SHLWAPI.@] + * + * See PathFileExistsA. + */ +BOOL WINAPI PathFileExistsW(LPCWSTR lpszPath) +{ + UINT iPrevErrMode; + DWORD dwAttr; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + + iPrevErrMode = SetErrorMode(1); + dwAttr = GetFileAttributesW(lpszPath); + SetErrorMode(iPrevErrMode); + return dwAttr == INVALID_FILE_ATTRIBUTES ? FALSE : TRUE; +} + +/************************************************************************* + * PathMatchSingleMaskA [internal] + */ +static BOOL WINAPI PathMatchSingleMaskA(LPCSTR name, LPCSTR mask) +{ + while (*name && *mask && *mask!=';') + { + if (*mask == '*') + { + do + { + if (PathMatchSingleMaskA(name,mask+1)) + return TRUE; /* try substrings */ + } while (*name++); + return FALSE; + } + + if (toupper(*mask) != toupper(*name) && *mask != '?') + return FALSE; + + name = CharNextA(name); + mask = CharNextA(mask); + } + + if (!*name) + { + while (*mask == '*') + mask++; + if (!*mask || *mask == ';') + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathMatchSingleMaskW [internal] + */ +static BOOL WINAPI PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask) +{ + while (*name && *mask && *mask != ';') + { + if (*mask == '*') + { + do + { + if (PathMatchSingleMaskW(name,mask+1)) + return TRUE; /* try substrings */ + } while (*name++); + return FALSE; + } + + if (toupperW(*mask) != toupperW(*name) && *mask != '?') + return FALSE; + + name = CharNextW(name); + mask = CharNextW(mask); + } + if (!*name) + { + while (*mask == '*') + mask++; + if (!*mask || *mask == ';') + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathMatchSpecA [SHLWAPI.@] + * + * Determine if a path matches one or more search masks. + * + * PARAMS + * lpszPath [I] Path to check + * lpszMask [I] Search mask(s) + * + * RETURNS + * TRUE If lpszPath is valid and is matched + * FALSE Otherwise + * + * NOTES + * Multiple search masks may be given if they are separated by ";". The + * pattern "*.*" is treated specially in that it matches all paths (for + * backwards compatibility with DOS). + */ +BOOL WINAPI PathMatchSpecA(LPCSTR lpszPath, LPCSTR lpszMask) +{ + TRACE("(%s,%s)\n", lpszPath, lpszMask); + + if (!lstrcmpA(lpszMask, "*.*")) + return TRUE; /* Matches every path */ + + while (*lpszMask) + { + if (PathMatchSingleMaskA(lpszPath, lpszMask)) + return TRUE; /* Matches the current mask */ + + while (*lpszMask && *lpszMask != ';') + lpszMask = CharNextA(lpszMask); + + if (*lpszMask == ';') + { + lpszMask++; + while (*lpszMask == ' ') + lpszMask++; /* masks may be separated by "; " */ + } + } + return FALSE; +} + +/************************************************************************* + * PathMatchSpecW [SHLWAPI.@] + * + * See PathMatchSpecA. + */ +BOOL WINAPI PathMatchSpecW(LPCWSTR lpszPath, LPCWSTR lpszMask) +{ + static const WCHAR szStarDotStar[] = { '*', '.', '*', '\0' }; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszMask)); + + if (!lstrcmpW(lpszMask, szStarDotStar)) + return TRUE; /* Matches every path */ + + while (*lpszMask) + { + if (PathMatchSingleMaskW(lpszPath, lpszMask)) + return TRUE; /* Matches the current path */ + + while (*lpszMask && *lpszMask != ';') + lpszMask++; + + if (*lpszMask == ';') + { + lpszMask++; + while (*lpszMask == ' ') + lpszMask++; /* Masks may be separated by "; " */ + } + } + return FALSE; +} + +/************************************************************************* + * PathIsSameRootA [SHLWAPI.@] + * + * Determine if two paths share the same root. + * + * PARAMS + * lpszPath1 [I] Source path + * lpszPath2 [I] Path to compare with + * + * RETURNS + * TRUE If both paths are valid and share the same root. + * FALSE If either path is invalid or the paths do not share the same root. + */ +BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2) +{ + LPCSTR lpszStart; + int dwLen; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2)); + + if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1))) + return FALSE; + + dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1; + if (lpszStart - lpszPath1 > dwLen) + return FALSE; /* Paths not common up to length of the root */ + return TRUE; +} + +/************************************************************************* + * PathIsSameRootW [SHLWAPI.@] + * + * See PathIsSameRootA. + */ +BOOL WINAPI PathIsSameRootW(LPCWSTR lpszPath1, LPCWSTR lpszPath2) +{ + LPCWSTR lpszStart; + int dwLen; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath1), debugstr_w(lpszPath2)); + + if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootW(lpszPath1))) + return FALSE; + + dwLen = PathCommonPrefixW(lpszPath1, lpszPath2, NULL) + 1; + if (lpszStart - lpszPath1 > dwLen) + return FALSE; /* Paths not common up to length of the root */ + return TRUE; +} + +/************************************************************************* + * PathIsContentTypeA [SHLWAPI.@] + * + * Determine if a file is of a given registered content type. + * + * PARAMS + * lpszPath [I] File to check + * lpszContentType [I] Content type to check for + * + * RETURNS + * TRUE If lpszPath is a given registered content type, + * FALSE Otherwise. + * + * NOTES + * This function looks up the registered content type for lpszPath. If + * a content type is registered, it is compared (case insensitively) to + * lpszContentType. Only if this matches does the function succeed. + */ +BOOL WINAPI PathIsContentTypeA(LPCSTR lpszPath, LPCSTR lpszContentType) +{ + LPCSTR szExt; + DWORD dwDummy; + char szBuff[MAX_PATH]; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszContentType)); + + if (lpszPath && (szExt = PathFindExtensionA(lpszPath)) && *szExt && + !SHGetValueA(HKEY_CLASSES_ROOT, szExt, "Content Type", + REG_NONE, szBuff, &dwDummy) && + !strcasecmp(lpszContentType, szBuff)) + { + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathIsContentTypeW [SHLWAPI.@] + * + * See PathIsContentTypeA. + */ +BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType) +{ + static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' }; + LPCWSTR szExt; + DWORD dwDummy; + WCHAR szBuff[MAX_PATH]; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType)); + + if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt && + !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType, + REG_NONE, szBuff, &dwDummy) && + !strcmpiW(lpszContentType, szBuff)) + { + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathIsFileSpecA [SHLWAPI.@] + * + * Determine if a path is a file specification. + * + * PARAMS + * lpszPath [I] Path to chack + * + * RETURNS + * TRUE If lpszPath is a file specification (i.e. Contains no directories). + * FALSE Otherwise. + */ +BOOL WINAPI PathIsFileSpecA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath == ':') + return FALSE; + lpszPath = CharNextA(lpszPath); + } + return TRUE; +} + +/************************************************************************* + * PathIsFileSpecW [SHLWAPI.@] + * + * See PathIsFileSpecA. + */ +BOOL WINAPI PathIsFileSpecW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath == ':') + return FALSE; + lpszPath = CharNextW(lpszPath); + } + return TRUE; +} + +/************************************************************************* + * PathIsPrefixA [SHLWAPI.@] + * + * Determine if a path is a prefix of another. + * + * PARAMS + * lpszPrefix [I] Prefix + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If lpszPath has lpszPrefix as its prefix, + * FALSE If either path is NULL or lpszPrefix is not a prefix + */ +BOOL WINAPI PathIsPrefixA (LPCSTR lpszPrefix, LPCSTR lpszPath) +{ + TRACE("(%s,%s)\n", debugstr_a(lpszPrefix), debugstr_a(lpszPath)); + + if (lpszPrefix && lpszPath && + PathCommonPrefixA(lpszPath, lpszPrefix, NULL) == (int)strlen(lpszPrefix)) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsPrefixW [SHLWAPI.@] + * + * See PathIsPrefixA. + */ +BOOL WINAPI PathIsPrefixW(LPCWSTR lpszPrefix, LPCWSTR lpszPath) +{ + TRACE("(%s,%s)\n", debugstr_w(lpszPrefix), debugstr_w(lpszPath)); + + if (lpszPrefix && lpszPath && + PathCommonPrefixW(lpszPath, lpszPrefix, NULL) == (int)strlenW(lpszPrefix)) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsSystemFolderA [SHLWAPI.@] + * + * Determine if a path or file attributes are a system folder. + * + * PARAMS + * lpszPath [I] Path to check. + * dwAttrib [I] Attributes to check, if lpszPath is NULL. + * + * RETURNS + * TRUE If lpszPath or dwAttrib are a system folder. + * FALSE If GetFileAttributesA() fails or neither parameter is a system folder. + */ +BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib) +{ + TRACE("(%s,0x%08lx)\n", debugstr_a(lpszPath), dwAttrib); + + if (lpszPath && *lpszPath) + dwAttrib = GetFileAttributesA(lpszPath); + + if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || + !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsSystemFolderW [SHLWAPI.@] + * + * See PathIsSystemFolderA. + */ +BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib) +{ + TRACE("(%s,0x%08lx)\n", debugstr_w(lpszPath), dwAttrib); + + if (lpszPath && *lpszPath) + dwAttrib = GetFileAttributesW(lpszPath); + + if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || + !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsUNCA [SHLWAPI.@] + * + * Determine if a path is in UNC format. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE: The path is UNC. + * FALSE: The path is not UNC or is NULL. + */ +BOOL WINAPI PathIsUNCA(LPCSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\')) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsUNCW [SHLWAPI.@] + * + * See PathIsUNCA. + */ +BOOL WINAPI PathIsUNCW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\')) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerA [SHLWAPI.@] + * + * Determine if a path is a UNC server name ("\\SHARENAME"). + * + * PARAMS + * lpszPath [I] Path to check. + * + * RETURNS + * TRUE If lpszPath is a valid UNC server name. + * FALSE Otherwise. + * + * NOTES + * This routine is bug compatible with Win32: Server names with a + * trailing backslash (e.g. "\\FOO\"), return FALSE incorrectly. + * Fixing this bug may break other shlwapi functions! + */ +BOOL WINAPI PathIsUNCServerA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') + { + while (*lpszPath) + { + if (*lpszPath == '\\') + return FALSE; + lpszPath = CharNextA(lpszPath); + } + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerW [SHLWAPI.@] + * + * See PathIsUNCServerA. + */ +BOOL WINAPI PathIsUNCServerW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') + { + while (*lpszPath) + { + if (*lpszPath == '\\') + return FALSE; + lpszPath = CharNextW(lpszPath); + } + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerShareA [SHLWAPI.@] + * + * Determine if a path is a UNC server share ("\\SHARENAME\SHARE"). + * + * PARAMS + * lpszPath [I] Path to check. + * + * RETURNS + * TRUE If lpszPath is a valid UNC server share. + * FALSE Otherwise. + * + * NOTES + * This routine is bug compatible with Win32: Server shares with a + * trailing backslash (e.g. "\\FOO\BAR\"), return FALSE incorrectly. + * Fixing this bug may break other shlwapi functions! + */ +BOOL WINAPI PathIsUNCServerShareA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') + { + BOOL bSeenSlash = FALSE; + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath = CharNextA(lpszPath); + } + return bSeenSlash; + } + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerShareW [SHLWAPI.@] + * + * See PathIsUNCServerShareA. + */ +BOOL WINAPI PathIsUNCServerShareW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') + { + BOOL bSeenSlash = FALSE; + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath = CharNextW(lpszPath); + } + return bSeenSlash; + } + return FALSE; +} + +/************************************************************************* + * PathCanonicalizeA [SHLWAPI.@] + * + * Convert a path to its canonical form. + * + * PARAMS + * lpszBuf [O] Output path + * lpszPath [I] Path to cnonicalize + * + * RETURNS + * Success: TRUE. lpszBuf contains the output path, + * Failure: FALSE, If input path is invalid. lpszBuf is undefined + */ +BOOL WINAPI PathCanonicalizeA(LPSTR lpszBuf, LPCSTR lpszPath) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s)\n", lpszBuf, debugstr_a(lpszPath)); + + if (lpszBuf) + *lpszBuf = '\0'; + + if (!lpszBuf || !lpszPath) + SetLastError(ERROR_INVALID_PARAMETER); + else + { + WCHAR szPath[MAX_PATH]; + WCHAR szBuff[MAX_PATH]; + MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathCanonicalizeW(szBuff, szPath); + WideCharToMultiByte(0,0,szBuff,-1,lpszBuf,MAX_PATH,0,0); + } + return bRet; +} + + +/************************************************************************* + * PathCanonicalizeW [SHLWAPI.@] + * + * See PathCanonicalizeA. + */ +BOOL WINAPI PathCanonicalizeW(LPWSTR lpszBuf, LPCWSTR lpszPath) +{ + LPWSTR lpszDst = lpszBuf; + LPCWSTR lpszSrc = lpszPath; + + TRACE("(%p,%s)\n", lpszBuf, debugstr_w(lpszPath)); + + if (lpszBuf) + *lpszDst = '\0'; + + if (!lpszBuf || !lpszPath) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!*lpszPath) + { + *lpszBuf++ = '\\'; + *lpszBuf = '\0'; + return TRUE; + } + + /* Copy path root */ + if (*lpszSrc == '\\') + { + *lpszDst++ = *lpszSrc++; + } + else if (*lpszSrc && lpszSrc[1] == ':') + { + /* X:\ */ + *lpszDst++ = *lpszSrc++; + *lpszDst++ = *lpszSrc++; + if (*lpszSrc == '\\') + *lpszDst++ = *lpszSrc++; + } + + /* Canonicalize the rest of the path */ + while (*lpszSrc) + { + if (*lpszSrc == '.') + { + if (lpszSrc[1] == '\\' && (lpszSrc == lpszPath || lpszSrc[-1] == '\\' || lpszSrc[-1] == ':')) + { + lpszSrc += 2; /* Skip .\ */ + } + else if (lpszSrc[1] == '.' && (lpszDst == lpszBuf || lpszDst[-1] == '\\')) + { + /* \.. backs up a directory, over the root if it has no \ following X:. + * .. is ignored if it would remove a UNC server name or inital \\ + */ + if (lpszDst != lpszBuf) + { + *lpszDst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */ + if (lpszDst > lpszBuf+1 && lpszDst[-1] == '\\' && + (lpszDst[-2] != '\\' || lpszDst > lpszBuf+2)) + { + if (lpszDst[-2] == ':' && (lpszDst > lpszBuf+3 || lpszDst[-3] == ':')) + { + lpszDst -= 2; + while (lpszDst > lpszBuf && *lpszDst != '\\') + lpszDst--; + if (*lpszDst == '\\') + lpszDst++; /* Reset to last '\' */ + else + lpszDst = lpszBuf; /* Start path again from new root */ + } + else if (lpszDst[-2] != ':' && !PathIsUNCServerShareW(lpszBuf)) + lpszDst -= 2; + } + while (lpszDst > lpszBuf && *lpszDst != '\\') + lpszDst--; + if (lpszDst == lpszBuf) + { + *lpszDst++ = '\\'; + lpszSrc++; + } + } + lpszSrc += 2; /* Skip .. in src path */ + } + else + *lpszDst++ = *lpszSrc++; + } + else + *lpszDst++ = *lpszSrc++; + } + /* Append \ to naked drive specs */ + if (lpszDst - lpszBuf == 2 && lpszDst[-1] == ':') + *lpszDst++ = '\\'; + *lpszDst++ = '\0'; + return TRUE; +} + +/************************************************************************* + * PathFindNextComponentA [SHLWAPI.@] + * + * Find the next component in a path. + * + * PARAMS + * lpszPath [I] Path to find next component in + * + * RETURNS + * Success: A pointer to the next component, or the end of the string. + * Failure: NULL, If lpszPath is invalid + * + * NOTES + * A 'component' is either a backslash character (\) or UNC marker (\\). + * Because of this, relative paths (e.g "c:foo") are regarded as having + * only one component. + */ +LPSTR WINAPI PathFindNextComponentA(LPCSTR lpszPath) +{ + LPSTR lpszSlash; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(!lpszPath || !*lpszPath) + return NULL; + + if ((lpszSlash = StrChrA(lpszPath, '\\'))) + { + if (lpszSlash[1] == '\\') + lpszSlash++; + return lpszSlash + 1; + } + return (LPSTR)lpszPath + strlen(lpszPath); +} + +/************************************************************************* + * PathFindNextComponentW [SHLWAPI.@] + * + * See PathFindNextComponentA. + */ +LPWSTR WINAPI PathFindNextComponentW(LPCWSTR lpszPath) +{ + LPWSTR lpszSlash; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(!lpszPath || !*lpszPath) + return NULL; + + if ((lpszSlash = StrChrW(lpszPath, '\\'))) + { + if (lpszSlash[1] == '\\') + lpszSlash++; + return lpszSlash + 1; + } + return (LPWSTR)lpszPath + strlenW(lpszPath); +} + +/************************************************************************* + * PathAddExtensionA [SHLWAPI.@] + * + * Add a file extension to a path + * + * PARAMS + * lpszPath [I/O] Path to add extension to + * lpszExtension [I] Extension to add to lpszPath + * + * RETURNS + * TRUE If the path was modified, + * FALSE If lpszPath or lpszExtension are invalid, lpszPath has an + * extension allready, or the new path length is too big. + * + * FIXME + * What version of shlwapi.dll adds "exe" if lpszExtension is NULL? Win2k + * does not do this, so the behaviour was removed. + */ +BOOL WINAPI PathAddExtensionA(LPSTR lpszPath, LPCSTR lpszExtension) +{ + size_t dwLen; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExtension)); + + if (!lpszPath || !lpszExtension || *(PathFindExtensionA(lpszPath))) + return FALSE; + + dwLen = strlen(lpszPath); + + if (dwLen + strlen(lpszExtension) >= MAX_PATH) + return FALSE; + + strcpy(lpszPath + dwLen, lpszExtension); + return TRUE; +} + +/************************************************************************* + * PathAddExtensionW [SHLWAPI.@] + * + * See PathAddExtensionA. + */ +BOOL WINAPI PathAddExtensionW(LPWSTR lpszPath, LPCWSTR lpszExtension) +{ + size_t dwLen; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExtension)); + + if (!lpszPath || !lpszExtension || *(PathFindExtensionW(lpszPath))) + return FALSE; + + dwLen = strlenW(lpszPath); + + if (dwLen + strlenW(lpszExtension) >= MAX_PATH) + return FALSE; + + strcpyW(lpszPath + dwLen, lpszExtension); + return TRUE; +} + +/************************************************************************* + * PathMakePrettyA [SHLWAPI.@] + * + * Convert an uppercase DOS filename into lowercase. + * + * PARAMS + * lpszPath [I/O] Path to convert. + * + * RETURNS + * TRUE If the path was an uppercase DOS path and was converted, + * FALSE Otherwise. + */ +BOOL WINAPI PathMakePrettyA(LPSTR lpszPath) +{ + LPSTR pszIter = lpszPath; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!pszIter || !*pszIter) + return FALSE; + + while (*pszIter) + { + if (islower(*pszIter) || IsDBCSLeadByte(*pszIter)) + return FALSE; /* Not DOS path */ + pszIter++; + } + pszIter = lpszPath + 1; + while (*pszIter) + { + *pszIter = tolower(*pszIter); + pszIter++; + } + return TRUE; +} + +/************************************************************************* + * PathMakePrettyW [SHLWAPI.@] + * + * See PathMakePrettyA. + */ +BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath) +{ + LPWSTR pszIter = lpszPath; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!pszIter || !*pszIter) + return FALSE; + + while (*pszIter) + { + if (islowerW(*pszIter)) + return FALSE; /* Not DOS path */ + pszIter++; + } + pszIter = lpszPath + 1; + while (*pszIter) + { + *pszIter = tolowerW(*pszIter); + pszIter++; + } + return TRUE; +} + +/************************************************************************* + * PathCommonPrefixA [SHLWAPI.@] + * + * Determine the length of the common prefix between two paths. + * + * PARAMS + * lpszFile1 [I] First path for comparison + * lpszFile2 [I] Second path for comparison + * achPath [O] Destination for common prefix string + * + * RETURNS + * The length of the common prefix. This is 0 if there is no common + * prefix between the paths or if any parameters are invalid. If the prefix + * is non-zero and achPath is not NULL, achPath is filled with the common + * part of the prefix and NUL terminated. + * + * NOTES + * A common prefix of 2 is always returned as 3. It is thus possible for + * the length returned to be invalid (i.e. Longer than one or both of the + * strings given as parameters). This Win32 behaviour has been implemented + * here, and cannot be changed (fixed?) without breaking other SHLWAPI calls. + * To work around this when using this function, always check that the byte + * at [common_prefix_len-1] is not a NUL. If it is, deduct 1 from the prefix. + */ +int WINAPI PathCommonPrefixA(LPCSTR lpszFile1, LPCSTR lpszFile2, LPSTR achPath) +{ + size_t iLen = 0; + LPCSTR lpszIter1 = lpszFile1; + LPCSTR lpszIter2 = lpszFile2; + + TRACE("(%s,%s,%p)\n", debugstr_a(lpszFile1), debugstr_a(lpszFile2), achPath); + + if (achPath) + *achPath = '\0'; + + if (!lpszFile1 || !lpszFile2) + return 0; + + /* Handle roots first */ + if (PathIsUNCA(lpszFile1)) + { + if (!PathIsUNCA(lpszFile2)) + return 0; + lpszIter1 += 2; + lpszIter2 += 2; + } + else if (PathIsUNCA(lpszFile2)) + return 0; /* Know already lpszFile1 is not UNC */ + + do + { + /* Update len */ + if ((!*lpszIter1 || *lpszIter1 == '\\') && + (!*lpszIter2 || *lpszIter2 == '\\')) + iLen = lpszIter1 - lpszFile1; /* Common to this point */ + + if (!*lpszIter1 || (tolower(*lpszIter1) != tolower(*lpszIter2))) + break; /* Strings differ at this point */ + + lpszIter1++; + lpszIter2++; + } while (1); + + if (iLen == 2) + iLen++; /* Feature/Bug compatible with Win32 */ + + if (iLen && achPath) + { + memcpy(achPath,lpszFile1,iLen); + achPath[iLen] = '\0'; + } + return iLen; +} + +/************************************************************************* + * PathCommonPrefixW [SHLWAPI.@] + * + * See PathCommonPrefixA. + */ +int WINAPI PathCommonPrefixW(LPCWSTR lpszFile1, LPCWSTR lpszFile2, LPWSTR achPath) +{ + size_t iLen = 0; + LPCWSTR lpszIter1 = lpszFile1; + LPCWSTR lpszIter2 = lpszFile2; + + TRACE("(%s,%s,%p)\n", debugstr_w(lpszFile1), debugstr_w(lpszFile2), achPath); + + if (achPath) + *achPath = '\0'; + + if (!lpszFile1 || !lpszFile2) + return 0; + + /* Handle roots first */ + if (PathIsUNCW(lpszFile1)) + { + if (!PathIsUNCW(lpszFile2)) + return 0; + lpszIter1 += 2; + lpszIter2 += 2; + } + else if (PathIsUNCW(lpszFile2)) + return 0; /* Know already lpszFile1 is not UNC */ + + do + { + /* Update len */ + if ((!*lpszIter1 || *lpszIter1 == '\\') && + (!*lpszIter2 || *lpszIter2 == '\\')) + iLen = lpszIter1 - lpszFile1; /* Common to this point */ + + if (!*lpszIter1 || (tolowerW(*lpszIter1) != tolowerW(*lpszIter2))) + break; /* Strings differ at this point */ + + lpszIter1++; + lpszIter2++; + } while (1); + + if (iLen == 2) + iLen++; /* Feature/Bug compatible with Win32 */ + + if (iLen && achPath) + { + memcpy(achPath,lpszFile1,iLen * sizeof(WCHAR)); + achPath[iLen] = '\0'; + } + return iLen; +} + +/************************************************************************* + * PathCompactPathA [SHLWAPI.@] + * + * Make a path fit into a given width when printed to a DC. + * + * PARAMS + * hDc [I] Destination DC + * lpszPath [I/O] Path to be printed to hDc + * dx [I] Desired width + * + * RETURNS + * TRUE If the path was modified. + * FALSE Otherwise. + */ +BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx); + + if (lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathCompactPathW(hDC, szPath, dx); + WideCharToMultiByte(0,0,szPath,-1,lpszPath,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * PathCompactPathW [SHLWAPI.@] + * + * See PathCompactPathA. + */ +BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx) +{ + static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; + BOOL bRet = TRUE; + HDC hdc = 0; + WCHAR buff[MAX_PATH]; + SIZE size; + DWORD dwLen; + + TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx); + + if (!lpszPath) + return bRet; + + if (!hDC) + hdc = hDC = GetDC(0); + + /* Get the length of the whole path */ + dwLen = strlenW(lpszPath); + GetTextExtentPointW(hDC, lpszPath, dwLen, &size); + + if ((UINT)size.cx > dx) + { + /* Path too big, must reduce it */ + LPWSTR sFile; + DWORD dwEllipsesLen = 0, dwPathLen = 0; + + sFile = PathFindFileNameW(lpszPath); + if (sFile != lpszPath) + sFile = CharPrevW(lpszPath, sFile); + + /* Get the size of ellipses */ + GetTextExtentPointW(hDC, szEllipses, 3, &size); + dwEllipsesLen = size.cx; + /* Get the size of the file name */ + GetTextExtentPointW(hDC, sFile, strlenW(sFile), &size); + dwPathLen = size.cx; + + if (sFile != lpszPath) + { + LPWSTR sPath = sFile; + BOOL bEllipses = FALSE; + + /* The path includes a file name. Include as much of the path prior to + * the file name as possible, allowing for the ellipses, e.g: + * c:\some very long path\filename ==> c:\some v...\filename + */ + strncpyW(buff, sFile, MAX_PATH); + + do + { + DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen; + + GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size); + dwTotalLen += size.cx; + if (dwTotalLen <= dx) + break; + sPath = CharPrevW(lpszPath, sPath); + if (!bEllipses) + { + bEllipses = TRUE; + sPath = CharPrevW(lpszPath, sPath); + sPath = CharPrevW(lpszPath, sPath); + } + } while (sPath > lpszPath); + + if (sPath > lpszPath) + { + if (bEllipses) + { + strcpyW(sPath, szEllipses); + strcpyW(sPath+3, buff); + } + bRet = TRUE; + goto end; + } + strcpyW(lpszPath, szEllipses); + strcpyW(lpszPath+3, buff); + bRet = FALSE; + goto end; + } + + /* Trim the path by adding ellipses to the end, e.g: + * A very long file name.txt ==> A very... + */ + dwLen = strlenW(lpszPath); + + if (dwLen > MAX_PATH - 3) + dwLen = MAX_PATH - 3; + strncpyW(buff, sFile, dwLen); + + do { + dwLen--; + GetTextExtentPointW(hDC, buff, dwLen, &size); + } while (dwLen && size.cx + dwEllipsesLen > dx); + + if (!dwLen) + { + DWORD dwWritten = 0; + + dwEllipsesLen /= 3; /* Size of a single '.' */ + + /* Write as much of the Ellipses string as possible */ + while (dwWritten + dwEllipsesLen < dx && dwLen < 3) + { + *lpszPath++ = '.'; + dwWritten += dwEllipsesLen; + dwLen++; + } + *lpszPath = '\0'; + bRet = FALSE; + } + else + { + strcpyW(buff + dwLen, szEllipses); + strcpyW(lpszPath, buff); + } + } + +end: + if (hdc) + ReleaseDC(0, hdc); + + return bRet; +} + +/************************************************************************* + * PathGetCharTypeA [SHLWAPI.@] + * + * Categorise a character from a file path. + * + * PARAMS + * ch [I] Character to get the type of + * + * RETURNS + * A set of GCT_ bit flags (from "shlwapi.h") indicating the character type. + */ +UINT WINAPI PathGetCharTypeA(UCHAR ch) +{ + return PathGetCharTypeW(ch); +} + +/************************************************************************* + * PathGetCharTypeW [SHLWAPI.@] + * + * See PathGetCharTypeA. + */ +UINT WINAPI PathGetCharTypeW(WCHAR ch) +{ + UINT flags = 0; + + TRACE("(%d)\n", ch); + + if (!ch || ch < ' ' || ch == '<' || ch == '>' || + ch == '"' || ch == '|' || ch == '/') + flags = GCT_INVALID; /* Invalid */ + else if (ch == '*' || ch=='?') + flags = GCT_WILD; /* Wildchars */ + else if ((ch == '\\') || (ch == ':')) + return GCT_SEPARATOR; /* Path separators */ + else + { + if (ch < 126) + { + if ((ch & 0x1 && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' || + ch == '.' || ch == '@' || ch == '^' || + ch == '\'' || ch == 130 || ch == '`') + flags |= GCT_SHORTCHAR; /* All these are valid for DOS */ + } + else + flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */ + flags |= GCT_LFNCHAR; /* Valid for long file names */ + } + return flags; +} + +/************************************************************************* + * SHLWAPI_UseSystemForSystemFolders + * + * Internal helper for PathMakeSystemFolderW. + */ +static BOOL WINAPI SHLWAPI_UseSystemForSystemFolders() +{ + static BOOL bCheckedReg = FALSE; + static BOOL bUseSystemForSystemFolders = FALSE; + + if (!bCheckedReg) + { + bCheckedReg = TRUE; + + /* Key tells Win what file attributes to use on system folders */ + if (SHGetValueA(HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", + "UseSystemForSystemFolders", 0, 0, 0)) + bUseSystemForSystemFolders = TRUE; + } + return bUseSystemForSystemFolders; +} + +/************************************************************************* + * PathMakeSystemFolderA [SHLWAPI.@] + * + * Set system folder attribute for a path. + * + * PARAMS + * lpszPath [I] The path to turn into a system folder + * + * RETURNS + * TRUE If the path was changed to/already was a system folder + * FALSE If the path is invalid or SetFileAttributesA() fails + */ +BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath) +{ + BOOL bRet = FALSE; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathMakeSystemFolderW(szPath); + } + return bRet; +} + +/************************************************************************* + * PathMakeSystemFolderW [SHLWAPI.@] + * + * See PathMakeSystemFolderA. + */ +BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath) +{ + DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr; + WCHAR buff[MAX_PATH]; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath) + return FALSE; + + /* If the directory is already a system directory, dont do anything */ + GetSystemDirectoryW(buff, MAX_PATH); + if (!strcmpW(buff, lpszPath)) + return TRUE; + + GetWindowsDirectoryW(buff, MAX_PATH); + if (!strcmpW(buff, lpszPath)) + return TRUE; + + /* "UseSystemForSystemFolders" Tells Win what attributes to use */ + if (SHLWAPI_UseSystemForSystemFolders()) + dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM; + + if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) + return FALSE; + + /* Change file attributes to system attributes */ + dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY); + return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr); +} + +/************************************************************************* + * PathRenameExtensionA [SHLWAPI.@] + * + * Swap the file extension in a path with another extension. + * + * PARAMS + * lpszPath [I/O] Path to swap the extension in + * lpszExt [I] The new extension + * + * RETURNS + * TRUE if lpszPath was modified, + * FALSE if lpszPath or lpszExt is NULL, or the new path is too long + */ +BOOL WINAPI PathRenameExtensionA(LPSTR lpszPath, LPCSTR lpszExt) +{ + LPSTR lpszExtension; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExt)); + + lpszExtension = PathFindExtensionA(lpszPath); + + if (!lpszExtension || (lpszExtension - lpszPath + strlen(lpszExt) >= MAX_PATH)) + return FALSE; + + strcpy(lpszExtension, lpszExt); + return TRUE; +} + +/************************************************************************* + * PathRenameExtensionW [SHLWAPI.@] + * + * See PathRenameExtensionA. + */ +BOOL WINAPI PathRenameExtensionW(LPWSTR lpszPath, LPCWSTR lpszExt) +{ + LPWSTR lpszExtension; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExt)); + + lpszExtension = PathFindExtensionW(lpszPath); + + if (!lpszExtension || (lpszExtension - lpszPath + strlenW(lpszExt) >= MAX_PATH)) + return FALSE; + + strcpyW(lpszExtension, lpszExt); + return TRUE; +} + +/************************************************************************* + * PathSearchAndQualifyA [SHLWAPI.@] + * + * Determine if a given path is correct and fully qualified. + * + * PARAMS + * lpszPath [I] Path to check + * lpszBuf [O] Output for correct path + * cchBuf [I] Size of lpszBuf + * + * RETURNS + * Unknown. + */ +BOOL WINAPI PathSearchAndQualifyA(LPCSTR lpszPath, LPSTR lpszBuf, UINT cchBuf) +{ + FIXME("(%s,%p,0x%08x)-stub\n", debugstr_a(lpszPath), lpszBuf, cchBuf); + return FALSE; +} + +/************************************************************************* + * PathSearchAndQualifyW [SHLWAPI.@] + * + * See PathSearchAndQualifyA. + */ +BOOL WINAPI PathSearchAndQualifyW(LPCWSTR lpszPath, LPWSTR lpszBuf, UINT cchBuf) +{ + FIXME("(%s,%p,0x%08x)-stub\n", debugstr_w(lpszPath), lpszBuf, cchBuf); + return FALSE; +} + +/************************************************************************* + * PathSkipRootA [SHLWAPI.@] + * + * Return the portion of a path following the drive letter or mount point. + * + * PARAMS + * lpszPath [I] The path to skip on + * + * RETURNS + * Success: A pointer to the next character after the root. + * Failure: NULL, if lpszPath is invalid, has no root or is a multibyte string. + */ +LPSTR WINAPI PathSkipRootA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath || !*lpszPath) + return NULL; + + if (*lpszPath == '\\' && lpszPath[1] == '\\') + { + /* Network share: skip share server and mount point */ + lpszPath += 2; + if ((lpszPath = StrChrA(lpszPath, '\\')) && + (lpszPath = StrChrA(lpszPath + 1, '\\'))) + lpszPath++; + return (LPSTR)lpszPath; + } + + if (IsDBCSLeadByte(*lpszPath)) + return NULL; + + /* Check x:\ */ + if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') + return (LPSTR)lpszPath + 3; + return NULL; +} + +/************************************************************************* + * PathSkipRootW [SHLWAPI.@] + * + * See PathSkipRootA. + */ +LPWSTR WINAPI PathSkipRootW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath) + return NULL; + + if (*lpszPath == '\\' && lpszPath[1] == '\\') + { + /* Network share: skip share server and mount point */ + lpszPath += 2; + if ((lpszPath = StrChrW(lpszPath, '\\')) && + (lpszPath = StrChrW(lpszPath + 1, '\\'))) + lpszPath++; + return (LPWSTR)lpszPath; + } + + /* Check x:\ */ + if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') + return (LPWSTR)lpszPath + 3; + return NULL; +} + +/************************************************************************* + * PathCreateFromUrlA [SHLWAPI.@] + * + * Create a path from a URL + * + * PARAMS + * lpszUrl [I] URL to convert into a path + * lpszPath [O] Output buffer for the resulting Path + * pcchPath [I] Length of lpszPath + * dwFlags [I] Flags controlling the conversion + * + * RETURNS + * Success: S_OK. lpszPath contains the URL in path format, + * Failure: An HRESULT error code such as E_INVALIDARG. + */ +HRESULT WINAPI PathCreateFromUrlA(LPCSTR lpszUrl, LPSTR lpszPath, + LPDWORD pcchPath, DWORD dwFlags) +{ + LPSTR pszPathPart; + TRACE("(%s,%p,%p,0x%08lx)\n", debugstr_a(lpszUrl), lpszPath, pcchPath, dwFlags); + + if (!lpszUrl || !lpszPath || !pcchPath || !*pcchPath) + return E_INVALIDARG; + + pszPathPart = StrChrA(lpszUrl, ':'); + if ((((pszPathPart - lpszUrl) == 1) && isalpha(*lpszUrl)) || + !lstrcmpA(lpszUrl, "file:")) + { + return UrlUnescapeA(pszPathPart, lpszPath, pcchPath, dwFlags); + } + /* extracts thing prior to : in pszURL and checks against: + * https + * shell + * local + * about - if match returns E_INVALIDARG + */ + + return E_INVALIDARG; +} + +/************************************************************************* + * PathCreateFromUrlW [SHLWAPI.@] + * + * See PathCreateFromUrlA. + */ +HRESULT WINAPI PathCreateFromUrlW(LPCWSTR lpszUrl, LPWSTR lpszPath, + LPDWORD pcchPath, DWORD dwFlags) +{ + static const WCHAR stemp[] = { 'f','i','l','e',':','/','/',0 }; + LPWSTR pwszPathPart; + HRESULT hr; + + TRACE("(%s,%p,%p,0x%08lx)\n", debugstr_w(lpszUrl), lpszPath, pcchPath, dwFlags); + + if (!lpszUrl || !lpszPath || !pcchPath || !*pcchPath) + return E_INVALIDARG; + + /* Path of the form file://... */ + if (!strncmpW(lpszUrl, stemp, 7)) + { + lpszUrl += 7; + } + /* Path of the form file:... */ + else if (!strncmpW(lpszUrl, stemp, 5)) + { + lpszUrl += 5; + } + + /* Ensure that path is of the form c:... or c|... */ + if (lpszUrl[1] != ':' && lpszUrl[1] != '|' && isalphaW(*lpszUrl)) + return E_INVALIDARG; + + hr = UrlUnescapeW(lpszUrl, lpszPath, pcchPath, dwFlags); + if (lpszPath[1] == '|') + lpszPath[1] = ':'; + + for (pwszPathPart = lpszPath; *pwszPathPart; pwszPathPart++) + if (*pwszPathPart == '/') + *pwszPathPart = '\\'; + + TRACE("Returning %s\n",debugstr_w(lpszPath)); + + return hr; +} + +/************************************************************************* + * PathRelativePathToA [SHLWAPI.@] + * + * Create a relative path from one path to another. + * + * PARAMS + * lpszPath [O] Destination for relative path + * lpszFrom [I] Source path + * dwAttrFrom [I] File attribute of source path + * lpszTo [I] Destination path + * dwAttrTo [I] File attributes of destination path + * + * RETURNS + * TRUE If a relative path can be formed. lpszPath contains the new path + * FALSE If the paths are not relavtive or any parameters are invalid + * + * NOTES + * lpszTo should be at least MAX_PATH in length. + * + * Calling this function with relative paths for lpszFrom or lpszTo may + * give erroneous results. + * + * The Win32 version of this function contains a bug where the lpszTo string + * may be referenced 1 byte beyond the end of the string. As a result random + * garbage may be written to the output path, depending on what lies beyond + * the last byte of the string. This bug occurs because of the behaviour of + * PathCommonPrefix() (see notes for that function), and no workaround seems + * possible with Win32. + * + * This bug has been fixed here, so for example the relative path from "\\" + * to "\\" is correctly determined as "." in this implementation. + */ +BOOL WINAPI PathRelativePathToA(LPSTR lpszPath, LPCSTR lpszFrom, DWORD dwAttrFrom, + LPCSTR lpszTo, DWORD dwAttrTo) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s,0x%08lx,%s,0x%08lx)\n", lpszPath, debugstr_a(lpszFrom), + dwAttrFrom, debugstr_a(lpszTo), dwAttrTo); + + if(lpszPath && lpszFrom && lpszTo) + { + WCHAR szPath[MAX_PATH]; + WCHAR szFrom[MAX_PATH]; + WCHAR szTo[MAX_PATH]; + MultiByteToWideChar(0,0,lpszFrom,-1,szFrom,MAX_PATH); + MultiByteToWideChar(0,0,lpszTo,-1,szTo,MAX_PATH); + bRet = PathRelativePathToW(szPath,szFrom,dwAttrFrom,szTo,dwAttrTo); + WideCharToMultiByte(0,0,szPath,-1,lpszPath,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * PathRelativePathToW [SHLWAPI.@] + * + * See PathRelativePathToA. + */ +BOOL WINAPI PathRelativePathToW(LPWSTR lpszPath, LPCWSTR lpszFrom, DWORD dwAttrFrom, + LPCWSTR lpszTo, DWORD dwAttrTo) +{ + static const WCHAR szPrevDirSlash[] = { '.', '.', '\\', '\0' }; + static const WCHAR szPrevDir[] = { '.', '.', '\0' }; + WCHAR szFrom[MAX_PATH]; + WCHAR szTo[MAX_PATH]; + DWORD dwLen; + + TRACE("(%p,%s,0x%08lx,%s,0x%08lx)\n", lpszPath, debugstr_w(lpszFrom), + dwAttrFrom, debugstr_w(lpszTo), dwAttrTo); + + if(!lpszPath || !lpszFrom || !lpszTo) + return FALSE; + + *lpszPath = '\0'; + strncpyW(szFrom, lpszFrom, MAX_PATH); + strncpyW(szTo, lpszTo, MAX_PATH); + + if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY)) + PathRemoveFileSpecW(szFrom); + if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY)) + PathRemoveFileSpecW(szTo); + + /* Paths can only be relative if they have a common root */ + if(!(dwLen = PathCommonPrefixW(szFrom, szTo, 0))) + return FALSE; + + /* Strip off lpszFrom components to the root, by adding "..\" */ + lpszFrom = szFrom + dwLen; + if (!*lpszFrom) + { + lpszPath[0] = '.'; + lpszPath[1] = '\0'; + } + if (*lpszFrom == '\\') + lpszFrom++; + + while (*lpszFrom) + { + lpszFrom = PathFindNextComponentW(lpszFrom); + strcatW(lpszPath, *lpszFrom ? szPrevDirSlash : szPrevDir); + } + + /* From the root add the components of lpszTo */ + lpszTo += dwLen; + /* We check lpszTo[-1] to avoid skipping end of string. See the notes for + * this function. + */ + if (*lpszTo && lpszTo[-1]) + { + if (*lpszTo != '\\') + lpszTo--; + dwLen = strlenW(lpszPath); + if (dwLen + strlenW(lpszTo) >= MAX_PATH) + { + *lpszPath = '\0'; + return FALSE; + } + strcpyW(lpszPath + dwLen, lpszTo); + } + return TRUE; +} + +/************************************************************************* + * PathUnmakeSystemFolderA [SHLWAPI.@] + * + * Remove the system folder attributes from a path. + * + * PARAMS + * lpszPath [I] The path to remove attributes from + * + * RETURNS + * Success: TRUE. + * Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling + * SetFileAttributesA() fails. + */ +BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES || + !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + return FALSE; + + dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + return SetFileAttributesA(lpszPath, dwAttr); +} + +/************************************************************************* + * PathUnmakeSystemFolderW [SHLWAPI.@] + * + * See PathUnmakeSystemFolderA. + */ +BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES || + !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + return FALSE; + + dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + return SetFileAttributesW(lpszPath, dwAttr); +} + + +/************************************************************************* + * PathSetDlgItemPathA [SHLWAPI.@] + * + * Set the text of a dialog item to a path, shrinking the path to fit + * if it is too big for the item. + * + * PARAMS + * hDlg [I] Dialog handle + * id [I] ID of item in the dialog + * lpszPath [I] Path to set as the items text + * + * RETURNS + * Nothing. + * + * NOTES + * If lpszPath is NULL, a blank string ("") is set (i.e. The previous + * window text is erased). + */ +VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath) +{ + WCHAR szPath[MAX_PATH]; + + TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath)); + + if (lpszPath) + MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH); + else + szPath[0] = '\0'; + PathSetDlgItemPathW(hDlg, id, szPath); +} + +/************************************************************************* + * PathSetDlgItemPathW [SHLWAPI.@] + * + * See PathSetDlgItemPathA. + */ +VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath) +{ + WCHAR path[MAX_PATH + 1]; + HWND hwItem; + RECT rect; + HDC hdc; + HGDIOBJ hPrevObj; + + TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath)); + + if (!(hwItem = GetDlgItem(hDlg, id))) + return; + + if (lpszPath) + strncpyW(path, lpszPath, sizeof(path)); + else + path[0] = '\0'; + + GetClientRect(hwItem, &rect); + hdc = GetDC(hDlg); + hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0)); + + if (hPrevObj) + { + PathCompactPathW(hdc, path, rect.right); + SelectObject(hdc, hPrevObj); + } + + ReleaseDC(hDlg, hdc); + SetWindowTextW(hwItem, path); +} + +/************************************************************************* + * PathIsNetworkPathA [SHLWAPI.@] + * + * Determine if the given path is a network path. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If lpszPath is a UNC share or mapped network drive, or + * FALSE If lpszPath is a local drive or cannot be determined + */ +BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath) +{ + int dwDriveNum; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + if (*lpszPath == '\\' && lpszPath[1] == '\\') + return TRUE; + dwDriveNum = PathGetDriveNumberA(lpszPath); + if (dwDriveNum == -1) + return FALSE; + GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ + return pIsNetDrive(dwDriveNum); +} + +/************************************************************************* + * PathIsNetworkPathW [SHLWAPI.@] + * + * See PathIsNetworkPathA. + */ +BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath) +{ + int dwDriveNum; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + if (*lpszPath == '\\' && lpszPath[1] == '\\') + return TRUE; + dwDriveNum = PathGetDriveNumberW(lpszPath); + if (dwDriveNum == -1) + return FALSE; + GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ + return pIsNetDrive(dwDriveNum); +} + +/************************************************************************* + * PathIsLFNFileSpecA [SHLWAPI.@] + * + * Determine if the given path is a long file name + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If path is a long file name, + * FALSE If path is a valid DOS short file name + */ +BOOL WINAPI PathIsLFNFileSpecA(LPCSTR lpszPath) +{ + DWORD dwNameLen = 0, dwExtLen = 0; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == ' ') + return TRUE; /* DOS names cannot have spaces */ + if (*lpszPath == '.') + { + if (dwExtLen) + return TRUE; /* DOS names have only one dot */ + dwExtLen = 1; + } + else if (dwExtLen) + { + dwExtLen++; + if (dwExtLen > 4) + return TRUE; /* DOS extensions are <= 3 chars*/ + } + else + { + dwNameLen++; + if (dwNameLen > 8) + return TRUE; /* DOS names are <= 8 chars */ + } + lpszPath += IsDBCSLeadByte(*lpszPath) ? 2 : 1; + } + return FALSE; /* Valid DOS path */ +} + +/************************************************************************* + * PathIsLFNFileSpecW [SHLWAPI.@] + * + * See PathIsLFNFileSpecA. + */ +BOOL WINAPI PathIsLFNFileSpecW(LPCWSTR lpszPath) +{ + DWORD dwNameLen = 0, dwExtLen = 0; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == ' ') + return TRUE; /* DOS names cannot have spaces */ + if (*lpszPath == '.') + { + if (dwExtLen) + return TRUE; /* DOS names have only one dot */ + dwExtLen = 1; + } + else if (dwExtLen) + { + dwExtLen++; + if (dwExtLen > 4) + return TRUE; /* DOS extensions are <= 3 chars*/ + } + else + { + dwNameLen++; + if (dwNameLen > 8) + return TRUE; /* DOS names are <= 8 chars */ + } + lpszPath++; + } + return FALSE; /* Valid DOS path */ +} + +/************************************************************************* + * PathIsDirectoryEmptyA [SHLWAPI.@] + * + * Determine if a given directory is empty. + * + * PARAMS + * lpszPath [I] Directory to check + * + * RETURNS + * TRUE If the directory exists and contains no files, + * FALSE Otherwise + */ +BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath) +{ + BOOL bRet = FALSE; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(0,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathIsDirectoryEmptyW(szPath); + } + return bRet; +} + +/************************************************************************* + * PathIsDirectoryEmptyW [SHLWAPI.@] + * + * See PathIsDirectoryEmptyA. + */ +BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath) +{ + static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' }; + WCHAR szSearch[MAX_PATH]; + DWORD dwLen; + HANDLE hfind; + BOOL retVal = FALSE; + WIN32_FIND_DATAW find_data; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath || !PathIsDirectoryW(lpszPath)) + return FALSE; + + strncpyW(szSearch, lpszPath, MAX_PATH); + PathAddBackslashW(szSearch); + dwLen = strlenW(szSearch); + if (dwLen > MAX_PATH - 4) + return FALSE; + + strcpyW(szSearch + dwLen, szAllFiles); + hfind = FindFirstFileW(szSearch, &find_data); + + if (hfind != INVALID_HANDLE_VALUE && + find_data.cFileName[0] == '.' && + find_data.cFileName[1] == '.') + { + /* The only directory entry should be the parent */ + if (!FindNextFileW(hfind, &find_data)) + retVal = TRUE; + FindClose(hfind); + } + return retVal; +} + + +/************************************************************************* + * PathFindSuffixArrayA [SHLWAPI.@] + * + * Find a suffix string in an array of suffix strings + * + * PARAMS + * lpszSuffix [I] Suffix string to search for + * lppszArray [I] Array of suffix strings to search + * dwCount [I] Number of elements in lppszArray + * + * RETURNS + * Success: The index of the position of lpszSuffix in lppszArray + * Failure: 0, if any parameters are invalid or lpszSuffix is not found + * + * NOTES + * The search is case sensitive. + * The match is made against the end of the suffix string, so for example: + * lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not. + */ +int WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount) +{ + size_t dwLen; + int dwRet = 0; + + TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount); + + if (lpszSuffix && lppszArray && dwCount > 0) + { + dwLen = strlen(lpszSuffix); + + while (dwRet < dwCount) + { + size_t dwCompareLen = strlen(*lppszArray); + if (dwCompareLen < dwLen) + { + if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) + return dwRet; /* Found */ + } + dwRet++; + lppszArray++; + } + } + return 0; +} + +/************************************************************************* + * PathFindSuffixArrayW [SHLWAPI.@] + * + * See PathFindSuffixArrayA. + */ +int WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount) +{ + size_t dwLen; + int dwRet = 0; + + TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount); + + if (lpszSuffix && lppszArray && dwCount > 0) + { + dwLen = strlenW(lpszSuffix); + + while (dwRet < dwCount) + { + size_t dwCompareLen = strlenW(*lppszArray); + if (dwCompareLen < dwLen) + { + if (!strcmpW(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) + return dwRet; /* Found */ + } + dwRet++; + lppszArray++; + } + } + return 0; +} + +/************************************************************************* + * PathUndecorateA [SHLWAPI.@] + * + * Undecorate a file path + * + * PARAMS + * lpszPath [I/O] Path to remove any decoration from + * + * RETURNS + * Nothing + * + * NOTES + * A decorations form is "path[n].ext" where "n" is an optional decimal number. + */ +VOID WINAPI PathUndecorateA(LPSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath) + { + LPSTR lpszExt = PathFindExtensionA(lpszPath); + if (lpszExt > lpszPath && lpszExt[-1] == ']') + { + LPSTR lpszSkip = lpszExt - 2; + if (*lpszSkip == '[') + lpszSkip++; /* [] (no number) */ + else + while (lpszSkip > lpszPath && isdigit(lpszSkip[-1])) + lpszSkip--; + if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') + { + /* remove the [n] */ + lpszSkip--; + while (*lpszExt) + *lpszSkip++ = *lpszExt++; + *lpszSkip = '\0'; + } + } + } +} + +/************************************************************************* + * PathUndecorateW [SHLWAPI.@] + * + * See PathUndecorateA. + */ +VOID WINAPI PathUndecorateW(LPWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath) + { + LPWSTR lpszExt = PathFindExtensionW(lpszPath); + if (lpszExt > lpszPath && lpszExt[-1] == ']') + { + LPWSTR lpszSkip = lpszExt - 2; + if (*lpszSkip == '[') + lpszSkip++; /* [] (no number) */ + else + while (lpszSkip > lpszPath && isdigitW(lpszSkip[-1])) + lpszSkip--; + if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') + { + /* remove the [n] */ + lpszSkip--; + while (*lpszExt) + *lpszSkip++ = *lpszExt++; + *lpszSkip = '\0'; + } + } + } +} + +/************************************************************************* + * @ [SHLWAPI.440] + * + * Find localised or default web content in "%WINDOWS%\web\". + * + * PARAMS + * lpszFile [I] File name containing content to look for + * lpszPath [O] Buffer to contain the full path to the file + * dwPathLen [I] Length of lpszPath + * + * RETURNS + * Success: S_OK. lpszPath contains the full path to the content. + * Failure: E_FAIL. The content does not exist or lpszPath is too short. + */ +HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen) +{ + WCHAR szFile[MAX_PATH], szPath[MAX_PATH]; + HRESULT hRet; + + TRACE("(%s,%p,%ld)\n", lpszFile, lpszPath, dwPathLen); + + MultiByteToWideChar(0, 0, lpszFile, -1, szFile, MAX_PATH); + szPath[0] = '\0'; + hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen); + WideCharToMultiByte(0, 0, szPath, -1, lpszPath, dwPathLen, 0, 0); + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.441] + * + * Unicode version of SHGetWebFolderFilePathA. + */ +HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen) +{ + static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'}; + static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'}; +#define szWebLen (sizeof(szWeb)/sizeof(WCHAR)) +#define szWebMuiLen ((sizeof(szWebMui)+1)/sizeof(WCHAR)) + DWORD dwLen, dwFileLen; + LANGID lidSystem, lidUser; + + TRACE("(%s,%p,%ld)\n", debugstr_w(lpszFile), lpszPath, dwPathLen); + + /* Get base directory for web content */ + dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen); + if (dwLen > 0 && lpszPath[dwLen-1] == '\\') + dwLen--; + + dwFileLen = strlenW(lpszFile); + + if (dwLen + dwFileLen + szWebLen >= dwPathLen) + return E_FAIL; /* lpszPath too short */ + + strcpyW(lpszPath+dwLen, szWeb); + dwLen += szWebLen; + dwPathLen = dwPathLen - dwLen; /* Remaining space */ + + lidSystem = GetSystemDefaultUILanguage(); + lidUser = GetUserDefaultUILanguage(); + + if (lidSystem != lidUser) + { + if (dwFileLen + szWebMuiLen < dwPathLen) + { + /* Use localised content in the users UI language if present */ + wsprintfW(lpszPath + dwLen, szWebMui, lidUser); + strcpyW(lpszPath + dwLen + szWebMuiLen, lpszFile); + if (PathFileExistsW(lpszPath)) + return S_OK; + } + } + + /* Fall back to OS default installed content */ + strcpyW(lpszPath + dwLen, lpszFile); + if (PathFileExistsW(lpszPath)) + return S_OK; + return E_FAIL; +} diff --git a/reactos/lib/shlwapi/reg.c b/reactos/lib/shlwapi/reg.c new file mode 100644 index 00000000000..1d4cfe1632f --- /dev/null +++ b/reactos/lib/shlwapi/reg.c @@ -0,0 +1,2196 @@ +/* + * SHLWAPI registry functions + * + * Copyright 1998 Juergen Schmied + * Copyright 2001 Guy Albertelli + * + * 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 +#include +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#include "windef.h" +#include "winbase.h" +#ifdef __REACTOS__ +#include "wingdi.h" +#endif +#include "winuser.h" +#include "winreg.h" +#include "wine/debug.h" +#define NO_SHLWAPI_STREAM +#include "shlwapi.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* Key/Value names for MIME content types */ +static const char *lpszContentTypeA = "Content Type"; +static const WCHAR lpszContentTypeW[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0'}; + +static const char *szMimeDbContentA = "MIME\\Database\\Content Type\\"; +static const WCHAR szMimeDbContentW[] = { 'M', 'I', 'M','E','\\', + 'D','a','t','a','b','a','s','e','\\','C','o','n','t','e','n','t', + ' ','T','y','p','e','\\', 0 }; +static const DWORD dwLenMimeDbContent = 27; /* strlen of szMimeDbContentA/W */ + +static const char *szExtensionA = "Extension"; +static const WCHAR szExtensionW[] = { 'E', 'x', 't','e','n','s','i','o','n','\0' }; + +/* internal structure of what the HUSKEY points to */ +typedef struct { + HKEY HKCUkey; /* HKEY of opened HKCU key */ + HKEY HKLMkey; /* HKEY of opened HKLM key */ + HKEY start; /* HKEY of where to start */ + WCHAR key_string[MAX_PATH]; /* additional path from 'start' */ +} Internal_HUSKEY, *LPInternal_HUSKEY; + +DWORD WINAPI SHStringFromGUIDW(REFGUID,LPWSTR,INT); +HRESULT WINAPI SHRegGetCLSIDKeyW(REFGUID,LPCWSTR,BOOL,BOOL,PHKEY); + + +#define REG_HKCU TRUE +#define REG_HKLM FALSE +/************************************************************************* + * REG_GetHKEYFromHUSKEY + * + * Function: Return the proper registry key from the HUSKEY structure + * also allow special predefined values. + */ +static HKEY WINAPI REG_GetHKEYFromHUSKEY(HUSKEY hUSKey, BOOL which) +{ + HKEY test = (HKEY) hUSKey; + LPInternal_HUSKEY mihk = (LPInternal_HUSKEY) hUSKey; + + if ((test == HKEY_CLASSES_ROOT) || + (test == HKEY_CURRENT_CONFIG) || + (test == HKEY_CURRENT_USER) || + (test == HKEY_DYN_DATA) || + (test == HKEY_LOCAL_MACHINE) || + (test == HKEY_PERFORMANCE_DATA) || +/* FIXME: need to define for Win2k, ME, XP + * (test == HKEY_PERFORMANCE_TEXT) || + * (test == HKEY_PERFORMANCE_NLSTEXT) || + */ + (test == HKEY_USERS)) return test; + if (which == REG_HKCU) return mihk->HKCUkey; + return mihk->HKLMkey; +} + + +/************************************************************************* + * SHRegOpenUSKeyA [SHLWAPI.@] + * + * Opens a user-specific registry key + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: An error code from RegOpenKeyExA(). + */ +LONG WINAPI SHRegOpenUSKeyA( + LPCSTR Path, /* [I] Key name to open */ + REGSAM AccessType, /* [I] Access type */ + HUSKEY hRelativeUSKey, /* [I] Relative user key */ + PHUSKEY phNewUSKey, /* [O] Destination for created key */ + BOOL fIgnoreHKCU) /* [I] TRUE=Don't check HKEY_CURRENT_USER */ +{ + HKEY openHKCUkey=0; + HKEY openHKLMkey=0; + LONG ret2, ret1 = ~ERROR_SUCCESS; + LPInternal_HUSKEY ihky; + + TRACE("(%s, 0x%lx, 0x%lx, %p, %s)\n", debugstr_a(Path), + (LONG)AccessType, (LONG)hRelativeUSKey, phNewUSKey, + (fIgnoreHKCU) ? "Ignoring HKCU" : "Process HKCU then HKLM"); + + /* now create the internal version of HUSKEY */ + ihky = (LPInternal_HUSKEY)HeapAlloc(GetProcessHeap(), 0 , + sizeof(Internal_HUSKEY)); + MultiByteToWideChar(0, 0, Path, -1, ihky->key_string, + sizeof(ihky->key_string)-1); + + if (hRelativeUSKey) { + openHKCUkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKCUkey; + openHKLMkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKLMkey; + } + else { + openHKCUkey = HKEY_CURRENT_USER; + openHKLMkey = HKEY_LOCAL_MACHINE; + } + + ihky->HKCUkey = 0; + ihky->HKLMkey = 0; + if (!fIgnoreHKCU) { + ret1 = RegOpenKeyExA(openHKCUkey, Path, + 0, AccessType, &ihky->HKCUkey); + /* if successful, then save real starting point */ + if (ret1 != ERROR_SUCCESS) + ihky->HKCUkey = 0; + } + ret2 = RegOpenKeyExA(openHKLMkey, Path, + 0, AccessType, &ihky->HKLMkey); + if (ret2 != ERROR_SUCCESS) + ihky->HKLMkey = 0; + + if ((ret1 != ERROR_SUCCESS) || (ret2 != ERROR_SUCCESS)) + TRACE("one or more opens failed: HKCU=%ld HKLM=%ld\n", ret1, ret2); + + /* if all attempts have failed then bail */ + if ((ret1 != ERROR_SUCCESS) && (ret2 != ERROR_SUCCESS)) { + HeapFree(GetProcessHeap(), 0, ihky); + if (phNewUSKey) + *phNewUSKey = NULL; + return ret2; + } + + TRACE("HUSKEY=%p\n", ihky); + if (phNewUSKey) + *phNewUSKey = (HUSKEY)ihky; + return ERROR_SUCCESS; +} + +/************************************************************************* + * SHRegOpenUSKeyW [SHLWAPI.@] + * + * See SHRegOpenUSKeyA. + */ +LONG WINAPI SHRegOpenUSKeyW( + LPCWSTR Path, + REGSAM AccessType, + HUSKEY hRelativeUSKey, + PHUSKEY phNewUSKey, + BOOL fIgnoreHKCU) +{ + HKEY openHKCUkey=0; + HKEY openHKLMkey=0; + LONG ret2, ret1 = ~ERROR_SUCCESS; + LPInternal_HUSKEY ihky; + + TRACE("(%s, 0x%lx, 0x%lx, %p, %s)\n", debugstr_w(Path), + (LONG)AccessType, (LONG)hRelativeUSKey, phNewUSKey, + (fIgnoreHKCU) ? "Ignoring HKCU" : "Process HKCU then HKLM"); + + /* now create the internal version of HUSKEY */ + ihky = (LPInternal_HUSKEY)HeapAlloc(GetProcessHeap(), 0 , + sizeof(Internal_HUSKEY)); + lstrcpynW(ihky->key_string, Path, sizeof(ihky->key_string)); + + if (hRelativeUSKey) { + openHKCUkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKCUkey; + openHKLMkey = ((LPInternal_HUSKEY)hRelativeUSKey)->HKLMkey; + } + else { + openHKCUkey = HKEY_CURRENT_USER; + openHKLMkey = HKEY_LOCAL_MACHINE; + } + + ihky->HKCUkey = 0; + ihky->HKLMkey = 0; + if (!fIgnoreHKCU) { + ret1 = RegOpenKeyExW(openHKCUkey, Path, + 0, AccessType, &ihky->HKCUkey); + /* if successful, then save real starting point */ + if (ret1 != ERROR_SUCCESS) + ihky->HKCUkey = 0; + } + ret2 = RegOpenKeyExW(openHKLMkey, Path, + 0, AccessType, &ihky->HKLMkey); + if (ret2 != ERROR_SUCCESS) + ihky->HKLMkey = 0; + + if ((ret1 != ERROR_SUCCESS) || (ret2 != ERROR_SUCCESS)) + TRACE("one or more opens failed: HKCU=%ld HKLM=%ld\n", ret1, ret2); + + /* if all attempts have failed then bail */ + if ((ret1 != ERROR_SUCCESS) && (ret2 != ERROR_SUCCESS)) { + HeapFree(GetProcessHeap(), 0, ihky); + if (phNewUSKey) + *phNewUSKey = NULL; + return ret2; + } + + TRACE("HUSKEY=0x%08lx\n", (LONG)ihky); + if (phNewUSKey) + *phNewUSKey = (HUSKEY)ihky; + return ERROR_SUCCESS; +} + +/************************************************************************* + * SHRegCloseUSKey [SHLWAPI.@] + * + * Close a user-specific registry key + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: An error code from RegCloseKey(). + */ +LONG WINAPI SHRegCloseUSKey( + HUSKEY hUSKey) /* [I] Key to close */ +{ + LPInternal_HUSKEY mihk = (LPInternal_HUSKEY)hUSKey; + LONG ret = ERROR_SUCCESS; + + if (mihk->HKCUkey) + ret = RegCloseKey(mihk->HKCUkey); + if (mihk->HKLMkey) + ret = RegCloseKey(mihk->HKLMkey); + HeapFree(GetProcessHeap(), 0, mihk); + return ret; +} + +/************************************************************************* + * SHRegQueryUSValueA [SHLWAPI.@] + * + * Query a user-specific registry value. + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: An error code from RegQueryValueExA(). + */ +LONG WINAPI SHRegQueryUSValueA( + HUSKEY hUSKey, /* [I] Key to query */ + LPCSTR pszValue, /* [I] Value name under hUSKey */ + LPDWORD pdwType, /* [O] Destination for value type */ + LPVOID pvData, /* [O] Destination for value data */ + LPDWORD pcbData, /* [O] Destination for value length */ + BOOL fIgnoreHKCU, /* [I] TRUE=Don't check HKEY_CURRENT_USER */ + LPVOID pvDefaultData, /* [I] Default data if pszValue does not exist */ + DWORD dwDefaultDataSize) /* [I] Length of pvDefaultData */ +{ + LONG ret = ~ERROR_SUCCESS; + LONG i, maxmove; + HKEY dokey; + CHAR *src, *dst; + + /* if user wants HKCU, and it exists, then try it */ + if (!fIgnoreHKCU && (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) { + ret = RegQueryValueExA(dokey, + pszValue, 0, pdwType, pvData, pcbData); + TRACE("HKCU RegQueryValue returned %08lx\n", ret); + } + + /* if HKCU did not work and HKLM exists, then try it */ + if ((ret != ERROR_SUCCESS) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) { + ret = RegQueryValueExA(dokey, + pszValue, 0, pdwType, pvData, pcbData); + TRACE("HKLM RegQueryValue returned %08lx\n", ret); + } + + /* if neither worked, and default data exists, then use it */ + if (ret != ERROR_SUCCESS) { + if (pvDefaultData && (dwDefaultDataSize != 0)) { + maxmove = (dwDefaultDataSize >= *pcbData) ? *pcbData : dwDefaultDataSize; + src = (CHAR*)pvDefaultData; + dst = (CHAR*)pvData; + for(i=0; i= *pcbData) ? *pcbData : dwDefaultDataSize; + src = (CHAR*)pvDefaultData; + dst = (CHAR*)pvData; + for(i=0; i\n", type, + (ret) ? "TRUE" : "FALSE"); + } + else { + ret = fDefault; + TRACE("returning default data <%s>\n", + (ret) ? "TRUE" : "FALSE"); + } + return ret; +} + +/************************************************************************* + * SHRegGetBoolUSValueW [SHLWAPI.@] + * + * See SHRegGetBoolUSValueA. + */ +BOOL WINAPI SHRegGetBoolUSValueW( + LPCWSTR pszSubKey, + LPCWSTR pszValue, + BOOL fIgnoreHKCU, + BOOL fDefault) +{ + static const WCHAR wYES[]= {'Y','E','S','\0'}; + static const WCHAR wTRUE[]= {'T','R','U','E','\0'}; + static const WCHAR wNO[]= {'N','O','\0'}; + static const WCHAR wFALSE[]={'F','A','L','S','E','\0'}; + LONG retvalue; + DWORD type, datalen, work; + BOOL ret = fDefault; + WCHAR data[10]; + + TRACE("key '%s', value '%s', %s\n", + debugstr_w(pszSubKey), debugstr_w(pszValue), + (fIgnoreHKCU) ? "Ignoring HKCU" : "Tries HKCU then HKLM"); + + datalen = (sizeof(data)-1) * sizeof(WCHAR); + if (!(retvalue = SHRegGetUSValueW( pszSubKey, pszValue, &type, + data, &datalen, + fIgnoreHKCU, 0, 0))) { + /* process returned data via type into bool */ + switch (type) { + case REG_SZ: + data[9] = L'\0'; /* set end of string */ + if (lstrcmpiW(data, wYES)==0 || lstrcmpiW(data, wTRUE)==0) + ret = TRUE; + else if (lstrcmpiW(data, wNO)==0 || lstrcmpiW(data, wFALSE)==0) + ret = FALSE; + break; + case REG_DWORD: + work = *(LPDWORD)data; + ret = (work != 0); + break; + case REG_BINARY: + if (datalen == 1) { + ret = (data[0] != L'\0'); + break; + } + default: + FIXME("Unsupported registry data type %ld\n", type); + ret = FALSE; + } + TRACE("got value (type=%ld), returing <%s>\n", type, + (ret) ? "TRUE" : "FALSE"); + } + else { + ret = fDefault; + TRACE("returning default data <%s>\n", + (ret) ? "TRUE" : "FALSE"); + } + return ret; +} + +/************************************************************************* + * SHRegQueryInfoUSKeyA [SHLWAPI.@] + * + * Get information about a user-specific registry key. + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: An error code from RegQueryInfoKeyA(). + */ +LONG WINAPI SHRegQueryInfoUSKeyA( + HUSKEY hUSKey, /* [I] Key to query */ + LPDWORD pcSubKeys, /* [O] Destination for number of sub keys */ + LPDWORD pcchMaxSubKeyLen, /* [O] Destination for the length of the biggest sub key name */ + LPDWORD pcValues, /* [O] Destination for number of values */ + LPDWORD pcchMaxValueNameLen,/* [O] Destination for the length of the biggest value */ + SHREGENUM_FLAGS enumRegFlags) /* [in] SHREGENUM_ flags from "shlwapi.h" */ +{ + HKEY dokey; + LONG ret; + + TRACE("(%p,%p,%p,%p,%p,%d)\n", + hUSKey,pcSubKeys,pcchMaxSubKeyLen,pcValues, + pcchMaxValueNameLen,enumRegFlags); + + /* if user wants HKCU, and it exists, then try it */ + if (((enumRegFlags == SHREGENUM_HKCU) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) { + ret = RegQueryInfoKeyA(dokey, 0, 0, 0, + pcSubKeys, pcchMaxSubKeyLen, 0, + pcValues, pcchMaxValueNameLen, 0, 0, 0); + if ((ret == ERROR_SUCCESS) || + (enumRegFlags == SHREGENUM_HKCU)) + return ret; + } + if (((enumRegFlags == SHREGENUM_HKLM) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) { + return RegQueryInfoKeyA(dokey, 0, 0, 0, + pcSubKeys, pcchMaxSubKeyLen, 0, + pcValues, pcchMaxValueNameLen, 0, 0, 0); + } + return ERROR_INVALID_FUNCTION; +} + +/************************************************************************* + * SHRegQueryInfoUSKeyW [SHLWAPI.@] + * + * See SHRegQueryInfoUSKeyA. + */ +LONG WINAPI SHRegQueryInfoUSKeyW( + HUSKEY hUSKey, + LPDWORD pcSubKeys, + LPDWORD pcchMaxSubKeyLen, + LPDWORD pcValues, + LPDWORD pcchMaxValueNameLen, + SHREGENUM_FLAGS enumRegFlags) +{ + HKEY dokey; + LONG ret; + + TRACE("(%p,%p,%p,%p,%p,%d)\n", + hUSKey,pcSubKeys,pcchMaxSubKeyLen,pcValues, + pcchMaxValueNameLen,enumRegFlags); + + /* if user wants HKCU, and it exists, then try it */ + if (((enumRegFlags == SHREGENUM_HKCU) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) { + ret = RegQueryInfoKeyW(dokey, 0, 0, 0, + pcSubKeys, pcchMaxSubKeyLen, 0, + pcValues, pcchMaxValueNameLen, 0, 0, 0); + if ((ret == ERROR_SUCCESS) || + (enumRegFlags == SHREGENUM_HKCU)) + return ret; + } + if (((enumRegFlags == SHREGENUM_HKLM) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) { + return RegQueryInfoKeyW(dokey, 0, 0, 0, + pcSubKeys, pcchMaxSubKeyLen, 0, + pcValues, pcchMaxValueNameLen, 0, 0, 0); + } + return ERROR_INVALID_FUNCTION; +} + +/************************************************************************* + * SHRegEnumUSKeyA [SHLWAPI.@] + * + * Enumerate a user-specific registry key. + * + * RETURNS + * Success: ERROR_SUCCESS + * Failure: An error code from RegEnumKeyExA(). + */ +LONG WINAPI SHRegEnumUSKeyA( + HUSKEY hUSKey, /* [in] Key to enumerate */ + DWORD dwIndex, /* [in] Index within hUSKey */ + LPSTR pszName, /* [out] Name of the enumerated value */ + LPDWORD pcchValueNameLen, /* [in/out] Length of pszName */ + SHREGENUM_FLAGS enumRegFlags) /* [in] SHREGENUM_ flags from "shlwapi.h" */ +{ + HKEY dokey; + + TRACE("(%p,%ld,%p,%p(%ld),%d)\n", + hUSKey, dwIndex, pszName, pcchValueNameLen, + *pcchValueNameLen, enumRegFlags); + + if (((enumRegFlags == SHREGENUM_HKCU) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) { + return RegEnumKeyExA(dokey, dwIndex, pszName, pcchValueNameLen, + 0, 0, 0, 0); + } + + if (((enumRegFlags == SHREGENUM_HKLM) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) { + return RegEnumKeyExA(dokey, dwIndex, pszName, pcchValueNameLen, + 0, 0, 0, 0); + } + FIXME("no support for SHREGENUM_BOTH\n"); + return ERROR_INVALID_FUNCTION; +} + +/************************************************************************* + * SHRegEnumUSKeyW [SHLWAPI.@] + * + * See SHRegEnumUSKeyA. + */ +LONG WINAPI SHRegEnumUSKeyW( + HUSKEY hUSKey, + DWORD dwIndex, + LPWSTR pszName, + LPDWORD pcchValueNameLen, + SHREGENUM_FLAGS enumRegFlags) +{ + HKEY dokey; + + TRACE("(%p,%ld,%p,%p(%ld),%d)\n", + hUSKey, dwIndex, pszName, pcchValueNameLen, + *pcchValueNameLen, enumRegFlags); + + if (((enumRegFlags == SHREGENUM_HKCU) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) { + return RegEnumKeyExW(dokey, dwIndex, pszName, pcchValueNameLen, + 0, 0, 0, 0); + } + + if (((enumRegFlags == SHREGENUM_HKLM) || + (enumRegFlags == SHREGENUM_DEFAULT)) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) { + return RegEnumKeyExW(dokey, dwIndex, pszName, pcchValueNameLen, + 0, 0, 0, 0); + } + FIXME("no support for SHREGENUM_BOTH\n"); + return ERROR_INVALID_FUNCTION; +} + + +/************************************************************************* + * SHRegWriteUSValueA [SHLWAPI.@] + * + * Write a user-specific registry value. + * + * PARAMS + * hUSKey [I] Key to write the value to + * pszValue [I] Name of value under hUSKey to write the value as + * dwType [I] Type of the value + * pvData [I] Data to set as the value + * cbData [I] length of pvData + * dwFlags [I] SHREGSET_ flags from "shlwapi.h" + * + * RETURNS + * Success: ERROR_SUCCESS. + * Failure: An error code from RegSetValueExA(). + */ +LONG WINAPI SHRegWriteUSValueA(HUSKEY hUSKey, LPCSTR pszValue, DWORD dwType, + LPVOID pvData, DWORD cbData, DWORD dwFlags) +{ + HKEY dokey; + + TRACE("(%p,%s,%ld,%p,%ld,%ld)\n", + hUSKey, debugstr_a(pszValue), dwType, pvData, cbData, dwFlags); + + if ((dwFlags & SHREGSET_FORCE_HKCU) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) { + RegSetValueExA(dokey, pszValue, 0, dwType, pvData, cbData); + } + + if ((dwFlags & SHREGSET_FORCE_HKLM) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) { + RegSetValueExA(dokey, pszValue, 0, dwType, pvData, cbData); + } + + if (dwFlags & (SHREGSET_FORCE_HKCU | SHREGSET_FORCE_HKLM)) + return ERROR_SUCCESS; + + FIXME("SHREGSET_HKCU or SHREGSET_HKLM not supported\n"); + return ERROR_SUCCESS; +} + +/************************************************************************* + * SHRegWriteUSValueW [SHLWAPI.@] + * + * See SHRegWriteUSValueA. + */ +LONG WINAPI SHRegWriteUSValueW(HUSKEY hUSKey, LPCWSTR pszValue, DWORD dwType, + LPVOID pvData, DWORD cbData, DWORD dwFlags) +{ + HKEY dokey; + + TRACE("(%p,%s,%ld,%p,%ld,%ld)\n", + hUSKey, debugstr_w(pszValue), dwType, pvData, cbData, dwFlags); + + if ((dwFlags & SHREGSET_FORCE_HKCU) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKCU))) { + RegSetValueExW(dokey, pszValue, 0, dwType, pvData, cbData); + } + + if ((dwFlags & SHREGSET_FORCE_HKLM) && + (dokey = REG_GetHKEYFromHUSKEY(hUSKey,REG_HKLM))) { + RegSetValueExW(dokey, pszValue, 0, dwType, pvData, cbData); + } + + if (dwFlags & (SHREGSET_FORCE_HKCU | SHREGSET_FORCE_HKLM)) + return ERROR_SUCCESS; + + FIXME("SHREGSET_HKCU or SHREGSET_HKLM not supported\n"); + return ERROR_SUCCESS; +} + +/************************************************************************* + * SHRegGetPathA [SHLWAPI.@] + * + * Get a path from the registry. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key containing path to get + * lpszValue [I] Name of value containing path to get + * lpszPath [O] Buffer for returned path + * dwFlags [I] Reserved + * + * RETURNS + * Success: ERROR_SUCCESS. lpszPath contains the path. + * Failure: An error code from RegOpenKeyExA() or SHQueryValueExA(). + */ +DWORD WINAPI SHRegGetPathA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue, + LPSTR lpszPath, DWORD dwFlags) +{ + DWORD dwSize = MAX_PATH; + + TRACE("(hkey=%p,%s,%s,%p,%ld)\n", hKey, debugstr_a(lpszSubKey), + debugstr_a(lpszValue), lpszPath, dwFlags); + + return SHGetValueA(hKey, lpszSubKey, lpszValue, 0, lpszPath, &dwSize); +} + +/************************************************************************* + * SHRegGetPathW [SHLWAPI.@] + * + * See SHRegGetPathA. + */ +DWORD WINAPI SHRegGetPathW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue, + LPWSTR lpszPath, DWORD dwFlags) +{ + DWORD dwSize = MAX_PATH; + + TRACE("(hkey=%p,%s,%s,%p,%ld)\n", hKey, debugstr_w(lpszSubKey), + debugstr_w(lpszValue), lpszPath, dwFlags); + + return SHGetValueW(hKey, lpszSubKey, lpszValue, 0, lpszPath, &dwSize); +} + + +/************************************************************************* + * SHRegSetPathA [SHLWAPI.@] + * + * Write a path to the registry. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key containing path to set + * lpszValue [I] Name of value containing path to set + * lpszPath [O] Path to write + * dwFlags [I] Reserved, must be 0. + * + * RETURNS + * Success: ERROR_SUCCESS. + * Failure: An error code from SHSetValueA(). + */ +DWORD WINAPI SHRegSetPathA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue, + LPCSTR lpszPath, DWORD dwFlags) +{ + char szBuff[MAX_PATH]; + + FIXME("(hkey=%p,%s,%s,%p,%ld) - semi-stub\n",hKey, debugstr_a(lpszSubKey), + debugstr_a(lpszValue), lpszPath, dwFlags); + + lstrcpyA(szBuff, lpszPath); + + /* FIXME: PathUnExpandEnvStringsA(szBuff); */ + + return SHSetValueA(hKey,lpszSubKey, lpszValue, REG_SZ, szBuff, + lstrlenA(szBuff)); +} + +/************************************************************************* + * SHRegSetPathW [SHLWAPI.@] + * + * See SHRegSetPathA. + */ +DWORD WINAPI SHRegSetPathW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue, + LPCWSTR lpszPath, DWORD dwFlags) +{ + WCHAR szBuff[MAX_PATH]; + + FIXME("(hkey=%p,%s,%s,%p,%ld) - semi-stub\n",hKey, debugstr_w(lpszSubKey), + debugstr_w(lpszValue), lpszPath, dwFlags); + + lstrcpyW(szBuff, lpszPath); + + /* FIXME: PathUnExpandEnvStringsW(szBuff); */ + + return SHSetValueW(hKey,lpszSubKey, lpszValue, REG_SZ, szBuff, + lstrlenW(szBuff)); +} + +/************************************************************************* + * SHGetValueA [SHLWAPI.@] + * + * Get a value from the registry. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key containing value to get + * lpszValue [I] Name of value to get + * pwType [O] Pointer to the values type + * pvData [O] Pointer to the values data + * pcbData [O] Pointer to the values size + * + * RETURNS + * Success: ERROR_SUCCESS. Output parameters contain the details read. + * Failure: An error code from RegOpenKeyExA() or SHQueryValueExA(). + */ +DWORD WINAPI SHGetValueA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue, + LPDWORD pwType, LPVOID pvData, LPDWORD pcbData) +{ + DWORD dwRet = 0; + HKEY hSubKey = 0; + + TRACE("(hkey=%p,%s,%s,%p,%p,%p)\n", hKey, debugstr_a(lpszSubKey), + debugstr_a(lpszValue), pwType, pvData, pcbData); + + /* lpszSubKey can be 0. In this case the value is taken from the + * current key. + */ + if(lpszSubKey) + dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_QUERY_VALUE, &hSubKey); + + if (! dwRet) + { + /* SHQueryValueEx expands Environment strings */ + dwRet = SHQueryValueExA(hSubKey ? hSubKey : hKey, lpszValue, 0, pwType, pvData, pcbData); + if (hSubKey) RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHGetValueW [SHLWAPI.@] + * + * See SHGetValueA. + */ +DWORD WINAPI SHGetValueW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue, + LPDWORD pwType, LPVOID pvData, LPDWORD pcbData) +{ + DWORD dwRet = 0; + HKEY hSubKey = 0; + + TRACE("(hkey=%p,%s,%s,%p,%p,%p)\n", hKey, debugstr_w(lpszSubKey), + debugstr_w(lpszValue), pwType, pvData, pcbData); + + if(lpszSubKey) + dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_QUERY_VALUE, &hSubKey); + + if (! dwRet) + { + dwRet = SHQueryValueExW(hSubKey ? hSubKey : hKey, lpszValue, 0, pwType, pvData, pcbData); + if (hSubKey) RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHSetValueA [SHLWAPI.@] + * + * Set a value in the registry. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key under hKey + * lpszValue [I] Name of value to set + * dwType [I] Type of the value + * pvData [I] Data of the value + * cbData [I] Size of the value + * + * RETURNS + * Success: ERROR_SUCCESS. The value is set with the data given. + * Failure: An error code from RegCreateKeyExA() or RegSetValueExA() + * + * NOTES + * If lpszSubKey does not exist, it is created before the value is set. If + * lpszSubKey is NULL or an empty string, then the value is added directly + * to hKey instead. + */ +DWORD WINAPI SHSetValueA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue, + DWORD dwType, LPCVOID pvData, DWORD cbData) +{ + DWORD dwRet = ERROR_SUCCESS, dwDummy; + HKEY hSubKey; + char szEmpty[] = ""; + + TRACE("(hkey=%p,%s,%s,%ld,%p,%ld)\n", hKey, debugstr_a(lpszSubKey), + debugstr_a(lpszValue), dwType, pvData, cbData); + + if (lpszSubKey && *lpszSubKey) + dwRet = RegCreateKeyExA(hKey, lpszSubKey, 0, szEmpty, + 0, KEY_SET_VALUE, NULL, &hSubKey, &dwDummy); + else + hSubKey = hKey; + if (!dwRet) + { + dwRet = RegSetValueExA(hSubKey, lpszValue, 0, dwType, pvData, cbData); + if (hSubKey != hKey) + RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHSetValueW [SHLWAPI.@] + * + * See SHSetValueA. + */ +DWORD WINAPI SHSetValueW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue, + DWORD dwType, LPCVOID pvData, DWORD cbData) +{ + DWORD dwRet = ERROR_SUCCESS, dwDummy; + HKEY hSubKey; + WCHAR szEmpty[] = { '\0' }; + + TRACE("(hkey=%p,%s,%s,%ld,%p,%ld)\n", hKey, debugstr_w(lpszSubKey), + debugstr_w(lpszValue), dwType, pvData, cbData); + + if (lpszSubKey && *lpszSubKey) + dwRet = RegCreateKeyExW(hKey, lpszSubKey, 0, szEmpty, + 0, KEY_SET_VALUE, NULL, &hSubKey, &dwDummy); + else + hSubKey = hKey; + if (!dwRet) + { + dwRet = RegSetValueExW(hSubKey, lpszValue, 0, dwType, pvData, cbData); + if (hSubKey != hKey) + RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHQueryInfoKeyA [SHLWAPI.@] + * + * Get information about a registry key. See RegQueryInfoKeyA(). + * + * RETURNS + * The result of calling RegQueryInfoKeyA(). + */ +LONG WINAPI SHQueryInfoKeyA(HKEY hKey, LPDWORD pwSubKeys, LPDWORD pwSubKeyMax, + LPDWORD pwValues, LPDWORD pwValueMax) +{ + TRACE("(hkey=%p,%p,%p,%p,%p)\n", hKey, pwSubKeys, pwSubKeyMax, + pwValues, pwValueMax); + return RegQueryInfoKeyA(hKey, NULL, NULL, NULL, pwSubKeys, pwSubKeyMax, + NULL, pwValues, pwValueMax, NULL, NULL, NULL); +} + +/************************************************************************* + * SHQueryInfoKeyW [SHLWAPI.@] + * + * See SHQueryInfoKeyA. + */ +LONG WINAPI SHQueryInfoKeyW(HKEY hKey, LPDWORD pwSubKeys, LPDWORD pwSubKeyMax, + LPDWORD pwValues, LPDWORD pwValueMax) +{ + TRACE("(hkey=%p,%p,%p,%p,%p)\n", hKey, pwSubKeys, pwSubKeyMax, + pwValues, pwValueMax); + return RegQueryInfoKeyW(hKey, NULL, NULL, NULL, pwSubKeys, pwSubKeyMax, + NULL, pwValues, pwValueMax, NULL, NULL, NULL); +} + +/************************************************************************* + * SHQueryValueExA [SHLWAPI.@] + * + * Get a value from the registry, expanding environment variable strings. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszValue [I] Name of value to query + * lpReserved [O] Reserved for future use; must be NULL + * pwType [O] Optional pointer updated with the values type + * pvData [O] Optional pointer updated with the values data + * pcbData [O] Optional pointer updated with the values size + * + * RETURNS + * Success: ERROR_SUCCESS. Any non NULL output parameters are updated with + * information about the value. + * Failure: ERROR_OUTOFMEMORY if memory allocation fails, or the type of the + * data is REG_EXPAND_SZ and pcbData is NULL. Otherwise an error + * code from RegQueryValueExA() or ExpandEnvironmentStringsA(). + * + * NOTES + * Either pwType, pvData or pcbData may be NULL if the caller doesn't want + * the type, data or size information for the value. + * + * If the type of the data is REG_EXPAND_SZ, it is expanded to REG_SZ. The + * value returned will be truncated if it is of type REG_SZ and bigger than + * the buffer given to store it. + * + * REG_EXPAND_SZ: + * case-1: the unexpanded string is smaller than the expanded one + * subcase-1: the buffer is to small to hold the unexpanded string: + * function fails and returns the size of the unexpanded string. + * + * subcase-2: buffer is to small to hold the expanded string: + * the function return success (!!) and the result is truncated + * *** This is clearly a error in the native implementation. *** + * + * case-2: the unexpanded string is bigger than the expanded one + * The buffer must have enough space to hold the unexpanded + * string even if the result is smaller. + * + */ +DWORD WINAPI SHQueryValueExA( HKEY hKey, LPCSTR lpszValue, + LPDWORD lpReserved, LPDWORD pwType, + LPVOID pvData, LPDWORD pcbData) +{ + DWORD dwRet, dwType, dwUnExpDataLen = 0, dwExpDataLen; + + TRACE("(hkey=%p,%s,%p,%p,%p,%p=%ld)\n", hKey, debugstr_a(lpszValue), + lpReserved, pwType, pvData, pcbData, pcbData ? *pcbData : 0); + + if (pcbData) dwUnExpDataLen = *pcbData; + + dwRet = RegQueryValueExA(hKey, lpszValue, lpReserved, &dwType, pvData, &dwUnExpDataLen); + + if (pcbData && (dwType == REG_EXPAND_SZ)) + { + DWORD nBytesToAlloc; + + /* Expand type REG_EXPAND_SZ into REG_SZ */ + LPSTR szData; + + /* If the caller didn't supply a buffer or the buffer is to small we have + * to allocate our own + */ + if ((!pvData) || (dwRet == ERROR_MORE_DATA) ) + { + char cNull = '\0'; + nBytesToAlloc = (!pvData || (dwRet == ERROR_MORE_DATA)) ? dwUnExpDataLen : *pcbData; + + szData = (LPSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc); + RegQueryValueExA (hKey, lpszValue, lpReserved, NULL, (LPBYTE)szData, &nBytesToAlloc); + dwExpDataLen = ExpandEnvironmentStringsA(szData, &cNull, 1); + dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen); + LocalFree((HLOCAL) szData); + } + else + { + nBytesToAlloc = lstrlenA(pvData) * sizeof (CHAR); + szData = (LPSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc + 1); + lstrcpyA(szData, pvData); + dwExpDataLen = ExpandEnvironmentStringsA(szData, pvData, *pcbData / sizeof(CHAR)); + if (dwExpDataLen > *pcbData) dwRet = ERROR_MORE_DATA; + dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen); + LocalFree((HLOCAL) szData); + } + } + + /* Update the type and data size if the caller wanted them */ + if ( dwType == REG_EXPAND_SZ ) dwType = REG_SZ; + if ( pwType ) *pwType = dwType; + if ( pcbData ) *pcbData = dwUnExpDataLen; + return dwRet; +} + + +/************************************************************************* + * SHQueryValueExW [SHLWAPI.@] + * + * See SHQueryValueExA. + */ +DWORD WINAPI SHQueryValueExW(HKEY hKey, LPCWSTR lpszValue, + LPDWORD lpReserved, LPDWORD pwType, + LPVOID pvData, LPDWORD pcbData) +{ + DWORD dwRet, dwType, dwUnExpDataLen = 0, dwExpDataLen; + + TRACE("(hkey=%p,%s,%p,%p,%p,%p=%ld)\n", hKey, debugstr_w(lpszValue), + lpReserved, pwType, pvData, pcbData, pcbData ? *pcbData : 0); + + if (pcbData) dwUnExpDataLen = *pcbData; + + dwRet = RegQueryValueExW(hKey, lpszValue, lpReserved, &dwType, pvData, &dwUnExpDataLen); + + if (pcbData && (dwType == REG_EXPAND_SZ)) + { + DWORD nBytesToAlloc; + + /* Expand type REG_EXPAND_SZ into REG_SZ */ + LPWSTR szData; + + /* If the caller didn't supply a buffer or the buffer is to small we have + * to allocate our own + */ + if ((!pvData) || (dwRet == ERROR_MORE_DATA) ) + { + WCHAR cNull = '\0'; + nBytesToAlloc = (!pvData || (dwRet == ERROR_MORE_DATA)) ? dwUnExpDataLen : *pcbData; + + szData = (LPWSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc); + RegQueryValueExW (hKey, lpszValue, lpReserved, NULL, (LPBYTE)szData, &nBytesToAlloc); + dwExpDataLen = ExpandEnvironmentStringsW(szData, &cNull, 1); + dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen); + LocalFree((HLOCAL) szData); + } + else + { + nBytesToAlloc = lstrlenW(pvData) * sizeof(WCHAR); + szData = (LPWSTR) LocalAlloc(GMEM_ZEROINIT, nBytesToAlloc + 1); + lstrcpyW(szData, pvData); + dwExpDataLen = ExpandEnvironmentStringsW(szData, pvData, *pcbData/sizeof(WCHAR) ); + if (dwExpDataLen > *pcbData) dwRet = ERROR_MORE_DATA; + dwUnExpDataLen = max(nBytesToAlloc, dwExpDataLen); + LocalFree((HLOCAL) szData); + } + } + + /* Update the type and data size if the caller wanted them */ + if ( dwType == REG_EXPAND_SZ ) dwType = REG_SZ; + if ( pwType ) *pwType = dwType; + if ( pcbData ) *pcbData = dwUnExpDataLen; + return dwRet; +} + +/************************************************************************* + * SHDeleteKeyA [SHLWAPI.@] + * + * Delete a registry key and any sub keys/values present + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key to delete + * + * RETURNS + * Success: ERROR_SUCCESS. The key is deleted. + * Failure: An error code from RegOpenKeyExA(), RegQueryInfoKeyA(), + * RegEnumKeyExA() or RegDeleteKeyA(). + */ +DWORD WINAPI SHDeleteKeyA(HKEY hKey, LPCSTR lpszSubKey) +{ + DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i; + CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf; + HKEY hSubKey = 0; + + TRACE("(hkey=%p,%s)\n", hKey, debugstr_a(lpszSubKey)); + + dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + if(!dwRet) + { + /* Find how many subkeys there are */ + dwRet = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, &dwKeyCount, + &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL); + if(!dwRet) + { + dwMaxSubkeyLen++; + if (dwMaxSubkeyLen > sizeof(szNameBuf)) + /* Name too big: alloc a buffer for it */ + lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(CHAR)); + + if(!lpszName) + dwRet = ERROR_NOT_ENOUGH_MEMORY; + else + { + /* Recursively delete all the subkeys */ + for(i = 0; i < dwKeyCount && !dwRet; i++) + { + dwSize = dwMaxSubkeyLen; + dwRet = RegEnumKeyExA(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL); + if(!dwRet) + dwRet = SHDeleteKeyA(hSubKey, lpszName); + } + if (lpszName != szNameBuf) + HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */ + } + } + + RegCloseKey(hSubKey); + if(!dwRet) + dwRet = RegDeleteKeyA(hKey, lpszSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHDeleteKeyW [SHLWAPI.@] + * + * See SHDeleteKeyA. + */ +DWORD WINAPI SHDeleteKeyW(HKEY hKey, LPCWSTR lpszSubKey) +{ + DWORD dwRet, dwKeyCount = 0, dwMaxSubkeyLen = 0, dwSize, i; + WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf; + HKEY hSubKey = 0; + + TRACE("(hkey=%p,%s)\n", hKey, debugstr_w(lpszSubKey)); + + dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + if(!dwRet) + { + /* Find how many subkeys there are */ + dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount, + &dwMaxSubkeyLen, NULL, NULL, NULL, NULL, NULL, NULL); + if(!dwRet) + { + dwMaxSubkeyLen++; + if (dwMaxSubkeyLen > sizeof(szNameBuf)/sizeof(WCHAR)) + /* Name too big: alloc a buffer for it */ + lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubkeyLen*sizeof(WCHAR)); + + if(!lpszName) + dwRet = ERROR_NOT_ENOUGH_MEMORY; + else + { + /* Recursively delete all the subkeys */ + for(i = 0; i < dwKeyCount && !dwRet; i++) + { + dwSize = dwMaxSubkeyLen; + dwRet = RegEnumKeyExW(hSubKey, i, lpszName, &dwSize, NULL, NULL, NULL, NULL); + if(!dwRet) + dwRet = SHDeleteKeyW(hSubKey, lpszName); + } + + if (lpszName != szNameBuf) + HeapFree(GetProcessHeap(), 0, lpszName); /* Free buffer if allocated */ + } + } + + RegCloseKey(hSubKey); + if(!dwRet) + dwRet = RegDeleteKeyW(hKey, lpszSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHDeleteEmptyKeyA [SHLWAPI.@] + * + * Delete a registry key with no sub keys. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key to delete + * + * RETURNS + * Success: ERROR_SUCCESS. The key is deleted. + * Failure: If the key is not empty, returns ERROR_KEY_HAS_CHILDREN. Otherwise + * returns an error code from RegOpenKeyExA(), RegQueryInfoKeyA() or + * RegDeleteKeyA(). + */ +DWORD WINAPI SHDeleteEmptyKeyA(HKEY hKey, LPCSTR lpszSubKey) +{ + DWORD dwRet, dwKeyCount = 0; + HKEY hSubKey = 0; + + TRACE("(hkey=%p,%s)\n", hKey, debugstr_a(lpszSubKey)); + + dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + if(!dwRet) + { + dwRet = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, &dwKeyCount, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + RegCloseKey(hSubKey); + if(!dwRet) + { + if (!dwKeyCount) + dwRet = RegDeleteKeyA(hKey, lpszSubKey); + else + dwRet = ERROR_KEY_HAS_CHILDREN; + } + } + return dwRet; +} + +/************************************************************************* + * SHDeleteEmptyKeyW [SHLWAPI.@] + * + * See SHDeleteEmptyKeyA. + */ +DWORD WINAPI SHDeleteEmptyKeyW(HKEY hKey, LPCWSTR lpszSubKey) +{ + DWORD dwRet, dwKeyCount = 0; + HKEY hSubKey = 0; + + TRACE("(hkey=%p, %s)\n", hKey, debugstr_w(lpszSubKey)); + + dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + if(!dwRet) + { + dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + RegCloseKey(hSubKey); + if(!dwRet) + { + if (!dwKeyCount) + dwRet = RegDeleteKeyW(hKey, lpszSubKey); + else + dwRet = ERROR_KEY_HAS_CHILDREN; + } + } + return dwRet; +} + +/************************************************************************* + * SHDeleteOrphanKeyA [SHLWAPI.@] + * + * Delete a registry key with no sub keys or values. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key to possibly delete + * + * RETURNS + * Success: ERROR_SUCCESS. The key has been deleted if it was an orphan. + * Failure: An error from RegOpenKeyExA(), RegQueryValueExA(), or RegDeleteKeyA(). + */ +DWORD WINAPI SHDeleteOrphanKeyA(HKEY hKey, LPCSTR lpszSubKey) +{ + HKEY hSubKey; + DWORD dwKeyCount = 0, dwValueCount = 0, dwRet; + + TRACE("(hkey=%p,%s)\n", hKey, debugstr_a(lpszSubKey)); + + dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + + if(!dwRet) + { + /* Get subkey and value count */ + dwRet = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, &dwKeyCount, + NULL, NULL, &dwValueCount, NULL, NULL, NULL, NULL); + + if(!dwRet && !dwKeyCount && !dwValueCount) + { + dwRet = RegDeleteKeyA(hKey, lpszSubKey); + } + RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHDeleteOrphanKeyW [SHLWAPI.@] + * + * See SHDeleteOrphanKeyA. + */ +DWORD WINAPI SHDeleteOrphanKeyW(HKEY hKey, LPCWSTR lpszSubKey) +{ + HKEY hSubKey; + DWORD dwKeyCount = 0, dwValueCount = 0, dwRet; + + TRACE("(hkey=%p,%s)\n", hKey, debugstr_w(lpszSubKey)); + + dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_READ, &hSubKey); + + if(!dwRet) + { + /* Get subkey and value count */ + dwRet = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, &dwKeyCount, + NULL, NULL, &dwValueCount, NULL, NULL, NULL, NULL); + + if(!dwRet && !dwKeyCount && !dwValueCount) + { + dwRet = RegDeleteKeyW(hKey, lpszSubKey); + } + RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHDeleteValueA [SHLWAPI.@] + * + * Delete a value from the registry. + * + * PARAMS + * hKey [I] Handle to registry key + * lpszSubKey [I] Name of sub key containing value to delete + * lpszValue [I] Name of value to delete + * + * RETURNS + * Success: ERROR_SUCCESS. The value is deleted. + * Failure: An error code from RegOpenKeyExA() or RegDeleteValueA(). + */ +DWORD WINAPI SHDeleteValueA(HKEY hKey, LPCSTR lpszSubKey, LPCSTR lpszValue) +{ + DWORD dwRet; + HKEY hSubKey; + + TRACE("(hkey=%p,%s,%s)\n", hKey, debugstr_a(lpszSubKey), debugstr_a(lpszValue)); + + dwRet = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_SET_VALUE, &hSubKey); + if (!dwRet) + { + dwRet = RegDeleteValueA(hSubKey, lpszValue); + RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHDeleteValueW [SHLWAPI.@] + * + * See SHDeleteValueA. + */ +DWORD WINAPI SHDeleteValueW(HKEY hKey, LPCWSTR lpszSubKey, LPCWSTR lpszValue) +{ + DWORD dwRet; + HKEY hSubKey; + + TRACE("(hkey=%p,%s,%s)\n", hKey, debugstr_w(lpszSubKey), debugstr_w(lpszValue)); + + dwRet = RegOpenKeyExW(hKey, lpszSubKey, 0, KEY_SET_VALUE, &hSubKey); + if (!dwRet) + { + dwRet = RegDeleteValueW(hSubKey, lpszValue); + RegCloseKey(hSubKey); + } + return dwRet; +} + +/************************************************************************* + * SHEnumKeyExA [SHLWAPI.@] + * + * Enumerate sub keys in a registry key. + * + * PARAMS + * hKey [I] Handle to registry key + * dwIndex [I] Index of key to enumerate + * lpszSubKey [O] Pointer updated with the subkey name + * pwLen [O] Pointer updated with the subkey length + * + * RETURNS + * Success: ERROR_SUCCESS. lpszSubKey and pwLen are updated. + * Failure: An error code from RegEnumKeyExA(). + */ +LONG WINAPI SHEnumKeyExA(HKEY hKey, DWORD dwIndex, LPSTR lpszSubKey, + LPDWORD pwLen) +{ + TRACE("(hkey=%p,%ld,%s,%p)\n", hKey, dwIndex, debugstr_a(lpszSubKey), pwLen); + + return RegEnumKeyExA(hKey, dwIndex, lpszSubKey, pwLen, NULL, NULL, NULL, NULL); +} + +/************************************************************************* + * SHEnumKeyExW [SHLWAPI.@] + * + * See SHEnumKeyExA. + */ +LONG WINAPI SHEnumKeyExW(HKEY hKey, DWORD dwIndex, LPWSTR lpszSubKey, + LPDWORD pwLen) +{ + TRACE("(hkey=%p,%ld,%s,%p)\n", hKey, dwIndex, debugstr_w(lpszSubKey), pwLen); + + return RegEnumKeyExW(hKey, dwIndex, lpszSubKey, pwLen, NULL, NULL, NULL, NULL); +} + +/************************************************************************* + * SHEnumValueA [SHLWAPI.@] + * + * Enumerate values in a registry key. + * + * PARAMS + * hKey [I] Handle to registry key + * dwIndex [I] Index of key to enumerate + * lpszValue [O] Pointer updated with the values name + * pwLen [O] Pointer updated with the values length + * pwType [O] Pointer updated with the values type + * pvData [O] Pointer updated with the values data + * pcbData [O] Pointer updated with the values size + * + * RETURNS + * Success: ERROR_SUCCESS. Output parameters are updated. + * Failure: An error code from RegEnumValueA(). + */ +LONG WINAPI SHEnumValueA(HKEY hKey, DWORD dwIndex, LPSTR lpszValue, + LPDWORD pwLen, LPDWORD pwType, + LPVOID pvData, LPDWORD pcbData) +{ + TRACE("(hkey=%p,%ld,%s,%p,%p,%p,%p)\n", hKey, dwIndex, + debugstr_a(lpszValue), pwLen, pwType, pvData, pcbData); + + return RegEnumValueA(hKey, dwIndex, lpszValue, pwLen, NULL, + pwType, pvData, pcbData); +} + +/************************************************************************* + * SHEnumValueW [SHLWAPI.@] + * + * See SHEnumValueA. + */ +LONG WINAPI SHEnumValueW(HKEY hKey, DWORD dwIndex, LPWSTR lpszValue, + LPDWORD pwLen, LPDWORD pwType, + LPVOID pvData, LPDWORD pcbData) +{ + TRACE("(hkey=%p,%ld,%s,%p,%p,%p,%p)\n", hKey, dwIndex, + debugstr_w(lpszValue), pwLen, pwType, pvData, pcbData); + + return RegEnumValueW(hKey, dwIndex, lpszValue, pwLen, NULL, + pwType, pvData, pcbData); +} + +/************************************************************************* + * @ [SHLWAPI.205] + * + * Get a value from the registry. + * + * PARAMS + * hKey [I] Handle to registry key + * pSubKey [I] Name of sub key containing value to get + * pValue [I] Name of value to get + * pwType [O] Destination for the values type + * pvData [O] Destination for the values data + * pbData [O] Destination for the values size + * + * RETURNS + * Success: ERROR_SUCCESS. Output parameters contain the details read. + * Failure: An error code from RegOpenKeyExA() or SHQueryValueExA(), + * or ERROR_INVALID_FUNCTION in the machine is in safe mode. + */ +DWORD WINAPI SHGetValueGoodBootA(HKEY hkey, LPCSTR pSubKey, LPCSTR pValue, + LPDWORD pwType, LPVOID pvData, LPDWORD pbData) +{ + if (GetSystemMetrics(SM_CLEANBOOT)) + return ERROR_INVALID_FUNCTION; + return SHGetValueA(hkey, pSubKey, pValue, pwType, pvData, pbData); +} + +/************************************************************************* + * @ [SHLWAPI.206] + * + * Unicode version of SHGetValueGoodBootW. + */ +DWORD WINAPI SHGetValueGoodBootW(HKEY hkey, LPCWSTR pSubKey, LPCWSTR pValue, + LPDWORD pwType, LPVOID pvData, LPDWORD pbData) +{ + if (GetSystemMetrics(SM_CLEANBOOT)) + return ERROR_INVALID_FUNCTION; + return SHGetValueW(hkey, pSubKey, pValue, pwType, pvData, pbData); +} + +/************************************************************************* + * @ [SHLWAPI.320] + * + * Set a MIME content type in the registry. + * + * PARAMS + * lpszSubKey [I] Name of key under HKEY_CLASSES_ROOT. + * lpszValue [I] Value to set + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI RegisterMIMETypeForExtensionA(LPCSTR lpszSubKey, LPCSTR lpszValue) +{ + DWORD dwRet; + + if (!lpszValue) + { + WARN("Invalid lpszValue would crash under Win32!\n"); + return FALSE; + } + + dwRet = SHSetValueA(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeA, + REG_SZ, lpszValue, strlen(lpszValue)); + return dwRet ? FALSE : TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.321] + * + * Unicode version of RegisterMIMETypeForExtensionA. + */ +BOOL WINAPI RegisterMIMETypeForExtensionW(LPCWSTR lpszSubKey, LPCWSTR lpszValue) +{ + DWORD dwRet; + + if (!lpszValue) + { + WARN("Invalid lpszValue would crash under Win32!\n"); + return FALSE; + } + + dwRet = SHSetValueW(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeW, + REG_SZ, lpszValue, strlenW(lpszValue)); + return dwRet ? FALSE : TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.322] + * + * Delete a MIME content type from the registry. + * + * PARAMS + * lpszSubKey [I] Name of sub key + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI UnregisterMIMETypeForExtensionA(LPCSTR lpszSubKey) +{ + HRESULT ret = SHDeleteValueA(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeA); + return ret ? FALSE : TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.323] + * + * Unicode version of UnregisterMIMETypeForExtensionA. + */ +BOOL WINAPI UnregisterMIMETypeForExtensionW(LPCWSTR lpszSubKey) +{ + HRESULT ret = SHDeleteValueW(HKEY_CLASSES_ROOT, lpszSubKey, lpszContentTypeW); + return ret ? FALSE : TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.328] + * + * Get the registry path to a MIME content key. + * + * PARAMS + * lpszType [I] Content type to get the path for + * lpszBuffer [O] Destination for path + * dwLen [I] Length of lpszBuffer + * + * RETURNS + * Success: TRUE. lpszBuffer contains the full path. + * Failure: FALSE. + * + * NOTES + * The base path for the key is "MIME\Database\Content Type\" + */ +BOOL WINAPI GetMIMETypeSubKeyA(LPCSTR lpszType, LPSTR lpszBuffer, DWORD dwLen) +{ + TRACE("(%s,%p,%ld)\n", debugstr_a(lpszType), lpszBuffer, dwLen); + + if (dwLen > dwLenMimeDbContent && lpszType && lpszBuffer) + { + size_t dwStrLen = strlen(lpszType); + + if (dwStrLen < dwLen - dwLenMimeDbContent) + { + memcpy(lpszBuffer, szMimeDbContentA, dwLenMimeDbContent); + memcpy(lpszBuffer + dwLenMimeDbContent, lpszType, dwStrLen + 1); + return TRUE; + } + } + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.329] + * + * Unicode version of GetMIMETypeSubKeyA. + */ +BOOL WINAPI GetMIMETypeSubKeyW(LPCWSTR lpszType, LPWSTR lpszBuffer, DWORD dwLen) +{ + TRACE("(%s,%p,%ld)\n", debugstr_w(lpszType), lpszBuffer, dwLen); + + if (dwLen > dwLenMimeDbContent && lpszType && lpszBuffer) + { + DWORD dwStrLen = strlenW(lpszType); + + if (dwStrLen < dwLen - dwLenMimeDbContent) + { + memcpy(lpszBuffer, szMimeDbContentW, dwLenMimeDbContent * sizeof(WCHAR)); + memcpy(lpszBuffer + dwLenMimeDbContent, lpszType, (dwStrLen + 1) * sizeof(WCHAR)); + return TRUE; + } + } + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.324] + * + * Set the file extension for a MIME content key. + * + * PARAMS + * lpszExt [I] File extension to set + * lpszType [I] Content type to set the extension for + * + * RETURNS + * Success: TRUE. The file extension is set in the registry. + * Failure: FALSE. + */ +BOOL WINAPI RegisterExtensionForMIMETypeA(LPCSTR lpszExt, LPCSTR lpszType) +{ + DWORD dwLen; + char szKey[MAX_PATH]; + + TRACE("(%s,%s)\n", debugstr_a(lpszExt), debugstr_a(lpszType)); + + if (!GetMIMETypeSubKeyA(lpszType, szKey, MAX_PATH)) /* Get full path to the key */ + return FALSE; + + dwLen = strlen(lpszExt) + 1; + + if (SHSetValueA(HKEY_CLASSES_ROOT, szKey, szExtensionA, REG_SZ, lpszExt, dwLen)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.325] + * + * Unicode version of RegisterExtensionForMIMETypeA. + */ +BOOL WINAPI RegisterExtensionForMIMETypeW(LPCWSTR lpszExt, LPCWSTR lpszType) +{ + DWORD dwLen; + WCHAR szKey[MAX_PATH]; + + TRACE("(%s,%s)\n", debugstr_w(lpszExt), debugstr_w(lpszType)); + + /* Get the full path to the key */ + if (!GetMIMETypeSubKeyW(lpszType, szKey, MAX_PATH)) /* Get full path to the key */ + return FALSE; + + dwLen = (lstrlenW(lpszExt) + 1) * sizeof(WCHAR); + + if (SHSetValueW(HKEY_CLASSES_ROOT, szKey, szExtensionW, REG_SZ, lpszExt, dwLen)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.326] + * + * Delete a file extension from a MIME content type. + * + * PARAMS + * lpszType [I] Content type to delete the extension for + * + * RETURNS + * Success: TRUE. The file extension is deleted from the registry. + * Failure: FALSE. The extension may have been removed but the key remains. + * + * NOTES + * If deleting the extension leaves an orphan key, the key is removed also. + */ +BOOL WINAPI UnregisterExtensionForMIMETypeA(LPCSTR lpszType) +{ + char szKey[MAX_PATH]; + + TRACE("(%s)\n", debugstr_a(lpszType)); + + if (!GetMIMETypeSubKeyA(lpszType, szKey, MAX_PATH)) /* Get full path to the key */ + return FALSE; + + if (!SHDeleteValueA(HKEY_CLASSES_ROOT, szKey, szExtensionA)) + return FALSE; + + if (!SHDeleteOrphanKeyA(HKEY_CLASSES_ROOT, szKey)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.327] + * + * Unicode version of UnregisterExtensionForMIMETypeA. + */ +BOOL WINAPI UnregisterExtensionForMIMETypeW(LPCWSTR lpszType) +{ + WCHAR szKey[MAX_PATH]; + + TRACE("(%s)\n", debugstr_w(lpszType)); + + if (!GetMIMETypeSubKeyW(lpszType, szKey, MAX_PATH)) /* Get full path to the key */ + return FALSE; + + if (!SHDeleteValueW(HKEY_CLASSES_ROOT, szKey, szExtensionW)) + return FALSE; + + if (!SHDeleteOrphanKeyW(HKEY_CLASSES_ROOT, szKey)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * SHRegDuplicateHKey [SHLWAPI.@] + * + * Create a duplicate of a registry handle. + * + * PARAMS + * hKey [I] key to duplicate. + * + * RETURNS + * A new handle pointing to the same key as hKey. + */ +HKEY WINAPI SHRegDuplicateHKey(HKEY hKey) +{ + HKEY newKey = 0; + + RegOpenKeyExA(hKey, 0, 0, MAXIMUM_ALLOWED, &newKey); + TRACE("new key is %p\n", newKey); + return newKey; +} + + +/************************************************************************* + * SHCopyKeyA [SHLWAPI.@] + * + * Copy a key and its values/sub keys to another location. + * + * PARAMS + * hKeyDst [I] Destination key + * lpszSubKey [I] Sub key under hKeyDst, or NULL to use hKeyDst directly + * hKeySrc [I] Source key to copy from + * dwReserved [I] Reserved, must be 0 + * + * RETURNS + * Success: ERROR_SUCCESS. The key is copied to the destination key. + * Failure: A standard windows error code. + * + * NOTES + * If hKeyDst is a key under hKeySrc, this function will misbehave + * (It will loop until out of stack, or the registry is full). This + * bug is present in Win32 also. + */ +DWORD WINAPI SHCopyKeyA(HKEY hKeyDst, LPCSTR lpszSubKey, HKEY hKeySrc, DWORD dwReserved) +{ + WCHAR szSubKeyW[MAX_PATH]; + + TRACE("(hkey=%p,%s,%p08x,%ld)\n", hKeyDst, debugstr_a(lpszSubKey), hKeySrc, dwReserved); + + if (lpszSubKey) + MultiByteToWideChar(0, 0, lpszSubKey, -1, szSubKeyW, MAX_PATH); + + return SHCopyKeyW(hKeyDst, lpszSubKey ? szSubKeyW : NULL, hKeySrc, dwReserved); +} + +/************************************************************************* + * SHCopyKeyW [SHLWAPI.@] + * + * See SHCopyKeyA. + */ +DWORD WINAPI SHCopyKeyW(HKEY hKeyDst, LPCWSTR lpszSubKey, HKEY hKeySrc, DWORD dwReserved) +{ + DWORD dwKeyCount = 0, dwValueCount = 0, dwMaxKeyLen = 0; + DWORD dwMaxValueLen = 0, dwMaxDataLen = 0, i; + BYTE buff[1024]; + LPVOID lpBuff = (LPVOID)buff; + WCHAR szName[MAX_PATH], *lpszName = szName; + DWORD dwRet = S_OK; + + TRACE("hkey=%p,%s,%p08x,%ld)\n", hKeyDst, debugstr_w(lpszSubKey), hKeySrc, dwReserved); + + if(!hKeyDst || !hKeySrc) + dwRet = ERROR_INVALID_PARAMETER; + else + { + /* Open destination key */ + if(lpszSubKey) + dwRet = RegOpenKeyExW(hKeyDst, lpszSubKey, 0, KEY_ALL_ACCESS, &hKeyDst); + + if(dwRet) + hKeyDst = 0; /* Don't close this key since we didn't open it */ + else + { + /* Get details about sub keys and values */ + dwRet = RegQueryInfoKeyW(hKeySrc, NULL, NULL, NULL, &dwKeyCount, &dwMaxKeyLen, + NULL, &dwValueCount, &dwMaxValueLen, &dwMaxDataLen, + NULL, NULL); + if(!dwRet) + { + if (dwMaxValueLen > dwMaxKeyLen) + dwMaxKeyLen = dwMaxValueLen; /* Get max size for key/value names */ + + if (dwMaxKeyLen++ > MAX_PATH - 1) + lpszName = HeapAlloc(GetProcessHeap(), 0, dwMaxKeyLen * sizeof(WCHAR)); + + if (dwMaxDataLen > sizeof(buff)) + lpBuff = HeapAlloc(GetProcessHeap(), 0, dwMaxDataLen); + + if (!lpszName || !lpBuff) + dwRet = ERROR_NOT_ENOUGH_MEMORY; + } + } + } + + /* Copy all the sub keys */ + for(i = 0; i < dwKeyCount && !dwRet; i++) + { + HKEY hSubKeySrc, hSubKeyDst; + DWORD dwSize = dwMaxKeyLen; + + dwRet = RegEnumKeyExW(hKeySrc, i, lpszName, &dwSize, NULL, NULL, NULL, NULL); + + if(!dwRet) + { + /* Open source sub key */ + dwRet = RegOpenKeyExW(hKeySrc, lpszName, 0, KEY_READ, &hSubKeySrc); + + if(!dwRet) + { + /* Create destination sub key */ + dwRet = RegCreateKeyW(hKeyDst, lpszName, &hSubKeyDst); + + if(!dwRet) + { + /* Recursively copy keys and values from the sub key */ + dwRet = SHCopyKeyW(hSubKeyDst, NULL, hSubKeySrc, 0); + RegCloseKey(hSubKeyDst); + } + } + RegCloseKey(hSubKeySrc); + } + } + + /* Copy all the values in this key */ + for (i = 0; i < dwValueCount && !dwRet; i++) + { + DWORD dwNameSize = dwMaxKeyLen, dwType, dwLen = dwMaxDataLen; + + dwRet = RegEnumValueW(hKeySrc, i, lpszName, &dwNameSize, NULL, &dwType, buff, &dwLen); + + if (!dwRet) + dwRet = SHSetValueW(hKeyDst, NULL, lpszName, dwType, lpBuff, dwLen); + } + + /* Free buffers if allocated */ + if (lpszName != szName) + HeapFree(GetProcessHeap(), 0, lpszName); + if (lpBuff != buff) + HeapFree(GetProcessHeap(), 0, lpBuff); + + if (lpszSubKey && hKeyDst) + RegCloseKey(hKeyDst); + return dwRet; +} + +/* + * The following functions are ORDINAL ONLY: + */ + +/************************************************************************* + * @ [SHLWAPI.280] + * + * Read an integer value from the registry, falling back to a default. + * + * PARAMS + * hKey [I] Registry key to read from + * lpszValue [I] Value name to read + * iDefault [I] Default value to return + * + * RETURNS + * The value contained in the given registry value if present, otherwise + * iDefault. + */ +int WINAPI SHRegGetIntW(HKEY hKey, LPCWSTR lpszValue, int iDefault) +{ + TRACE("(%p,%s,%d)", hKey, debugstr_w(lpszValue), iDefault); + + if (hKey) + { + WCHAR szBuff[32]; + DWORD dwSize = sizeof(szBuff); + szBuff[0] = '\0'; + SHQueryValueExW(hKey, lpszValue, 0, 0, szBuff, &dwSize); + + if(*szBuff >= '0' && *szBuff <= '9') + return StrToIntW(szBuff); + } + return iDefault; +} + +/************************************************************************* + * @ [SHLWAPI.343] + * + * Create or open an explorer ClassId Key. + * + * PARAMS + * guid [I] Explorer ClassId key to open + * lpszValue [I] Value name under the ClassId Key + * bUseHKCU [I] TRUE=Use HKEY_CURRENT_USER, FALSE=Use HKEY_CLASSES_ROOT + * bCreate [I] TRUE=Create the key if it doesn't exist, FALSE=Don't + * phKey [O] Destination for the resulting key handle + * + * RETURNS + * Success: S_OK. phKey contains the resulting registry handle. + * Failure: An HRESULT error code indicating the problem. + */ +HRESULT WINAPI SHRegGetCLSIDKeyA(REFGUID guid, LPCSTR lpszValue, BOOL bUseHKCU, BOOL bCreate, PHKEY phKey) +{ + WCHAR szValue[MAX_PATH]; + + if (lpszValue) + MultiByteToWideChar(CP_ACP, 0, lpszValue, -1, szValue, sizeof(szValue)/sizeof(WCHAR)); + + return SHRegGetCLSIDKeyW(guid, lpszValue ? szValue : NULL, bUseHKCU, bCreate, phKey); +} + +/************************************************************************* + * @ [SHLWAPI.344] + * + * Unicode version of SHRegGetCLSIDKeyA. + */ +HRESULT WINAPI SHRegGetCLSIDKeyW(REFGUID guid, LPCWSTR lpszValue, BOOL bUseHKCU, + BOOL bCreate, PHKEY phKey) +{ + static const WCHAR szClassIdKey[] = { 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'E','x','p','l','o','r','e','r','\\','C','L','S','I','D','\\' }; +#define szClassIdKeyLen (sizeof(szClassIdKey)/sizeof(WCHAR)) + WCHAR szKey[MAX_PATH]; + DWORD dwRet; + HKEY hkey; + + /* Create the key string */ + memcpy(szKey, szClassIdKey, sizeof(szClassIdKey)); + SHStringFromGUIDW(guid, szKey + szClassIdKeyLen, 39); /* Append guid */ + + if(lpszValue) + { + szKey[szClassIdKeyLen + 39] = '\\'; + strcpyW(szKey + szClassIdKeyLen + 40, lpszValue); /* Append value name */ + } + + hkey = bUseHKCU ? HKEY_CURRENT_USER : HKEY_CLASSES_ROOT; + + if(bCreate) + dwRet = RegCreateKeyW(hkey, szKey, phKey); + else + dwRet = RegOpenKeyExW(hkey, szKey, 0, KEY_READ, phKey); + + return dwRet ? HRESULT_FROM_WIN32(dwRet) : S_OK; +} diff --git a/reactos/lib/shlwapi/regstream.c b/reactos/lib/shlwapi/regstream.c new file mode 100644 index 00000000000..ef05a09bcee --- /dev/null +++ b/reactos/lib/shlwapi/regstream.c @@ -0,0 +1,573 @@ +/* + * SHLWAPI Registry Stream functions + * + * Copyright 1999 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 + */ + +#include +#include + +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#include "winerror.h" +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "winreg.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +typedef struct +{ ICOM_VFIELD(IStream); + DWORD ref; + HKEY hKey; + LPBYTE pbBuffer; + DWORD dwLength; + DWORD dwPos; +} ISHRegStream; + +/************************************************************************** +* IStream_fnQueryInterface +*/ +static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, REFIID riid, LPVOID *ppvObj) +{ + ICOM_THIS(ISHRegStream, iface); + + TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj); + + *ppvObj = NULL; + + if(IsEqualIID(riid, &IID_IUnknown)) /*IUnknown*/ + *ppvObj = This; + else if(IsEqualIID(riid, &IID_IStream)) /*IStream*/ + *ppvObj = This; + + if(*ppvObj) + { + IStream_AddRef((IStream*)*ppvObj); + TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj); + return S_OK; + } + TRACE("-- Interface: E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +/************************************************************************** +* IStream_fnAddRef +*/ +static ULONG WINAPI IStream_fnAddRef(IStream *iface) +{ + ICOM_THIS(ISHRegStream, iface); + + TRACE("(%p)->(count=%lu)\n",This, This->ref); + + return InterlockedIncrement(&This->ref); +} + +/************************************************************************** +* IStream_fnRelease +*/ +static ULONG WINAPI IStream_fnRelease(IStream *iface) +{ + ICOM_THIS(ISHRegStream, iface); + + TRACE("(%p)->()\n",This); + + if (!InterlockedDecrement(&This->ref)) + { + TRACE(" destroying SHReg IStream (%p)\n",This); + + if (This->pbBuffer) + HeapFree(GetProcessHeap(),0,This->pbBuffer); + + if (This->hKey) + RegCloseKey(This->hKey); + + HeapFree(GetProcessHeap(),0,This); + return 0; + } + return This->ref; +} + +/************************************************************************** + * IStream_fnRead + */ +static HRESULT WINAPI IStream_fnRead (IStream * iface, void* pv, ULONG cb, ULONG* pcbRead) +{ + ICOM_THIS(ISHRegStream, iface); + + DWORD dwBytesToRead, dwBytesLeft; + + TRACE("(%p)->(%p,0x%08lx,%p)\n",This, pv, cb, pcbRead); + + if (!pv) + return STG_E_INVALIDPOINTER; + + dwBytesLeft = This->dwLength - This->dwPos; + + if ( 0 >= dwBytesLeft ) /* end of buffer */ + return S_FALSE; + + dwBytesToRead = ( cb > dwBytesLeft) ? dwBytesLeft : cb; + + memmove ( pv, (This->pbBuffer) + (This->dwPos), dwBytesToRead); + + This->dwPos += dwBytesToRead; /* adjust pointer */ + + if (pcbRead) + *pcbRead = dwBytesToRead; + + return S_OK; +} + +/************************************************************************** + * IStream_fnWrite + */ +static HRESULT WINAPI IStream_fnWrite (IStream * iface, const void* pv, ULONG cb, ULONG* pcbWritten) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + + if (pcbWritten) + *pcbWritten = 0; + + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnSeek + */ +static HRESULT WINAPI IStream_fnSeek (IStream * iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + + if (plibNewPosition) + plibNewPosition->QuadPart = 0; + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnSetSize + */ +static HRESULT WINAPI IStream_fnSetSize (IStream * iface, ULARGE_INTEGER libNewSize) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnCopyTo + */ +static HRESULT WINAPI IStream_fnCopyTo (IStream * iface, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + if (pcbRead) + pcbRead->QuadPart = 0; + if (pcbWritten) + pcbWritten->QuadPart = 0; + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnCommit + */ +static HRESULT WINAPI IStream_fnCommit (IStream * iface, DWORD grfCommitFlags) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnRevert + */ +static HRESULT WINAPI IStream_fnRevert (IStream * iface) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +/************************************************************************** + * IStream_fnLockUnlockRegion + */ +static HRESULT WINAPI IStream_fnLockUnlockRegion (IStream * iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +/************************************************************************* + * IStream_fnStat + */ +static HRESULT WINAPI IStream_fnStat (IStream * iface, STATSTG* pstatstg, DWORD grfStatFlag) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +/************************************************************************* + * IStream_fnClone + */ +static HRESULT WINAPI IStream_fnClone (IStream * iface, IStream** ppstm) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + + TRACE("(%p)\n",This); + if (ppstm) + *ppstm = NULL; + return E_NOTIMPL; +} + +static struct ICOM_VTABLE(IStream) rstvt = +{ + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IStream_fnQueryInterface, + IStream_fnAddRef, + IStream_fnRelease, + IStream_fnRead, + IStream_fnWrite, + IStream_fnSeek, + IStream_fnSetSize, + IStream_fnCopyTo, + IStream_fnCommit, + IStream_fnRevert, + IStream_fnLockUnlockRegion, + IStream_fnLockUnlockRegion, + IStream_fnStat, + IStream_fnClone +}; + +/* Methods overridden by the dummy stream */ + +/************************************************************************** + * IStream_fnAddRefDummy + */ +static ULONG WINAPI IStream_fnAddRefDummy(IStream *iface) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + TRACE("(%p)\n", This); + return 2; +} + +/************************************************************************** + * IStream_fnReleaseDummy + */ +static ULONG WINAPI IStream_fnReleaseDummy(IStream *iface) +{ +#ifndef NDEBUG + ICOM_THIS(ISHRegStream, iface); +#endif + TRACE("(%p)\n", This); + return 1; +} + +/************************************************************************** + * IStream_fnReadDummy + */ +static HRESULT WINAPI IStream_fnReadDummy(IStream *iface, LPVOID pv, ULONG cb, ULONG* pcbRead) +{ + if (pcbRead) + *pcbRead = 0; + return E_NOTIMPL; +} + +static struct ICOM_VTABLE(IStream) DummyRegStreamVTable = +{ + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IStream_fnQueryInterface, + IStream_fnAddRefDummy, /* Overridden */ + IStream_fnReleaseDummy, /* Overridden */ + IStream_fnReadDummy, /* Overridden */ + IStream_fnWrite, + IStream_fnSeek, + IStream_fnSetSize, + IStream_fnCopyTo, + IStream_fnCommit, + IStream_fnRevert, + IStream_fnLockUnlockRegion, + IStream_fnLockUnlockRegion, + IStream_fnStat, + IStream_fnClone +}; + +/* Dummy registry stream object */ +static ISHRegStream rsDummyRegStream = +{ + &DummyRegStreamVTable, + 1, + NULL, + NULL, + 0, + 0 +}; + +/************************************************************************** + * IStream_Create + * + * Internal helper: Create and initialise a new registry stream object. + */ +static IStream *IStream_Create(HKEY hKey, LPBYTE pbBuffer, DWORD dwLength) +{ + ISHRegStream* regStream; + + regStream = (ISHRegStream*)HeapAlloc(GetProcessHeap(), 0, sizeof(ISHRegStream)); + + if (regStream) + { + regStream->lpVtbl = &rstvt; + regStream->ref = 1; + regStream->hKey = hKey; + regStream->pbBuffer = pbBuffer; + regStream->dwLength = dwLength; + regStream->dwPos = 0; + } + TRACE ("Returning %p\n", regStream); + return (IStream *)regStream; +} + +/************************************************************************* + * SHOpenRegStream2A [SHLWAPI.@] + * + * Create a stream to read binary registry data. + * + * PARAMS + * hKey [I] Registry handle + * pszSubkey [I] The sub key name + * pszValue [I] The value name under the sub key + * dwMode [I] Unused + * + * RETURNS + * Success: An IStream interface referring to the registry data + * Failure: NULL, if the registry key could not be opened or is not binary. + */ +IStream * WINAPI SHOpenRegStream2A(HKEY hKey, LPCSTR pszSubkey, + LPCSTR pszValue,DWORD dwMode) +{ + HKEY hStrKey = NULL; + LPBYTE lpBuff = NULL; + DWORD dwLength, dwType; + + TRACE("(%p,%s,%s,0x%08lx)\n", hKey, pszSubkey, pszValue, dwMode); + + /* Open the key, read in binary data and create stream */ + if (!RegOpenKeyExA (hKey, pszSubkey, 0, KEY_READ, &hStrKey) && + !RegQueryValueExA (hStrKey, pszValue, 0, 0, 0, &dwLength) && + (lpBuff = HeapAlloc (GetProcessHeap(), 0, dwLength)) && + !RegQueryValueExA (hStrKey, pszValue, 0, &dwType, lpBuff, &dwLength) && + dwType == REG_BINARY) + return IStream_Create(hStrKey, lpBuff, dwLength); + + if (lpBuff) + HeapFree (GetProcessHeap(), 0, lpBuff); + if (hStrKey) + RegCloseKey(hStrKey); + return NULL; +} + +/************************************************************************* + * SHOpenRegStream2W [SHLWAPI.@] + * + * See SHOpenRegStream2A. + */ +IStream * WINAPI SHOpenRegStream2W(HKEY hKey, LPCWSTR pszSubkey, + LPCWSTR pszValue, DWORD dwMode) +{ + HKEY hStrKey = NULL; + LPBYTE lpBuff = NULL; + DWORD dwLength, dwType; + + TRACE("(%p,%s,%s,0x%08lx)\n", hKey, debugstr_w(pszSubkey), + debugstr_w(pszValue), dwMode); + + /* Open the key, read in binary data and create stream */ + if (!RegOpenKeyExW (hKey, pszSubkey, 0, KEY_READ, &hStrKey) && + !RegQueryValueExW (hStrKey, pszValue, 0, 0, 0, &dwLength) && + (lpBuff = HeapAlloc (GetProcessHeap(), 0, dwLength)) && + !RegQueryValueExW (hStrKey, pszValue, 0, &dwType, lpBuff, &dwLength) && + dwType == REG_BINARY) + return IStream_Create(hStrKey, lpBuff, dwLength); + + if (lpBuff) + HeapFree (GetProcessHeap(), 0, lpBuff); + if (hStrKey) + RegCloseKey(hStrKey); + return NULL; +} + +/************************************************************************* + * SHOpenRegStreamA [SHLWAPI.@] + * + * Create a stream to read binary registry data. + * + * PARAMS + * hKey [I] Registry handle + * pszSubkey [I] The sub key name + * pszValue [I] The value name under the sub key + * dwMode [I] STGM mode for opening the file + * + * RETURNS + * Success: An IStream interface referring to the registry data + * Failure: If the registry key could not be opened or is not binary, + * A dummy (empty) IStream object is returned. + */ +IStream * WINAPI SHOpenRegStreamA(HKEY hkey, LPCSTR pszSubkey, + LPCSTR pszValue, DWORD dwMode) +{ + IStream *iStream; + + TRACE("(%p,%s,%s,0x%08lx)\n", hkey, pszSubkey, pszValue, dwMode); + + iStream = SHOpenRegStream2A(hkey, pszSubkey, pszValue, dwMode); + return iStream ? iStream : (IStream *)&rsDummyRegStream; +} + +/************************************************************************* + * SHOpenRegStreamW [SHLWAPI.@] + * + * See SHOpenRegStreamA. + */ +IStream * WINAPI SHOpenRegStreamW(HKEY hkey, LPCWSTR pszSubkey, + LPCWSTR pszValue, DWORD dwMode) +{ + IStream *iStream; + + TRACE("(%p,%s,%s,0x%08lx)\n", hkey, debugstr_w(pszSubkey), + debugstr_w(pszValue), dwMode); + iStream = SHOpenRegStream2W(hkey, pszSubkey, pszValue, dwMode); + return iStream ? iStream : (IStream *)&rsDummyRegStream; +} + +/************************************************************************* + * @ [SHLWAPI.12] + * + * Create an IStream object on a block of memory. + * + * PARAMS + * lpbData [I] Memory block to create the IStream object on + * dwDataLen [I] Length of data block + * + * RETURNS + * Success: A pointer to the IStream object. + * Failure: NULL, if any parameters are invalid or an error occurs. + * + * NOTES + * A copy of the memory pointed to by lpbData is made, and is freed + * when the stream is released. + */ +IStream * WINAPI SHCreateMemStream(LPBYTE lpbData, DWORD dwDataLen) +{ + IStream *iStrmRet = NULL; + + TRACE("(%p,%ld)\n", lpbData, dwDataLen); + + if (lpbData) + { + LPBYTE lpbDup = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwDataLen); + + if (lpbDup) + { + memcpy(lpbDup, lpbData, dwDataLen); + iStrmRet = IStream_Create(NULL, lpbDup, dwDataLen); + + if (!iStrmRet) + HeapFree(GetProcessHeap(), 0, lpbDup); + } + } + return iStrmRet; +} + +/************************************************************************* + * SHCreateStreamWrapper [SHLWAPI.@] + * + * Create an IStream object on a block of memory. + * + * PARAMS + * lpbData [I] Memory block to create the IStream object on + * dwDataLen [I] Length of data block + * dwReserved [I] Reserved, Must be 0. + * lppStream [O] Destination for IStream object + * + * RETURNS + * Success: S_OK. lppStream contains the new IStream object. + * Failure: E_INVALIDARG, if any parameters are invalid, + * E_OUTOFMEMORY if memory allocation fails. + * + * NOTES + * The stream assumes ownership of the memory passed to it. + */ +HRESULT WINAPI SHCreateStreamWrapper(LPBYTE lpbData, DWORD dwDataLen, + DWORD dwReserved, IStream **lppStream) +{ + IStream* lpStream; + + if (lppStream) + *lppStream = NULL; + + if(dwReserved || !lppStream) + return E_INVALIDARG; + + lpStream = IStream_Create(NULL, lpbData, dwDataLen); + + if(!lpStream) + return E_OUTOFMEMORY; + + IStream_QueryInterface(lpStream, &IID_IStream, (void**)lppStream); + IStream_Release(lpStream); + return S_OK; +} diff --git a/reactos/lib/shlwapi/shlwapi.def b/reactos/lib/shlwapi/shlwapi.def new file mode 100644 index 00000000000..3de2b46759f --- /dev/null +++ b/reactos/lib/shlwapi/shlwapi.def @@ -0,0 +1,459 @@ +; File generated automatically from shlwapi/shlwapi.spec; do not edit! + +LIBRARY shlwapi.dll + +EXPORTS + ParseURLA@8 @1 NONAME + ParseURLW@8 @2 NONAME + PathFileExistsDefExtA@8 @3 NONAME + PathFileExistsDefExtW@8 @4 NONAME + PathFindOnPathExA@12 @5 NONAME + PathFindOnPathExW@12 @6 NONAME + SHAllocShared@12 @7 NONAME + SHLockShared@8 @8 NONAME + SHUnlockShared@4 @9 NONAME + SHFreeShared@8 @10 NONAME + SHMapHandle@20 @11 NONAME + SHCreateMemStream@8 @12 NONAME + RegisterDefaultAcceptHeaders@8 @13 NONAME + GetAcceptLanguagesA@8 @14 NONAME + GetAcceptLanguagesW@8 @15 NONAME + SHCreateThread@16 @16 + SHWriteDataBlockList@8 @17 NONAME + SHReadDataBlockList@8 @18 NONAME + SHFreeDataBlockList@4 @19 NONAME + SHAddDataBlock@8 @20 NONAME + SHRemoveDataBlock@8 @21 NONAME + SHFindDataBlock@8 @22 NONAME + SHStringFromGUIDA@12 @23 NONAME + SHStringFromGUIDW@12 @24 NONAME + IsCharAlphaWrapW@4 @25 NONAME + IsCharUpperWrapW@4 @26 NONAME + IsCharLowerWrapW@4 @27 NONAME + IsCharAlphaNumericWrapW@4 @28 NONAME + IsCharSpaceW@4 @29 NONAME + IsCharBlankW@4 @30 NONAME + IsCharPunctW@4 @31 NONAME + IsCharCntrlW@4 @32 NONAME + IsCharDigitW@4 @33 NONAME + IsCharXDigitW@4 @34 NONAME + GetStringType3ExW@12 @35 NONAME + AppendMenuWrapW@16 @36 NONAME + GetDlgItemTextWrapW@16 @74 NONAME + SetDlgItemTextWrapW@12 @138 NONAME + StrCmpNCA@12 @151 NONAME + StrCmpNCW@12 @152 NONAME + StrCmpNICA@12 @153 NONAME + StrCmpNICW@12 @154 NONAME + StrCmpCA@8 @155 NONAME + StrCmpCW@8 @156 NONAME + StrCmpICA@8 @157 NONAME + StrCmpICW@8 @158 NONAME + SHAboutInfoA@8 @160 NONAME + SHAboutInfoW@8 @161 NONAME + SHTruncateString@8 @162 NONAME + IUnknown_QueryStatus@20 @163 NONAME + IUnknown_Exec@24 @164 NONAME + SHSetWindowBits@16 @165 NONAME + SHIsEmptyStream@4 @166 NONAME + SHSetParentHwnd@8 @167 NONAME + ConnectToConnectionPoint@24 @168 NONAME + IUnknown_AtomicRelease@4 @169 NONAME + PathSkipLeadingSlashesA@4 @170 NONAME + SHIsSameObject@8 @171 NONAME + IUnknown_GetWindow@8 @172 NONAME + IUnknown_SetOwner@8 @173 NONAME + IUnknown_SetSite@8 @174 NONAME + IUnknown_GetClassID@8 @175 NONAME + IUnknown_QueryService@16 @176 NONAME + SHLoadMenuPopup@8 @177 NONAME + SHPropagateMessage@20 @178 NONAME + SHRemoveAllSubMenus@4 @180 NONAME + SHEnableMenuItem@12 @181 NONAME + SHCheckMenuItem@12 @182 NONAME + SHRegisterClassA@4 @183 NONAME + SHLoadFromPropertyBag@8 @187 NONAME + IUnknown_OnFocusOCS@8 @189 NONAME + SHGetMenuFromID@8 @192 NONAME + SHGetCurColorRes@0 @193 NONAME + SHFillRectClr@12 @197 NONAME + IUnknown_Set@8 @199 NONAME + MayExecForward@28 @201 NONAME + IsQSForward@12 @202 NONAME + SHStripMneumonicA@4 @203 NONAME + SHIsChildOrSelf@8 @204 NONAME + SHGetValueGoodBootA@24 @205 NONAME + SHGetValueGoodBootW@24 @206 NONAME + FDSA_Initialize@20 @208 NONAME + FDSA_Destroy@4 @209 NONAME + FDSA_InsertItem@12 @210 NONAME + FDSA_DeleteItem@8 @211 NONAME + IStream_Reset@4 @213 NONAME + IStream_Size@8 @214 NONAME + SHAnsiToUnicode@12 @215 NONAME + SHUnicodeToAnsi@12 @217 NONAME + SHUnicodeToAnsiCP@16 @218 NONAME + QISearch@16 @219 NONAME + SHRemoveDefaultDialogFont@4 @221 NONAME + _SHGlobalCounterCreate@4 @222 NONAME + _SHGlobalCounterGetValue@4 @223 NONAME + _SHGlobalCounterIncrement@4 @224 NONAME + SHPinDllOfCLSID@4 @236 NONAME + SHRegisterClassW@4 @237 NONAME + SHUnregisterClassesA@12 @238 NONAME + SHUnregisterClassesW@12 @239 NONAME + SHDefWindowProc@16 @240 NONAME + StopWatchMode@0 @241 NONAME + SHCreateWorkerWindowA@24 @257 NONAME + SHQueueUserWorkItem@28 @260 NONAME + SHRestrictionLookup@16 @266 NONAME + SHWeakQueryInterface@16 @267 NONAME + SHWeakReleaseInterface@8 @268 NONAME + GUIDFromStringA@8 @269 NONAME + GUIDFromStringW@8 @270 NONAME + SHGetRestriction@12 @271 NONAME + WhichPlatform@0 @276 NONAME + SHCreateWorkerWindowW@24 @278 NONAME + SHInvokeDefaultCommand@12 @279 NONAME + SHRegGetIntW@12 @280 NONAME + SHPackDispParamsV@16 @281 NONAME + SHPackDispParams@16 @282 NONAME + IConnectionPoint_SimpleInvoke@12 @284 NONAME + IConnectionPoint_OnChanged@8 @285 NONAME + IUnknown_CPContainerOnChanged@8 @287 NONAME + PlaySoundWrapW@12 @289 NONAME + SHGetIniStringW@20 @294 NONAME + SHSetIniStringW@16 @295 NONAME + ExtTextOutWrapW@32 @299 NONAME + SHGetFileInfoWrapW@20 @313 NONAME + DragQueryFileWrapW@16 @318 NONAME + RegisterMIMETypeForExtensionA@8 @320 NONAME + RegisterMIMETypeForExtensionW@8 @321 NONAME + UnregisterMIMETypeForExtensionA@4 @322 NONAME + UnregisterMIMETypeForExtensionW@4 @323 NONAME + RegisterExtensionForMIMETypeA@8 @324 NONAME + RegisterExtensionForMIMETypeW@8 @325 NONAME + UnregisterExtensionForMIMETypeA@4 @326 NONAME + UnregisterExtensionForMIMETypeW@4 @327 NONAME + GetMIMETypeSubKeyA@12 @328 NONAME + GetMIMETypeSubKeyW@12 @329 NONAME + SHBrowseForFolderWrapW@4 @333 NONAME + SHGetPathFromIDListWrapW@8 @334 NONAME + ShellExecuteExWrapW@4 @335 NONAME + SHFileOperationWrapW@4 @336 NONAME + ExtractIconExWrapW@20 @337 NONAME + SHInterlockedCompareExchange@12 @342 NONAME + SHRegGetCLSIDKeyA@20 @343 NONAME + SHRegGetCLSIDKeyW@20 @344 NONAME + SHUnicodeToUnicode@12 @346 NONAME + GetFileVersionInfoSizeWrapW@8 @350 NONAME + GetFileVersionInfoWrapW@16 @351 NONAME + VerQueryValueWrapW@16 @352 NONAME + IUnknown_EnableModeless@8 @355 NONAME + _CreateAllAccessSecurityAttributes@8 @356 NONAME + SHGetNewLinkInfoWrapW@20 @357 NONAME + SHDefExtractIconWrapW@24 @358 NONAME + SHInvokeCommand@16 @363 NONAME + DoesStringRoundTripA@12 @364 NONAME + ExtractIconWrapW@12 @370 NONAME + MLGetUILanguage@0 @376 NONAME + MLLoadLibraryA@12 @377 + MLLoadLibraryW@12 @378 + GetSaveFileNameWrapW@4 @389 NONAME + WNetRestoreConnectionWrapW@8 @390 NONAME + WNetGetLastErrorWrapW@20 @391 NONAME + StrCpyNXA@12 @399 NONAME + StrCpyNXW@12 @400 NONAME + PageSetupDlgWrapW@4 @401 NONAME + PrintDlgWrapW@4 @402 NONAME + GetOpenFileNameWrapW@4 @403 NONAME + MLBuildResURLA@24 @405 NONAME + MLBuildResURLW@24 @406 NONAME + SHGetMachineInfo@4 @413 NONAME + MLFreeLibrary@4 @418 NONAME + _SHGlobalCounterCreateNamedA@8 @422 NONAME + _SHGlobalCounterCreateNamedW@8 @423 NONAME + _SHGlobalCounterDecrement@4 @424 NONAME + MLSetMLHInstance@8 @430 NONAME + MLClearMLHInstance@4 @431 NONAME + CLSIDFromStringWrap@8 @436 NONAME + IsOS@4 @437 NONAME + SHGetWebFolderFilePathA@12 @440 NONAME + SHGetWebFolderFilePathW@12 @441 NONAME + SHGetShellKey@12 @491 NONAME + IUnknown_OnFocusChangeIS@12 @509 NONAME + SKGetValueW@24 @516 NONAME + AssocCreate@24 @487 + AssocQueryKeyA@20 @501 + AssocQueryKeyW@20 @502 + AssocQueryStringA@24 @503 + AssocQueryStringByKeyA@24 @504 + AssocQueryStringByKeyW@24 @515 + AssocQueryStringW@24 @547 + ChrCmpIA@8 @552 + ChrCmpIW@8 @553 + ColorAdjustLuma@12 @554 + ColorHLSToRGB@12 @555 + ColorRGBToHLS@16 @556 + DllGetVersion@4=SHLWAPI_DllGetVersion@4 @557 + GetMenuPosFromID@8 @558 + HashData@16 @559 + PathAddBackslashA@4 @562 + PathAddBackslashW@4 @563 + PathAddExtensionA@8 @564 + PathAddExtensionW@8 @565 + PathAppendA@8 @566 + PathAppendW@8 @567 + PathBuildRootA@8 @568 + PathBuildRootW@8 @569 + PathCanonicalizeA@8 @570 + PathCanonicalizeW@8 @571 + PathCombineA@12 @572 + PathCombineW@12 @573 + PathCommonPrefixA@12 @574 + PathCommonPrefixW@12 @575 + PathCompactPathA@12 @576 + PathCompactPathExA@16 @577 + PathCompactPathExW@16 @578 + PathCompactPathW@12 @579 + PathCreateFromUrlA@16 @580 + PathCreateFromUrlW@16 @581 + PathFileExistsA@4 @582 + PathFileExistsW@4 @583 + PathFindExtensionA@4 @584 + PathFindExtensionW@4 @585 + PathFindFileNameA@4 @586 + PathFindFileNameW@4 @587 + PathFindNextComponentA@4 @588 + PathFindNextComponentW@4 @589 + PathFindOnPathA@8 @590 + PathFindOnPathW@8 @591 + PathGetArgsA@4 @594 + PathGetArgsW@4 @595 + PathGetCharTypeA@4 @596 + PathGetCharTypeW@4 @597 + PathGetDriveNumberA@4 @598 + PathGetDriveNumberW@4 @599 + PathIsContentTypeA@8 @600 + PathIsContentTypeW@8 @601 + PathIsDirectoryA@4 @602 + PathIsDirectoryW@4 @605 + PathIsFileSpecA@4 @606 + PathIsFileSpecW@4 @607 + PathIsPrefixA@8 @612 + PathIsPrefixW@8 @613 + PathIsRelativeA@4 @614 + PathIsRelativeW@4 @615 + PathIsRootA@4 @616 + PathIsRootW@4 @617 + PathIsSameRootA@8 @618 + PathIsSameRootW@8 @619 + PathIsSystemFolderA@8 @620 + PathIsSystemFolderW@8 @621 + PathIsUNCA@4 @622 + PathIsUNCServerA@4 @623 + PathIsUNCServerShareA@4 @624 + PathIsUNCServerShareW@4 @625 + PathIsUNCServerW@4 @626 + PathIsUNCW@4 @627 + PathIsURLA@4 @628 + PathIsURLW@4 @629 + PathMakePrettyA@4 @630 + PathMakePrettyW@4 @631 + PathMakeSystemFolderA@4 @632 + PathMakeSystemFolderW@4 @633 + PathMatchSpecA@8 @634 + PathMatchSpecW@8 @635 + PathParseIconLocationA@4 @636 + PathParseIconLocationW@4 @637 + PathQuoteSpacesA@4 @638 + PathQuoteSpacesW@4 @639 + PathRelativePathToA@20 @640 + PathRelativePathToW@20 @641 + PathRemoveArgsA@4 @642 + PathRemoveArgsW@4 @643 + PathRemoveBackslashA@4 @644 + PathRemoveBackslashW@4 @645 + PathRemoveBlanksA@4 @646 + PathRemoveBlanksW@4 @647 + PathRemoveExtensionA@4 @648 + PathRemoveExtensionW@4 @649 + PathRemoveFileSpecA@4 @650 + PathRemoveFileSpecW@4 @651 + PathRenameExtensionA@8 @652 + PathRenameExtensionW@8 @653 + PathSearchAndQualifyA@12 @654 + PathSearchAndQualifyW@12 @655 + PathSetDlgItemPathA@12 @656 + PathSetDlgItemPathW@12 @657 + PathSkipRootA@4 @658 + PathSkipRootW@4 @659 + PathStripPathA@4 @660 + PathStripPathW@4 @661 + PathStripToRootA@4 @662 + PathStripToRootW@4 @663 + PathUnmakeSystemFolderA@4 @668 + PathUnmakeSystemFolderW@4 @669 + PathUnquoteSpacesA@4 @670 + PathUnquoteSpacesW@4 @671 + SHCreateShellPalette@4 @675 + SHDeleteEmptyKeyA@8 @680 + SHDeleteEmptyKeyW@8 @681 + SHDeleteKeyA@8 @682 + SHDeleteKeyW@8 @683 + SHDeleteOrphanKeyA@8 @684 + SHDeleteOrphanKeyW@8 @685 + SHDeleteValueA@12 @686 + SHDeleteValueW@12 @687 + SHEnumKeyExA@16 @688 + SHEnumKeyExW@16 @689 + SHEnumValueA@28 @690 + SHEnumValueW@28 @691 + SHGetInverseCMAP@8 @692 + SHGetValueA@24 @694 + SHGetValueW@24 @695 + SHIsLowMemoryMachine@4 @696 + SHOpenRegStreamA@16 @699 + SHOpenRegStreamW@16 @700 + SHOpenRegStream2A@16 @697 + SHOpenRegStream2W@16 @698 + SHQueryInfoKeyA@20 @701 + SHQueryInfoKeyW@20 @702 + SHQueryValueExA@24 @703 + SHQueryValueExW@24 @704 + SHRegCloseUSKey@4 @705 + SHRegEnumUSKeyA@20 @713 + SHRegEnumUSKeyW@20 @714 + SHRegGetBoolUSValueA@16 @717 + SHRegGetBoolUSValueW@16 @718 + SHRegGetUSValueA@32 @721 + SHRegGetUSValueW@32 @722 + SHRegOpenUSKeyA@20 @723 + SHRegOpenUSKeyW@20 @724 + SHRegQueryInfoUSKeyA@24 @725 + SHRegQueryInfoUSKeyW@24 @726 + SHRegQueryUSValueA@32 @727 + SHRegQueryUSValueW@32 @728 + SHRegSetUSValueA@24 @731 + SHRegSetUSValueW@24 @732 + SHRegWriteUSValueA@24 @733 + SHRegWriteUSValueW@24 @734 + SHSetValueA@24 @738 + SHSetValueW@24 @739 + StrCSpnA@8 @743 + StrCSpnIA@8 @744 + StrCSpnIW@8 @745 + StrCSpnW@8 @746 + StrCatBuffA@12 @747 + StrCatBuffW@12 @748 + StrCatW@8 @749 + StrChrA@8 @750 + StrChrIA@8 @751 + StrChrIW@8 @752 + StrChrW@8 @753 + StrCmpIW@8 @754 + StrCmpNA@12 @756 + StrCmpNIA@12 @757 + StrCmpNIW@12 @758 + StrCmpNW@12 @759 + StrCmpW@8 @760 + StrCpyNW@12 @761 + StrCpyW@8 @762 + StrDupA@4 @763 + StrDupW@4 @764 + StrFormatByteSizeA@12 @766 + StrFormatByteSizeW@16 @767 + StrFromTimeIntervalA@16 @770 + StrFromTimeIntervalW@16 @771 + StrIsIntlEqualA@16 @772 + StrIsIntlEqualW@16 @773 + StrNCatA@12 @774 + StrNCatW@12 @775 + StrPBrkA@8 @776 + StrPBrkW@8 @777 + StrRChrA@12 @778 + StrRChrIA@12 @779 + StrRChrIW@12 @780 + StrRChrW@12 @781 + StrRStrIA@12 @782 + StrRStrIW@12 @783 + StrSpnA@8 @788 + StrSpnW@8 @789 + StrStrA@8 @790 + StrStrIA@8 @791 + StrStrIW@8 @792 + StrStrW@8 @793 + StrToIntA@4 @794 + StrToIntExA@12 @795 + StrToIntExW@12 @796 + StrToIntW@4 @797 + StrTrimA@8 @798 + StrTrimW@8 @799 + UrlApplySchemeA@16 @800 + UrlApplySchemeW@16 @801 + UrlCanonicalizeA@16 @802 + UrlCanonicalizeW@16 @803 + UrlCombineA@20 @804 + UrlCombineW@20 @805 + UrlCompareA@12 @806 + UrlCompareW@12 @807 + UrlCreateFromPathA@16 @808 + UrlCreateFromPathW@16 @809 + UrlEscapeA@16 @810 + UrlEscapeW@16 @811 + UrlGetLocationA@4 @812 + UrlGetLocationW@4 @813 + UrlGetPartA@20 @814 + UrlGetPartW@20 @815 + UrlHashA@12 @816 + UrlHashW@12 @817 + UrlIsA@8 @818 + UrlIsNoHistoryA@4 @819 + UrlIsNoHistoryW@4 @820 + UrlIsOpaqueA@4 @821 + UrlIsOpaqueW@4 @822 + UrlIsW@8 @823 + UrlUnescapeA@16 @824 + UrlUnescapeW@16 @825 + wnsprintfA @827 + wnsprintfW @828 + wvnsprintfA@16 @829 + wvnsprintfW@16 @830 + AssocIsDangerous@4 @500 + StrRetToBufA@16 @784 + StrRetToBufW@16 @785 + StrRetToStrA@12 @786 + StrRetToStrW@12 @787 + SHRegGetPathA@20 @719 + SHRegGetPathW@20 @720 + PathIsDirectoryEmptyA@4 @603 + PathIsDirectoryEmptyW@4 @604 + PathIsNetworkPathA@4 @610 + PathIsNetworkPathW@4 @611 + PathIsLFNFileSpecA@4 @608 + PathIsLFNFileSpecW@4 @609 + PathFindSuffixArrayA@12 @592 + PathFindSuffixArrayW@12 @593 + _SHGetInstanceExplorer@4 @826 + PathUndecorateA@4 @666 + PathUndecorateW@4 @667 + SHCopyKeyA@16 @673 + SHCopyKeyW@16 @674 + SHAutoComplete@8 @672 + SHCreateStreamOnFileA@12 @676 + SHCreateStreamOnFileW@12 @678 + SHCreateStreamOnFileEx@24 @677 + SHCreateStreamWrapper@16 @679 + SHGetThreadRef@4 @693 + SHRegDuplicateHKey@4 @712 + SHRegSetPathA@20 @729 + SHRegSetPathW@20 @730 + SHSetThreadRef@4 @737 + SHReleaseThreadRef@0 @736 + SHSkipJunction@8 @740 + SHStrDupA@8 @741 + SHStrDupW@8 @742 + StrFormatByteSize64A@16 @765 + StrFormatKBSizeA@16 @768 + StrFormatKBSizeW@16 @769 + StrCmpLogicalW@8 @755 diff --git a/reactos/lib/shlwapi/shlwapi_main.c b/reactos/lib/shlwapi/shlwapi_main.c new file mode 100644 index 00000000000..5d68bb71971 --- /dev/null +++ b/reactos/lib/shlwapi/shlwapi_main.c @@ -0,0 +1,130 @@ +/* + * SHLWAPI initialisation + * + * Copyright 1998 Marcus Meissner + * Copyright 1998 Juergen Schmied (jsch) + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#define NO_SHLWAPI_REG +#define NO_SHLWAPI_STREAM +#include "shlwapi.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +HINSTANCE shlwapi_hInstance = 0; +HMODULE SHLWAPI_hshell32 = 0; +HMODULE SHLWAPI_hwinmm = 0; +HMODULE SHLWAPI_hcomdlg32 = 0; +HMODULE SHLWAPI_hcomctl32 = 0; +HMODULE SHLWAPI_hmpr = 0; +HMODULE SHLWAPI_hmlang = 0; +HMODULE SHLWAPI_hurlmon = 0; +HMODULE SHLWAPI_hversion = 0; + +DWORD SHLWAPI_ThreadRef_index = TLS_OUT_OF_INDEXES; + +/************************************************************************* + * SHLWAPI {SHLWAPI} + * + * The Shell Light-Weight Api dll provides a large number of utility functions + * which are commonly required by Win32 programs. Originally distributed with + * Internet Explorer as a free download, it became a core part of Windows when + * Internet Explorer was 'integrated' into the O/S with the release of Win98. + * + * All functions exported by ordinal are undocumented by MS. The vast majority + * of these are wrappers for Unicode functions that may not exist on early 16 + * bit platforms. The remainder perform various small tasks and presumably were + * added to facilitate code reuse amongst the MS developers. + */ + +/************************************************************************* + * SHLWAPI DllMain + * + * NOTES + * calling oleinitialize here breaks sone apps. + */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) +{ + TRACE("%p 0x%lx %p\n", hinstDLL, fdwReason, fImpLoad); + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + shlwapi_hInstance = hinstDLL; + SHLWAPI_ThreadRef_index = TlsAlloc(); + break; + case DLL_PROCESS_DETACH: + if (SHLWAPI_hshell32) FreeLibrary(SHLWAPI_hshell32); + if (SHLWAPI_hwinmm) FreeLibrary(SHLWAPI_hwinmm); + if (SHLWAPI_hcomdlg32) FreeLibrary(SHLWAPI_hcomdlg32); + if (SHLWAPI_hcomctl32) FreeLibrary(SHLWAPI_hcomctl32); + if (SHLWAPI_hmpr) FreeLibrary(SHLWAPI_hmpr); + if (SHLWAPI_hmlang) FreeLibrary(SHLWAPI_hmlang); + if (SHLWAPI_hurlmon) FreeLibrary(SHLWAPI_hurlmon); + if (SHLWAPI_hversion) FreeLibrary(SHLWAPI_hversion); + if (SHLWAPI_ThreadRef_index != TLS_OUT_OF_INDEXES) TlsFree(SHLWAPI_ThreadRef_index); + break; + } + return TRUE; +} + +/*********************************************************************** + * DllGetVersion [SHLWAPI.@] + * + * Retrieve "shlwapi.dll" version information. + * + * PARAMS + * pdvi [O] pointer to version information structure. + * + * RETURNS + * Success: S_OK. pdvi is updated with the version information + * Failure: E_INVALIDARG, if pdvi->cbSize is not set correctly. + * + * NOTES + * You may pass either a DLLVERSIONINFO of DLLVERSIONINFO2 structure + * as pdvi, provided that the size is set correctly. + * Returns version as shlwapi.dll from IE5.01. + */ +HRESULT WINAPI SHLWAPI_DllGetVersion (DLLVERSIONINFO *pdvi) +{ + DLLVERSIONINFO2 *pdvi2 = (DLLVERSIONINFO2*)pdvi; + + TRACE("(%p)\n",pdvi); + + switch (pdvi2->info1.cbSize) + { + case sizeof(DLLVERSIONINFO2): + pdvi2->dwFlags = 0; + pdvi2->ullVersion = MAKEDLLVERULL(5, 0, 2314, 0); + /* Fall through */ + case sizeof(DLLVERSIONINFO): + pdvi2->info1.dwMajorVersion = 5; + pdvi2->info1.dwMinorVersion = 0; + pdvi2->info1.dwBuildNumber = 2314; + pdvi2->info1.dwPlatformID = 1000; + return S_OK; + } + if (pdvi) + WARN("pdvi->cbSize = %ld, unhandled\n", pdvi2->info1.cbSize); + return E_INVALIDARG; +} diff --git a/reactos/lib/shlwapi/string.c b/reactos/lib/shlwapi/string.c new file mode 100644 index 00000000000..ed739fea73a --- /dev/null +++ b/reactos/lib/shlwapi/string.c @@ -0,0 +1,2382 @@ +/* + * 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 +#include +#include +#include + +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "windef.h" +#include "winbase.h" +#define NO_SHLWAPI_REG +#define NO_SHLWAPI_STREAM +#include "shlwapi.h" +#include "wingdi.h" +#include "winuser.h" +#include "shlobj.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +static HRESULT WINAPI _SHStrDupAA(LPCSTR,LPSTR*); +static HRESULT WINAPI _SHStrDupAW(LPCWSTR,LPSTR*); + +/************************************************************************* + * 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 + */ +#ifndef __REACTOS__ +static LPWSTR WINAPI SHLWAPI_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch, + int (*pStrCmpFn)(LPCWSTR,LPCWSTR,int)) +#else +static LPWSTR WINAPI SHLWAPI_StrStrHelperW(LPCWSTR lpszStr, LPCWSTR lpszSearch, + int (*pStrCmpFn)(const wchar_t*,const wchar_t*,size_t)) +#endif +{ + 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)); + +#ifdef __REACTOS__ + return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, wcsncmp); +#else + return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpW); +#endif +} + +/************************************************************************* + * 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)); + +#ifdef __REACTOS__ + return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, wcsnicmp); +#else + return SHLWAPI_StrStrHelperW(lpszStr, lpszSearch, strncmpiW); +#endif +} + +/************************************************************************* + * 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 determines 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. + * + * PARAMS + * lpStrRet [O] STRRET to convert + * pidl [I] ITEMIDLIST for lpStrRet->uType = STRRET_OFFSETA + * ppszName [O] Destination for converted string + * + * RETURNS + * Success: S_OK. ppszName contains the new string, allocated with CoTaskMemAlloc(). + * Failure: E_FAIL, if any parameters are invalid. + */ +HRESULT WINAPI StrRetToStrA(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPSTR *ppszName) +{ + HRESULT hRet = E_FAIL; + + switch (lpStrRet->uType) + { + case STRRET_WSTR: + hRet = _SHStrDupAW(lpStrRet->u.pOleStr, ppszName); + CoTaskMemFree(lpStrRet->u.pOleStr); + break; + + case STRRET_CSTR: + hRet = _SHStrDupAA(lpStrRet->u.cStr, ppszName); + break; + + case STRRET_OFFSET: + hRet = _SHStrDupAA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName); + break; + + default: + *ppszName = NULL; + } + + return hRet; +} + +/************************************************************************* + * StrRetToStrW [SHLWAPI.@] + * + * See StrRetToStrA. + */ +HRESULT WINAPI StrRetToStrW(LPSTRRET lpStrRet, const ITEMIDLIST *pidl, LPWSTR *ppszName) +{ + HRESULT hRet = E_FAIL; + + switch (lpStrRet->uType) + { + case STRRET_WSTR: + hRet = SHStrDupW(lpStrRet->u.pOleStr, ppszName); + CoTaskMemFree(lpStrRet->u.pOleStr); + break; + + case STRRET_CSTR: + hRet = SHStrDupA(lpStrRet->u.cStr, ppszName); + break; + + case STRRET_OFFSET: + hRet = SHStrDupA(((LPCSTR)&pidl->mkid) + lpStrRet->u.uOffset, ppszName); + break; + + default: + *ppszName = NULL; + } + + return hRet; +} + +/************************************************************************* + * 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)/sizeof(WCHAR) - 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 determines 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; +} + +/************************************************************************* + * _SHStrDupAA [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 [SHLWAPI.@] + * + * 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 lpszStr, LPWSTR * lppszDest) +{ + HRESULT hRet; + int len = 0; + + if (lpszStr) + { + len = MultiByteToWideChar(0, 0, lpszStr, -1, 0, 0) * sizeof(WCHAR); + *lppszDest = CoTaskMemAlloc(len); + } + else + *lppszDest = NULL; + + if (*lppszDest) + { + MultiByteToWideChar(0, 0, lpszStr, -1, *lppszDest, len); + hRet = S_OK; + } + else + hRet = E_OUTOFMEMORY; + + TRACE("%s->(%p)\n", debugstr_a(lpszStr), *lppszDest); + return hRet; +} + +/************************************************************************* + * _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 [SHLWAPI.@] + * + * 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 WINAPI 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 StrCpyNXA(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 StrCpyNXA. + */ +LPWSTR WINAPI StrCpyNXW(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"; + +#define KB ((ULONGLONG)1024) +#define MB (KB*KB) +#define GB (KB*KB*KB) +#define TB (KB*KB*KB*KB) +#define PB (KB*KB*KB*KB*KB) + + static const SHLWAPI_BYTEFORMATS bfFormats[] = + { + { 10*KB, 10.24, 100.0, sz3_2, 'K' }, /* 10 KB */ + { 100*KB, 102.4, 10.0, sz3_1, 'K' }, /* 100 KB */ + { 1000*KB, 1024.0, 1.0, sz3_0, 'K' }, /* 1000 KB */ + { 10*MB, 10485.76, 100.0, sz3_2, 'M' }, /* 10 MB */ + { 100*MB, 104857.6, 10.0, sz3_1, 'M' }, /* 100 MB */ + { 1000*MB, 1048576.0, 1.0, sz3_0, 'M' }, /* 1000 MB */ + { 10*GB, 10737418.24, 100.0, sz3_2, 'G' }, /* 10 GB */ + { 100*GB, 107374182.4, 10.0, sz3_1, 'G' }, /* 100 GB */ + { 1000*GB, 1073741824.0, 1.0, sz3_0, 'G' }, /* 1000 GB */ + { 10*TB, 10485.76, 100.0, sz3_2, 'T' }, /* 10 TB */ + { 100*TB, 104857.6, 10.0, sz3_1, 'T' }, /* 100 TB */ + { 1000*TB, 1048576.0, 1.0, sz3_0, 'T' }, /* 1000 TB */ + { 10*PB, 10737418.24, 100.00, sz3_2, 'P' }, /* 10 PB */ + { 100*PB, 107374182.4, 10.00, sz3_1, 'P' }, /* 100 PB */ + { 1000*PB, 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); +} + +/************************************************************************* + * @ [SHLWAPI.203] + * + * Remove a single non-trailing ampersand ('&') from a string. + * + * PARAMS + * lpszStr [I/O] String to remove ampersand from. + * + * RETURNS + * The character after the first ampersand in lpszStr, or the first character + * in lpszStr if there is no ampersand in the string. + */ +char WINAPI SHStripMneumonicA(LPCSTR lpszStr) +{ + LPSTR lpszIter, lpszTmp; + char ch; + + TRACE("(%s)\n", debugstr_a(lpszStr)); + + ch = *lpszStr; + + if ((lpszIter = StrChrA(lpszStr, '&'))) + { + lpszTmp = CharNextA(lpszIter); + if (lpszTmp && *lpszTmp) + { + if (*lpszTmp != '&') + ch = *lpszTmp; + + while (lpszIter && *lpszIter) + { + lpszTmp = CharNextA(lpszIter); + *lpszIter = *lpszTmp; + lpszIter = lpszTmp; + } + } + } + + return ch; +} diff --git a/reactos/lib/shlwapi/thread.c b/reactos/lib/shlwapi/thread.c new file mode 100644 index 00000000000..ef090758d6f --- /dev/null +++ b/reactos/lib/shlwapi/thread.c @@ -0,0 +1,480 @@ +/* + * SHLWAPI thread and MT synchronisation functions + * + * Copyright 2002 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 + */ +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "wine/debug.h" +#define NO_SHLWAPI_REG +#define NO_SHLWAPI_PATH +#define NO_SHLWAPI_GDI +#define NO_SHLWAPI_STREAM +#define NO_SHLWAPI_USER +#include "shlwapi.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* Get a function pointer from a DLL handle */ +#define GET_FUNC(func, module, name, fail) \ + do { \ + if (!func) { \ + if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \ + if (!(func = (void*)GetProcAddress(SHLWAPI_h##module, name))) return fail; \ + } \ + } while (0) + +/* DLL handles for late bound calls */ +extern HMODULE SHLWAPI_hshell32; + +/* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */ +static HRESULT (WINAPI *pSHGetInstanceExplorer)(IUnknown**); + +extern DWORD SHLWAPI_ThreadRef_index; /* Initialised in shlwapi_main.c */ + +DWORD WINAPI SHStringFromGUIDA(REFGUID,LPSTR,INT); + +/************************************************************************** + * _CreateAllAccessSecurityAttributes [SHLWAPI.356] + * + * Initialise security attributes from a security descriptor. + * + * PARAMS + * lpAttr [O] Security attributes + * lpSec [I] Security descriptor + * + * RETURNS + * Success: lpAttr, initialised using lpSec. + * Failure: NULL, if any parameters are invalid. + * + * NOTES + * This function always returns NULL if the underlying OS version + * Wine is impersonating does not use security descriptors (i.e. anything + * before Windows NT). + */ +LPSECURITY_ATTRIBUTES WINAPI _CreateAllAccessSecurityAttributes( + LPSECURITY_ATTRIBUTES lpAttr, + PSECURITY_DESCRIPTOR lpSec) +{ + /* This function is used within SHLWAPI only to create security attributes + * for shell semaphores. */ + + TRACE("(%p,%p)\n", lpAttr, lpSec); + + if (!(GetVersion() & 0x80000000)) /* NT */ + { + if (!lpSec || !lpAttr) + return NULL; + + if (InitializeSecurityDescriptor(lpSec, 1)) + { + if (SetSecurityDescriptorDacl(lpSec, TRUE, NULL, FALSE)) + { + lpAttr->nLength = sizeof(SECURITY_ATTRIBUTES); + lpAttr->lpSecurityDescriptor = lpSec; + lpAttr->bInheritHandle = FALSE; + return lpAttr; + } + } + } + return NULL; +} + +/************************************************************************* + * _SHGetInstanceExplorer [SHLWAPI.@] + * + * Get an interface to the shell explorer. + * + * PARAMS + * lppUnknown [O] Destination for explorers IUnknown interface. + * + * RETURNS + * Success: S_OK. lppUnknown contains the explorer interface. + * Failure: An HRESULT error code. + */ +HRESULT WINAPI _SHGetInstanceExplorer(IUnknown **lppUnknown) +{ + /* This function is used within SHLWAPI only to hold the IE reference + * for threads created with the CTF_PROCESS_REF flag set. */ + + GET_FUNC(pSHGetInstanceExplorer, shell32, "SHGetInstanceExplorer", E_FAIL); + return pSHGetInstanceExplorer(lppUnknown); +} + +/* Internal thread information structure */ +typedef struct tagSHLWAPI_THREAD_INFO +{ + LPTHREAD_START_ROUTINE pfnThreadProc; /* Thread start */ + LPTHREAD_START_ROUTINE pfnCallback; /* Thread initialisation */ + PVOID pData; /* Application specific data */ + BOOL bInitCom; /* Initialise COM for the thread? */ + HANDLE hEvent; /* Signal for creator to continue */ + IUnknown *refThread; /* Reference to thread creator */ + IUnknown *refIE; /* Reference to the IE process */ +} SHLWAPI_THREAD_INFO, *LPSHLWAPI_THREAD_INFO; + + +/************************************************************************* + * SHGetThreadRef [SHLWAPI.@] + * + * Get a per-thread object reference set by SHSetThreadRef(). + * + * PARAMS + * lppUnknown [O] Destination to receive object reference + * + * RETURNS + * Success: S_OK. lppUnknown is set to the object reference. + * Failure: E_NOINTERFACE, if an error occurs or lppUnknown is NULL. + */ +HRESULT WINAPI SHGetThreadRef(IUnknown **lppUnknown) +{ + TRACE("(%p)\n", lppUnknown); + + if (!lppUnknown || SHLWAPI_ThreadRef_index == TLS_OUT_OF_INDEXES) + return E_NOINTERFACE; + + *lppUnknown = (IUnknown*)TlsGetValue(SHLWAPI_ThreadRef_index); + if (!*lppUnknown) + return E_NOINTERFACE; + + /* Add a reference. Caller will Release() us when finished */ + IUnknown_AddRef(*lppUnknown); + return S_OK; +} + +/************************************************************************* + * SHSetThreadRef [SHLWAPI.@] + * + * Store a per-thread object reference. + * + * PARAMS + * lpUnknown [I] Object reference to store + * + * RETURNS + * Success: S_OK. lpUnknown is stored and can be retrieved by SHGetThreadRef() + * Failure: E_NOINTERFACE, if an error occurs or lpUnknown is NULL. + */ +HRESULT WINAPI SHSetThreadRef(IUnknown *lpUnknown) +{ + TRACE("(%p)\n", lpUnknown); + + if (!lpUnknown || SHLWAPI_ThreadRef_index == 0xffffffff) + return E_NOINTERFACE; + + TlsSetValue(SHLWAPI_ThreadRef_index, lpUnknown); + return S_OK; +} + +/************************************************************************* + * SHReleaseThreadRef [SHLWAPI.@] + * + * Release a per-thread object reference. + * + * PARAMS + * None. + * + * RETURNS + * Success: S_OK. The threads object reference is released. + * Failure: An HRESULT error code. + */ +HRESULT WINAPI SHReleaseThreadRef() +{ + FIXME("() - stub!\n"); + return S_OK; +} + +/************************************************************************* + * SHLWAPI_ThreadWrapper + * + * Internal wrapper for executing user thread functions from SHCreateThread. + */ +static DWORD WINAPI SHLWAPI_ThreadWrapper(PVOID pTi) +{ + SHLWAPI_THREAD_INFO ti; + HRESULT hCom = E_FAIL; + DWORD dwRet; + + TRACE("(%p)", pTi); + + /* We are now executing in the context of the newly created thread. + * So we copy the data passed to us (it is on the stack of the function + * that called us, which is waiting for us to signal an event before + * returning). */ + memcpy(&ti, pTi, sizeof(SHLWAPI_THREAD_INFO)); + + /* Initialise COM for the thread, if desired */ + if (ti.bInitCom) + { + hCom = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE); + + if (FAILED(hCom)) + hCom = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE); + } + + /* Execute the callback function before returning */ + if (ti.pfnCallback) + ti.pfnCallback(ti.pData); + + /* Signal the thread that created us; it can return now */ + SetEvent(ti.hEvent); + + /* Execute the callers start code */ + dwRet = ti.pfnThreadProc(ti.pData); + + /* Release references to the caller and IE process, if held */ + if (ti.refThread) + IUnknown_Release(ti.refThread); + + if (ti.refIE) + IUnknown_Release(ti.refIE); + + if (SUCCEEDED(hCom)) + CoUninitialize(); + + /* Return the users thread return value */ + return dwRet; +} + +/************************************************************************* + * SHCreateThread [SHLWAPI.16] + * + * Create a new thread. + * + * PARAMS + * pfnThreadProc [I] Function to execute in new thread + * pData [I] Application specific data passed to pfnThreadProc + * dwFlags [I] CTF_ flags from "shlwapi.h" + * pfnCallback [I] Function to execute before pfnThreadProc + * + * RETURNS + * Success: TRUE. pfnThreadProc was executed. + * Failure: FALSE. pfnThreadProc was not executed. + * + * NOTES + * If the thread cannot be created, pfnCallback is NULL, and dwFlags + * has bit CTF_INSIST set, pfnThreadProc will be executed synchronously. + */ +BOOL WINAPI SHCreateThread(LPTHREAD_START_ROUTINE pfnThreadProc, VOID *pData, + DWORD dwFlags, LPTHREAD_START_ROUTINE pfnCallback) +{ + SHLWAPI_THREAD_INFO ti; + BOOL bCalled = FALSE; + + TRACE("(%p,%p,0x%lX,%p)\n", pfnThreadProc, pData, dwFlags, pfnCallback); + + /* Set up data to pass to the new thread (On our stack) */ + ti.pfnThreadProc = pfnThreadProc; + ti.pfnCallback = pfnCallback; + ti.pData = pData; + ti.bInitCom = dwFlags & CTF_COINIT ? TRUE : FALSE; + ti.hEvent = CreateEventA(NULL,FALSE,FALSE,NULL); + + /* Hold references to the current thread and IE process, if desired */ + if(dwFlags & CTF_THREAD_REF) + SHGetThreadRef(&ti.refThread); + else + ti.refThread = NULL; + + if(dwFlags & CTF_PROCESS_REF) + _SHGetInstanceExplorer(&ti.refIE); + else + ti.refIE = NULL; + + /* Create the thread */ + if(ti.hEvent) + { + DWORD dwRetVal; + HANDLE hThread; + + hThread = CreateThread(NULL, 0, SHLWAPI_ThreadWrapper, &ti, 0, &dwRetVal); + + if(hThread) + { + /* Wait for the thread to signal us to continue */ + WaitForSingleObject(ti.hEvent, -1); + CloseHandle(hThread); + bCalled = TRUE; + } + CloseHandle(ti.hEvent); + } + + if (!bCalled) + { + if (!ti.pfnCallback && dwFlags & CTF_INSIST) + { + /* Couldn't call, call synchronously */ + pfnThreadProc(pData); + bCalled = TRUE; + } + else + { + /* Free references, since thread hasn't run to do so */ + if(ti.refThread) + IUnknown_Release(ti.refThread); + + if(ti.refIE) + IUnknown_Release(ti.refIE); + } + } + return bCalled; +} + +/************************************************************************* + * _SHGlobalCounterGetValue [SHLWAPI.223] + * + * Get the current count of a semaphore. + * + * PARAMS + * hSem [I] Semaphore handle + * + * RETURNS + * The current count of the semaphore. + */ +LONG WINAPI _SHGlobalCounterGetValue(HANDLE hSem) +{ + LONG dwOldCount = 0; + + TRACE("(%p)\n", hSem); + ReleaseSemaphore(hSem, 1, &dwOldCount); /* +1 */ + WaitForSingleObject(hSem, 0); /* -1 */ + return dwOldCount; +} + +/************************************************************************* + * _SHGlobalCounterIncrement [SHLWAPI.224] + * + * Claim a semaphore. + * + * PARAMS + * hSem [I] Semaphore handle + * + * RETURNS + * The new count of the semaphore. + */ +LONG WINAPI _SHGlobalCounterIncrement(HANDLE hSem) +{ + LONG dwOldCount = 0; + + TRACE("(%p)\n", hSem); + ReleaseSemaphore(hSem, 1, &dwOldCount); + return dwOldCount + 1; +} + +/************************************************************************* + * _SHGlobalCounterDecrement [SHLWAPI.424] + * + * Release a semaphore. + * + * PARAMS + * hSem [I] Semaphore handle + * + * RETURNS + * The new count of the semaphore. + */ +DWORD WINAPI _SHGlobalCounterDecrement(HANDLE hSem) +{ + DWORD dwOldCount = 0; + + TRACE("(%p)\n", hSem); + + dwOldCount = _SHGlobalCounterGetValue(hSem); + WaitForSingleObject(hSem, 0); + return dwOldCount - 1; +} + +/************************************************************************* + * _SHGlobalCounterCreateNamedW [SHLWAPI.423] + * + * Unicode version of _SHGlobalCounterCreateNamedA. + */ +HANDLE WINAPI _SHGlobalCounterCreateNamedW(LPCWSTR lpszName, DWORD iInitial) +{ + static const WCHAR szPrefix[] = { 's', 'h', 'e', 'l', 'l', '.', '\0' }; + const int iPrefixLen = 6; + WCHAR szBuff[MAX_PATH]; + const int iBuffLen = sizeof(szBuff)/sizeof(WCHAR); + SECURITY_DESCRIPTOR sd; + SECURITY_ATTRIBUTES sAttr, *pSecAttr; + HANDLE hRet; + + TRACE("(%s,%ld)\n", debugstr_w(lpszName), iInitial); + + /* Create Semaphore name */ + memcpy(szBuff, szPrefix, (iPrefixLen + 1) * sizeof(WCHAR)); + if (lpszName) + StrCpyNW(szBuff + iPrefixLen, lpszName, iBuffLen - iPrefixLen); + + /* Initialise security attributes */ + pSecAttr = _CreateAllAccessSecurityAttributes(&sAttr, &sd); + + if (!(hRet = CreateSemaphoreW(pSecAttr , iInitial, MAXLONG, szBuff))) + hRet = OpenSemaphoreW(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE, 0, szBuff); + return hRet; +} + +/************************************************************************* + * _SHGlobalCounterCreateNamedA [SHLWAPI.422] + * + * Create a semaphore. + * + * PARAMS + * lpszName [I] Name of semaphore + * iInitial [I] Initial count for semaphore + * + * RETURNS + * A new semaphore handle. + */ +HANDLE WINAPI _SHGlobalCounterCreateNamedA(LPCSTR lpszName, DWORD iInitial) +{ + WCHAR szBuff[MAX_PATH]; + + TRACE("(%s,%ld)\n", debugstr_a(lpszName), iInitial); + + if (lpszName) + MultiByteToWideChar(0, 0, lpszName, -1, szBuff, MAX_PATH); + return _SHGlobalCounterCreateNamedW(lpszName ? szBuff : NULL, iInitial); +} + +/************************************************************************* + * _SHGlobalCounterCreate [SHLWAPI.222] + * + * Create a semaphore using the name of a GUID. + * + * PARAMS + * guid [I] GUID to use as semaphore name + * + * RETURNS + * A handle to the semaphore. + * + * NOTES + * The initial count of the semaphore is set to 0. + */ +HANDLE WINAPI _SHGlobalCounterCreate (REFGUID guid) +{ + char szName[40]; + + TRACE("(%s)\n", debugstr_guid(guid)); + + /* Create a named semaphore using the GUID string */ + SHStringFromGUIDA(guid, szName, sizeof(szName) - 1); + return _SHGlobalCounterCreateNamedA(szName, 0); +} diff --git a/reactos/lib/shlwapi/url.c b/reactos/lib/shlwapi/url.c new file mode 100644 index 00000000000..bddcba9f162 --- /dev/null +++ b/reactos/lib/shlwapi/url.c @@ -0,0 +1,2469 @@ +/* + * Url functions + * + * Copyright 2000 Huw D M Davies for CodeWeavers. + * + * 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 + */ + +#ifdef __REACTOS__ +#include "wine/icom.h" +#endif +#include "config.h" +#include "wine/port.h" +#include +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winerror.h" +#include "wine/unicode.h" +#include "wininet.h" +#include "winreg.h" +#define NO_SHLWAPI_STREAM +#include "shlwapi.h" +#include "wine/debug.h" + +HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD); +BOOL WINAPI MLFreeLibrary(HMODULE); +HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD); + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +/* The following schemes were identified in the native version of + * SHLWAPI.DLL version 5.50 + */ +typedef enum { + URL_SCHEME_INVALID = -1, + URL_SCHEME_UNKNOWN = 0, + URL_SCHEME_FTP, + URL_SCHEME_HTTP, + URL_SCHEME_GOPHER, + URL_SCHEME_MAILTO, + URL_SCHEME_NEWS, + URL_SCHEME_NNTP, + URL_SCHEME_TELNET, + URL_SCHEME_WAIS, + URL_SCHEME_FILE, + URL_SCHEME_MK, + URL_SCHEME_HTTPS, + URL_SCHEME_SHELL, + URL_SCHEME_SNEWS, + URL_SCHEME_LOCAL, + URL_SCHEME_JAVASCRIPT, + URL_SCHEME_VBSCRIPT, + URL_SCHEME_ABOUT, + URL_SCHEME_RES, + URL_SCHEME_MAXVALUE +} URL_SCHEME; + +typedef struct { + URL_SCHEME scheme_number; + LPCSTR scheme_name; +} SHL_2_inet_scheme; + +static const SHL_2_inet_scheme shlwapi_schemes[] = { + {URL_SCHEME_FTP, "ftp"}, + {URL_SCHEME_HTTP, "http"}, + {URL_SCHEME_GOPHER, "gopher"}, + {URL_SCHEME_MAILTO, "mailto"}, + {URL_SCHEME_NEWS, "news"}, + {URL_SCHEME_NNTP, "nntp"}, + {URL_SCHEME_TELNET, "telnet"}, + {URL_SCHEME_WAIS, "wais"}, + {URL_SCHEME_FILE, "file"}, + {URL_SCHEME_MK, "mk"}, + {URL_SCHEME_HTTPS, "https"}, + {URL_SCHEME_SHELL, "shell"}, + {URL_SCHEME_SNEWS, "snews"}, + {URL_SCHEME_LOCAL, "local"}, + {URL_SCHEME_JAVASCRIPT, "javascript"}, + {URL_SCHEME_VBSCRIPT, "vbscript"}, + {URL_SCHEME_ABOUT, "about"}, + {URL_SCHEME_RES, "res"}, + {0, 0} +}; + +typedef struct { + LPCWSTR pScheme; /* [out] start of scheme */ + DWORD szScheme; /* [out] size of scheme (until colon) */ + LPCWSTR pUserName; /* [out] start of Username */ + DWORD szUserName; /* [out] size of Username (until ":" or "@") */ + LPCWSTR pPassword; /* [out] start of Password */ + DWORD szPassword; /* [out] size of Password (until "@") */ + LPCWSTR pHostName; /* [out] start of Hostname */ + DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */ + LPCWSTR pPort; /* [out] start of Port */ + DWORD szPort; /* [out] size of Port (until "/" or eos) */ + LPCWSTR pQuery; /* [out] start of Query */ + DWORD szQuery; /* [out] size of Query (until eos) */ +} WINE_PARSE_URL; + +typedef enum { + SCHEME, + HOST, + PORT, + USERPASS, +} WINE_URL_SCAN_TYPE; + +typedef struct { + INT size; /* [in] (always 0x18) */ + LPCSTR ap1; /* [out] start of scheme */ + INT sizep1; /* [out] size of scheme (until colon) */ + LPCSTR ap2; /* [out] pointer following first colon */ + INT sizep2; /* [out] size of remainder */ + INT fcncde; /* [out] function match of p1 (0 if unknown) */ +} UNKNOWN_SHLWAPI_1; + +typedef struct { + INT size; /* [in] (always 0x18) */ + LPCWSTR ap1; /* [out] start of scheme */ + INT sizep1; /* [out] size of scheme (until colon) */ + LPCWSTR ap2; /* [out] pointer following first colon */ + INT sizep2; /* [out] size of remainder */ + INT fcncde; /* [out] function match of p1 (0 if unknown) */ +} UNKNOWN_SHLWAPI_2; + +static const CHAR hexDigits[] = "0123456789ABCDEF"; + +static const WCHAR fileW[] = {'f','i','l','e','\0'}; + +static const unsigned char HashDataLookup[256] = { + 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B, + 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07, + 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, + 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46, + 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0, + 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, + 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F, + 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9, + 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF, + 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40, + 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, + 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4, + 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB, + 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, + 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D, + 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47, + 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD, + 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17, + 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, + 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 }; + +static inline BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags) +{ + + if (isalnum(ch)) + return FALSE; + + if(dwFlags & URL_ESCAPE_SPACES_ONLY) { + if(ch == ' ') + return TRUE; + else + return FALSE; + } + + if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%')) + return TRUE; + + if (ch <= 31 || ch >= 127) + return TRUE; + + else { + switch (ch) { + case ' ': + case '<': + case '>': + case '\"': + case '{': + case '}': + case '|': +/* case '\\': */ + case '^': + case ']': + case '[': + case '`': + case '&': + return TRUE; + + case '/': + case '?': + if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE; + default: + return FALSE; + } + } +} + +static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags) +{ + + if (isalnumW(ch)) + return FALSE; + + if(dwFlags & URL_ESCAPE_SPACES_ONLY) { + if(ch == L' ') + return TRUE; + else + return FALSE; + } + + if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%')) + return TRUE; + + if (ch <= 31 || ch >= 127) + return TRUE; + + else { + switch (ch) { + case L' ': + case L'<': + case L'>': + case L'\"': + case L'{': + case L'}': + case L'|': + case L'\\': + case L'^': + case L']': + case L'[': + case L'`': + case L'&': + return TRUE; + + case L'/': + case L'?': + if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE; + default: + return FALSE; + } + } +} + +static BOOL URL_JustLocation(LPCWSTR str) +{ + while(*str && (*str == L'/')) str++; + if (*str) { + while (*str && ((*str == L'-') || + (*str == L'.') || + isalnumW(*str))) str++; + if (*str == L'/') return FALSE; + } + return TRUE; +} + + +/************************************************************************* + * @ [SHLWAPI.1] + * + * Parse a Url into its constituent parts. + * + * PARAMS + * x [I] Url to parse + * y [O] Undocumented structure holding the parsed information + * + * RETURNS + * Success: S_OK. y contains the parsed Url details. + * Failure: An HRESULT error code. + */ +DWORD WINAPI ParseURLA(LPCSTR x, UNKNOWN_SHLWAPI_1 *y) +{ + DWORD cnt; + const SHL_2_inet_scheme *inet_pro; + + y->fcncde = URL_SCHEME_INVALID; + if (y->size != 0x18) return E_INVALIDARG; + /* FIXME: leading white space generates error of 0x80041001 which + * is undefined + */ + if (*x <= ' ') return 0x80041001; + cnt = 0; + y->sizep1 = 0; + y->ap1 = x; + while (*x) { + if (*x == ':') { + y->sizep1 = cnt; + cnt = -1; + y->ap2 = x+1; + break; + } + x++; + cnt++; + } + + /* check for no scheme in string start */ + /* (apparently schemes *must* be larger than a single character) */ + if ((*x == '\0') || (y->sizep1 <= 1)) { + y->ap1 = 0; + return 0x80041001; + } + + /* found scheme, set length of remainder */ + y->sizep2 = lstrlenA(y->ap2); + + /* see if known scheme and return indicator number */ + y->fcncde = URL_SCHEME_UNKNOWN; + inet_pro = shlwapi_schemes; + while (inet_pro->scheme_name) { + if (!strncasecmp(inet_pro->scheme_name, y->ap1, + min(y->sizep1, lstrlenA(inet_pro->scheme_name)))) { + y->fcncde = inet_pro->scheme_number; + break; + } + inet_pro++; + } + return S_OK; +} + +/************************************************************************* + * @ [SHLWAPI.2] + * + * Unicode version of ParseURLA. + */ +DWORD WINAPI ParseURLW(LPCWSTR x, UNKNOWN_SHLWAPI_2 *y) +{ + DWORD cnt; + const SHL_2_inet_scheme *inet_pro; + LPSTR cmpstr; + INT len; + + y->fcncde = URL_SCHEME_INVALID; + if (y->size != 0x18) return E_INVALIDARG; + /* FIXME: leading white space generates error of 0x80041001 which + * is undefined + */ + if (*x <= L' ') return 0x80041001; + cnt = 0; + y->sizep1 = 0; + y->ap1 = x; + while (*x) { + if (*x == L':') { + y->sizep1 = cnt; + cnt = -1; + y->ap2 = x+1; + break; + } + x++; + cnt++; + } + + /* check for no scheme in string start */ + /* (apparently schemes *must* be larger than a single character) */ + if ((*x == L'\0') || (y->sizep1 <= 1)) { + y->ap1 = 0; + return 0x80041001; + } + + /* found scheme, set length of remainder */ + y->sizep2 = lstrlenW(y->ap2); + + /* see if known scheme and return indicator number */ + len = WideCharToMultiByte(0, 0, y->ap1, y->sizep1, 0, 0, 0, 0); + cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len+1); + WideCharToMultiByte(0, 0, y->ap1, y->sizep1, cmpstr, len+1, 0, 0); + y->fcncde = URL_SCHEME_UNKNOWN; + inet_pro = shlwapi_schemes; + while (inet_pro->scheme_name) { + if (!strncasecmp(inet_pro->scheme_name, cmpstr, + min(len, lstrlenA(inet_pro->scheme_name)))) { + y->fcncde = inet_pro->scheme_number; + break; + } + inet_pro++; + } + HeapFree(GetProcessHeap(), 0, cmpstr); + return S_OK; +} + +/************************************************************************* + * UrlCanonicalizeA [SHLWAPI.@] + * + * Canonicalize a Url. + * + * PARAMS + * pszUrl [I] Url to cCanonicalize + * pszCanonicalized [O] Destination for converted Url. + * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized + * dwFlags [I] Flags controlling the conversion. + * + * RETURNS + * Success: S_OK. The pszCanonicalized contains the converted Url. + * Failure: E_POINTER, if *pcchCanonicalized is too small. + * + * MSDN incorrectly describes the flags for this function. They should be: + *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000 + *| URL_ESCAPE_SPACES_ONLY 0x04000000 + *| URL_ESCAPE_PERCENT 0x00001000 + *| URL_ESCAPE_UNSAFE 0x10000000 + *| URL_UNESCAPE 0x10000000 + *| URL_DONT_SIMPLIFY 0x08000000 + *| URL_ESCAPE_SEGMENT_ONLY 0x00002000 + */ +HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized, + LPDWORD pcchCanonicalized, DWORD dwFlags) +{ + LPWSTR base, canonical; + DWORD ret, len, len2; + + TRACE("(%s %p %p 0x%08lx) using W version\n", + debugstr_a(pszUrl), pszCanonicalized, + pcchCanonicalized, dwFlags); + + base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, + (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR)); + canonical = base + INTERNET_MAX_URL_LENGTH; + + MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH); + len = INTERNET_MAX_URL_LENGTH; + + ret = UrlCanonicalizeW(base, canonical, &len, dwFlags); + if (ret != S_OK) { + HeapFree(GetProcessHeap(), 0, base); + return ret; + } + + len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0); + if (len2 > *pcchCanonicalized) { + *pcchCanonicalized = len; + HeapFree(GetProcessHeap(), 0, base); + return E_POINTER; + } + WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized, + *pcchCanonicalized, 0, 0); + *pcchCanonicalized = len2; + HeapFree(GetProcessHeap(), 0, base); + return S_OK; +} + +/************************************************************************* + * UrlCanonicalizeW [SHLWAPI.@] + * + * See UrlCanonicalizeA. + */ +HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized, + LPDWORD pcchCanonicalized, DWORD dwFlags) +{ + HRESULT hr = S_OK; + DWORD EscapeFlags; + LPWSTR lpszUrlCpy, wk1, wk2, mp, root; + INT nLen, nByteLen, state; + + TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized, + pcchCanonicalized, dwFlags); + + nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */ + lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen); + + if (dwFlags & URL_DONT_SIMPLIFY) + memcpy(lpszUrlCpy, pszUrl, nByteLen); + else { + + /* + * state = + * 0 initial 1,3 + * 1 have 2[+] alnum 2,3 + * 2 have scheme (found :) 4,6,3 + * 3 failed (no location) + * 4 have // 5,3 + * 5 have 1[+] alnum 6,3 + * 6 have location (found /) save root location + */ + + wk1 = (LPWSTR)pszUrl; + wk2 = lpszUrlCpy; + state = 0; + while (*wk1) { + switch (state) { + case 0: + if (!isalnumW(*wk1)) {state = 3; break;} + *wk2++ = *wk1++; + if (!isalnumW(*wk1)) {state = 3; break;} + *wk2++ = *wk1++; + state = 1; + break; + case 1: + *wk2++ = *wk1; + if (*wk1++ == L':') state = 2; + break; + case 2: + if (*wk1 != L'/') {state = 3; break;} + *wk2++ = *wk1++; + if (*wk1 != L'/') {state = 6; break;} + *wk2++ = *wk1++; + state = 4; + break; + case 3: + strcpyW(wk2, wk1); + wk1 += strlenW(wk1); + wk2 += strlenW(wk2); + break; + case 4: + if (!isalnumW(*wk1) && (*wk1 != L'-')) {state = 3; break;} + while(isalnumW(*wk1) || (*wk1 == L'-')) *wk2++ = *wk1++; + state = 5; + break; + case 5: + if (*wk1 != L'/') {state = 3; break;} + *wk2++ = *wk1++; + state = 6; + break; + case 6: + /* Now at root location, cannot back up any more. */ + /* "root" will point at the '/' */ + root = wk2-1; + while (*wk1) { + TRACE("wk1=%c\n", (CHAR)*wk1); + mp = strchrW(wk1, L'/'); + if (!mp) { + strcpyW(wk2, wk1); + wk1 += strlenW(wk1); + wk2 += strlenW(wk2); + continue; + } + nLen = mp - wk1 + 1; + strncpyW(wk2, wk1, nLen); + wk2 += nLen; + wk1 += nLen; + if (*wk1 == L'.') { + TRACE("found '/.'\n"); + if (*(wk1+1) == L'/') { + /* case of /./ -> skip the ./ */ + wk1 += 2; + } + else if (*(wk1+1) == L'.') { + /* found /.. look for next / */ + TRACE("found '/..'\n"); + if (*(wk1+2) == L'/') { + /* case /../ -> need to backup wk2 */ + TRACE("found '/../'\n"); + *(wk2-1) = L'\0'; /* set end of string */ + mp = strrchrW(root, L'/'); + if (mp && (mp >= root)) { + /* found valid backup point */ + wk2 = mp + 1; + wk1 += 3; + } + else { + /* did not find point, restore '/' */ + *(wk2-1) = L'/'; + } + } + } + } + } + *wk2 = L'\0'; + break; + default: + FIXME("how did we get here - state=%d\n", state); + return E_INVALIDARG; + } + } + *wk2 = L'\0'; + TRACE("Simplified, orig <%s>, simple <%s>\n", + debugstr_w(pszUrl), debugstr_w(lpszUrlCpy)); + } + + if(dwFlags & URL_UNESCAPE) + UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE); + + if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE | + URL_ESCAPE_SPACES_ONLY | + URL_ESCAPE_PERCENT | + URL_DONT_ESCAPE_EXTRA_INFO | + URL_ESCAPE_SEGMENT_ONLY ))) { + EscapeFlags &= ~URL_ESCAPE_UNSAFE; + hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized, + EscapeFlags); + } else { /* No escaping needed, just copy the string */ + nLen = lstrlenW(lpszUrlCpy); + if(nLen < *pcchCanonicalized) + memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR)); + else { + hr = E_POINTER; + nLen++; + } + *pcchCanonicalized = nLen; + } + + HeapFree(GetProcessHeap(), 0, lpszUrlCpy); + + if (hr == S_OK) + TRACE("result %s\n", debugstr_w(pszCanonicalized)); + + return hr; +} + +/************************************************************************* + * UrlCombineA [SHLWAPI.@] + * + * Combine two Urls. + * + * PARAMS + * pszBase [I] Base Url + * pszRelative [I] Url to combine with pszBase + * pszCombined [O] Destination for combined Url + * pcchCombined [O] Destination for length of pszCombined + * dwFlags [I] URL_ flags from "shlwapi.h" + * + * RETURNS + * Success: S_OK. pszCombined contains the combined Url, pcchCombined + * contains its length. + * Failure: An HRESULT error code indicating the error. + */ +HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative, + LPSTR pszCombined, LPDWORD pcchCombined, + DWORD dwFlags) +{ + LPWSTR base, relative, combined; + DWORD ret, len, len2; + + TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n", + debugstr_a(pszBase),debugstr_a(pszRelative), + *pcchCombined,dwFlags); + + base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, + (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR)); + relative = base + INTERNET_MAX_URL_LENGTH; + combined = relative + INTERNET_MAX_URL_LENGTH; + + MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH); + MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH); + len = INTERNET_MAX_URL_LENGTH; + + ret = UrlCombineW(base, relative, combined, &len, dwFlags); + if (ret != S_OK) { + HeapFree(GetProcessHeap(), 0, base); + return ret; + } + + len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0); + if (len2 > *pcchCombined) { + *pcchCombined = len2; + HeapFree(GetProcessHeap(), 0, base); + return E_POINTER; + } + WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined, + 0, 0); + *pcchCombined = len2; + HeapFree(GetProcessHeap(), 0, base); + return S_OK; +} + +/************************************************************************* + * UrlCombineW [SHLWAPI.@] + * + * See UrlCombineA. + */ +HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative, + LPWSTR pszCombined, LPDWORD pcchCombined, + DWORD dwFlags) +{ + UNKNOWN_SHLWAPI_2 base, relative; + DWORD myflags, sizeloc = 0; + DWORD len, res1, res2, process_case = 0; + LPWSTR work, preliminary, mbase, mrelative; + WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'}; + WCHAR single_slash[] = {'/','\0'}; + HRESULT ret; + + TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n", + debugstr_w(pszBase),debugstr_w(pszRelative), + *pcchCombined,dwFlags); + + base.size = 24; + relative.size = 24; + + /* Get space for duplicates of the input and the output */ + preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) * + sizeof(WCHAR)); + mbase = preliminary + INTERNET_MAX_URL_LENGTH; + mrelative = mbase + INTERNET_MAX_URL_LENGTH; + *preliminary = L'\0'; + + /* Canonicalize the base input prior to looking for the scheme */ + myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE); + len = INTERNET_MAX_URL_LENGTH; + ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags); + + /* Canonicalize the relative input prior to looking for the scheme */ + len = INTERNET_MAX_URL_LENGTH; + ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags); + + /* See if the base has a scheme */ + res1 = ParseURLW(mbase, &base); + if (res1) { + /* if pszBase has no scheme, then return pszRelative */ + TRACE("no scheme detected in Base\n"); + process_case = 1; + } + else do { + + /* get size of location field (if it exists) */ + work = (LPWSTR)base.ap2; + sizeloc = 0; + if (*work++ == L'/') { + if (*work++ == L'/') { + /* At this point have start of location and + * it ends at next '/' or end of string. + */ + while(*work && (*work != L'/')) work++; + sizeloc = (DWORD)(work - base.ap2); + } + } + + /* Change .sizep2 to not have the last leaf in it, + * Note: we need to start after the location (if it exists) + */ + work = strrchrW((base.ap2+sizeloc), L'/'); + if (work) { + len = (DWORD)(work - base.ap2 + 1); + base.sizep2 = len; + } + /* + * At this point: + * .ap2 points to location (starting with '//') + * .sizep2 length of location (above) and rest less the last + * leaf (if any) + * sizeloc length of location (above) up to but not including + * the last '/' + */ + + res2 = ParseURLW(mrelative, &relative); + if (res2) { + /* no scheme in pszRelative */ + TRACE("no scheme detected in Relative\n"); + relative.ap2 = mrelative; /* case 3,4,5 depends on this */ + relative.sizep2 = strlenW(mrelative); + if (*pszRelative == L':') { + /* case that is either left alone or uses pszBase */ + if (dwFlags & URL_PLUGGABLE_PROTOCOL) { + process_case = 5; + break; + } + process_case = 1; + break; + } + if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) { + /* case that becomes "file:///" */ + strcpyW(preliminary, myfilestr); + process_case = 1; + break; + } + if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) { + /* pszRelative has location and rest */ + process_case = 3; + break; + } + if (*mrelative == L'/') { + /* case where pszRelative is root to location */ + process_case = 4; + break; + } + process_case = (*base.ap2 == L'/') ? 5 : 3; + break; + } + + /* handle cases where pszRelative has scheme */ + if ((base.sizep1 == relative.sizep1) && + (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) { + + /* since the schemes are the same */ + if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) { + /* case where pszRelative replaces location and following */ + process_case = 3; + break; + } + if (*relative.ap2 == L'/') { + /* case where pszRelative is root to location */ + process_case = 4; + break; + } + /* case where scheme is followed by document path */ + process_case = 5; + break; + } + if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) { + /* case where pszRelative replaces scheme, location, + * and following and handles PLUGGABLE + */ + process_case = 2; + break; + } + process_case = 1; + break; + } while(FALSE); /* a litte trick to allow easy exit from nested if's */ + + + ret = S_OK; + switch (process_case) { + + case 1: /* + * Return pszRelative appended to what ever is in pszCombined, + * (which may the string "file:///" + */ + len = strlenW(mrelative) + strlenW(preliminary); + if (len+1 > *pcchCombined) { + *pcchCombined = len; + ret = E_POINTER; + break; + } + strcatW(preliminary, mrelative); + break; + + case 2: /* + * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified + * and pszRelative starts with "//", then append a "/" + */ + len = strlenW(mrelative) + 1; + if (len+1 > *pcchCombined) { + *pcchCombined = len; + ret = E_POINTER; + break; + } + strcpyW(preliminary, mrelative); + if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) && + URL_JustLocation(relative.ap2)) + strcatW(preliminary, single_slash); + break; + + case 3: /* + * Return the pszBase scheme with pszRelative. Basicly + * keeps the scheme and replaces the domain and following. + */ + len = base.sizep1 + 1 + relative.sizep2 + 1; + if (len+1 > *pcchCombined) { + *pcchCombined = len; + ret = E_POINTER; + break; + } + strncpyW(preliminary, base.ap1, base.sizep1 + 1); + work = preliminary + base.sizep1 + 1; + strcpyW(work, relative.ap2); + if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) && + URL_JustLocation(relative.ap2)) + strcatW(work, single_slash); + break; + + case 4: /* + * Return the pszBase scheme and location but everything + * after the location is pszRelative. (Replace document + * from root on.) + */ + len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1; + if (len+1 > *pcchCombined) { + *pcchCombined = len; + ret = E_POINTER; + break; + } + strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc); + work = preliminary + base.sizep1 + 1 + sizeloc; + if (dwFlags & URL_PLUGGABLE_PROTOCOL) + *(work++) = L'/'; + strcpyW(work, relative.ap2); + break; + + case 5: /* + * Return the pszBase without its document (if any) and + * append pszRelative after its scheme. + */ + len = base.sizep1 + 1 + base.sizep2 + relative.sizep2; + if (len+1 > *pcchCombined) { + *pcchCombined = len; + ret = E_POINTER; + break; + } + strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2); + work = preliminary + base.sizep1+1+base.sizep2 - 1; + if (*work++ != L'/') + *(work++) = L'/'; + strcpyW(work, relative.ap2); + break; + + default: + FIXME("How did we get here????? process_case=%ld\n", process_case); + ret = E_INVALIDARG; + } + + if (ret == S_OK) { + /* + * Now that the combining is done, process the escape options if + * necessary, otherwise just copy the string. + */ + myflags = dwFlags & (URL_ESCAPE_PERCENT | + URL_ESCAPE_SPACES_ONLY | + URL_DONT_ESCAPE_EXTRA_INFO | + URL_ESCAPE_SEGMENT_ONLY); + if (myflags) + ret = UrlEscapeW(preliminary, pszCombined, + pcchCombined, myflags); + else { + len = (strlenW(preliminary) + 1) * sizeof(WCHAR); + memcpy(pszCombined, preliminary, len); + *pcchCombined = strlenW(preliminary); + } + TRACE("return-%ld len=%ld, %s\n", + process_case, *pcchCombined, debugstr_w(pszCombined)); + } + HeapFree(GetProcessHeap(), 0, preliminary); + return ret; +} + +/************************************************************************* + * UrlEscapeA [SHLWAPI.@] + * + * Converts unsafe characters in a Url into escape sequences. + * + * PARAMS + * pszUrl [I] Url to modify + * pszEscaped [O] Destination for modified Url + * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped + * dwFlags [I] URL_ flags from "shlwapi.h" + * + * RETURNS + * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped + * contains its length. + * Failure: E_POINTER, if pszEscaped is not large enough. In this case + * pcchEscaped is set to the required length. + + * Converts unsafe characters into their escape sequences. + * + * NOTES + * - By default this function stops converting at the first '?' or + * '#' character (MSDN does not document this). + * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are + * converted, but the conversion continues past a '?' or '#'. + * - Note that this function did not work well (or at all) in shlwapi version 4. + * + * BUGS + * Only the following flags are implemented: + *| URL_ESCAPE_SPACES_ONLY + *| URL_DONT_ESCAPE_EXTRA_INFO + *| URL_ESCAPE_SEGMENT_ONLY + *| URL_ESCAPE_PERCENT + */ +HRESULT WINAPI UrlEscapeA( + LPCSTR pszUrl, + LPSTR pszEscaped, + LPDWORD pcchEscaped, + DWORD dwFlags) +{ + LPCSTR src; + DWORD needed = 0, ret; + BOOL stop_escaping = FALSE; + char next[3], *dst = pszEscaped; + INT len; + + TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped, + *pcchEscaped, dwFlags); + + if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY | + URL_ESCAPE_SEGMENT_ONLY | + URL_DONT_ESCAPE_EXTRA_INFO | + URL_ESCAPE_PERCENT)) + FIXME("Unimplemented flags: %08lx\n", dwFlags); + + /* fix up flags */ + if (dwFlags & URL_ESCAPE_SPACES_ONLY) + /* if SPACES_ONLY specified, reset the other controls */ + dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO | + URL_ESCAPE_PERCENT | + URL_ESCAPE_SEGMENT_ONLY); + + else + /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */ + dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO; + + for(src = pszUrl; *src; src++) { + if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) && + (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) && + (*src == '#' || *src == '?')) + stop_escaping = TRUE; + + if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) { + /* TRACE("escaping %c\n", *src); */ + next[0] = '%'; + next[1] = hexDigits[(*src >> 4) & 0xf]; + next[2] = hexDigits[*src & 0xf]; + len = 3; + } else { + /* TRACE("passing %c\n", *src); */ + next[0] = *src; + len = 1; + } + + if(needed + len <= *pcchEscaped) { + memcpy(dst, next, len); + dst += len; + } + needed += len; + } + + if(needed < *pcchEscaped) { + *dst = '\0'; + ret = S_OK; + } else { + needed++; /* add one for the '\0' */ + ret = E_POINTER; + } + *pcchEscaped = needed; + return ret; +} + +/************************************************************************* + * UrlEscapeW [SHLWAPI.@] + * + * See UrlEscapeA. + */ +HRESULT WINAPI UrlEscapeW( + LPCWSTR pszUrl, + LPWSTR pszEscaped, + LPDWORD pcchEscaped, + DWORD dwFlags) +{ + LPCWSTR src; + DWORD needed = 0, ret; + BOOL stop_escaping = FALSE; + WCHAR next[5], *dst = pszEscaped; + INT len; + + TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped, + pcchEscaped, dwFlags); + + if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY | + URL_ESCAPE_SEGMENT_ONLY | + URL_DONT_ESCAPE_EXTRA_INFO | + URL_ESCAPE_PERCENT)) + FIXME("Unimplemented flags: %08lx\n", dwFlags); + + /* fix up flags */ + if (dwFlags & URL_ESCAPE_SPACES_ONLY) + /* if SPACES_ONLY specified, reset the other controls */ + dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO | + URL_ESCAPE_PERCENT | + URL_ESCAPE_SEGMENT_ONLY); + + else + /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */ + dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO; + + for(src = pszUrl; *src; src++) { + /* + * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) && + * (*src == L'#' || *src == L'?')) + * stop_escaping = TRUE; + */ + if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) && + (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) && + (*src == L'#' || *src == L'?')) + stop_escaping = TRUE; + + if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) { + /* TRACE("escaping %c\n", *src); */ + next[0] = L'%'; + /* + * I would have assumed that the W form would escape + * the character with 4 hex digits (or even 8), + * however, experiments show that native shlwapi escapes + * with only 2 hex digits. + * next[1] = hexDigits[(*src >> 12) & 0xf]; + * next[2] = hexDigits[(*src >> 8) & 0xf]; + * next[3] = hexDigits[(*src >> 4) & 0xf]; + * next[4] = hexDigits[*src & 0xf]; + * len = 5; + */ + next[1] = hexDigits[(*src >> 4) & 0xf]; + next[2] = hexDigits[*src & 0xf]; + len = 3; + } else { + /* TRACE("passing %c\n", *src); */ + next[0] = *src; + len = 1; + } + + if(needed + len <= *pcchEscaped) { + memcpy(dst, next, len*sizeof(WCHAR)); + dst += len; + } + needed += len; + } + + if(needed < *pcchEscaped) { + *dst = L'\0'; + ret = S_OK; + } else { + needed++; /* add one for the '\0' */ + ret = E_POINTER; + } + *pcchEscaped = needed; + return ret; +} + + +/************************************************************************* + * UrlUnescapeA [SHLWAPI.@] + * + * Converts Url escape sequences back to ordinary characters. + * + * PARAMS + * pszUrl [I/O] Url to convert + * pszUnescaped [O] Destination for converted Url + * pcchUnescaped [I/O] Size of output string + * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h" + * + * RETURNS + * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if + * dwFlags includes URL_ESCAPE_INPLACE. + * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In + * this case pcchUnescaped is set to the size required. + * NOTES + * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at + * the first occurrence of either a '?' or '#' character. + */ +HRESULT WINAPI UrlUnescapeA( + LPCSTR pszUrl, + LPSTR pszUnescaped, + LPDWORD pcchUnescaped, + DWORD dwFlags) +{ + char *dst, next; + LPCSTR src; + HRESULT ret; + DWORD needed; + BOOL stop_unescaping = FALSE; + + TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped, + pcchUnescaped, dwFlags); + + if(dwFlags & URL_UNESCAPE_INPLACE) + dst = (char*)pszUrl; + else + dst = pszUnescaped; + + for(src = pszUrl, needed = 0; *src; src++, needed++) { + if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO && + (*src == '#' || *src == '?')) { + stop_unescaping = TRUE; + next = *src; + } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2)) + && stop_unescaping == FALSE) { + INT ih; + char buf[3]; + memcpy(buf, src + 1, 2); + buf[2] = '\0'; + ih = strtol(buf, NULL, 16); + next = (CHAR) ih; + src += 2; /* Advance to end of escape */ + } else + next = *src; + + if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) + *dst++ = next; + } + + if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) { + *dst = '\0'; + ret = S_OK; + } else { + needed++; /* add one for the '\0' */ + ret = E_POINTER; + } + if(!(dwFlags & URL_UNESCAPE_INPLACE)) + *pcchUnescaped = needed; + + if (ret == S_OK) { + TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ? + debugstr_a(pszUrl) : debugstr_a(pszUnescaped)); + } + + return ret; +} + +/************************************************************************* + * UrlUnescapeW [SHLWAPI.@] + * + * See UrlUnescapeA. + */ +HRESULT WINAPI UrlUnescapeW( + LPCWSTR pszUrl, + LPWSTR pszUnescaped, + LPDWORD pcchUnescaped, + DWORD dwFlags) +{ + WCHAR *dst, next; + LPCWSTR src; + HRESULT ret; + DWORD needed; + BOOL stop_unescaping = FALSE; + + TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped, + pcchUnescaped, dwFlags); + + if(dwFlags & URL_UNESCAPE_INPLACE) + dst = (WCHAR*)pszUrl; + else + dst = pszUnescaped; + + for(src = pszUrl, needed = 0; *src; src++, needed++) { + if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO && + (*src == L'#' || *src == L'?')) { + stop_unescaping = TRUE; + next = *src; + } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2)) + && stop_unescaping == FALSE) { + INT ih; + WCHAR buf[3]; + memcpy(buf, src + 1, 2*sizeof(WCHAR)); + buf[2] = L'\0'; + ih = StrToIntW(buf); + next = (WCHAR) ih; + src += 2; /* Advance to end of escape */ + } else + next = *src; + + if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) + *dst++ = next; + } + + if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) { + *dst = L'\0'; + ret = S_OK; + } else { + needed++; /* add one for the '\0' */ + ret = E_POINTER; + } + if(!(dwFlags & URL_UNESCAPE_INPLACE)) + *pcchUnescaped = needed; + + if (ret == S_OK) { + TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ? + debugstr_w(pszUrl) : debugstr_w(pszUnescaped)); + } + + return ret; +} + +/************************************************************************* + * UrlGetLocationA [SHLWAPI.@] + * + * Get the location from a Url. + * + * PARAMS + * pszUrl [I] Url to get the location from + * + * RETURNS + * A pointer to the start of the location in pszUrl, or NULL if there is + * no location. + * + * NOTES + * - MSDN erroneously states that "The location is the segment of the Url + * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll + * stop at '?' and always return a NULL in this case. + * - MSDN also erroneously states that "If a file URL has a query string, + * the returned string is the query string". In all tested cases, if the + * Url starts with "fi" then a NULL is returned. V5 gives the following results: + *| Result Url + *| ------ --- + *| NULL file://aa/b/cd#hohoh + *| #hohoh http://aa/b/cd#hohoh + *| NULL fi://aa/b/cd#hohoh + *| #hohoh ff://aa/b/cd#hohoh + */ +LPCSTR WINAPI UrlGetLocationA( + LPCSTR pszUrl) +{ + UNKNOWN_SHLWAPI_1 base; + DWORD res1; + + base.size = 24; + res1 = ParseURLA(pszUrl, &base); + if (res1) return NULL; /* invalid scheme */ + + /* if scheme is file: then never return pointer */ + if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL; + + /* Look for '#' and return its addr */ + return strchr(base.ap2, '#'); +} + +/************************************************************************* + * UrlGetLocationW [SHLWAPI.@] + * + * See UrlGetLocationA. + */ +LPCWSTR WINAPI UrlGetLocationW( + LPCWSTR pszUrl) +{ + UNKNOWN_SHLWAPI_2 base; + DWORD res1; + + base.size = 24; + res1 = ParseURLW(pszUrl, &base); + if (res1) return NULL; /* invalid scheme */ + + /* if scheme is file: then never return pointer */ + if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL; + + /* Look for '#' and return its addr */ + return strchrW(base.ap2, L'#'); +} + +/************************************************************************* + * UrlCompareA [SHLWAPI.@] + * + * Compare two Urls. + * + * PARAMS + * pszUrl1 [I] First Url to compare + * pszUrl2 [I] Url to compare to pszUrl1 + * fIgnoreSlash [I] TRUE = compare only up to a final slash + * + * RETURNS + * less than zero, zero, or greater than zero indicating pszUrl2 is greater + * than, equal to, or less than pszUrl1 respectively. + */ +INT WINAPI UrlCompareA( + LPCSTR pszUrl1, + LPCSTR pszUrl2, + BOOL fIgnoreSlash) +{ + INT ret, len, len1, len2; + + if (!fIgnoreSlash) + return strcmp(pszUrl1, pszUrl2); + len1 = strlen(pszUrl1); + if (pszUrl1[len1-1] == '/') len1--; + len2 = strlen(pszUrl2); + if (pszUrl2[len2-1] == '/') len2--; + if (len1 == len2) + return strncmp(pszUrl1, pszUrl2, len1); + len = min(len1, len2); + ret = strncmp(pszUrl1, pszUrl2, len); + if (ret) return ret; + if (len1 > len2) return 1; + return -1; +} + +/************************************************************************* + * UrlCompareW [SHLWAPI.@] + * + * See UrlCompareA. + */ +INT WINAPI UrlCompareW( + LPCWSTR pszUrl1, + LPCWSTR pszUrl2, + BOOL fIgnoreSlash) +{ + INT ret; + size_t len, len1, len2; + + if (!fIgnoreSlash) + return strcmpW(pszUrl1, pszUrl2); + len1 = strlenW(pszUrl1); + if (pszUrl1[len1-1] == '/') len1--; + len2 = strlenW(pszUrl2); + if (pszUrl2[len2-1] == '/') len2--; + if (len1 == len2) + return strncmpW(pszUrl1, pszUrl2, len1); + len = min(len1, len2); + ret = strncmpW(pszUrl1, pszUrl2, len); + if (ret) return ret; + if (len1 > len2) return 1; + return -1; +} + +/************************************************************************* + * HashData [SHLWAPI.@] + * + * Hash an input block into a variable sized digest. + * + * PARAMS + * lpSrc [I] Input block + * nSrcLen [I] Length of lpSrc + * lpDest [I] Output for hash digest + * nDestLen [I] Length of lpDest + * + * RETURNS + * Success: TRUE. lpDest is filled with the computed hash value. + * Failure: FALSE, if any argument is invalid. + */ +BOOL WINAPI HashData(const unsigned char *lpSrc, INT nSrcLen, + unsigned char *lpDest, INT nDestLen) +{ + INT srcCount = nSrcLen - 1, destCount = nDestLen - 1; + + if (IsBadReadPtr(lpSrc, nSrcLen) || + IsBadWritePtr(lpDest, nDestLen)) + return FALSE; + + while (destCount >= 0) + { + lpDest[destCount] = (destCount & 0xff); + destCount--; + } + + while (srcCount >= 0) + { + destCount = nDestLen - 1; + while (destCount >= 0) + { + lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]]; + destCount--; + } + srcCount--; + } + return TRUE; +} + +/************************************************************************* + * UrlHashA [SHLWAPI.@] + * + * Produce a Hash from a Url. + * + * PARAMS + * pszUrl [I] Url to hash + * lpDest [O] Destinationh for hash + * nDestLen [I] Length of lpDest + * + * RETURNS + * Success: S_OK. lpDest is filled with the computed hash value. + * Failure: E_INVALIDARG, if any argument is invalid. + */ +HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, INT nDestLen) +{ + if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen)) + return E_INVALIDARG; + + HashData((PBYTE)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen); + return S_OK; +} + +/************************************************************************* + * UrlHashW [SHLWAPI.@] + * + * See UrlHashA. + */ +HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, INT nDestLen) +{ + char szUrl[MAX_PATH]; + + TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen); + + if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen)) + return E_INVALIDARG; + + /* Win32 hashes the data as an ASCII string, presumably so that both A+W + * return the same digests for the same URL. + */ + WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0); + HashData((PBYTE)szUrl, (int)strlen(szUrl), lpDest, nDestLen); + return S_OK; +} + +/************************************************************************* + * UrlApplySchemeA [SHLWAPI.@] + * + * Apply a scheme to a Url. + * + * PARAMS + * pszIn [I] Url to apply scheme to + * pszOut [O] Destination for modified Url + * pcchOut [I/O] Length of pszOut/destination for length of pszOut + * dwFlags [I] URL_ flags from "shlwapi.h" + * + * RETURNS + * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length. + * Failure: An HRESULT error code describing the error. + */ +HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags) +{ + LPWSTR in, out; + DWORD ret, len, len2; + + TRACE("(in %s, out size %ld, flags %08lx) using W version\n", + debugstr_a(pszIn), *pcchOut, dwFlags); + + in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, + (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR)); + out = in + INTERNET_MAX_URL_LENGTH; + + MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH); + len = INTERNET_MAX_URL_LENGTH; + + ret = UrlApplySchemeW(in, out, &len, dwFlags); + if ((ret != S_OK) && (ret != S_FALSE)) { + HeapFree(GetProcessHeap(), 0, in); + return ret; + } + + len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0); + if (len2 > *pcchOut) { + *pcchOut = len2; + HeapFree(GetProcessHeap(), 0, in); + return E_POINTER; + } + WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0); + *pcchOut = len2; + HeapFree(GetProcessHeap(), 0, in); + return ret; +} + +static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut) +{ + HKEY newkey; + BOOL j; + INT index; + DWORD value_len, data_len, dwType, i; + WCHAR reg_path[MAX_PATH]; + WCHAR value[MAX_PATH], data[MAX_PATH]; + WCHAR Wxx, Wyy; + + MultiByteToWideChar(0, 0, + "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes", + -1, reg_path, MAX_PATH); + RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey); + index = 0; + while(value_len = data_len = MAX_PATH, + RegEnumValueW(newkey, index, value, &value_len, + 0, &dwType, (LPVOID)data, &data_len) == 0) { + TRACE("guess %d %s is %s\n", + index, debugstr_w(value), debugstr_w(data)); + + j = FALSE; + for(i=0; i *pcchOut) { + *pcchOut = strlenW(data) + strlenW(pszIn) + 1; + RegCloseKey(newkey); + return E_POINTER; + } + strcpyW(pszOut, data); + strcatW(pszOut, pszIn); + *pcchOut = strlenW(pszOut); + TRACE("matched and set to %s\n", debugstr_w(pszOut)); + RegCloseKey(newkey); + return S_OK; + } + index++; + } + RegCloseKey(newkey); + return -1; +} + +static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut) +{ + HKEY newkey; + DWORD data_len, dwType; + WCHAR reg_path[MAX_PATH]; + WCHAR value[MAX_PATH], data[MAX_PATH]; + + /* get and prepend default */ + MultiByteToWideChar(0, 0, + "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix", + -1, reg_path, MAX_PATH); + RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey); + data_len = MAX_PATH; + value[0] = L'@'; + value[1] = L'\0'; + RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len); + RegCloseKey(newkey); + if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) { + *pcchOut = strlenW(data) + strlenW(pszIn) + 1; + return E_POINTER; + } + strcpyW(pszOut, data); + strcatW(pszOut, pszIn); + *pcchOut = strlenW(pszOut); + TRACE("used default %s\n", debugstr_w(pszOut)); + return S_OK; +} + +/************************************************************************* + * UrlApplySchemeW [SHLWAPI.@] + * + * See UrlApplySchemeA. + */ +HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags) +{ + UNKNOWN_SHLWAPI_2 in_scheme; + DWORD res1; + HRESULT ret; + + TRACE("(in %s, out size %ld, flags %08lx)\n", + debugstr_w(pszIn), *pcchOut, dwFlags); + + if (dwFlags & URL_APPLY_GUESSFILE) { + FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n", + debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags); + strcpyW(pszOut, pszIn); + *pcchOut = strlenW(pszOut); + return S_FALSE; + } + + in_scheme.size = 24; + /* See if the base has a scheme */ + res1 = ParseURLW(pszIn, &in_scheme); + if (res1) { + /* no scheme in input, need to see if we need to guess */ + if (dwFlags & URL_APPLY_GUESSSCHEME) { + if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1) + return ret; + } + } + else { + /* we have a scheme, see if valid (known scheme) */ + if (in_scheme.fcncde) { + /* have valid scheme, so just copy and exit */ + if (strlenW(pszIn) + 1 > *pcchOut) { + *pcchOut = strlenW(pszIn) + 1; + return E_POINTER; + } + strcpyW(pszOut, pszIn); + *pcchOut = strlenW(pszOut); + TRACE("valid scheme, returing copy\n"); + return S_OK; + } + } + + /* If we are here, then either invalid scheme, + * or no scheme and can't/failed guess. + */ + if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) || + ((res1 != 0)) ) && + (dwFlags & URL_APPLY_DEFAULT)) { + /* find and apply default scheme */ + return URL_ApplyDefault(pszIn, pszOut, pcchOut); + } + + /* just copy and give proper return code */ + if (strlenW(pszIn) + 1 > *pcchOut) { + *pcchOut = strlenW(pszIn) + 1; + return E_POINTER; + } + strcpyW(pszOut, pszIn); + *pcchOut = strlenW(pszOut); + TRACE("returning copy, left alone\n"); + return S_FALSE; +} + +/************************************************************************* + * UrlIsA [SHLWAPI.@] + * + * Determine if a Url is of a certain class. + * + * PARAMS + * pszUrl [I] Url to check + * Urlis [I] URLIS_ constant from "shlwapi.h" + * + * RETURNS + * TRUE if pszUrl belongs to the class type in Urlis. + * FALSE Otherwise. + */ +BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis) +{ + UNKNOWN_SHLWAPI_1 base; + DWORD res1; + + switch (Urlis) { + + case URLIS_OPAQUE: + base.size = 24; + res1 = ParseURLA(pszUrl, &base); + if (res1) return FALSE; /* invalid scheme */ + if ((*base.ap2 == '/') && (*(base.ap2+1) == '/')) + /* has scheme followed by 2 '/' */ + return FALSE; + return TRUE; + + case URLIS_URL: + case URLIS_NOHISTORY: + case URLIS_FILEURL: + case URLIS_APPLIABLE: + case URLIS_DIRECTORY: + case URLIS_HASQUERY: + default: + FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis); + } + return FALSE; +} + +/************************************************************************* + * UrlIsW [SHLWAPI.@] + * + * See UrlIsA. + */ +BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis) +{ + UNKNOWN_SHLWAPI_2 base; + DWORD res1; + + switch (Urlis) { + + case URLIS_OPAQUE: + base.size = 24; + res1 = ParseURLW(pszUrl, &base); + if (res1) return FALSE; /* invalid scheme */ + if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/')) + /* has scheme followed by 2 '/' */ + return FALSE; + return TRUE; + + case URLIS_URL: + case URLIS_NOHISTORY: + case URLIS_FILEURL: + case URLIS_APPLIABLE: + case URLIS_DIRECTORY: + case URLIS_HASQUERY: + default: + FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis); + } + return FALSE; +} + +/************************************************************************* + * UrlIsNoHistoryA [SHLWAPI.@] + * + * Determine if a Url should not be stored in the users history list. + * + * PARAMS + * pszUrl [I] Url to check + * + * RETURNS + * TRUE, if pszUrl should be excluded from the history list, + * FALSE otherwise. + */ +BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl) +{ + return UrlIsA(pszUrl, URLIS_NOHISTORY); +} + +/************************************************************************* + * UrlIsNoHistoryW [SHLWAPI.@] + * + * See UrlIsNoHistoryA. + */ +BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl) +{ + return UrlIsW(pszUrl, URLIS_NOHISTORY); +} + +/************************************************************************* + * UrlIsOpaqueA [SHLWAPI.@] + * + * Determine if a Url is opaque. + * + * PARAMS + * pszUrl [I] Url to check + * + * RETURNS + * TRUE if pszUrl is opaque, + * FALSE Otherwise. + * + * NOTES + * An opaque Url is one that does not start with "://". + */ +BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl) +{ + return UrlIsA(pszUrl, URLIS_OPAQUE); +} + +/************************************************************************* + * UrlIsOpaqueW [SHLWAPI.@] + * + * See UrlIsOpaqueA. + */ +BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl) +{ + return UrlIsW(pszUrl, URLIS_OPAQUE); +} + +/************************************************************************* + * Scans for characters of type "type" and when not matching found, + * returns pointer to it and length in size. + * + * Characters tested based on RFC 1738 + */ +static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type) +{ + static DWORD alwayszero = 0; + BOOL cont = TRUE; + + *size = 0; + + switch(type){ + + case SCHEME: + while (cont) { + if ( (islowerW(*start) && isalphaW(*start)) || + isdigitW(*start) || + (*start == L'+') || + (*start == L'-') || + (*start == L'.')) { + start++; + (*size)++; + } + else + cont = FALSE; + } + break; + + case USERPASS: + while (cont) { + if ( isalphaW(*start) || + isdigitW(*start) || + /* user/password only characters */ + (*start == L';') || + (*start == L'?') || + (*start == L'&') || + (*start == L'=') || + /* *extra* characters */ + (*start == L'!') || + (*start == L'*') || + (*start == L'\'') || + (*start == L'(') || + (*start == L')') || + (*start == L',') || + /* *safe* characters */ + (*start == L'$') || + (*start == L'_') || + (*start == L'+') || + (*start == L'-') || + (*start == L'.')) { + start++; + (*size)++; + } else if (*start == L'%') { + if (isxdigitW(*(start+1)) && + isxdigitW(*(start+2))) { + start += 3; + *size += 3; + } else + cont = FALSE; + } else + cont = FALSE; + } + break; + + case PORT: + while (cont) { + if (isdigitW(*start)) { + start++; + (*size)++; + } + else + cont = FALSE; + } + break; + + case HOST: + while (cont) { + if (isalnumW(*start) || + (*start == L'-') || + (*start == L'.') ) { + start++; + (*size)++; + } + else + cont = FALSE; + } + break; + default: + FIXME("unknown type %d\n", type); + return (LPWSTR)&alwayszero; + } + /* TRACE("scanned %ld characters next char %p<%c>\n", + *size, start, *start); */ + return start; +} + +/************************************************************************* + * Attempt to parse URL into pieces. + */ +static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl) +{ + LPCWSTR work; + + memset(pl, 0, sizeof(WINE_PARSE_URL)); + pl->pScheme = pszUrl; + work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME); + if (!*work || (*work != L':')) goto _ERROR; + work++; + if ((*work != L'/') || (*(work+1) != L'/')) goto _ERROR; + pl->pUserName = work + 2; + work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS); + if (*work == L':' ) { + /* parse password */ + work++; + pl->pPassword = work; + work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS); + if (*work != L'@') { + /* what we just parsed must be the hostname and port + * so reset pointers and clear then let it parse */ + pl->szUserName = pl->szPassword = 0; + work = pl->pUserName - 1; + pl->pUserName = pl->pPassword = 0; + } + } else if (*work == L'@') { + /* no password */ + pl->szPassword = 0; + pl->pPassword = 0; + } else if (!*work || (*work == L'/') || (*work == L'.')) { + /* what was parsed was hostname, so reset pointers and let it parse */ + pl->szUserName = pl->szPassword = 0; + work = pl->pUserName - 1; + pl->pUserName = pl->pPassword = 0; + } else goto _ERROR; + + /* now start parsing hostname or hostnumber */ + work++; + pl->pHostName = work; + work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST); + if (*work == L':') { + /* parse port */ + work++; + pl->pPort = work; + work = URL_ScanID(pl->pPort, &pl->szPort, PORT); + } + if (*work == L'/') { + /* see if query string */ + pl->pQuery = strchrW(work, L'?'); + if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery); + } + TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n", + pl->pScheme, pl->szScheme, + pl->pUserName, pl->szUserName, + pl->pPassword, pl->szPassword, + pl->pHostName, pl->szHostName, + pl->pPort, pl->szPort, + pl->pQuery, pl->szQuery); + return S_OK; + _ERROR: + FIXME("failed to parse %s\n", debugstr_w(pszUrl)); + return E_INVALIDARG; +} + +/************************************************************************* + * UrlGetPartA [SHLWAPI.@] + * + * Retrieve part of a Url. + * + * PARAMS + * pszIn [I] Url to parse + * pszOut [O] Destination for part of pszIn requested + * pcchOut [I/O] Length of pszOut/destination for length of pszOut + * dwPart [I] URL_PART_ enum from "shlwapi.h" + * dwFlags [I] URL_ flags from "shlwapi.h" + * + * RETURNS + * Success: S_OK. pszOut contains the part requested, pcchOut contains its length. + * Failure: An HRESULT error code describing the error. + */ +HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, + DWORD dwPart, DWORD dwFlags) +{ + LPWSTR in, out; + DWORD ret, len, len2; + + in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, + (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR)); + out = in + INTERNET_MAX_URL_LENGTH; + + MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH); + + len = INTERNET_MAX_URL_LENGTH; + ret = UrlGetPartW(in, out, &len, dwPart, dwFlags); + + if (ret != S_OK) { + HeapFree(GetProcessHeap(), 0, in); + return ret; + } + + len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0); + if (len2 > *pcchOut) { + *pcchOut = len2; + HeapFree(GetProcessHeap(), 0, in); + return E_POINTER; + } + WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0); + *pcchOut = len2; + HeapFree(GetProcessHeap(), 0, in); + return S_OK; +} + +/************************************************************************* + * UrlGetPartW [SHLWAPI.@] + * + * See UrlGetPartA. + */ +HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, + DWORD dwPart, DWORD dwFlags) +{ + WINE_PARSE_URL pl; + HRESULT ret; + DWORD size, schsize; + LPCWSTR addr, schaddr; + LPWSTR work; + + TRACE("(%s %p %p(%ld) %08lx %08lx)\n", + debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags); + + ret = URL_ParseUrl(pszIn, &pl); + if (!ret) { + schaddr = pl.pScheme; + schsize = pl.szScheme; + + switch (dwPart) { + case URL_PART_SCHEME: + if (!pl.szScheme) return E_INVALIDARG; + addr = pl.pScheme; + size = pl.szScheme; + break; + + case URL_PART_HOSTNAME: + if (!pl.szHostName) return E_INVALIDARG; + addr = pl.pHostName; + size = pl.szHostName; + break; + + case URL_PART_USERNAME: + if (!pl.szUserName) return E_INVALIDARG; + addr = pl.pUserName; + size = pl.szUserName; + break; + + case URL_PART_PASSWORD: + if (!pl.szPassword) return E_INVALIDARG; + addr = pl.pPassword; + size = pl.szPassword; + break; + + case URL_PART_PORT: + if (!pl.szPort) return E_INVALIDARG; + addr = pl.pPort; + size = pl.szPort; + break; + + case URL_PART_QUERY: + if (!pl.szQuery) return E_INVALIDARG; + addr = pl.pQuery; + size = pl.szQuery; + break; + + default: + return E_INVALIDARG; + } + + if (dwFlags == URL_PARTFLAG_KEEPSCHEME) { + if (*pcchOut < size + schsize + 2) { + *pcchOut = size + schsize + 2; + return E_POINTER; + } + strncpyW(pszOut, schaddr, schsize); + work = pszOut + schsize; + *work = L':'; + strncpyW(work+1, addr, size); + *pcchOut = size + schsize + 1; + work += (size + 1); + *work = L'\0'; + } + else { + if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;} + strncpyW(pszOut, addr, size); + *pcchOut = size; + work = pszOut + size; + *work = L'\0'; + } + TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut)); + } + return ret; +} + +/************************************************************************* + * PathIsURLA [SHLWAPI.@] + * + * Check if the given path is a Url. + * + * PARAMS + * lpszPath [I] Path to check. + * + * RETURNS + * TRUE if lpszPath is a Url. + * FALSE if lpszPath is NULL or not a Url. + */ +BOOL WINAPI PathIsURLA(LPCSTR lpstrPath) +{ + UNKNOWN_SHLWAPI_1 base; + DWORD res1; + + if (!lpstrPath || !*lpstrPath) return FALSE; + + /* get protocol */ + base.size = sizeof(base); + res1 = ParseURLA(lpstrPath, &base); + return (base.fcncde > 0); +} + +/************************************************************************* + * PathIsURLW [SHLWAPI.@] + * + * See PathIsURLA. + */ +BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath) +{ + UNKNOWN_SHLWAPI_2 base; + DWORD res1; + + if (!lpstrPath || !*lpstrPath) return FALSE; + + /* get protocol */ + base.size = sizeof(base); + res1 = ParseURLW(lpstrPath, &base); + return (base.fcncde > 0); +} + +/************************************************************************* + * UrlCreateFromPathA [SHLWAPI.@] + * + * Create a Url from a file path. + * + * PARAMS + * pszPath [I] Path to convert + * pszUrl [O] Destination for the converted Url + * pcchUrl [I/O] Length of pszUrl + * dwReserved [I] Reserved, must be 0 + * + * RETURNS + * Success: S_OK. pszUrl contains the converted path. + * Failure: An HRESULT error code. + */ +HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved) +{ + DWORD nCharBeforeColon = 0; + DWORD nSlashes = 0; + DWORD dwChRequired = 0; + LPSTR pszNewUrl = NULL; + LPCSTR pszConstPointer = NULL; + LPSTR pszPointer = NULL; + DWORD i; + HRESULT ret; + + TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved); + + /* Validate arguments */ + if (dwReserved != 0) + { + FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved); + return E_INVALIDARG; + } + if (!pszUrl || !pcchUrl || !pszUrl) + { + ERR("Invalid argument\n"); + return E_INVALIDARG; + } + + for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++) + { + if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) || + *pszConstPointer == '.' || *pszConstPointer == '-') + nCharBeforeColon++; + else break; + } + if (*pszConstPointer == ':') /* then already in URL format, so copy */ + { + dwChRequired = lstrlenA(pszPath); + if (dwChRequired > *pcchUrl) + { + *pcchUrl = dwChRequired; + return E_POINTER; + } + else + { + *pcchUrl = dwChRequired; + StrCpyA(pszUrl, pszPath); + return S_FALSE; + } + } + /* then must need converting to file: format */ + + /* Strip off leading slashes */ + while (*pszPath == '\\' || *pszPath == '/') + { + pszPath++; + nSlashes++; + } + + dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */ + TRACE("pszUrl: %s\n", debugstr_a(pszPath)); + pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1); + ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT); + TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl)); + TRACE("%ld\n", dwChRequired); + if (ret != E_POINTER && FAILED(ret)) + return ret; + dwChRequired += 5; /* "file:" */ + if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':')) + { + dwChRequired += 3; /* "///" */ + nSlashes = 3; + } + else + switch (nSlashes) + { + case 0: /* no slashes */ + break; + case 2: /* two slashes */ + case 4: + case 5: + case 6: + dwChRequired += 2; + nSlashes = 2; + break; + default: /* three slashes */ + dwChRequired += 3; + nSlashes = 3; + } + + if (dwChRequired > *pcchUrl) + return E_POINTER; + *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */ + StrCpyA(pszUrl, "file:"); + pszPointer = pszUrl + lstrlenA(pszUrl); + for (i=0; i < nSlashes; i++) + { + *pszPointer = '/'; + pszPointer++; + } + StrCpyA(pszPointer, pszNewUrl); + TRACE("<- %s\n", debugstr_a(pszUrl)); + return S_OK; +} + +/************************************************************************* + * UrlCreateFromPathW [SHLWAPI.@] + * + * See UrlCreateFromPathA. + */ +HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved) +{ + DWORD nCharBeforeColon = 0; + DWORD nSlashes = 0; + DWORD dwChRequired = 0; + LPWSTR pszNewUrl = NULL; + LPCWSTR pszConstPointer = NULL; + LPWSTR pszPointer = NULL; + DWORD i; + HRESULT ret; + + TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved); + + /* Validate arguments */ + if (dwReserved != 0) + return E_INVALIDARG; + if (!pszUrl || !pcchUrl || !pszUrl) + return E_INVALIDARG; + + for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++) + { + if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) || + *pszConstPointer == '.' || *pszConstPointer == '-') + nCharBeforeColon++; + else break; + } + if (*pszConstPointer == ':') /* then already in URL format, so copy */ + { + dwChRequired = lstrlenW(pszPath); + *pcchUrl = dwChRequired; + if (dwChRequired > *pcchUrl) + return E_POINTER; + else + { + StrCpyW(pszUrl, pszPath); + return S_FALSE; + } + } + /* then must need converting to file: format */ + + /* Strip off leading slashes */ + while (*pszPath == '\\' || *pszPath == '/') + { + pszPath++; + nSlashes++; + } + + dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */ + ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT); + if (ret != E_POINTER && FAILED(ret)) + return ret; + dwChRequired += 5; /* "file:" */ + if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':')) + { + dwChRequired += 3; /* "///" */ + nSlashes = 3; + } + else + switch (nSlashes) + { + case 0: /* no slashes */ + break; + case 2: /* two slashes */ + case 4: + case 5: + case 6: + dwChRequired += 2; + nSlashes = 2; + break; + default: /* three slashes */ + dwChRequired += 3; + nSlashes = 3; + } + + *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */ + if (dwChRequired > *pcchUrl) + return E_POINTER; + pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR)); + StrCpyW(pszNewUrl, fileW); + pszPointer = pszNewUrl + 4; + *pszPointer = ':'; + pszPointer++; + for (i=0; i < nSlashes; i++) + { + *pszPointer = '/'; + pszPointer++; + } + StrCpyW(pszPointer, pszPath); + StrCpyW(pszUrl, pszNewUrl); + return S_OK; +} + +/************************************************************************* + * SHAutoComplete [SHLWAPI.@] + * + * Enable auto-completion for an edit control. + * + * PARAMS + * hwndEdit [I] Handle of control to enable auto-completion for + * dwFlags [I] SHACF_ flags from "shlwapi.h" + * + * RETURNS + * Success: S_OK. Auto-completion is enabled for the control. + * Failure: An HRESULT error code indicating the error. + */ +HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags) +{ + FIXME("SHAutoComplete stub\n"); + return S_FALSE; +} + +/************************************************************************* + * MLBuildResURLA [SHLWAPI.405] + * + * Create a Url pointing to a resource in a module. + * + * PARAMS + * lpszLibName [I] Name of the module containing the resource + * hMod [I] Callers module handle + * dwFlags [I] Undocumented flags for loading the module + * lpszRes [I] Resource name + * lpszDest [O] Destination for resulting Url + * dwDestLen [I] Length of lpszDest + * + * RETURNS + * Success: S_OK. lpszDest constains the resource Url. + * Failure: E_INVALIDARG, if any argument is invalid, or + * E_FAIL if dwDestLen is too small. + */ +HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags, + LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen) +{ + WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH]; + HRESULT hRet; + + if (lpszLibName) + MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR)); + + if (lpszRes) + MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR)); + + if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR)) + dwDestLen = sizeof(szLibName)/sizeof(WCHAR); + + hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags, + lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen); + if (SUCCEEDED(hRet) && lpszDest) + WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0); + + return hRet; +} + +/************************************************************************* + * MLBuildResURLA [SHLWAPI.406] + * + * See MLBuildResURLA. + */ +HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags, + LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen) +{ + static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' }; +#define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR)) + HRESULT hRet = E_FAIL; + + TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags, + debugstr_w(lpszRes), lpszDest, dwDestLen); + + if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes || + !lpszDest || (dwFlags && dwFlags != 2)) + return E_INVALIDARG; + + if (dwDestLen >= szResLen + 1) + { + dwDestLen -= (szResLen + 1); + memcpy(lpszDest, szRes, sizeof(szRes)); + + hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags); + + if (hMod) + { + WCHAR szBuff[MAX_PATH]; + + if (GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR))) + { + DWORD dwPathLen = strlenW(szBuff) + 1; + + if (dwDestLen >= dwPathLen) + { + DWORD dwResLen; + + dwDestLen -= dwPathLen; + memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR)); + + dwResLen = strlenW(lpszRes) + 1; + if (dwDestLen >= dwResLen + 1) + { + lpszDest[szResLen + dwPathLen + dwResLen] = '/'; + memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR)); + hRet = S_OK; + } + } + } + MLFreeLibrary(hMod); + } + } + return hRet; +} diff --git a/reactos/lib/shlwapi/wsprintf.c b/reactos/lib/shlwapi/wsprintf.c new file mode 100644 index 00000000000..77a867d6d5f --- /dev/null +++ b/reactos/lib/shlwapi/wsprintf.c @@ -0,0 +1,531 @@ +/* + * wsprintf functions + * + * Copyright 1996 Alexandre Julliard + * + * 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 + * + * NOTE: + * This code is duplicated in user32. If you change something here make sure + * to change it in user32 too. + */ + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(string); + + +#define WPRINTF_LEFTALIGN 0x0001 /* Align output on the left ('-' prefix) */ +#define WPRINTF_PREFIX_HEX 0x0002 /* Prefix hex with 0x ('#' prefix) */ +#define WPRINTF_ZEROPAD 0x0004 /* Pad with zeros ('0' prefix) */ +#define WPRINTF_LONG 0x0008 /* Long arg ('l' prefix) */ +#define WPRINTF_SHORT 0x0010 /* Short arg ('h' prefix) */ +#define WPRINTF_UPPER_HEX 0x0020 /* Upper-case hex ('X' specifier) */ +#define WPRINTF_WIDE 0x0040 /* Wide arg ('w' prefix) */ + +typedef enum +{ + WPR_UNKNOWN, + WPR_CHAR, + WPR_WCHAR, + WPR_STRING, + WPR_WSTRING, + WPR_SIGNED, + WPR_UNSIGNED, + WPR_HEXA +} WPRINTF_TYPE; + +typedef struct +{ + UINT flags; + UINT width; + UINT precision; + WPRINTF_TYPE type; +} WPRINTF_FORMAT; + +typedef union { + WCHAR wchar_view; + CHAR char_view; + LPCSTR lpcstr_view; + LPCWSTR lpcwstr_view; + INT int_view; +} WPRINTF_DATA; + +static const CHAR null_stringA[] = "(null)"; +static const WCHAR null_stringW[] = { '(', 'n', 'u', 'l', 'l', ')', 0 }; + +/*********************************************************************** + * WPRINTF_ParseFormatA + * + * Parse a format specification. A format specification has the form: + * + * [-][#][0][width][.precision]type + * + * Return value is the length of the format specification in characters. + */ +static INT WPRINTF_ParseFormatA( LPCSTR format, WPRINTF_FORMAT *res ) +{ + LPCSTR p = format; + + res->flags = 0; + res->width = 0; + res->precision = 0; + if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } + if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } + if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } + while ((*p >= '0') && (*p <= '9')) /* width field */ + { + res->width = res->width * 10 + *p - '0'; + p++; + } + if (*p == '.') /* precision field */ + { + p++; + while ((*p >= '0') && (*p <= '9')) + { + res->precision = res->precision * 10 + *p - '0'; + p++; + } + } + if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; } + else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; } + else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; } + switch(*p) + { + case 'c': + res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR; + break; + case 'C': + res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR; + break; + case 'd': + case 'i': + res->type = WPR_SIGNED; + break; + case 's': + res->type = (res->flags & (WPRINTF_LONG |WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING; + break; + case 'S': + res->type = (res->flags & (WPRINTF_SHORT|WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING; + break; + case 'u': + res->type = WPR_UNSIGNED; + break; + case 'X': + res->flags |= WPRINTF_UPPER_HEX; + /* fall through */ + case 'x': + res->type = WPR_HEXA; + break; + default: /* unknown format char */ + res->type = WPR_UNKNOWN; + p--; /* print format as normal char */ + break; + } + return (INT)(p - format) + 1; +} + + +/*********************************************************************** + * WPRINTF_ParseFormatW + * + * Parse a format specification. A format specification has the form: + * + * [-][#][0][width][.precision]type + * + * Return value is the length of the format specification in characters. + */ +static INT WPRINTF_ParseFormatW( LPCWSTR format, WPRINTF_FORMAT *res ) +{ + LPCWSTR p = format; + + res->flags = 0; + res->width = 0; + res->precision = 0; + if (*p == '-') { res->flags |= WPRINTF_LEFTALIGN; p++; } + if (*p == '#') { res->flags |= WPRINTF_PREFIX_HEX; p++; } + if (*p == '0') { res->flags |= WPRINTF_ZEROPAD; p++; } + while ((*p >= '0') && (*p <= '9')) /* width field */ + { + res->width = res->width * 10 + *p - '0'; + p++; + } + if (*p == '.') /* precision field */ + { + p++; + while ((*p >= '0') && (*p <= '9')) + { + res->precision = res->precision * 10 + *p - '0'; + p++; + } + } + if (*p == 'l') { res->flags |= WPRINTF_LONG; p++; } + else if (*p == 'h') { res->flags |= WPRINTF_SHORT; p++; } + else if (*p == 'w') { res->flags |= WPRINTF_WIDE; p++; } + switch((CHAR)*p) + { + case 'c': + res->type = (res->flags & WPRINTF_SHORT) ? WPR_CHAR : WPR_WCHAR; + break; + case 'C': + res->type = (res->flags & WPRINTF_LONG) ? WPR_WCHAR : WPR_CHAR; + break; + case 'd': + case 'i': + res->type = WPR_SIGNED; + break; + case 's': + res->type = ((res->flags & WPRINTF_SHORT) && !(res->flags & WPRINTF_WIDE)) ? WPR_STRING : WPR_WSTRING; + break; + case 'S': + res->type = (res->flags & (WPRINTF_LONG|WPRINTF_WIDE)) ? WPR_WSTRING : WPR_STRING; + break; + case 'u': + res->type = WPR_UNSIGNED; + break; + case 'X': + res->flags |= WPRINTF_UPPER_HEX; + /* fall through */ + case 'x': + res->type = WPR_HEXA; + break; + default: + res->type = WPR_UNKNOWN; + p--; /* print format as normal char */ + break; + } + return (INT)(p - format) + 1; +} + + +/*********************************************************************** + * WPRINTF_GetLen + */ +static UINT WPRINTF_GetLen( WPRINTF_FORMAT *format, WPRINTF_DATA *arg, + LPSTR number, UINT maxlen ) +{ + UINT len; + + if (format->flags & WPRINTF_LEFTALIGN) format->flags &= ~WPRINTF_ZEROPAD; + if (format->width > maxlen) format->width = maxlen; + switch(format->type) + { + case WPR_CHAR: + case WPR_WCHAR: + return (format->precision = 1); + case WPR_STRING: + if (!arg->lpcstr_view) arg->lpcstr_view = null_stringA; + for (len = 0; !format->precision || (len < format->precision); len++) + if (!*(arg->lpcstr_view + len)) break; + if (len > maxlen) len = maxlen; + return (format->precision = len); + case WPR_WSTRING: + if (!arg->lpcwstr_view) arg->lpcwstr_view = null_stringW; + for (len = 0; !format->precision || (len < format->precision); len++) + if (!*(arg->lpcwstr_view + len)) break; + if (len > maxlen) len = maxlen; + return (format->precision = len); + case WPR_SIGNED: + len = sprintf( number, "%d", arg->int_view ); + break; + case WPR_UNSIGNED: + len = sprintf( number, "%u", (UINT)arg->int_view ); + break; + case WPR_HEXA: + len = sprintf( number, + (format->flags & WPRINTF_UPPER_HEX) ? "%X" : "%x", + (UINT)arg->int_view); + break; + default: + return 0; + } + if (len > maxlen) len = maxlen; + if (format->precision < len) format->precision = len; + if (format->precision > maxlen) format->precision = maxlen; + if ((format->flags & WPRINTF_ZEROPAD) && (format->width > format->precision)) + format->precision = format->width; + if (format->flags & WPRINTF_PREFIX_HEX) len += 2; + return len; +} + + +/*********************************************************************** + * wvnsprintfA (SHLWAPI.@) + * + * Print formatted output to a string, up to a maximum number of chars. + * + * PARAMS + * buffer [O] Destination for output string + * maxlen [I] Maximum number of characters to write + * spec [I] Format string + * + * RETURNS + * Success: The number of characters written. + * Failure: -1. + */ +INT WINAPI wvnsprintfA( LPSTR buffer, UINT maxlen, LPCSTR spec, va_list args ) +{ + WPRINTF_FORMAT format; + LPSTR p = buffer; + UINT i, len, sign; + CHAR number[20]; + WPRINTF_DATA argData; + + TRACE("%p %u %s\n", buffer, maxlen, debugstr_a(spec)); + + while (*spec && (maxlen > 1)) + { + if (*spec != '%') { *p++ = *spec++; maxlen--; continue; } + spec++; + if (*spec == '%') { *p++ = *spec++; maxlen--; continue; } + spec += WPRINTF_ParseFormatA( spec, &format ); + + switch(format.type) + { + case WPR_WCHAR: + argData.wchar_view = (WCHAR)va_arg( args, int ); + break; + case WPR_CHAR: + argData.char_view = (CHAR)va_arg( args, int ); + break; + case WPR_STRING: + argData.lpcstr_view = va_arg( args, LPCSTR ); + break; + case WPR_WSTRING: + argData.lpcwstr_view = va_arg( args, LPCWSTR ); + break; + case WPR_HEXA: + case WPR_SIGNED: + case WPR_UNSIGNED: + argData.int_view = va_arg( args, INT ); + break; + default: + argData.wchar_view = 0; + break; + } + + len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 ); + sign = 0; + if (!(format.flags & WPRINTF_LEFTALIGN)) + for (i = format.precision; i < format.width; i++, maxlen--) + *p++ = ' '; + switch(format.type) + { + case WPR_WCHAR: + *p++ = argData.wchar_view; + break; + case WPR_CHAR: + *p++ = argData.char_view; + break; + case WPR_STRING: + memcpy( p, argData.lpcstr_view, len ); + p += len; + break; + case WPR_WSTRING: + { + LPCWSTR ptr = argData.lpcwstr_view; + for (i = 0; i < len; i++) *p++ = (CHAR)*ptr++; + } + break; + case WPR_HEXA: + if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3)) + { + *p++ = '0'; + *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x'; + maxlen -= 2; + len -= 2; + } + /* fall through */ + case WPR_SIGNED: + /* Transfer the sign now, just in case it will be zero-padded*/ + if (number[0] == '-') + { + *p++ = '-'; + sign = 1; + } + /* fall through */ + case WPR_UNSIGNED: + for (i = len; i < format.precision; i++, maxlen--) *p++ = '0'; + memcpy( p, number + sign, len - sign ); + p += len - sign; + break; + case WPR_UNKNOWN: + continue; + } + if (format.flags & WPRINTF_LEFTALIGN) + for (i = format.precision; i < format.width; i++, maxlen--) + *p++ = ' '; + maxlen -= len; + } + *p = 0; + TRACE("%s\n",debugstr_a(buffer)); + return (maxlen > 1) ? (INT)(p - buffer) : -1; +} + + +/*********************************************************************** + * wvnsprintfW (SHLWAPI.@) + * + * See wvnsprintfA. + */ +INT WINAPI wvnsprintfW( LPWSTR buffer, UINT maxlen, LPCWSTR spec, va_list args ) +{ + WPRINTF_FORMAT format; + LPWSTR p = buffer; + UINT i, len, sign; + CHAR number[20]; + WPRINTF_DATA argData; + + TRACE("%p %u %s\n", buffer, maxlen, debugstr_w(spec)); + + while (*spec && (maxlen > 1)) + { + if (*spec != '%') { *p++ = *spec++; maxlen--; continue; } + spec++; + if (*spec == '%') { *p++ = *spec++; maxlen--; continue; } + spec += WPRINTF_ParseFormatW( spec, &format ); + + switch(format.type) + { + case WPR_WCHAR: + argData.wchar_view = (WCHAR)va_arg( args, int ); + break; + case WPR_CHAR: + argData.char_view = (CHAR)va_arg( args, int ); + break; + case WPR_STRING: + argData.lpcstr_view = va_arg( args, LPCSTR ); + break; + case WPR_WSTRING: + argData.lpcwstr_view = va_arg( args, LPCWSTR ); + break; + case WPR_HEXA: + case WPR_SIGNED: + case WPR_UNSIGNED: + argData.int_view = va_arg( args, INT ); + break; + default: + argData.wchar_view = 0; + break; + } + + len = WPRINTF_GetLen( &format, &argData, number, maxlen - 1 ); + sign = 0; + if (!(format.flags & WPRINTF_LEFTALIGN)) + for (i = format.precision; i < format.width; i++, maxlen--) + *p++ = ' '; + switch(format.type) + { + case WPR_WCHAR: + *p++ = argData.wchar_view; + break; + case WPR_CHAR: + *p++ = argData.char_view; + break; + case WPR_STRING: + { + LPCSTR ptr = argData.lpcstr_view; + for (i = 0; i < len; i++) *p++ = (WCHAR)*ptr++; + } + break; + case WPR_WSTRING: + if (len) memcpy( p, argData.lpcwstr_view, len * sizeof(WCHAR) ); + p += len; + break; + case WPR_HEXA: + if ((format.flags & WPRINTF_PREFIX_HEX) && (maxlen > 3)) + { + *p++ = '0'; + *p++ = (format.flags & WPRINTF_UPPER_HEX) ? 'X' : 'x'; + maxlen -= 2; + len -= 2; + } + /* fall through */ + case WPR_SIGNED: + /* Transfer the sign now, just in case it will be zero-padded*/ + if (number[0] == '-') + { + *p++ = '-'; + sign = 1; + } + /* fall through */ + case WPR_UNSIGNED: + for (i = len; i < format.precision; i++, maxlen--) *p++ = '0'; + for (i = sign; i < len; i++) *p++ = (WCHAR)number[i]; + break; + case WPR_UNKNOWN: + continue; + } + if (format.flags & WPRINTF_LEFTALIGN) + for (i = format.precision; i < format.width; i++, maxlen--) + *p++ = ' '; + maxlen -= len; + } + *p = 0; + TRACE("%s\n",debugstr_w(buffer)); + return (maxlen > 1) ? (INT)(p - buffer) : -1; +} + + +/************************************************************************* + * wnsprintfA (SHLWAPI.@) + * + * Print formatted output to a string, up to a maximum number of chars. + * + * PARAMS + * lpOut [O] Destination for output string + * cchLimitIn [I] Maximum number of characters to write + * lpFmt [I] Format string + * + * RETURNS + * Success: The number of characters written. + * Failure: -1. + */ +int WINAPIV wnsprintfA(LPSTR lpOut, int cchLimitIn, LPCSTR lpFmt, ...) +{ + va_list valist; + INT res; + + va_start( valist, lpFmt ); + res = wvnsprintfA( lpOut, cchLimitIn, lpFmt, valist ); + va_end( valist ); + return res; +} + + +/************************************************************************* + * wnsprintfW (SHLWAPI.@) + * + * See wnsprintfA. + */ +int WINAPIV wnsprintfW(LPWSTR lpOut, int cchLimitIn, LPCWSTR lpFmt, ...) +{ + va_list valist; + INT res; + + va_start( valist, lpFmt ); + res = wvnsprintfW( lpOut, cchLimitIn, lpFmt, valist ); + va_end( valist ); + return res; +}