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