f90769eca75762e552d1db2f01caceebc92a2290
[reactos.git] / reactos / dll / win32 / shlwapi / clist.c
1 /*
2 * SHLWAPI DataBlock List functions
3 *
4 * Copyright 2002 Jon Griffiths
5 *
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.
10 *
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.
15 *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "precomp.h"
22
23 /* dwSignature for contained DATABLOCK_HEADER items */
24 #define CLIST_ID_CONTAINER (~0U)
25
26 /*************************************************************************
27 * NextItem
28 *
29 * Internal helper: move a DataBlock pointer to the next item.
30 */
31 static inline LPDATABLOCK_HEADER NextItem(LPDBLIST lpList)
32 {
33 char* address = (char*)lpList;
34 address += lpList->cbSize;
35 return (LPDATABLOCK_HEADER)address;
36 }
37
38 /*************************************************************************
39 * @ [SHLWAPI.20]
40 *
41 * Insert a new item into a DataBlock list.
42 *
43 * PARAMS
44 * lppList [0] Pointer to the List
45 * lpNewItem [I] The new item to add to the list
46 *
47 * RETURNS
48 * Success: S_OK. The item is added to the list.
49 * Failure: An HRESULT error code.
50 *
51 * NOTES
52 * If the size of the element to be inserted is less than the size of a
53 * DATABLOCK_HEADER node, or the Id for the item is CLIST_ID_CONTAINER,
54 * the call returns S_OK but does not actually add the element.
55 * See SHWriteDataBlockList.
56 */
57 #ifndef __REACTOS__
58 HRESULT
59 #else
60 BOOL
61 #endif
62 WINAPI SHAddDataBlock(LPDBLIST* lppList, const DATABLOCK_HEADER *lpNewItem)
63 {
64 LPDATABLOCK_HEADER lpInsertAt = NULL;
65 ULONG ulSize;
66
67 TRACE("(%p,%p)\n", lppList, lpNewItem);
68
69 if(!lppList || !lpNewItem )
70 #ifndef __REACTOS__
71 return E_INVALIDARG;
72 #else
73 return FALSE;
74 #endif
75
76 if (lpNewItem->cbSize < sizeof(DATABLOCK_HEADER) ||
77 lpNewItem->dwSignature == CLIST_ID_CONTAINER)
78 #ifndef __REACTOS__
79 return S_OK;
80 #else
81 return FALSE;
82 #endif
83
84 ulSize = lpNewItem->cbSize;
85
86 if(ulSize & 0x3)
87 {
88 /* Tune size to a ULONG boundary, add space for container element */
89 ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(DATABLOCK_HEADER);
90 TRACE("Creating container item, new size = %d\n", ulSize);
91 }
92
93 if(!*lppList)
94 {
95 /* An empty list. Allocate space for terminal ulSize also */
96 *lppList = LocalAlloc(LMEM_ZEROINIT, ulSize + sizeof(ULONG));
97 lpInsertAt = *lppList;
98 }
99 else
100 {
101 /* Append to the end of the list */
102 ULONG ulTotalSize = 0;
103 LPDATABLOCK_HEADER lpIter = *lppList;
104
105 /* Iterate to the end of the list, calculating the total size */
106 while (lpIter->cbSize)
107 {
108 ulTotalSize += lpIter->cbSize;
109 lpIter = NextItem(lpIter);
110 }
111
112 /* Increase the size of the list */
113 lpIter = LocalReAlloc(*lppList, ulTotalSize + ulSize+sizeof(ULONG),
114 LMEM_ZEROINIT | LMEM_MOVEABLE);
115 if(lpIter)
116 {
117 *lppList = lpIter;
118 lpInsertAt = (LPDATABLOCK_HEADER)((char*)lpIter + ulTotalSize); /* At end */
119 }
120 }
121
122 if(lpInsertAt)
123 {
124 /* Copy in the new item */
125 LPDATABLOCK_HEADER lpDest = lpInsertAt;
126
127 if(ulSize != lpNewItem->cbSize)
128 {
129 lpInsertAt->cbSize = ulSize;
130 lpInsertAt->dwSignature = CLIST_ID_CONTAINER;
131 lpDest++;
132 }
133 memcpy(lpDest, lpNewItem, lpNewItem->cbSize);
134
135 /* Terminate the list */
136 lpInsertAt = NextItem(lpInsertAt);
137 lpInsertAt->cbSize = 0;
138
139 #ifdef __REACTOS__
140 return TRUE;
141 #endif
142 }
143 #ifndef __REACTOS__
144 return S_OK;
145 #else
146 return FALSE;
147 #endif
148 }
149
150 /*************************************************************************
151 * @ [SHLWAPI.17]
152 *
153 * Write a DataBlock list to an IStream object.
154 *
155 * PARAMS
156 * lpStream [I] IStream object to write the list to
157 * lpList [I] List of items to write
158 *
159 * RETURNS
160 * Success: S_OK. The object is written to the stream.
161 * Failure: An HRESULT error code
162 *
163 * NOTES
164 * Ordinals 17,18,19,20,21 and 22 are related and together provide a compact
165 * list structure (a "DataBlock List"), which may be stored and retrieved from
166 * an IStream object.
167 *
168 * The exposed API consists of:
169 *
170 * - SHWriteDataBlockList() - Write a DataBlock list to a stream,
171 * - SHReadDataBlockList() - Read and create a list from a stream,
172 * - SHFreeDataBlockList() - Free a list,
173 * - SHAddDataBlock() - Insert a new item into a list,
174 * - SHRemoveDataBlock() - Remove an item from a list,
175 * - SHFindDataBlock() - Find an item in a list.
176 *
177 * The DataBlock list is stored packed into a memory array. Each element has a
178 * size and an associated ID. Elements must be less than 64k if the list is
179 * to be subsequently read from a stream.
180 *
181 * Elements are aligned on DWORD boundaries. If an elements data size is not
182 * a DWORD size multiple, the element is wrapped by inserting a surrounding
183 * element with an Id of 0xFFFFFFFF, and size sufficient to pad to a DWORD boundary.
184 *
185 * These functions are slow for large objects and long lists.
186 */
187 HRESULT WINAPI SHWriteDataBlockList(IStream* lpStream, LPDBLIST lpList)
188 {
189 ULONG ulSize;
190 HRESULT hRet = S_OK;
191
192 TRACE("(%p,%p)\n", lpStream, lpList);
193
194 if(lpList)
195 {
196 while (lpList->cbSize)
197 {
198 LPDATABLOCK_HEADER lpItem = lpList;
199
200 if(lpList->dwSignature == CLIST_ID_CONTAINER)
201 lpItem++;
202
203 hRet = IStream_Write(lpStream,lpItem,lpItem->cbSize,&ulSize);
204 if (FAILED(hRet))
205 return hRet;
206
207 if(lpItem->cbSize != ulSize)
208 return STG_E_MEDIUMFULL;
209
210 lpList = NextItem(lpList);
211 }
212 }
213
214 if(SUCCEEDED(hRet))
215 {
216 ULONG ulDummy;
217 ulSize = 0;
218
219 /* Write a terminating list entry with zero size */
220 hRet = IStream_Write(lpStream, &ulSize,sizeof(ulSize),&ulDummy);
221 }
222
223 return hRet;
224 }
225
226 /*************************************************************************
227 * @ [SHLWAPI.18]
228 *
229 * Read and create a DataBlock list from an IStream object.
230 *
231 * PARAMS
232 * lpStream [I] Stream to read the list from
233 * lppList [0] Pointer to receive the new List
234 *
235 * RETURNS
236 * Success: S_OK
237 * Failure: An HRESULT error code
238 *
239 * NOTES
240 * When read from a file, list objects are limited in size to 64k.
241 * See SHWriteDataBlockList.
242 */
243 HRESULT WINAPI SHReadDataBlockList(IStream* lpStream, LPDBLIST* lppList)
244 {
245 DATABLOCK_HEADER bBuff[128]; /* Temporary storage for new list item */
246 ULONG ulBuffSize = sizeof(bBuff);
247 LPDATABLOCK_HEADER pItem = bBuff;
248 ULONG ulRead, ulSize;
249 HRESULT hRet = S_OK;
250
251 TRACE("(%p,%p)\n", lpStream, lppList);
252
253 if(*lppList)
254 {
255 /* Free any existing list */
256 LocalFree(*lppList);
257 *lppList = NULL;
258 }
259
260 do
261 {
262 /* Read the size of the next item */
263 hRet = IStream_Read(lpStream, &ulSize,sizeof(ulSize),&ulRead);
264
265 if(FAILED(hRet) || ulRead != sizeof(ulSize) || !ulSize)
266 break; /* Read failed or read zero size (the end of the list) */
267
268 if(ulSize > 0xFFFF)
269 {
270 LARGE_INTEGER liZero;
271 ULARGE_INTEGER ulPos;
272
273 liZero.QuadPart = 0;
274
275 /* Back the stream up; this object is too big for the list */
276 if(SUCCEEDED(IStream_Seek(lpStream, liZero, STREAM_SEEK_CUR, &ulPos)))
277 {
278 liZero.QuadPart = ulPos.QuadPart - sizeof(ULONG);
279 IStream_Seek(lpStream, liZero, STREAM_SEEK_SET, NULL);
280 }
281 break;
282 }
283 else if (ulSize >= sizeof(DATABLOCK_HEADER))
284 {
285 /* Add this new item to the list */
286 if(ulSize > ulBuffSize)
287 {
288 /* We need more buffer space, allocate it */
289 LPDATABLOCK_HEADER lpTemp;
290
291 if (pItem == bBuff)
292 lpTemp = LocalAlloc(LMEM_ZEROINIT, ulSize);
293 else
294 lpTemp = LocalReAlloc(pItem, ulSize, LMEM_ZEROINIT|LMEM_MOVEABLE);
295
296 if(!lpTemp)
297 {
298 hRet = E_OUTOFMEMORY;
299 break;
300 }
301 ulBuffSize = ulSize;
302 pItem = lpTemp;
303 }
304
305 pItem->cbSize = ulSize;
306 ulSize -= sizeof(pItem->cbSize); /* already read this member */
307
308 /* Read the item Id and data */
309 hRet = IStream_Read(lpStream, &pItem->dwSignature, ulSize, &ulRead);
310
311 if(FAILED(hRet) || ulRead != ulSize)
312 break;
313
314 SHAddDataBlock(lppList, pItem); /* Insert Item */
315 }
316 } while(1);
317
318 /* If we allocated space, free it */
319 if(pItem != bBuff)
320 LocalFree(pItem);
321
322 return hRet;
323 }
324
325 /*************************************************************************
326 * @ [SHLWAPI.19]
327 *
328 * Free a DataBlock list.
329 *
330 * PARAMS
331 * lpList [I] List to free
332 *
333 * RETURNS
334 * Nothing.
335 *
336 * NOTES
337 * See SHWriteDataBlockList.
338 */
339 VOID WINAPI SHFreeDataBlockList(LPDBLIST lpList)
340 {
341 TRACE("(%p)\n", lpList);
342
343 if (lpList)
344 LocalFree(lpList);
345 }
346
347 /*************************************************************************
348 * @ [SHLWAPI.21]
349 *
350 * Remove an item from a DataBlock list.
351 *
352 * PARAMS
353 * lppList [O] List to remove the item from
354 * dwSignature [I] Id of item to remove
355 *
356 * RETURNS
357 * Success: TRUE.
358 * Failure: FALSE, If any parameters are invalid, or the item was not found.
359 *
360 * NOTES
361 * See SHWriteDataBlockList.
362 */
363 BOOL WINAPI SHRemoveDataBlock(LPDBLIST* lppList, DWORD dwSignature)
364 {
365 #ifndef __REACTOS__
366 LPDATABLOCK_HEADER lpList = 0;
367 #else
368 LPDATABLOCK_HEADER lpList = NULL;
369 #endif
370 LPDATABLOCK_HEADER lpItem = NULL;
371 LPDATABLOCK_HEADER lpNext;
372 ULONG ulNewSize;
373
374 TRACE("(%p,%d)\n", lppList, dwSignature);
375
376 if(lppList && (lpList = *lppList))
377 {
378 /* Search for item in list */
379 while (lpList->cbSize)
380 {
381 if(lpList->dwSignature == dwSignature ||
382 (lpList->dwSignature == CLIST_ID_CONTAINER && lpList[1].dwSignature == dwSignature))
383 {
384 lpItem = lpList; /* Found */
385 break;
386 }
387 lpList = NextItem(lpList);
388 }
389 }
390
391 if(!lpItem)
392 return FALSE;
393
394 lpList = lpNext = NextItem(lpItem);
395
396 /* Locate the end of the list */
397 while (lpList->cbSize)
398 lpList = NextItem(lpList);
399
400 /* Resize the list */
401 ulNewSize = LocalSize(*lppList) - lpItem->cbSize;
402
403 /* Copy following elements over lpItem */
404 memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG));
405
406 if(ulNewSize <= sizeof(ULONG))
407 {
408 LocalFree(*lppList);
409 *lppList = NULL; /* Removed the last element */
410 }
411 else
412 {
413 lpList = LocalReAlloc(*lppList, ulNewSize, LMEM_ZEROINIT|LMEM_MOVEABLE);
414 if(lpList)
415 *lppList = lpList;
416 }
417 return TRUE;
418 }
419
420 /*************************************************************************
421 * @ [SHLWAPI.22]
422 *
423 * Find an item in a DataBlock list.
424 *
425 * PARAMS
426 * lpList [I] List to search
427 * dwSignature [I] Id of item to find
428 *
429 * RETURNS
430 * Success: A pointer to the list item found
431 * Failure: NULL
432 *
433 * NOTES
434 * See SHWriteDataBlockList.
435 */
436 DATABLOCK_HEADER* WINAPI SHFindDataBlock(LPDBLIST lpList, DWORD dwSignature)
437 {
438 TRACE("(%p,%d)\n", lpList, dwSignature);
439
440 if(lpList)
441 {
442 while(lpList->cbSize)
443 {
444 if(lpList->dwSignature == dwSignature)
445 return lpList; /* Matched */
446 else if(lpList->dwSignature == CLIST_ID_CONTAINER && lpList[1].dwSignature == dwSignature)
447 return lpList + 1; /* Contained item matches */
448
449 lpList = NextItem(lpList);
450 }
451 }
452 return NULL;
453 }