1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <string.h> // memcpy()
23 #include "sot/stg.hxx"
24 #include "stgelem.hxx"
25 #include "stgcache.hxx"
26 #include "stgstrms.hxx"
31 //////////////////////////// class StgDirEntry /////////////////////////////
33 // This class holds the dir entry data and maintains dirty flags for both
34 // the entry and the data.
36 // Transacted mode for streams: On the first write, a temp stream pTmpStrm
37 // is created and operated on. A commit moves pTmpStrm to pCurStrm, which
38 // is used for subsequent reads. A new write creates a new copy of pTmpStrm
39 // based on pCurStrm. Reverting throws away pTmpStrm.
40 // Transacted mode for storages: A copy of the dir ents is kept in aSave.
41 // Committing means copying aEntry to aSave. Reverting means to copy aSave
42 // to aEntry, delete newly created entries and to reactivate removed entries.
44 // Problem der Implementation: Keine Hierarchischen commits. Daher nur
45 // insgesamt transaktionsorientert oder direkt.
47 StgDirEntry::StgDirEntry( const void* pBuffer
, sal_uInt32 nBufferLen
, bool * pbOk
) : StgAvlNode()
49 *pbOk
= aEntry
.Load( pBuffer
, nBufferLen
);
54 StgDirEntry::StgDirEntry( const StgEntry
& r
) : StgAvlNode(), aEntry( r
)
59 // Helper for all ctors
61 void StgDirEntry::InitMembers()
84 StgDirEntry::~StgDirEntry()
92 // Comparison function
94 short StgDirEntry::Compare( const StgAvlNode
* p
) const
99 const StgDirEntry
* pEntry
= (const StgDirEntry
*) p
;
100 nResult
= aEntry
.Compare( pEntry
->aEntry
);
105 // Enumerate the entry numbers.
106 // n is incremented to show the total # of entries.
107 // These number are later used as page numbers when storing
108 // the TOC tree into the TOC stream. Remember that aSave is
109 // stored, not aEntry.
111 void StgDirEntry::Enum( sal_Int32
& n
)
113 sal_Int32 nLeft
= STG_FREE
, nRight
= STG_FREE
, nDown
= STG_FREE
;
117 ((StgDirEntry
*) pLeft
)->Enum( n
); nLeft
= ((StgDirEntry
*) pLeft
)->nEntry
;
121 ((StgDirEntry
*) pRight
)->Enum( n
); nRight
= ((StgDirEntry
*) pRight
)->nEntry
;
125 pDown
->Enum( n
); nDown
= pDown
->nEntry
;
127 aSave
.SetLeaf( STG_LEFT
, nLeft
);
128 aSave
.SetLeaf( STG_RIGHT
, nRight
);
129 aSave
.SetLeaf( STG_CHILD
, nDown
);
132 // Delete all temporary entries before writing the TOC stream.
133 // Until now Deltem is never called with bForce True
135 void StgDirEntry::DelTemp( bool bForce
)
138 ((StgDirEntry
*) pLeft
)->DelTemp( false );
140 ((StgDirEntry
*) pRight
)->DelTemp( false );
143 // If the storage is dead, of course all elements are dead, too
144 if( bInvalid
&& aEntry
.GetType() == STG_STORAGE
)
146 pDown
->DelTemp( bForce
);
148 if( ( bForce
|| bInvalid
)
149 && ( aEntry
.GetType() != STG_ROOT
) /* && ( nRefCnt <= 1 ) */ )
154 // this deletes the element if refcnt == 0!
155 bool bDel
= nRefCnt
== 0;
156 StgAvlNode::Remove( (StgAvlNode
**) &pUp
->pDown
, this, bDel
);
159 pLeft
= pRight
= pDown
= 0;
160 bInvalid
= bZombie
= true;
166 // Save the tree into the given dir stream
168 bool StgDirEntry::Store( StgDirStrm
& rStrm
)
170 void* pEntry
= rStrm
.GetEntry( nEntry
, true );
173 // Do not store the current (maybe not commited) entry
174 aSave
.Store( pEntry
);
176 if( !((StgDirEntry
*) pLeft
)->Store( rStrm
) )
179 if( !((StgDirEntry
*) pRight
)->Store( rStrm
) )
182 if( !pDown
->Store( rStrm
) )
187 bool StgDirEntry::StoreStream( StgIo
& rIo
)
189 if( aEntry
.GetType() == STG_STREAM
|| aEntry
.GetType() == STG_ROOT
)
193 // Delete the stream if needed
197 delete pStgStrm
, pStgStrm
= NULL
;
200 pStgStrm
->SetSize( 0 );
202 // or write the data stream
203 else if( !Tmp2Strm() )
209 // Save all dirty streams
211 bool StgDirEntry::StoreStreams( StgIo
& rIo
)
213 if( !StoreStream( rIo
) )
216 if( !((StgDirEntry
*) pLeft
)->StoreStreams( rIo
) )
219 if( !((StgDirEntry
*) pRight
)->StoreStreams( rIo
) )
222 if( !pDown
->StoreStreams( rIo
) )
227 // Revert all directory entries after failure to write the TOC stream
229 void StgDirEntry::RevertAll()
233 ((StgDirEntry
*) pLeft
)->RevertAll();
235 ((StgDirEntry
*) pRight
)->RevertAll();
240 // Look if any element of the tree is dirty
242 bool StgDirEntry::IsDirty()
244 if( bDirty
|| bInvalid
)
246 if( pLeft
&& ((StgDirEntry
*) pLeft
)->IsDirty() )
248 if( pRight
&& ((StgDirEntry
*) pRight
)->IsDirty() )
250 if( pDown
&& pDown
->IsDirty() )
257 void StgDirEntry::OpenStream( StgIo
& rIo
, bool bForceBig
)
259 sal_Int32 nThreshold
= (sal_uInt16
) rIo
.aHdr
.GetThreshold();
261 if( !bForceBig
&& aEntry
.GetSize() < nThreshold
)
262 pStgStrm
= new StgSmallStrm( rIo
, *this );
264 pStgStrm
= new StgDataStrm( rIo
, *this );
265 if( bInvalid
&& aEntry
.GetSize() )
267 // This entry has invalid data, so delete that data
269 // bRemoved = bInvalid = false;
274 // Close the open stream without committing. If the entry is marked as
275 // temporary, delete it.
276 // Do not delete pCurStrm here!
277 // (TLX:??? Zumindest pStgStrm muss deleted werden.)
279 void StgDirEntry::Close()
287 // Get the current stream size
289 sal_Int32
StgDirEntry::GetSize()
293 n
= pTmpStrm
->GetSize();
295 n
= pCurStrm
->GetSize();
296 else n
= aEntry
.GetSize();
300 // Set the stream size. This means also creating a temp stream.
302 bool StgDirEntry::SetSize( sal_Int32 nNewSize
)
305 !( nMode
& STREAM_WRITE
) ||
306 (!bDirect
&& !pTmpStrm
&& !Strm2Tmp())
312 if( nNewSize
< nPos
)
316 pTmpStrm
->SetSize( nNewSize
);
317 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
318 return pTmpStrm
->GetError() == SVSTREAM_OK
;
322 OSL_ENSURE( pStgStrm
, "The pointer may not be NULL!" );
327 StgIo
& rIo
= pStgStrm
->GetIo();
328 sal_Int32 nThreshold
= rIo
.aHdr
.GetThreshold();
329 // ensure the correct storage stream!
330 StgStrm
* pOld
= NULL
;
331 sal_uInt16 nOldSize
= 0;
332 if( nNewSize
>= nThreshold
&& pStgStrm
->IsSmallStrm() )
335 nOldSize
= (sal_uInt16
) pOld
->GetSize();
336 pStgStrm
= new StgDataStrm( rIo
, STG_EOF
, 0 );
338 else if( nNewSize
< nThreshold
&& !pStgStrm
->IsSmallStrm() )
341 nOldSize
= (sal_uInt16
) nNewSize
;
342 pStgStrm
= new StgSmallStrm( rIo
, STG_EOF
, 0 );
344 // now set the new size
345 if( pStgStrm
->SetSize( nNewSize
) )
347 // did we create a new stream?
350 // if so, we probably need to copy the old data
353 void* pBuf
= new sal_uInt8
[ nOldSize
];
354 pOld
->Pos2Page( 0L );
355 pStgStrm
->Pos2Page( 0L );
356 if( pOld
->Read( pBuf
, nOldSize
)
357 && pStgStrm
->Write( pBuf
, nOldSize
) )
359 delete[] static_cast<sal_uInt8
*>(pBuf
);
367 pStgStrm
->Pos2Page( nPos
);
368 pStgStrm
->SetEntry( *this );
372 pStgStrm
->SetSize( 0 );
379 pStgStrm
->Pos2Page( nPos
);
387 // Seek. On negative values, seek to EOF.
389 sal_Int32
StgDirEntry::Seek( sal_Int32 nNew
)
394 nNew
= pTmpStrm
->GetSize();
395 nNew
= pTmpStrm
->Seek( nNew
);
400 nNew
= pCurStrm
->GetSize();
401 nNew
= pCurStrm
->Seek( nNew
);
405 OSL_ENSURE( pStgStrm
, "The pointer may not be NULL!" );
409 sal_Int32 nSize
= aEntry
.GetSize();
414 // try to enlarge, the readonly streams should not allow this
417 if ( !( nMode
& STREAM_WRITE
) || !SetSize( nNew
) )
419 OSL_ENSURE( nMode
& STREAM_WRITE
, "Trying to resize readonly stream by seeking, could be a wrong offset!" );
425 pStgStrm
->Pos2Page( nNew
);
426 nNew
= pStgStrm
->GetPos();
434 sal_Int32
StgDirEntry::Read( void* p
, sal_Int32 nLen
)
439 nLen
= pTmpStrm
->Read( p
, nLen
);
441 nLen
= pCurStrm
->Read( p
, nLen
);
444 OSL_ENSURE( pStgStrm
, "The pointer may not be NULL!" );
448 nLen
= pStgStrm
->Read( p
, nLen
);
457 sal_Int32
StgDirEntry::Write( const void* p
, sal_Int32 nLen
)
459 if( nLen
<= 0 || !( nMode
& STREAM_WRITE
) )
462 // Was this stream committed internally and reopened in direct mode?
463 if( bDirect
&& ( pCurStrm
|| pTmpStrm
) && !Tmp2Strm() )
465 // Is this stream opened in transacted mode? Do we have to make a copy?
466 if( !bDirect
&& !pTmpStrm
&& !Strm2Tmp() )
469 OSL_ENSURE( pStgStrm
, "The pointer may not be NULL!" );
475 nLen
= pTmpStrm
->Write( p
, nLen
);
476 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
480 sal_Int32 nNew
= nPos
+ nLen
;
481 if( nNew
> pStgStrm
->GetSize() )
483 if( !SetSize( nNew
) )
485 pStgStrm
->Pos2Page( nPos
);
487 nLen
= pStgStrm
->Write( p
, nLen
);
493 void StgDirEntry::Copy( BaseStorageStream
& rDest
)
495 sal_Int32 n
= GetSize();
496 if( rDest
.SetSize( n
) && n
)
498 sal_uLong Pos
= rDest
.Tell();
499 sal_uInt8 aTempBytes
[ 4096 ];
500 void* p
= static_cast<void*>( aTempBytes
);
508 if( Read( p
, nn
) != nn
)
510 if( sal::static_int_cast
<sal_Int32
>(rDest
.Write( p
, nn
)) != nn
)
514 rDest
.Seek( Pos
); // ?! Seems to be undocumented !
520 bool StgDirEntry::Commit()
522 // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
526 if( aEntry
.GetType() == STG_STREAM
)
529 delete pCurStrm
, pCurStrm
= pTmpStrm
, pTmpStrm
= NULL
;
531 // Delete the stream if needed
533 pStgStrm
->SetSize( 0 );
535 else if( aEntry
.GetType() == STG_STORAGE
&& bDirect
&& bRes
)
537 StgIterator
aIter( *this );
538 for( StgDirEntry
* p
= aIter
.First(); p
&& bRes
; p
= aIter
.Next() )
546 bool StgDirEntry::Revert()
549 switch( aEntry
.GetType() )
553 delete pTmpStrm
, pTmpStrm
= pCurStrm
, pCurStrm
= NULL
;
557 bool bSomeRenamed
= false;
558 StgIterator
aOIter( *this );
559 StgDirEntry
* op
= aOIter
.First();
562 op
->aEntry
= op
->aSave
;
564 bSomeRenamed
= ( bSomeRenamed
| op
->bRenamed
);
565 // Remove any new entries
568 op
->bCreated
= false;
572 // Reactivate any removed entries
573 else if( op
->bRemoved
)
574 op
->bRemoved
= op
->bInvalid
= op
->bTemp
= false;
577 // Resort all renamed entries
580 StgIterator
aIter( *this );
581 StgDirEntry
* p
= aIter
.First();
587 ( (StgAvlNode
**) &p
->pUp
->pDown
,
588 (StgAvlNode
**) &p
->pUp
->pDown
, p
);
606 // Copy the stg stream to the temp stream
608 bool StgDirEntry::Strm2Tmp()
615 // It was already commited once
616 pTmpStrm
= new StgTmpStrm
;
617 if( pTmpStrm
->GetError() == SVSTREAM_OK
&& pTmpStrm
->Copy( *pCurStrm
) )
619 n
= 1; // indicates error
623 n
= aEntry
.GetSize();
624 pTmpStrm
= new StgTmpStrm( n
);
625 if( pTmpStrm
->GetError() == SVSTREAM_OK
)
629 OSL_ENSURE( pStgStrm
, "The pointer may not be NULL!" );
633 sal_uInt8 aTempBytes
[ 4096 ];
634 void* p
= static_cast<void*>( aTempBytes
);
635 pStgStrm
->Pos2Page( 0L );
641 if( (sal_uLong
) pStgStrm
->Read( p
, nn
) != nn
)
643 if( pTmpStrm
->Write( p
, nn
) != nn
)
647 pStgStrm
->Pos2Page( nPos
);
648 pTmpStrm
->Seek( nPos
);
657 OSL_ENSURE( pStgStrm
, "The pointer may not be NULL!" );
659 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
669 // Copy the temp stream to the stg stream during the final commit
671 bool StgDirEntry::Tmp2Strm()
673 // We did commit once, but have not written since then
675 pTmpStrm
= pCurStrm
, pCurStrm
= NULL
;
678 OSL_ENSURE( pStgStrm
, "The pointer may not be NULL!" );
681 sal_uLong n
= pTmpStrm
->GetSize();
683 StgIo
& rIo
= pStgStrm
->GetIo();
684 sal_uLong nThreshold
= (sal_uLong
) rIo
.aHdr
.GetThreshold();
686 pNewStrm
= new StgSmallStrm( rIo
, STG_EOF
, 0 );
688 pNewStrm
= new StgDataStrm( rIo
, STG_EOF
, 0 );
689 if( pNewStrm
->SetSize( n
) )
692 pTmpStrm
->Seek( 0L );
698 if( pTmpStrm
->Read( p
, nn
) != nn
)
700 if( (sal_uLong
) pNewStrm
->Write( p
, nn
) != nn
)
706 pTmpStrm
->Seek( nPos
);
707 pStgStrm
->GetIo().SetError( pTmpStrm
->GetError() );
713 pStgStrm
->SetSize( 0L );
716 pNewStrm
->SetEntry( *this );
717 pNewStrm
->Pos2Page( nPos
);
720 pTmpStrm
= pCurStrm
= NULL
;
728 // Check if the given entry is contained in this entry
730 bool StgDirEntry::IsContained( StgDirEntry
* pStg
)
732 if( aEntry
.GetType() == STG_STORAGE
)
734 StgIterator
aIter( *this );
735 StgDirEntry
* p
= aIter
.First();
738 if( !p
->aEntry
.Compare( pStg
->aEntry
) )
740 if( p
->aEntry
.GetType() == STG_STORAGE
)
741 if( !p
->IsContained( pStg
) )
749 // Invalidate all open entries by setting the RefCount to 0. If the bDel
750 // flag is set, also set the invalid flag to indicate deletion during the
751 // next dir stream flush.
753 void StgDirEntry::Invalidate( bool bDel
)
757 bRemoved
= bInvalid
= true;
758 switch( aEntry
.GetType() )
763 StgIterator
aIter( *this );
764 for( StgDirEntry
* p
= aIter
.First(); p
; p
= aIter
.Next() )
765 p
->Invalidate( bDel
);
773 ///////////////////////////// class StgDirStrm ////////////////////////////
775 // This specialized stream is the maintenance stream for the directory tree.
777 StgDirStrm::StgDirStrm( StgIo
& r
)
778 : StgDataStrm( r
, r
.aHdr
.GetTOCStart(), -1 )
784 nEntries
= nPageSize
/ STGENTRY_SIZE
;
785 if( nStart
== STG_EOF
)
789 aRoot
.SetName( OUString("Root Entry") );
790 aRoot
.SetType( STG_ROOT
);
791 pRoot
= new StgDirEntry( aRoot
);
796 // temporarily use this instance as owner, so
797 // the TOC pages can be removed.
798 pEntry
= (StgDirEntry
*) this; // just for a bit pattern
799 SetupEntry( 0, pRoot
);
804 StgDirStrm::~StgDirStrm()
809 // Recursively parse the directory tree during reading the TOC stream
811 void StgDirStrm::SetupEntry( sal_Int32 n
, StgDirEntry
* pUpper
)
813 void* p
= ( n
== STG_FREE
) ? NULL
: GetEntry( n
);
817 StgDirEntry
* pCur
= new StgDirEntry( p
, STGENTRY_SIZE
, &bOk
);
822 rIo
.SetError( SVSTREAM_GENERALERROR
);
829 pCur
->aEntry
.SetType( STG_ROOT
);
831 sal_Int32 nLeft
= pCur
->aEntry
.GetLeaf( STG_LEFT
);
832 sal_Int32 nRight
= pCur
->aEntry
.GetLeaf( STG_RIGHT
);
834 sal_Int32 nLeaf
= STG_FREE
;
835 if( pCur
->aEntry
.GetType() == STG_STORAGE
|| pCur
->aEntry
.GetType() == STG_ROOT
)
837 nLeaf
= pCur
->aEntry
.GetLeaf( STG_CHILD
);
838 if (nLeaf
!= STG_FREE
&& nLeaf
== n
)
841 rIo
.SetError( SVSTREAM_GENERALERROR
);
846 if( nLeaf
!= 0 && nLeft
!= 0 && nRight
!= 0 )
848 //fdo#41642 Do we need to check full chain upwards for loops ?
851 if (pUpper
->aEntry
.GetLeaf(STG_CHILD
) == nLeaf
)
853 OSL_FAIL("Leaf node of upper StgDirEntry is same as current StgDirEntry's leaf node. Circular entry chain, discarding link");
858 StgDirEntry
*pUpperUpper
= pUpper
->pUp
;
859 if (pUpperUpper
&& pUpperUpper
->aEntry
.GetLeaf(STG_CHILD
) == nLeaf
)
861 OSL_FAIL("Leaf node of upper-upper StgDirEntry is same as current StgDirEntry's leaf node. Circular entry chain, discarding link");
867 if( StgAvlNode::Insert
868 ( (StgAvlNode
**) ( pUpper
? &pUpper
->pDown
: &pRoot
), pCur
) )
871 pCur
->ppRoot
= &pRoot
;
875 // bnc#682484: There are some really broken docs out there
876 // that contain duplicate entries in 'Directory' section
877 // so don't set the error flag here and just skip those
878 // (was: rIo.SetError( SVSTREAM_CANNOT_MAKE );)
882 SetupEntry( nLeft
, pUpper
);
883 SetupEntry( nRight
, pUpper
);
884 SetupEntry( nLeaf
, pCur
);
893 // Extend or shrink the directory stream.
895 bool StgDirStrm::SetSize( sal_Int32 nBytes
)
897 // Always allocate full pages
901 nBytes
= ( ( nBytes
+ nPageSize
- 1 ) / nPageSize
) * nPageSize
;
902 return StgStrm::SetSize( nBytes
);
905 // Save the TOC stream into a new substream after saving all data streams
907 bool StgDirStrm::Store()
909 if( !pRoot
|| !pRoot
->IsDirty() )
911 if( !pRoot
->StoreStreams( rIo
) )
913 // After writing all streams, the data FAT stream has changed,
914 // so we have to commit the root again
916 // We want a completely new stream, so fake an empty stream
917 sal_Int32 nOldStart
= nStart
; // save for later deletion
918 sal_Int32 nOldSize
= nSize
;
919 nStart
= nPage
= STG_EOF
;
922 // Delete all temporary entries
923 pRoot
->DelTemp( false );
924 // set the entry numbers
927 if( !SetSize( n
* STGENTRY_SIZE
) )
929 nStart
= nOldStart
; nSize
= nOldSize
;
933 // set up the cache elements for the new stream
934 if( !Copy( STG_FREE
, nSize
) )
939 // Write the data to the new stream
940 if( !pRoot
->Store( *this ) )
945 // fill any remaining entries with empty data
946 sal_Int32 ne
= nSize
/ STGENTRY_SIZE
;
951 void* p
= GetEntry( n
++, true );
959 // Now we can release the old stream
960 pFat
->FreePages( nOldStart
, true );
961 rIo
.aHdr
.SetTOCStart( nStart
);
967 void* StgDirStrm::GetEntry( sal_Int32 n
, bool bDirty
)
975 return GetPtr( n
, true, bDirty
);
980 StgDirEntry
* StgDirStrm::Find( StgDirEntry
& rStg
, const String
& rName
)
986 if( !aEntry
.SetName( rName
) )
988 rIo
.SetError( SVSTREAM_GENERALERROR
);
991 // Look in the directory attached to the entry
992 StgDirEntry
aTest( aEntry
);
993 return (StgDirEntry
*) rStg
.pDown
->Find( &aTest
);
999 // Create a new entry.
1001 StgDirEntry
* StgDirStrm::Create
1002 ( StgDirEntry
& rStg
, const String
& rName
, StgEntryType eType
)
1006 aEntry
.SetType( eType
);
1007 if( !aEntry
.SetName( rName
) )
1009 rIo
.SetError( SVSTREAM_GENERALERROR
);
1012 StgDirEntry
* pRes
= Find( rStg
, rName
);
1015 if( !pRes
->bInvalid
)
1017 rIo
.SetError( SVSTREAM_CANNOT_MAKE
);
1022 pRes
->bTemp
= false;
1024 pRes
->bDirty
= true;
1028 pRes
= new StgDirEntry( aEntry
);
1029 if( StgAvlNode::Insert( (StgAvlNode
**) &rStg
.pDown
, pRes
) )
1032 pRes
->ppRoot
= &pRoot
;
1034 pRes
->bDirty
= true;
1038 rIo
.SetError( SVSTREAM_CANNOT_MAKE
);
1039 delete pRes
; pRes
= NULL
;
1045 // Rename the given entry.
1047 bool StgDirStrm::Rename( StgDirEntry
& rStg
, const String
& rOld
, const String
& rNew
)
1049 StgDirEntry
* p
= Find( rStg
, rOld
);
1053 if( !StgAvlNode::Remove( (StgAvlNode
**) &rStg
.pDown
, p
, false ) )
1055 p
->aEntry
.SetName( rNew
);
1056 if( !StgAvlNode::Insert( (StgAvlNode
**) &rStg
.pDown
, p
) )
1058 p
->bRenamed
= p
->bDirty
= true;
1063 rIo
.SetError( SVSTREAM_FILE_NOT_FOUND
);
1068 // Move the given entry to a different storage.
1070 bool StgDirStrm::Move( StgDirEntry
& rStg1
, StgDirEntry
& rStg2
, const String
& rName
)
1072 StgDirEntry
* p
= Find( rStg1
, rName
);
1075 if( !StgAvlNode::Move
1076 ( (StgAvlNode
**) &rStg1
.pDown
, (StgAvlNode
**) &rStg2
.pDown
, p
) )
1083 rIo
.SetError( SVSTREAM_FILE_NOT_FOUND
);
1088 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */