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