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