Avoid some non-portable makefile constructs, and get rid of the
[wine] / dlls / ole32 / stg_stream.c
1 /*
2  * Compound Storage (32 bit version)
3  * Stream implementation
4  *
5  * This file contains the implementation of the stream interface
6  * for streams contained in a compound storage.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Thuy Nguyen
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winreg.h"
38 #include "winternl.h"
39 #include "wine/debug.h"
40
41 #include "storage32.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(storage);
44
45
46 /*
47  * Virtual function table for the StgStreamImpl class.
48  */
49 static ICOM_VTABLE(IStream) StgStreamImpl_Vtbl =
50 {
51     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
52     StgStreamImpl_QueryInterface,
53     StgStreamImpl_AddRef,
54     StgStreamImpl_Release,
55     StgStreamImpl_Read,
56     StgStreamImpl_Write,
57     StgStreamImpl_Seek,
58     StgStreamImpl_SetSize,
59     StgStreamImpl_CopyTo,
60     StgStreamImpl_Commit,
61     StgStreamImpl_Revert,
62     StgStreamImpl_LockRegion,
63     StgStreamImpl_UnlockRegion,
64     StgStreamImpl_Stat,
65     StgStreamImpl_Clone
66 };
67
68 /******************************************************************************
69 ** StgStreamImpl implementation
70 */
71
72 /***
73  * This is the constructor for the StgStreamImpl class.
74  *
75  * Params:
76  *    parentStorage - Pointer to the storage that contains the stream to open
77  *    ownerProperty - Index of the property that points to this stream.
78  */
79 StgStreamImpl* StgStreamImpl_Construct(
80                 StorageBaseImpl* parentStorage,
81     DWORD            grfMode,
82     ULONG            ownerProperty)
83 {
84   StgStreamImpl* newStream;
85
86   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
87
88   if (newStream!=0)
89   {
90     /*
91      * Set-up the virtual function table and reference count.
92      */
93     newStream->lpVtbl    = &StgStreamImpl_Vtbl;
94     newStream->ref       = 0;
95
96     /*
97      * We want to nail-down the reference to the storage in case the
98      * stream out-lives the storage in the client application.
99      */
100     newStream->parentStorage = parentStorage;
101     IStorage_AddRef((IStorage*)newStream->parentStorage);
102
103     newStream->grfMode = grfMode;
104     newStream->ownerProperty = ownerProperty;
105
106     /*
107      * Start the stream at the beginning.
108      */
109     newStream->currentPosition.u.HighPart = 0;
110     newStream->currentPosition.u.LowPart = 0;
111
112     /*
113      * Initialize the rest of the data.
114      */
115     newStream->streamSize.u.HighPart = 0;
116     newStream->streamSize.u.LowPart  = 0;
117     newStream->bigBlockChain       = 0;
118     newStream->smallBlockChain     = 0;
119
120     /*
121      * Read the size from the property and determine if the blocks forming
122      * this stream are large or small.
123      */
124     StgStreamImpl_OpenBlockChain(newStream);
125   }
126
127   return newStream;
128 }
129
130 /***
131  * This is the destructor of the StgStreamImpl class.
132  *
133  * This method will clean-up all the resources used-up by the given StgStreamImpl
134  * class. The pointer passed-in to this function will be freed and will not
135  * be valid anymore.
136  */
137 void StgStreamImpl_Destroy(StgStreamImpl* This)
138 {
139   TRACE("(%p)\n", This);
140
141   /*
142    * Release the reference we are holding on the parent storage.
143    */
144   IStorage_Release((IStorage*)This->parentStorage);
145   This->parentStorage = 0;
146
147   /*
148    * Make sure we clean-up the block chain stream objects that we were using.
149    */
150   if (This->bigBlockChain != 0)
151   {
152     BlockChainStream_Destroy(This->bigBlockChain);
153     This->bigBlockChain = 0;
154   }
155
156   if (This->smallBlockChain != 0)
157   {
158     SmallBlockChainStream_Destroy(This->smallBlockChain);
159     This->smallBlockChain = 0;
160   }
161
162   /*
163    * Finally, free the memory used-up by the class.
164    */
165   HeapFree(GetProcessHeap(), 0, This);
166 }
167
168 /***
169  * This implements the IUnknown method QueryInterface for this
170  * class
171  */
172 HRESULT WINAPI StgStreamImpl_QueryInterface(
173                   IStream*     iface,
174                   REFIID         riid,        /* [in] */
175                   void**         ppvObject)   /* [iid_is][out] */
176 {
177   StgStreamImpl* const This=(StgStreamImpl*)iface;
178
179   /*
180    * Perform a sanity check on the parameters.
181    */
182   if (ppvObject==0)
183     return E_INVALIDARG;
184
185   /*
186    * Initialize the return parameter.
187    */
188   *ppvObject = 0;
189
190   /*
191    * Compare the riid with the interface IDs implemented by this object.
192    */
193   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
194   {
195     *ppvObject = (IStream*)This;
196   }
197   else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
198   {
199     *ppvObject = (IStream*)This;
200   }
201
202   /*
203    * Check that we obtained an interface.
204    */
205   if ((*ppvObject)==0)
206     return E_NOINTERFACE;
207
208   /*
209    * Query Interface always increases the reference count by one when it is
210    * successful
211    */
212   StgStreamImpl_AddRef(iface);
213
214   return S_OK;
215 }
216
217 /***
218  * This implements the IUnknown method AddRef for this
219  * class
220  */
221 ULONG WINAPI StgStreamImpl_AddRef(
222                 IStream* iface)
223 {
224   StgStreamImpl* const This=(StgStreamImpl*)iface;
225
226   This->ref++;
227
228   return This->ref;
229 }
230
231 /***
232  * This implements the IUnknown method Release for this
233  * class
234  */
235 ULONG WINAPI StgStreamImpl_Release(
236                 IStream* iface)
237 {
238   StgStreamImpl* const This=(StgStreamImpl*)iface;
239
240   ULONG newRef;
241
242   This->ref--;
243
244   newRef = This->ref;
245
246   /*
247    * If the reference count goes down to 0, perform suicide.
248    */
249   if (newRef==0)
250   {
251     StgStreamImpl_Destroy(This);
252   }
253
254   return newRef;
255 }
256
257 /***
258  * This method will open the block chain pointed by the property
259  * that describes the stream.
260  * If the stream's size is null, no chain is opened.
261  */
262 void StgStreamImpl_OpenBlockChain(
263         StgStreamImpl* This)
264 {
265   StgProperty    curProperty;
266   BOOL         readSucessful;
267
268   /*
269    * Make sure no old object is left over.
270    */
271   if (This->smallBlockChain != 0)
272   {
273     SmallBlockChainStream_Destroy(This->smallBlockChain);
274     This->smallBlockChain = 0;
275   }
276
277   if (This->bigBlockChain != 0)
278   {
279     BlockChainStream_Destroy(This->bigBlockChain);
280     This->bigBlockChain = 0;
281   }
282
283   /*
284    * Read the information from the property.
285    */
286   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
287                                              This->ownerProperty,
288                                              &curProperty);
289
290   if (readSucessful)
291   {
292     This->streamSize = curProperty.size;
293
294     /*
295      * This code supports only streams that are <32 bits in size.
296      */
297     assert(This->streamSize.u.HighPart == 0);
298
299     if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
300     {
301       assert( (This->streamSize.u.HighPart == 0) && (This->streamSize.u.LowPart == 0) );
302     }
303     else
304     {
305       if ( (This->streamSize.u.HighPart == 0) &&
306            (This->streamSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
307       {
308         This->smallBlockChain = SmallBlockChainStream_Construct(
309                                                                 This->parentStorage->ancestorStorage,
310                                                                 This->ownerProperty);
311       }
312       else
313       {
314         This->bigBlockChain = BlockChainStream_Construct(
315                                                          This->parentStorage->ancestorStorage,
316                                                          NULL,
317                                                          This->ownerProperty);
318       }
319     }
320   }
321 }
322
323 /***
324  * This method is part of the ISequentialStream interface.
325  *
326  * It reads a block of information from the stream at the current
327  * position. It then moves the current position at the end of the
328  * read block
329  *
330  * See the documentation of ISequentialStream for more info.
331  */
332 HRESULT WINAPI StgStreamImpl_Read(
333                   IStream*     iface,
334                   void*          pv,        /* [length_is][size_is][out] */
335                   ULONG          cb,        /* [in] */
336                   ULONG*         pcbRead)   /* [out] */
337 {
338   StgStreamImpl* const This=(StgStreamImpl*)iface;
339
340   ULONG bytesReadBuffer;
341   ULONG bytesToReadFromBuffer;
342   HRESULT res = S_FALSE;
343
344   TRACE("(%p, %p, %ld, %p)\n",
345         iface, pv, cb, pcbRead);
346
347   /*
348    * If the caller is not interested in the number of bytes read,
349    * we use another buffer to avoid "if" statements in the code.
350    */
351   if (pcbRead==0)
352     pcbRead = &bytesReadBuffer;
353
354   /*
355    * Using the known size of the stream, calculate the number of bytes
356    * to read from the block chain
357    */
358   bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
359
360   /*
361    * Depending on the type of chain that was opened when the stream was constructed,
362    * we delegate the work to the method that reads the block chains.
363    */
364   if (This->smallBlockChain!=0)
365   {
366     SmallBlockChainStream_ReadAt(This->smallBlockChain,
367                                  This->currentPosition,
368                                  bytesToReadFromBuffer,
369                                  pv,
370                                  pcbRead);
371
372   }
373   else if (This->bigBlockChain!=0)
374   {
375     BlockChainStream_ReadAt(This->bigBlockChain,
376                             This->currentPosition,
377                             bytesToReadFromBuffer,
378                             pv,
379                             pcbRead);
380   }
381   else
382   {
383     /*
384      * Small and big block chains are both NULL. This case will happen
385      * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
386      */
387
388     *pcbRead = 0;
389     res = S_OK;
390     goto end;
391   }
392
393   /*
394    * We should always be able to read the proper amount of data from the
395    * chain.
396    */
397   assert(bytesToReadFromBuffer == *pcbRead);
398
399   /*
400    * Advance the pointer for the number of positions read.
401    */
402   This->currentPosition.u.LowPart += *pcbRead;
403
404   if(*pcbRead != cb)
405   {
406     WARN("read %ld instead of the required %ld bytes !\n", *pcbRead, cb);
407     /*
408      * this used to return S_FALSE, however MSDN docu says that an app should
409      * be prepared to handle error in case of stream end reached, as *some*
410      * implementations *might* return an error (IOW: most do *not*).
411      * As some program fails on returning S_FALSE, I better use S_OK here.
412      */
413     res = S_OK;
414   }
415   else
416     res = S_OK;
417
418 end:
419   TRACE("<-- %08lx\n", res);
420   return res;
421 }
422
423 /***
424  * This method is part of the ISequentialStream interface.
425  *
426  * It writes a block of information to the stream at the current
427  * position. It then moves the current position at the end of the
428  * written block. If the stream is too small to fit the block,
429  * the stream is grown to fit.
430  *
431  * See the documentation of ISequentialStream for more info.
432  */
433 HRESULT WINAPI StgStreamImpl_Write(
434                   IStream*     iface,
435                   const void*    pv,          /* [size_is][in] */
436                   ULONG          cb,          /* [in] */
437                   ULONG*         pcbWritten)  /* [out] */
438 {
439   StgStreamImpl* const This=(StgStreamImpl*)iface;
440
441   ULARGE_INTEGER newSize;
442   ULONG bytesWritten = 0;
443
444   TRACE("(%p, %p, %ld, %p)\n",
445         iface, pv, cb, pcbWritten);
446
447   /*
448    * Do we have permission to write to this stream?
449    */
450   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE))) {
451       return STG_E_ACCESSDENIED;
452   }
453
454   /*
455    * If the caller is not interested in the number of bytes written,
456    * we use another buffer to avoid "if" statements in the code.
457    */
458   if (pcbWritten == 0)
459     pcbWritten = &bytesWritten;
460
461   /*
462    * Initialize the out parameter
463    */
464   *pcbWritten = 0;
465
466   if (cb == 0)
467   {
468     return S_OK;
469   }
470   else
471   {
472     newSize.u.HighPart = 0;
473     newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
474   }
475
476   /*
477    * Verify if we need to grow the stream
478    */
479   if (newSize.u.LowPart > This->streamSize.u.LowPart)
480   {
481     /* grow stream */
482     IStream_SetSize(iface, newSize);
483   }
484
485   /*
486    * Depending on the type of chain that was opened when the stream was constructed,
487    * we delegate the work to the method that readwrites to the block chains.
488    */
489   if (This->smallBlockChain!=0)
490   {
491     SmallBlockChainStream_WriteAt(This->smallBlockChain,
492                                   This->currentPosition,
493                                   cb,
494                                   pv,
495                                   pcbWritten);
496
497   }
498   else if (This->bigBlockChain!=0)
499   {
500     BlockChainStream_WriteAt(This->bigBlockChain,
501                              This->currentPosition,
502                              cb,
503                              pv,
504                              pcbWritten);
505   }
506   else
507     assert(FALSE);
508
509   /*
510    * Advance the position pointer for the number of positions written.
511    */
512   This->currentPosition.u.LowPart += *pcbWritten;
513
514   return S_OK;
515 }
516
517 /***
518  * This method is part of the IStream interface.
519  *
520  * It will move the current stream pointer according to the parameters
521  * given.
522  *
523  * See the documentation of IStream for more info.
524  */
525 HRESULT WINAPI StgStreamImpl_Seek(
526                   IStream*      iface,
527                   LARGE_INTEGER   dlibMove,         /* [in] */
528                   DWORD           dwOrigin,         /* [in] */
529                   ULARGE_INTEGER* plibNewPosition) /* [out] */
530 {
531   StgStreamImpl* const This=(StgStreamImpl*)iface;
532
533   ULARGE_INTEGER newPosition;
534
535   TRACE("(%p, %ld, %ld, %p)\n",
536         iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
537
538   /*
539    * The caller is allowed to pass in NULL as the new position return value.
540    * If it happens, we assign it to a dynamic variable to avoid special cases
541    * in the code below.
542    */
543   if (plibNewPosition == 0)
544   {
545     plibNewPosition = &newPosition;
546   }
547
548   /*
549    * The file pointer is moved depending on the given "function"
550    * parameter.
551    */
552   switch (dwOrigin)
553   {
554     case STREAM_SEEK_SET:
555       plibNewPosition->u.HighPart = 0;
556       plibNewPosition->u.LowPart  = 0;
557       break;
558     case STREAM_SEEK_CUR:
559       *plibNewPosition = This->currentPosition;
560       break;
561     case STREAM_SEEK_END:
562       *plibNewPosition = This->streamSize;
563       break;
564     default:
565       return STG_E_INVALIDFUNCTION;
566   }
567
568   plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
569
570   /*
571    * tell the caller what we calculated
572    */
573   This->currentPosition = *plibNewPosition;
574
575   return S_OK;
576 }
577
578 /***
579  * This method is part of the IStream interface.
580  *
581  * It will change the size of a stream.
582  *
583  * TODO: Switch from small blocks to big blocks and vice versa.
584  *
585  * See the documentation of IStream for more info.
586  */
587 HRESULT WINAPI StgStreamImpl_SetSize(
588                                      IStream*      iface,
589                                      ULARGE_INTEGER  libNewSize)   /* [in] */
590 {
591   StgStreamImpl* const This=(StgStreamImpl*)iface;
592
593   StgProperty    curProperty;
594   BOOL         Success;
595
596   TRACE("(%p, %ld)\n", iface, libNewSize.u.LowPart);
597
598   /*
599    * As documented.
600    */
601   if (libNewSize.u.HighPart != 0)
602     return STG_E_INVALIDFUNCTION;
603
604   /*
605    * Do we have permission?
606    */
607   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
608     return STG_E_ACCESSDENIED;
609
610   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
611     return S_OK;
612
613   /*
614    * This will happen if we're creating a stream
615    */
616   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
617   {
618     if (libNewSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
619     {
620       This->smallBlockChain = SmallBlockChainStream_Construct(
621                                     This->parentStorage->ancestorStorage,
622                                     This->ownerProperty);
623     }
624     else
625     {
626       This->bigBlockChain = BlockChainStream_Construct(
627                                 This->parentStorage->ancestorStorage,
628                                 NULL,
629                                 This->ownerProperty);
630     }
631   }
632
633   /*
634    * Read this stream's property to see if it's small blocks or big blocks
635    */
636   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
637                                        This->ownerProperty,
638                                        &curProperty);
639   /*
640    * Determine if we have to switch from small to big blocks or vice versa
641    */
642   if ( (This->smallBlockChain!=0) &&
643        (curProperty.size.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
644   {
645     if (libNewSize.u.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
646     {
647       /*
648        * Transform the small block chain into a big block chain
649        */
650       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
651                                 This->parentStorage->ancestorStorage,
652                                 &This->smallBlockChain);
653     }
654   }
655
656   if (This->smallBlockChain!=0)
657   {
658     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
659   }
660   else
661   {
662     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
663   }
664
665   /*
666    * Write the new information about this stream to the property
667    */
668   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
669                                        This->ownerProperty,
670                                        &curProperty);
671
672   curProperty.size.u.HighPart = libNewSize.u.HighPart;
673   curProperty.size.u.LowPart = libNewSize.u.LowPart;
674
675   if (Success)
676   {
677     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
678                                 This->ownerProperty,
679                                 &curProperty);
680   }
681
682   This->streamSize = libNewSize;
683
684   return S_OK;
685 }
686
687 /***
688  * This method is part of the IStream interface.
689  *
690  * It will copy the 'cb' Bytes to 'pstm' IStream.
691  *
692  * See the documentation of IStream for more info.
693  */
694 HRESULT WINAPI StgStreamImpl_CopyTo(
695                                     IStream*      iface,
696                                     IStream*      pstm,         /* [unique][in] */
697                                     ULARGE_INTEGER  cb,           /* [in] */
698                                     ULARGE_INTEGER* pcbRead,      /* [out] */
699                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
700 {
701   HRESULT        hr = S_OK;
702   BYTE           tmpBuffer[128];
703   ULONG          bytesRead, bytesWritten, copySize;
704   ULARGE_INTEGER totalBytesRead;
705   ULARGE_INTEGER totalBytesWritten;
706
707   TRACE("(%p, %p, %ld, %p, %p)\n",
708         iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
709
710   /*
711    * Sanity check
712    */
713   if ( pstm == 0 )
714     return STG_E_INVALIDPOINTER;
715
716   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
717   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
718
719   /*
720    * use stack to store data temporarily
721    * there is surely a more performant way of doing it, for now this basic
722    * implementation will do the job
723    */
724   while ( cb.u.LowPart > 0 )
725   {
726     if ( cb.u.LowPart >= 128 )
727       copySize = 128;
728     else
729       copySize = cb.u.LowPart;
730
731     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
732
733     totalBytesRead.u.LowPart += bytesRead;
734
735     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
736
737     totalBytesWritten.u.LowPart += bytesWritten;
738
739     /*
740      * Check that read & write operations were successful
741      */
742     if (bytesRead != bytesWritten)
743     {
744       hr = STG_E_MEDIUMFULL;
745       break;
746     }
747
748     if (bytesRead!=copySize)
749       cb.u.LowPart = 0;
750     else
751       cb.u.LowPart -= bytesRead;
752   }
753
754   /*
755    * Update number of bytes read and written
756    */
757   if (pcbRead)
758   {
759     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
760     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
761   }
762
763   if (pcbWritten)
764   {
765     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
766     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
767   }
768   return hr;
769 }
770
771 /***
772  * This method is part of the IStream interface.
773  *
774  * For streams contained in structured storages, this method
775  * does nothing. This is what the documentation tells us.
776  *
777  * See the documentation of IStream for more info.
778  */
779 HRESULT WINAPI StgStreamImpl_Commit(
780                   IStream*      iface,
781                   DWORD           grfCommitFlags)  /* [in] */
782 {
783   return S_OK;
784 }
785
786 /***
787  * This method is part of the IStream interface.
788  *
789  * For streams contained in structured storages, this method
790  * does nothing. This is what the documentation tells us.
791  *
792  * See the documentation of IStream for more info.
793  */
794 HRESULT WINAPI StgStreamImpl_Revert(
795                   IStream* iface)
796 {
797   return S_OK;
798 }
799
800 HRESULT WINAPI StgStreamImpl_LockRegion(
801                                         IStream*     iface,
802                                         ULARGE_INTEGER libOffset,   /* [in] */
803                                         ULARGE_INTEGER cb,          /* [in] */
804                                         DWORD          dwLockType)  /* [in] */
805 {
806   FIXME("not implemented!\n");
807   return E_NOTIMPL;
808 }
809
810 HRESULT WINAPI StgStreamImpl_UnlockRegion(
811                                           IStream*     iface,
812                                           ULARGE_INTEGER libOffset,   /* [in] */
813                                           ULARGE_INTEGER cb,          /* [in] */
814                                           DWORD          dwLockType)  /* [in] */
815 {
816   FIXME("not implemented!\n");
817   return E_NOTIMPL;
818 }
819
820 /***
821  * This method is part of the IStream interface.
822  *
823  * This method returns information about the current
824  * stream.
825  *
826  * See the documentation of IStream for more info.
827  */
828 HRESULT WINAPI StgStreamImpl_Stat(
829                   IStream*     iface,
830                   STATSTG*       pstatstg,     /* [out] */
831                   DWORD          grfStatFlag)  /* [in] */
832 {
833   StgStreamImpl* const This=(StgStreamImpl*)iface;
834
835   StgProperty    curProperty;
836   BOOL         readSucessful;
837
838   /*
839    * Read the information from the property.
840    */
841   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
842                                              This->ownerProperty,
843                                              &curProperty);
844
845   if (readSucessful)
846   {
847     StorageUtl_CopyPropertyToSTATSTG(pstatstg,
848                                      &curProperty,
849                                      grfStatFlag);
850
851     pstatstg->grfMode = This->grfMode;
852
853     return S_OK;
854   }
855
856   return E_FAIL;
857 }
858
859 /***
860  * This method is part of the IStream interface.
861  *
862  * This method returns a clone of the interface that allows for
863  * another seek pointer
864  *
865  * See the documentation of IStream for more info.
866  *
867  * I am not totally sure what I am doing here but I presume that this
868  * should be basically as simple as creating a new stream with the same
869  * parent etc and positioning its seek cursor.
870  */
871 HRESULT WINAPI StgStreamImpl_Clone(
872                                    IStream*     iface,
873                                    IStream**    ppstm) /* [out] */
874 {
875   StgStreamImpl* const This=(StgStreamImpl*)iface;
876   HRESULT hres;
877   StgStreamImpl* new_stream;
878   LARGE_INTEGER seek_pos;
879
880   /*
881    * Sanity check
882    */
883   if ( ppstm == 0 )
884     return STG_E_INVALIDPOINTER;
885
886   new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
887
888   if (!new_stream)
889     return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
890
891   *ppstm = (IStream*) new_stream;
892   seek_pos.QuadPart = This->currentPosition.QuadPart;
893
894   hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
895
896   assert (SUCCEEDED(hres));
897
898   return S_OK;
899 }