clist.c 11.2 KB
Newer Older
1
/*
Jon Griffiths's avatar
Jon Griffiths committed
2
 * SHLWAPI DataBlock List functions
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * Copyright 2002 Jon Griffiths
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22
#include <string.h>

23 24
#define COBJMACROS

25
#include "windef.h"
26
#include "winbase.h"
27
#include "winuser.h"
28
#include "objbase.h"
29
#include "shlobj.h"
30 31 32 33
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(shell);

34
/* dwSignature for contained DATABLOCK_HEADER items */
35
#define CLIST_ID_CONTAINER (~0U)
36 37 38 39

/*************************************************************************
 * NextItem
 *
Jon Griffiths's avatar
Jon Griffiths committed
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 46
  address += lpList->cbSize;
  return (LPDATABLOCK_HEADER)address;
47 48
}

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
/*************************************************************************
 *      @	[SHLWAPI.20]
 *
 * Insert a new item into a DataBlock list.
 *
 * PARAMS
 *  lppList   [0] Pointer to the List
 *  lpNewItem [I] The new item to add to the list
 *
 * RETURNS
 *  Success: S_OK. The item is added to the list.
 *  Failure: An HRESULT error code.
 *
 * NOTES
 *  If the size of the element to be inserted is less than the size of a
 *  DATABLOCK_HEADER node, or the Id for the item is CLIST_ID_CONTAINER,
 *  the call returns S_OK but does not actually add the element.
 *  See SHWriteDataBlockList.
 */
HRESULT WINAPI SHAddDataBlock(LPDBLIST* lppList, const DATABLOCK_HEADER *lpNewItem)
{
  LPDATABLOCK_HEADER lpInsertAt = NULL;
  ULONG ulSize;

  TRACE("(%p,%p)\n", lppList, lpNewItem);

  if(!lppList || !lpNewItem )
    return E_INVALIDARG;

  if (lpNewItem->cbSize < sizeof(DATABLOCK_HEADER) ||
      lpNewItem->dwSignature == CLIST_ID_CONTAINER)
    return S_OK;

  ulSize = lpNewItem->cbSize;

  if(ulSize & 0x3)
  {
    /* Tune size to a ULONG boundary, add space for container element */
    ulSize = ((ulSize + 0x3) & 0xFFFFFFFC) + sizeof(DATABLOCK_HEADER);
    TRACE("Creating container item, new size = %d\n", ulSize);
  }

  if(!*lppList)
  {
    /* An empty list. Allocate space for terminal ulSize also */
94
    *lppList = LocalAlloc(LMEM_ZEROINIT, ulSize + sizeof(ULONG));
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
    lpInsertAt = *lppList;
  }
  else
  {
    /* Append to the end of the list */
    ULONG ulTotalSize = 0;
    LPDATABLOCK_HEADER lpIter = *lppList;

    /* Iterate to the end of the list, calculating the total size */
    while (lpIter->cbSize)
    {
      ulTotalSize += lpIter->cbSize;
      lpIter = NextItem(lpIter);
    }

    /* Increase the size of the list */
111
    lpIter = LocalReAlloc(*lppList, ulTotalSize + ulSize+sizeof(ULONG),
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
                                          LMEM_ZEROINIT | LMEM_MOVEABLE);
    if(lpIter)
    {
      *lppList = lpIter;
      lpInsertAt = (LPDATABLOCK_HEADER)((char*)lpIter + ulTotalSize); /* At end */
    }
  }

  if(lpInsertAt)
  {
    /* Copy in the new item */
    LPDATABLOCK_HEADER lpDest = lpInsertAt;

    if(ulSize != lpNewItem->cbSize)
    {
      lpInsertAt->cbSize = ulSize;
      lpInsertAt->dwSignature = CLIST_ID_CONTAINER;
      lpDest++;
    }
    memcpy(lpDest, lpNewItem, lpNewItem->cbSize);

    /* Terminate the list */
    lpInsertAt = NextItem(lpInsertAt);
    lpInsertAt->cbSize = 0;

    return lpNewItem->cbSize;
  }
  return S_OK;
}

142 143 144
/*************************************************************************
 *      @	[SHLWAPI.17]
 *
Jon Griffiths's avatar
Jon Griffiths committed
145
 * Write a DataBlock list to an IStream object.
146 147
 *
 * PARAMS
148
 *  lpStream  [I] IStream object to write the list to
149 150 151
 *  lpList    [I] List of items to write
 *
 * RETURNS
152
 *  Success: S_OK. The object is written to the stream.
153 154 155 156
 *  Failure: An HRESULT error code
 *
 * NOTES
 *  Ordinals 17,18,19,20,21 and 22 are related and together provide a compact
Jon Griffiths's avatar
Jon Griffiths committed
157 158
 *  list structure (a "DataBlock List"), which may be stored and retrieved from
 *  an IStream object.
159 160
 *
 *  The exposed API consists of:
161
 *
Jon Griffiths's avatar
Jon Griffiths committed
162 163 164 165 166 167
 *  - SHWriteDataBlockList() - Write a DataBlock list to a stream,
 *  - SHReadDataBlockList() - Read and create a list from a stream,
 *  - SHFreeDataBlockList() - Free a list,
 *  - SHAddDataBlock() - Insert a new item into a list,
 *  - SHRemoveDataBlock() - Remove an item from a list,
 *  - SHFindDataBlock() - Find an item in a list.
168
 *
Jon Griffiths's avatar
Jon Griffiths committed
169
 *  The DataBlock list is stored packed into a memory array. Each element has a
170 171 172 173 174
 *  size and an associated ID. Elements must be less than 64k if the list is
 *  to be subsequently read from a stream.
 *
 *  Elements are aligned on DWORD boundaries. If an elements data size is not
 *  a DWORD size multiple, the element is wrapped by inserting a surrounding
Jon Griffiths's avatar
Jon Griffiths committed
175
 *  element with an Id of 0xFFFFFFFF, and size sufficient to pad to a DWORD boundary.
176 177 178
 *
 *  These functions are slow for large objects and long lists.
 */
179
HRESULT WINAPI SHWriteDataBlockList(IStream* lpStream, LPDBLIST lpList)
180 181
{
  ULONG ulSize;
182
  HRESULT hRet = S_OK;
183 184 185 186 187

  TRACE("(%p,%p)\n", lpStream, lpList);

  if(lpList)
  {
188
    while (lpList->cbSize)
189
    {
190
      LPDATABLOCK_HEADER lpItem = lpList;
191

192
      if(lpList->dwSignature == CLIST_ID_CONTAINER)
193 194
        lpItem++;

195
      hRet = IStream_Write(lpStream,lpItem,lpItem->cbSize,&ulSize);
196 197 198
      if (FAILED(hRet))
        return hRet;

199
      if(lpItem->cbSize != ulSize)
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
        return STG_E_MEDIUMFULL;

      lpList = NextItem(lpList);
    }
  }

  if(SUCCEEDED(hRet))
  {
    ULONG ulDummy;
    ulSize = 0;

    /* Write a terminating list entry with zero size */
    hRet = IStream_Write(lpStream, &ulSize,sizeof(ulSize),&ulDummy);
  }

  return hRet;
}

/*************************************************************************
 *      @	[SHLWAPI.18]
 *
Jon Griffiths's avatar
Jon Griffiths committed
221
 * Read and create a DataBlock list from an IStream object.
222 223 224
 *
 * PARAMS
 *  lpStream  [I] Stream to read the list from
Francois Gouget's avatar
Francois Gouget committed
225
 *  lppList   [0] Pointer to receive the new List
226 227 228 229 230 231 232
 *
 * RETURNS
 *  Success: S_OK
 *  Failure: An HRESULT error code
 *
 * NOTES
 *  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 241 242 243 244 245 246 247
  ULONG ulRead, ulSize;
  HRESULT hRet = S_OK;

  TRACE("(%p,%p)\n", lpStream, lppList);

  if(*lppList)
  {
    /* Free any existing list */
248
    LocalFree(*lppList);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
    *lppList = NULL;
  }

  do
  {
    /* Read the size of the next item */
    hRet = IStream_Read(lpStream, &ulSize,sizeof(ulSize),&ulRead);

    if(FAILED(hRet) || ulRead != sizeof(ulSize) || !ulSize)
      break; /* Read failed or read zero size (the end of the list) */

    if(ulSize > 0xFFFF)
    {
      LARGE_INTEGER liZero;
      ULARGE_INTEGER ulPos;

      liZero.QuadPart = 0;

      /* Back the stream up; this object is too big for the list */
      if(SUCCEEDED(IStream_Seek(lpStream, liZero, STREAM_SEEK_CUR, &ulPos)))
      {
        liZero.QuadPart = ulPos.QuadPart - sizeof(ULONG);
        IStream_Seek(lpStream, liZero, STREAM_SEEK_SET, NULL);
      }
      break;
    }
275
    else if (ulSize >= sizeof(DATABLOCK_HEADER))
276 277 278 279 280
    {
      /* Add this new item to the list */
      if(ulSize > ulBuffSize)
      {
        /* 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 289 290 291 292 293 294 295 296

        if(!lpTemp)
        {
          hRet = E_OUTOFMEMORY;
          break;
        }
        ulBuffSize = ulSize;
        pItem = lpTemp;
      }

297 298
      pItem->cbSize = ulSize;
      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 304 305

      if(FAILED(hRet) || ulRead != ulSize)
        break;

306
      SHAddDataBlock(lppList, pItem); /* Insert Item */
307 308 309 310 311
    }
  } while(1);

  /* If we allocated space, free it */
  if(pItem != bBuff)
312
    LocalFree(pItem);
313 314 315 316 317 318 319

  return hRet;
}

/*************************************************************************
 *      @	[SHLWAPI.19]
 *
Jon Griffiths's avatar
Jon Griffiths committed
320
 * Free a DataBlock list.
321 322 323 324 325 326
 *
 * PARAMS
 *  lpList [I] List to free
 *
 * RETURNS
 *  Nothing.
327 328
 *
 * NOTES
329
 *  See SHWriteDataBlockList.
330
 */
331
VOID WINAPI SHFreeDataBlockList(LPDBLIST lpList)
332 333 334 335
{
  TRACE("(%p)\n", lpList);

  if (lpList)
336
    LocalFree(lpList);
337 338 339 340 341
}

/*************************************************************************
 *      @	[SHLWAPI.21]
 *
Jon Griffiths's avatar
Jon Griffiths committed
342
 * Remove an item from a DataBlock list.
343 344
 *
 * PARAMS
345 346
 *  lppList     [O] List to remove the item from
 *  dwSignature [I] Id of item to remove
347 348 349 350
 *
 * RETURNS
 *  Success: TRUE.
 *  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 358 359
  LPDATABLOCK_HEADER lpList = 0;
  LPDATABLOCK_HEADER lpItem = NULL;
  LPDATABLOCK_HEADER lpNext;
360 361
  ULONG ulNewSize;

362
  TRACE("(%p,%d)\n", lppList, dwSignature);
363 364 365 366

  if(lppList && (lpList = *lppList))
  {
    /* Search for item in list */
367
    while (lpList->cbSize)
368
    {
369 370
      if(lpList->dwSignature == dwSignature ||
        (lpList->dwSignature == CLIST_ID_CONTAINER && lpList[1].dwSignature == dwSignature))
371 372 373 374 375 376 377 378 379 380 381 382 383 384
      {
        lpItem = lpList; /* Found */
        break;
      }
      lpList = NextItem(lpList);
    }
  }

  if(!lpItem)
    return FALSE;

  lpList = lpNext = NextItem(lpItem);

  /* Locate the end of the list */
385
  while (lpList->cbSize)
386 387 388
    lpList = NextItem(lpList);

  /* Resize the list */
389
  ulNewSize = LocalSize(*lppList) - lpItem->cbSize;
390 391 392 393 394 395

  /* Copy following elements over lpItem */
  memmove(lpItem, lpNext, (char *)lpList - (char *)lpNext + sizeof(ULONG));

  if(ulNewSize <= sizeof(ULONG))
  {
396
    LocalFree(*lppList);
397 398 399 400
    *lppList = NULL; /* Removed the last element */
  }
  else
  {
401
    lpList = LocalReAlloc(*lppList, ulNewSize, LMEM_ZEROINIT|LMEM_MOVEABLE);
402 403 404 405 406 407 408 409 410
    if(lpList)
      *lppList = lpList;
  }
  return TRUE;
}

/*************************************************************************
 *      @	[SHLWAPI.22]
 *
Jon Griffiths's avatar
Jon Griffiths committed
411
 * Find an item in a DataBlock list.
412 413
 *
 * PARAMS
414 415
 *  lpList      [I] List to search
 *  dwSignature [I] Id of item to find
416 417 418 419
 *
 * RETURNS
 *  Success: A pointer to the list item found
 *  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 429

  if(lpList)
  {
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 436 437 438 439 440 441
        return lpList + 1; /* Contained item matches */

      lpList = NextItem(lpList);
    }
  }
  return NULL;
}