| /* | |
| * SHLWAPI Compact 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 <string.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winuser.h" | |
| #include "wine/obj_base.h" | |
| #include "wine/obj_storage.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(shell); | |
| /* Compact list element (ordinals 17-22) */ | |
| typedef struct tagSHLWAPI_CLIST | |
| { | |
| ULONG ulSize; /* Size of this list element and its data */ | |
| ULONG ulId; /* If -1, 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 */ | |
| static const ULONG CLIST_ID_CONTAINER = -1u; | |
| HRESULT WINAPI SHLWAPI_20(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST); | |
| /************************************************************************* | |
| * NextItem | |
| * | |
| * Internal helper: move a clist 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 compact list to a stream. | |
| * | |
| * PARAMS | |
| * lpStream [I] Stream to write the list to | |
| * lpList [I] List of items to write | |
| * | |
| * RETURNS | |
| * Success: S_OK | |
| * Failure: An HRESULT error code | |
| * | |
| * NOTES | |
| * Ordinals 17,18,19,20,21 and 22 are related and together provide a compact | |
| * list structure which may be stored and retrieved from a stream. | |
| * | |
| * The exposed API consists of: | |
| * @17 Write a compact list to a stream | |
| * @18 Read and create a list from a stream | |
| * @19 Free a list | |
| * @20 Insert a new item into a list | |
| * @21 Remove an item from a list | |
| * @22 Find an item in a list | |
| * | |
| * The compact 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 -1, and size sufficient to pad to a DWORD boundary. | |
| * | |
| * These functions are slow for large objects and long lists. | |
| */ | |
| HRESULT WINAPI SHLWAPI_17(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 compact list from a stream | |
| * | |
| * PARAMS | |
| * lpStream [I] Stream to read the list from | |
| * lppList [0] Pointer to recieve 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. | |
| */ | |
| HRESULT WINAPI SHLWAPI_18(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; | |
| SHLWAPI_20(lppList, pItem); /* Insert Item */ | |
| } | |
| } while(1); | |
| /* If we allocated space, free it */ | |
| if(pItem != bBuff) | |
| LocalFree((HLOCAL)pItem); | |
| return hRet; | |
| } | |
| /************************************************************************* | |
| * @ [SHLWAPI.19] | |
| * | |
| * Free a compact list. | |
| * | |
| * PARAMS | |
| * lpList [I] List to free | |
| * | |
| * RETURNS | |
| * Nothing. | |
| */ | |
| VOID WINAPI SHLWAPI_19(LPSHLWAPI_CLIST lpList) | |
| { | |
| TRACE("(%p)\n", lpList); | |
| if (lpList) | |
| LocalFree((HLOCAL)lpList); | |
| } | |
| /************************************************************************* | |
| * @ [SHLWAPI.20] | |
| * | |
| * Insert a new item into a compact list. | |
| * | |
| * PARAMS | |
| * lppList [0] Pointer to the List | |
| * lpNewItem [I] The new item to add to the list | |
| * | |
| * RETURNS | |
| * Success: The size of the inserted item. | |
| * Failure: An HRESULT error code. | |
| */ | |
| HRESULT WINAPI SHLWAPI_20(LPSHLWAPI_CLIST* lppList, LPCSHLWAPI_CLIST lpNewItem) | |
| { | |
| LPSHLWAPI_CLIST lpInsertAt = NULL; | |
| ULONG ulSize; | |
| TRACE("(%p,%p)\n", lppList, lpNewItem); | |
| if(!lppList || !lpNewItem || | |
| lpNewItem->ulId == CLIST_ID_CONTAINER || | |
| lpNewItem->ulSize < sizeof(SHLWAPI_CLIST)) | |
| return E_INVALIDARG; | |
| 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 compact 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. | |
| */ | |
| BOOL WINAPI SHLWAPI_21(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 compact 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 | |
| */ | |
| LPSHLWAPI_CLIST WINAPI SHLWAPI_22(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; | |
| } |