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