campo-sirio/arch/arcentry.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

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