| /* |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| #include <stdarg.h> |
| #include <string.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "objbase.h" |
| #include "shlobj.h" |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(shell); |
| |
| /* dwSignature for contained DATABLOCK_HEADER items */ |
| #define CLIST_ID_CONTAINER (~0U) |
| |
| /************************************************************************* |
| * NextItem |
| * |
| * Internal helper: move a DataBlock pointer to the next item. |
| */ |
| static inline LPDATABLOCK_HEADER NextItem(LPDBLIST lpList) |
| { |
| char* address = (char*)lpList; |
| address += lpList->cbSize; |
| return (LPDATABLOCK_HEADER)address; |
| } |
| |
| /************************************************************************* |
| * @ [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 |
| * DATABLOCK_HEADER 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(LPDBLIST* lppList, const DATABLOCK_HEADER *lpNewItem) |
| { |
| LPDATABLOCK_HEADER lpInsertAt = NULL; |
| ULONG ulSize; |
| |
| TRACE("(%p,%p)\n", lppList, lpNewItem); |
| |
| if(!lppList || !lpNewItem ) |
| return E_INVALIDARG; |
| |
| if (lpNewItem->cbSize < sizeof(DATABLOCK_HEADER) || |
| lpNewItem->dwSignature == CLIST_ID_CONTAINER) |
| return S_OK; |
| |
| ulSize = lpNewItem->cbSize; |
| |
| if(ulSize & 0x3) |
| { |
| /* Tune size to a ULONG boundary, add space for container element */ |
| ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(DATABLOCK_HEADER); |
| TRACE("Creating container item, new size = %d\n", ulSize); |
| } |
| |
| if(!*lppList) |
| { |
| /* An empty list. Allocate space for terminal ulSize also */ |
| *lppList = LocalAlloc(LMEM_ZEROINIT, ulSize + sizeof(ULONG)); |
| lpInsertAt = *lppList; |
| } |
| else |
| { |
| /* Append to the end of the list */ |
| ULONG ulTotalSize = 0; |
| LPDATABLOCK_HEADER lpIter = *lppList; |
| |
| /* Iterate to the end of the list, calculating the total size */ |
| while (lpIter->cbSize) |
| { |
| ulTotalSize += lpIter->cbSize; |
| lpIter = NextItem(lpIter); |
| } |
| |
| /* Increase the size of the list */ |
| lpIter = LocalReAlloc(*lppList, ulTotalSize + ulSize+sizeof(ULONG), |
| LMEM_ZEROINIT | LMEM_MOVEABLE); |
| if(lpIter) |
| { |
| *lppList = lpIter; |
| lpInsertAt = (LPDATABLOCK_HEADER)((char*)lpIter + ulTotalSize); /* At end */ |
| } |
| } |
| |
| if(lpInsertAt) |
| { |
| /* Copy in the new item */ |
| LPDATABLOCK_HEADER lpDest = lpInsertAt; |
| |
| if(ulSize != lpNewItem->cbSize) |
| { |
| lpInsertAt->cbSize = ulSize; |
| lpInsertAt->dwSignature = CLIST_ID_CONTAINER; |
| lpDest++; |
| } |
| memcpy(lpDest, lpNewItem, lpNewItem->cbSize); |
| |
| /* Terminate the list */ |
| lpInsertAt = NextItem(lpInsertAt); |
| lpInsertAt->cbSize = 0; |
| |
| return lpNewItem->cbSize; |
| } |
| return S_OK; |
| } |
| |
| /************************************************************************* |
| * @ [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, LPDBLIST lpList) |
| { |
| ULONG ulSize; |
| HRESULT hRet = S_OK; |
| |
| TRACE("(%p,%p)\n", lpStream, lpList); |
| |
| if(lpList) |
| { |
| while (lpList->cbSize) |
| { |
| LPDATABLOCK_HEADER lpItem = lpList; |
| |
| if(lpList->dwSignature == CLIST_ID_CONTAINER) |
| lpItem++; |
| |
| hRet = IStream_Write(lpStream,lpItem,lpItem->cbSize,&ulSize); |
| if (FAILED(hRet)) |
| return hRet; |
| |
| if(lpItem->cbSize != 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, LPDBLIST* lppList) |
| { |
| DATABLOCK_HEADER bBuff[128]; /* Temporary storage for new list item */ |
| ULONG ulBuffSize = sizeof(bBuff); |
| LPDATABLOCK_HEADER pItem = bBuff; |
| ULONG ulRead, ulSize; |
| HRESULT hRet = S_OK; |
| |
| TRACE("(%p,%p)\n", lpStream, lppList); |
| |
| if(*lppList) |
| { |
| /* Free any existing list */ |
| LocalFree(*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(DATABLOCK_HEADER)) |
| { |
| /* Add this new item to the list */ |
| if(ulSize > ulBuffSize) |
| { |
| /* We need more buffer space, allocate it */ |
| LPDATABLOCK_HEADER lpTemp; |
| |
| if (pItem == bBuff) |
| lpTemp = LocalAlloc(LMEM_ZEROINIT, ulSize); |
| else |
| lpTemp = LocalReAlloc(pItem, ulSize, LMEM_ZEROINIT|LMEM_MOVEABLE); |
| |
| if(!lpTemp) |
| { |
| hRet = E_OUTOFMEMORY; |
| break; |
| } |
| ulBuffSize = ulSize; |
| pItem = lpTemp; |
| } |
| |
| pItem->cbSize = ulSize; |
| ulSize -= sizeof(pItem->cbSize); /* already read this member */ |
| |
| /* Read the item Id and data */ |
| hRet = IStream_Read(lpStream, &pItem->dwSignature, 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(pItem); |
| |
| return hRet; |
| } |
| |
| /************************************************************************* |
| * @ [SHLWAPI.19] |
| * |
| * Free a DataBlock list. |
| * |
| * PARAMS |
| * lpList [I] List to free |
| * |
| * RETURNS |
| * Nothing. |
| * |
| * NOTES |
| * See SHWriteDataBlockList. |
| */ |
| VOID WINAPI SHFreeDataBlockList(LPDBLIST lpList) |
| { |
| TRACE("(%p)\n", lpList); |
| |
| if (lpList) |
| LocalFree(lpList); |
| } |
| |
| /************************************************************************* |
| * @ [SHLWAPI.21] |
| * |
| * Remove an item from a DataBlock list. |
| * |
| * PARAMS |
| * lppList [O] List to remove the item from |
| * dwSignature [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(LPDBLIST* lppList, DWORD dwSignature) |
| { |
| LPDATABLOCK_HEADER lpList = 0; |
| LPDATABLOCK_HEADER lpItem = NULL; |
| LPDATABLOCK_HEADER lpNext; |
| ULONG ulNewSize; |
| |
| TRACE("(%p,%d)\n", lppList, dwSignature); |
| |
| if(lppList && (lpList = *lppList)) |
| { |
| /* Search for item in list */ |
| while (lpList->cbSize) |
| { |
| if(lpList->dwSignature == dwSignature || |
| (lpList->dwSignature == CLIST_ID_CONTAINER && lpList[1].dwSignature == dwSignature)) |
| { |
| lpItem = lpList; /* Found */ |
| break; |
| } |
| lpList = NextItem(lpList); |
| } |
| } |
| |
| if(!lpItem) |
| return FALSE; |
| |
| lpList = lpNext = NextItem(lpItem); |
| |
| /* Locate the end of the list */ |
| while (lpList->cbSize) |
| lpList = NextItem(lpList); |
| |
| /* Resize the list */ |
| ulNewSize = LocalSize(*lppList) - lpItem->cbSize; |
| |
| /* Copy following elements over lpItem */ |
| memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG)); |
| |
| if(ulNewSize <= sizeof(ULONG)) |
| { |
| LocalFree(*lppList); |
| *lppList = NULL; /* Removed the last element */ |
| } |
| else |
| { |
| lpList = LocalReAlloc(*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 |
| * dwSignature [I] Id of item to find |
| * |
| * RETURNS |
| * Success: A pointer to the list item found |
| * Failure: NULL |
| * |
| * NOTES |
| * See SHWriteDataBlockList. |
| */ |
| DATABLOCK_HEADER* WINAPI SHFindDataBlock(LPDBLIST lpList, DWORD dwSignature) |
| { |
| TRACE("(%p,%d)\n", lpList, dwSignature); |
| |
| if(lpList) |
| { |
| while(lpList->cbSize) |
| { |
| if(lpList->dwSignature == dwSignature) |
| return lpList; /* Matched */ |
| else if(lpList->dwSignature == CLIST_ID_CONTAINER && lpList[1].dwSignature == dwSignature) |
| return lpList + 1; /* Contained item matches */ |
| |
| lpList = NextItem(lpList); |
| } |
| } |
| return NULL; |
| } |