915 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			915 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
//
 | 
						|
// ARCENTRY.CPP
 | 
						|
//
 | 
						|
//  Source file for ArchiveLib 1.0
 | 
						|
//
 | 
						|
//  Copyright (c) Greenleaf Software, Inc. 1994
 | 
						|
//  All Rights Reserved
 | 
						|
//
 | 
						|
// CONTENTS
 | 
						|
//
 | 
						|
//  ALEntry::operator new()
 | 
						|
//  ALEntryList::operator new()
 | 
						|
//  ALEntry::ALEntry()
 | 
						|
//  ALEntry::~ALEntry()
 | 
						|
//  ALEntry::Duplicate()
 | 
						|
//  ALEntry::InsertBefore()
 | 
						|
//  ALEntry::SetComment()
 | 
						|
//  ALEntry::CompressionRatio()
 | 
						|
//  ALEntryList::ALEntryList()
 | 
						|
//  ALEntryList::~ALEntryList()
 | 
						|
//  ALEntryList::SetMarkState()
 | 
						|
//  ALEntryList::ToggleMarks()
 | 
						|
//  ALEntry::GetNextEntry()
 | 
						|
//  ALEntryList::UnmarkDuplicates()
 | 
						|
//  ALEntryList::DeleteUnmarked()
 | 
						|
//  ALEntryList::FillListBox()          //Windows GUI only
 | 
						|
//  ALEntryList::SetMarksFromListBox()  //Windows GUI only
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This file contains the source code for two class, ALEntry and
 | 
						|
//  ALEntryList.  They are so tightly bound together that it made
 | 
						|
//  sense to go ahead and stick them both in the same file.
 | 
						|
//
 | 
						|
//  Class ALEntry describes the state of an object that is in an archive.
 | 
						|
//  It contains a pointer to a storage object and a compression engine,
 | 
						|
//  which define what goes in the archive and how it is put there.  It
 | 
						|
//  also defines how to extract it.  The ALEntry object also contains 
 | 
						|
//  miscellaneous items that go with the object in the archive, such
 | 
						|
//  as its time/date stamp, its CRC, and its comment.
 | 
						|
//
 | 
						|
//  You have to create an ALEntry *before* you put an object into an
 | 
						|
//  archive.  The archiving class member function figure out what you want
 | 
						|
//  to do by looking at objects of the ALEntry.  You also have to read
 | 
						|
//  the contents of the archive into a list of ALEntry objects before you
 | 
						|
//  can extract anything.
 | 
						|
//
 | 
						|
//  ALEntryList is simply a class that is used to keep a linked list of
 | 
						|
//  ALEntry objects together.  ALEntryList objects are passed to the high
 | 
						|
//  level ALArchiveBase functions for common operations, such as Create()
 | 
						|
//  Extract(), and so on.  You get an ALEntryList back when you read the
 | 
						|
//  directory of an archive.  The ALEntryList object tells you everything
 | 
						|
//  there is to know about the object stored in the archive.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//  May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
//
 | 
						|
 | 
						|
#include "arclib.h"
 | 
						|
#include <utility.h>
 | 
						|
#pragma hdrstop
 | 
						|
 | 
						|
//
 | 
						|
// void * ALEntry::operator new( size_t size )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  size  :  The number of bytes needed to create a new ALEntry 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 create 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 schemed, 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 ALEntry::operator new( size_t size )
 | 
						|
{
 | 
						|
  return ::new char[ size ];
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
//
 | 
						|
// void * ALEntryList::operator new( size_t size )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  size  :  The number of bytes that are going to need to be allocated
 | 
						|
//           to create a new ALEntryList object.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A pointer to the newly allocated storage area.  A 0 is returned if no
 | 
						|
//  storage could be found.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Look at the explanation for ALEntry::operator new(), directly above
 | 
						|
//  this guy.  The description is identical.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
#if defined( AL_BUILDING_DLL )
 | 
						|
void AL_DLL_FAR * AL_PROTO ALEntryList::operator new( size_t size )
 | 
						|
{
 | 
						|
  return ::new char[ size ];
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
//
 | 
						|
// ALEntry::ALEntry( ALEntryList &list,
 | 
						|
//                   ALStorage *object,
 | 
						|
//                   ALCompressionEngine *engine )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  list        :  A reference to the list the ALEntry object is going to
 | 
						|
//                 be linked into. ALEntry objects aren't allowed to exist
 | 
						|
//                 without being in a list.
 | 
						|
//
 | 
						|
//  object      :  A pointer to the storage object that is attached to this
 | 
						|
//                 entry.  Remember, this is an unopened storage object,
 | 
						|
//                 so it is not consuming very much space.  It is okay
 | 
						|
//                 to have a zillion or so of these just lying around.
 | 
						|
//                 Don't forget that the ALEntry dtor is going to destroy
 | 
						|
//                 this guy for you, don't you dare try it!.
 | 
						|
//
 | 
						|
//  engine      :  A pointer to the compression engine that is going to
 | 
						|
//                 be used to create/insert/extract the storage object
 | 
						|
//                 to/from the archive.  Just like with the compression
 | 
						|
//                 engine, it is a low cost object, and you can keep lots
 | 
						|
//                 of them on hand.  This engine will be destroyed in the
 | 
						|
//                 ALEntry dtor, so be sure to give up any claim you might
 | 
						|
//                 have on this guy.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing, this is a ctor.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This ctor creates a new ALEntry object.  You can do this by hand, but
 | 
						|
//  frequently you will ask ArchiveLib to create ALEntry objects for you,
 | 
						|
//  maybe by pulling them out of a list box, or reading them in from and
 | 
						|
//  archive.  Note that ALEntry objects aren't allowed to ever exist
 | 
						|
//  outside a list, each entry absolutely has to appear in a list.  
 | 
						|
//
 | 
						|
//  dtor issues relating to the ALEntry object are very important.  Since
 | 
						|
//  ALEntry objects always are part of a list, it made sense for the 
 | 
						|
//  ALEntryList destructor to clean up all the entries in its list.  So
 | 
						|
//  even though you might have created this ALEntry object, you don't get to
 | 
						|
//  delete it, that will be done for you.
 | 
						|
//
 | 
						|
//  Also, the storage object and compression engine in the ALEntry object
 | 
						|
//  are going to be automatically destroyed by the ALEntry dtor.  Don't
 | 
						|
//  even think about trying it yourself!
 | 
						|
//
 | 
						|
//  You can think of an ALEntryList as a directory of an archive, and each
 | 
						|
//  ALEntry object in the list is a single entry in that directory.  
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
AL_PROTO ALEntry::ALEntry( ALEntryList &list,
 | 
						|
                          ALStorage *object,
 | 
						|
                          ALCompressionEngine *engine )
 | 
						|
: mrList( list ) // Initialize our own pointer to the list we will
 | 
						|
  // be a member of.
 | 
						|
{
 | 
						|
  mpNextItem = this;
 | 
						|
  mpPreviousItem = this;
 | 
						|
  mpStorageObject = object;
 | 
						|
  mpCompressionEngine = engine;
 | 
						|
  mlCompressedSize = -1;
 | 
						|
  mlCompressedObjectPosition = -1;
 | 
						|
  miMark = 1; //Always construct with the mark turned on
 | 
						|
  mszComment = 0;
 | 
						|
  //
 | 
						|
  // I check for the object member to be non-zero because of a clunky design
 | 
						|
  // choice I made a while back.  Each ALEntryList has an ALEntry member that
 | 
						|
  // points to the first and last members of the list.  I could have (and
 | 
						|
  // probably should have) made the root of the list just be a pair of pointers,
 | 
						|
  // instead of a dummy ALEntry.  Anyway, I can tell that dummy entry apart
 | 
						|
  // from the valid entries by virtue of the fact that it has a null
 | 
						|
  // pointer in its object pointer.
 | 
						|
  //
 | 
						|
  // So anyway, when I create this dummy object, I don't want to try to add
 | 
						|
  // it to the list, because by definition it is already in the list.  So
 | 
						|
  // I do a check before adding any ALEntry to the list.
 | 
						|
  //
 | 
						|
  if ( object )
 | 
						|
    InsertBefore( *list.mpListHead );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// ALEntry::~ALEntry()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing, this is a destructor.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This destructor should normally be called by the ALEntryList dtor.  The
 | 
						|
//  list that owns an entry will always try to delete it when the list
 | 
						|
//  is deleted.
 | 
						|
//
 | 
						|
//  The ALEntry object tries to delete three dynamically allocated objects
 | 
						|
//  that it has control over:  the storage object, the compression engine,
 | 
						|
//  and the comment.  In each case it won't do it if the object pointer
 | 
						|
//  is 0.  This provides a convenient mechanism for you to steal a storage
 | 
						|
//  object from an ALEntry.  All you have to do is take the pointer, and
 | 
						|
//  then sent ALEntry::mpStorageObject to 0.  This is an especially useful
 | 
						|
//  thing to do for ALMemory objects.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
AL_PROTO ALEntry::~ALEntry()
 | 
						|
{
 | 
						|
  AL_ASSERT( GoodTag(), "~ALEntry: Attempting to delete invalid object" );
 | 
						|
  if ( mszComment )
 | 
						|
    delete[] mszComment;
 | 
						|
  if ( mpStorageObject != 0 )
 | 
						|
    delete mpStorageObject;
 | 
						|
  if ( mpCompressionEngine != 0 )
 | 
						|
    delete mpCompressionEngine;
 | 
						|
  AL_ASSERT( mpNextItem != 0 ,"~ALEntry: next item is null" );
 | 
						|
  AL_ASSERT( mpPreviousItem != 0, "~ALEntry: previous item is null" );
 | 
						|
 | 
						|
  ALEntry *next_job = mpNextItem;
 | 
						|
  ALEntry *previous_job = mpPreviousItem;
 | 
						|
 | 
						|
  if ( next_job != this ) {
 | 
						|
    next_job->mpPreviousItem = previous_job;
 | 
						|
    previous_job->mpNextItem = next_job;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Note that I check the object twice, one at the start of the dtor, and
 | 
						|
  // once again at the end.  With all the linked list and dynamic deletion
 | 
						|
  // being done here, it seems like it would be really easy to hose things
 | 
						|
  // up if any mistakes were made.
 | 
						|
  //
 | 
						|
  AL_ASSERT( GoodTag(), "~ALEntry: Attempting to delete invalid object" );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int ALEntry::Duplicate( ALEntryList &list )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  list  : A list of ALEntry objects to scan.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  0 if the entry is not duplicated, 1 if it is.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is used to scan a list of ALEntry objects to see if
 | 
						|
//  any of them have the same name as this.  Unmarked objects are ignored.
 | 
						|
//  All the function does is zip through the ALEntryList, checking each
 | 
						|
//  marked member for an ASCII match with the name of the storage object
 | 
						|
//  pointed to by this.  You can see that the case sensitivity of this
 | 
						|
//  is observed when making the comparison.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int AL_PROTO ALEntry::Duplicate( ALEntryList &list )
 | 
						|
{
 | 
						|
  char *name = mpStorageObject->mName;
 | 
						|
  int case_sensitive = mpStorageObject->mName.mCase == AL_MIXED;
 | 
						|
  ALEntry *job = list.GetFirstEntry();
 | 
						|
  while ( job ) {
 | 
						|
    if ( job->GetMark() && job != this ) {
 | 
						|
      if ( case_sensitive ) {
 | 
						|
        if ( strcmp( name, job->mpStorageObject->mName ) == 0 )
 | 
						|
          return 1;
 | 
						|
      } else {
 | 
						|
        if ( stricmp( name, job->mpStorageObject->mName ) == 0 )
 | 
						|
          return 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    job = job->GetNextEntry();
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// PROTECTED FUNCTION
 | 
						|
//
 | 
						|
// void ALEntry::InsertBefore( ALEntry &job )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  job : A reference to another job in the target list.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is used inside the ALEntryList class to add a new ALEntry
 | 
						|
//  object to an ALEntryList.  Since the list is a doubly linked list, the
 | 
						|
//  code to do the job is pretty simple.  It would have been a little more
 | 
						|
//  complicated if I used a pair of pointers in the ALEntryList to start
 | 
						|
//  the list, instead of a dummy ALEntry object.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
void AL_PROTO ALEntry::InsertBefore( ALEntry &job )
 | 
						|
{
 | 
						|
  mpNextItem = &job;
 | 
						|
  mpPreviousItem = job.mpPreviousItem;
 | 
						|
  (job.mpPreviousItem)->mpNextItem = this;
 | 
						|
  job.mpPreviousItem = this;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int ALEntry::SetComment( const char *comment )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  comment : The new comment that is going to be associated with the
 | 
						|
//            ALEntry object.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  AL_SUCCESS if the new comment was set, < 0 if an error occurred.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Before adding an object to an archive, you may want to change or set
 | 
						|
//  its comment.  You do so by calling this function before performing any
 | 
						|
//  operation that will write the directory, such as Create() or 
 | 
						|
//  WriteDirectory().  It has to dynamically allocate the space in the
 | 
						|
//  ALEntry object in order to store the new comment.  This is good for
 | 
						|
//  you, because it means you don't have to worry about who owns the comment
 | 
						|
//  you just passed in.  
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int AL_PROTO ALEntry::SetComment( const char AL_DLL_FAR *comment )
 | 
						|
{
 | 
						|
  if ( mszComment )
 | 
						|
    delete[] mszComment;
 | 
						|
  if ( comment ) {
 | 
						|
    mszComment = new char[ strlen( comment ) + 1 ];
 | 
						|
    if ( mszComment )
 | 
						|
      strcpy( mszComment, comment );
 | 
						|
    else
 | 
						|
      return mrList.mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
 | 
						|
                                     "Failed to allocate memory when "
 | 
						|
                                     "adding comment to storage object %s",
 | 
						|
                                     (char *) mpStorageObject->mName );
 | 
						|
  } else
 | 
						|
    mszComment = 0;
 | 
						|
  return AL_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int ALEntry::CompressionRatio()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The integer representing the compression ratio.  The ration is a number
 | 
						|
//  from 0 to 100 (or maybe more) with 0 being perfect compression.
 | 
						|
//
 | 
						|
//  It is possible to get a -1 back from this routine if the compression
 | 
						|
//  ratio is not presently known.  This will be the case if you have
 | 
						|
//  not created the archive yet, or have a new object that hasn't been
 | 
						|
//  inserted yet.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This calculates and returns the compression ratio.  We don't store the
 | 
						|
//  ratio in ALEntry, because it is so darned easy to calculate when
 | 
						|
//  we need it.  However, there are going to be times when we don't have
 | 
						|
//  it.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int AL_PROTO ALEntry::CompressionRatio()
 | 
						|
{
 | 
						|
  long uncompressed_size = mpStorageObject->GetSize();
 | 
						|
 | 
						|
  if ( uncompressed_size  <= 0 )
 | 
						|
    return -1;
 | 
						|
  if ( mlCompressedSize <= 0 )
 | 
						|
    return -1;
 | 
						|
  return (int) ( 100 * mlCompressedSize / uncompressed_size );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// ALEntryList::ALEntryList( ALMonitor * monitor = 0 )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  monitor : A pointer to a monitor that will be used whenever we are
 | 
						|
//            processing objects in the list.  If no argument is supplied,
 | 
						|
//            the default argument value of 0 is used.  When the ctor sees
 | 
						|
//            that the value of the monitor pointer is 0, it assigns the
 | 
						|
//            default monitor instead.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  No returns from constructors.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Constructing an ALEntryList object doesn't take much work.  I have to
 | 
						|
//  initialize two data members.  The first is the pointer to the monitor
 | 
						|
//  that will be used when processing objects in the list.  The second is
 | 
						|
//  the root of the linked list, which is a dummy ALEntry object.  Note
 | 
						|
//  that the root is created as a dummy by setting the storage object pointer
 | 
						|
//  to 0.
 | 
						|
//
 | 
						|
//  The default monitor is defined below.  If you don't specify a real
 | 
						|
//  monitor, you get the default, which is a do nothing function.  Everyone
 | 
						|
//  can share one instance of the default monitor, because it doesn't have
 | 
						|
//  any data members to be concerned about.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
ALMonitor ALDefaultMonitor( AL_MONITOR_OBJECTS );
 | 
						|
 | 
						|
AL_PROTO ALEntryList::ALEntryList( ALMonitor AL_DLL_FAR * monitor /* = 0 */ )
 | 
						|
: mrMonitor( monitor ? *monitor : ALDefaultMonitor )
 | 
						|
{
 | 
						|
  mpListHead = new ALEntry( *this, 0, 0 );
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// ALEntryList::~ALEntryList()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  None, destructors don't get to return anything.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  The destructor for ALEntryList goes through the list and deletes every
 | 
						|
//  ALEntry object it finds.  Note that this also causes the ALEntry
 | 
						|
//  object to destroy its storage object and compression engine.  Once
 | 
						|
//  the whole list is obliterated, the list head ALEntry object can be
 | 
						|
//  safely deleted.  Then the whole thing is done.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
AL_PROTO ALEntryList::~ALEntryList()
 | 
						|
{
 | 
						|
  AL_ASSERT( GoodTag(), "~ALEntryList: attempting to delete invalid object" );
 | 
						|
  ALEntry *job = GetFirstEntry();
 | 
						|
  while ( job ) {
 | 
						|
    ALEntry *next_job = job->GetNextEntry();
 | 
						|
    delete job;
 | 
						|
    job = next_job;
 | 
						|
  }
 | 
						|
  if ( mpListHead )
 | 
						|
    delete mpListHead;
 | 
						|
  AL_ASSERT( GoodTag(), "~ALEntryList: attempting to delete invalid object" );
 | 
						|
}
 | 
						|
 | 
						|
// PROTECTED FUNCTION
 | 
						|
//
 | 
						|
//  int ALEntryList::SetMarkState( const char *name,
 | 
						|
//                                 short int new_state )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  name       :  The object name, specifying which storage objects are
 | 
						|
//                to have their state set.  This name can include
 | 
						|
//                wild card characters.  Note that passing a null
 | 
						|
//                pointer here will cause a match to *every* object name.
 | 
						|
//
 | 
						|
//  new_state  :  The new state that the ALEntry mark should be set to.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A count of the number of ALEntry objects whose state was changed.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This protected function is used internally to help out a couple of the 
 | 
						|
//  public functions.  It rips through every entry of the list, checks to
 | 
						|
//  see if storage object associate with the entry has a name that matches
 | 
						|
//  the wildcard specification, and sets the mark if it does.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int AL_PROTO ALEntryList::SetMarkState( const char AL_DLL_FAR *name,
 | 
						|
                                       short int new_state )
 | 
						|
{
 | 
						|
  int count = 0;
 | 
						|
 | 
						|
  ALEntry *job = GetFirstEntry();
 | 
						|
  while ( job ) {
 | 
						|
    if ( name ) {
 | 
						|
      if ( job->mpStorageObject->mName.WildCardMatch( name ) ) {
 | 
						|
        job->SetMarkState( new_state );
 | 
						|
        count++;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      job->SetMarkState( new_state );
 | 
						|
      count++;
 | 
						|
    }
 | 
						|
    job = job->GetNextEntry();
 | 
						|
  }
 | 
						|
  return count;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int ALEntryList::ToggleMarks()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A count of the number of entries whose mark was changed.
 | 
						|
//  (Just the total number of entries.)
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This simple member function just goes through the entire list,
 | 
						|
//  toggling the mark state of every entry.  In other words, if the mark
 | 
						|
//  was previously set, it will now be cleared, and vice versa.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int AL_PROTO ALEntryList::ToggleMarks()
 | 
						|
{
 | 
						|
  int count = 0;
 | 
						|
  ALEntry *job = GetFirstEntry();
 | 
						|
  while ( job ) {
 | 
						|
    job->SetMarkState( (short int) !job->GetMark() );
 | 
						|
    job = job->GetNextEntry();
 | 
						|
    count++;
 | 
						|
  }
 | 
						|
  return count;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// ALEntry * ALEntry::GetNextEntry()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A pointer to the next entry in the list.  If the next entry is the
 | 
						|
//  list head, it means we have reached the end of the list, and a value
 | 
						|
//  of 0 is returned.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is used to iterate through the list.  Each entry has 
 | 
						|
//  a pointer to the next and previous entries, so this function is really
 | 
						|
//  simple.  The only complications comes from trying to detect the end of
 | 
						|
//  the list, which is denoted by the list head instance of ALEntry.  We
 | 
						|
//  can tell it apart from all the legitimate entries by the fact that
 | 
						|
//  its storage object is 0.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
ALEntry AL_DLL_FAR * AL_PROTO ALEntry::GetNextEntry()
 | 
						|
{
 | 
						|
  ALEntry *next_entry = this->mpNextItem;
 | 
						|
  //
 | 
						|
  // The list head has the special case where both the compression engine
 | 
						|
  // and storage object pointers are 0, and that makes the end of the list.
 | 
						|
  //
 | 
						|
  if ( mpNextItem->mpStorageObject == 0 )
 | 
						|
    return 0;
 | 
						|
  else
 | 
						|
    return next_entry;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// ALEntry * ALEntryList::GetFirstEntry()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A pointer to the first valid ALEntry object in the list, or 0 if there
 | 
						|
//  are no entries.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  If you are going to iterate through the entire list, this function is
 | 
						|
//  used to start you off.  It gets the first entry in the list by call
 | 
						|
//  GetNextEntry() for the list head.  Don't worry about what happens if
 | 
						|
//  the list is empty, the GetNextEntry() code figures that out with no
 | 
						|
//  problem, and returns a 0.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
ALEntry AL_DLL_FAR * AL_PROTO ALEntryList::GetFirstEntry()
 | 
						|
{
 | 
						|
  return mpListHead->GetNextEntry();
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// void ALEntryList::UnmarkDuplicates( ALEntryList &list,
 | 
						|
//                                     const char *error_message = 0 )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  list           :  The list that is going to be compared to this.
 | 
						|
//
 | 
						|
//  error_message  : Each entry in this that turns out to have a duplicate
 | 
						|
//                   entry in the list argument will not only be unmarked,
 | 
						|
//                   it will also have its error status set, if an error
 | 
						|
//                   message is provide.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  Nothing.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  I think this function is a little confusing.  At first blush, you would
 | 
						|
//  probably expect this function to scan all the items in a single list,
 | 
						|
//  and unmark any object that turn out to have duplicates elsewhere 
 | 
						|
//  in the list.  Unfortunately, it doesn't work that way.
 | 
						|
//
 | 
						|
//  Instead, this function goes through the list specified by this, and
 | 
						|
//  checks to see if each entry in this appears in the list specified by
 | 
						|
//  the list parameter.  This means that we are working with two different
 | 
						|
//  lists, which certainly offers plenty of chances to get confused.
 | 
						|
//
 | 
						|
//  Anyway, each entry in this that turns out to have a duplicate gets its
 | 
						|
//  mark cleared.  If the calling program specifies and error message,
 | 
						|
//  the entry also gets its mStatus error member set to flag this as an
 | 
						|
//  error.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
void AL_PROTO ALEntryList::UnmarkDuplicates( ALEntryList &list,
 | 
						|
                                            const char *error_message /* = 0 */ )
 | 
						|
{
 | 
						|
  ALEntry *job = GetFirstEntry();
 | 
						|
  while ( job ) {
 | 
						|
    if ( job->GetMark() ) {
 | 
						|
      if ( job->Duplicate( list ) ) {
 | 
						|
        job->ClearMark();
 | 
						|
        if ( error_message && error_message[ 0 ] != '\0' )
 | 
						|
          job->mpStorageObject->mStatus.SetError(
 | 
						|
            AL_DUPLICATE_ENTRY,
 | 
						|
            error_message );
 | 
						|
      }
 | 
						|
    }
 | 
						|
    job = job->GetNextEntry();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// int ALEntryList::DeleteUnmarked()
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  None.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The number of entries that are deleted.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  Sometimes you may have a list with a whole bunch of unmarked entries.
 | 
						|
//  Those unmarked entries are just sitting there taking up space, so it
 | 
						|
//  would be handle to be able to just delete them.  That is what this
 | 
						|
//  function does.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int AL_PROTO ALEntryList::DeleteUnmarked()
 | 
						|
{
 | 
						|
  ALEntry *job;
 | 
						|
  int count = 0;
 | 
						|
 | 
						|
  job = GetFirstEntry();
 | 
						|
  while ( job ) {
 | 
						|
    ALEntry *next_job = job->GetNextEntry();
 | 
						|
    if ( job->GetMark() == 0 ) {
 | 
						|
      count++;
 | 
						|
      delete job;
 | 
						|
    }
 | 
						|
    job = next_job;
 | 
						|
  }
 | 
						|
  return count;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// in ALEntryList::FillListBox( HWND hDlg, int list_box = -1 )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  hDlg       : The handle of the dialog box that contains the list box
 | 
						|
//               control.  If the list box is not a control in a dialog,
 | 
						|
//               set the next parameter to -1, and just pass the handle
 | 
						|
//               of the list box in this argument.
 | 
						|
//
 | 
						|
//  list_box   : The ID of the list box, if and only if the list box is
 | 
						|
//               in a dialog box specified by by the hDlg argument.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  The number of entries that were stuffed into the list box.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is a handy helper when using the Windows GUI.  It goes
 | 
						|
//  through an ALEntryList, and finds all the marked entries.  For every
 | 
						|
//  marked entry, it stuffs the name of the storage object into the list box.
 | 
						|
//  This means that if you are planning on letting the user select a list
 | 
						|
//  of storage objects, you can initialize the list with just one 
 | 
						|
//  function call.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
#if defined( AL_WINDOWS_GUI )
 | 
						|
int AL_PROTO ALEntryList::FillListBox( HWND hDlg, int list_box /* = -1 */ )
 | 
						|
{
 | 
						|
  HWND window;
 | 
						|
 | 
						|
  if ( list_box != -1 )
 | 
						|
    window = GetDlgItem( hDlg, (short int) list_box );
 | 
						|
  else
 | 
						|
    window = hDlg;
 | 
						|
  SendMessage( window, LB_RESETCONTENT, 0, 0 );
 | 
						|
  ALEntry *job = GetFirstEntry();
 | 
						|
  int count = 0;
 | 
						|
  while ( job ) {
 | 
						|
    if ( job->GetMark() ) {
 | 
						|
      count++;
 | 
						|
      SendMessage( window,
 | 
						|
                  LB_ADDSTRING,
 | 
						|
                  0,
 | 
						|
                  (LPARAM)( (LPSTR) job->mpStorageObject->mName ) );
 | 
						|
    }
 | 
						|
    job = job->GetNextEntry();
 | 
						|
  }
 | 
						|
  if ( count == 0 )
 | 
						|
    SendMessage( window,
 | 
						|
                LB_ADDSTRING,
 | 
						|
                0,
 | 
						|
                (LPARAM)( (LPSTR) "<none>" ) );
 | 
						|
  return count;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// int ALEntryList::SetMarksFromListBox( HWND hDlg, int list_box = - 1 )
 | 
						|
//
 | 
						|
// ARGUMENTS:
 | 
						|
//
 | 
						|
//  hDlg        : The handle of the dialog box that contains the list box.
 | 
						|
//                If the list box control is standalone window, this parameter
 | 
						|
//                can be its handle, if the list_box argument is set to -1.
 | 
						|
//
 | 
						|
//  list_box    : The ID of the list box, if and only if it is contained in
 | 
						|
//                a dialog box whose handle is specified in the hDlg param.
 | 
						|
//
 | 
						|
// RETURNS
 | 
						|
//
 | 
						|
//  A count of the number of items whose marks were set.
 | 
						|
//
 | 
						|
// DESCRIPTION
 | 
						|
//
 | 
						|
//  This function is called after you have given a user the opportunity
 | 
						|
//  to set and clear items in a multiselection list box.  Once the user
 | 
						|
//  has done so, you can call this function, which will go through the
 | 
						|
//  list and set all the marks that have been set in the list box by the
 | 
						|
//  user.  Note that it will not clear the marks on any of the ALEntry
 | 
						|
//  objects in the list, you might want to do that first.
 | 
						|
//
 | 
						|
// REVISION HISTORY
 | 
						|
//
 | 
						|
//   May 23, 1994  1.0A  : First release
 | 
						|
//
 | 
						|
 | 
						|
int AL_PROTO ALEntryList::SetMarksFromListBox( HWND hDlg, int list_box /* = -1 */ )
 | 
						|
{
 | 
						|
  HWND window;
 | 
						|
 | 
						|
  if ( list_box != -1 )
 | 
						|
    window = GetDlgItem( hDlg, (short int) list_box );
 | 
						|
  else
 | 
						|
    window = hDlg;
 | 
						|
 | 
						|
  WORD count = (WORD) SendMessage( window, LB_GETSELCOUNT, 0, 0L );
 | 
						|
  int *items = new int[ count ];
 | 
						|
  if ( items == 0 )
 | 
						|
    return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
 | 
						|
                            "Memory allocation failure in SetMarksFromListBox()" );
 | 
						|
#ifdef AL_FLAT_MODEL
 | 
						|
  if ( count != (WORD) SendMessage( window, LB_GETSELITEMS, count, (LPARAM) ( items ) ) ) {
 | 
						|
#else
 | 
						|
    if ( count != (WORD) SendMessage( window, LB_GETSELITEMS, count, (LPARAM) ( (int _far * ) items ) ) ) {
 | 
						|
#endif
 | 
						|
      mStatus.SetError( AL_LOGIC_ERROR,
 | 
						|
                       "Logic error in SetMarksFromListBox()."
 | 
						|
                       "Mismatch in select count from list box." );
 | 
						|
      delete[] items;
 | 
						|
      return AL_LOGIC_ERROR;
 | 
						|
    }
 | 
						|
    for ( WORD i = 0 ; i < count ; i++ ) {
 | 
						|
      WORD length = (WORD) SendMessage( window, LB_GETTEXTLEN, (short int) items[ i ], 0L );
 | 
						|
      AL_ASSERT( length != (WORD) LB_ERR, "SetMarksFromListBox: LB_ERR returned from list box" );
 | 
						|
      if ( length > 0 ) {
 | 
						|
        char *name = new char[ length + 1 ];
 | 
						|
        if ( name ) {
 | 
						|
          if ( SendMessage( window, LB_GETTEXT, (short int) items[ i ], (LPARAM)( (LPSTR) name ) ) >= 0 )
 | 
						|
            SetMarks( name );
 | 
						|
          delete[] name;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    delete[] items;
 | 
						|
    return count;
 | 
						|
  }
 | 
						|
 | 
						|
#endif
 |