campo-sirio/arch/archiveb.cpp
villa ea150a8931 Prime modifiche per compatibilita' UNIX
git-svn-id: svn://10.65.10.50/trunk@33 c028cbd2-c16b-5b4b-a496-9718f37d4682
1994-08-18 10:40:18 +00:00

1457 lines
48 KiB
C++
Executable File

//
// ARCHIVEB.CPP
//
// Source file for ArchiveLib 1.0
//
// Copyright (c) Greenleaf Software, Inc. 1994
// All Rights Reserved
//
// CONTENTS
//
// ALArchiveBase::operator new()
// ALArchiveBase::ALArchiveBase()
// ALArchiveBase::~ALArchiveBase()
// ALArchiveBase::SetComment()
// ALArchiveBase::WriteDirectory()
// ALArchiveBase::ScanStatus()
// ALArchiveBase::Extract()
// ALArchiveBase::AddJobs()
// ALArchiveBase::AddDirectoryEntries()
// ALArchiveBase::CalculateJobSize()
// ALArchiveBase::CalculateCompressedJobSize()
// ALArchiveBase::Create(ALEntryList&)
// ALArchiveBase::CopyJobs()
// ALArchiveBase::Create( ALArchiveBase&,ALEntryList&)
// ALArchiveBase::Append(ALEntryList&)
// ALArchiveBase::Append(ALArchiveBase&,ALEntryList&)
// ALArchiveBase::ReadDirectory()
// ALArchiveBase::WriteArchiveData()
// ALArchiveBase::ReadArchiveData()
// ALArchiveBase::Delete(ALEntryList&,ALArchiveBase&)
// ALArchiveBase::FillListBox()
//
//
// DESCRIPTION
//
// This file contains all of the source code for the base class,
// ALArchiveBase. Classes derived from ALArchiveBase don't actually
// do much work, they just bind different sorts of storage objects and
// compression engines to an application, so this is where all the action
// is. The details on how things get inserted and extracted from an
// archive will all be found here.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
//
#include "arclib.h"
#pragma hdrstop
#include "_openf.h"
//
// void * ALArchiveBase::operator new( size_t size )
//
// ARGUMENTS:
//
// size : The number of bytes needed to create a new ALArchiveBase object.
//
// RETURNS
//
// A pointer to the newly allocated storage area, or 0 if no storage
// was available.
//
// DESCRIPTION
//
// When using a DLL, it is easy to enter a dangerous situation when
// creating objects whose ctor and dtor are both in the DLL. The problem
// arises because when you create an object using new, the memory for
// the object will be allocated from the EXE. However, when you destroy
// the object using delete, the memory is freed inside the DLL. Since
// the DLL doesn't really own that memory, bad things can happen.
//
// But, you say, won't the space just go back to the Windows heap regardless
// of who tries to free it? Maybe, but maybe not. If the DLL is using
// a subsegment allocation scheme, it might do some sort of local free
// before returning the space to the windows heap. That is the point where
// you could conceivably cook your heap.
//
// By providing our own version of operator new inside this class, we
// ensure that all memory allocation for the class will be done from
// inside the DLL, not the EXE calling the DLL.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
#if defined( AL_BUILDING_DLL )
void AL_DLL_FAR * AL_PROTO ALArchiveBase::operator new( size_t size )
{
return ::new char[ size ];
}
#endif
//
// ALArchiveBase::ALArchiveBase( ALStorage *storage_object,
// short int delete_in_dtor )
//
// ARGUMENTS:
//
// storage_object : A pointer to the storage object that will/does
// hold the archive.
//
// delete_in_dtor : This flags whether the ALArchiveBase object should call
// the destructor for the storage object when the
// ALArchiveBase object is created.
//
// RETURNS
//
// Nothing, it is a constructor.
//
// DESCRIPTION
//
// This is the ALArchiveBase constructor. It is a public member function,
// but in practice it should only be called by the constructors for
// class derived from ALArchiveBase. Since there are pure functions
// in this class, you can't construct an object of this type anyway,
// no matter how hard you try.
//
// Despite the complexity of this class, and the vast array of member
// functions it contains, the constructor doesn't have much to do. It
// just sets up the contents of a few data members, and that's that.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
AL_PROTO ALArchiveBase::ALArchiveBase( ALStorage AL_DLL_FAR *storage_object,
short int delete_in_dtor )
: miDeleteStorageObject( delete_in_dtor )
{
mpArchiveStorageObject = storage_object;
mszComment = 0;
mlDirectoryOffset = -1L;
miVersion = -1;
}
//
// ALArchiveBase::~ALArchiveBase()
//
// ARGUMENTS:
//
// None.
//
// RETURNS
//
// None, destructors don't get any.
//
// DESCRIPTION
//
// The destructor for ALArchiveBase has a few pieces of busy work to
// complete. First, it might have a comment to delete. Second, it
// might have to delete its storage object, but only if it was told
// to in the constructor.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
AL_PROTO ALArchiveBase::~ALArchiveBase()
{
AL_ASSERT( GoodTag(), "~Archive(): Attempting to delete invalid ALArchiveBase" );
if ( mszComment )
delete[] mszComment;
if ( mpArchiveStorageObject && miDeleteStorageObject )
delete mpArchiveStorageObject;
AL_ASSERT( GoodTag(), "~Archive::Attempting to delete invalid ALArchiveBase" );
}
//
// int ALArchiveBase::SetComment( char * comment )
//
// ARGUMENTS:
//
// comment : The new comment that is going to be attached to the archive.
//
// RETURNS
//
// AL_SUCCESS, if things went well, AL_CANT_ALLOCATE_MEMORY if allocation
// of the character array failed.
//
// DESCRIPTION
//
// The archive object has a comment member, that is blank when first
// constructed. It can be set to something interesting either by
// reading in a new comment along with the archive directory, or by
// setting it using this function.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::SetComment( char AL_DLL_FAR * comment )
{
if ( mszComment )
delete[] mszComment;
if ( comment == 0 )
mszComment = 0;
else {
mszComment = new char[ strlen( comment ) + 1 ];
if ( mszComment )
strcpy( mszComment, comment );
else
return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
"Failed to allocate memory for "
"comment in archive %s",
mpArchiveStorageObject->mName.GetName() );
}
return mStatus;
}
//
// int ALArchiveBase::WriteDirectory( ALEntryList &list )
//
// ARGUMENTS:
//
// list : The ALEntryList object that contains the Archive's
// up to date directory.
//
// RETURNS
//
// The integer stored in mStatus, which ought to be AL_SUCCESS if everything
// went okay, or some int < AL_SUCCESS on error.
//
// DESCRIPTION
//
// This is the public function the user can call to rewrite the directory
// for an archive. It is also called internally be several of the functions
// the update archive contents. Probably the main reason to call this under
// normal circumstances would be after modifying the comment field of an
// archive.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
//
// Don't call ArchiveOperation() here, because this might
// just be a component of a directory write (during an append, eg.)
//
int AL_PROTO ALArchiveBase::WriteDirectory( ALEntryList AL_DLL_FAR &list )
{
ALOpenInputFile archive( *mpArchiveStorageObject );
mpArchiveStorageObject->Seek( mlDirectoryOffset );
mpArchiveStorageObject->WritePortableShort( miVersion );
WriteArchiveData();
mpArchiveStorageObject->WriteString( mszComment );
AddDirectoryEntries( list );
return mStatus;
}
// PRIVATE MEMBER FUNCTION
//
// void ALArchiveBase::ScanStatus( ALEntryList &list )
//
// ARGUMENTS:
//
// list : The list of entries that have just been processed.
//
// RETURNS
//
// None. This function sort of has a return, it will update
// the member mStatus with an error code if one is found.
//
// DESCRIPTION
//
//
// After an archive operation, I use this function to update the
// status member of the archive. If the archive doesn't already
// have an error, I check through all the storage objects and
// compression engines to see if any of them hosed up. Any error
// of any sort by any of them is copied into the archive status.
// The whole point of this is to ensure that if
// ALArchiveBase.mStatus == AL_SUCCESS, it means everything worked.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
void AL_PROTO ALArchiveBase::ScanStatus( ALEntryList AL_DLL_FAR &list )
{
if ( mStatus < AL_SUCCESS )
return;
ALEntry *job = list.GetFirstEntry();
while ( job ) {
if ( job->mpStorageObject->mStatus < AL_SUCCESS ) {
mStatus.SetError( job->mpStorageObject->mStatus,
"%s: %s",
job->mpStorageObject->mName.GetSafeName(),
job->mpStorageObject->mStatus.GetStatusDetail() );
return;
}
job = job->GetNextEntry();
}
}
//
// int ALArchiveBase::Extract( ALEntryList &list )
//
// ARGUMENTS:
//
// list : A list of storage objects to be extracted (if marked.)
//
// RETURNS
//
// AL_SUCCESS if all went well, or < AL_SUCCESS if the process
// went sour at any point.
//
// DESCRIPTION
//
// This function is one of the high level functions that can be called
// from a user program. It has several important things it needs to do
// in order to extract the appropriate objects from an archive:
//
// o Flag any duplicates. We don't extract anything twice, that will
// be considered an error.
//
// o Open the Archive storage object.
//
// o Calculate the total number of bytes to be processed in the entire
// job, and give this information to the monitor, who might care if
// he is in AL_MONITOR_JOB mode.
//
// o Iterate through the list, performing the following actions for
// each object marked for extraction:
//
// o Update the monitor with information about the object destined
// for immediate extraction.
//
// o Locate the compressed object in the archive.
//
// o Decompress the object, and check for CRC errors.
//
// o Update the monitor.
//
// o After all objects have been extracted, update the monitor again.
//
// o Scan for extraction errors, then return the result.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::Extract( ALEntryList AL_DLL_FAR &list )
{
//
// Open the input storage object, if not already open. Let the monitor
// know about it.
//
ALOpenInputFile archive( *mpArchiveStorageObject );
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
//
// Get rid of any duplicate entries, and set up the monitor sizes.
//
list.UnmarkDuplicates( list, "Duplicate entry in list passed to Extract()" );
list.mrMonitor.mlJobSoFar = 0L;
if ( list.mrMonitor.miMonitorType == AL_MONITOR_JOB )
list.mrMonitor.mlJobSize = CalculateCompressedJobSize( list );
//
// This loop iterates through the entire ALEntryList. We only care about
// ALEntry objects that have their mark set.
//
ALEntry *job = list.GetFirstEntry();
while ( job ) {
if ( job->miMark ) {
//
// Go to the correct input position in this, and set up the monitor for
// this particular object.
//
list.mrMonitor.ArchiveOperation( AL_EXTRACTION_OPEN, this, job );
mpArchiveStorageObject->Seek( job->mlCompressedObjectPosition );
list.mrMonitor.mlObjectStart = job->mlCompressedObjectPosition;
list.mrMonitor.mlObjectSize = job->mlCompressedSize;
mpArchiveStorageObject->mpMonitor = &list.mrMonitor;
//
// Extract it, then check the CRC.
//
job->mpCompressionEngine->Decompress( *mpArchiveStorageObject,
*job->mpStorageObject,
job->mlCompressedSize );
if ( job->mpStorageObject->GetCrc32() != job->GetCrc32() )
job->mpStorageObject->mStatus.SetError(
AL_CRC_ERROR,
"CRC32 was supposed to be %08lx, was %08lx",
job->GetCrc32(),
job->mpStorageObject->GetCrc32() );
//
// Update the monitor data, and yield some time. Note that I turn off
// the monitor at this point so it doesn't jump around while I seek to the
// next position in the archive.
//
list.mrMonitor.mlJobSoFar += job->mlCompressedSize;
mpArchiveStorageObject->YieldTime();
mpArchiveStorageObject->mpMonitor = 0;
list.mrMonitor.ArchiveOperation( AL_EXTRACTION_CLOSE, this, job );
job->mpStorageObject->mpMonitor = 0;
}
job = job->GetNextEntry();
}
//
// Update the monitor, then scan the list for status errors.
//
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
ScanStatus( list );
return mStatus;
}
// PRIVATE MEMBER FUNCTION
//
// int ALArchiveBase::AddJobs( ALEntryList &list )
//
// ARGUMENTS:
//
// list : A list of marked objects to be added to the archive.
//
// RETURNS
//
// AL_SUCCESS if things are going well, < AL_SUCCESS in case of error.
//
// DESCRIPTION
//
// This is a helper function that is called by both Create() and
// Append(). There is enough code here to justify breaking this
// out into a separate module.
//
// All this guy does is sit in a loop, look for marked entries in the
// list, and compress each one into the archive. Before it adds each object
// to the archive, it has to set up the monitor so that progress on the
// selected object will be monitored properly. It has to dink with the
// monitor once again when the object has been compressed. It relies on
// the calling function to have set up the total job size and other info
// that the monitor might need. It also has to set up some of the
// data in the ALEntry object for each job, as not all of this information
// is available until *after* the job has been compressed. For example,
// the storage object's CRC32 gets calculated as a byproduct of the
// compression process.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::AddJobs( ALEntryList AL_DLL_FAR &list )
{
list.mrMonitor.mlObjectStart = 0L; // This will be true for all input jobs
//
// This loop iterates through all of the entries in the list, picking off
// only the marked entries.
//
ALEntry *job = list.GetFirstEntry();
while ( job ) {
if ( job->miMark ) {
//
// We need to keep track of the position in the archive where the compressed
// data is going to go.
//
job->mlCompressedObjectPosition = mpArchiveStorageObject->Tell();
//
// Attach the monitor to the storage object that is going to be inserted
// in the archive.
//
list.mrMonitor.ArchiveOperation( AL_INSERTION_OPEN, this, job );
list.mrMonitor.mlObjectSize = -1L; // This means we ask for it in ALMonitor, after the object is opened
job->mpStorageObject->mpMonitor = &list.mrMonitor;
//
// Compress the object into the archive. Then store the resulting CRC
// the compressed size in the ALEntry object.
//
job->mpCompressionEngine->Compress( *job->mpStorageObject,
*mpArchiveStorageObject );
job->mlCrc32 = job->mpStorageObject->GetCrc32();
job->mpStorageObject->mpMonitor = 0;
if ( job->mpCompressionEngine->mStatus < 0 )
return mStatus = job->mpCompressionEngine->mStatus;
job->mlCompressedSize = mpArchiveStorageObject->Tell() -
job->mlCompressedObjectPosition;
//
// Update the monitor
//
list.mrMonitor.mlJobSoFar += job->mpStorageObject->GetSize();
list.mrMonitor.ArchiveOperation( AL_INSERTION_CLOSE, this, job );
}
job = job->GetNextEntry();
if ( mStatus < 0 )
break;
}
return mStatus;
}
// PRIVATE MEMBER FUNCTION
//
// int ALArchiveBase::AddDirectoryEntries( ALEntryList &list )
//
// ARGUMENTS:
//
// list : The list of ALEntry objects to be written to the directory.
//
// RETURNS
//
// AL_SUCCESS if everything goes well, < AL_SUCCESS otherwise.
//
// DESCRIPTION
//
// This function writes all the entries in the list to the Archive
// directory. It doesn't do a seek() to the start of the directory,
// so the calling routine needs to be absolutely sure that it is in
// the write spot when it invokes this.
//
// This routine leaves the output pointer of the storage object pointing
// at just the right spot to write some more entries. That means you can
// call this function repeatedly as new entries are added to the list.
// The function also terminates the directory properly, so that if you
// don't add any more directory entries, the archive is still ready
// for primetime.
//
// This function is called by WriteDirectory(), and both versions of
// Append().
//
// Writing directory entries is a real simple linear task. The source
// code here should explain it all.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
//
// No call to ArchiveOperation here, either. The setup and everything
// else has to be done by the calling routine.
//
int AL_PROTO ALArchiveBase::AddDirectoryEntries( ALEntryList AL_DLL_FAR &list )
{
ALEntry *job = list.GetFirstEntry();
while ( job ) {
if ( job->miMark ) {
mpArchiveStorageObject->WriteString( job->mpStorageObject->mName.GetSafeName() );
mpArchiveStorageObject->WriteChar( job->mpCompressionEngine->miCompressionType );
job->mpCompressionEngine->WriteEngineData( mpArchiveStorageObject );
mpArchiveStorageObject->WriteChar( job->mpStorageObject->miStorageObjectType );
job->mpStorageObject->WriteStorageObjectData( mpArchiveStorageObject );
mpArchiveStorageObject->WritePortableLong( job->mpStorageObject->GetSize() );
mpArchiveStorageObject->WritePortableLong( job->GetCompressedSize() );
mpArchiveStorageObject->WritePortableLong( job->GetCrc32() );
mpArchiveStorageObject->WritePortableLong( job->mlCompressedObjectPosition );
mpArchiveStorageObject->WriteString( job->GetComment() );
mpArchiveStorageObject->WritePortableLong( job->mpStorageObject->mTimeDate.GetUnixTime() );
mpArchiveStorageObject->WritePortableShort( job->mpStorageObject->mAttributes.PackedAttributes() );
if ( mpArchiveStorageObject->mStatus < 0 )
return mStatus = mpArchiveStorageObject->mStatus;
}
job = job->GetNextEntry();
}
//
// I write out the end of directory string here. But then I back up the
// file pointer so new entries can be appended without causing any trouble
// The end of the directory is denoted by an entry with an empty name.
//
mpArchiveStorageObject->WriteString( "" );
mpArchiveStorageObject->Seek( mpArchiveStorageObject->Tell() - 2 );
return AL_SUCCESS;
}
// PRIVATE MEMBER FUNCTION
//
// long ALArchiveBase::CalculateJobSize( ALEntryList &list )
//
// ARGUMENTS:
//
// list : The list of entries in the job.
//
// RETURNS
//
// This function is used to calculate the total number of bytes that
// are going to have to be moved when performing a Create() or Append()
// operation. We need that info in order to set up a monitor properly
// when its mode is AL_MONITOR_JOB. Naturally, we don't really care
// about the total size when the monitor is in AL_MONITOR_OBJECTS mode.
// Anyway, it returns the total size of all the objects.
//
// DESCRIPTION
//
// If a monitor is running in AL_MONITOR_JOB mode, we need to add up
// the sizes of all the storage objects we are going to process, so
// that we can accurately track our progress from 0 to 100%. In many
// cases, the sizes of all the files will not yet be known, which means
// this routine will have to open the files up and check the values.
// That is why we only call this routine when we have to.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
long AL_PROTO ALArchiveBase::CalculateJobSize( ALEntryList AL_DLL_FAR &list )
{
long total = 0;
ALEntry *job = list.GetFirstEntry();
while ( job ) {
if ( job->miMark ) {
long obj_size;
if ( ( obj_size = job->mpStorageObject->GetSize() ) == -1 ) {
job->mpStorageObject->Open();
obj_size = job->mpStorageObject->GetSize();
job->mpStorageObject->Close();
if ( obj_size == -1 )
return -1;
}
total += obj_size;
}
job = job->GetNextEntry();
}
return total;
}
// PRIVATE MEMBER FUNCTION
//
// long ALArchiveBase::CalculateCompressedJobSize( ALEntryList &list )
//
// ARGUMENTS:
//
// list : The list of compressed jobs to be processed.
//
// RETURNS
//
// The total size of a bunch of compressed objects, not the uncompressed
// size.
//
// DESCRIPTION
//
// When we are monitoring an Extract() command, the monitor object
// gets attached to the Archive, not to the objects that are getting
// sucked out of it. This means that progress is being measured
// against the compressed objects, not the true size objects. So
// before I start the extract, I call this function to see just how
// much compressed space is taken up by the compressed objects in
// the archive.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
long AL_PROTO ALArchiveBase::CalculateCompressedJobSize( ALEntryList AL_DLL_FAR &list )
{
long total = 0;
ALEntry *job = list.GetFirstEntry();
while ( job ) {
if ( job->miMark ) {
if ( job->mlCompressedSize == -1 )
return -1;
else
total += job->mlCompressedSize;
}
job = job->GetNextEntry();
}
return total;
}
//
// int ALArchiveBase::Create( ALEntryList &list )
//
// ARGUMENTS:
//
// list : A list of ALEntry objects describing what is going to
// be stuffed into the archive.
//
// RETURNS
//
// AL_SUCCESS if things went well, <AL_SUCCESS if things sucked.
//
// DESCRIPTION
//
// This is one of two public Create() functions. This is the one you
// call if you have a bunch of storage objects just sitting around
// and you want to put them into an archive. It works by simply
// walking through the list, and adding each object to the archive
// storage object by stuffing it through the compression engine.
//
// This routine has to first create the archive object by opening the
// associated storage object an reserving a long for a pointer to
// the directory (which will have to be written later, since we don't
// have any idea where it is going to be at this time).
//
// After creating the archive storage object, we go through and remove any
// duplicated entries in the input list. If the monitor we will use
// for this operation is in AL_MONITOR_JOB mode, we then have to
// calculate the total job size by scanning all the input files
// (this is really done by CalculateJobSize().) Finally, we call
// AddJobs() to do the real work. Once that is done, we can call
// WriteDirectory() to finish up. Note that there are a few calls
// to ArchiveOperation scattered at key points throughout the process.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::Create( ALEntryList AL_DLL_FAR &list )
{
//
// Miscellaneous: open the archive, set the archive version, initialize
// the monitor. If the storage object is broken, quite now!
//
ALOpenOutputFile archive( *mpArchiveStorageObject );
miVersion = 0x100;
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
if ( mpArchiveStorageObject->mStatus < 0 )
return mStatus = mpArchiveStorageObject->mStatus;
//
// We don't want to create an archive with duplicate entries, so we check here.
//
list.UnmarkDuplicates( list,
"Duplicate entry in list passed to Create()" );
//
// The first four bytes in the archive are a long that points to the
// first byte of the directory. I don't know where the directory is
// going to be, so I just reserve space at this time with a dummy value.
//
mpArchiveStorageObject->WritePortableLong( 0x12345678L );
//
// Set up the monitor.
//
list.mrMonitor.mlJobSoFar = 0L;
if ( list.mrMonitor.miMonitorType == AL_MONITOR_JOB )
list.mrMonitor.mlJobSize = CalculateJobSize( list );
//
// AddJobs() takes care of actually adding the jobs to the archive.
//
AddJobs( list );
//
// All the jobs are written, now I can figure out where the
// directory is in the storage object. I copy it, then write
// it out to the storage object at position 0.
//
mlDirectoryOffset = mpArchiveStorageObject->Tell();
mpArchiveStorageObject->Seek( 0L );
mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
//
// Return without writing the directory if there is an error in the
// archive storage object.
//
if ( mpArchiveStorageObject->mStatus < 0 ) {
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
return mStatus = mpArchiveStorageObject->mStatus;
}
//
// Finally, write out the directory to the storage object.
//
list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE, this, 0 );
WriteDirectory( list );
//
// Update the monitor, check for errors, and blow.
//
list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
ScanStatus( list );
return mStatus;
}
// PRIVATE MEMBER FUNCTIONS
//
// int ALArchiveBase::CopyJobs( ALArchiveBase & source_archive,
// ALEntryList & source_list )
//
// ARGUMENTS:
//
// source_archive : The source for all the ALEntry objects that are
// going to get copied to this.
//
// source_list : The list of ALEntry objects that are going to be copied.
//
// RETURNS
//
// An mStatus value, either AL_SUCCESS or < AL_SUCCESS.
//
// DESCRIPTION
//
// This private member function is used by the Create() and Append() member
// functions. Each of these two public functions has two versions, one
// which compresses freestanding storage objects into an archive, and
// another which copies jobs out of one archive and into this. The
// second versions of the two functions use CopyJobs() to get the
// compressed objects out of one archive and put it into this.
//
// The actual operation of this guy is pretty simple. It would be almost
// trivial without having to take the monitor into account. Basically,
// it just has to work its way through the list of entries. For each
// marked entry, we just seek to the correct position in the input file,
// the copy the correct number of bytes to this.
//
// One thing kind of funny here is that the ALEntryList starts off with
// offsets for the objects within the source archive. But after copying them
// over, we change the offset field in the ALEntry object to reflect the new
// position in this. This means that after this function has completed,
// you ALEntryList object is no longer associated with source_archive, it
// is instead associated with this.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::CopyJobs( ALArchiveBase AL_DLL_FAR &source_archive,
ALEntryList AL_DLL_FAR &source_list )
{
//
// Open the storage object attached to the input archive. The storage object
// attached to this is already open.
//
ALOpenInputFile input( *(source_archive.mpArchiveStorageObject) );
//
// Iterate through the list of entries in the list, selecting only the
// marked entries.
//
ALEntry *job = source_list.GetFirstEntry();
while ( job ) {
if ( job->miMark ) {
//
// Seek the compressed object in the source archive, then update the monitor
// to work properly during the copy operation.
//
source_archive.mpArchiveStorageObject->Seek( job->mlCompressedObjectPosition );
source_list.mrMonitor.mlObjectStart = job->mlCompressedObjectPosition;
source_list.mrMonitor.mlObjectSize = job->mlCompressedSize;
source_list.mrMonitor.ArchiveOperation( AL_COPY_OPEN, this, job );
source_archive.mpArchiveStorageObject->mpMonitor = &source_list.mrMonitor;
//
// Save the new position in the destination archive, then copy the
// whole thing across.
//
job->mlCompressedObjectPosition = mpArchiveStorageObject->Tell();
for ( long i = 0 ; i < job->mlCompressedSize ; i++ ) {
int c = source_archive.mpArchiveStorageObject->ReadChar();
mpArchiveStorageObject->WriteChar( c );
}
//
// Update the monitor now that the copy is complete.
//
source_list.mrMonitor.ArchiveOperation( AL_COPY_CLOSE, this, job );
source_archive.mpArchiveStorageObject->YieldTime();
source_list.mrMonitor.mlJobSoFar += job->mlCompressedSize;
source_archive.mpArchiveStorageObject->mpMonitor = 0;
if ( source_archive.mpArchiveStorageObject->mStatus < 0 )
return mStatus = source_archive.mpArchiveStorageObject->mStatus;
if ( mpArchiveStorageObject->mStatus < 0 )
return mStatus = mpArchiveStorageObject->mStatus;
}
job = job->GetNextEntry();
if ( mStatus < 0 )
break;
}
return mStatus;
}
//
// int ALArchiveBase::Create( ALArchiveBase &source_archive,
// ALEntryList &source_list )
//
// ARGUMENTS:
//
// source_archive : The archive that contains the compressed objects
// we are using to create this.
//
// source_list : The ALEntryList that contains the marked ALEntry
// objects that are going to be inserted in this.
//
// RETURNS
//
// AL_SUCCESS if things went well, < AL_SUCCESS to flag an error.
//
// DESCRIPTION
//
// This is the second version of Create(). Instead of creating a new
// archive by using a bunch of freestanding objects, this guy just
// sucks existing compressed objects out of one archive and copies
// them directly into another. The actual copying gets done in
// CopyJobs().
//
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::Create( ALArchiveBase AL_DLL_FAR &source_archive,
ALEntryList AL_DLL_FAR &source_list )
{
//
// Open the source archive, set the version, and blow if for some reason
// the storage object I am writing to isn't working right.
//
ALOpenOutputFile archive( *mpArchiveStorageObject );
miVersion = 0x100;
if ( mpArchiveStorageObject->mStatus < 0 )
return mStatus = mpArchiveStorageObject->mStatus;
//
// I don't want to create an archive with duplicates, that would be bad.
//
source_list.UnmarkDuplicates( source_list, "Duplicate entry in list passed to Create()" );
//
// At this point, just for fun, I am going to calculate the total
// compressed size of the jobs I am copying. Hey, it looks like I
// could substitute a call to CalculateCompressedSize() here!
//
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
source_list.mrMonitor.mlJobSoFar = 0L;
source_list.mrMonitor.mlJobSize = 0L;
for ( ALEntry *job = source_list.GetFirstEntry();
job != 0;
job = job->GetNextEntry() ) {
if ( job->GetMark() )
source_list.mrMonitor.mlJobSize += job->mlCompressedSize;
}
//
// Since I am creating a new archive, I write a long out as a place
// holder for the directory pointer. When I am done copying jobs,
// I'll come back here and write a pointer to the directory.
//
mpArchiveStorageObject->WritePortableLong( 0x12345678L );
//
// Now copy the data.
//
CopyJobs( source_archive, source_list );
//
// Write out the directory offset, then the directory itself.
//
mlDirectoryOffset = mpArchiveStorageObject->Tell();
mpArchiveStorageObject->Seek( 0L );
mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
if ( mpArchiveStorageObject->mStatus < 0 ) {
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
return mStatus = mpArchiveStorageObject->mStatus;
}
source_list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE,this, 0 );
WriteDirectory( source_list );
source_list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
//
// Update the error status, and then we are done.
//
ScanStatus( source_list );
return mStatus;
}
//
// int ALArchiveBase::Append( ALEntryList &list )
//
// ARGUMENTS:
//
// list : A list of objects to append to this.
//
// RETURNS
//
// AL_SUCCESS if things go okay, < AL_SUCCESS if they didn't.
//
// DESCRIPTION
//
// This routine is one of the public functions. It is called to add
// a list of standalone objects to an existing archive, this.
// To accomplish this, we have to read in the existing directory, then
// add the new batch of objects to the archive. Finally, I write out
// the old directory, then the directory for the new batch of objects.
//
// There is another version of Append() that takes as input a list of
// entries that are in another archive.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::Append( ALEntryList AL_DLL_FAR &list )
{
ALEntryList old_list;
//
// Open the storage object for this.
//
ALOpenInputFile archive( *mpArchiveStorageObject );
//
// I read in the current directory for this archive. I am going to
// write over the directory with new stuff, so I will have to write it
// back out later.
//
ReadDirectory( old_list );
if ( mStatus < 0 )
return mStatus;
//
// The list of new objects I am going to add needs to be scanned for
// duplicates. First I clear duplicate entries from the list itself.
// Then I clear any duplicates between the current list and the
// stuff already in the archive.
//
list.UnmarkDuplicates( list, "Duplicate entry in list passed to Append()" );
list.UnmarkDuplicates( old_list, "Duplicate entry in list passed to Append()" );
//
// I get the monitor set up, for the batch of entries I am about to do.
//
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
list.mrMonitor.mlJobSoFar = 0L;
if ( list.mrMonitor.miMonitorType == AL_MONITOR_JOB )
list.mrMonitor.mlJobSize = CalculateJobSize( list );
//
// The new entries start at the position currently occupied by the
// directory. I seek to that point, then call AddJobs() to do the
// dirty work.
//
mpArchiveStorageObject->Seek( mlDirectoryOffset );
AddJobs( list );
if ( mStatus < 0 ) {
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
return mStatus;
}
//
// Now that all the new stuff is in the archive, I can figure
// out where the directory belongs, and write it out to position
// 0 in the archive.
//
mlDirectoryOffset = mpArchiveStorageObject->Tell();
mpArchiveStorageObject->Seek( 0L );
mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
if ( mpArchiveStorageObject->mStatus < 0 ) {
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
return mStatus = mpArchiveStorageObject->mStatus;
}
//
// Now I write the old directory out, and then add in the new
// directory entries.
//
list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE, this, 0 );
WriteDirectory( old_list );
AddDirectoryEntries( list );
//
// Update the monitor, check for errors, then leave.
//
list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
ScanStatus( list );
return mStatus;
}
//
// int ALArchiveBase::Append( ALArchiveBase &source_archive,
// ALEntryList &source_list )
//
// ARGUMENTS:
//
// source_archive : The archive where the objects that we are going
// use can be found.
//
// source_list : The ALEntryList that has a batch of marked entries.
//
// RETURNS
//
// AL_SUCCESS if things work, < AL_SUCCESS if they don't.
//
// DESCRIPTION
//
// This append function works just like the previous one, except it
// is appending jobs that have already been compressed and can be found
// in a different archive. It has to go through exactly the same process,
// which consists of reading the current directory in from this, appending
// the new compressed objects to this, then writing out the old directory
// and the new list.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::Append( ALArchiveBase AL_DLL_FAR &source_archive,
ALEntryList AL_DLL_FAR &source_list )
{
ALEntryList old_list;
//
// Open the storage object associated with this.
//
ALOpenInputFile archive( *mpArchiveStorageObject );
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
//
// I have to read the current directory into memory, because as soon as
// I start to write objects out to this, I am going to obliterate
// the directory.
//
ReadDirectory( old_list );
if ( mStatus < 0 ) {
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
return mStatus;
}
//
// We don't want to create an archive that has duplicate entries, that would
// be a bad thing. So I first comb all the duplicate entries out of the list
// of objects to append. I then compare that list for duplicates against
// the list of objects already in the library, and comb out any matches
// there as well.
//
source_list.UnmarkDuplicates( source_list, "Duplicate entry in list passed to Append()" );
source_list.UnmarkDuplicates( old_list, "Duplicate entry in list passed to Append()" );
//
// I am going to start writing new stuff at the location where the
// the directory starts right now.
//
mpArchiveStorageObject->Seek( mlDirectoryOffset );
//
// Before starting to copy jobs, I have to set up the monitor.
// This includes calculating the total number of compressed bytes
// in all the marked jobs. I could do this a lot easier by calling
// the CalculateCompressedBytes() function, but it's too late to change now.
//
source_list.mrMonitor.mlJobSoFar = 0L;
source_list.mrMonitor.mlJobSize = 0L;
for ( ALEntry *job = source_list.GetFirstEntry();
job != 0;
job = job->GetNextEntry() ) {
if ( job->GetMark() )
source_list.mrMonitor.mlJobSize += job->mlCompressedSize;
}
//
// CopyJobs() does the hard work for me.
//
CopyJobs( source_archive, source_list );
if ( mStatus < 0 ) {
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
return mStatus;
}
//
// The jobs are now in, I just have to update the directory with
// the old files and the new files.
//
mlDirectoryOffset = mpArchiveStorageObject->Tell();
mpArchiveStorageObject->Seek( 0L );
mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
if ( mpArchiveStorageObject->mStatus < 0 ) {
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
return mStatus = mpArchiveStorageObject->mStatus;
}
source_list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE, this, 0 );
WriteDirectory( old_list );
AddDirectoryEntries( source_list );
//
// Wrap it up.
//
source_list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
ScanStatus( source_list );
return mStatus;
}
//
// int ALArchiveBase::ReadDirectory( ALEntryList &list )
//
// ARGUMENTS:
//
// list : The target for the directory listing.
//
// RETURNS
//
// AL_SUCCESS or < AL_SUCCESS if things don't work.
//
// DESCRIPTION
//
// This function reads the directory from archive this and places the
// results in the list parameter. I have to apologize for the fact
// that it is so long. The only thing I can say in my defense is that
// even though it is really long, it is also really simple, not tricky
// bits here.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::ReadDirectory( ALEntryList AL_DLL_FAR &list )
{
list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_READ, this, 0 );
ALOpenInputFile archive( *mpArchiveStorageObject );
if ( mpArchiveStorageObject->mStatus < 0 )
return mStatus = mpArchiveStorageObject->mStatus;
//
// First I seek to the start of the directory (offset found at 0), and
// read in the version. This function only supports the directory
// structure defined in version 0x100.
//
mpArchiveStorageObject->Seek( 0 );
mpArchiveStorageObject->ReadPortableLong( mlDirectoryOffset );
mpArchiveStorageObject->Seek( mlDirectoryOffset );
mpArchiveStorageObject->ReadPortableShort( miVersion );
if ( miVersion != 0x100 )
return mStatus.SetError( AL_INVALID_ARCHIVE,
"%s is not a valid archive file",
mpArchiveStorageObject->mName.GetSafeName() );
//
// Read in any customized archive data defined by a derived class.
//
ReadArchiveData();
//
// Read in the comment, deleting the old one if necessary.
//
if ( mszComment )
delete[] mszComment;
mszComment = mpArchiveStorageObject->ReadString();
//
// Now, the big loop. I have to read in each entry, one at a time, and
// add it to the list. If I broke this out into a separate routine it
// would make the whole thing a lot more manageable.
//
for ( ; ; ) {
if ( mpArchiveStorageObject->mStatus < 0 )
return mStatus = mpArchiveStorageObject->mStatus;
char *name = mpArchiveStorageObject->ReadString();
if ( name == 0 )
break;
//
// The directory ends with a blank name.
//
if ( strlen( name ) == 0 ) {
delete[] name;
break;
}
//
// Derived classes are responsible for providing a version of
// CreateCompressionEngine() that will convert the engine_type
// integer into a created compression engine. The derived class is
// then also responsible for reading in the engine data from the archive.
//
int engine_type = mpArchiveStorageObject->ReadChar();
ALCompressionEngine *engine = CreateCompressionEngine( engine_type );
if ( engine )
engine->ReadEngineData( mpArchiveStorageObject );
else {
char *temp = mpArchiveStorageObject->ReadString();
if ( temp )
delete[] temp;
return mStatus.SetError( AL_CANT_CREATE_ENGINE,
"Failure creating compression engine for object %s",
name );
}
//
// Now we go through a nearly identical process to create the storage object.
// The derived class is responsible for writing a CreateStorageObject()
// function that converts an object_type integer to a created storage
// object. The derived class also has to read in the storage object
// data.
//
int object_type = mpArchiveStorageObject->ReadChar();
ALStorage *storage_object = CreateStorageObject( name, object_type );
delete[] name; // Don't need it any more
name = 0;
if ( storage_object )
storage_object->ReadStorageObjectData( mpArchiveStorageObject );
else {
char *temp = mpArchiveStorageObject->ReadString();
if ( temp )
delete[] temp;
return mStatus.SetError( AL_CANT_CREATE_STORAGE_OBJECT,
"Failure creating storage object for object %s",
name );
}
//
// The rest of the stuff in the entry is pretty straightforward.
//
mpArchiveStorageObject->ReadPortableLong( storage_object->mlSize );
ALEntry *job = new ALEntry( list, storage_object, engine );
mpArchiveStorageObject->ReadPortableLong( job->mlCompressedSize );
mpArchiveStorageObject->ReadPortableLong( job->mlCrc32 );
mpArchiveStorageObject->ReadPortableLong( job->mlCompressedObjectPosition );
char *comment = mpArchiveStorageObject->ReadString();
job->SetComment( comment );
if ( comment )
delete[] comment;
long unix_time;
mpArchiveStorageObject->ReadPortableLong( unix_time );
storage_object->mTimeDate.SetTimeDate( unix_time );
short int packed_attributes;
mpArchiveStorageObject->ReadPortableShort( packed_attributes );
storage_object->mAttributes.SetFromPackedAttributes( packed_attributes );
}
list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_READ, this, 0 );
return mStatus;
}
//
// int ALArchiveBase::WriteArchiveData()
//
// ARGUMENTS:
//
// None.
//
// RETURNS
//
// AL_SUCCESS if everything writes out okay, or < AL_SUCCESS for trouble.
//
// DESCRIPTION
//
// Derived classes can write out customized archive data, for whatever
// reasons they deem necessary. Our base class has nothing that it
// needs to save, so it just writes out a zero length string, which takes
// two bytes to save. Instead of using WriteString like I ought to, for
// some reason I write the 0 out directly.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::WriteArchiveData()
{
return mpArchiveStorageObject->WritePortableShort( 0 );
}
//
// int ALArchiveBase::ReadArchiveData()
//
// ARGUMENTS:
//
// None.
//
// RETURNS
//
// AL_SUCCESS if things went well, < AL_SUCCESS it things go sour.
//
// DESCRIPTION
//
// The base class doesn't store anything in the archive specific
// data area. That means that when I am reading the archive specific
// data in, I should see a zero length string, which is the same thing
// as a single short of value 0. I read it in and verify it here.
//
// Note that derived classes are free to override this function, but
// nothing we ship with ArchiveLib does so.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::ReadArchiveData()
{
short temp;
mpArchiveStorageObject->ReadPortableShort( temp );
AL_ASSERT( temp == 0, "ReadArchiveData(): archive data != 0" );
return mpArchiveStorageObject->mStatus;
}
//
// int ALArchiveBase::Delete( ALEntryList &list,
// ALArchiveBase &destination )
//
// ARGUMENTS:
//
// list : A list of the objects to delete from the archive.
//
// destination : The destination archive, which is the result after
// deleting all the objects from this.
//
// RETURNS
//
// AL_SUCCESS if things went well, < AL_SUCCESS otherwise.
//
// DESCRIPTION
//
// Delete is really more like copy. It doesn't actually delete objects
// out of an existing archive. Instead it deletes by excluding the
// specified objects from a copy command, copying only those objects
// that aren't in the delete list. The resulting archive looks as if
// it is one that has had objects deleted from it.
//
// After deleting, we do some renaming to make it look like the delete
// operation did what you really expected. As a result, the original
// archive (this) has been renamed to a backup, and the new archive
// now has the original name of this.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
int AL_PROTO ALArchiveBase::Delete( ALEntryList AL_DLL_FAR &list,
ALArchiveBase AL_DLL_FAR &destination )
{
destination.SetComment( mszComment );
list.ToggleMarks();
destination.Create( *this, list );
list.ToggleMarks();
ALName temp = mpArchiveStorageObject->mName;
mpArchiveStorageObject->RenameToBackup();
destination.mpArchiveStorageObject->Rename( (const char*)temp );
if ( destination.mStatus < 0 )
return mStatus = destination.mStatus;
return mStatus;
}
//
// int ALArchiveBase::FillListBox( HWND hDlg, int list_box = -1 )
//
// ARGUMENTS:
//
// hDlg : The handle of the dialog box that has a list box in it.
// If the value of list_box is set to -1, it means that
// hDlg doesn't refer to a dialog box, instead it refers
// to the actual list box itself.
//
// list_box : This is set to the id of the list box control found in
// the hDlg dialog box. If this value is set to -1, it
// means the hDlg parameter is the handle of the list box.
//
// RETURNS
//
// The count of marked items stuffed into the list box.
//
// DESCRIPTION
//
// This is a quicky useful function to read the names of all the
// storage objects out of this, then stuffing them all into a list
// box.
//
// REVISION HISTORY
//
// May 23, 1994 1.0A : First release
//
#if defined( AL_WINDOWS_GUI )
int AL_PROTO ALArchiveBase::FillListBox( HWND hDlg, int list_box /* = -1 */ )
{
ALEntryList list;
HWND window;
ReadDirectory( list );
if ( list_box != -1 )
window = GetDlgItem( hDlg, (short int) list_box );
else
window = hDlg;
int count;
if ( ( count = list.FillListBox( window ) ) == 0 ) {
if ( mStatus < 0 ) {
SendMessage( window, LB_RESETCONTENT, 0, 0 );
SendMessage( window,
LB_ADDSTRING,
0,
(LPARAM)( (LPSTR) "Error!" ) );
}
}
return count;
}
#endif