| /* |
| * 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; |
| } |