update dev300-m58
[ooovba.git] / sot / source / sdstor / stgdir.cxx
blobe8ba84b7e768b928fa776a070535dbf5fb7e36b6
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: stgdir.cxx,v $
10 * $Revision: 1.12 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sot.hxx"
34 #include <string.h> // memcpy()
36 #include "stg.hxx"
37 #include "stgelem.hxx"
38 #include "stgcache.hxx"
39 #include "stgstrms.hxx"
40 #include "stgdir.hxx"
41 #include "stgio.hxx"
44 //////////////////////////// class StgDirEntry /////////////////////////////
46 // This class holds the dir entry data and maintains dirty flags for both
47 // the entry and the data.
49 // Transacted mode for streams: On the first write, a temp stream pTmpStrm
50 // is created and operated on. A commit moves pTmpStrm to pCurStrm, which
51 // is used for subsequent reads. A new write creates a new copy of pTmpStrm
52 // based on pCurStrm. Reverting throws away pTmpStrm.
53 // Transacted mode for storages: A copy of the dir ents is kept in aSave.
54 // Committing means copying aEntry to aSave. Reverting means to copy aSave
55 // to aEntry, delete newly created entries and to reactivate removed entries.
57 // Problem der Implementation: Keine Hierarchischen commits. Daher nur
58 // insgesamt transaktionsorientert oder direkt.
60 StgDirEntry::StgDirEntry( const void* pFrom, BOOL * pbOk ) : StgAvlNode()
62 *pbOk = aEntry.Load( pFrom );
64 InitMembers();
67 StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r )
69 InitMembers();
72 // Helper for all ctors
74 void StgDirEntry::InitMembers()
76 aSave = aEntry;
77 pUp =
78 pDown = NULL;
79 ppRoot = NULL;
80 pStgStrm = NULL;
81 pCurStrm =
82 pTmpStrm = NULL;
83 nPos =
84 nEntry =
85 nRefCnt = 0;
86 nMode = STREAM_READ;
87 bDirect = TRUE;
88 bInvalid =
89 bCreated =
90 bRenamed =
91 bRemoved =
92 bTemp =
93 bDirty =
94 bZombie = FALSE;
97 StgDirEntry::~StgDirEntry()
99 Close();
100 delete pCurStrm;
101 delete pStgStrm;
102 delete pDown;
105 // Comparison function
107 short StgDirEntry::Compare( const StgAvlNode* p ) const
109 const StgDirEntry* pEntry = (const StgDirEntry*) p;
110 return aEntry.Compare( pEntry->aEntry );
113 // Enumerate the entry numbers.
114 // n is incremented to show the total # of entries.
115 // These number are later used as page numbers when storing
116 // the TOC tree into the TOC stream. Remember that aSave is
117 // stored, not aEntry.
119 void StgDirEntry::Enum( INT32& n )
121 INT32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE;
122 nEntry = n++;
123 if( pLeft )
125 ((StgDirEntry*) pLeft)->Enum( n ); nLeft = ((StgDirEntry*) pLeft)->nEntry;
127 if( pRight )
129 ((StgDirEntry*) pRight)->Enum( n ); nRight = ((StgDirEntry*) pRight)->nEntry;
131 if( pDown )
133 pDown->Enum( n ); nDown = pDown->nEntry;
135 aSave.SetLeaf( STG_LEFT, nLeft );
136 aSave.SetLeaf( STG_RIGHT, nRight );
137 aSave.SetLeaf( STG_CHILD, nDown );
140 // Delete all temporary entries before writing the TOC stream.
141 // Until now Deltem is never called with bForce True
143 void StgDirEntry::DelTemp( BOOL bForce )
145 if( pLeft )
146 ((StgDirEntry*) pLeft)->DelTemp( FALSE );
147 if( pRight )
148 ((StgDirEntry*) pRight)->DelTemp( FALSE );
149 if( pDown )
151 // If the storage is dead, of course all elements are dead, too
152 if( bInvalid && aEntry.GetType() == STG_STORAGE )
153 bForce = TRUE;
154 pDown->DelTemp( bForce );
156 if( ( bForce || bInvalid )
157 && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ )
159 Close();
160 if( pUp )
162 // this deletes the element if refcnt == 0!
163 BOOL bDel = nRefCnt == 0;
164 StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel );
165 if( !bDel )
167 pLeft = pRight = pDown = 0;
168 bInvalid = bZombie = TRUE;
174 // Save the tree into the given dir stream
176 BOOL StgDirEntry::Store( StgDirStrm& rStrm )
178 void* pEntry = rStrm.GetEntry( nEntry, TRUE );
179 if( !pEntry )
180 return FALSE;
181 // Do not store the current (maybe not commited) entry
182 aSave.Store( pEntry );
183 if( pLeft )
184 if( !((StgDirEntry*) pLeft)->Store( rStrm ) )
185 return FALSE;
186 if( pRight )
187 if( !((StgDirEntry*) pRight)->Store( rStrm ) )
188 return FALSE;
189 if( pDown )
190 if( !pDown->Store( rStrm ) )
191 return FALSE;
192 return TRUE;
195 BOOL StgDirEntry::StoreStream( StgIo& rIo )
197 if( aEntry.GetType() == STG_STREAM || aEntry.GetType() == STG_ROOT )
199 if( bInvalid )
201 // Delete the stream if needed
202 if( !pStgStrm )
204 OpenStream( rIo );
205 delete pStgStrm, pStgStrm = NULL;
207 else
208 pStgStrm->SetSize( 0 );
210 // or write the data stream
211 else if( !Tmp2Strm() )
212 return FALSE;
214 return TRUE;
217 // Save all dirty streams
219 BOOL StgDirEntry::StoreStreams( StgIo& rIo )
221 if( !StoreStream( rIo ) )
222 return FALSE;
223 if( pLeft )
224 if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) )
225 return FALSE;
226 if( pRight )
227 if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) )
228 return FALSE;
229 if( pDown )
230 if( !pDown->StoreStreams( rIo ) )
231 return FALSE;
232 return TRUE;
235 // Revert all directory entries after failure to write the TOC stream
237 void StgDirEntry::RevertAll()
239 aEntry = aSave;
240 if( pLeft )
241 ((StgDirEntry*) pLeft)->RevertAll();
242 if( pRight )
243 ((StgDirEntry*) pRight)->RevertAll();
244 if( pDown )
245 pDown->RevertAll();
248 // Look if any element of the tree is dirty
250 BOOL StgDirEntry::IsDirty()
252 if( bDirty || bInvalid )
253 return TRUE;
254 if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() )
255 return TRUE;
256 if( pRight && ((StgDirEntry*) pRight)->IsDirty() )
257 return TRUE;
258 if( pDown && pDown->IsDirty() )
259 return TRUE;
260 return FALSE;
263 // Set up a stream.
265 void StgDirEntry::OpenStream( StgIo& rIo, BOOL bForceBig )
267 INT32 nThreshold = (USHORT) rIo.aHdr.GetThreshold();
268 delete pStgStrm;
269 if( !bForceBig && aEntry.GetSize() < nThreshold )
270 pStgStrm = new StgSmallStrm( rIo, this );
271 else
272 pStgStrm = new StgDataStrm( rIo, this );
273 if( bInvalid && aEntry.GetSize() )
275 // This entry has invalid data, so delete that data
276 SetSize( 0L );
277 // bRemoved = bInvalid = FALSE;
279 nPos = 0;
282 // Close the open stream without committing. If the entry is marked as
283 // temporary, delete it.
284 // Do not delete pCurStrm here!
285 // (TLX:??? Zumindest pStgStrm muss deleted werden.)
287 void StgDirEntry::Close()
289 delete pTmpStrm;
290 pTmpStrm = NULL;
291 // nRefCnt = 0;
292 bInvalid = bTemp;
295 // Get the current stream size
297 INT32 StgDirEntry::GetSize()
299 INT32 n;
300 if( pTmpStrm )
301 n = pTmpStrm->GetSize();
302 else if( pCurStrm )
303 n = pCurStrm->GetSize();
304 else n = aEntry.GetSize();
305 return n;
308 // Set the stream size. This means also creating a temp stream.
310 BOOL StgDirEntry::SetSize( INT32 nNewSize )
312 if (
313 !( nMode & STREAM_WRITE ) ||
314 (!bDirect && !pTmpStrm && !Strm2Tmp())
317 return FALSE;
320 if( nNewSize < nPos )
321 nPos = nNewSize;
322 if( pTmpStrm )
324 pTmpStrm->SetSize( nNewSize );
325 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
326 return BOOL( pTmpStrm->GetError() == SVSTREAM_OK );
328 else
330 BOOL bRes = FALSE;
331 StgIo& rIo = pStgStrm->GetIo();
332 INT32 nThreshold = rIo.aHdr.GetThreshold();
333 // ensure the correct storage stream!
334 StgStrm* pOld = NULL;
335 USHORT nOldSize = 0;
336 if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() )
338 pOld = pStgStrm;
339 nOldSize = (USHORT) pOld->GetSize();
340 pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 );
342 else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() )
344 pOld = pStgStrm;
345 nOldSize = (USHORT) nNewSize;
346 pStgStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
348 // now set the new size
349 if( pStgStrm->SetSize( nNewSize ) )
351 // did we create a new stream?
352 if( pOld )
354 // if so, we probably need to copy the old data
355 if( nOldSize )
357 void* pBuf = new BYTE[ nOldSize ];
358 pOld->Pos2Page( 0L );
359 pStgStrm->Pos2Page( 0L );
360 if( pOld->Read( pBuf, nOldSize )
361 && pStgStrm->Write( pBuf, nOldSize ) )
362 bRes = TRUE;
363 delete[] static_cast<BYTE*>(pBuf);
365 else
366 bRes = TRUE;
367 if( bRes )
369 pOld->SetSize( 0 );
370 delete pOld;
371 pStgStrm->Pos2Page( nPos );
372 pStgStrm->SetEntry( *this );
374 else
376 pStgStrm->SetSize( 0 );
377 delete pStgStrm;
378 pStgStrm = pOld;
381 else
383 pStgStrm->Pos2Page( nPos );
384 bRes = TRUE;
387 return bRes;
391 // Seek. On negative values, seek to EOF.
393 INT32 StgDirEntry::Seek( INT32 nNew )
395 if( pTmpStrm )
397 if( nNew < 0 )
398 nNew = pTmpStrm->GetSize();
399 nNew = pTmpStrm->Seek( nNew );
401 else if( pCurStrm )
403 if( nNew < 0 )
404 nNew = pCurStrm->GetSize();
405 nNew = pCurStrm->Seek( nNew );
407 else
409 INT32 nSize = aEntry.GetSize();
411 if( nNew < 0 )
412 nNew = nSize;
414 // try to enlarge, the readonly streams should not allow this
415 if( nNew > nSize )
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!" );
420 return nPos;
422 else
423 return Seek( nNew );
425 pStgStrm->Pos2Page( nNew );
426 nNew = pStgStrm->GetPos();
428 return nPos = nNew;
431 // Read
433 INT32 StgDirEntry::Read( void* p, INT32 nLen )
435 if( nLen <= 0 )
436 return 0;
437 if( pTmpStrm )
438 nLen = pTmpStrm->Read( p, nLen );
439 else if( pCurStrm )
440 nLen = pCurStrm->Read( p, nLen );
441 else
442 nLen = pStgStrm->Read( p, nLen );
443 nPos += nLen;
444 return nLen;
447 // Write
449 INT32 StgDirEntry::Write( const void* p, INT32 nLen )
451 if( nLen <= 0 || !( nMode & STREAM_WRITE ) )
452 return 0;
454 // Was this stream committed internally and reopened in direct mode?
455 if( bDirect && ( pCurStrm || pTmpStrm ) && !Tmp2Strm() )
456 return 0;
457 // Is this stream opened in transacted mode? Do we have to make a copy?
458 if( !bDirect && !pTmpStrm && !Strm2Tmp() )
459 return 0;
460 if( pTmpStrm )
462 nLen = pTmpStrm->Write( p, nLen );
463 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
465 else
467 INT32 nNew = nPos + nLen;
468 if( nNew > pStgStrm->GetSize() )
470 if( !SetSize( nNew ) )
471 return 0L;
472 pStgStrm->Pos2Page( nPos );
474 nLen = pStgStrm->Write( p, nLen );
476 nPos += nLen;
477 return nLen;
480 // Copy the data of one entry into another entry.
482 void StgDirEntry::Copy( StgDirEntry& rDest )
484 INT32 n = GetSize();
485 if( rDest.SetSize( n ) && n )
487 BYTE aTempBytes[ 4096 ];
488 void* p = static_cast<void*>( aTempBytes );
489 Seek( 0L );
490 rDest.Seek( 0L );
491 while( n )
493 INT32 nn = n;
494 if( nn > 4096 )
495 nn = 4096;
496 if( Read( p, nn ) != nn )
497 break;
498 if( rDest.Write( p, nn ) != nn )
499 break;
500 n -= nn;
505 void StgDirEntry::Copy( BaseStorageStream& rDest )
507 INT32 n = GetSize();
508 if( rDest.SetSize( n ) && n )
510 ULONG Pos = rDest.Tell();
511 BYTE aTempBytes[ 4096 ];
512 void* p = static_cast<void*>( aTempBytes );
513 Seek( 0L );
514 rDest.Seek( 0L );
515 while( n )
517 INT32 nn = n;
518 if( nn > 4096 )
519 nn = 4096;
520 if( Read( p, nn ) != nn )
521 break;
522 if( sal::static_int_cast<INT32>(rDest.Write( p, nn )) != nn )
523 break;
524 n -= nn;
526 rDest.Seek( Pos ); // ?! Seems to be undocumented !
530 // Commit this entry
532 BOOL StgDirEntry::Commit()
534 // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
536 aSave = aEntry;
537 BOOL bRes = TRUE;
538 if( aEntry.GetType() == STG_STREAM )
540 if( pTmpStrm )
541 delete pCurStrm, pCurStrm = pTmpStrm, pTmpStrm = NULL;
542 if( bRemoved )
543 // Delete the stream if needed
544 if( pStgStrm )
545 pStgStrm->SetSize( 0 );
547 else if( aEntry.GetType() == STG_STORAGE && bDirect && bRes )
549 StgIterator aIter( *this );
550 for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() )
551 bRes = p->Commit();
553 return bRes;
556 // Revert the entry
558 BOOL StgDirEntry::Revert()
560 aEntry = aSave;
561 switch( aEntry.GetType() )
563 case STG_STREAM:
564 if( pCurStrm )
565 delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL;
566 break;
567 case STG_STORAGE:
569 BOOL bSomeRenamed = FALSE;
570 StgIterator aOIter( *this );
571 StgDirEntry* op = aOIter.First();
572 while( op )
574 op->aEntry = op->aSave;
575 op->bDirty = FALSE;
576 bSomeRenamed = BOOL( bSomeRenamed | op->bRenamed );
577 // Remove any new entries
578 if( op->bCreated )
580 op->bCreated = FALSE;
581 op->Close();
582 op->bInvalid = TRUE;
584 // Reactivate any removed entries
585 else if( op->bRemoved )
586 op->bRemoved = op->bInvalid = op->bTemp = FALSE;
587 op = aOIter.Next();
589 // Resort all renamed entries
590 if( bSomeRenamed )
592 StgIterator aIter( *this );
593 StgDirEntry* p = aIter.First();
594 while( p )
596 if( p->bRenamed )
598 StgAvlNode::Move
599 ( (StgAvlNode**) &p->pUp->pDown,
600 (StgAvlNode**) &p->pUp->pDown, p );
601 p->bRenamed = FALSE;
603 p = aIter.Next();
606 DelTemp( FALSE );
607 break;
609 case STG_EMPTY:
610 case STG_LOCKBYTES:
611 case STG_PROPERTY:
612 case STG_ROOT:
613 break;
615 return TRUE;
618 // Copy the stg stream to the temp stream
620 BOOL StgDirEntry::Strm2Tmp()
622 if( !pTmpStrm )
624 ULONG n = 0;
625 if( pCurStrm )
627 // It was already commited once
628 pTmpStrm = new StgTmpStrm;
629 if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) )
630 return TRUE;
631 n = 1; // indicates error
633 else
635 n = aEntry.GetSize();
636 pTmpStrm = new StgTmpStrm( n );
637 if( pTmpStrm->GetError() == SVSTREAM_OK )
639 if( n )
641 BYTE aTempBytes[ 4096 ];
642 void* p = static_cast<void*>( aTempBytes );
643 pStgStrm->Pos2Page( 0L );
644 while( n )
646 ULONG nn = n;
647 if( nn > 4096 )
648 nn = 4096;
649 if( (ULONG) pStgStrm->Read( p, nn ) != nn )
650 break;
651 if( pTmpStrm->Write( p, nn ) != nn )
652 break;
653 n -= nn;
655 pStgStrm->Pos2Page( nPos );
656 pTmpStrm->Seek( nPos );
659 else
660 n = 1;
662 if( n )
664 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
665 delete pTmpStrm;
666 pTmpStrm = NULL;
667 return FALSE;
670 return TRUE;
673 // Copy the temp stream to the stg stream during the final commit
675 BOOL StgDirEntry::Tmp2Strm()
677 // We did commit once, but have not written since then
678 if( !pTmpStrm )
679 pTmpStrm = pCurStrm, pCurStrm = NULL;
680 if( pTmpStrm )
682 ULONG n = pTmpStrm->GetSize();
683 StgStrm* pNewStrm;
684 StgIo& rIo = pStgStrm->GetIo();
685 ULONG nThreshold = (ULONG) rIo.aHdr.GetThreshold();
686 if( n < nThreshold )
687 pNewStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
688 else
689 pNewStrm = new StgDataStrm( rIo, STG_EOF, 0 );
690 if( pNewStrm->SetSize( n ) )
692 BYTE p[ 4096 ];
693 pTmpStrm->Seek( 0L );
694 while( n )
696 ULONG nn = n;
697 if( nn > 4096 )
698 nn = 4096;
699 if( pTmpStrm->Read( p, nn ) != nn )
700 break;
701 if( (ULONG) pNewStrm->Write( p, nn ) != nn )
702 break;
703 n -= nn;
705 if( n )
707 pTmpStrm->Seek( nPos );
708 pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
709 delete pNewStrm;
710 return FALSE;
712 else
714 pStgStrm->SetSize( 0L );
715 delete pStgStrm;
716 pStgStrm = pNewStrm;
717 pNewStrm->SetEntry( *this );
718 pNewStrm->Pos2Page( nPos );
719 delete pTmpStrm;
720 delete pCurStrm;
721 pTmpStrm = pCurStrm = NULL;
722 aSave = aEntry;
726 return TRUE;
729 // Check if the given entry is contained in this entry
731 BOOL StgDirEntry::IsContained( StgDirEntry* pStg )
733 if( aEntry.GetType() == STG_STORAGE )
735 StgIterator aIter( *this );
736 StgDirEntry* p = aIter.First();
737 while( p )
739 if( !p->aEntry.Compare( pStg->aEntry ) )
740 return FALSE;
741 if( p->aEntry.GetType() == STG_STORAGE )
742 if( !p->IsContained( pStg ) )
743 return FALSE;
744 p = aIter.Next();
747 return TRUE;
750 // Invalidate all open entries by setting the RefCount to 0. If the bDel
751 // flag is set, also set the invalid flag to indicate deletion during the
752 // next dir stream flush.
754 void StgDirEntry::Invalidate( BOOL bDel )
756 // nRefCnt = 0;
757 if( bDel )
758 bRemoved = bInvalid = TRUE;
759 switch( aEntry.GetType() )
761 case STG_STORAGE:
762 case STG_ROOT:
764 StgIterator aIter( *this );
765 for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() )
766 p->Invalidate( bDel );
767 break;
769 default:
770 break;
774 ///////////////////////////// class StgDirStrm ////////////////////////////
776 // This specialized stream is the maintenance stream for the directory tree.
778 StgDirStrm::StgDirStrm( StgIo& r )
779 : StgDataStrm( r, r.aHdr.GetTOCStart(), -1 )
780 , pRoot( NULL )
781 , nEntries( 0 )
783 if( r.GetError() )
784 return;
785 nEntries = nPageSize / STGENTRY_SIZE;
786 if( nStart == STG_EOF )
788 StgEntry aRoot;
789 aRoot.Init();
790 aRoot.SetName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "Root Entry" ) ) );
791 aRoot.SetType( STG_ROOT );
792 pRoot = new StgDirEntry( aRoot );
793 pRoot->SetDirty();
795 else
797 // temporarily use this instance as owner, so
798 // the TOC pages can be removed.
799 pEntry = (StgDirEntry*) this; // just for a bit pattern
800 SetupEntry( 0, pRoot );
801 rIo.Revert( pEntry );
802 pEntry = NULL;
806 StgDirStrm::~StgDirStrm()
808 delete pRoot;
811 // Recursively parse the directory tree during reading the TOC stream
813 void StgDirStrm::SetupEntry( INT32 n, StgDirEntry* pUpper )
815 void* p = ( n == STG_FREE ) ? NULL : GetEntry( n );
816 if( p )
818 BOOL bOk(FALSE);
819 StgDirEntry* pCur = new StgDirEntry( p, &bOk );
821 if( !bOk )
823 delete pCur;
824 rIo.SetError( SVSTREAM_GENERALERROR );
825 // an error occured
826 return;
829 // better it is
830 if( !pUpper )
831 pCur->aEntry.SetType( STG_ROOT );
833 INT32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT );
834 INT32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT );
835 // substorage?
836 INT32 nLeaf = STG_FREE;
837 if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT )
839 nLeaf = pCur->aEntry.GetLeaf( STG_CHILD );
840 if (nLeaf != STG_FREE && nLeaf == n)
842 delete pCur;
843 rIo.SetError( SVSTREAM_GENERALERROR );
844 return;
848 if( nLeaf != 0 && nLeft != 0 && nRight != 0 )
850 if( StgAvlNode::Insert
851 ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) )
853 pCur->pUp = pUpper;
854 pCur->ppRoot = &pRoot;
856 else
858 rIo.SetError( SVSTREAM_CANNOT_MAKE );
859 delete pCur; pCur = NULL;
860 return;
862 SetupEntry( nLeft, pUpper );
863 SetupEntry( nRight, pUpper );
864 SetupEntry( nLeaf, pCur );
869 // Extend or shrink the directory stream.
871 BOOL StgDirStrm::SetSize( INT32 nBytes )
873 // Always allocate full pages
874 nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
875 return StgStrm::SetSize( nBytes );
878 // Save the TOC stream into a new substream after saving all data streams
880 BOOL StgDirStrm::Store()
882 if( !pRoot->IsDirty() )
883 return TRUE;
884 if( !pRoot->StoreStreams( rIo ) )
885 return FALSE;
886 // After writing all streams, the data FAT stream has changed,
887 // so we have to commit the root again
888 pRoot->Commit();
889 // We want a completely new stream, so fake an empty stream
890 INT32 nOldStart = nStart; // save for later deletion
891 INT32 nOldSize = nSize;
892 nStart = nPage = STG_EOF;
893 nSize = nPos = 0;
894 nOffset = 0;
895 // Delete all temporary entries
896 pRoot->DelTemp( FALSE );
897 // set the entry numbers
898 INT32 n = 0;
899 pRoot->Enum( n );
900 if( !SetSize( n * STGENTRY_SIZE ) )
902 nStart = nOldStart; nSize = nOldSize;
903 pRoot->RevertAll();
904 return FALSE;
906 // set up the cache elements for the new stream
907 if( !Copy( STG_FREE, nSize ) )
909 pRoot->RevertAll();
910 return FALSE;
912 // Write the data to the new stream
913 if( !pRoot->Store( *this ) )
915 pRoot->RevertAll();
916 return FALSE;
918 // fill any remaining entries with empty data
919 INT32 ne = nSize / STGENTRY_SIZE;
920 StgEntry aEmpty;
921 aEmpty.Init();
922 while( n < ne )
924 void* p = GetEntry( n++, TRUE );
925 if( !p )
927 pRoot->RevertAll();
928 return FALSE;
930 aEmpty.Store( p );
932 // Now we can release the old stream
933 pFat->FreePages( nOldStart, TRUE );
934 rIo.aHdr.SetTOCStart( nStart );
935 return TRUE;
938 // Get a dir entry.
940 void* StgDirStrm::GetEntry( INT32 n, BOOL bDirty )
942 n *= STGENTRY_SIZE;
943 if( n >= nSize )
944 return NULL;
945 return GetPtr( n, TRUE, bDirty );
948 // Find a dir entry.
950 StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const String& rName )
952 if( rStg.pDown )
954 StgEntry aEntry;
955 aEntry.Init();
956 if( !aEntry.SetName( rName ) )
958 rIo.SetError( SVSTREAM_GENERALERROR );
959 return NULL;
961 // Look in the directory attached to the entry
962 StgDirEntry aTest( aEntry );
963 return (StgDirEntry*) rStg.pDown->Find( &aTest );
965 else
966 return NULL;
969 // Create a new entry.
971 StgDirEntry* StgDirStrm::Create
972 ( StgDirEntry& rStg, const String& rName, StgEntryType eType )
974 StgEntry aEntry;
975 aEntry.Init();
976 aEntry.SetType( eType );
977 if( !aEntry.SetName( rName ) )
979 rIo.SetError( SVSTREAM_GENERALERROR );
980 return NULL;
982 StgDirEntry* pRes = Find( rStg, rName );
983 if( pRes )
985 if( !pRes->bInvalid )
987 rIo.SetError( SVSTREAM_CANNOT_MAKE );
988 return NULL;
990 pRes->bInvalid =
991 pRes->bRemoved =
992 pRes->bTemp = FALSE;
993 pRes->bCreated =
994 pRes->bDirty = TRUE;
996 else
998 pRes = new StgDirEntry( aEntry );
999 if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) )
1001 pRes->pUp = &rStg;
1002 pRes->ppRoot = &pRoot;
1003 pRes->bCreated =
1004 pRes->bDirty = TRUE;
1006 else
1008 rIo.SetError( SVSTREAM_CANNOT_MAKE );
1009 delete pRes; pRes = NULL;
1012 return pRes;
1015 // Rename the given entry.
1017 BOOL StgDirStrm::Rename( StgDirEntry& rStg, const String& rOld, const String& rNew )
1019 StgDirEntry* p = Find( rStg, rOld );
1020 if( p )
1023 if( !StgAvlNode::Remove( (StgAvlNode**) &rStg.pDown, p, FALSE ) )
1024 return FALSE;
1025 p->aEntry.SetName( rNew );
1026 if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) )
1027 return FALSE;
1028 p->bRenamed = p->bDirty = TRUE;
1029 return TRUE;
1031 else
1033 rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1034 return FALSE;
1038 // Move the given entry to a different storage.
1040 BOOL StgDirStrm::Move( StgDirEntry& rStg1, StgDirEntry& rStg2, const String& rName )
1042 StgDirEntry* p = Find( rStg1, rName );
1043 if( p )
1045 if( !StgAvlNode::Move
1046 ( (StgAvlNode**) &rStg1.pDown, (StgAvlNode**) &rStg2.pDown, p ) )
1047 return FALSE;
1048 p->bDirty = TRUE;
1049 return TRUE;
1051 else
1053 rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1054 return FALSE;