//-*-c++-*--------------------------------------------------------------
//  nsrEAttr.cpp
//   Source file that handles UDF extended attributes
//
//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1995. All rights reserved
// 
// The contents of this software are proprietary and confidential to
// the Hewlett- Packard Company, and are limited in distribution to
// those with a direct need to know.  Individuals having access to
// this software are responsible for maintaining the confidentiality
// of the content and for keeping the software secure when not in use.
// Transfer to any party is strictly forbidden other than as expressly
// permitted in writing by Hewlett-Packard Company.  Unauthorized
// transfer to or possession by any unauthorized party may be a
// criminal offense.
//
//          RESTRICTED RIGHTS LEGEND
//
//      Use,  duplication,  or disclosure by the U. S. Government is
//      subject to restrictions as set forth in subparagraph (c) (1)
//      (ii) in the Rights in Technical Data and Computer Software
//      clause in DFARS 252.227-7013 or any other successor clause.
//
//          HEWLETT-PACKARD COMPANY
//          3000 Hanover St.
//          Palo Alto, CA  94304 U.S.A.
//
//      Rights for non-DOD U.S. Government Departments and Agencies
//      are as set forth in FAR 52.227-19 (c) (1,2) or any other
//      successor clause.
//
//  CLASSES:
//    EARegistryEntry
//    EARegistry
//    EAView
//    ShortEAView
//    LongEAView
//    EAViewItem
//    ShortEAViewItem
//    EAViewHeaderItem
//    LongEAViewItem
//    GroupOfShortsEAViewItem
//    EASpace
//    EmbeddedEASpace
//    ExternalEASpace
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfCore/ss/nsrEAttr.cpp,v $
// $Revision: 1.9 $ $Author: hmgr $
// $State: Exp $    $Locker:  $
// $Date: 96/05/21 23:16:42 $
//

#include <nsrLib.h>

// Define a constant to signify an invalid index for the methods that index into physical
// or virtual lists of view items.
const NSR::INT16     InvalidIndex = -1;


// Define the number of spaces for each level of indenting, for printing the various objects.
#if defined ENABLE_PRINTING
  const NSR::UINT16  INDENT_AMOUNT   = 3;    // number of spaces per indent level
#endif



// Create some static null objects, to be returned in error cases where a reference must be
// returned, but the values in the object itself are not meaningful.  If you can think of a
// better way to do this, you're welcome to do it!  Also define and initialize a static
// member variable of EASpace, for use in creating and deleting these objects.
NSR::ByteArray       NSR::nullBA;
NSR::LogExtentList  *NSR::nullLEL = NULL;
NSR::UINT32          NSR::EASpace::numInstances = 0;



// Initialization code for the EARegistry:
NSR::EARegistryEntry    NSR::EARegistry::eaRegistryEntries_[EA_REGISTRY__MAX_ENTRIES] = {
    EARegistryEntry (ea_Hdr_Key,                0,      0,  String(""),                     ea_MayBeInEitherSpace,      False,  False),
    EARegistryEntry (ea_ISO_CharSetInfo_Key,    1,      1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_ISO_AltPermissions_Key, 3,      1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_ISO_FileTimes_Key,      5,      1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_ISO_InfoTimes_Key,      6,      1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_ISO_DeviceSpec_Key,     12,     1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_ISO_Default_Key,        2047,   1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Impl_FreeSpace_Key,     2048,   1,  String("*UDF FreeEASpace"),     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Impl_OS2ExtAtt_Key,     2048,   1,  String("*UDF OS/2 EA"),         ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Impl_OS2EALen_Key,      2048,   1,  String("*UDF OS/2 EALength"),   ea_ShouldBeInEmbeddedSpace, True,   False),
    EARegistryEntry (ea_Impl_MacVolInfo_Key,    2048,   1,  String("*UDF Mac VolumeInfo"),  ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Impl_MacFinderInfo_Key, 2048,   1,  String("*UDF Mac FinderInfo"),  ea_ShouldBeInEmbeddedSpace, True,   False),
    EARegistryEntry (ea_Impl_MacUniqueIDTable_Key,2048, 1,  String("*UDF Mac UniqueIDTable"),ea_MayBeInEitherSpace,     True,   False),
    EARegistryEntry (ea_Impl_MacResourceFork_Key,2048,  1,  String("*UDF Mac ResourceFork"),ea_MayBeInEitherSpace,      True,   True),
    EARegistryEntry (ea_Impl_Default_Key,       2048,   1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Appl_HPNovellAttr_Key,  65536,  1,  String("*HP Novell Attributes"),ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Appl_HPNovellTrustee_Key,65536, 1,  String("*HP Novell Trustee"),   ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Appl_HPICBTable_Key,    65536,  1,  String("*HP ICB Table"),        ea_MayBeInEitherSpace,      True,   False),
    EARegistryEntry (ea_Appl_Default_Key,       65536,  1,  String(""),                     ea_MayBeInEitherSpace,      True,   False),
};
NSR::UINT32  NSR::EARegistry::numEntries_ = 19;      // IMPORTANT: must match number in table


void
NSR::SAIEAInspector::clear(void)
{
// Sets everything up just the way it was immediately after construction.
        attrType_ = (UINT32)NSR::ea_Unknown;
        attrSubtype_ = 1;
        attrLen_ = 0;
        attrID_ = String();
        fixedLen_ = 0;
        varyingLen_ = 0;
        error_ = NSR::error_None;
} // clear

void
NSR::SAIEAInspector::fromISO(CONST_BYTE_PTR rbp,
                             UINT32 len)
{
    if (len < sizeof(UINT32)) {
        error_ = NSR::error_EA_Invalid_EA_length;
        return;
    }
    // Can't use EAType from EAPrefix descriptor because of enum problem
    // on Borland compiler with values > 65535.
    NSR::Uint32         tmpType(0);
    tmpType.fromISO(rbp);
    attrType_ = (UINT32)tmpType;

    switch (attrType_) {
    case NSR::ea_CharSetInfo:
        fixedLen_ = 17;
        if (len < fixedLen_) {
            this->clear();
            error_ = NSR::error_EA_Invalid_EA_length;
            return;
        }
        this->setupvaryingLength(rbp);
        this->setupAttrLength(rbp);
        attrID_ = String();
        break;
    case NSR::ea_AlternatePerms:
        fixedLen_ = 18;
        varyingLen_ = 0;
        if (len < fixedLen_) {
            this->clear();
            error_ = NSR::error_EA_Invalid_EA_length;
            return;
        }
        this->setupAttrLength(rbp);
        attrID_ = String();
        break;
    case NSR::ea_FileTimes:
        fixedLen_ = 20;
        if (len < fixedLen_) {
            this->clear();
            error_ = NSR::error_EA_Invalid_EA_length;
            return;
        }
        this->setupvaryingLength(rbp);
        this->setupAttrLength(rbp);
        attrID_ = String();
        break;
    case NSR::ea_InfoTimes:
        fixedLen_ = 20;
        if (len < fixedLen_) {
            this->clear();
            error_ = NSR::error_EA_Invalid_EA_length;
            return;
        }
        this->setupvaryingLength(rbp);
        this->setupAttrLength(rbp);
        attrID_ = String();
        break;
    case NSR::ea_DeviceSpec:
        fixedLen_ = 24;
        if (len < fixedLen_) {
            this->clear();
            error_ = NSR::error_EA_Invalid_EA_length;
            return;
        }
        this->setupvaryingLength(rbp);
        this->setupAttrLength(rbp);
        attrID_ = String();
        break;
    case NSR::ea_ImplementUse:
    //case NSR::ea_ApplicationUse:              // !!! doesn't work on some compilers because of
    case 65536:                                 // !!! enum/int problem--workaround: use actual value
        fixedLen_ = 48;
        if (len < fixedLen_) {
            this->clear();
            error_ = NSR::error_EA_Invalid_EA_length;
            return;
        }
        this->setupvaryingLength(rbp);
        this->setupAttrLength(rbp);
        this->setupId(rbp);
        break;
    default:
        // All invalid types
        this->clear();
        error_ = NSR::error_EA_Invalid_EA_type;
        return;
    } // end of switch
    
    // Do some more checking for invalid lengths.
    if (fixedLen_+varyingLen_ > attrLen_) {
        this->clear();
        error_ = NSR::error_EA_Invalid_EA_length;
        return;
    }
    // Check for an error on the attrID String object.
    if (attrID_.error()) {
        this->clear();
        error_ = attrID_.error();
        return;
    }
    error_ = NSR::error_None;

} // fromISO

void
NSR::SAIEAInspector::setupAttrLength(CONST_BYTE_PTR rbp)
{
    NSR::EAPrefix       eaPrefix(0);
    eaPrefix.fromISO (rbp);
    attrLen_ = eaPrefix.attrLen();
} // setupAttrLength

void
NSR::SAIEAInspector::setupvaryingLength(CONST_BYTE_PTR rbp)
{
    //NSR::ImplEAPrefix impleaPrefix(0);
    //impleaPrefix.fromISO (rbp);
    //varyingLen_ = impleaPrefix.implUseLen();
    // Can't use EAType from ImplEAPrefix descriptor because there may or may not
    // be enough valid data in the source buffer to process the full descriptor
    // (including the Id String)--the fromISO method may choke on bad data, or get
    // a memory protection violation.
    NSR::Uint32         tmpType(0);
    tmpType.fromISO(rbp+12);
    varyingLen_ = (UINT32)tmpType;
} // setupLengths

void
NSR::SAIEAInspector::setupId(CONST_BYTE_PTR rbp)
{
    NSR::ImplEAPrefix   impleaPrefix(0);
    impleaPrefix.fromISO (rbp);
    String myTempId = impleaPrefix.id();    // !!! this always returns 23 (length of regid field)
    attrID_ = ((CONST_CHAR_PTR)myTempId);   // !!! isn't there a better way to get true length?
} // setupLengthsAndId
// !!! ------------------- end of temporary EA Inspector --------------------------

// --------------------------------------------------------------------------------
// Methods of the EARegistryEntry class:



//----------------------------------------------------------------------
// METHOD: EARegistryEntry::eaCategory  --CJB
//  
//  Method to get EACategory (it is computed at run time)
//
// ARGS:
//  UINT32                  eaType          IN  type of this EA
//  UINT8                   eaSubType       IN  subtype of this EA
//
// RETURNS:
//  EACategory -- Any EACategory enum value, including ea_InvalidCategory.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This is entirely determined from the Type and subType. This is just a
//  deterministic helping function, available for internal and external use.
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EACategory
NSR::EARegistryEntry::eaCategory(UINT32 eaType, UINT8 /*eaSubType*/)
{
    if (eaType > 0  &&  eaType < 2048)
        return NSR::ea_ISOCategory;
    if (eaType == 2048)
        return NSR::ea_ImplUseCategory;
    if (eaType == 65536)
        return NSR::ea_ApplUseCategory;
    return NSR::ea_InvalidCategory;
} // eaCategory

// --------------------------------------------------------------------------------
// Methods of the EARegistry class:



//----------------------------------------------------------------------
// METHOD: EARegistry::add  --CJB
//  
//  Enters an EARegistryEntry into the registry.
//
// ARGS:
//  const EARegistryEntry&  eare            IN  completed EA registry entry
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  New entry is added to the registry.
// 
// ERRORS:
//  error_REGISTRY_Invalid_key.
//  error_REGISTRY_Duplicate_key.
//  error_No_free_space.
// 
// NOTES:
//  This method checks for validity and uniqueness, and then adds the new entry
//  to the Registry.
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EARegistry::add (const EARegistryEntry & eare,
                      Error & err )
{
    // Check for invalid input data.
    if (eare.regKey() == ea_InvalidKey) {
        err = error_REGISTRY_Invalid_key;
        return;
    }
    
    // See if this entry already exists in the registry.  We don't allow duplicate regKeys,
    // nor a duplicate combination of Type, subType and Id.
    for (UINT32 index=0; index<numEntries_; index++) {
        if (eaRegistryEntries_[index].regKey() != eare.regKey()) {
            if (eaRegistryEntries_[index].eaType() != eare.eaType())
                continue;
            if (eaRegistryEntries_[index].eaSubType() != eare.eaSubType())
                continue;
            if (eaRegistryEntries_[index].eaString() != eare.eaString())
                continue;
        }
        err = error_REGISTRY_Duplicate_key;
        return;
    }

    // The entry doesn't already exist.  See if we have any more available entries.
    if (numEntries_ >= EA_REGISTRY__MAX_ENTRIES) {
        err = error_No_free_space;
        return;
    }

    // Copy this entry into the next available registry entry.
    err = error_None;
    eaRegistryEntries_[numEntries_++] = eare;
} // add


//----------------------------------------------------------------------
// METHOD: EARegistry::at  --CJB
//  
//  Fetches an entry by regKey.
//
// ARGS:
//  UINT32                  regKey          IN  key to search for in registry
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EARegistryEntry* -- returns NULL if an entry isn't found with the
//  specified regKey. Otherwise returns a const pointer to the found EARegistryEntry.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EARegistryEntry*
NSR::EARegistry::at (UINT32 regKey,
                     Error & err )
{
    for (UINT32 index=0; index<numEntries_; index++) {
        if (eaRegistryEntries_[index].regKey() == regKey) {
            err = error_None;
            return &eaRegistryEntries_[index];
        }
    }

    err = error_REGISTRY_Entry_not_found;
    return NULL;
} // at


//----------------------------------------------------------------------
// METHOD: EARegistry::find  --CJB
//  
//  Fetches an entry by EAType, EASubType and EAString.
//
// ARGS:
//  UINT32                  eaType          IN  type to search for
//  UINT8                   eaSubType       IN  subtype to search for
//  String&                 eaString        IN  string to search for
//  Error&                  err             OUT error status
//
// RETURNS:
//  UINT32 -- returns the regKey of the found entry. If no entry is found,
//  returns ea_InvalidKey.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//  Symetrical to "at"--returns the Key.  If no specific entry matches, it returns
//  the key of the appropriate "default" entry.  If none are appropriate, it returns
//  an error.
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EARegistry::find (UINT32 eaType,
                       UINT8 eaSubType,
                       String &eaString,
                       Error & err )
{
    err = error_None;

    for (UINT32 index=0; index<numEntries_; index++) {
        if (eaRegistryEntries_[index].eaType() != eaType)
            continue;
        if (eaRegistryEntries_[index].eaSubType() != eaSubType)
            continue;
        if (eaRegistryEntries_[index].eaString() != eaString)
            continue;
        return eaRegistryEntries_[index].regKey();
    }

    // A specific match was not found in the registry.  If eaType is an allowed value, return the
    // key of the appropriate default entry; otherwise, return an error.
    if (eaType > 0  &&  eaType < 2048)
        return ea_ISO_Default_Key;
    if (eaType == 2048)
        return ea_Impl_Default_Key;
    if (eaType == 65536)
        return ea_Appl_Default_Key;

    err = error_REGISTRY_Entry_not_found;
    return ea_InvalidKey;
} // find


//----------------------------------------------------------------------
// METHOD: EARegistry::getFirstKey  --CJB
//  
//  Returns the first regKey value
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  UINT32 -- returns the first regKey. If there are no regKeys, returns ea_InvalidKey.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//  The ordering that determines the first regKey is not defined.  The only
//  guarantee is that after getFirstKey/getNextKey has been called until there
//  are no more keys, every key will have been returned once and only once.
//  See EARegistry::getNextKey.
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EARegistry::getFirstKey (Error & err)
{
    // Return the first key if it exists.
    if (numEntries_ > 0) {
        err = error_None;
        return eaRegistryEntries_[0].regKey();
    }

    err = error_REGISTRY_Entry_not_found;
    return ea_InvalidKey;
} // getFirstKey


//----------------------------------------------------------------------
// METHOD: EARegistry::getNextKey  --CJB
//  
//  After a call to getFirstKey, successive calls to this method return
//  successive regKey values.
//
// ARGS:
//  UINT32                  regKey          IN  the previous regKey returned
//  Error&                  err             OUT error status
//
// RETURNS:
//  UINT32 -- returns the first regKey. If there are no more regKeys, returns ea_InvalidKey.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//  The ordering that determines the next regKey is not defined.  The only
//  guarantee is that after getFirstKey/getNextKey has been called until there
//  are no more keys, every key will have been returned once and only once.
//  See EARegistry::getFirstKey.
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EARegistry::getNextKey (UINT32 previousKey,
                             Error & err)
{
    UINT32              index;

    // FIrst advance the index to the key following the caller's previousKey.
    for (index=0; index<numEntries_; index++) {
        if (eaRegistryEntries_[index].regKey() == previousKey) {
            index++;
            break;
        }
    }

    // Return this next key if it exists.
    if (index < numEntries_) {
        err = error_None;
        return eaRegistryEntries_[index].regKey();
    }

    err = error_REGISTRY_Entry_not_found;
    return ea_InvalidKey;
} // getNextKey




// --------------------------------------------------------------------------------
// Methods of the EAView class:



//----------------------------------------------------------------------
// METHOD: EAView::copy_  --CJB
//  
//  Method to do the actual work of the copy constructor and assignment operator.
//
// ARGS:
//  const EAView&           x               IN  source object to copy
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Copies the source object into this object; all member variables reflect this.
// 
// ERRORS:
//  error_Null_pointer
//  error_EA_Invalid_EA_type
//  error_No_free_store
// 
// NOTES:
//  If the source object is the same as this, nothing is done--ie., we don't
//  allow copying to oneself.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EAView::copy_ (const EAView & x,
                    Error & err)
{
    EAViewItem      *src;           // source item ptr
    EAViewItem      *dst;           // Destination item ptr

    if (&x == this) {
        err = error_None;
        return;
    }

    parentGOSViewItem_ = NULL;
    myEASpace_ = NULL;

    // Copy the list.
    list_ = new RWOrdered(*(x.list_));      // first do a shallow copy
    // Then replace each item with a new copy of that item.
    for (UINT32 i = 0; i < list_->entries(); i++) {
        src = (EAViewItem*)x.list_->at(i);     // get source item ptr
        if (src == NULL) {
            list_->clear();
            err = error_Null_pointer;
            return;      
        }
        // Sure like to get rid of this switch.
        switch (src->isA()) {
        case ShortEAViewItemType:
            dst = new ShortEAViewItem((ShortEAViewItem&)*src);
            break;
        case EAViewHeaderItemType:
            dst = new EAViewHeaderItem((EAViewHeaderItem&)*src);
            break;
        case LongEAViewItemType:
            dst = new LongEAViewItem((LongEAViewItem&)*src);
            break;
        case GroupOfShortsEAViewItemType:
            dst = new GroupOfShortsEAViewItem((GroupOfShortsEAViewItem&)*src);
            break;
        default:
            list_->clear();
            err = error_EA_Invalid_EA_type;
            return;
        } // switch
        if (dst == NULL) {
            list_->clear();
            err = error_No_free_store;
            return;      
        }
        dst->parentView(this);      // set parentView in new item to this view
        list_->at(i) = dst;
    } // for

    err = error_None;
} // copy_


//----------------------------------------------------------------------
// METHOD: EAView::~EAView  --CJB
//  
//  Destructor.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Object is destroyed; all memory is deallocated.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method deletes every EA view item that it holds, but does not
//  deallocate any disk space.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAView::~EAView(void)
{
    // Remove and delete every item in the list.
    this->clear();

    if (list_ != NULL) {
        delete list_;
        list_ = NULL;
    }
} // EAView destructor


//----------------------------------------------------------------------
// METHOD: EAView::clear  --CJB
//  
//  Removes all entries and resets object to same state it had immediately after construction.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  All EA view items held by this boject are deleted; all memory is freed;
//  all member variables are reset to their initial values.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method deletes all internal objects that have previously been created,
//  but does not deallocate any disk space.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EAView::clear(void)
{
    Error           err;
    UINT16          numEntries;
    
    if (list_ == NULL)
        return;

    // Remove and delete every item in the list.
    numEntries = this->numEntries();
    for (UINT16 i=0; i<numEntries; i++) {
        EAViewItem *item = removeAt (0, err);
        if (item == NULL)
            break;
        delete item;
    }
} // clear


//----------------------------------------------------------------------
// METHOD: EAView::numEntries  --CJB
//  
//  Returns the number of entries in the view.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT16 -- returns the number of entries; returns 0 if there is any problem;
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT16
NSR::EAView::numEntries(void) const
{
    if (list_ == NULL)
        return 0;

    return list_->entries();
} // numEntries


//----------------------------------------------------------------------
// METHOD: EAView::length  --CJB
//  
//  Returns the combined length that all EAs in the view will have when they
//  are written to their ByteArray or LogExtentList
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the total length; returns 0 if there is any problem;
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EAView::length(void) const
{
    Error           err;
    EAViewItem *    item;
    UINT32          len = 0;

    if (list_ == NULL)
        return 0;

    // Sum the length of every item in the list.
    for (INT16 i=0; ; i++) {
        item = this->at(i, err);
        if (item == NULL)
            break;
        len += item->length();
    }
    return len;
} // length


//----------------------------------------------------------------------
// METHOD: EAView::at  --CJB
//  
//  Returns a pointer to an item at a specified index in this view.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the specified EA view item;
//  returns NULL if there is any problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Index_out_of_bounds
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::EAView::at(INT16 index,
                Error & err) const
{
    if (index < 0  ||  list_ == NULL  ||  index >= list_->entries()) {
        err = error_Index_out_of_bounds;
        return NULL;
    }
    err = error_None;
    return (EAViewItem*)list_->at (index);
} // at


//----------------------------------------------------------------------
// METHOD: EAView::removeAt  --CJB
//  
//  Removes an item from the view and returns a pointer to it.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the specified EA view item and
//  removes it from this EA view. Returns NULL if there is any problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The specified EA view item is removed from this EA view.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  This method does not delete the returned object--It is the
//  caller's responsibility to do that.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::EAView::removeAt(INT16 index,
                      Error & err)
{
    EAViewItem *        item;

    if (list_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }

    item = this->at(index, err);        // "at" checks range of index
    if (item == NULL)
        return NULL;

    list_->remove (item);
    item->parentView(NULL);             // set parentView in EAViewItem to NULL
    err = error_None;
    return item;
    
} // removeAt


//----------------------------------------------------------------------
// METHOD: EAView::last  --CJB
//  
//  Returns a pointer to the last item in this view.
//
// ARGS:
//  (none)
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the last EA view item. Returns NULL
//  if there are no items, or if there is any problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::EAView::last(void) const
{
    if (list_ == NULL)
        return NULL;
    return (EAViewItem*)list_->last();
} // last


//----------------------------------------------------------------------
// METHOD: EAView::setPaddingToMin  --CJB
//  
//  For every item in this view, sets the attribute length to the end of the valid
//  data, rounded up to the next four byte boundary.
//
// ARGS:
//  UINT32                  blockSize       IN  length of a block on the media
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The padding of all items in this EA view is modified as necessary.
// 
// ERRORS:
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
// 
// NOTES:
//  This method sets every item in the view to its minimum length (ie., padded up
//  to the next four byte boundary past the end of its data).  It also guarantees
//  that all data concerned with length is consistent in each item.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EAView::setPaddingToMin (UINT32 blockSize,
                              Error & err)
{
    EAViewItem  *item;
    
    // Call the setPaddingToMin method of every item in this view.
    for (INT16 index=0; ; index++) {
        item = (EAViewItem*)this->at(index, err);
        if (item == NULL)
            break;
        item->setPaddingToMin (blockSize, err);
        if (err)
            return;
    } // for
    err = error_None;;
} // setPaddingToMin

    
//----------------------------------------------------------------------
// METHOD: EAView::printList  --CJB
//  
//  Prints all the EA view items in this EA view.
//
// ARGS:
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is part of the mechanism that prints physical lists, not composite lists.
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::EAView::printList (INT16 indent) const
{
    Error           err;
    EAViewItem *    item;

    // Print every item in the list.
    for (INT16 i=0; ; i++) {
        item = this->at(i, err);
        if (item == NULL)
            break;
        item->printItem(i, False, indent);
    }
} // printList
#endif


// ---------------start of protected methods of the EAView class----------------------



//----------------------------------------------------------------------
// METHOD: EAView::putIntoList  --CJB
//  
//  Does the low level details of adding an item to this view's list at
//  a specified index.
//
// ARGS:
//  EAViewItem*             eavi            IN  pointer to EA view item
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The specified EA view item is put into our internal list.  All other
//  applicable member variables are modified as necessary. 
// 
// ERRORS:
//  error_Index_out_of_bounds
// 
// NOTES:
//  Notice we allow index to be equal to the number of entries here, unlike
//  in retrieval methods where it must be less than the number of entries.
//  Think about it--you'd never be able to insert anything if this weren't so.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EAView::putIntoList (EAViewItem * eavi,
                          INT16 index,
                          Error & err)
{
    if (index < 0  ||  list_ == NULL  ||  index > list_->entries()) {
        err = error_Index_out_of_bounds;
        return;
    }

    // Insert the item into our list.
    list_->insertAt (index, eavi);
    eavi->parentView(this);                 // set parentView in EAViewItem to this view
    err = error_None;

} // putIntoList




// --------------------------------------------------------------------------------
// Methods of the ShortEAView class:



//----------------------------------------------------------------------
// METHOD: ShortEAView::toISO  --CJB
//  
//  Writes all items in this EA view into the caller's ByteArray.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The caller's ByteArray is updated to contain the current data of all items
//  in this EA view.
// 
// ERRORS:
//  (only error_None)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ShortEAView::toISO(Error & err) const
{
    EAViewItem              *item;

    // Set the caller's ByteArray to its correct length.
    eaViewBA_.setLen(this->length());

    // Call the toISO method of every item in this view.  Every item will
    // write its bytes at the appropriate offset in the caller's ByteArray.
    for (INT16 index=0; ; index++) {
        item = this->at(index, err);
        if (item == NULL)
            break;
        item->toISO (err);
        if (err)
            return;
    }
    err = error_None;
} // toISO


//----------------------------------------------------------------------
// METHOD: ShortEAView::insertAt  --CJB
//  
//  Inserts a short EA view item into this view at a specified index.
//
// ARGS:
//  ShortEAViewItem*        seavi           IN  pointer to short EA view item to be inserted
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal list, and any other applicable member variables, are updated
//  to include the specified EA view item.
// 
// ERRORS:
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
// 
// NOTES:
//  The EA view item is not copied--it is put directly into our list.
//  The implications of this are (1) The EA view item must be constructed
//  using the "new" operator, not on the stack; (2) This EA view assumes
//  responsiblity for deleting the item.  This is done because the interface
//  presented to the eventual user (in one of the EA space classes) does a copy;
//  this interface can in a way be considered internal.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ShortEAView::insertAt(ShortEAViewItem * seavi,
                           INT16 index,
                           Error & err)
{
    if (seavi == NULL) {
        err = error_Null_pointer;
        return;
    }
    // Check that this EAViewItem has a valid regKey--we won't let it in if it doesn't.
    EARegistry::at (seavi->regKey(), err);
    if (err)
        return;
    this->putIntoList (seavi, index,  err);
} // insertAt


//----------------------------------------------------------------------
// METHOD: ShortEAView::findIndex  --CJB
//  
//  Returns the index of a specified item, relative to this view.
//
// ARGS:
//  const EAViewItem*       itemToFind      IN  pointer to item to find index for
//
// RETURNS:
//  INT16 -- index of the specified item (relative to this EA view); returns
//  InvalidIndex if the item is not found or if there is some other problem.
// 
// PRE-CONDITIONS:
// (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  See the notes for EAViewItem::findIndex and GroupOfShortsEAViewItem::findIndex
//  for more background on the workings of this method.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::INT16
NSR::ShortEAView::findIndex(const EAViewItem* itemToFind) const
{
    if (list_ == NULL)
        return InvalidIndex;
    INT16 foundIndex = list_->index(itemToFind);
    if (foundIndex == (INT16)RW_NPOS)
        return InvalidIndex;
    return foundIndex;
} // findIndex


//----------------------------------------------------------------------
// METHOD: ShortEAView::printList  --CJB
//  
//  Prints all the EAs in this view.
//
// ARGS:
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::ShortEAView::printList (INT16 indent) const
{
    UINT16          i;

    for (i=0; i<indent; i++)
        printf (" ");
    printf ("ShortEAView   (obj addr:%lu)   list:\n", (UINT32)this);
    this->EAView::printList(INDENT_AMOUNT+indent);
} // printList
#endif





// --------------------------------------------------------------------------------
// Methods of the LongEAView class:



//----------------------------------------------------------------------
// METHOD: LongEAView::toDisk  --CJB
//  
//  Writes the data of every item in this EA view to its respective LogExtentList.
//
// ARGS:
//  SpaceResource&          space           IN  pointer to space resource object for allocations
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  When this method is done, the LogExtentList of every item in this view is updated,
//  and all the LogExtentLists are ready to be concatonated to form a new overall
//  LogExtentList for the caller.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_SR_Insufficient_space
//  error_SR_Bitmap_corrupt
//  error_No_free_store
//  error_Empty_path_element
//  error_Write_EOM
//  error_Write_EOLM
//  error_Drive_write
// 
// NOTES:
//  This method calls the toDisk() method of every EA view item in this view, but
//  are only interested in the GroupOfShort EA view items--they must call toISO()
//  for their subviews (to cause the header to be updated) and then write their
//  ByteArrays back to their respective LogExtentLists.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::LongEAView::toDisk(SpaceResource & space,
                        Error & err) const
{
    EAViewItem                  *item;

    for (INT16 index=0; ; index++) {
        item = this->at(index, err);
        if (item == NULL)
            break;
        item->toDisk (space, err);
        if (err)
            return;
    }
    err = error_None;
} // toDisk


//----------------------------------------------------------------------
// METHOD: LongEAView::insertAt  --CJB
//  
//  Inserts an item into this view at a specified index.
//
// ARGS:
//  LongEAViewItem*         leavi           IN  pointer to long EA view item to be inserted
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal list, and any other applicable member variables, are updated
//  to include the specified EA view item.
// 
// ERRORS:
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
//
// NOTES:
//  The EA view item is not copied--it is put directly into our list.
//  The implications of this are (1) The EA view item must be constructed
//  using the "new" operator, not on the stack; (2) This EA view assumes
//  responsiblity for deleting the item.  This is because the interface
//  presented to the eventual user (in the external EA space class) does a copy;
//  this interface can in a way be consdered internal.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::LongEAView::insertAt(NSR::LongEAViewItem * leavi,
                          INT16 index,
                          Error & err)
{
    if (leavi == NULL) {
        err = error_Null_pointer;
        return;
    }
    // Except for GroupOfShortsEAViewItems, check that this EAViewItem has a valid regKey--we
    // won't let it in if it doesn't.
    if (leavi->isA() != GroupOfShortsEAViewItemType) {
        EARegistry::at (leavi->regKey(), err);
        if (err)
            return;
    }
    this->putIntoList (leavi, index, err);  
} // insertAt


//----------------------------------------------------------------------
// METHOD: LongEAView::findIndex  --CJB
//  
//  Returns the index of a specified item, relative to this view.
//
// ARGS:
//  const EAViewItem*       itemToFind      IN  pointer to item to find index for
//
// RETURNS:
//  INT16 -- index of the specified item (relative to this EA view), or InvalidIndex
//  if the item is not found, or if there is some other problem.
// 
// PRE-CONDITIONS:
// (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  See the notes for EAViewItem::findIndex and GroupOfShortsEAViewItem::findIndex
//  for more background on the workings of this method.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::INT16
NSR::LongEAView::findIndex(const EAViewItem* itemToFind) const
{
    Error           err;
    EAViewItem*     myItem;
    INT16           i;
    INT16           foundIndex;
    INT16           cumulativeIndex;
    
    if (list_ == NULL)
        return InvalidIndex;

    cumulativeIndex = 0;
    for (i=0; ; i++) {
        myItem = this->at(i, err);
        if (myItem == NULL)
            break;
        foundIndex = myItem->findIndex(itemToFind);
        if (foundIndex != InvalidIndex)
            return cumulativeIndex + foundIndex;
        cumulativeIndex += myItem->numEntries();
    }

    return InvalidIndex;
} // findIndex


//----------------------------------------------------------------------
// METHOD: LongEAView::removeAllMyExtents  --CJB
//  
//  Moves all extents held by the items in this view, to the specified target list.
//
// ARGS:
//  Boolean                 GOSOnly         IN  indicates we should operate on GOS items only
//  LogExtentList&          targetList      IN  target list to move log extents to
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  All logical extents of all items in this EA view are removed from their respective
//  LogExtentLists, and appended to the target list.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_No_free_store
//  error_Null_pointer;
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::LongEAView::removeAllMyExtents (Boolean GOSOnly,
                                     LogExtentList & targetList,
                                     Error & err)
{
    EAViewItem*             item;

    // Call the removeAllMyExtents method of every item in this view.
    for (INT16 index=0; ; index++) {
        item = (EAViewItem*)this->at(index, err);
        if (item == NULL)
            break;
        if (GOSOnly  &&  item->isA() != GroupOfShortsEAViewItemType)
            continue;
        item->removeAllMyExtents(targetList, err);
        if (err)
            return;
    }
    err = error_None;
} // removeAllMyExtents


//----------------------------------------------------------------------
// METHOD: LongEAView::removeCallersDuplicateExtents  --CJB
//  
//  This method removes all extents from sourceList that are also in this EA view,
//  and adds them to targetList.
//
// ARGS:
//  LogExtentList&          sourceList      IN/OUT extent list to remove extents from
//  LogExtentList&          targetList      IN/OUT extent list to move extents into
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The specified extents are moved from sourceList to targetList.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_No_free_store
//  error_Null_pointer;
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::LongEAView::removeCallersDuplicateExtents (LogExtentList & sourceList,
                                                LogExtentList & targetList,
                                                Error & err) {
    EAViewItem* item;

    // Call the removeCallersDuplicateExtents method of every item in this view.
    for (INT16 index=0; ; index++) {
        item = (EAViewItem*)this->at(index, err);
        if (item == NULL)
            break;
        item->removeCallersDuplicateExtents(sourceList, targetList, err);
        if (err)
            return;
    }
    err = error_None;
} // removeCallersDuplicateExtents


//----------------------------------------------------------------------
// METHOD: LongEAView::countDiskSpace  --CJB
//  
//  For testing. Returns the total number of disk blocks held by all the items
//  in this view.
//
// ARGS:
//  UINT32                  blockSize       IN  size of a block on the media
//
// RETURNS:
//  UINT32 -- returns the total number of blocks for all items in this EA view.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
NSR::UINT32
NSR::LongEAView::countDiskSpace(UINT32 blockSize) const
{
    Error                   err;
    const EAViewItem*       item;
    UINT32                  totalBlocks;

    // Call the countDiskSpace method of every item in this view.
    totalBlocks = 0;
    for (INT16 index=0; ; index++) {
        item = (EAViewItem*)this->at(index, err);
        if (item == NULL)
            break;
        totalBlocks += item->countDiskSpace(blockSize);
    }
    return totalBlocks;
} // countDiskSpace
#endif


//----------------------------------------------------------------------
// METHOD: LongEAView::printAllocatedBlocks  --CJB
//  
//  For testing. Prints a list of all disk blocks held by the items in this view.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::LongEAView::printAllocatedBlocks(void) const
{
    Error                   err;
    const EAViewItem*       item;

    // Call the printAllocatedBlocks method of every item in this view.
    for (INT16 index=0; ; index++) {
        item = (EAViewItem*)this->at(index, err);
        if (item == NULL)
            break;
        item->printAllocatedBlocks();
    }
} // printAllocatedBlocks
#endif


//----------------------------------------------------------------------
// METHOD: LongEAView::printList  --CJB
//  
//  Prints all the EAs in this view.
//
// ARGS:
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::LongEAView::printList (INT16 indent) const
{
    for (INT16 i=0; i<indent; i++)
        printf (" ");
    printf ("LongEAView   (obj addr:%lu)   list:\n", (UINT32)this);
    this->EAView::printList(INDENT_AMOUNT+indent);
} // printList
#endif
 
// --------------------------------------------------------------------------------
// Methods of the EAViewItem class:

//----------------------------------------------------------------------
// METHOD: EAViewItem::copy_  --CJB
//  
//  Method to do the actual work of the copy constructor and assignment operator.
//
// ARGS:
//  const EAViewItem&       x               IN  source object to copy
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Copies the source object into this object; all member variables reflect this.
// 
// ERRORS:
//  error_Null_pointer
//  error_EA_Invalid_EA_type
//  error_No_free_store
// 
// NOTES:
//  If the source object is the same as this, nothing is done--ie., we don't
//  allow copying to oneself.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EAViewItem::copy_ (const EAViewItem & x,
                        Error & err)
{
    if (&x == this) {
        err = error_None;
        return;
    }
    regKey_ = x.regKey_;
    parentView_ = NULL;
    err = error_None;
} // copy_


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaType  --CJB
//  
//  Getter method for eaType_ (determined by value of regKey_).
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  UINT32 -- returns the eaType of this view item, or (arbitrarily)
//  zero if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EAViewItem::eaType(Error &err)  const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaType();
    else return 0;                      // arbitrary
} // eaType


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaSubType  --CJB
//  
//  Getter metho for eaSubType_ (determined by value of regKey_).
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  UINT8 -- returns the eaSubType of this view item, or (arbitrarily)
//  zero if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT8
NSR::EAViewItem::eaSubType(Error &err)  const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaSubType();
    else return 0;                      // arbitrary
} // eaSubType


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaString  --CJB
//  
//  Getter metho for eaString_ (determined by value of regKey_).
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  String -- returns the eaString of this view item, or (arbitrarily) an
//  empty String if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::String
NSR::EAViewItem::eaString(Error &err)  const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaString();
    else return String();               // arbitrary
} // eaString


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaPlacement  --CJB
//  
//  Getter metho for eaPlacement_ (determined by value of regKey_).
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAPlc -- returns the eaPlacement value of this view item, or (arbitrarily)
//  ea_MayBeInEitherSpace if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAPlc
NSR::EAViewItem::eaPlacement(Error &err)  const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaPlacement();
    else return ea_MayBeInEitherSpace;  // arbitrary
} // eaPlacement


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaMultInstAllwd  --CJB
//  
//  Getter metho for eaMultInstAllwd_ (determined by value of regKey_).
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  Boolean -- returns the eaMultInstAllwd value of this view item, or (arbitrarily)
//  True if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::Boolean
NSR::EAViewItem::eaMultInstAllwd(Error &err) const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaMultInstAllwd();
    else return True;                   // arbitrary
} // eaMultInstAllwd


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaCritical  --CJB
//  
//  Getter metho for eaCritical_ (determined by value of regKey_).
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  Boolean -- returns the eaCritical value of this view item, or (arbitrarily)
//  False if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::Boolean
NSR::EAViewItem::eaCritical(Error &err) const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaCritical();
    else return False;                  // arbitrary
} // eaCritical


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaCategory  --CJB
//  
//  Method to get EACategory (determined by value of regKey_).
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  EACategory -- returns the eaCategory value of this view item, or (arbitrarily)
//  ea_ImplUseCategory if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EACategory
NSR::EAViewItem::eaCategory(Error &err)  const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaCategory();
    else return ea_ImplUseCategory;     // arbitrary
} // eaCategory


//----------------------------------------------------------------------
// METHOD: EAViewItem::eaDefaultKey  --CJB
//  
//  Tells if this entry's regKey is one of the default values (determined by
//  value of regKey_)
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  Boolean -- returns the eaDefaultKey value of this view item, or (arbitrarily)
//  False if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_REGISTRY_Entry_not_found
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::Boolean
NSR::EAViewItem::eaDefaultKey(Error &err) const
{
    const EARegistryEntry *MyEntry = EARegistry::at(regKey(), err);
    if (MyEntry != NULL)
        return MyEntry->eaDefaultKey();
    else return False;                  // arbitrary
} // eaDefaultKey


//----------------------------------------------------------------------
// METHOD: EAViewItem::offset  --CJB
//  
//  Returns the offset of this item's data, relative to its EAView.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the offset of this item's data, or zero if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  The offset returned is the offset, relative to the data of its containing
//  EA view, that this item's data will have when a toDIsk() is done on the
//  whole EA view.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EAViewItem::offset(void) const
{
// Returns zero if there is any problem.
    Error               err;
    EAViewItem          *item;
    UINT32              offset;
    
    if (parentView_ == NULL)
        return 0;

    // Add the lengths of every item in the list up to, but not including, this one.
    offset = 0;
    for (INT16 index=0; ; index++) {
        item = parentView_->at(index, err);
        if (item == NULL)
            break;
        if (item == this)
            return offset;
        offset += item->length();
    }

    // Huh? Didn't find this item in the list--shouldn't happen.
    return 0;
} // offset


//----------------------------------------------------------------------
// METHOD: EAViewItem::overallOffset  --CJB
//  
//  Returns the offset of this item's data, relative to the containing EASpace.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the offset of this item's data, or zero if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  The offset returned is the offset, relative to the data of its containing
//  EA space, that this item's data will have when a toDIsk() is done on the
//  whole EA space. If this EA view item is not in an EA space, the offset
//  returned is relative to this EA view.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EAViewItem::overallOffset(void) const
{
    EAViewItem          *parentGOSViewItem;

    if (parentView_ == NULL)
        return this->offset();

    parentGOSViewItem = parentView_->parentGOSViewItem();
    if (parentGOSViewItem == NULL)
        return this->offset();

    return this->offset() + parentGOSViewItem->offset();
} // overallOffset


//----------------------------------------------------------------------
// METHOD: EAViewItem::EASpaceType  --CJB
//  
//  Returns the EASpaceType of the containing EASpace.
//
// ARGS:
//  (none)
//
// RETURNS:
//  EASpaceType -- returns the EASpaceType of the containing EA space.
//  If this item is not in an EA space, the value NoEASpaceType is returned.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EASpaceType
NSR::EAViewItem::EASpaceType (void) const
{
    EASpace         *myEASpace;

    myEASpace = this->myEASpace ();
    if (myEASpace != NULL)      
        return myEASpace->isA();
    return NoEASpaceType;
} // EASpaceType


//----------------------------------------------------------------------
// METHOD: EAViewItem::myIndex  --CJB
//  
//  Returns the virtual index of this item, relative to the containing EASpace.
//
// ARGS:
//  (none)
//
// RETURNS:
// INT16 -- returns the virtual index of this EAViewItem within its EASpace,
// or invalidIndex if it is not in any EASpace.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::INT16
NSR::EAViewItem::myIndex (void) const
{
    EAView          *myTopLevelEAView;
    INT16           tempIndex;

    myTopLevelEAView = this->myTopLevelEAView();
    if (myTopLevelEAView == NULL)
        return InvalidIndex;

    // Notice we adjust the returned value to be a virtual index.
    tempIndex = myTopLevelEAView->findIndex(this);
    if (tempIndex > 0)
        return tempIndex - 1;
    else return InvalidIndex;
} // myIndex


//----------------------------------------------------------------------
// METHOD: EAViewItem::findIndex  --CJB
//  
//  Returns the index of a specified item, relative to this EA view item.
//
// ARGS:
//  const EAViewItem*       itemToFind      IN  pointer to the item to match
//
// RETURNS:
//  INT16 -- returns zero if this item is the same one specified by the input
//  parameter, otherwise invalidIndex.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method retuns zero if this item is the one specified by the input
//  parameter; otherwise it returns InvalidIndex.  This method for this class
//  is mainly a simpleminded placeholder; the real reason for this method
//  is to handle the GroupOfShortEAViewItem class.  See the notes for
//  LongEAView::findIndex and GroupOfShortsEAViewItem::findIndex
//  for more background on the workings of this method.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::INT16
NSR::EAViewItem::findIndex(const EAViewItem* itemToFind) const
{
    if (itemToFind == this)
        return 0;
    return InvalidIndex;
} // findIndex


//----------------------------------------------------------------------
// METHOD: EAViewItem::at  --CJB
//  
//  Returns a const pointer to an item at a specified index in this view.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a const pointer to this item, or NULL. 
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Index_out_of_bounds
// 
// NOTES:
//  This method retuns a const pointer to this item if the specified index
//  is zero; otherwise it returns NULL.  This method for this class is
//  mainly a simpleminded placeholder; the real reason for this method
//  is to handle the GroupOfShortEAViewItem class.  See the notes for
//  that method.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem*
NSR::EAViewItem::at(INT16 index,
                    Error & err) const
{
    // This is overridden for GroupOfShortsEAViewItems--this method applies
    // to all others.
    if (index == 0) {
        err = error_None;
        return this;
    }
    err = error_Index_out_of_bounds;
    return NULL;
} // at


//----------------------------------------------------------------------
// METHOD: EAViewItem::at  --CJB
//  
//  Returns a non-const pointer to an item at a specified index in this view.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a non-const pointer to this item, or NULL. 
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Index_out_of_bounds
// 
// NOTES:
//  This method retuns a pointer to this item if the specified index is
//  zero; otherwise it returns NULL.  This method for this class is
//  mainly a simpleminded placeholder; the real reason for this method
//  is to handle the GroupOfShortEAViewItem class.  See the notes for
//  that method.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem*
NSR::EAViewItem::at(INT16 index,
                    Error & err)
{
    // This is overridden for GroupOfShortsEAViewItems--this method applies
    // to all others.
    if (index == 0) {
        err = error_None;
        return this;
    }
    err = error_Index_out_of_bounds;
    return NULL;
} // at


//----------------------------------------------------------------------
// METHOD: EAViewItem::compareForInsertion  --CJB
//  
//  Compares this item to a specifed item. 
//
// ARGS:
//  const EAViewItem*       a               IN  pointer to item to compare
//
// RETURNS:
//  INT16 -- returns -1 if (this < a),   0 if (this == a),  +1 if (this > a).
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  (1) This method is used to determine the insertion point when adding items to the
//  EA view's list
//  (2) Compares based on (A) the EAViewHeaderItem is always first; (B) eaCategory;
//  (C) EAViewItem type--ShortEAViewItems before LongEAViewItems;
//  (3) This search is strictly by the criteria of OSTA 3.3.4.  These criteria for
//  ordering are different than the criteria that a user would apply in searching
//  for items of a specified kind. Specifically, OSTA 3.3.4 says nothing about the
//  order of items within a group of short or long items of the same category.
//  (4) Notice that this method depends on the order of values in the EACategory and
//  EAViewItemType enums.  See those enum definitions for details.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::INT16
NSR::EAViewItem::compareForInsertion(const EAViewItem * a) const
{
    Error   err;

    if (this->isA() == EAViewHeaderItemType)        // is "this" an EAViewHeaderItem?
        return -1;
    if (a->isA() == EAViewHeaderItemType)           // is "a" an EAViewHeaderItem?
        return 1;       
    if (this->eaCategory(err) < a->eaCategory(err)) // see note in definition of EACategory enum
        return -1;
    if (this->eaCategory(err) > a->eaCategory(err))
        return 1;
    if (this->isA() < a->isA())                     // see note in definition of EAViewItemType enum
        return -1;
    if (this->isA() > a->isA())
        return 1;
    return 0;
} // compareForInsertion


// ---------------start of protected methods of the EAViewItem class----------------------



//----------------------------------------------------------------------
// METHOD: EAViewItem::myTopLevelEAView  --CJB
//  
//  Returns the highest level EAView that contains this item.
//
// ARGS:
//  (none)
//
// RETURNS:
//  EAView* -- returns a pointer to the highest level EA view that contains this
//  item, or NULL if this item is not in any view.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAView *
NSR::EAViewItem::myTopLevelEAView (void) const
{
    EAViewItem      *parentGOSViewItem;

    // Get the view that this item is in.
    if (parentView_ == NULL)
        return NULL;

    // See if our parentView is in a GroupOfShortsEAViewItem.
    parentGOSViewItem = parentView_->parentGOSViewItem();
    if (parentGOSViewItem == NULL)
        return parentView_;     
    
    // Our parentView is in a GOSEAViewItem.  Get IT's parentView.
    return parentGOSViewItem->parentView();
} // myTopLevelEAView


//----------------------------------------------------------------------
// METHOD: EAViewItem::myEASpace  --CJB
//  
//  Getter method for myEASpace_, which points to the EASpace object that holds
//  this item.
//
// ARGS:
//  (none)
//
// RETURNS:
//  EASpace* -- returns a pointer to the EA space that contains this item, or
//  NULL if this item is not in an EA space.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EASpace *
NSR::EAViewItem::myEASpace (void) const
{
// Returns the EASpace that contains this item, or NULL if none.
    EAView          *topLevelEAView;

    topLevelEAView = this->myTopLevelEAView ();
    if (topLevelEAView == NULL)
        return NULL;
    return topLevelEAView->myEASpace();     
} // myEASpace





// --------------------------------------------------------------------------------
// Methods of the ShortEAViewItem class:


//----------------------------------------------------------------------
// METHOD: ShortEAViewItem::ReadWriteEAByteArray  --CJB
//  
//  Returns a reference to the ByteArray, and marks the containing EA space dirty.
//
// ARGS:
//  (none)
//
// RETURNS:
//  ByteArray& -- returns a reference to the ByteArray of this item.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::ByteArray&
NSR::ShortEAViewItem::ReadWriteEAByteArray(void)
{
    EASpace *myEASpace = this->myEASpace();
    if (myEASpace != NULL)
        myEASpace->dirty(True);
    return myLocalBA_;
} // ReadWriteEAByteArray


//----------------------------------------------------------------------
// METHOD: ShortEAViewItem::printItem  --CJB
//  
//  Prints data about this item.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Boolean                 compositeList   IN  indicates composite (versus physical) list
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::ShortEAViewItem::printItem (INT16 index,
                                 Boolean compositeList,
                                 INT16 indent) const
{
    const UINT32    BUFLEN = 64;
    UINT32          dataOffset;
    UINT32          dataLen;
    char            buf[BUFLEN+1];
    UINT16          i;

    for (i=0; i<indent; i++)
        printf (" ");
    printf ("index:%-2d ShortEAViewItem  ", index);
    if (regKey_ == ea_InvalidKey)
        printf ("regKey:(InvalidKey)  ");
    else printf ("regKey:%-2lu  ", regKey_);
    printf ("offset:%-3lu  length:%-3lu  (obj addr:%lu)\n",
        compositeList?this->overallOffset():this->offset(), this->length(), (UINT32)this);

    // Set up the EA Inspector.
    ByteArrayRef    myBAR((ByteArray&)myLocalBA_);
    SAIEAInspector  myEAInspector;
    myEAInspector.fromISO ((BYTE_PTR)myBAR);
    
    // Print the prefix of the data associated with this item.
    for (i=0; i<INDENT_AMOUNT+indent; i++)
        printf (" ");
    printf ("prefix:type:%lu  attrLen:%lu  varyingLen:%lu  fixedLen+varyingLen:%lu\n",
        (UINT32)myEAInspector.attrType(), (UINT32)myEAInspector.attrLen(), (UINT32)myEAInspector.varyingLen(),
        (UINT32)myEAInspector.fixedLen()+(UINT32)myEAInspector.varyingLen());
    for (i=0; i<INDENT_AMOUNT+indent; i++)
        printf (" ");
    printf ("       id:\"%s\"\n", (CONST_CHAR_PTR)myEAInspector.attrID());

    // Get the actual data portion of this item in our ByteArray.
    for (i=0; i<INDENT_AMOUNT+indent; i++)
        printf (" ");
    dataOffset = myEAInspector.fixedLen();
    dataLen = myEAInspector.varyingLen();
    if (dataLen > BUFLEN)
        dataLen = BUFLEN;
    if (dataLen == 0) {
        printf ("data:\n");
        return;
    }
    myBAR.length(dataLen);
    myBAR.offset(myBAR.offset()+dataOffset);
    myBAR.toMem((BYTE_PTR)buf);
    
    // Print the data associated with this item.
    for (i=0; i<dataLen; i++) {
        if (buf[i] < ' '  ||  buf[i] > '~')
            buf[i] = '.';
    }
    buf[dataLen] = '\0';
    printf ("data: \"%s\"\n", buf);
} // printItem
#endif


//----------------------------------------------------------------------
// METHOD: ShortEAViewItem::setPaddingToMin  --CJB
//  
//  Sets the attribute length to the end of the valid data, rounded up to
//  the next four byte boundary.
//
// ARGS:
//  UINT32                  blockSize       IN  length of a block on the media
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The padding of this item is modified as necessary.
// 
// ERRORS:
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
// 
// NOTES:
//  This method sets this item to its minimum length (ie., padded up to the
//  next four byte boundary past the end of its data).  It also guarantees
//  that all data concerned with length is consistent.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ShortEAViewItem::setPaddingToMin (UINT32 blockSize, Error & err)
{
    SAIEAInspector      myEAInspector;
    UINT32              unpaddedDataLen;
    UINT32              paddedDataLen;
    UINT32              oldLen;

    oldLen = myLocalBA_.length();

    // Read the actual data length from the ByteArray, and do some consistency
    // checks on it.
    myEAInspector.fromISO ((BYTE_PTR)myLocalBA_, myLocalBA_.length());
    if (err = myEAInspector.error())
        return;
    unpaddedDataLen = myEAInspector.fixedLen() + myEAInspector.varyingLen();
    paddedDataLen = (unpaddedDataLen+3) & 0xFFFFFFFCUL;
    if (unpaddedDataLen > oldLen  ||  paddedDataLen >= blockSize) {
        err = error_EA_Invalid_EA_length;
        return;
    }

    if (paddedDataLen < oldLen)
        myLocalBA_.removeBytes(paddedDataLen, oldLen-paddedDataLen);
    else if (paddedDataLen > oldLen)
        myLocalBA_.insertBytes(oldLen, paddedDataLen-oldLen, NULL);
    
    // Set attrLen in the prefix of our local ByteArray, to agree with
    // the length of our local ByteArray that we just set.
    EAPrefix    eaPrefix(0);
    eaPrefix.fromISO ((BYTE_PTR)myLocalBA_);
    eaPrefix.attrLen(myLocalBA_.length());
    eaPrefix.toISO ((BYTE_PTR)myLocalBA_);

    err = error_None;
} // setPaddingToMin


//----------------------------------------------------------------------
// METHOD: ShortEAViewItem::toISO  --CJB
//  
//  Writes the data of this item into the caller's ByteArray at the appropriate offset.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The caller's ByteArray is updated to contain the current data of this item
//  at the appropriate offset.
// 
// ERRORS:
//  (only error_None)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ShortEAViewItem::toISO (Error & err)
{
    err = error_None;

    if (parentView_ == NULL)
        return;

    // Copy the data in our local ByteArray to the appropriate offset of the caller's ByteArray.
    ByteArrayRef myBAR((ByteArray&)parentView_->eaViewBA(), this->offset(),
        myLocalBA_.length());
    myBAR.fromMem((BYTE_PTR)myLocalBA_);
} // to ISO




// --------------------------------------------------------------------------------
// Methods of the EAViewHeaderItem class:



//----------------------------------------------------------------------
// METHOD: EAViewHeaderItem::toISO  --CJB
//  
//  Writes the data of this header item into the caller's ByteArray at the
//  appropriate offset.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  The offsets and location information in the EA header view item must be
//  valid before this method is called.
// 
// POST-CONDITIONS:
//  The caller's ByteArray is updated to contain the current data of this header
//  item at the appropriate offset.
// 
// ERRORS:
//  (only error_None)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EAViewHeaderItem::toISO (Error & err)
{
    ExtendedAttrHeaderDesc  myHeaderDesc;

    err = error_None;
    if (myLocalBA_.length() < myHeaderDesc.descLen())
        return;

    // First set up the header in our local ByteArray.  Notice we use fromISOBlind, because
    // we don't care what location was previously in the tag.
    myHeaderDesc.fromISOBlind ((CONST_BYTE_PTR)myLocalBA_);
    myHeaderDesc.implAttrLocation(implOffset_);
    myHeaderDesc.applAttrLocation(applOffset_);
    myHeaderDesc.toISO ((BYTE_PTR)myLocalBA_, location_);

    // Now call the toISO method of the class that we are derived from, to actually
    // write the header to the ShortEAView's ByteArray.
    ShortEAViewItem::toISO (err);
} // to ISO


//----------------------------------------------------------------------
// METHOD: EAViewHeaderItem::printItem  --CJB
//  
//  Prints data about this item.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Boolean                 compositeList   IN  indicates composite (versus physical) list
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::EAViewHeaderItem::printItem (INT16 index,
                                  Boolean compositeList,
                                  INT16 indent) const
{
    UINT16          i;

    for (i=0; i<indent; i++)
        printf (" ");
    if (compositeList)
        printf ("Header:  offset:%-3lu  length:%-3lu  (obj addr:%lu)\n",
            this->overallOffset(), this->length(), (UINT32)this);
        else printf ("index:%-2d Header:  offset:%-3lu  length:%-3lu  (obj addr:%lu)\n",
            index, this->offset(), this->length(), (UINT32)this);

    // Print the header data.
    for (i=0; i<INDENT_AMOUNT+indent; i++)
        printf (" ");
    printf ("Impl use offset:%lu  Appl use offset:%lu\n", implOffset_, applOffset_);
} // printItem
#endif




// --------------------------------------------------------------------------------
// Methods of the LongEAViewItem class:



//----------------------------------------------------------------------
// METHOD: LongEAViewItem::ReadWriteEALogExtentList  --CJB
//  
//  Returns a reference to this item's extent list, and marks the containing
//  EA space dirty.
//
// ARGS:
//  (none)
//
// RETURNS:
//  LogExtentList& -- returns a reference to the LogExtentList of this item.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::LogExtentList&
NSR::LongEAViewItem::ReadWriteEALogExtentList(void) const
{
    EASpace *myEASpace = this->myEASpace();
    if (myEASpace != NULL)
        myEASpace->dirty(True);
    return (LogExtentList&)myLocalExtList_;
} // ReadWriteEALogExtentList


//----------------------------------------------------------------------
// METHOD: LongEAViewItem::printAllocatedBlocks  --CJB
//  
//  For testing. Prints a list of all disk sectors held by this item.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::LongEAViewItem::printAllocatedBlocks(void) const
{
    printf ("   LongEAViewItem LEL--blocks: ");
    ExternalEASpace::printLogExtentList (myLocalExtList_);
} // printAllocatedBlocks
#endif


//----------------------------------------------------------------------
// METHOD: LongEAViewItem::printItem  --CJB
//  
//  Prints data about this item.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Boolean                 compositeList   IN  indicates composite (versus physical) list
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::LongEAViewItem::printItem (INT16 index,
                                Boolean compositeList,
                                INT16 indent) const
{
    const UINT32    MAX_DATA_LEN = 64;
    const UINT32    READ_SIZE = 128 + MAX_DATA_LEN + 1;
    UINT32          myReadSize;
    UINT32          dataOffset;
    UINT32          dataLen;
    UINT32          readLen;
    BYTE_PTR        bufptr;
    Error           err;
    UINT16          i;

    for (i=0; i<indent; i++)
        printf (" ");
    printf ("index:%-2d LongEAViewItem   ", index);
    if (regKey_ == ea_InvalidKey)
        printf ("regKey:(InvalidKey)  ");
    else printf ("regKey:%-2lu  ", regKey_);
    printf ("offset:%-3lu  length:%-3lu  (obj addr:%lu)\n",
        compositeList?this->overallOffset():this->offset(), this->length(), (UINT32)this);

    // Read some data from the start of our logExtentList.
    if (myLocalExtList_.infoLen() < READ_SIZE)
        myReadSize = myLocalExtList_.infoLen();
    else myReadSize = READ_SIZE;
    ByteArray       myBA(myReadSize);
    ByteArrayRef    myBAR(myBA);
    LogExtentList   tempLEL(myLocalExtList_);
    readLen = tempLEL.read(myBAR, err);
    if (err == error_Read_EOLM)
        err = error_None;
    if (err  ||  readLen != myReadSize)
        return;

    // Set up the EA Inspector.
    SAIEAInspector  myEAInspector;
    myEAInspector.fromISO ((BYTE_PTR)myBAR);
    
    // Print the prefix of the data associated with this item.
    for (i=0; i<INDENT_AMOUNT+indent; i++)
        printf (" ");
    printf ("prefix:type:%lu  attrLen:%lu  varyingLen:%lu  fixedLen+varyingLen:%lu\n",
        (UINT32)myEAInspector.attrType(), (UINT32)myEAInspector.attrLen(), (UINT32)myEAInspector.varyingLen(),
        (UINT32)myEAInspector.fixedLen()+(UINT32)myEAInspector.varyingLen());
    for (i=0; i<INDENT_AMOUNT+indent; i++)
        printf (" ");
    printf ("       id:\"%s\"\n", (CONST_CHAR_PTR)myEAInspector.attrID());

    // Get the actual data portion of this item in our ByteArray.
    for (i=0; i<INDENT_AMOUNT+indent; i++)
        printf (" ");
    dataOffset = myEAInspector.fixedLen();
    if (dataOffset >= myBAR.length()) {
        printf ("data:\n");
        return;
    }
    dataLen = myEAInspector.varyingLen();
    if (dataLen > myBAR.length() - dataOffset)
        dataLen = myBAR.length() - dataOffset;
    if (dataLen > MAX_DATA_LEN)
        dataLen = MAX_DATA_LEN;
    if (dataLen == 0) {
        printf ("data:\n");
        return;
    }
    myBAR.offset(dataOffset);
    myBAR.length(dataLen);
    bufptr = (BYTE_PTR)myBAR;
    if (bufptr == NULL) {
        printf ("data:\n");
        return;
    }
    
    // Print the data associated with this item.
    for (i=0; i<dataLen; i++,bufptr++) {
        if (*bufptr < ' '  || *bufptr > '~')
            *bufptr = '.';
    }
    *bufptr = '\0';
    printf ("data: \"%s\"\n", (BYTE_PTR)myBAR);
} // printItem
#endif




// --------------------------------------------------------------------------------
// Methods of the GroupOfShortsEAViewItem class:



//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::GroupOfShortsEAViewItem  --CJB
//  
//  Constructor.
//
// ARGS:
//  UINT32                  blockSize       IN  size of a block on the media
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  A GroupOfShortsEAViewItem object is constructed.
// 
// ERRORS:
//  (call error() to get the object's error status.)
//  error_No_free_store
// 
// NOTES:
//
//----------------------------------------------------------------------
//  Notice we set the EASpace pointer for the subview to NULL--its ignored for
//  GOS EAView items; they chase the parentGOSViewItem pointer instead.
//----------------------------------------------------------------------
NSR::GroupOfShortsEAViewItem::GroupOfShortsEAViewItem(UINT32 blockSize) :
    LongEAViewItem(*nullLEL),
    blockSize_(blockSize) {

    // Create the subview.
    subView_ = new ShortEAView(eaSubViewBA_, NULL, this);
    if (subView_ == NULL) {
        error_ = error_No_free_store;
        return;
    }
    error_ = subView_->error();
} // GroupOfShortsEAViewItem constructor


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::copy_  --CJB
//  
//  Method to do the actual work of the copy constructor and assignment operator.
//
// ARGS:
//  const GroupOfShortsEAViewItem& x        IN  source object to copy
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Copies the source object into this object; all member variables reflect this.
// 
// ERRORS:
//  error_Null_pointer
//  error_EA_Invalid_EA_type
//  error_No_free_store
// 
// NOTES:
//  If the source object is the same as this, nothing is done--ie., we don't
//  allow copying to oneself.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::GroupOfShortsEAViewItem::copy_ (const GroupOfShortsEAViewItem & x,
                                     Error & err)
{
    ShortEAViewItem         *src;
    ShortEAViewItem         *dst;

    err = error_None;
    if (&x == this)
        return;

    eaSubViewBA_ = x.eaSubViewBA_;
    blockSize_ = x.blockSize_;

    // Notice this is not the ShortEAView copy constructor.
    subView_ = new ShortEAView(eaSubViewBA_, NULL, this);
    if (subView_ == NULL) {
        err = error_No_free_store;
        return;
    }
    if (err = subView_->error())
        return;

    // Copy the items in the source's subview to our subview.
    for (INT16 i=0; i<x.numEntries(); i++) {
        src = (ShortEAViewItem*)x.at(i, err);
        if (src == NULL)
            break;
        // Sure like to get rid of this switch.
        switch (src->isA()) {
        case ShortEAViewItemType:
            dst = new ShortEAViewItem((ShortEAViewItem&)*src);
            break;
        case EAViewHeaderItemType:
            dst = new EAViewHeaderItem((EAViewHeaderItem&)*src);
            break;
        default:
            subView_->clear();
            err = error_EA_Invalid_EA_type;
            return;
        } // switch
        if (dst == NULL) {
            subView_->clear();
            err = error_No_free_store;
            return;      
        }
        subView_->insertAt(dst, i, err);
        if (err) {
            delete dst;
            subView_->clear();
            return;
        }       
    } // for

    err = error_None;
} // copy_


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::toDisk  --CJB
//  
//  Writes the data of this item to its local LogExtentList.
//
// ARGS:
//  SpaceResource&          space           IN  pointer to space resource object, for allocations
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The local extent list of this item is updated with the item's current data.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_SR_Insufficient_space
//  error_SR_Bitmap_corrupt
//  error_No_free_store
//  error_Empty_path_element
//  error_Write_EOM
//  error_Write_EOLM
//  error_Drive_write
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::GroupOfShortsEAViewItem::toDisk (SpaceResource & space,
                                      Error & err)
{
    UINT32              oldLen;
    UINT32              baLen;
    UINT32              growLastItemLen;
    UINT32              freeSpaceEALen;
    UINT32              tempLen;
    UINT32              minFreeEASpaceEALength;
    EAViewItem          *lastItem;
    EAViewItem          *firstItem;
    EASpace             *myEASpace;

    myEASpace = this->myEASpace ();
    if (subView_ == NULL  ||  myEASpace == NULL) {
        err = error_Required_object_not_present;
        return;
    }
    
    // Determine the amount and the kind of padding needed.
    this->determinePaddingNeeded (growLastItemLen, freeSpaceEALen, err);
    if (err)
        return;

    // Call toISO for our subview.  This causes all the items of the subview
    // to write their data to our ByteArray.
    subView_->toISO(err);
    if (err)
        return;

    // If we are going to add some padding to the ByteArray, increase its length now.
    oldLen = subView_->length();            // can't use this->length()--it adds length of padding
    eaSubViewBA_.setLen(oldLen+growLastItemLen+freeSpaceEALen);

    // See if any padding is needed, and if so which kinds (do it only in our local ByteArray).
    if (growLastItemLen > 0) {
        // We need to pad the last item.  Zero out "growLastItemLen" bytes at the end
        // of our eaSubViewBA_ ByteArray.
        ByteArrayRef  tempBAR(eaSubViewBA_, oldLen, growLastItemLen);
        BYTE_PTR tempPtr = (BYTE_PTR)tempBAR;
        if (tempPtr != NULL)                // shouldn't happen, but just in case
            memset (tempPtr, 0, growLastItemLen);
        // Increase the attribute length in the prefix of the last item, by "growLastItemLen" bytes.
        lastItem = subView_->last();
        if (lastItem == NULL) {
            err = error_Required_object_not_present;
            return;
        }
        tempBAR = ByteArrayRef(eaSubViewBA_, lastItem->offset(),
            eaSubViewBA_.length()-lastItem->offset());
        EAPrefix        eaPrefix(0);
        eaPrefix.fromISO ((BYTE_PTR)tempBAR);
        eaPrefix.attrLen(eaPrefix.attrLen()+growLastItemLen);
        eaPrefix.toISO ((BYTE_PTR)tempBAR);
        oldLen += growLastItemLen;
    }
    if (freeSpaceEALen > 0) {
        // An ISOFreeSpaceEA pad item has been specified.  Allocate a buffer the length
        // that we need, and construct an image of the ISOFreeSpaceEA in it.
        // If the length specified is >= blockSize (entirely possible, though unusual), we
        // must break it up into more than one free space EA.
        BYTE_PTR  tempBuf = (BYTE_PTR)AllocateMemory(freeSpaceEALen);
        memset (tempBuf, 0, freeSpaceEALen);
        FreeEASpaceEA   myFreeEASpaceEA(0);
        minFreeEASpaceEALength = (myFreeEASpaceEA.minAttrLen()+3) & 0xFFFFFFFCUL;
        while (freeSpaceEALen > 0) {
            if (freeSpaceEALen >= blockSize_) {
                tempLen = minFreeEASpaceEALength;
            } else {
                tempLen = freeSpaceEALen;
            }
            myFreeEASpaceEA.reinit(tempLen);
            myFreeEASpaceEA.attrLen(tempLen, err);
            if (err) {
                FreeMemory(tempBuf);
                return;
            }
            myFreeEASpaceEA.toISO(tempBuf);
            // Good.  Now write this image to our eaSubViewBA_ ByteArray.
            ByteArrayRef  tempBAR(eaSubViewBA_, oldLen, tempLen);
            tempBAR.fromMem(tempBuf);
            oldLen += tempLen;
            freeSpaceEALen -= tempLen;
        }
        FreeMemory(tempBuf);
    }

    // Put the old LogExtentList into the trashList, and allocate a new one.
    ByteArrayRef    bar(eaSubViewBA_);
    baLen = bar.length();
  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("GOSEAViewItem::toDisk--putting old LEL into trash list--blocks: ");
    ExternalEASpace::printLogExtentList (myLocalExtList_);
  #endif
    myEASpace->putIntoTrashList (myLocalExtList_, err);
    if (err)
        return;
    space.takeLogExtentList(myLocalExtList_, baLen, err);
    if (err)
        return;
  #if defined DEBUG_EA__TRACK_DISK_ALLOCATION
    NSR::ExternalEASpace::markExtentsAllocatedForTesting (myLocalExtList_);
  #endif
  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("GOSEAViewItem::toDisk--getting new LEL--blocks: ");
    ExternalEASpace::printLogExtentList (myLocalExtList_);
  #endif

    // Because we changed the actual blocks of the LogExtentList, we must update the
    // location (ie., the logical block number where the header lives) in the header,
    // if there is one (the header will always be the first item in our list if it exists).
    firstItem = subView_->at(0, err);
    if (firstItem != NULL  &&  firstItem->isA() == EAViewHeaderItemType) {
        firstItem->location(myLocalExtList_.logBlockNo(0));
        firstItem->toISO(err);
        if (err)
            return;
    }

    myLocalExtList_.write(bar, err);

} // toDisk


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::length  --CJB
//  
//  Returns the total length that the data of this item will have when it is
//  written to its LogExtentList.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the length of the data
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::GroupOfShortsEAViewItem::length(void) const
{
    UINT32                  growLastItemLen;
    UINT32                  freeSpaceEALen;
    Error                   err;

    if (subView_ == NULL)
        return 0;

    // Return the length that this item will be after it is padded--that's what it will be
    // after we call toDisk(). 
    this->determinePaddingNeeded (growLastItemLen, freeSpaceEALen, err);
    return  subView_->length() + growLastItemLen + freeSpaceEALen;
} // length


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::numEntries  --CJB
//  
//  Returns the number of entries in this view item.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT16 -- returns the number of entries in this GroupOfShorts EA view item
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  The number of entries can be greater than one for GroupOfShortsEAViewItems;
//  for all others it is one.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT16
NSR::GroupOfShortsEAViewItem::numEntries(void)  const
{ 
    if (subView_ == NULL)
        return 0;
    return subView_->numEntries();
} // numEntries


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::findIndex  --CJB
//  
//  Returns the index of a specified item, relative to this EA view item.
//
// ARGS:
//  const EAViewItem*       itemToFind      IN  pointer to the item to find index for
//
// RETURNS:
//  INT16 -- returns the index of the specified item if it is found in this
//  GroupOFShorts EA view item's subview, otherwise returns invalidIndex.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  See the notes for LongEAView::findIndex and EAViewItem::findIndex for more
//  background on the workings of this method.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::INT16
NSR::GroupOfShortsEAViewItem::findIndex(const EAViewItem* itemToFind) const
{
    if (subView_ == NULL)
        return InvalidIndex;
    return subView_->findIndex(itemToFind);
} // findIndex


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::setPaddingToMin  --CJB
//  
//  For every item in this item's subview, sets the attribute length to the end of
//  the valid data, rounded up to the next four byte boundary.
//
// ARGS:
//  UINT32                  blockSize       IN  length of a block on the media
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The padding of all items in this item's subview is modified as necessary.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
// 
// NOTES:
//  This method sets every item in this item's/sub view to its minimum length (ie.,
//  padded up to the next four byte boundary past the end of its data).  It also
//  guarantees that all data concerned with length is consistent in each item.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::GroupOfShortsEAViewItem::setPaddingToMin (UINT32 blockSize,
                                               Error & err)
{
// This method resets every item in our subview to its minimum length (ie., padded up to the next
// four byte boundary past the end of its data).  It also guarantees that all data concerned with
// length is consistent in each item.
    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return;
    }
    subView_->setPaddingToMin(blockSize, err);
} // setPaddingToMin


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::determinePaddingNeeded  --CJB
//  
//  Returns the amount and kind of padding needed to round this group of short
//  items up to a block boundary.
//
// ARGS:
//  UINT32&                 growLastItemLen OUT amount to pad last item in the list
//  UINT32&                 freeSpaceEALen  OUT length of free space EA item needed
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
// 
// NOTES:
//  Although ordinarily only one of the two methods of padding (growing the last
//  item or adding a free space EA view item) will be specified by this method,
//  It is possible that both types could be specified.
//  It is also possible that a freeSpace EA view item specified by this method
//  could be equal to or greater than blockSize.  This indicates that more than
//  one free space EA view item must be inserted (like any short EA view item,
//  they must be less than blocksize).
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::GroupOfShortsEAViewItem::determinePaddingNeeded (UINT32 &growLastItemLen,
                                                      UINT32 &freeSpaceEALen,
                                                      Error & err) const
{
    ShortEAViewItem     *lastItem;
    UINT32              paddingNeeded;

    // Init the values for the lengths of the two possible types of padding..
    growLastItemLen = 0;
    freeSpaceEALen = 0;

    // If this is the last item in the top level list, get out--we don't pad those.
    if (parentView_ != NULL  &&  (EAViewItem*)this == parentView_->last()) {
        err = error_None;
        return;
    }
    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return;
    }

    // Get the last item in our subView, and calculate the length of padding is needed.
    lastItem = (ShortEAViewItem*)subView_->last();
    if (lastItem == NULL) {
        err = error_None;
        return;
    }
    paddingNeeded = blockSize_ - ((lastItem->offset()+lastItem->length()) % blockSize_);
    // If a full block of padding is needed, that means it already ends exactly on
    // a block boundary--we're done.
    if (paddingNeeded == blockSize_) {
        err = error_None;
        return;
    }

    // We need to do some padding somehow.  There are two special cases that require
    // an ISOFreeSpaceEA pad item to be appended to the list:
    //   (1) The last (and hopefully only) item is a header item. Since these don't have an
    //       attrLen field, we can't grow them.
    //   (2) We would have to grow the last item to be equal to or greater than blockSize--this
    //       isn't allowed.
    if (lastItem->isA() == EAViewHeaderItemType) {
        // If the padding needed is not a multiple of 4, the EAFreeSpace descriptor
        // will choke.  This should never happen because there should only be one header item
        // and its length is a multiple of 4.  But we need to cover ouselves--we can't do
        // anything in this case.
        if (paddingNeeded % 4 != 0) {
            err = error_EA_Invalid_EA_length;
            return;
        }
        // It should also never happen for this case, but if the amount of padding
        // needed is less than the minimum size of a free space EA, specify a free space
        // length that will extend to the end of the following block--the caller must break
        // this up into two free spaces EAs.
        if (paddingNeeded < ImplementUseEA::fixedLen())
            paddingNeeded += blockSize_;
        freeSpaceEALen = paddingNeeded;
    }
    else if ((lastItem->length() + paddingNeeded) >= blockSize_) {
        // We would have to grow the last item to be equal to or greater than blockSize--
        // instead, we must specify a free space EA.
        // If the padding needed is not a multiple of 4, the EAFreeSpace descriptor
        // will choke.  For this unusual case, also specify enough padding on the last
        // item to round it up to a 4 byte boundary.
        if (paddingNeeded % 4 != 0) {
            if ((lastItem->length() + (paddingNeeded%4)) >= blockSize_) {
                // Can't round the last item up--it is within 3 bytes of blockSize.
                err = error_EA_Invalid_EA_length;
                return;
            }
            growLastItemLen = paddingNeeded % 4;
            paddingNeeded -= paddingNeeded % 4;
        }
        // If the amount of padding needed is less than the minimum size of a free space EA,
        // specify a free space length that will extend to the end of the following block--the
        // caller must break this up into two free spaces EAs.
        if (paddingNeeded < ImplementUseEA::fixedLen())
            paddingNeeded += blockSize_;
        freeSpaceEALen = paddingNeeded;
    }
    else {
        // This is not a special case.  Just grow the last item enough so it ends on a block boundary.
        growLastItemLen = paddingNeeded;
    }

    err = error_None;
} // determinePaddingNeeded


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::findPadItemOffset  --CJB
//  
//  Returns the offset, relative to this item, to the first point where
//  padding will be added during the toDisk() call.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  UINT32 -- returns the offset, relative to this EA view item, to the first
//  point where padding will be added.  Returns zero if this item has no
//  pad item, or if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
// 
// NOTES:
//  Notice that no padding is carried in this item or its subview.  It is
//  only added to the data written out by the toDisk() method--even then it
//  is not put into this item or its subview.  If you need to know where
//  and how much padding will be added by toDisk(), see the
//  determinePaddingNeeded() method of this class.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::GroupOfShortsEAViewItem::findPadItemOffset(Error & err) const
{
    UINT32              growLastItemLen;
    UINT32              freeSpaceEALen;
    ShortEAViewItem     *lastItem;

    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return 0;
    }

    this->determinePaddingNeeded (growLastItemLen, freeSpaceEALen, err);
    if (err)
        return 0;

    if (freeSpaceEALen == 0)
        return 0;

    // Get the last item in our subView, and return its end (after any growing).
    lastItem = (ShortEAViewItem*)subView_->last();
    if (lastItem == NULL) {
        err = error_None;
        return 0;
    }
    err = error_None;
    return lastItem->offset()  + lastItem->length() + growLastItemLen;
} // findPadItemOffset


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::at  --CJB
//  
//  Returns a const pointer to an item at a specified index into the subview
//  of this view.
//
// ARGS:
//  INT16                   index           IN  index into this item's subview
//  Error&                  err             OUT error status
//
// RETURNS:
//  const NSR::EAViewItem* -- returns a const pointer to the specified item,
//  or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::GroupOfShortsEAViewItem::at(INT16 index,
                                 Error & err) const
{
    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }
    return subView_->at(index, err);
} // at


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::at  --CJB
//  
//  Returns a non-const pointer to an item at a specified index into the subview
//  of this view.
//
// ARGS:
//  INT16                   index           IN  index into this item's subview
//  Error&                  err             OUT error status
//
// RETURNS:
//  const NSR::EAViewItem* -- returns a pointer to the specified item,
//  or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::GroupOfShortsEAViewItem::at(INT16 index,
                                 Error & err)
{
    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }
    return subView_->at(index, err);
} // at


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::insertAt  --CJB
//  
//  Inserts an item into this item at a specified index.
//
// ARGS:
//  ShortEAViewItem*        seavi           IN  pointer to short EA view item to be inserted
//  INT16                   index           IN  index into this EA view item's subview
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The subview's internal list, and any other applicable member variables, are
//  updated to include the specified EA view item.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
// 
// NOTES:
//  The EA view item is not copied--it is put directly into the subview's list.
//  The implications of this are (1) The EA view item must be constructed
//  using the "new" operator, not on the stack; (2) This EA view item assumes
//  responsiblity for deleting the item.  This is done because the interface
//  presented to the eventual user (in the external EA space class) does a copy;
//  this interface can in a way be consdered internal.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::GroupOfShortsEAViewItem::insertAt(ShortEAViewItem * seavi,
                                       INT16 index,
                                       Error & err)
{
    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return;
    }
    subView_->insertAt(seavi, index, err);
}  // insertAt


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::removeAt  --CJB
//  
//  Removes an item from the view and returns a pointer to it.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the item that was removed, or NULL
//  if there was a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The subview and all other applicable member variables are updated to
//  indicate that the specified item is removed.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  This method does not delete the returned object--It is the
//  caller's responsibility to do that.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::GroupOfShortsEAViewItem::removeAt(INT16 index,
                                       Error & err)
{
// This method does not delete the EAViewItem object.
    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }
    return subView_->removeAt(index, err);
}  // removeAt

//----------------------------------------------------------------------
// METHOD: :GroupOfShortsEAViewItem::splitItem  --CJB
//  
//  Splits this item, at a specified index, into two items.
//
// ARGS:
//  INT16                   startIndexOfNewItem IN  index before which to split this item
//  Error&                  err             OUT error status
//
// RETURNS:
//  GroupOfShortsEAViewItem* -- returns a pointer to the new second
//  GroupOfShortsEAViewItem, or NULL if no split was done.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  This GroupOFShorts EA view item is split into two items.  Our lists and
//  all member variables are updated to reflect this.
// 
// ERRORS:
//  error_Index_out_of_bounds
//  error_Required_object_not_present
//  error_No_free_store
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Null_pointer
// 
// NOTES:
//  Does not split the item if startIndexOfNewItem is zero or is greater
//  than the highest index in our subView.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::GroupOfShortsEAViewItem*
NSR::GroupOfShortsEAViewItem::splitItem (INT16 startIndexOfNewItem,
                                         Error & err)
{
    const EAViewItem        *oldEAViewItem;
    ShortEAViewItem         *newEAViewItem;
    GroupOfShortsEAViewItem *newGOSEAViewItem;

    // Do some checks first.
    if (startIndexOfNewItem < 0) {
        err = error_Index_out_of_bounds;
        return NULL;
    }
    if (subView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }
    if (startIndexOfNewItem == 0  ||  startIndexOfNewItem >= subView_->numEntries()) {
        err = error_None;
        return NULL;
    }
    
    // Create a new GroupOfShortsEAViewItem, and move all items starting with startIndexOfNewItem
    // to the new GroupOfShortsEAViewItem.
    newGOSEAViewItem = new GroupOfShortsEAViewItem(blockSize_);
    if (newGOSEAViewItem == NULL) {
        err = error_No_free_store;
        return NULL;
    }
    if (err = newGOSEAViewItem->error())
        return NULL;

    // Remove each ShortEAViewItem (starting with the one at startIndexOfNewItem) from this 
    // GroupOfShortsEAViewItem, and insert a copy of it into the new GroupOfShortsEAViewItem.
    for (INT16 i=0; ; i++) {
        oldEAViewItem = this->at (startIndexOfNewItem, err);
        if (oldEAViewItem == NULL)
            break;

        const ByteArrayRef  bar((ByteArray&)oldEAViewItem->ReadOnlyEAByteArray());
        newEAViewItem = new ShortEAViewItem (bar);
        if (newEAViewItem == NULL) {
            err = error_No_free_store;
            return NULL;
        }
        if (err = newEAViewItem->error()) {
            delete newEAViewItem;
            return NULL;
        }
        newEAViewItem->regKey(oldEAViewItem->regKey());
        newGOSEAViewItem->insertAt(newEAViewItem, i, err);
        if (err) {
            delete newEAViewItem;
            return NULL;
        }
        this->removeAndDestroyAt (startIndexOfNewItem, err);
        if (err)
            return NULL;
    }

    err = error_None;
    return newGOSEAViewItem;

} // splitItem


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::combineWithNextItem  --CJB
//  
//  Combines this item with the next item.
//
// ARGS:
//  EAViewItem*             nextItem        IN  pointer to the next item in the EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  If the conditions are met, this GroupOfShorts EA view item is combined with
//  the next one.  Our lists and all applicable member variables are updated to
//  reflect this.
// 
// ERRORS:
//  error_Index_out_of_bounds
//  error_Required_object_not_present
//  error_No_free_store
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Null_pointer
// 
// NOTES:
//  Does nothing unless nextItem (1) is not NULL and (2) is a GroupOfShortsEAViewItem.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::GroupOfShortsEAViewItem::combineWithNextItem (EAViewItem * nextItem,
                                                   Error & err)
{
    const EAViewItem        *oldEAViewItem;
    ShortEAViewItem         *newEAViewItem;

    if (nextItem == NULL  ||  nextItem->isA() != GroupOfShortsEAViewItemType) {
        err = error_None;
        return;
    }

    // Remove each ShortEAViewItem from the next GroupOfShortsEAViewItem, and insert a copy of
    // it into this GroupOfShortsEAViewItem.
    for (INT16 i=this->numEntries(); ; i++) {
        oldEAViewItem = nextItem->at (0, err);
        if (oldEAViewItem == NULL)
            break;
        const ByteArrayRef  bar((ByteArray&)oldEAViewItem->ReadOnlyEAByteArray());
        newEAViewItem = new ShortEAViewItem (bar);
        if (newEAViewItem == NULL) {
            err = error_No_free_store;
            return;
        }
        if (err = newEAViewItem->error())
            return;
        newEAViewItem->regKey(oldEAViewItem->regKey());
        this->insertAt(newEAViewItem, i, err);
        if (err) {
            delete newEAViewItem;
            return;
        }
        nextItem->removeAndDestroyAt (0, err);
        if (err)
            return;
    }

    err = error_None;
} // combineWithNextItem


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::printAllocatedBlocks  --CJB
//  
//  For testing. Prints a list of all disk sectors held by this item.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::GroupOfShortsEAViewItem::printAllocatedBlocks(void) const
{
    printf ("   GOSEAViewItem LEL--blocks: ");
    ExternalEASpace::printLogExtentList (myLocalExtList_);
} // printAllocatedBlocks
#endif


//----------------------------------------------------------------------
// METHOD: GroupOfShortsEAViewItem::printItem  --CJB
//  
//  Prints data about this item.
//
// ARGS:
//  INT16                   index           IN  index into this EA view
//  Boolean                 compositeList   IN  indicates composite (versus physical) list
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  <Notes_go_here>
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::GroupOfShortsEAViewItem::printItem (INT16 index,
                                         Boolean compositeList,
                                         INT16 indent) const
{
    Error               err;
    const EAViewItem*   lastItem;
    UINT32              growLastItemLen;
    UINT32              freeSpaceEALen;

    for (INT16 i=0; i<indent; i++)
        printf (" ");
    printf ("index:%-2d GroupOfShortsEAViewItem  offset:%-3lu  length:%-3lu  (obj addr:%lu)\n",
        index, compositeList?this->overallOffset():this->offset(), this->length(), (UINT32)this);
        
    if (subView_ != NULL)
        subView_->printList(INDENT_AMOUNT+indent);

    // Get the lengths of any padding that needs to be added (we don't actually add it now).
    this->determinePaddingNeeded (growLastItemLen, freeSpaceEALen, err);
    if (err)
        return;

    // If any padding of either type is needed, write a note here indicating that.
    if (growLastItemLen > 0) {
        lastItem = at(this->numEntries()-1, err);
        if (lastItem == NULL)
            return;
        for (INT16 i=0; i<(3*INDENT_AMOUNT)+indent; i++)
            printf (" ");
        printf ("(this item will be be padded to be %lu bytes)\n", lastItem->length()+growLastItemLen);
    }
    if (freeSpaceEALen > 0) {
        lastItem = at(this->numEntries()-1, err);
        if (lastItem == NULL)
            return;
        for (INT16 i=0; i<(2*INDENT_AMOUNT)+indent; i++)
            printf (" ");
        if (freeSpaceEALen >= blockSize_)
            printf ("(multiple ISOFreeSpaceEAs will be written here  offset:%-3lu  total length:%-3lu)\n",
                lastItem->offset()+lastItem->length(), freeSpaceEALen);
        else printf ("(an ISOFreeSpaceEA will be written here  offset:%-3lu  length:%-3lu)\n",
            lastItem->offset()+lastItem->length(), freeSpaceEALen);
    }
} // printItem
#endif





// --------------------------------------------------------------------------------
// Methods of the EASpace class:



//----------------------------------------------------------------------
// METHOD: EASpace::EASpace  --CJB
//  
//  Constructor.
//
// ARGS:
//  const LogicalVolume&    logicalVolume   IN  the logical volume object
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  An EASpace object is constructed.
// 
// ERRORS:
//  (Call error() to get the object's error status.)
//  error_No_free_store
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EASpace::EASpace(LogicalVolume& logicalVolume) :
        logicalVolume_(logicalVolume),
        blockSize_(logicalVolume.blockSize()),
        dirty_(False),
        error_(error_None)
{
    numInstances++;
    if (numInstances == 1) {
        nullLEL = new LogExtentList(logicalVolume);
        if (nullLEL == NULL) {
            error_ = error_No_free_store;
            return;
        }
        error_ = nullLEL->error();
    }
} // EASpace constructor


//----------------------------------------------------------------------
// METHOD: EASpace::~EASpace  --CJB
//  
//  Destructor.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Object is destroyed; all memory is freed.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  Notice that this object keeps a static count of the number of instances
//  (numInstances).  This is used to determine when the last instance is being
// destroyed, so that nullLEL can be deleted.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EASpace::~EASpace (void)
{
    if (numInstances <= 0) {    // shouldn't happen, but just in case
        numInstances = 0;
        return;
    }
    numInstances--;
    if (numInstances > 0)
        return;
    if (nullLEL == NULL)
        return;
    delete nullLEL;
    nullLEL = NULL;
} // EASpace destructor


//----------------------------------------------------------------------
// METHOD: EASpace::findNextMatch  --CJB
//  
//  Returns the first or next item with a specified regKey.
//
// ARGS:
//  UINT32                  regKey          IN  regKey value to match
//  INT16*                  index           IN/OUT virtual index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the EA view item that was found, or NULL if none.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_EA_Not_found
// 
// NOTES:
//  To find the first match, index must be set to a negative value.  The order
//  in which matches are returned is their order in the composite list.
//  IMPORTANT: This method expects and returns virtual indexes.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::EASpace::findNextMatch(UINT32 regKey,
                            INT16 * index,
                            Error & err) const
{
    const EAViewItem *  item;

    // Skip past the current item, or until nonnegative.
    do          
        (*index)++;
    while (*index < 0);

    for ( ; ; (*index)++) {
        item = this->at(*index, err);           // fetches by virtual index
        if (item == NULL)
            break;
        if (item->regKey() == regKey) {
            err = error_None;
            return item;
        }
    } // for loop

    *index = InvalidIndex;
    err = error_EA_Not_found;
    return NULL;
    
} // findNextMatch


//----------------------------------------------------------------------
// METHOD: EASpace::printCompositeList  --CJB
//  
//  Prints the virtual list of items in this EA space.
//
// ARGS:
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::EASpace::printCompositeList (INT16 indent) const
{
    const EAViewItem            *MyEAViewItem;
    Error                       err;
    
    this->updateHeaderOffsets (err);
    printf ("\n");
    printf ("Composite list:\n");

    // First print the header (we need to get it by physical index).
    MyEAViewItem = this->privateAt(0, err);
    if (MyEAViewItem == NULL)
        return;
    MyEAViewItem->printItem (0, True, INDENT_AMOUNT+indent);

    // Now print all the "real" items by virtual index.
    for (INT16 index=0; ; index++) {
        MyEAViewItem = this->at(index, err);
        if (MyEAViewItem == NULL)
            break;
        MyEAViewItem->printItem (index, True, INDENT_AMOUNT+indent);
    }

} // printCompositeList
#endif



// ---------------start of protected methods of the EASpace class----------------------



//----------------------------------------------------------------------
// METHOD: EASpace::findNextOfThisCategory  --CJB
//  
//  Returns the first or next item of a specified EACategory.
//
// ARGS:
//  EACategory              category        IN  category to search for
//  INT16*                  index           IN/OUT virtual index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the EA view item that was found, or NULL if none.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_EA_Not_found
// 
// NOTES:
//  To find the first item, index must be set to a negative value.  The order
//  in which items are returned is their order in the EA Space.
//  See EARegistryEntry::eaCategory() for more background on categories.
//  IMPORTANT: This method expects and returns virtual indexes.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::EASpace::findNextOfThisCategory(EACategory category,
                                     INT16 *index,
                                     Error & err) const
{
    const EAViewItem *  item;

    do          // skip past current item, or until nonnegative
        (*index)++;
    while (*index < 0);

    for ( ; ; (*index)++) {
        item = this->at(*index, err);           // fetches by virtual index
        if (item == NULL)
            break;
        if (item->eaCategory(err) == category) {
            err = error_None;
            return item;
        }
    } // for loop

    *index = InvalidIndex;
    err = error_EA_Not_found;
    return NULL;
    
} // findNextOfThisCategory


//----------------------------------------------------------------------
// METHOD: EASpace::createHeader  --CJB
//  
//  Creates a header item, and inserts it into the EA space.
//
// ARGS:
//  UINT32                  location        IN  logical block number where header is to be written
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  A header EA view item is created and inserted into our internal list.
//  The dirty flag is set.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found
//  error_Index_out_of_bounds
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EASpace::createHeader (UINT32 location,
                            Error & err)
{
    ExtendedAttrHeaderDesc  myHeaderDesc;
    UINT32                  headerLen;

    headerLen = myHeaderDesc.descLen();
    myHeaderDesc.implAttrLocation(0);
    myHeaderDesc.applAttrLocation(0);
    myHeaderDesc.descLen(headerLen);
    ByteArray       myHdrBA(headerLen);
    myHeaderDesc.toISO ((BYTE_PTR)myHdrBA, location);
    ByteArrayRef    myHdrBAR(myHdrBA, 0, headerLen);

    EAViewHeaderItem  heavi(myHdrBAR, location);
    if (err = heavi.error())
        return;
    
    // Call the private method that bypasses all checks--the public method doesn't allow
    // headers to be inserted.  We trust ourselves to not try to insert anything foolish.
    this->privateInsert (heavi, ea_Hdr_Key, err);
} // createHeader


//----------------------------------------------------------------------
// METHOD: EASpace::updateHeaderOffsets  --CJB
//  
//  Updates the offsets that the header item keeps, to the first
//  implementation and application use EAs.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  If the header EA view item doesn't exist, an error will be returned.
// 
// POST-CONDITIONS:
//  The offsets in the header EA view item are updated to reflect the current list.
// 
// ERRORS:
//  error_EA_Not_found
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
// 
// NOTES:
//  Notice this method only updates the offset information in the header item
//  object; it is not written to the caller's ByteArray or extent list here--that
//  is done by toISO() or toDisk().
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EASpace::updateHeaderOffsets(Error & err) const
{
    const EAViewItem    *myEAViewItem;
    EAViewHeaderItem    *myHeaderViewItem;
    Error               tempErr;
    INT16               index;
    UINT32              offset;
    UINT32              firstPadItemOffset;

    // Get the header view item.
    myHeaderViewItem = (EAViewHeaderItem*)this->privateAt(0, err);
    if (myHeaderViewItem == NULL  ||  myHeaderViewItem->isA() != EAViewHeaderItemType) {
        err = error_EA_Not_found;
        return;
    }

    // Find and set the offset to the first implementation use EA.  If there are none, use
    // the total length of all EAs (per OSTA 3.3.4.1)
    index = -1;
    myEAViewItem = this->findNextOfThisCategory(ea_ImplUseCategory, &index, err);
    if (myEAViewItem == NULL) {
        // Ignore the error.
        err = error_None;
        offset = this->length();
    }
    else offset = myEAViewItem->overallOffset();
    // See if there will be a pad item (to be inserted by toDisk) that will come before
    // the first Implementation Use item that we just found.
    firstPadItemOffset = this->findFirstPadItemOffset(tempErr);     // save this error
    if (firstPadItemOffset > 0  &&  firstPadItemOffset < offset)
        offset = firstPadItemOffset;
    myHeaderViewItem->implOffset(offset);

    // In a similar way, set the offset to the first application use EA.
    index = -1;
    myEAViewItem = this->findNextOfThisCategory(ea_ApplUseCategory, &index, err);
    if (myEAViewItem == NULL) {
        // Ignore the error.
        err = error_None;
        offset = this->length();
    }
    else offset = myEAViewItem->overallOffset();
    myHeaderViewItem->applOffset(offset);

    // If we stored an error, return it now.
    if (tempErr  &&  !err)
        err = tempErr;
} // updateHeaderOffsets


//----------------------------------------------------------------------
// METHOD: EASpace::updateHeaderLocation  --CJB
//  
//  Updates the location that the header item keeps, of the logical block number
//  where it is to be written on disk.
//
// ARGS:
//  UINT32                  location        IN  logical block number where header is to be written
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  If the header EA view item doesn't exist, an error will be returned.
// 
// POST-CONDITIONS:
//  The location in the header EA view item is updated to the specified value.
// 
// ERRORS:
//  error_EA_Not_found
// 
// NOTES:
//  Notice this method only updates the location information in the header item
//  object; it is not written to the caller's ByteArray or extent list here--that
//  is done by toISO() or toDisk().
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EASpace::updateHeaderLocation(UINT32 location,
                                   Error & err) const
{
    EAViewHeaderItem    *myHeaderViewItem;

    // Get the header view item.
    myHeaderViewItem = (EAViewHeaderItem*)this->privateAt(0, err);
    if (myHeaderViewItem == NULL  ||  myHeaderViewItem->isA() != EAViewHeaderItemType) {
        err = error_EA_Not_found;
        return;
    }
    myHeaderViewItem->location(location);
    err = error_None;
} // updateHeaderLocation




// --------------------------------------------------------------------------------
// Methods of the EmbeddedEASpace class:



//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::EmbeddedEASpace  --CJB
//  
//  Constructor.
//
// ARGS:
//  const LogicalVolume&    logicalVolume   IN  the logical volume object
//  ByteArray&              srcBA           IN  the caller's ByteArray
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  An EmbeddedEASpace object is constructed.
// 
// ERRORS:
//  (Call error() to get the object's error status.)
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found
//  error_Index_out_of_bounds
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EmbeddedEASpace::EmbeddedEASpace(LogicalVolume& logicalVolume,
                                      ByteArray & srcBA) :
        EASpace(logicalVolume),
        srcByteArray_(srcBA)
{
    // Check for any errors from the base class' constructor.
    if (error_)
        return;

    // Create the view.  Notice we set the parentGOSViewItem pointer to NULL--it doesn't apply here.
    eaView_ = new ShortEAView(srcByteArray_, this, NULL);
    if (eaView_ == NULL) {
        error_ = error_No_free_store;
        return;
    }
    if (error_ = eaView_->error())
        return;

    // Create a header view item, and insert it into the view.  We don't know the node
    // location yet--use zero.
    this->createHeader (0, error_);
} // EmbeddedEASpace constructor


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::copy_  --CJB
//  
//  Method to do the actual work of the copy constructor and assignment operator.
//
// ARGS:
//  const EmbeddedEASpace&  x               IN  source object to copy
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Copies the source object into this object; all member variables reflect this.
// 
// ERRORS:
//  error_Null_pointer
//  error_EA_Invalid_EA_type
//  error_No_free_store
// 
// NOTES:
//  If the source object is the same as this, nothing is done--ie., we don't
//  allow copying to oneself.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EmbeddedEASpace::copy_ (const EmbeddedEASpace & x,
                             Error & err)
{
    err = error_None;
    if (&x == this)
        return;

    srcByteArray_ = x.srcByteArray_;

    eaView_ = new ShortEAView(*(x.eaView_));
    if (eaView_ == NULL) {
        err = error_No_free_store;
        return;
    }
    err = eaView_->error();
    eaView_->myEASpace(this);
} // copy_


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::~EmbeddedEASpace  --CJB
//  
//  Destructor.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The object is destroyed; all memory is freed.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EmbeddedEASpace::~EmbeddedEASpace (void)
{
    if (eaView_ != NULL) {
        delete eaView_;
        eaView_ = NULL;
    }
} // EmbeddedEASpace destructor


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::fromISO  --CJB
//  
//  Reads the data into this EA space from the caller's ByteArray.
//
// ARGS:
//  UINT32                  nodeLocation    IN  logical block number of NSR file node
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists are populated to reflect the data of the caller's ByteArray.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_in_src_BA
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found
//  error_Index_out_of_bounds
//  error_EA_Not_found
//  error_Bad_Parameter_EASpace
//  error_EA_Invalid_multiple_occurences_of_EA
//  error_EA_Invalid_EA_placement
// 
// NOTES:
//  It is not required to call this method, and it may be called at any time, but
//  if it is called the only reasonable time to call it is immediately after
//  construction. This is because it deletes all current entries of the EASpace
//  before adding the entries from the caller's ByteArray--is this what you want
//  to do?
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EmbeddedEASpace::fromISO (UINT32 nodeLocation,
                               Error & err)
{
// The eaViewBA_ must have valid data before this method is called.
    INT32                   totalLen;
    UINT32                  offset;
    UINT32                  myAttrType;
    UINT8                   mySubtype;
    UINT32                  regKey;
    UINT32                  myAttrLen;
    ExtendedAttrHeaderDesc  myHeaderDesc;
    UINT32                  headerLen;
    SAIEAInspector          myEAInspector;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return;
    }

    // Clear anything that may be in the list, and then create a header item.
    eaView_->clear();
    this->createHeader (nodeLocation, err);
    if (err)
        return;
        
    // See if there is a header at the start of srcByteArray_--we need to skip past it,
    // if it is present.
    myHeaderDesc.fromISO ((BYTE_PTR)srcByteArray_, nodeLocation);
    if ((!myHeaderDesc.error())  &&  (myHeaderDesc.type() == tag_EAHD))
        headerLen = myHeaderDesc.descLen();
    else headerLen = 0;

    ByteArrayRef  myBAR (srcByteArray_);
    totalLen = myBAR.length() - headerLen;
    offset = headerLen;

    for (;;) {
        // Use the EA Inspector to get the basic information we need to operate on this EA.
        myBAR.offset(offset);
        myBAR.length(totalLen);
        myEAInspector.fromISO ((BYTE_PTR)myBAR, totalLen);
        if (err = myEAInspector.error()) {
            if (myEAInspector.error() == error_EA_Invalid_EA_length)
                err = error_None;
            break;
        }
        myAttrLen = myEAInspector.attrLen();    
        myAttrType = myEAInspector.attrType();
        mySubtype = myEAInspector.attrSubtype();
        String myattrID = myEAInspector.attrID();
        regKey = EARegistry::find (myAttrType, mySubtype, myattrID, err);

        // First check to see if this looks like a valid EA, then determine if we should ignore it.
        if (regKey == ea_InvalidKey  || myAttrLen == 0  ||  myAttrLen > totalLen) {
            // Garbage--don't know what else to do to protect ourselves, except get out.
            err = error_EA_Invalid_EA_in_src_BA;
            break;
        }

        // Then check for EA types that we want to not let in.
        else if (regKey == ea_Hdr_Key  ||  regKey == ea_Impl_FreeSpace_Key) {
        }

        else {
            // Insert a new short item into the list.
            myBAR.length(myAttrLen);
            ShortEAViewItem  seavi(myBAR);
            if (err = seavi.error())
                break;
            this->insert (seavi, err);
            if (err)
                break;
        }

        offset += myAttrLen;
        totalLen -= myAttrLen;
    } // for loop

    // If we reached this point with an error, preserve it acoross the updateheader call.
    Error   tempErr;
    this->updateHeaderOffsets (tempErr);
    if (!err)
        err = tempErr;

  #if defined DEBUG_EA__BASIC_DISK_SPACE_TRACE  ||  defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("leaving EmbeddedEASpace::fromISO  error %d\n", (int)err);
  #endif

} // fromISO


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::toISO  --CJB
//  
//  Writes all items in this EA space into the caller's ByteArray.
//
// ARGS:
//  UINT32                  nodeLocation    IN  logical block number of NSR file node
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The caller's ByteArray is updated to contain the current data of all items
//  in this EA space.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_EA_Not_found
// 
// NOTES:
//  If nothing has been modified (ie., the dirty flag isn't set) we don't write
//  anything back. Notice we never reset the dirty flag, because the caller
//  could potentially keep references to the ByteArrays around for the life of
//  this object.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EmbeddedEASpace::toISO (UINT32 nodeLocation,
                             Error & err)
{
    // If nothing has been modified, don't write anything back.
    if (!dirty_) {
        err = error_None;
        return;
    }

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return;
    }

    // Go through all items and set their padding to the minimum length.
    // This also guarantees that all data concerned with length is consistent in each item.
    eaView_->setPaddingToMin (blockSize_, err);
    if (err)
        return;

    // Update the information in the header item.  This must be done after the setPaddingToMin
    // call, but before the eaView_->toISO call.
    this->updateHeaderOffsets (err);
    if (err)
        return;
    this->updateHeaderLocation (nodeLocation, err);
    if (err)
        return;

    // Now call toISO for our view.  Each item will write its bytes at the approporate
    // offset in the caller's ByteArray.
    eaView_->toISO(err);

  #if defined DEBUG_EA__BASIC_DISK_SPACE_TRACE  ||  defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("leaving EmbeddedEASpace::toISO  error %d\n", (int)err);
  #endif

} // toISO


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::length  --CJB
//  
//  Returns the total data length this EA space will have when it is written
//  to its ByteArray.  It includes the length of the header.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the total data length of this EA space (including the header).
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EmbeddedEASpace::length(void) const
{
    if (eaView_ == NULL)
        return 0;
    return eaView_->length();
} // length


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::numEntries  --CJB
//  
//  Returns the total number of items in this EA space, not including the header.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the total number of entries in this EA space (not including
//  the header).
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::EmbeddedEASpace::numEntries(void) const
{
    UINT32      totalEntries;

    if (eaView_ == NULL)
        return 0;
    totalEntries = eaView_->numEntries();
    if (totalEntries == 0)
        return 0;
    return totalEntries - 1;        // adjust for header--we pretend those don't exist here
} // numEntries


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::insert  --CJB
//  
//  Inserts an item into the embedded EA space.  Ordering is done automatically,
//  according to the ISO and OSTA rules.
//
// ARGS:
//  ShortEAViewItem&        seavi           IN  short EA view item to be inserted
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a pointer to the item inserted in our lists (notice
//  this is a copy of the caller's item), or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists and all applicable member variables are updated to
//  reflect the insertion of the specified item. The dirty flag is set.
// 
// ERRORS:
//  error_EA_Invalid_EA_type
//  error_Bad_Parameter_EASpace
//  error_EA_Invalid_multiple_occurences_of_EA
//  error_EA_Invalid_EA_placement
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
//  error_No_free_store
// 
// NOTES:
//  (1) The caller's EA view item is copied--it is not put directly into our list.
//  The implication of this is that the caller is responsible for deleting
//  his original item.
//  (2) This method, not having an insertion point parameter, enables this
//  layer to ensure that the proper ordering rules are followed.
//  It uses EAViewItem::CompareItem() to determine the insertion point.
//  (3) This method verifies that this attribute is allowed in the embedded EA space.
//  (It finds that out from the registry).  It also verifies that this attribute
//  is unique in the embedded EA space if it needs to be (it finds that out from
//  the registry). Notice that we cannot check for duplication of instances between
//  the embedded and external EA spaces.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::EmbeddedEASpace::insert (const ShortEAViewItem & seavi,
                              Error & err)
{
    const EARegistryEntry * myRegEntry;
    SAIEAInspector          myEAInspector;
    UINT32                  myAttrType;
    UINT8                   mySubtype;
    UINT32                  regKey;

    // Get a ByteArrayRef to the item's data.
    ByteArrayRef myBAR((ByteArray&)seavi.ReadOnlyEAByteArray());

    // Get the regKey from data obtained from the EA Inspector.
    myEAInspector.fromISO ((BYTE_PTR)myBAR, myBAR.length());
    if (err = myEAInspector.error())
        return NULL;
    myAttrType = myEAInspector.attrType();
    mySubtype = myEAInspector.attrSubtype();
    String myAttrID = myEAInspector.attrID();
    regKey = EARegistry::find (myAttrType, mySubtype, myAttrID, err);

    // Check for invalid EA types, or types that we don't want to let in.
    if (regKey == ea_InvalidKey  ||
        regKey == ea_Hdr_Key  ||
        regKey == ea_Impl_FreeSpace_Key) {
        err = error_EA_Invalid_EA_type;
        return NULL;
    }

    // Get the registry entry for the caller's regKey.
    myRegEntry = EARegistry::at (regKey, err);
    if (myRegEntry == NULL) {
        err = error_Bad_Parameter_EASpace;
        return NULL;
    }

    // If only one instance of this type of EA is allowed, check for that.
    if (myRegEntry->eaMultInstAllwd() == False) {
        INT16               vIndex = -1;
        findNextMatch(regKey, &vIndex, err);
        if (err == error_None) {
            err = error_EA_Invalid_multiple_occurences_of_EA;
            return NULL;
        }
    }

    // If this type of EA must be in the external space, we can't allow it here.
    if (myRegEntry->eaPlacement() == ea_MustBeInExternalSpace) {
        err = error_EA_Invalid_EA_placement;
        return NULL;
    }

    // All checks have been passed.  Go do the actual insert.
    return this->privateInsert (seavi, regKey, err);
} // insert


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::bytesAvailable  --CJB
//  
//  Returns the remaining number of bytes available for use in the embedded
//  EA space of the NSR file node.
//
// ARGS:
//  Node&                   myNode          IN  the NSR file node
//  Error&                  err             OUT error status
//
// RETURNS:
//  INT32 -- returns the remaining number of bytes available in the embedded
//  EA space of the NSR file node. A negative number indicates a deficit--ie.,
//  the data will fail to fit by that many bytes.  Zero indicates that the
//  data will just fit.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (only error_None)
// 
// NOTES:
//  This EA space does not have to be formally associated with the NSR file
//  node in any way--it could have been constructed and populated via inserts
//  and removes without ever havin done a fromISO().  In that case this method
//  returns the hypothetical number of bytes available if the caller were to
//  do a toISO() call.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::INT32
NSR::EmbeddedEASpace::bytesAvailable(Node& myNode,
                                     Error& err) const
{
    err = error_None;
    return myNode.descriptorBytesAvailable (blockSize_)
            + srcByteArray_.length() - this->length();  
} // BytesAvailable


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::printPhysicalLists  --CJB
//  
//  Prints the various underlying physical lists in this EA space.
//
// ARGS:
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::EmbeddedEASpace::printPhysicalLists (INT16 indent) const
{
    Error           err;

    if (eaView_ == NULL)
        return;
    this->updateHeaderOffsets (err);
    printf ("\n");
    printf ("EmbeddedEASpace   (obj addr:%lu)   Physical lists:\n", (UINT32)this);
    eaView_->printList(INDENT_AMOUNT+indent);

} // printPhysicalList
#endif



// ---------------start of private methods of the EmbeddedEASpace class----------------------



//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::findInsertionPoint  --CJB
//  
//  Finds the index where a specified item should be inserted.
//
// ARGS:
//  const EAViewItem*       myItem          IN  pointer to item to be inserted
//  INT16&                  index           OUT physical index into this EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
// 
// NOTES:
//  This method returns an index to the point just after all similar items
//  in the composite list.  ("Similar" is defined by EASpace::compareForInsertion).
//  IMPORTANT: this method returns a physical index.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::EmbeddedEASpace::findInsertionPoint(const EAViewItem *myItem,
                                         INT16& index,
                                         Error & err) const {
    const EAViewItem *  item;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        index = InvalidIndex;
        return;
    }

    for (index=0; ; index++) {
        item = eaView_->at(index, err);
        if (item == NULL)
            break;
        if (item->compareForInsertion(myItem) > 0)
            break;
    } // for loop
    err = error_None;
}  // findInsertionPoint


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::privateAt  --CJB
//  
//  Returns a const pointer to an EAViewItem by physical index into the EASpace.
//
// ARGS:
//  INT16                   index           IN  physical index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a const pointer to the EA view item at the
//  specified physical index, or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  IMPORTANT: This method expects a physical index.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::EmbeddedEASpace::privateAt(INT16 index,
                                Error & err) const
{
    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;    
    }
    return eaView_->at(index, err);
} // privateAt


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::privateAt  --CJB
//  
//  Returns a non-const pointer to an EAViewItem by physical index of the EASpace.
//
// ARGS:
//  INT16                   index           IN  physical index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the EA view item at the specified
//  physical index, or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  IMPORTANT: This method expects a physical index.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::EmbeddedEASpace::privateAt(INT16 index,
                                Error & err)
{
    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;    
    }
    return eaView_->at(index, err);
} // privateAt


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::privateRemoveAt  --CJB
//  
//  Removes an EAViewItem by physical index into the EASpace, and returns a
//  pointer to the removed item.
//
// ARGS:
//  INT16                   index           IN  physical index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the specified EA view item and
//  removes it from this EA view. Returns NULL if there is any problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The specified EA view item is removed from this EA space. The dirty flag is set.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  This method does not delete the returned object--It is the
//  caller's responsibility to do that.
//  IMPORTANT: This method expects a physical index.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// This method removes the EAViewItem.
NSR::EAViewItem *
NSR::EmbeddedEASpace::privateRemoveAt(INT16 index,
                                      Error & err)
{
    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;    
    }

    // We are committed to the remove.  Mark this EA space dirty, so it will get written
    // back on the toISO call.
    dirty_ = True;

    return eaView_->removeAt(index, err);
} // privateRemoveAt


//----------------------------------------------------------------------
// METHOD: EmbeddedEASpace::privateInsert  --CJB
//  
//  Does the actual work of inserting a short EA view item into the embedded EA space.
//
// ARGS:
//  const ShortEAViewItem&  seavi           IN  short EA view item to be inserted
//  UINT32                  regKey          IN  regKey value for the item to be inserted
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a pointer to the item inserted in our lists (notice
//  this item is a copy of the caller's item), or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists and all applicable member variables are updated to
//  reflect the insertion of the specified item. The dirty flag is set.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
// 
// NOTES:
//  (1) The caller's EA view item is copied--it is not put directly into our list.
//  The implication of this is that the caller is responsible for deleting
//  his original item.
//  (2) All checking must be done before this private method is called.
//  (3) This method, not having an insertion point parameter, enables this
//  layer to ensure that the proper ordering rules are followed.
//  It uses EAViewItem::CompareItem() to determine the insertion point.
//  (4) This method verifies that this attribute is allowed in the embedded EA space.
//  (It finds that out from the registry).  It also verifies that this attribute
//  is unique in the embedded EA space if it needs to be (it finds that out from
//  the registry). Notice that we cannot check for duplication of instances between
//  the embedded and external EA spaces.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::EmbeddedEASpace::privateInsert (const ShortEAViewItem &seavi,
                                     UINT32 regKey,
                                     Error & err)
{
    INT16                   index;
    ShortEAViewItem         *mySeavi;
    
    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }

    // Sure would like to get rid of this switch.
    switch (seavi.isA()) {
    case EAViewHeaderItemType:
        mySeavi = new EAViewHeaderItem((EAViewHeaderItem&)seavi);
        break;
    case ShortEAViewItemType:
        mySeavi = new ShortEAViewItem(seavi);
        break;
    default:
        err = error_EA_Invalid_EA_type;
        return NULL;
    } // switch
    if (mySeavi == NULL) {
        err = error_No_free_store;
        return NULL;
    }
    if (err = mySeavi->error()) {
        delete mySeavi;
        return NULL;
    }

    // Set the regkey in our new item.
    mySeavi->regKey (regKey);

    // Set up the padding for this item so it just extends to the next four byte boundary past
    // the the data.  This also guarantees that all data concerned with length is consistent.
    mySeavi->setPaddingToMin (blockSize_, err);
    if (err) {
        delete mySeavi;
        return NULL;
    }

    this->findInsertionPoint(mySeavi, index, err);
    if (err) {
        delete mySeavi;
        return NULL;
    }

    eaView_->insertAt(mySeavi, index, err);
    if (err) {
        delete mySeavi;
        return NULL;
    }

    // We did the insert.  Mark this EA space dirty, so it will get written
    // back on the toISO call.
    dirty_ = True;
    
    return mySeavi;
} // privateInsert




// --------------------------------------------------------------------------------
// Methods of the ExternalEASpace class:



//----------------------------------------------------------------------
// METHOD: ExternalEASpace::ExternalEASpace  --CJB
//  
//  Constructor.
//
// ARGS:
//  LogExtentList&          lel             IN  the caller's extent list
//  ISOSpaceResource*       spaceResource   IN  pointer to space resource object
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  An ExternalEASpace object is constructed.
// 
// ERRORS:
//  (Call error() to get the object's error status.)
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found
//  error_Index_out_of_bounds
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::ExternalEASpace::ExternalEASpace(LogExtentList & lel,
                                      ISOSpaceResource *spaceResource) :
        EASpace(lel.logicalVolume()),
        callersLel_(lel),
        trashLel_(lel.logicalVolume()),
        spaceResource_(spaceResource)
{
    // Check for any errors from the base class' constructor.
    if (error_)
        return;

    if (spaceResource_ == NULL) {
        error_ = error_Required_object_not_present;
        return;     
    }
    if (error_ = callersLel_.error())
        return;
    if (error_ = trashLel_.error())
        return;
    eaView_ = new LongEAView(this);
    if (eaView_ == NULL) {
        error_ = error_No_free_store;
        return;     
    }
    if (error_ = eaView_->error())
        return;

    // Create a header view item, and insert it into the view.  We don't know the
    // location yet--use zero.
    this->createHeader (0, error_);
} // ExternalEASpace constructor


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::copy_  --CJB
//  
//  Method to do the actual work of the copy constructor and assignment operator.
//
// ARGS:
//  const ExternalEASpace&  x               IN  source object to copy
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Copies the source object into this object; all member variables reflect this.
// 
// ERRORS:
//  error_Null_pointer
//  error_EA_Invalid_EA_type
//  error_No_free_store
// 
// NOTES:
//  (1) If the source object is the same as this, nothing is done--ie., we don't
//  allow copying to oneself.
//  (2) Notice that we don't copy the trash list--we always create the new object
//  with an empty trash list. The source object's trash list will be deallocated
//  when that object is destroyed; if we copied the trash list then at some point
//  we would try to deallocate the same extents in this object also--we can't allow
//  that.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::copy_ (const ExternalEASpace & x,
                             Error & err)
{
    err = error_None;
    if (&x == this)
        return;

    callersLel_ = x.callersLel_;
    if (err = callersLel_.error())
        return;
    // Don't copy the trash list--see note.
    trashLel_ = LogExtentList(x.logicalVolume_);
    if (error_ = trashLel_.error())
        return;
    dirty_ = x.dirty_;
    spaceResource_ = x.spaceResource_;

    eaView_ = new LongEAView(*(x.eaView_));
    if (eaView_ == NULL) {
        err = error_No_free_store;
        return;
    }
    err = eaView_->error();
    eaView_->myEASpace(this);
} // copy_


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::~ExternalEASpace  --CJB
//  
//  Destructor.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Destroys the object; all memory is freed.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  As part of the destruction, all disk space that we "hold" internally,
//  that is not also in the caller's extent list is actually deallocated.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::ExternalEASpace::~ExternalEASpace (void)
{
    Error               err;

    // The combination of these two calls will deallocate all disk space we hold,
    // that is not also in the caller's extent list.  We always want to do this
    // before we are destroyed.
    this->deallocateEADiskSpace(err);
    this->emptyTrashList(err);

    if (eaView_ != NULL) {
        delete eaView_;
        eaView_ = NULL;
    }
} // ExternalEASpace destructor


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::fromDisk  --CJB
//  
//  Reads the data of this EA space from the caller's extent list.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists are populated to reflect the data of the caller's extent list.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_Index_out_of_bounds
//  error_REGISTRY_Entry_not_found
//  error_SR_Cannot_free_space
//
// NOTES:
//  It is not required to call this method, and it may be called at any time, but
//  if it is called the only reasonable time to call it is immediately after
//  construction. This is because it deletes all current entries of the EASpace
//  before adding the entries from the caller's extent list--is this what you want
//  to do?
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::fromDisk (Error & err)
{
    INT32                   bytesLeftInBuf;
    INT32                   originalReadLen;
    UINT32                  offset;
    UINT32                  blocksToAdvance;
    UINT32                  myAttrType;
    UINT8                   mySubtype;
    UINT32                  regKey;
    UINT32                  myAttrLen;
    UINT32                  myFixedPlusVaryingLen;
    UINT32                  headerLocation;
    Error                   tempErr;
    SAIEAInspector          myEAInspector;

    // Make sure we have the object we need.
    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return;
    }

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("entering ExternalEASpace::fromDisk\n");
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #endif
  
    // Put the extents of all items into our trash list.
    eaView_->removeAllMyExtents (False, trashLel_, err);
    if (err)
        return;
        
  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("ExternalEASpace::fromDisk  after deallocate of all LELs\n");
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #endif
    
    // Now we are sure there is no disk space allocated in our list.  Clear the list.
    eaView_->clear();
  
    // Create a header item. The location of the the header is the logical block number
    // of the first block of the LogExtentList, if it exists.
    if (callersLel_.numLogExtents() > 0)
        headerLocation = callersLel_.logBlockNo(0);
    else headerLocation = 0;
    this->createHeader (headerLocation, err);
    if (err)
        return;

    // Create the ByteArray and ByteArrayRef objects that we need..
    ByteArray               myBA(2 * blockSize_);
    ByteArrayRef            myBAR(myBA);

    // Create a local copy of the source LogExtentList.
    LogExtentList           myLEL (callersLel_);
    if (err = myLEL.error())
        return;

    // Read the first chunk of data from the local copy of the source LEL.
    bytesLeftInBuf = 0;
    originalReadLen = 0;
    blocksToAdvance = 0;
    this->readBlockOfLEL (myLEL, myBA, bytesLeftInBuf, originalReadLen,
        blocksToAdvance, err);
    if (err)
        return;
    offset = 0;
    myBAR = NSR::ByteArrayRef(myBA);                // reinit--myBA could have changed
    myBAR.offset(offset);
    myBAR.length(bytesLeftInBuf);

    // See if there is a header at the start of the LogExtentList--we need to skip past it if present.
    ExtendedAttrHeaderDesc  myHeaderDesc;
    myHeaderDesc.fromISO ((BYTE_PTR)myBAR, headerLocation);
    if ((!myHeaderDesc.error())  &&  (myHeaderDesc.type() == tag_EAHD)) {
        UINT32  headerLen = myHeaderDesc.descLen();
        bytesLeftInBuf -= headerLen;
        offset += headerLen;
    }

    for (;;) {
        // Use the EA Inspector to get the basic information we need to operate on this EA.
        // If it says there is not enough data in the ByteArray, read another block and try
        // it again.
        myBAR.offset(offset);
        myBAR.length(bytesLeftInBuf);
        myEAInspector.fromISO ((BYTE_PTR)myBAR, bytesLeftInBuf);
        if (err = myEAInspector.error()) {
            if (myEAInspector.error() != error_EA_Invalid_EA_length) {
                break;
            }
            else {
                this->readBlockOfLEL (myLEL, myBA, bytesLeftInBuf, originalReadLen,
                    blocksToAdvance, err);
                if (err)
                    break;
                offset = 0;
                myBAR = NSR::ByteArrayRef(myBA);                // reinit--myBA could have changed
                myBAR.offset(offset);
                myBAR.length(bytesLeftInBuf);
                myEAInspector.fromISO ((BYTE_PTR)myBAR, bytesLeftInBuf);
                if (err = myEAInspector.error()) {
                    if (myEAInspector.error() == error_EA_Invalid_EA_length) {
                        // Valid end of data in the LEL--this is the only non-error way
                        // out of the loop.
                        err = error_None;
                    }
                    break;
                }
            } // end of else
        } // end of if
        myAttrLen = myEAInspector.attrLen();    
        myAttrType = myEAInspector.attrType();
        String myAttrID = myEAInspector.attrID();
        mySubtype = myEAInspector.attrSubtype();
        myFixedPlusVaryingLen = myEAInspector.fixedLen() + myEAInspector.varyingLen();
        regKey = EARegistry::find (myAttrType, mySubtype, myAttrID, err);

        // First check to see if this looks like a valid EA.
        if (regKey == ea_InvalidKey  || myAttrLen == 0) {
            // Garbage--don't know what else to do to protect ourselves, except get out.
            err = error_EA_Invalid_EA_in_src_LEL;
            break;
        }

        // Then check for EA types that we want to not let in.
        else if (regKey == ea_Hdr_Key  ||  regKey == ea_Impl_FreeSpace_Key) {
        }
        
        else if (((myFixedPlusVaryingLen+3) & 0xFFFFFFFCUL) < blockSize_) {
            // This is a short item.  First make sure that we have this much data left in the ByteArray.
            if (bytesLeftInBuf < myFixedPlusVaryingLen) {
                this->readBlockOfLEL (myLEL, myBA, bytesLeftInBuf, originalReadLen,
                    blocksToAdvance, err);
                if (err)
                    break;
                if (bytesLeftInBuf < myFixedPlusVaryingLen) {
                    err = error_EA_Invalid_EA_length;
                    break;
                }
                offset = 0;
                myBAR = NSR::ByteArrayRef(myBA);                // reinit--myBA could have changed
                myBAR.offset(offset);
                myBAR.length(bytesLeftInBuf);
            }
            // This should be a short item.  Insert one into the view.
            myBAR.length(myFixedPlusVaryingLen);
            ShortEAViewItem  seavi(myBAR);
            if (err = seavi.error())
                break;
            this->insert (seavi, err);
            if (err)
                break;
        }

        else {
            // This is a long item.  Make sure that its a multiple of the block size.
            // Use the attrLen for this, not the prefix plus data.
            if ((myAttrLen % blockSize_) != 0) {
                err = error_EA_Invalid_EA_length;
                break;
            }
            // Create one and insert it into the view. First create a temporary LEL.
            LogExtentList   tempLEL(logicalVolume_);    
            if (err = tempLEL.error())
                break;
            tempLEL.HeadOf(myLEL, myAttrLen, err);
            if (err)
                break;
            // OK. Now insert a LongEAViewItem into our view.
            LongEAViewItem  leavi(tempLEL);
            if (err = leavi.error())
                break;
            this->insert (leavi, err);
            if (err)
                break;
            // Fix everything so that we will immediately do the next read, starting just
            // after the end of this long item.
            blocksToAdvance = myAttrLen / blockSize_;
            bytesLeftInBuf = myAttrLen;
        }
        offset += myAttrLen;
        bytesLeftInBuf -= myAttrLen;
    } // for loop

    // Update the offsets of our header item.  If we reached this point with an error,
    // preserve it across this call.
    this->updateHeaderOffsets (tempErr);
    if (!err)
        err = tempErr;

    // Finally, deallocate the extents in our trash list (that aren't duplicated in the
    // caller's list).  If we reached this point with an error, preserve it across this call.
    this->emptyTrashList(err);
    if (!err)
        err = tempErr;

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("leaving ExternalEASpace::fromDisk  error:%d\n", (int)err);
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #elif defined DEBUG_EA__BASIC_DISK_SPACE_TRACE
    printf ("leaving ExternalEASpace::fromDisk  error:%d\n", (int)err);
  #endif
  
} // fromDisk


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::toDisk  --CJB
//  
//  Writes the data of this EA space to the caller's extent list.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The caller's extent list is updated to contain all the items of this
//  EA space.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_No_free_store
//  error_Null_pointer;
//  error_Empty_path_element
//  error_Write_EOM
//  error_Write_EOLM
//  error_Drive_write
//  error_SR_Insufficient_space
//  error_SR_Bitmap_corrupt
//  error_SR_Cannot_free_space
// 
// NOTES:
//  If nothing has been modified (ie., the dirty flag isn't set) we don't write
//  anything back. Notice we never reset the dirty flag, because the caller
//  could potentially keep references to the extent lists around for the life of
//  this object.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::toDisk (Error & err)
{
    EAViewItem *    topLevelItem;
    Error           tempErr;

    if (eaView_ == NULL  ||  spaceResource_ == NULL) {
        err = error_Required_object_not_present;
        return;
    }

    // If nothing has been modified, don't write anything back.
    // Notice we never reset this flag--the caller could potentially keep references
    // to the ByteArrays and/or LogExtentLists around for the life of this object.
    if (!dirty_) {
        err = error_None;
        return;
    }

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("entering ExternalEASpace::toDisk:\n");
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #endif

    // Before we begin, deallocate any extents in the caller's extent list that aren't
    // duplicated in this EA Space, then clear the caller's extent list.
    this->deallocateCallersExtentList(err);
    if (err)
        return;

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("in ExternalEASpace::toDisk, after deallocateCallersExtentList call:\n");
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #endif

    // Go through all items and set their padding to the minimum length.
    // This also guarantees that all data concerned with length is consistent in each item.
    eaView_->setPaddingToMin (blockSize_, err);
    if (err)
        return;

    // Update the information in the header item.  This must be done after the setPaddingToMin
    // call but before the toDisk call.
    this->updateHeaderOffsets (err);
    if (err)
        return;

    // Call toDisk for our view.  This will cause all its GroupOfShortEAViewItems to update
    // their ByteArrays and write them to the respective LogicalExtentLists.
    eaView_->toDisk(*spaceResource_, err);
    if (err)
        return;

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("in ExternalEASpace::toDisk, after toDisk call:\n");
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #endif

    // Now construct a temporary LogicalExtentList that is a concatonation of the LELs of
    // all the top level objects, in their order.
    LogExtentList   tempLEL(logicalVolume_);
    for (INT16 topLevelIndex=0; ; topLevelIndex++) {
        topLevelItem = eaView_->at(topLevelIndex, err);
        if (topLevelItem == NULL)
            break;
        tempLEL.concat(topLevelItem->ReadOnlyEALogExtentList(), err);
        if (err) {
            // Error. Put the disk space of all GOS items into our trash list.
            eaView_->removeAllMyExtents (True, trashLel_, err);
            return;
        }
    }

    // We successfuly constructed the temporary LogExtentList.  Assign (copy) it to the
    // caller's LogExtentList.
    callersLel_ = tempLEL;

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("in ExternalEASpace::toDisk, after assignment to callersLel_:\n");
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #endif

    // Put the disk space of any GroupOfShortEAViewItems int the trash list--it is only
    // needed until we copy its LEL to the caller's LEL.  If we reached this point with an
    // error, preserve it across this call.
    eaView_->removeAllMyExtents (True, trashLel_, err);
    if (!err)
        err = tempErr;

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("in ExternalEASpace::toDisk, after deallocating EADiskSpace:\n");
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #endif

    // Finally, deallocate the extents in our trash list (that aren't duplicated in the
    // caller's list).  If we reached this point with an error, preserve it across this call.
    this->emptyTrashList(err);
    if (!err)
        err = tempErr;

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("leaving ExternalEASpace::toDisk (after emptyTrashList call)  error:%d\n", (int)err);
    ExternalEASpace::printSpaceInfo (*this, callersLel_, True);
    printf("\n");
  #elif defined DEBUG_EA__BASIC_DISK_SPACE_TRACE
    printf ("leaving ExternalEASpace::toDisk  error:%d\n", (int)err);
  #endif

} // toDisk


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::length  --CJB
//  
//  Returns the total data length this EA space will have when it is written
//  to its LogExtentList.  It includes the length of the header.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the total data length of this EA space (including the header).
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::ExternalEASpace::length(void) const
{
    if (eaView_ == NULL)
        return 0;
    return eaView_->length();
} // length


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::numEntries  --CJB
//  
//  Returns the total number of items in this EA space, not including
//  the header.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the total number of entries in this EA space (not including
//  the header).
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::ExternalEASpace::numEntries(void) const
{
    EAViewItem *    topLevelItem;
    Error           err;
    UINT32          totalEntries = 0;

    if (eaView_ == NULL)
        return 0;

    for (INT16 topLevelIndex=0; ; topLevelIndex++) {
        topLevelItem = eaView_->at(topLevelIndex, err);
        if (topLevelItem == NULL)
            break;
        totalEntries += topLevelItem->numEntries ();
    }
    if (totalEntries < 1)
        return 0;
    return totalEntries - 1;        // adjust for header--we pretend those don't exist here
} // numEntries


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::removeDeallocateAndDestroyAt  --CJB
//  
//  Removes the item at an virtual index in this EA space, destroys the item object, and
//  deallocates any associated disk space.
//
// ARGS:
//  INT16                   index           IN  virtual index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The specified item is removed from this EA space and deleted; all disk space
//  that it held is deallocated. The dirty flag is set.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
//  error_No_free_store
//  error_Null_pointer;
// 
// NOTES:
//  It is always safe to call this method. It is aware that some of the disk space
//  held by this item may also be held in the caller's extent list, and it acts
//  accordingly. 
//
//----------------------------------------------------------------------
// Notice that the disk space isn't actually deallocated immediately, but is put
// into the trash list and deallocated at a later time.  When the trash list is
// emptied, it is first checked to make sure that the extents are not held
// anywhere else.  If not done explicitly before that, the trash list is always
// emptied by the destructor.
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::removeDeallocateAndDestroyAt (INT16 index,
                                                    Error & err)
{
    EAViewItem      *item;

    item = this->privateRemoveAt(index+1, err);
    if (item == NULL)
        return;

    // Put the LogExtentList of this item into our trash list.
    item->removeAllMyExtents (trashLel_, err);

    delete item;
    
} // removeDeallocateAndDestroyAt


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::insert  --CJB
//  
//  Inserts a short EA view item into the external EA space.  Ordering is done
//  automatically,  according to the ISO and OSTA rules.
//
// ARGS:
//  const ShortEAViewItem&  seavi           IN  short EA view item to be inserted
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a ppointer to the item inserted in our lists (notice
//  this is a copy of the caller's item), or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists and all applicable member variables are updated to
//  reflect the insertion of the specified item. The dirty flag is set.
// 
// ERRORS:
//  error_EA_Invalid_EA_type
//  error_Bad_Parameter_EASpace
//  error_EA_Invalid_multiple_occurences_of_EA
//  error_EA_Invalid_EA_placement
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
//  error_No_free_store
// 
// NOTES:
//  (1) The caller's EA view item is copied--it is not put directly into our list.
//  The implication of this is that the caller is responsible for deleting
//  (his copy of) the item.
//  (2) This method, not having an insertion point parameter, enables this
//  layer to ensure that the proper ordering rules are followed.
//  It uses EAViewItem::CompareItem() to determine the insertion point.
//  (3) This method verifies that this attribute is allowed in the external EA space.
//  (It finds that out from the registry).  It also verifies that this attribute
//  is unique in the external EA space if it needs to be (it finds that out from
//  the registry). Notice that we cannot check for duplication of instances between
//  the embedded and external EA spaces.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::ExternalEASpace::insert (const NSR::ShortEAViewItem & seavi,
                              Error & err)
{
    const EARegistryEntry * myRegEntry;
    SAIEAInspector          myEAInspector;
    UINT32                  myAttrType;
    UINT8                   mySubtype;
    UINT32                  regKey;

    // Get a ByteArrayRef to the item's data.
    ByteArrayRef myBAR((ByteArray&)seavi.ReadOnlyEAByteArray());

    // Get the regKey from data obtained from the EA Inspector.
    myEAInspector.fromISO ((BYTE_PTR)myBAR, myBAR.length());
    if (err = myEAInspector.error())
        return NULL;
    myAttrType = myEAInspector.attrType();
    mySubtype = myEAInspector.attrSubtype();
    String myattrID = myEAInspector.attrID();
    regKey = EARegistry::find (myAttrType, mySubtype, myattrID, err);

    // Check for invalid EA types, or types that we don't want to let in.
    if (regKey == ea_InvalidKey  ||
        regKey == ea_Hdr_Key  ||
        regKey == ea_Impl_FreeSpace_Key) {
        err = error_EA_Invalid_EA_type;
        return NULL;
    }

    // Get the registry entry for the caller's regKey.
    myRegEntry = EARegistry::at (regKey, err);
    if (myRegEntry == NULL) {
        err = error_Bad_Parameter_EASpace;
        return NULL;
    }

    // If only one instance of this type of EA is allowed, check for that.
    if (myRegEntry->eaMultInstAllwd() == False) {
        INT16               vIndex = -1;
        findNextMatch(regKey, &vIndex, err);
        if (err == error_None) {
            err = error_EA_Invalid_multiple_occurences_of_EA;
            return NULL;
        }
    }

    // If this type of EA must be in the external space, we can't allow it here.
    if (myRegEntry->eaPlacement() == ea_MustBeInExternalSpace) {
        err = error_EA_Invalid_EA_placement;
        return NULL;
    }

    // All checks have been passed.  Go do the actual insert.
    return this->privateInsert (seavi, regKey, err);

} // insert


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::insert  --CJB
//  
//  Inserts a long EA view item into the external EA space.  Ordering is done
//  automatically,  according to the ISO and OSTA rules.
//
// ARGS:
//  const LongEAViewItem&   leavi           IN  long EA view item to be inserted
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a ppointer to the item inserted in our lists (notice
//  this is a copy of the caller's item), or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists and all applicable member variables are updated to
//  reflect the insertion of the specified item. The dirty flag is set.
// 
// ERRORS:
//  error_EA_Invalid_EA_type
//  error_Bad_Parameter_EASpace
//  error_EA_Invalid_multiple_occurences_of_EA
//  error_EA_Invalid_EA_placement
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
// 
// NOTES:
//  (1) The caller's EA view item is copied--it is not put directly into our list.
//  The implication of this is that the caller is responsible for deleting
//  (his copy of) the item.
//  (2) This method, not having an insertion point parameter, enables this
//  layer to ensure that the proper ordering rules are followed.
//  It uses EAViewItem::CompareItem() to determine the insertion point.
//  (3) This method verifies that this attribute is allowed in the external EA space.
//  (It finds that out from the registry).  It also verifies that this attribute
//  is unique in the external EA space if it needs to be (it finds that out from
//  the registry). Notice that we cannot check for duplication of instances between
//  the embedded and external EA spaces.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::ExternalEASpace::insert (const LongEAViewItem & leavi,
                              Error & err)
{
    const EARegistryEntry * myRegEntry;
    SAIEAInspector          myEAInspector;
    UINT32                  myAttrLen;
    UINT32                  myAttrType;
    UINT8                   mySubtype;
    UINT32                  regKey;

    // Get a ByteArrayRef to the first block of the item's data.
    ByteArray myBA(blockSize_);
    ByteArrayRef myBAR(myBA);
    LogExtentList&  myLEL = (LogExtentList&)leavi.ReadOnlyEALogExtentList();
    myLEL.read(myBAR, err);
    if (err == error_Read_EOLM)
        err = error_None;
    if (err)
        return NULL;

    // Get the regKey from data obtained from the EA Inspector.
    myEAInspector.fromISO ((BYTE_PTR)myBAR, myBAR.length());
    if (err = myEAInspector.error())
        return NULL;
    myAttrType = myEAInspector.attrType();
    mySubtype = myEAInspector.attrSubtype();
    String myattrID = myEAInspector.attrID();
    regKey = EARegistry::find (myAttrType, mySubtype, myattrID, err);

    // Check for invalid EA types, or types that we don't want to let in.
    if (regKey == ea_InvalidKey  ||
        regKey == ea_Hdr_Key  ||
        regKey == ea_Impl_FreeSpace_Key) {
        err = error_EA_Invalid_EA_type;
        return NULL;
    }

    // Check that the attrLen from the EA Inspector matches the length of the extent list.
    myAttrLen = myEAInspector.attrLen();
    if (myAttrLen != myLEL.infoLen()) {
        err = error_EA_Invalid_EA_length;
        return NULL;
    }

    // Get the registry entry for the caller's regKey.
    myRegEntry = EARegistry::at (regKey, err);
    if (myRegEntry == NULL) {
        err = error_Bad_Parameter_EASpace;
        return NULL;
    }

    // If only one instance of this type of EA is allowed, check for that.
    if (myRegEntry->eaMultInstAllwd() == False) {
        INT16               vIndex = -1;
        findNextMatch(regKey, &vIndex, err);
        if (err == error_None) {
            err = error_EA_Invalid_multiple_occurences_of_EA;
            return NULL;
        }
    }

    // If this type of EA must be in the embedded space, we can't allow it here.
    if (myRegEntry->eaPlacement() == ea_MustBeInEmbeddedSpace) {
        err = error_EA_Invalid_EA_placement;
        return NULL;
    }

    // All checks have been passed.  Go do the actual insert.
    return this->privateInsert (leavi, regKey, err);
    
} // insert


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::findFirstPadItemOffset  --CJB
//  
//  Returns the offset, relative to this EA space, to the first point where
//  an EA free space item will be added during the toDisk() call.
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  UINT32 -- returns the offset, relative to this entire EA space, where
//  the data of the first EA free space item will be put by the toDisk() call.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
// 
// NOTES:
//  Notice that no padding is carried in this EA space or its subview.
//  (The attribute length of each item is always rounded up to a four byte
//  boundary; that's it.) Padding is only added to the data written out by the
//  toDisk() method--even there it is not put into this item or its subview.
//  If you need to know where and how much padding will be added by toDisk(),
//  see the GroupOfShortsEAViewItem::determinePaddingNeeded() method.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::UINT32
NSR::ExternalEASpace::findFirstPadItemOffset(Error & err) const
{
    EAViewItem *    topLevelItem;
    UINT32          padItemOffset = 0;

    if (eaView_ == NULL) {
        err = error_None;
        return 0;
    }

    for (INT16 topLevelIndex=0; ; topLevelIndex++) {
        topLevelItem = eaView_->at(topLevelIndex, err);
        if (topLevelItem == NULL)
            break;
        padItemOffset = topLevelItem->findPadItemOffset (err);
        if (padItemOffset > 0)
            return padItemOffset + topLevelItem->offset();
    }

    err = error_None;
    return 0;
} // findFirstPadItemOffset


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::emptyTrashList  --CJB
//  
//  Actually deallocates the disk space that is in the "trash list".
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The trash list is emptied.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_No_free_store
//  error_Null_pointer;
//  error_SR_Cannot_free_space
// 
// NOTES:
//  Notice that when we said in other methods that we were deallocating disk space,
//  we lied. All "deallocations" except this one merely put their extents into the
//  trash list, to be actually deallocated by this method at a later time. Before
//  deallocating the trash list, it is first checked to make sure that the extents
//  are not held anywhere else.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::emptyTrashList(Error & err)
{
    // First discard every extent from the trash list that is also in the caller's extent list.
    LogExtentList   tempLEL (logicalVolume_);

    ExternalEASpace::moveExtentsFromSourceToTarget(trashLel_, callersLel_, tempLEL, err);
    if (err)
        return;

    // Next discard every extent from the trash list that is also in any list of our EAView.
    tempLEL.clear();
    if (eaView_ != NULL) {
        eaView_->removeCallersDuplicateExtents (trashLel_, tempLEL, err);
        if (err)
            return;
    }

    // Now deallocate all remaining extents from the trash list, if there are any.
    if (trashLel_.blockMultLen() > 0) {
      #if defined DEBUG_EA__TRACK_DISK_ALLOCATION
        ExternalEASpace::markExtentsFreeForTesting (trashLel_);
      #endif
        spaceResource_->putLogExtentList(trashLel_, err);
    }

    // Finally, clear the trashList.
    trashLel_.clear();

} // emptyTrashList


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::deallocateEADiskSpace  --CJB
//  
//  Puts all disk space held internally in this EA space, into the "trash list".
//
// ARGS:
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  All log extents in this EA space are moved into the trash list.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_No_free_store
//  error_Null_pointer;
// 
// NOTES:
// Notice that the disk space isn't actually deallocated immediately, but is put
// into the trash list and deallocated at a later time.  When the trash list is
// emptied, it is first checked to make sure that the extents are not held
// anywhere else.  If not done explicitly before that, the trash list is always
// emptied by the destructor.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::deallocateEADiskSpace(Error & err)
{
    if (eaView_ == NULL) {
        err = error_None;
        return;
    }
    // Move all extents of our EAView into our trash list.
    eaView_->removeAllMyExtents (False, trashLel_, err);
} // deallocateEADiskSpace


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::printPhysicalLists  --CJB
//  
//  Prints the various underlying physical lists in this EA space.
//
// ARGS:
//  INT16                   indent          IN  number of spaces to indent the printing
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This print method does not need to be included in the final product.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined ENABLE_PRINTING
void
NSR::ExternalEASpace::printPhysicalLists (INT16 indent) const
{
    Error           err;

    if (eaView_ == NULL)
        return;
    this->updateHeaderOffsets (err);
    printf ("\n");
    printf ("ExternalEASpace   (obj addr:%lu)   Physical lists:\n", (UINT32)this);
    eaView_->printList(INDENT_AMOUNT+indent);

} // printPhysicalList
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::moveExtentsFromSourceToTarget  --CJB
//  
//  Overloaded method to move extents from a specified source extent list to a
//  specified target extent list. This method unconditionally moves all extents.
//
// ARGS:
//  LogExtentList&          sourceList      IN  extent list to move extents from
//  LogExtentList&          targetList      OUT extent list to move extents to
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  All log extents in the source list are removed and appended to the target list.
// 
// ERRORS:
//  error_No_free_store
//  error_Null_pointer;
// 
// NOTES:
//  (1) This is a static method--it is called without a "this" pointer.
//  (2) Extents that are already in the target list will not be inserted again, but
//  they will still be removed from the target list.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::moveExtentsFromSourceToTarget(LogExtentList & sourceList,
                                                    LogExtentList & targetList,
                                                    Error & err)
{
// Method to remove all extents from sourceList, adding them to targetList.
    UINT32                  j, k;
    UINT32                  sourceListNumExtents;
    UINT32                  sourceListStartBlock;
    UINT32                  sourceListNumBlocks;
    const NSR::LogExtent    *sourceListExtent;
    NSR::Partition          *sourceListPartitionp;
    UINT32                  targetListNumExtents;
    const NSR::LogExtent    *targetListExtent;
#if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    NSR::Boolean            firstBlock = True;
#endif

    if (sourceList.blockMultLen() == 0) {
        // If sourceList list is empty, there is nothing to do.
        err = error_None;
        return;
    }

    sourceListNumExtents = sourceList.numLogExtents();
    // Get an extent of the sourceList.
    for (j=0; j<sourceListNumExtents; j++) {
        sourceListExtent = sourceList.at(j, err);
        if (sourceListExtent == NULL)
            break;
        sourceListStartBlock = sourceListExtent->startBlock();
        sourceListNumBlocks = sourceListExtent->numBlocks();
        sourceListPartitionp = sourceListExtent->partitionp();
        if (sourceListNumBlocks == 0)
            continue;
        // At this point we have an entry of the sourceList.  If it is in the targetList,
        // we won't insert it again.
        targetListNumExtents = targetList.numLogExtents();
        for (k=0; k<targetListNumExtents; k++) {
            targetListExtent = targetList.at(k, err);
            if (targetListExtent == NULL)
                break;
            if (sourceListStartBlock != targetListExtent->startBlock())
                continue;
            if (sourceListNumBlocks != targetListExtent->numBlocks())
                continue;
            if (sourceListPartitionp != targetListExtent->partitionp())
                continue;
            // I know they tell you in CS school to avoid gotos, but this is the most
            // elegant construction to jump to an arbitrary point from within a loop.
            goto skip_append;
        } // inner loop
        targetList.append (*sourceListExtent, err);
        if (err)
            return;
    skip_append:
            ;       // null statement to satisfy some compilers
  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
        if (firstBlock)
            printf ("ExternalEASpace::moveExtentsFromSourceToTarget--removing extent from sourceList: ");
        else printf (", ");
        firstBlock = False;
        if (sourceListNumBlocks == 1)
            printf ("%lu", sourceListStartBlock);
        else printf ("%lu-%lu", sourceListStartBlock, sourceListStartBlock+sourceListNumBlocks-1);
  #endif
    } // outer loop

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("\n");
  #endif

    // Now clear the sourceList and return.
    sourceList.clear();
    err = error_None;

} // moveExtentsFromSourceToTarget


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::moveExtentsFromSourceToTarget  --CJB
//  
//  Overloaded method to move extents from a specified source extent list to a
//  specified target extent list. This method moves only those extents that are
//  also in a third "compare list".
//
// ARGS:
//  LogExtentList&          sourceList      IN/OUT extent list to move extents from
//  const LogExtentList&    compareList     IN  extent list to check for duplicates
//  LogExtentList&          targetList      OUT extent list to move extents to
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  All log extents in the source list, that are also in the compare list,
//  are removed and appended to the target list.
// 
// ERRORS:
//  error_No_free_store
//  error_Null_pointer;
// 
// NOTES:
//  (1) This is a static method--it is called without a "this" pointer.
//  (2) Extents that are already in the target list will not be inserted again, but
//  they will still be removed from the target list.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::moveExtentsFromSourceToTarget(LogExtentList & sourceList,
                                                    const LogExtentList & compareList,
                                                    LogExtentList & targetList,
                                                    Error & err)
{
    UINT32                  i, j, k;
    UINT32                  sourceListNumExtents;
    const NSR::LogExtent    *sourceListExtent;
    UINT32                  compareListNumExtents;
    UINT32                  compareListStartBlock;
    UINT32                  compareListNumBlocks;
    const NSR::LogExtent    *compareListExtent;
    NSR::Partition          *compareListPartitionp;
    UINT32                  targetListNumExtents;
    const NSR::LogExtent    *targetListExtent;
#if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    NSR::Boolean            firstBlock = True;
#endif

    if (sourceList.blockMultLen() == 0  ||  compareList.blockMultLen() == 0) {
        // If either list is empty, there is nothing to do.
        err = error_None;
        return;
    }

    if (&sourceList == &compareList) {
        // The caller specified the same list for sourceList and compareList.  This signifies
        // that all extents of sourceList should be moved to targetList, checking for
        // duplicates in targetList of course.
        ExternalEASpace::moveExtentsFromSourceToTarget(sourceList, targetList, err);
        return;
    }
    
    // Nested loops to remove all extents from sourceList that are also in compareList, but
    // not in targetList.
    compareListNumExtents = compareList.numLogExtents();
    for (i=0; i<compareListNumExtents; i++) {
        compareListExtent = compareList.at(i, err);
        if (compareListExtent == NULL)
            break;
        compareListStartBlock = compareListExtent->startBlock();
        compareListNumBlocks = compareListExtent->numBlocks();
        compareListPartitionp = compareListExtent->partitionp();
        if (compareListNumBlocks == 0)
            continue;
        sourceListNumExtents = sourceList.numLogExtents();
        // We got an extent of the compareList.  See if it is also in the sourceList.
        for (j=0; j<sourceListNumExtents; j++) {
            sourceListExtent = sourceList.at(j, err);
            if (sourceListExtent == NULL)
                break;
            if (compareListStartBlock != sourceListExtent->startBlock())
                continue;
            if (compareListNumBlocks != sourceListExtent->numBlocks())
                continue;
            if (compareListPartitionp != sourceListExtent->partitionp())
                continue;
            // At this point we have an entry that is duplicated in the compareList and
            // the sourceList.  If it is in the targetList, we won't insert it again but
            // we will still remove it from the sourceList.
            targetListNumExtents = targetList.numLogExtents();
            for (k=0; k<targetListNumExtents; k++) {
                targetListExtent = targetList.at(k, err);
                if (targetListExtent == NULL)
                    break;
                if (compareListStartBlock != targetListExtent->startBlock())
                    continue;
                if (compareListNumBlocks != targetListExtent->numBlocks())
                    continue;
                if (compareListPartitionp != targetListExtent->partitionp())
                    continue;
                // I know they tell you in CS school to avoid gotos, but this is the most
                // elegant construction to jump to an arbitrary point from within a loop.
                goto skip_append;
            } // inner loop
            targetList.append (*sourceListExtent, err);
            if (err)
                return;
        skip_append:
            sourceList.remove(j, err); 
            if (err)
                return;
            j--;                            // adjust for remove
            sourceListNumExtents--;         // adjust for remove
  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
            if (firstBlock)
                printf ("ExternalEASpace::moveExtentsFromSourceToTarget--removing extent from sourceList: ");
            else printf (", ");
            firstBlock = False;
            if (compareListNumBlocks == 1)
                printf ("%lu", compareListStartBlock);
            else printf ("%lu-%lu", compareListStartBlock, compareListStartBlock+compareListNumBlocks-1);
  #endif
        } // middle loop
    } // outer loop

  #if defined DEBUG_EA__VERBOSE_DISK_SPACE_TRACE
    printf ("\n");
  #endif

    err = error_None;

} // moveExtentsFromSourceToTarget


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::countDiskSpace  --CJB
//  
//  Method to return the total number of disk sectors held internally by this EA space.
//
// ARGS:
//  UINT32                  blockSize       IN  the size of a block on the media
//
// RETURNS:
//  UINT32 -- returns the number of blocks held internally by this EA space.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  The returned count does not include any sectors in the trash list that
//  are not also in active use in this EA space.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
NSR::UINT32
NSR::ExternalEASpace::countDiskSpace(UINT32 blockSize) const
{
    if (eaView_ == NULL)
        return 0;
    return eaView_->countDiskSpace(blockSize);
} // countDiskSpace
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::printAllocatedBlocks  --CJB
//  
//  Method to print a list of all disk sectors held internally by this EA space.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This method does not print the trash list--ie., blocks in the trash list will
//  not be printed unless they are also in active use in this EA space.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::ExternalEASpace::printAllocatedBlocks(void) const
{
    if (eaView_ == NULL)
        return;
    eaView_->printAllocatedBlocks();
} // printAllocatedBlocks
#endif



// ---------------start of private methods of the ExternalEASpace class----------------------



//----------------------------------------------------------------------
// METHOD: ExternalEASpace::findInsertionPoint  --CJB
//  
//  Returns the internal physical indices needed to insert an item into this EA space.
//
// ARGS:
//  const EAViewItem*       myItem                  IN  item to determine insertion indices for
//  INT16&                  topLevelIndex           OUT physical index into the top level long EA view
//  INT16&                  secondLevelIndex        OUT physical index into the second level GOS EA view
//  INT16&                  topLevelIndexOfPrev     OUT topLevelIndex of previous item
//  INT16&                  secondLevelIndexOfPrev  OUT secondLevelIndex of previous item
//  Error&                  err                     OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
// 
// NOTES:
//  If the item is to be inserted at the top level (ie., it is a long EA view
//  item), the returned secondLevelIndex will be "InvalidIndex".  The same is
//  true of the returned secondLevelIndexOfPrev.  In fact, if there is no
//  previous item, both topLevelIndexOfPrev and secondLevelIndexOfPrev will
//  be "InvalidIndex".  In case of errors, all returned indices will be
//  "InvalidIndex".
//  IMPORTANT: The top level and second level indexes that are returned are,
//  of course, physical indexes to the respective subviews.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::findInsertionPoint(const EAViewItem * myItem,
                                         INT16& topLevelIndex,
                                         INT16& secondLevelIndex,
                                         INT16& topLevelIndexOfPrev,
                                         INT16& secondLevelIndexOfPrev,
                                         Error & err) const
{
    const EAViewItem *  topLevelItem;
    const EAViewItem *  secondLevelItem;
    Error               tempErr;

    topLevelIndexOfPrev = InvalidIndex;
    secondLevelIndexOfPrev = InvalidIndex;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        topLevelIndex = InvalidIndex;
        secondLevelIndex = InvalidIndex;
        return;
    }

    err = error_None;
    for (topLevelIndex=0; ; topLevelIndex++) {
        topLevelItem = eaView_->at(topLevelIndex, tempErr);
        if (topLevelItem == NULL)
            break;
        if (topLevelItem->isA() == GroupOfShortsEAViewItemType) {
            // This is a GroupOfShortsEAViewItem.
            for (secondLevelIndex=0; ; secondLevelIndex++) {
                secondLevelItem = topLevelItem->at(secondLevelIndex, tempErr);
                if (secondLevelItem == NULL)
                    break;
                if (secondLevelItem->compareForInsertion(myItem) > 0)
                    return;
                topLevelIndexOfPrev = topLevelIndex;
                secondLevelIndexOfPrev = secondLevelIndex;
            } // end of inner for loop
        } // end of if
        else {
            // This is a simple EAViewItem.
            secondLevelIndex = InvalidIndex;
            if (topLevelItem->compareForInsertion(myItem) > 0)
                return;
            topLevelIndexOfPrev = topLevelIndex;
            secondLevelIndexOfPrev = secondLevelIndex;
        } // end of if/else
    } // end of outer for loop

    secondLevelIndex = InvalidIndex;

}  // findInsertionPoint


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::privateAt  --CJB
//  
//  Returns a const pointer to an EAViewItem by physical index of the EASpace.
//
// ARGS:
//  INT16                   index           IN  physical index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a pointer to the specified EA view item, or NULL
//  if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  IMPORTANT: This method expects a physical index.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// Returns the item that corresponds to the specified physical index.
const NSR::EAViewItem *
NSR::ExternalEASpace::privateAt(INT16 index,
                                Error & err) const
{
    const EAViewItem *  topLevelItem;
    const EAViewItem *  secondLevelItem;
    INT16               myTopLevelIndex;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }

    for (myTopLevelIndex=0; index>=0; myTopLevelIndex++) {
        topLevelItem = eaView_->at(myTopLevelIndex, err);
        if (topLevelItem == NULL)
            break;
        // If index is within range for this topLevelItem, this "at" call will succeed.
        secondLevelItem = topLevelItem->at(index, err);
        if (secondLevelItem != NULL) {
            err = error_None;
            return secondLevelItem;
        }       
        index -= topLevelItem->numEntries();
    } // for loop

    err = error_Index_out_of_bounds;
    return NULL;
} // privateAt


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::privateAt  --CJB
//  
//  Returns a non-const pointer to an EAViewItem by physical index of the EASpace.
//
// ARGS:
//  INT16                   index           IN  physical index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the specified EA view item, or NULL
//  if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  IMPORTANT: This method expects a physical index.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::ExternalEASpace::privateAt(INT16 index,
                                Error & err)
{
    EAViewItem *        topLevelItem;
    EAViewItem *        secondLevelItem;
    INT16               myTopLevelIndex;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }

    for (myTopLevelIndex=0; index>=0; myTopLevelIndex++) {
        topLevelItem = eaView_->at(myTopLevelIndex, err);
        if (topLevelItem == NULL)
            break;
        // If index is within range for this topLevelItem, this "at" call will succeed.
        secondLevelItem = topLevelItem->at(index, err);
        if (secondLevelItem != NULL) {
            err = error_None;
            return secondLevelItem;
        }       
        index -= topLevelItem->numEntries();
    } // for loop

    err = error_Index_out_of_bounds;
    return NULL;
} // privateAt


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::privateAt  --CJB
//  
//  Returns a non-const pointer to an EAViewItem by physical index of the EASpace.
//  It also returns the top level and second level physical indices of the
//  specified item.
//
// ARGS:
//  INT16                   index           IN  physical index into this EA space
//  INT16*                  topLevelIndex   OUT physical index into the top level long EA view
//  INT16*                  secondLevelIndex OUT physical index into the second level GOS EA view
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the specified EA view item, or NULL
//  if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
// 
// NOTES:
//  If the specified item is at the top level (ie., it is a long EA view
//  item), the returned secondLevelIndex will be "InvalidIndex".  In case of
//  errors, both returned indices will be "InvalidIndex".
//  IMPORTANT: This method expects a physical index.  The top level and second
//  level indexes that are returned are, of course, physical indexes to the
//  respective subviews.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
NSR::EAViewItem *
NSR::ExternalEASpace::privateAt(INT16 index,
                                INT16 * topLevelIndex,
                                INT16 * secondLevelIndex,
                                Error & err)
{
    EAViewItem *    topLevelItem;
    EAViewItem *    secondLevelItem;

    if (eaView_ == NULL) {
        *topLevelIndex = InvalidIndex;
        *secondLevelIndex = InvalidIndex;
        err = error_Required_object_not_present;
        return NULL;
    }

    for (*topLevelIndex=0; index>=0; (*topLevelIndex)++) {
        topLevelItem = eaView_->at(*topLevelIndex, err);
        if (topLevelItem == NULL)
            break;
        // If index is within range for this topLevelItem, this "at" call will succeed.
        secondLevelItem = topLevelItem->at(index, err);
        if (secondLevelItem != NULL) {
            if (topLevelItem->isA() == GroupOfShortsEAViewItemType)
                *secondLevelIndex = index;
            else *secondLevelIndex = InvalidIndex;
            err = error_None;
            return secondLevelItem;
        }       
        index -= topLevelItem->numEntries();
    } // for loop

    *topLevelIndex = InvalidIndex;
    *secondLevelIndex = InvalidIndex;
    err = error_Index_out_of_bounds;
    return NULL;
} // privateAt


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::privateRemoveAt  --CJB
//  
//  Removes an EAViewItem by physical index of the EASpace, and returns a
//  pointer to the removed item.
//
// ARGS:
//  INT16                   index           IN  physical index into this EA space
//  Error&                  err             OUT error status
//
// RETURNS:
//  EAViewItem* -- returns a pointer to the specified EA view item and
//  removes it from this EA view. Returns NULL if there is any problem.
//  The dirty flag is set.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The specified EA view item is removed from this EA space.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_Index_out_of_bounds
//  error_No_free_store
//  error_Null_pointer;
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
// 
// NOTES:
//  This method does not delete the returned object--It is the caller's
//  responsibility to do that.
//  IMPORTANT: This method expects a physical index.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// This method removes the EAViewItem.
NSR::EAViewItem *
NSR::ExternalEASpace::privateRemoveAt(INT16 index,
                                      Error & err)
{
    INT16               topLevelIndex;
    INT16               secondLevelIndex;
    EAViewItem *        topLevelViewItem;
    EAViewItem *        thisItem;
    EAViewItem *        prevTopLevelItem;
    EAViewItem *        nextTopLevelItem;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }

    thisItem = privateAt(index, &topLevelIndex, &secondLevelIndex, err);
    if (err)
        return NULL;

    // We are committed to the remove.  Mark this EA space dirty, so it will get written
    // back on the toDisk call.
    dirty_ = True;
        
    switch (thisItem->isA()) {
    case LongEAViewItemType:
        eaView_->removeAt(topLevelIndex, err);
        if (err)
            return NULL;
        // If there are GroupOfShortsEAViewItems on both sides, combine the two items;
        prevTopLevelItem = eaView_->at(topLevelIndex-1, err);
        nextTopLevelItem = eaView_->at(topLevelIndex, err);
        if (prevTopLevelItem != NULL) {
            if (nextTopLevelItem != NULL) {
                prevTopLevelItem->combineWithNextItem (nextTopLevelItem, err);
                if (err)
                    return NULL;
                // If the next item was a GroupOfShortsEAViewItems, it will be empty now.
                // if so, put its disk space into our trash list, and remove and destroy it.
                if (nextTopLevelItem->numEntries() == 0) {
                    // First put its disk space into our trash list.
                    nextTopLevelItem->removeAllMyExtents (trashLel_, err);
                    if (err)
                        return NULL;
                    eaView_->removeAndDestroyAt(topLevelIndex, err);
                    if (err)
                        return NULL;
                    break;
                }
            }
        }
        break;
    case ShortEAViewItemType:
        // This ShortEAViewItem is in a GroupOfShortsEAViewItem, of course.
        topLevelViewItem = eaView_->at(topLevelIndex, err);
        if (topLevelViewItem == NULL)               // Huh? shouldn't happen
            return NULL;
        topLevelViewItem->removeAt(secondLevelIndex, err);
        if (err)
            return NULL;
        if (topLevelViewItem->numEntries() == 0) {
            // There was just one entry in this GroupOfShortsEAViewItem.  Remove and destroy
            // the GroupOfShortsEAViewItem.  First put its disk space into our trash list.
            topLevelViewItem->removeAllMyExtents (trashLel_, err);
            if (err)
                return NULL;
            eaView_->removeAndDestroyAt(topLevelIndex, err);        
            if (err)
                return NULL;
        }
        break;
    default:
        err = error_EA_Invalid_EA_type;
        return NULL;
    }  // end of switch

    err = error_None;
    return thisItem;

} // privateRemoveAt


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::privateInsert  --CJB
//  
//  Does the actual work of inserting a short EA view item into the external EA space.
//
// ARGS:
//  const ShortEAViewItem&  seavi           IN  short EA view item to be inserted
//  UINT32                  regKey          IN  regKey value for the item to be inserted
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a pointer to the item inserted in our lists (notice
//  this item is a copy of the caller's item), or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists and all applicable member variables are updated to
//  reflect the insertion of the specified item. The dirty flag is set.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
// 
// NOTES:
//  (1) The caller's EA view item is copied--it is not put directly into our list.
//  The implication of this is that the caller is responsible for deleting
//  his original item.
//  (2) All checking must be done before this private method is called.
//  (3) This method, not having an insertion point parameter, enables this
//  layer to ensure that the proper ordering rules are followed.
//  It uses EAViewItem::CompareItem() to determine the insertion point.
//  (4) This method verifies that this attribute is allowed in the external EA space.
//  (It finds that out from the registry).  It also verifies that this attribute
//  is unique in the external EA space if it needs to be (it finds that out from
//  the registry). Notice that we cannot check for duplication of instances between
//  the embedded and external EA spaces.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
const NSR::EAViewItem *
NSR::ExternalEASpace::privateInsert (const ShortEAViewItem & seavi,
                                     UINT32 regKey,
                                     Error & err)
{
    INT16                   topLevelIndex;
    INT16                   secondLevelIndex;
    INT16                   topLevelIndexOfPrev;
    INT16                   secondLevelIndexOfPrev;
    EAViewItem              *topLevelViewItem;
    LongEAViewItem          *newGOSEAViewItem;
    ShortEAViewItem         *mySeavi;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }

    // Sure would like to get rid of this switch.
    switch (seavi.isA()) {
    case EAViewHeaderItemType:
        mySeavi = new EAViewHeaderItem((EAViewHeaderItem&)seavi);
        break;
    case ShortEAViewItemType:
        mySeavi = new ShortEAViewItem(seavi);
        break;
    default:
        err = error_EA_Invalid_EA_type;
        return NULL;
    } // switch
    if (mySeavi == NULL) {
        err = error_No_free_store;
        return NULL;
    }
    if (err = mySeavi->error()) {
        delete mySeavi;
        return NULL;
    }

    // Set the regkey in our new item.
    mySeavi->regKey (regKey);

    // Set up the padding for this item so it just extends to the next four byte boundary past
    // the the data.  This also guarantees that all data concerned with length is consistent.
    mySeavi->setPaddingToMin (blockSize_, err);
    if (err) {
        delete mySeavi;
        return NULL;
    }

    findInsertionPoint(mySeavi, topLevelIndex, secondLevelIndex,
        topLevelIndexOfPrev, secondLevelIndexOfPrev, err);
    if (err) {
        delete mySeavi;
        return NULL;
    }

    // We are committed to the insert.  Mark this EA space dirty, so it will get written
    // back on the toDisk call.
    dirty_ = True;

    if (topLevelIndex == topLevelIndexOfPrev  && topLevelIndex != InvalidIndex) {
        // The insertion is within a GroupOfShortsEAViewItem.  This is easy.
        topLevelViewItem = eaView_->at(topLevelIndex, err);
        if (topLevelViewItem == NULL) {     // shouldn't happen, but just in case
            delete mySeavi;
            return NULL;
        }
        topLevelViewItem->insertAt(mySeavi, secondLevelIndex, err);
        if (err) {
            delete mySeavi;
            return NULL;
        }
        return mySeavi;
    }

    // The insertion is not within a GroupOfShortsEAViewItem.  If an item on
    // either side of the insertion point is a GroupOfShortsEAViewItem, this is easy.
    if (topLevelIndex != InvalidIndex) {
        topLevelViewItem = eaView_->at(topLevelIndex, err);
        if (topLevelViewItem != NULL) {     // ie., insert isn't at end of list
            if (topLevelViewItem->isA() == GroupOfShortsEAViewItemType) {
                // This is a GroupOfShortsEAViewItem--do it.
                topLevelViewItem->insertAt(mySeavi, secondLevelIndex, err);
                if (err) {
                    delete mySeavi;
                    return NULL;
                }
                return mySeavi;
            }
        }
    }
    if (topLevelIndexOfPrev != InvalidIndex) {
        topLevelViewItem = eaView_->at(topLevelIndexOfPrev, err);
        if (topLevelViewItem != NULL) {     // shouldn't happen, but just in case
            if (topLevelViewItem->isA() == GroupOfShortsEAViewItemType) {
                // This is a GroupOfShortsEAViewItem--do it.
                topLevelViewItem->insertAt(mySeavi, secondLevelIndexOfPrev+1, err);
                if (err) {
                    delete mySeavi;
                    return NULL;
                }
                return mySeavi;
            }
        }
    }

    // Neither one is a GroupOfShortsEAViewItem.  Create a new GroupOfShortsEAViewItem,
    // insert our new item, then insert the GroupOfShortsEAViewItem into the top level view.
    newGOSEAViewItem = new GroupOfShortsEAViewItem(blockSize_);
    if (newGOSEAViewItem == NULL) {
        err = error_No_free_store;
        delete mySeavi;
        return NULL;
    }
    if (err = newGOSEAViewItem->error()) {
        delete newGOSEAViewItem;
        delete mySeavi;
        return NULL;
    }
    newGOSEAViewItem->insertAt(mySeavi, 0, err);
    if (err) {
        delete newGOSEAViewItem;
        delete mySeavi;
        return NULL;
    }
    eaView_->insertAt(newGOSEAViewItem, topLevelIndex, err);
    if (err) {
        delete newGOSEAViewItem;
        delete mySeavi;
        return NULL;
    }
    return mySeavi;

} // privateInsert


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::privateInsert  --CJB
//  
//  Does the actual work of inserting a long EA view item into the external EA space.
//
// ARGS:
//  const LongEAViewItem&   leavi           IN  long EA view item to be inserted
//  UINT32                  regKey          IN  regKey value for the item to be inserted
//  Error&                  err             OUT error status
//
// RETURNS:
//  const EAViewItem* -- returns a pointer to the item inserted in our lists (notice
//  this item is a copy of the caller's item), or NULL if there is a problem.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Our internal lists and all applicable member variables are updated to
//  reflect the insertion of the specified item. The dirty flag is set.
// 
// ERRORS:
//  error_Required_object_not_present
//  error_EA_Invalid_EA_length
//  error_EA_Invalid_EA_type
//  error_No_free_store
//  error_Null_pointer
//  error_REGISTRY_Entry_not_found  (if regKey is invalid)
//  error_Index_out_of_bounds
// 
// NOTES:
//  (1) The caller's EA view item is copied--it is not put directly into our list.
//  The implication of this is that the caller is responsible for deleting
//  his original item.
//  (2) All checking must be done before this private method is called.
//  (3) This method, not having an insertion point parameter, enables this
//  layer to ensure that the proper ordering rules are followed.
//  It uses EAViewItem::CompareItem() to determine the insertion point.
//  (4) This method verifies that this attribute is allowed in the external EA space.
//  (It finds that out from the registry).  It also verifies that this attribute
//  is unique in the external EA space if it needs to be (it finds that out from
//  the registry). Notice that we cannot check for duplication of instances between
//  the embedded and external EA spaces.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// Method to do the actual work of insertion.  All checking must be done before this method is called.
const NSR::EAViewItem *
NSR::ExternalEASpace::privateInsert (const NSR::LongEAViewItem & leavi,
                                     UINT32 regKey,
                                     Error & err)
{
    INT16                   topLevelIndex;
    INT16                   secondLevelIndex;
    INT16                   topLevelIndexOfPrev;
    INT16                   secondLevelIndexOfPrev;
    EAViewItem              *topLevelViewItem;
    LongEAViewItem          *newGOSEAViewItem;
    LongEAViewItem          *myLeavi;

    if (eaView_ == NULL) {
        err = error_Required_object_not_present;
        return NULL;
    }

    if (leavi.isA() != LongEAViewItemType) {
        err = error_EA_Invalid_EA_type;
        return NULL;
    }
    
    myLeavi = new LongEAViewItem(leavi);
    if (myLeavi == NULL) {
        err = error_No_free_store;
        return NULL;
    }
    if (err = myLeavi->error()) {
        delete myLeavi;
        return NULL;
    }

    // Set the regkey in our new item.
    myLeavi->regKey (regKey);

    findInsertionPoint(myLeavi, topLevelIndex, secondLevelIndex,
        topLevelIndexOfPrev, secondLevelIndexOfPrev, err);
    if (err) {
        delete myLeavi;
        return NULL;
    }

    // We are committed to the insert.  Mark this EA space dirty, so it will get written
    // back on the toDisk call.
    dirty_ = True;

    // If the insertion is within a GroupOfShortsEAViewItem (but not at either end of one),
    // split the item.
    topLevelViewItem = eaView_->at(topLevelIndex, err);
    if (topLevelViewItem != NULL) {
        newGOSEAViewItem = topLevelViewItem->splitItem (secondLevelIndex, err);
        if (newGOSEAViewItem) {
            // It was within a GroupOfShortsEAViewItem--we did a split.
            eaView_->insertAt(newGOSEAViewItem, topLevelIndex+1, err);  // insert the new GOS item
            if (err) {
                delete newGOSEAViewItem;
                delete myLeavi;
                return NULL;
            }
            eaView_->insertAt(myLeavi, topLevelIndex+1, err);   // then insert the caller's item
            if (err) {
                delete newGOSEAViewItem;
                delete myLeavi;
                return NULL;
            }
            return myLeavi;
        }
    }

    // The insertion is not within a GroupOfShortsEAViewItem; do the actual insertion.
    eaView_->insertAt(myLeavi, topLevelIndex, err);
    if (err) {
        delete myLeavi;
        return NULL;
    }
    return myLeavi; 

} // privateInsert


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::readBlockOfLEL  --CJB
//  
//  Method to read data from the caller's extent list one block at a time.
//
// ARGS:
//  LogExtentList&          myLEL           IN/OUT  caller's extent list to read from
//  ByteArray&              myBA            IN/OUT  ByteArray to read into
//  INT32&                  bytesLeftInBuf  IN/OUT  number of bytes of unprocessed data in myBA
//  INT32&                  originalReadLen IN/OUT  number of bytes read by this method
//  INT32&                  blocksToAdvance IN/OUT  number of blocks to "chop off" myLEL before read
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The caller's extent list has the specified number of blocks removed from
//  its beginning.
// 
// ERRORS:
//  error_No_free_store
//  error_Index_out_of_bounds
//  error_Empty_path_element
//  error_Read_EOM
//  error_Read_blank_check
//  error_Drive_read
// 
// NOTES:
//  This method was written to be used by fromDisk().  It interacts closely
//  with several of that method's variables.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NSR::ExternalEASpace::readBlockOfLEL (LogExtentList &myLEL,
                                      ByteArray &myBA,
                                      INT32 &bytesLeftInBuf,
                                      INT32 &originalReadLen,
                                      UINT32 &blocksToAdvance,
                                      Error &err)
{
    // First chop the part that we have processed, off the front of the list.
    if (blocksToAdvance > 0) {
        myLEL.TailOf(myLEL, blocksToAdvance*blockSize_, err);
        if (err) {
            bytesLeftInBuf = 0;
            originalReadLen = 0;
            blocksToAdvance = 0;
            return;
        }
    }

    // Read the next block from the local copy of the source LEL.
    // If there are any bytes remaining, move them up to the start of the ByteArray, and
    // read the next block immediately following them.
    if (bytesLeftInBuf > 0) {
        UINT32  tempLen = myBA.length();
        myBA.removeBytes(0, originalReadLen-bytesLeftInBuf);
        myBA.setLen(tempLen);
    }
    else bytesLeftInBuf = 0;                        // shouldn't be negative, but just in case
    // IMPORTANT: myLocalBAR can't be initialized before the "myBA.setLen(tempLen)" call 
    // above, because that call potentially changes the array that myLocalBAR points to. 
    ByteArrayRef            myLocalBAR(myBA);
    myLocalBAR.offset(bytesLeftInBuf);
    if (myLEL.infoLen() < blockSize_)
        myLocalBAR.length(myLEL.infoLen());
    else myLocalBAR.length(blockSize_);
    bytesLeftInBuf += myLEL.read(myLocalBAR, err);
    if (err == error_Read_EOLM)
        err = error_None;
    if (err) {
        bytesLeftInBuf = 0;
        originalReadLen = 0;
        blocksToAdvance = 0;
        return;
    }
    originalReadLen = bytesLeftInBuf;
    blocksToAdvance = 1;
} // readBlockOfLEL



// ------------- Start of methods of the ExternalEASpace class used only for testing --------
// ------------- These methods are all static, so they can be called from -------------------
// ----------------- anywhere without a "this" pointer --------------------------------------


#if defined DEBUG_EA__TRACK_DISK_ALLOCATION
// Static data used only for debugging methods:
NSR::UINT32         NSR::ExternalEASpace::allocatedBlkTabEntries = 0;
struct NSR::ABT     NSR::ExternalEASpace::allocatedBlkTab[MAX_ALLOC_TAB_ENTRIES];
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::markExtentsAllocatedForTesting  --CJB
//  
//  Method to mark the extents of a specified extent list as allocated in our
//  internal test tables. Prints a message if any part of the extent list is
//  already marked as allocated in the table.
//
// ARGS:
//  const LogExtentList&    lel             IN  list of extents to mark allocated
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Extents are marked as allocated in our internal table.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  IMPORTANT: if these mark allocated/mark free methods are used, they must
//  be called everywhere that the space resource is called to allocate or free
//  extents, either in this library, or in the caller's program.  They will
//  not give valid results if any calls are missed.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__TRACK_DISK_ALLOCATION
void
NSR::ExternalEASpace::markExtentsAllocatedForTesting (const LogExtentList& lel)
{
    UINT32                  index;
    const LogExtent         *myLogExtent;
    Error                   err;

    for (index=0; ; index++) {
        myLogExtent = lel.at(index, err);
        if (myLogExtent == NULL)
            break;
        insertIntoAllocBlksTab(myLogExtent->startBlock(), myLogExtent->numBlocks());
    }
} // markExtentsAllocatedForTesting
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::markExtentsFreeForTesting  --CJB
//  
//  Method to mark the extents of a specified extent list as free in our
//  internal test tables. Prints a message if any part of the extent list is
//  already marked as free in the table.
//
// ARGS:
//  const LogExtentList&    lel             IN  list of extents to mark free
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  Extents are marked as free in our internal table.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  IMPORTANT: if these mark allocated/mark free methods are used, they must
//  be called everywhere that the space resource is called to allocate or free
//  extents, either in this library, or in the caller's program.  They will
//  not give valid results if any calls are missed.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__TRACK_DISK_ALLOCATION
void
NSR::ExternalEASpace::markExtentsFreeForTesting (const LogExtentList& lel)
{
    UINT32                  index;
    const LogExtent         *myLogExtent;
    Error                   err;

    for (index=0; ; index++) {
        myLogExtent = lel.at(index, err);
        if (myLogExtent == NULL)
            break;
        removeFromAllocBlksTab(myLogExtent->startBlock(), myLogExtent->numBlocks());
    }
} // markExtentsFreeForTesting
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::insertIntoAllocBlksTab  --CJB
//  
//  Method to insert a single extent into our internal test table.
//  Prints a message if any part of the extent is already in the table.
//
// ARGS:
//  UINT32                  startBlock      IN  starting block number of the extent
//  UINT32                  numBlocks       IN  number of blocks in the extent
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  This extent is marked as allocated in our internal table.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  If the table fills up, we mark that fact and only print one error message.
//  The table is not updated on any subsequent insert or remove calls.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__TRACK_DISK_ALLOCATION
void
NSR::ExternalEASpace::insertIntoAllocBlksTab (UINT32 startBlock,
                                              UINT32 numBlocks)
{
    UINT32                  index, i;

    // See if the table is full.
    if (allocatedBlkTabEntries == MAX_ALLOC_TAB_ENTRIES) {
        printf ("***** allocatedBlkTab is full  (%lu tab entries)\n", allocatedBlkTabEntries);
        // Indicate that the table is full by incrementing allocatedBlkTabEntries
        // to be greater than MAX_ALLOC_TAB_ENTRIES.
        // This prevents us from giving this message forever after the table fills up.
        allocatedBlkTabEntries++;
    }
    if (allocatedBlkTabEntries > MAX_ALLOC_TAB_ENTRIES)
        return;

    // Find the point to insert the new entry.
    for (index=0; index<allocatedBlkTabEntries; index++) {
        if ((startBlock >= allocatedBlkTab[index].startBlock  &&
             startBlock <= allocatedBlkTab[index].endBlock)  ||
            (startBlock+numBlocks-1 >= allocatedBlkTab[index].startBlock  &&
             startBlock+numBlocks-1 <= allocatedBlkTab[index].endBlock)) {
            printf ("***** attempt to allocated a previously allocated extent:%lu-%lu  (%lu tab entries)\n",
                startBlock, startBlock+numBlocks-1, allocatedBlkTabEntries);
            return;
        }
        if (startBlock < allocatedBlkTab[index].startBlock)
            break;
    }

    // Index is at the entry to insert the new entry.  Move the rest of the entries up.
    for (i=allocatedBlkTabEntries; i>index; i--) {
        allocatedBlkTab[i].startBlock = allocatedBlkTab[i-1].startBlock;    
        allocatedBlkTab[i].endBlock = allocatedBlkTab[i-1].endBlock;
    }
    allocatedBlkTab[index].startBlock = startBlock; 
    allocatedBlkTab[index].endBlock = startBlock+numBlocks-1;   
    allocatedBlkTabEntries++;

} // insertIntoAllocBlksTab
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::removeFromAllocBlksTab  --CJB
//  
//  Method to remove a single extent from our internal test table.
//  Prints a message if any part of the extent is not in the table.
//
// ARGS:
//  UINT32                  startBlock      IN  starting block number of the extent
//  UINT32                  numBlocks       IN  number of blocks in the extent
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  This extent is marked as free in our internal table.
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  If the table filled up on an insert, we mark that fact.
//  The table is not updated on any subsequent insert or remove calls.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__TRACK_DISK_ALLOCATION
void
NSR::ExternalEASpace::removeFromAllocBlksTab (UINT32 startBlock,
                                              UINT32 numBlocks)
{
    UINT32                  index, i;

    // See if the table is full. The Indication that the table is full is that
    // allocatedBlkTabEntries is greater than MAX_ALLOC_TAB_ENTRIES.
    // After the table has filled up, nothing we do is valid.
    if (allocatedBlkTabEntries > MAX_ALLOC_TAB_ENTRIES)
        return;

    // Find this entry in the table.
    for (index=0; index<allocatedBlkTabEntries; index++) {
        if (startBlock == allocatedBlkTab[index].startBlock  &&
            startBlock+numBlocks-1 == allocatedBlkTab[index].endBlock) {
            // Found the entry.  Move the rest of the entries down.
            allocatedBlkTabEntries--;
            for (i=index; i<allocatedBlkTabEntries; i++) {
                allocatedBlkTab[i].startBlock = allocatedBlkTab[i+1].startBlock;
                allocatedBlkTab[i].endBlock = allocatedBlkTab[i+1].endBlock;
            }   
            return;
        }
    }

    printf ("***** attempt to free an extent that was not allocated:%lu-%lu  (%lu tab entries)\n",
        startBlock, startBlock+numBlocks-1, allocatedBlkTabEntries);

} // removeFromAllocBlksTab
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::printSpaceResourceAllocatedBlks  --CJB
//  
//  Method to print the extents that are morked as allocated in our internal
//  test table.
//
// ARGS:
//  (none)
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  If the table filled up on an insert, we mark that fact.
//  The table is not updated on any subsequent insert or remove calls.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__TRACK_DISK_ALLOCATION
void
NSR::ExternalEASpace::printSpaceResourceAllocatedBlks (void)
{
    UINT32                  index;
    UINT32                  allocatedBlocks;
    UINT32                  indexesToProcess;
    
    allocatedBlocks = NSR::ExternalEASpace::countSpaceResourceAllocatedBlks ();

    printf ("%lu allocated blocks (per our SR table):", allocatedBlocks);

    // See if the table is full. The Indication that the table is full is that
    // allocatedBlkTabEntries is greater than MAX_ALLOC_TAB_ENTRIES.
    if (allocatedBlkTabEntries > MAX_ALLOC_TAB_ENTRIES)
        indexesToProcess = MAX_ALLOC_TAB_ENTRIES;
    else indexesToProcess = allocatedBlkTabEntries;

    // Print every entry in the table.
    for (index=0; index<indexesToProcess; index++) {
        if (allocatedBlkTab[index].startBlock == allocatedBlkTab[index].endBlock)
            printf (" %lu", allocatedBlkTab[index].startBlock, allocatedBlkTab[index].endBlock); 
        else printf (" %lu-%lu", allocatedBlkTab[index].startBlock, allocatedBlkTab[index].endBlock); 
    }
    printf ("\n");
    
} // printSpaceResourceAllocatedBlks
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::countSpaceResourceAllocatedBlks  --CJB
//  
//  Method to return the number of extents that are morked as allocated in
//  our internal test table.
//
// ARGS:
//  (none)
//
// RETURNS:
//  UINT32 -- returns the nubmer of blocks that are marked as allocated in
//  our internal test table.
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  If the table filled up on an insert, we mark that fact.
//  The table is not updated on any subsequent insert or remove calls.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__TRACK_DISK_ALLOCATION
NSR::UINT32
NSR::ExternalEASpace::countSpaceResourceAllocatedBlks (void)
{
// This method counts the number of blocks that are allocated (according to
// our internal test table).
    UINT32                  index;
    UINT32                  totalAllocBlks = 0;
    UINT32                  indexesToProcess;

    // See if the table is full. The Indication that the table is full is that
    // allocatedBlkTabEntries is greater than MAX_ALLOC_TAB_ENTRIES.
    if (allocatedBlkTabEntries > MAX_ALLOC_TAB_ENTRIES)
        indexesToProcess = MAX_ALLOC_TAB_ENTRIES;
    else indexesToProcess = allocatedBlkTabEntries;

    // Count every entry in the table.
    for (index=0; index<indexesToProcess; index++)
        totalAllocBlks += allocatedBlkTab[index].endBlock - allocatedBlkTab[index].startBlock + 1;

    return  totalAllocBlks;
} // countSpaceResourceAllocatedBlks
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::printSpaceInfo  --CJB
//  
//  Overloaded method to print all information about the disk space held
//  internally by this EA space, and held in the caller's extent list.
//
// ARGS:
//  ExternalEASpace&        externalEASpace IN  the external EA space object
//  LogExtentList&          extList         IN  the extent list of the EA spaces' caller
//  Boolean                 verbose         IN  indicates to print all/summary information
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::ExternalEASpace::printSpaceInfo (ExternalEASpace& externalEASpace,
                                      LogExtentList& extList,
                                      Boolean verbose)
{
    UINT32              eaSpaceSectors;
    UINT32              extListSectors;
    UINT32              trashListSectors;
    UINT32              blockSize;
    INT32               freeSectors;

    NSR::LogicalVolume& lv = extList.logicalVolume();
    blockSize = lv.blockSize();

    NSR::ISOSpaceResource  *sr = externalEASpace.spaceResource_;
    if (sr == NULL)
        freeSectors = -1;
    else freeSectors = (UINT32)(sr->availBytes() / blockSize);

    eaSpaceSectors = externalEASpace.countDiskSpace(blockSize);
    extListSectors = extList.blockMultLen() / blockSize;
    trashListSectors = externalEASpace.trashLel_.blockMultLen() / blockSize;

    NSR::ExternalEASpace::printSpaceInfo (freeSectors, extListSectors, eaSpaceSectors,
        trashListSectors, verbose);

    if (verbose) {
        printf ("%lu blocks in caller's extList: ", extListSectors);
        NSR::ExternalEASpace::printLogExtentList (extList);

        printf ("%lu blocks in EASpace:\n", eaSpaceSectors);
        externalEASpace.printAllocatedBlocks();
    
        printf ("%lu blocks in trashList_: ", trashListSectors);
        NSR::ExternalEASpace::printLogExtentList (externalEASpace.trashLel_);
    }
} // printSpaceInfo
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::printSpaceInfo  --CJB
//  
//  Overloaded method to print all information about the disk space held
//  in the caller's extent list.
//
// ARGS:
//  LogExtentList&          extList         IN  the extent list of the EA spaces' caller
//  INT32                   freeSectors     IN  number of free blocks on the volume
//  Boolean                 verbose         IN  indicates to print all/summary information
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  If freeSectors is negative, that figure will not be printed.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::ExternalEASpace::printSpaceInfo (LogExtentList& extList,
                                      INT32 freeSectors,
                                      Boolean verbose)
{
    UINT32              extListSectors;
    UINT32              blockSize;

    NSR::LogicalVolume& lv = extList.logicalVolume();
    blockSize = lv.blockSize();

    extListSectors = extList.blockMultLen() / blockSize;

    NSR::ExternalEASpace::printSpaceInfo (freeSectors, extListSectors, -1, -1, verbose);

    if (verbose) {
        printf ("%lu blocks in caller's extList: ", extListSectors);
        NSR::ExternalEASpace::printLogExtentList (extList);
    }
} // printSpaceInfo
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::printSpaceInfo  --CJB
//  
//  Method to print all information about the disk space held internally by
//  this EA space, and held in the caller's extent list.
//
// ARGS:
//  INT32                   freeSectors     IN  number of free blocks on the volume
//  INT32                   extListSectors  IN  number of blocks in the caller's extent list
//  INT32                   eaSpaceSectors  IN  number of blocks in active use by the EA space
//  INT32                   trashListSectors IN number of blocks in the EA space's trash list
//  Boolean                 verbose         IN  indicates to print all/summary information
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  If any of the INT32 parameters are negative, that figure will not be printed.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::ExternalEASpace::printSpaceInfo (INT32 freeSectors,
                                      INT32 extListSectors,
                                      INT32 eaSpaceSectors,
                                      INT32 trashListSectors,
                                      NSR::Boolean verbose)
{
    if (verbose) {
        if (freeSectors >= 0)
            printf ("%lu free blocks (per SpaceResource)\n", freeSectors);
   #if defined DEBUG_EA__TRACK_DISK_ALLOCATION
        NSR::ExternalEASpace::printSpaceResourceAllocatedBlks ();
   #endif
    }

    else {
        printf ("block usage:");
        if (freeSectors >= 0)
            printf ("  %lu free", freeSectors);
    
      #if defined DEBUG_EA__TRACK_DISK_ALLOCATION
        UINT32 allocatedSectors = NSR::ExternalEASpace::countSpaceResourceAllocatedBlks ();  
        printf ("  %lu allocated", allocatedSectors);
      #endif
    
        if (extListSectors >= 0)
            printf ("  %lu in caller's extList", extListSectors);
        if (eaSpaceSectors >= 0)
            printf ("  %lu in EASpace", eaSpaceSectors);
        if (trashListSectors >= 0)
            printf ("  %lu in trash list", trashListSectors);
        printf ("\n");
    } // else

} // printSpaceInfo
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::printLogExtentList  --CJB
//  
//  Method to print the logical block numbers of every extent in a specified
//  extent list.
//
// ARGS:
//  LogExtentList&          lel             IN  the extent list to print
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  (none)
// 
// ERRORS:
//  (none)
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL
void
NSR::ExternalEASpace::printLogExtentList (const LogExtentList& lel)
{
    UINT32                  index;
    UINT32                  startBlock;
    UINT32                  numBlocks;
    const LogExtent         *myLogExtent;
    Error                   err;
    Boolean                 firstBlock = True;

    for (index=0; ; index++) {
        myLogExtent = lel.at(index, err);
        if (myLogExtent == NULL)
            break;
        startBlock = myLogExtent->startBlock();
        numBlocks = myLogExtent->numBlocks();
        if (numBlocks == 0)
            continue;
        if (!firstBlock)
            printf (", ");
        firstBlock = False;
        if (numBlocks == 1)
            printf ("%lu", startBlock);
        else printf ("%lu-%lu", startBlock, startBlock+numBlocks-1);
    }
    printf ("\n");
}
#endif


//----------------------------------------------------------------------
// METHOD: ExternalEASpace::dumpLogExtentList  --CJB
//  
//  Method to dump the entire data of a specified extent list to a file for
//  examination, on the native file system.
//
// ARGS:
//  LogExtentList&          lel             IN  the extent list to dump
//  UINT16                  extension_num   IN  the dump file's extension will be named this (eg., ".001")
//  Error&                  err             OUT error status
//
// RETURNS:
//  (none)
// 
// PRE-CONDITIONS:
//  (none)
// 
// POST-CONDITIONS:
//  The dump file is created on the native file system.
// 
// ERRORS:
//  error_File_not_found
//  error_Drive_write
//  error_Drive_read
// 
// NOTES:
//  This method is for testing only, and will not appear in the final product.
//  This is a static method--it is called without a "this" pointer.
//  This method is currently supported only on the Mac platform. The output
//  file's name is EA_Test:LEL_dump.nnn, where nnn is the extension_num parameter.
//  (The EA_Test directory must already exist.)
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
#if defined DEBUG_EA__GENERAL  &&  defined MAC_PLATFORM
    // Declarations of local functions used in this method:
    void MacStrCpy(StringPtr dst, CONST_CHAR_PTR src);
    void MacStrCat(StringPtr dst, CONST_CHAR_PTR src);
void
NSR::ExternalEASpace::dumpLogExtentList (const LogExtentList & lel,
                                         UINT16 extension_num,
                                         Error & err)
{
    INT16                   temp_stat;
    INT16                   df_file_num;
    struct FSSpec           my_spec;
    UINT16                  myVRef;
    Str255                  name_Str;
    INT32                   temp_byte_count;
    INT32                   readLen;
    UINT32                  testInfoLen;
    UINT32                  testBMLen;
    UINT32                  myBlockSize;
    LogicalVolume           *myLV;
    
    // Get some values from the source LogExtentList.
    myLV = &lel.logicalVolume();
    myBlockSize = myLV->blockSize();

    // Create a local copy of the source LogExtentList.
    NSR::LogExtentList      myLEL (*myLV);
    myLEL = lel;

    // Create the ByteArray and ByteArrayRef objects that we need..
    NSR::ByteArray          myBA(4 * myBlockSize);
    NSR::ByteArrayRef       myBAR(myBA);
    myBAR.offset(0);

    // Call FSMakeFSSpec to set up my_spec.
    char    name_buf[13] = "LEL_dump.000";
    name_buf[9] = (extension_num/100) + '0';
    extension_num = extension_num % 100;
    name_buf[10] = (extension_num/10) + '0';
    name_buf[11] = (extension_num%10) + '0';
    myVRef = 0;
    ::MacStrCpy((StringPtr)&name_Str, ":EA_Test:");
    ::MacStrCat((StringPtr)&name_Str, name_buf);
    temp_stat = FSMakeFSSpec (myVRef, fsRtDirID, name_Str, &my_spec);
    if (temp_stat != 0  &&  temp_stat != -43) {
        err = error_File_not_found;
        return;
    }

    // Create the file.
    temp_stat = FSpOpenDF (&my_spec, fsRdWrPerm, &df_file_num);
    if (temp_stat == -43) {
        // The file didn't exist--create it (as a simpleText file).
        temp_stat = FSpCreate (&my_spec, 'ttxt', 'TEXT', smSystemScript);
        if (temp_stat != 0) {
            err = error_Drive_write;
            return;
        }
    }

    // Open the file.
    temp_stat = FSpOpenDF (&my_spec, fsRdWrPerm, &df_file_num);
    if (temp_stat != 0) {
        err = error_Drive_read;
        return;
    }

    temp_stat = 0;
    for (UINT32 index=0; ; index++) {
        testBMLen = myLEL.blockMultLen();
        testInfoLen = myLEL.infoLen();

        // Read the first block of the extent list.
        readLen = myLEL.read(myBAR, err);
        if (err == NSR::error_Read_EOLM)
            err = NSR::error_None;
        if (err)
            break;
        if (readLen <= 0) {
            err = error_Drive_read;
            break;
        }

        // Write this block out to the dump file.
        temp_byte_count = readLen;
        temp_stat = FSWrite (df_file_num, &temp_byte_count, (BYTE_PTR)myBAR);
        if (temp_stat != 0)
            break;
        if (temp_byte_count != readLen) {
            err = error_Drive_write;
            break;
        }

        // Chop the block that we just read, off the front of the list.
        myLEL.TailOf(myLEL, myBlockSize, err);
        if (err)
            break;

    } // for loop

    // Close the dump file.
    FSClose (df_file_num);
    
} // dumpLogExtentList
void
MacStrCpy(StringPtr dst,
          CONST_CHAR_PTR src)
{
    int             i, temp_len;
    CHAR_PTR        temp_dst = (CHAR_PTR)&dst[1];

    temp_len = strlen (src);
    if (temp_len > 255)
        temp_len = 255;
    
    for (i=0; i<temp_len; i++)
        *temp_dst++ = *src++;
    dst[0] = temp_len;
} // MacStrCpy
void
MacStrCat(StringPtr dst,
          CONST_CHAR_PTR src)
{
    int             i, temp_len;
    CHAR_PTR        temp_dst = (CHAR_PTR)&dst[dst[0]+1];

    temp_len = strlen(src);
    if (temp_len > 255 - dst[0])
        temp_len = 255 - dst[0];
    if (temp_len <= 0)
        return;

    for (i=0; i<temp_len; i++)
        *temp_dst++ = *src++;
    dst[0] += i;
} // MacStrCat
#endif
