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