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