//
// STATUS.CPP
//
//  Source file for ArchiveLib 1.0
//
//  Copyright (c) 1994 Greenleaf Software, Inc.
//  All Rights Reserved
//
// CONTENTS
//
//  ALStatus::operator new()
//  ALStatus::ALStatus()
//  ALStatus::~ALStatus()
//  ALStatus::SetError()
//  ALStatus::GetStatusString()
//  ALStatus::GetStatusDetail()
//  ALStatus::operator = ()
//
//
// DESCRIPTION
//
//  This file contains LibMain() and the WEP() for ArchiveLib DLLs.
//  We don't really do anything exciting in the WEP, it is just
//  here for decoration.  LibMain() has to set up memory allocation
//  for Borland.
//
// REVISION HISTORY
//
//  May 26, 1994  1.0A  : First release
//
//

#include "arclib.h"
#pragma hdrstop

#include <stdio.h>
#include <stdarg.h>

//
// void * ALStatus::operator new( size_t size )
//
// ARGUMENTS:
//
//  size  :  The number of bytes needed to create a new ALStatus 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 get into 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 26, 1994  1.0A  : First release
//

#if defined( AL_BUILDING_DLL )
void AL_DLL_FAR * AL_PROTO ALStatus::operator new( size_t size )
{
    return ::new char[ size ];
}
#endif

//
//  ALStatus::ALStatus()
//
// ARGUMENTS:
//
//  None.
//
// RETURNS
//
//  Nothing, this is a constructor.
//
// DESCRIPTION
//
//  This is the only constructor for objects of class ALStatus.  It
//  initializes the detail length member to 129, which is a const and
//  won't change.  The initial status is AL_SUCCESS, and there is no
//  detail string to start with.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

AL_PROTO ALStatus::ALStatus() : miStatusDetailLength( 129 )
{
    miStatus = AL_SUCCESS;
    mszStatusDetail = 0;
}

//
// ALStatus::~ALStatus()
//
// ARGUMENTS:
//
//  None, destructor.
//
// RETURNS
//
//  None, destructor.
//
// DESCRIPTION
//
//  The destructor has to free up any space allocated for the detailed
//  error status string.  That's all.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

AL_PROTO ALStatus::~ALStatus()
{
    if ( mszStatusDetail )
        delete[] mszStatusDetail;
}

//
// int ALStatus::SetError( int error, const char AL_DLL_FAR *fmt, ... )
//
// ARGUMENTS:
//
//  error   :  The new error code to set the miStatus member to.  A value
//             less than 0 (AL_SUCCESS) will always be interpreted as
//             an error.
//
//  fmt     :  A sprintf style formatting string.  This is for the 
//             message that is going to go into the status detail message.
//   
//  ...     :  Any additional arguments needed by the formatting string.
//
//
// RETURNS
//
//  error, the error code that just got passed in.
//
// DESCRIPTION
//
//  I don't know why I did the status detail allocation the way it is
//  done here, it is really stupid. I should just allocate whatever space
//  is necessary after formatting the string.  This will probably be
//  fixed in 1.x.
//
//  This function is used to set the status of an object to an error state.
//  Normally this is done by sending an error code, along with a detailed
//  message explaining what went wrong and why.  Note that to clear
//  and error state, you can pass AL_SUCCESS for the error code and
//  0 for the format.  The object will look like it is healthy and happy
//  after that.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

int AL_PROTO ALStatus::SetError( int error, const char AL_DLL_FAR *fmt, ... )
{
    char detail[ 256 ];
    va_list argptr;

    miStatus = error;
    if ( fmt == 0 ) {
        if ( mszStatusDetail )
            delete[] mszStatusDetail;
            mszStatusDetail = 0;
    } else {
        va_start( argptr, fmt );
        vsprintf( detail, fmt, argptr );
        va_end( argptr );
        if ( mszStatusDetail == 0 )
            mszStatusDetail = new char[ miStatusDetailLength ];
        if ( mszStatusDetail ) {
            strncpy( mszStatusDetail, detail, miStatusDetailLength - 1 );
            mszStatusDetail[ miStatusDetailLength - 1 ] = '\0';
        }
    }
    return error;
}

//
// const char * ALStatus::GetStatusString()
//
// ARGUMENTS:
//
//  None.
//
// RETURNS
//
//  A short ASCII translation of the current error code.
//
// DESCRIPTION
//
//  Rather than just printing an error code number, it is usually more
//  helpful to translate that number into ASCII text, so a user or
//  programmer can read the description.  This function is used to
//  do just that.  It translates the current error code into a short
//  ASCII text string.  Note that this is not the same as the detail 
//  string, which is tailored for each specific occurrence of an error code.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

const char AL_DLL_FAR * AL_PROTO ALStatus::GetStatusString()
{
    switch ( miStatus ) {
        case AL_SUCCESS                   : return "Success";
        case AL_END_OF_FILE               : return "End of file";
        case AL_CANT_OPEN_BUFFER          : return "Can't allocate buffer";
        case AL_CANT_CREATE_ENGINE        : return "Can't create compression engine";
        case AL_CANT_CREATE_STORAGE_OBJECT: return "Can't create storage object";
        case AL_CANT_ALLOCATE_MEMORY      : return "Memory allocation failure";
        case AL_RENAME_ERROR              : return "Error renaming file";
        case AL_CANT_OPEN_FILE            : return "Can't open file";
        case AL_SEEK_ERROR                : return "Seek error";
        case AL_READ_ERROR                : return "Read error";
        case AL_WRITE_ERROR               : return "Write error";
        case AL_DELETE_ERROR              : return "File deletion error";
        case AL_ILLEGAL_PARAMETER         : return "Illegal parameter";
        case AL_INTERNAL_ERROR            : return "Internal error";
        case AL_USER_ABORT                : return "User abort";
        case AL_SERVER_NOT_PRESENT        : return "Server not present";
        case AL_COMPRESSION_TYPE_MISMATCH : return "Mismatch in compression type";
        case AL_NEED_LENGTH               : return "Missing length parameter";
        case AL_CRC_ERROR                 : return "CRC Error";
        case AL_COMPARE_ERROR             : return "Comparison error";
        case AL_UNKNOWN_COMPRESSION_TYPE  : return "Unknown compression type";
        case AL_UNKNOWN_STORAGE_OBJECT    : return "Unknown type of storage object";
        case AL_INVALID_ARCHIVE           : return "Invalid archive";
        case AL_LOGIC_ERROR               : return "Logic error";
        case AL_BACKUP_FAILURE            : return "Could not create backup";
        case AL_GETSEL_ERROR              : return "Error getting selections from list box";
        case AL_DUPLICATE_ENTRY           : return "Duplicate entry";
        default                           : return "Unknown error";
    }
}

//
// const char * ALStatus::GetStatusDetail() const
//
// ARGUMENTS:
//
//  None.
//
// RETURNS
//
//  Guaranteed to return a valid character string.
//
// DESCRIPTION
//
//  Whenever we set the error code for an object in ArchiveLib, we
//  call ALStatus::SetError().  At the same time that we set the
//  error code of the object to a non-zero value, we supply a formatted
//  string providing some detail about when and where the error
//  took place, maybe even including some other information provided by the
//  O/S.  That information is stored in the detail string, which is a
//  private data member.  This function provides the ability to get at
//  that detail string.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

const char AL_DLL_FAR * AL_PROTO ALStatus::GetStatusDetail() const
{
    if ( mszStatusDetail )
        return mszStatusDetail;
    else if ( miStatus == AL_SUCCESS )
        return "No errors";
    else
        return "Unable to allocate memory for error detail message";
}

//
// ALStatus & ALStatus::operator = ( ALStatus &rhs )
//
// ARGUMENTS:
//
//  rhs  : Another ALStatus object that I want to copy into this object.
//
// RETURNS
//
//  A reference to this.
//
// DESCRIPTION
//
//  Somewhere in ArchiveLib I want to be able to copy one status 
//  into another.  This function does just that.  It has to allocate
//  new space to make a copy of the detail string, and be sure to
//  free up any old space, and all that.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

ALStatus AL_DLL_FAR & AL_PROTO ALStatus::operator = ( ALStatus AL_DLL_FAR &rhs )
 {
    if ( rhs.mszStatusDetail == 0 ) {
        if ( mszStatusDetail ) {
            delete[] mszStatusDetail;
            mszStatusDetail = 0;
        }
    } else {
        if ( mszStatusDetail == 0 )        
            mszStatusDetail = new char[ miStatusDetailLength ];
        if ( mszStatusDetail )
            strcpy( mszStatusDetail, rhs.mszStatusDetail );
    }
    miStatus = rhs.miStatus;
    return *this;
}