Implement/document @17,18,19,20,21,22 (Compact list API).
[wine.git] / dlls / shlwapi / clist.c
blob27084719f22a97b95d2dbec02d50e881c9a7f937
1 /*
2 * SHLWAPI Compact List functions
4 * Copyright 2002 Jon Griffiths
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <string.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "wine/obj_base.h"
26 #include "wine/obj_storage.h"
27 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(shell);
31 /* Compact list element (ordinals 17-22) */
32 typedef struct tagSHLWAPI_CLIST
34 ULONG ulSize; /* Size of this list element and its data */
35 ULONG ulId; /* If -1, The real element follows */
36 /* Item data (or a contained SHLWAPI_CLIST) follows... */
37 } SHLWAPI_CLIST, *LPSHLWAPI_CLIST;
39 typedef const SHLWAPI_CLIST* LPCSHLWAPI_CLIST;
41 /* ulId for contained SHLWAPI_CLIST items */
42 static const ULONG CLIST_ID_CONTAINER = -1u;
44 HRESULT WINAPI SHLWAPI_20(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST);
46 /*************************************************************************
47 * NextItem
49 * Internal helper: move a clist pointer to the next item.
51 inline static LPSHLWAPI_CLIST NextItem(LPCSHLWAPI_CLIST lpList)
53 const char* address = (char*)lpList;
54 address += lpList->ulSize;
55 return (LPSHLWAPI_CLIST)address;
58 /*************************************************************************
59 * @ [SHLWAPI.17]
61 * Write a compact list to a stream.
63 * PARAMS
64 * lpStream [I] Stream to write the list to
65 * lpList [I] List of items to write
67 * RETURNS
68 * Success: S_OK
69 * Failure: An HRESULT error code
71 * NOTES
72 * Ordinals 17,18,19,20,21 and 22 are related and together provide a compact
73 * list structure which may be stored and retrieved from a stream.
75 * The exposed API consists of:
76 * @17 Write a compact list to a stream
77 * @18 Read and create a list from a stream
78 * @19 Free a list
79 * @20 Insert a new item into a list
80 * @21 Remove an item from a list
81 * @22 Find an item in a list
83 * The compact list is stored packed into a memory array. Each element has a
84 * size and an associated ID. Elements must be less than 64k if the list is
85 * to be subsequently read from a stream.
87 * Elements are aligned on DWORD boundaries. If an elements data size is not
88 * a DWORD size multiple, the element is wrapped by inserting a surrounding
89 * element with an Id of -1, and size sufficient to pad to a DWORD boundary.
91 * These functions are slow for large objects and long lists.
93 HRESULT WINAPI SHLWAPI_17(IStream* lpStream, LPSHLWAPI_CLIST lpList)
95 ULONG ulSize;
96 HRESULT hRet = E_FAIL;
98 TRACE("(%p,%p)\n", lpStream, lpList);
100 if(lpList)
102 while (lpList->ulSize)
104 LPSHLWAPI_CLIST lpItem = lpList;
106 if(lpList->ulId == CLIST_ID_CONTAINER)
107 lpItem++;
109 hRet = IStream_Write(lpStream,lpItem,lpItem->ulSize,&ulSize);
110 if (FAILED(hRet))
111 return hRet;
113 if(lpItem->ulSize != ulSize)
114 return STG_E_MEDIUMFULL;
116 lpList = NextItem(lpList);
120 if(SUCCEEDED(hRet))
122 ULONG ulDummy;
123 ulSize = 0;
125 /* Write a terminating list entry with zero size */
126 hRet = IStream_Write(lpStream, &ulSize,sizeof(ulSize),&ulDummy);
129 return hRet;
132 /*************************************************************************
133 * @ [SHLWAPI.18]
135 * Read and create a compact list from a stream
137 * PARAMS
138 * lpStream [I] Stream to read the list from
139 * lppList [0] Pointer to recieve the new List
141 * RETURNS
142 * Success: S_OK
143 * Failure: An HRESULT error code
145 * NOTES
146 * When read from a file, list objects are limited in size to 64k.
148 HRESULT WINAPI SHLWAPI_18(IStream* lpStream, LPSHLWAPI_CLIST* lppList)
150 SHLWAPI_CLIST bBuff[128]; /* Temporary storage for new list item */
151 ULONG ulBuffSize = sizeof(bBuff);
152 LPSHLWAPI_CLIST pItem = bBuff;
153 ULONG ulRead, ulSize;
154 HRESULT hRet = S_OK;
156 TRACE("(%p,%p)\n", lpStream, lppList);
158 if(*lppList)
160 /* Free any existing list */
161 LocalFree((HLOCAL)*lppList);
162 *lppList = NULL;
167 /* Read the size of the next item */
168 hRet = IStream_Read(lpStream, &ulSize,sizeof(ulSize),&ulRead);
170 if(FAILED(hRet) || ulRead != sizeof(ulSize) || !ulSize)
171 break; /* Read failed or read zero size (the end of the list) */
173 if(ulSize > 0xFFFF)
175 LARGE_INTEGER liZero;
176 ULARGE_INTEGER ulPos;
178 liZero.QuadPart = 0;
180 /* Back the stream up; this object is too big for the list */
181 if(SUCCEEDED(IStream_Seek(lpStream, liZero, STREAM_SEEK_CUR, &ulPos)))
183 liZero.QuadPart = ulPos.QuadPart - sizeof(ULONG);
184 IStream_Seek(lpStream, liZero, STREAM_SEEK_SET, NULL);
186 break;
188 else if (ulSize >= sizeof(SHLWAPI_CLIST))
190 /* Add this new item to the list */
191 if(ulSize > ulBuffSize)
193 /* We need more buffer space, allocate it */
194 LPSHLWAPI_CLIST lpTemp;
196 if (pItem == bBuff)
197 lpTemp = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT, ulSize);
198 else
199 lpTemp = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)pItem, ulSize,
200 LMEM_ZEROINIT|LMEM_MOVEABLE);
202 if(!lpTemp)
204 hRet = E_OUTOFMEMORY;
205 break;
207 ulBuffSize = ulSize;
208 pItem = lpTemp;
211 pItem->ulSize = ulSize;
212 ulSize -= sizeof(pItem->ulSize); /* already read this member */
214 /* Read the item Id and data */
215 hRet = IStream_Read(lpStream, &pItem->ulId, ulSize, &ulRead);
217 if(FAILED(hRet) || ulRead != ulSize)
218 break;
220 SHLWAPI_20(lppList, pItem); /* Insert Item */
222 } while(1);
224 /* If we allocated space, free it */
225 if(pItem != bBuff)
226 LocalFree((HLOCAL)pItem);
228 return hRet;
231 /*************************************************************************
232 * @ [SHLWAPI.19]
234 * Free a compact list.
236 * PARAMS
237 * lpList [I] List to free
239 * RETURNS
240 * Nothing.
242 VOID WINAPI SHLWAPI_19(LPSHLWAPI_CLIST lpList)
244 TRACE("(%p)\n", lpList);
246 if (lpList)
247 LocalFree((HLOCAL)lpList);
250 /*************************************************************************
251 * @ [SHLWAPI.20]
253 * Insert a new item into a compact list.
255 * PARAMS
256 * lppList [0] Pointer to the List
257 * lpNewItem [I] The new item to add to the list
259 * RETURNS
260 * Success: The size of the inserted item.
261 * Failure: An HRESULT error code.
263 HRESULT WINAPI SHLWAPI_20(LPSHLWAPI_CLIST* lppList, LPCSHLWAPI_CLIST lpNewItem)
265 LPSHLWAPI_CLIST lpInsertAt = NULL;
266 ULONG ulSize;
268 TRACE("(%p,%p)\n", lppList, lpNewItem);
270 if(!lppList || !lpNewItem ||
271 lpNewItem->ulId == CLIST_ID_CONTAINER ||
272 lpNewItem->ulSize < sizeof(SHLWAPI_CLIST))
273 return E_INVALIDARG;
275 ulSize = lpNewItem->ulSize;
277 if(ulSize & 0x3)
279 /* Tune size to a ULONG boundary, add space for container element */
280 ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(SHLWAPI_CLIST);
281 TRACE("Creating container item, new size = %ld\n", ulSize);
284 if(!*lppList)
286 /* An empty list. Allocate space for terminal ulSize also */
287 *lppList = (LPSHLWAPI_CLIST)LocalAlloc(LMEM_ZEROINIT,
288 ulSize + sizeof(ULONG));
289 lpInsertAt = *lppList;
291 else
293 /* Append to the end of the list */
294 ULONG ulTotalSize = 0;
295 LPSHLWAPI_CLIST lpIter = *lppList;
297 /* Iterate to the end of the list, calculating the total size */
298 while (lpIter->ulSize)
300 ulTotalSize += lpIter->ulSize;
301 lpIter = NextItem(lpIter);
304 /* Increase the size of the list */
305 lpIter = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList,
306 ulTotalSize + ulSize+sizeof(ULONG),
307 LMEM_ZEROINIT | LMEM_MOVEABLE);
308 if(lpIter)
310 *lppList = lpIter;
311 lpInsertAt = (LPSHLWAPI_CLIST)((char*)lpIter + ulTotalSize); /* At end */
315 if(lpInsertAt)
317 /* Copy in the new item */
318 LPSHLWAPI_CLIST lpDest = lpInsertAt;
320 if(ulSize != lpNewItem->ulSize)
322 lpInsertAt->ulSize = ulSize;
323 lpInsertAt->ulId = CLIST_ID_CONTAINER;
324 lpDest++;
326 memcpy(lpDest, lpNewItem, lpNewItem->ulSize);
328 /* Terminate the list */
329 lpInsertAt = NextItem(lpInsertAt);
330 lpInsertAt->ulSize = 0;
332 return lpNewItem->ulSize;
334 return S_OK;
337 /*************************************************************************
338 * @ [SHLWAPI.21]
340 * Remove an item from a compact list.
342 * PARAMS
343 * lppList [O] List to remove the item from
344 * ulId [I] Id of item to remove
346 * RETURNS
347 * Success: TRUE.
348 * Failure: FALSE, If any parameters are invalid, or the item was not found.
350 BOOL WINAPI SHLWAPI_21(LPSHLWAPI_CLIST* lppList, ULONG ulId)
352 LPSHLWAPI_CLIST lpList = 0;
353 LPSHLWAPI_CLIST lpItem = NULL;
354 LPSHLWAPI_CLIST lpNext;
355 ULONG ulNewSize;
357 TRACE("(%p,%ld)\n", lppList, ulId);
359 if(lppList && (lpList = *lppList))
361 /* Search for item in list */
362 while (lpList->ulSize)
364 if(lpList->ulId == ulId ||
365 (lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId))
367 lpItem = lpList; /* Found */
368 break;
370 lpList = NextItem(lpList);
374 if(!lpItem)
375 return FALSE;
377 lpList = lpNext = NextItem(lpItem);
379 /* Locate the end of the list */
380 while (lpList->ulSize)
381 lpList = NextItem(lpList);
383 /* Resize the list */
384 ulNewSize = LocalSize((HLOCAL)*lppList) - lpItem->ulSize;
386 /* Copy following elements over lpItem */
387 memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG));
389 if(ulNewSize <= sizeof(ULONG))
391 LocalFree((HLOCAL)*lppList);
392 *lppList = NULL; /* Removed the last element */
394 else
396 lpList = (LPSHLWAPI_CLIST)LocalReAlloc((HLOCAL)*lppList, ulNewSize,
397 LMEM_ZEROINIT|LMEM_MOVEABLE);
398 if(lpList)
399 *lppList = lpList;
401 return TRUE;
404 /*************************************************************************
405 * @ [SHLWAPI.22]
407 * Find an item in a compact list.
409 * PARAMS
410 * lpList [I] List to search
411 * ulId [I] ID of item to find
413 * RETURNS
414 * Success: A pointer to the list item found
415 * Failure: NULL
417 LPSHLWAPI_CLIST WINAPI SHLWAPI_22(LPSHLWAPI_CLIST lpList, ULONG ulId)
419 TRACE("(%p,%ld)\n", lpList, ulId);
421 if(lpList)
423 while(lpList->ulSize)
425 if(lpList->ulId == ulId)
426 return lpList; /* Matched */
427 else if(lpList->ulId == CLIST_ID_CONTAINER && lpList[1].ulId == ulId)
428 return lpList + 1; /* Contained item matches */
430 lpList = NextItem(lpList);
433 return NULL;