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