//-*-c++-*--------------------------------------------------------------
//  nsrDir.cpp
//   Handles directories.
//
//      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:
//   DirEntry
//   DirList
//  
//  NOTES:  
//  I originally coded some of the DE methods to return an error for the
//  following cases.  Although these errors are no longer used, the 
//  conditions are worth noting for possible use during code testing.  --JLK
// 
//   error_DIR_Bad_dir_extent_type;     
//      Directory ICB_Addr extType should always be 'Recorded' but isn't
//      3/23/95-- Could be something else if the entry is deleted.
//   error_DIR_Parent_and_not_dir;      
//      Dir is marked as parent yet not marked as directory             
//
//----------------------------------------------------------------------
// $Source: /projects/HMS/nsr/udfCore/ss/nsrDir.cpp,v $
// $Revision: 1.45 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/21 23:15:17 $

#include "nsrLib.h"

//----------------------------------------------------------------------
//  METHOD:  DirEntry::entryType() // set                       --JLK
// 
//  Sets the type of the directory entry.  
//
//  Directory entries can be a Parent, Subdir, or File.
//  DirEntType is an abstraction of the Directory and Parent bits of the File
//  characteristics field of the FID.  See enum DirEntType.
//
//  ARGS:
//  det                 IN      directory entry type
//
//  RETURNS:
//  void
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  Directory and Parent bits of fid will be set/cleared based on det.
// 
//  ERRORS:
//  none        
//
//  NOTES:
// For reference:
//   Parent  == PARENT  & DIRECTORY 
//   Subdir  == !PARENT & DIRECTORY
//   File    == !PARENT & !DIRECTORY 
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  METHOD:  DirEntry::entryType()  // Get                      --JLK
//      
//  Returns type of dir entry: parent, subdir, or file.
//
//  ARGS:
//  none
//
//  RETURNS:
//  DirEntType 
// 
//  PRE-CONDS:  (E.g., assumes)
// 
//  POST-CONDS: (E.g., changes to class data members)
//  none
// 
//  ERRORS:
//  none        
// 
//  NOTES:
//  if (Parent & not directory) will return Parent.
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  METHOD:  DirEntry::isNull()                                 --JLK
//    Returns true if infoLen of ICB Addr == 0
//
//  ARGS:
//  none
//
//  RETURNS:
//    Returns true if infoLen of ICB Addr == 0
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  none
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//
//----------------------------------------------------------------------

NSR::Boolean
NSR::DirEntry::isNull()
{
    // !!! This should simply check for a null extent!
    INT16 isNil = (icbAddress_.infoLen() == 0);
    return isNil? True : False;
} // DirEntry::isNull()

//----------------------------------------------------------------------
//  METHOD:  DirEntry::name()                                   --DVM
//    Sets file name (a.k.a. file identifier in standard-ese).
//
//  ARGS:
//   name       IN    the file name             
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:
// 
//  POST-CONDS:
//  Truncates the stored copy of the input param 'name' to 254 chars,
//  and holds it.
// 
//  ERRORS:
//  none        
// 
//  NOTES:
//
//----------------------------------------------------------------------

void 
NSR::DirEntry::name( const String & name )
{
    name_ = name;
    UINT16 MAX_FILE_ID = 254;
    if (name_.length() > MAX_FILE_ID)
        name_.remove(MAX_FILE_ID); // truncate name
} // DirEntry::name(String)


//----------------------------------------------------------------------
//  METHOD:  DirEntry::operator!=()                             --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//
//  RETURNS:
//  Explain_returned_value
// 
//  PRE-CONDS:  (E.g., assumes)
//  List_preconds
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List_postconds
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Notes_go_here
//
//----------------------------------------------------------------------

NSR::Boolean 
NSR::DirEntry::operator!=(DirEntry & obj)
{
    UINT16 bad = 0;

    bad += ( name_       != obj.name_       );
    bad += ( hidden_     != obj.hidden_     );
    bad += ( deleted_    != obj.deleted_    );
    bad += ( icbAddress_ != obj.icbAddress_ );
    bad += ( entryType_  != obj.entryType_  );

    return bad != 0 ? True : False;
}

//----------------------------------------------------------------------
//  METHOD:  DirEntry::toISO()                                  --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//    ByteArray & buffer    IN/OUT  DE is written here in ISO form 
//    UINT32 offset         IN      Where to start writing 
//    UINT32 tagLoc         IN      tag location.  block # of fid on disk
//                                  I.e., where you will write it.
//    Error & err           OUT     Resulting error
//
//  RETURNS:
//  Bytes written.  0 indicates an error.
// 
//  PRE-CONDS:  (E.g., assumes)
//  Assume:  offset < buffer.length()  // this is checked in DirList.toISO
//  Assume:  buffer.length() - offset >= ISOlen()  // checkd in DL.2ISO
//  DE is not null
// 
//  POST-CONDS: (E.g., changes to class data members)
//  DE is written to buffer in ISO form
//  Changes:  buffer.  fills buffer, at offset, with ISO form of the dir entry.
// 
//  ERRORS:
//  error_No_free_store -- can come from fid.toISO()
//  error_DIR_null_DirEntry             // tried to write a null DE
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Note that since you have to pass in tagLoc, you need to know where
//  on the disk you are going to write this DE.
//
// USE WITH CARE:  If the ISOlen is bigger than the space to write it you'll
// be trashing variables 
//
// NOTE:  This is not intended for general use.  Public should use 
// DirList.toISO instead for most purposes.
//
// FUTURE:  JLK. DE: A fid.toISO that takes ByteArray not BYTE_PTR.
//      Benefit is that it could check for end of buffer and prevent us
//      from writing out of bounds.  Code for possible toISO(ByteArray) :
//              fid.toISO( buffer, offset, tagLoc );
//----------------------------------------------------------------------

NSR::UINT32 
NSR::DirEntry::toISO(ByteArray & buffer, 
                     UINT32 offset, 
                     UINT32 tagLoc,
                     Error & err)
{
    UINT32 bytesWritten = 0;
    if( isNull() ) {
        err = NSR::error_DIR_null_DirEntry; 
    } else {
        FileIdentifierDesc fid(name_);
        if (hidden_)
            fid.markHidden();
        else
            fid.clearHidden();
        if (deleted_)
            fid.markDel();
        else        
            fid.clearDel();
        fid.icbAddr.fromLogExtent(icbAddress_,err);
        switch(entryType_) {
        case de_Parent: fid.markParent();  fid.markDir();  break;
        case de_Subdir: fid.clearParent(); fid.markDir();  break;
        case de_File:   fid.clearParent(); fid.clearDir(); break;
        default: ;
#ifdef DEBUG
            printf("DEBUG: %d is invalid entryType in dirEntry.toISO()\n",
                   (UINT16)entryType_);
            exit(-1);
#endif
        }
        
        fid.toISO( ((BYTE_PTR) buffer)+offset, tagLoc);
        if (err = fid.error()) 
            bytesWritten = 0;  // Bytes may have been written but they are
                               // unreliable.  therefore toISO returns 0.
        else
            bytesWritten = fid.descLen();
    }
    return bytesWritten;
} // DirEntry::toISO
    

//----------------------------------------------------------------------
//  METHOD:  DirEntry::fromISO()                                --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//
//  RETURNS:
//  Explain_returned_value
// 
//  PRE-CONDS:  (E.g., assumes)
//  List_preconds
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List_postconds
// 
//  ERRORS:
// error_End_of_buffer                          -- buffer ended before FID did
// error_Bad_tag_descriptor_type                -- not a FID
// error_Bad_tag_location                       -- tag location field mismatch
//      or you are parsing an area of all 00's
//      (Because there is a tag_Unknown = tag# 0 in our toolkit, you don't 
//      get error_Bad_tag_descriptor_type when parsing 00's.)
// error_Bad_tag_checksum                       -- tag checksum mismatch
// error_Bad_descr_CRC                          -- descriptor CRC mismatch
// error_Bad_tag_descriptor_version             -- not ISO/IEC 13346
//
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  More than one error condition may exist, but the first one found is
//  returned.  Errors are checked in this order:
// 
// End of buffer (meaning, Not enough space to read tag)
// Bad tag location (Field value != actual sector)
// Bad tag checksum
// Bad tag descriptor version ( != 02)
// Bad tag descriptor type ( != 257 (fid) )
// End of buffer (meaning, Not enough space to read body)
// Bad descr CRC
// 
//
// Changes:  fid.  fid is read from disk and fields are filled up.
// Returns:  Bytes processed.  
// That is, returns the length of the directory entry in bytes if success
// or returns 0 if failure.  (Technically, it returns the descriptor 
// length of the FID as written on the disk.)  Only succeeds if 
// it can process a complete FID.
// 
// Algo note:
// In DE, check for offset < buffer.len Not needed 
// because fid.fromISO() will return end_of_buffer error.
//
// FUTURE:  JLK. DE: make a speedy fid.fromISO w/no tag loc check 
//----------------------------------------------------------------------

NSR::UINT32 
NSR::DirEntry::fromISO(const ByteArray & buffer, 
                       UINT32 offset, 
                       UINT32 tagLoc,
                       LogicalVolume *lvp,
                       Error & err)
{
    FileIdentifierDesc fid;
    fid.fromISO( buffer, offset, tagLoc );
    if (err = fid.error()) {
        return 0;
    }
    name_    = fid.fileID();
    hidden_  = fid.isHidden();
    deleted_ = fid.isDel();
    Boolean parent = fid.isParent();
    Boolean directory = fid.isDir();
    if (parent)  
        entryType_ = de_Parent;                         
    else if (directory)
        entryType_ = de_Subdir;         
    else
        entryType_ = de_File;   
    fid.icbAddr.toLogExtent(lvp,icbAddress_,err);
    if (err)
        return 0;
    if (fid.icbAddr.extLength() == 0 ) {
        err = error_DIR_null_DirEntry;
        return 0;               // !!! OK? better to return something else?
    }
    return fid.descLen();
}

//----------------------------------------------------------------------
//  METHOD:  DirEntry::print()                                  --JLK
//    Prints out a DirEntry
//
//  ARGS:
//  indent                  IN      Number of spaces to indent
//  header                  IN      String to print as a header title
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  0 <= indent < 160
// 
//  POST-CONDS: (E.g., changes to class data members)
//  none
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Must compile with DEBUG compiler flag.
//
//----------------------------------------------------------------------

#ifdef DEBUG
void 
NSR::DirEntry::print( UINT32 indent, 
                      const String & header )
{
    Error err; 
    CONST_CHAR_PTR ind = (CONST_CHAR_PTR) NSR::space(indent); // cast to CHAR_PTR

    printf("%s----- DirEntry --------------------------------\n", ind); 
    printf("%s<%s>\n", ind, (CHAR_PTR) header);
    printf("%s Filename            = %s\n", ind, (CONST_CHAR_PTR) name());
    String et;
    if (entryType() == de_File)
        et = "File";
    else if (entryType() == de_Subdir)
        et = "Subdir";
    else if (entryType() == de_Parent)
        et = "Parent";
    else
        et = "**Invalid***";
    printf("%s Dir Entry Type      = %s\n", ind, (CONST_CHAR_PTR)et);
    printf("%s Hidden?             = %s\n", ind, hidden() ? "True" : "False");
    printf("%s Deleted?            = %s\n", ind, deleted() ? "True" : "False");
    printf("%s ICB Address         = (an extent)\n", ind);
    LogExtent  icb;
    getICBAddress( icb, err );
    if (err) {
        printf("Error after DE.print.getICBAddress(): ");
        printf("%s \n", (CHAR_PTR) err.message() );
        exit(-1);                               // !!! NOTE EXIT LOCATION HERE
    } // if
    icb.print(indent + 3);
    printf("\n");
    printf("\n");
} // DirEntry::print()
#endif


//----------------------------------------------------------------------
//  METHOD:  DirList::copy()                                    --JLK
//    Makes a copy of contents of input DirList & assigns to *this DirList.
//
//  ARGS:
//  dl                      IN      DirList to copy
//  err                     OUT     error
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none.  Previously, assumed that free() had been called before calling
//  copy for operator= (but not for copy constructor).  Now, it 
//  calls clear() (which calls free() ) at the very beginning.  The 
//  penalty to the copy constructor is one function call and one conditional
//  test.
// 
//  POST-CONDS: (E.g., changes to class data members)
//  Sets err.  If err is set to anything besides error_None, this->dlist_ will
//      be cleared to empty list.
//      Else, dlist_ contains pointers to newly allocated DirEntries that are
//      copies of the DEs in the input arg dl.
// 
//  ERRORS:
//  error_Null_pointer          --source dl.dlist_ contains nulls.
//  error_No_free_store         --no memory to make a complete dest DirList 
//  error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  User must instatiate an Error var and pass it in to us.
//  Can't do a call like dl.copy(dl).  Similar to saying dl=dl.  Meaningless.
//  In that case, copy() does nothing.  Just returns.
//
//  If copy() has to return an error (other than error_None), post condition
//  is that this->dlist_ will be clear()'ed.
// 
//  Since we do two conditionals (one for error_Null_pointer, one for
//  error_No_free_store) in a loop, this is a fairly slow routine.
//----------------------------------------------------------------------

void
NSR::DirList::copy( const DirList & dl, Error & err)
{
    DirEntry * dep;     // dep = dir entry pointer, Destination list
    DirEntry * dlDep;   // Source list
    err = error_None;

    if (&dl != this) {
        this->clear();
        dlist_ = dl.dlist_;             // Shallow copy
        for (UINT32 i = 0; i < dlist_.entries(); i++) {
            dlDep = dl.dlist_.at(i);     // Check source ptr
            if (dlDep == NULL) {
                dlist_.clear();
                err = error_Null_pointer;
                return;         
            } // if
            dep = new DirEntry( *dlDep );
            if (dep == NULL) {
                dlist_.clear();
                err = error_No_free_store;
                return;         
            } // if
            else {
                dlist_.at(i) = dep;
            } // else
        } // for
    } //if
    else {  // We are being asked to do: dl = dl.  would overwrite ourselves
            // instead, do nothing to this->dlist_.  just leave it as is.
    }
    return;             
} // DirList::copy


//----------------------------------------------------------------------
//  METHOD:  DirList::nullPointersError()                       --JLK
//      Checks list to see if it's in the invalid state where it
//      contains null pointers instead of pointers to DirEntries.
//
//  ARGS:
//  none
//
//  RETURNS:
//  error_Null_pointer  --the contained dlist_ has null pointers inside.
//                      --This should never happen unless someone deleted
//                      --a list entry with a call like delete dlist_.at(i).
//                      --All dlist_ ptrs should point to DirEntries.
//  error_None
//                       
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  returns error_Null_pointer or error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Could write a faster algo if needed.
//----------------------------------------------------------------------
NSR::Error
NSR::DirList::nullPointersError() const
{
    Error err = error_None;
    for (UINT32 i = 0; i < dlist_.entries(); i++) {
        if ( dlist_.at(i) == NULL) {
            err = error_Null_pointer;
            return err;
        } //if
    } // for
    return err;
} // DirList::nullPointersError()


//----------------------------------------------------------------------
//  METHOD:  DirList::ISOlen()                                  --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//
//  RETURNS:
//  Explain_returned_value
// 
//  PRE-CONDS:  (E.g., assumes)
//  List_preconds
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List_postconds
// 
//  ERRORS:
//  none
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Notes_go_here
//
//----------------------------------------------------------------------

NSR::UINT32
NSR::DirList::ISOlen(void) const
{
    DirEntry    *curEntP;       // current Entry pointer
    UINT32      totLen;         // total length of dir list
    totLen = 0;
    for (UINT32 i = 0; i < dlist_.entries(); i++) {
        curEntP = dlist_.at(i);
        totLen += curEntP->ISOlen();    
    } // for
    return totLen;
} // DirList::ISOlen


//----------------------------------------------------------------------
//  METHOD:  DirList::operator!=()                              --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//
//  RETURNS:
//  Explain_returned_value
// 
//  PRE-CONDS:  (E.g., assumes)
//  List_preconds
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List_postconds
// 
//  ERRORS:
//  error_Error_name    
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Notes_go_here
//
//----------------------------------------------------------------------

NSR::Boolean
NSR::DirList::operator!=(const DirList & other)
{
    UINT32 thisSize( this->entries() );
    UINT32 otherSize( other.entries() );
    if (thisSize != otherSize) { 
        return True;
    }
    else {
        DirEntry    *thisEntP;  // current Entry pointers
        DirEntry    *otherEntP; //  
        INT16       notEqual;   // true if any part is !=
        notEqual = 0;
        for (UINT32 i = 0; (i < thisSize) && (notEqual == 0); i++) {
            thisEntP = this->dlist_.at(i);
            otherEntP = other.dlist_.at(i);
            notEqual += ( *thisEntP != *otherEntP );// test for DirEnt != DirEnt
        } // for
        return (notEqual != 0 ? True : False);
    } // else
} // DirList::operator!=


//----------------------------------------------------------------------
//  METHOD:  DirList::free()                                    --JLK
//    Deep deletes
//  
//  Deletes items pointed to by the ptrs in the list.  Removes the ptrs
//  from the list.
//
//  ARGS:
//  none
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none.
// 
//  POST-CONDS: (E.g., changes to class data members)
//  dlist_ will contain 0 entries.
//  all items contained before in dlist_ will be deleted (mem freed).
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Fct is INLINE.  Code's in .h file.
//
//----------------------------------------------------------------------


//----------------------------------------------------------------------
//  METHOD:  DirList::fromISO()                                 --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//
//  RETURNS:
//  Explain_returned_value
// 
//  PRE-CONDS:  (E.g., assumes)
//  List_preconds
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List_postconds
// 
//  ERRORS:
// Most error conditions come back from de.fromISO(). (SEE docs there.)
// + buffer isn't big enough to hold the whole fid -- this is a std exit condition
// + bad tag location  -- a std exit cond when parsing all 0's
// + various drive errors
// + no free store
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
// fromISO()
//      Returns number of bytes in buffer processed.  Starts processing
//      buffer at offset.  Parses as many full FIDs as it finds and 
//      adds DirEntries to the list.  NOTE:  Calls this->clear() to
//      clear the current object first.
// 
//      Uses dirEL to verify correctness of tag location field in FID.
//      Assumes start of first extent maps to start of buffer.
// PARAMETERS
// fromISO() processes 'buffer' for directory entries.  Processing 
// starts at 'offset'.  The first byte on disk pointed to by 'dirEL' 
// corresponds to first byte of 'buffer'. 
//
// STRUCTURES CHANGED
// It clears the current DirList.
// It reads NSR FIDs into DirEntries and appends them onto the DirList.
//
// HOW TO USE:  'dirEL' is an extent list that points to a directory on disk.
// A portion of this has already been read into 'buffer'. 
// The idea is that buffer contains the beginning of the dirEL.
// You will process the buffer using fromISO() which puts fids into
// a list of dirEntries.  
//
// fromISO() returns the number of bytes it processed. 
// After fromISO() do your stuff to this portion of the directory,
// and toISO() and write it if necessary. Next step is to 
// chop off the front of the dirEL extent list, using head/tail functs
// in nsrExt.h, calc offset, do read disk, and call fromISO() again.
//
// WHY WE NEED 'dirEL'
// fromISO() needs the dirEL because when you read a fid.fromISO()
// that routine checks to see if the 'tag location' (a logical block
// number) was originally written correctly.  So you have to supply the
// tag location. 
//
// To calc this, we ASSUME the first byte of dirEL maps to first byte of buffer.
// This is so that 'offset' into the buffer refers to the same byte
// as 'offset' into the dirEL, which lets us call one of dirEL's methods.
//
// WHY WE NEED 'offset'
// fromISO() wants to start processing the buffer at the start of a FID.
// But the start of the dirEL may not be the start of a FID, since FIDs are
// packed back-to-back.  Therefore, you give us the offset in bytes
// at which to start processing the buffer. 
// When offset==0, processing starts with the first byte in the buffer. 
//
// ALGO NOTE:  check offset < buffer.len Not needed because DE.fromISO checks.
//
// For reference, Meaning of:  de->fromISO( buffer, offset, Tag_location, lvp)
// buffer:          Memory image of disk
// offset:          Where to start reading a descriptor tag
// Tag location:    What sector on the disk this tag is stored at.
//    == Logical block number of the sector at byte 'offset' in dirEL 
// lvp              Pointer to LogicalVolume containing DirEntry.
//----------------------------------------------------------------------

NSR::UINT32 
NSR::DirList::fromISO(  const ByteArray & buffer, 
                        UINT32 offsetIn, 
                        const LogExtentList & dirEL,
                        Error & err)
{
    err = error_None;
    UINT32      offset = offsetIn;        // make copy of const param
    UINT32      bytes_processed = 0;      // return variable
    UINT32      fidLength;                // length of fid on disk
    UINT32      extLen = dirEL.infoLen(); // length of directory Ext List
    DirEntry    *de = new DirEntry();     // directory entry
    if (de == NULL) {
        err = error_No_free_store;
        return bytes_processed;
    }   

    clear(); // CLEAR THIS DIR LIST BEFORE APPENDING DIR ENTRIES
    while (offset < extLen) {
        fidLength = de->fromISO( buffer,
                                 offset,
                                 dirEL.logBlockNo(offset),
                                 &(dirEL.logicalVolume()),
                                 err);
        if (err) {
            delete de;
            return bytes_processed;
        }
        append(*de, err);       // append a copy of de to dir list
        if (err) {  // no free store?
            delete de;
            return bytes_processed;
        }
        bytes_processed += fidLength;
        offset += fidLength;  
        // note: offset != bytes_processed when orig offset > 0
    } //while
    delete de;
    return bytes_processed;
} // DirList::fromISO


//----------------------------------------------------------------------
//  METHOD:  DirList::toISO()                                   --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//
//  RETURNS:
//  Explain_returned_value
// 
//  PRE-CONDS:  (E.g., assumes)
//  List_preconds
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List_postconds
// 
//  ERRORS:
//      error_End_of_buffer         // buffer to small to write all entries 
//      error_Index_out_of_bounds   // offset too big. (offset > buffer length)
//      error_No_free_store         // this err can come from fid.toISO
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Notes_go_here
//
//----------------------------------------------------------------------
//  IMPLEMENTATION NOTES:
//      Converts DirEntries to FIDs and writes them to buffer.
//      Returns number of bytes written in buffer.  
//      That is, it returns 0 if failure.  Generally, returns 
//      this->ISOlen() if success, but you'll get a short count instead
//      if fid object in the DE reports an error.  
// 
//      A short count would happen when out_of_free_store error occurs 
//      upon creation of the FID during one of the DE toISO's.
//      In that case, only some of the DirEntries will have been written
//      to disk.  The recovery from this is to give the cpu more memory
//      by calling this routine with a smaller buffer.    
//
//      Starts writing at offset.  Uses dirEL to write tag location in FID.  
//      Does not modify the dirList (*this).
// 
//      ASSUMES:
//      NOTE!!!: Usage model is that the dirList.fromISO came from this buffer.
//
//      Assumes start of first extent maps to start of buffer.  
//      Assumes buffer length <= extentList length. (Ignore extent too small.)
//      Requires buffer length is big enough to write all entries in current object.
//      Assumes buffer.length >= dirList.ISOlen() 
//      Offset <= buffer.length
//      Offset <= extent.length
//
// USE WITH CARE:  ToISO assumes buffer.length >= dirList.ISOlen().  In 
// English, the buffer must be as big as or bigger than the disk image of
// the directory.  The usage model is that you call this method when you 
// want to write the ENTIRE DirList toISO.
// 
//      ERRORS:
//      error_End_of_buffer         // buffer to small to write all entries 
//      error_Index_out_of_bounds   // offset too big. (offset > buffer length)
//      error_No_free_store         // this err can come from fid.toISO
//
//      USAGE MODEL:  
//      If making changes to an existing directory...
//      You already did a fromISO to fill up this DirList based on
//      this buffer.  Buffer contents before offset is tail part of a FID.  
//      Buffer contents, if any, after dirEL.ISOlen() will generally be 
//      first part of next FID.     
//
//      When making changes, you don't want to overwrite or underwrite 
//      on a toISO().  Save the return value of fromISO() and make sure it ==
//      ISOlen() before doing a toISO().
//
//      General algo for modifying a directory:  read part of EL into buffer,
//      fromISO the buffer, change dirList, toISO to same buffer, repeat.
//      NOTE:  Not supporting a model where you read all dir entries
//      into a big dirList and then buffer the dirList writes.
//
// Some algo decisions:
//
// check that buffer length is the same as the extList?
//      No. because the extList is supposed to be bigger than the buffer.
//      Or equal.
//   !!! for now, assume buffer <= extListLen and ignore buffer > extListLen
//       because assume error would be found by fromISO
// 
// UINT32 extListLen;                   // length of directory Ext List
// extListLen = dirEL.infoLen();
// if buffer.len < extListLen
//   that's okay.  we fill up the buffer.  
// else buffer.len = extListLen
//   perfect.  no problem.
// else buffer.len > extListLen
//   error.  if we write buffer then we run out of space on disk (extent too 
//   small)  
//   When we do a extent.read(buffer) and the buffer is bigger than
//   the extent then it's an error because the buffer length is considered a 
//   request to read length number of bytes from the extent. 
//   the extent.read() returns error_End_Of_logical_media (meaning is, I've 
//   hit the end of the extent before I've finished reading).   
//
// exit conditions:
// we run out of dir entries to write to the buffer
//      not error.  buffer is bigger than our dirEnts...
// we run out of buffer
//      an error.  we check that buffer is big enough at start.
//      NOTE!!!: Usage model is that the dirList.fromISO came from this buffer.
//
// FUTURE:  DL.toISO could return number of fids written. (minor)
//      We could check ISOlen (!!! speed hit!) before
//      every toISO call (or in toISO itself) to ensure we don't even try to 
//      write a partial FID.  Currently we get the out of buffer error. 
//      ...this would be useful if we allowed you to buffer the writes.
//      (different usage model).
// 
// Error checking:  
// 
// if dirEntry.toISO checked for end_of_buffer 
// (which it currently doesn't)
// DirList.toISO could fail because of the DE.error 
//
// The only way to get an error on fid.toISO (called by
// de.toISO) is if the fid was in error state to begin with,
// for example, if it ran out of free_store during the
// fid constructor.  
//
// Wouldn't this error pop up in fid.fromISO() before?
// Yes, but maybe you're creating new ones and didn't fromISO.
//----------------------------------------------------------------------

NSR::UINT32 
NSR::DirList::toISO(ByteArray & buffer, 
                    UINT32 offsetIn, 
                    const LogExtentList & dirEL,
                    Error & err)
{
    err = error_None;
    UINT32 offset = offsetIn;           // make copy of const param
    UINT32 bytesProcessed;              // return variable
    UINT32 fidLength;                   // length of fid on disk
    UINT32 tagLoc;                      // block location of fid

    bytesProcessed = 0;
    if ( ! isEmpty() ) {
        // !!! BAIL OUT if not enough space to write the whole dirList
        if ( (buffer.length() - offset) < ISOlen() ) {
            err = error_End_of_buffer;
            return 0;
        } // if

        // !!! BAIL OUT if bad offset
        if (offset >= buffer.length()) {
            err = error_Index_out_of_bounds;
            return 0;
        } // if

        DirListIter     dli( *this );           // iterator
        DirEntry        *dep;                   // dir ent ptr (ie current ent)
        dep = dli();
        while (dep != NULL) { 
            tagLoc = dirEL.logBlockNo(offset);
            fidLength = (dep->toISO(buffer, offset, tagLoc, err) );  
            if (err) {
                return bytesProcessed;
            }

            bytesProcessed += fidLength;
            offset += fidLength;  
            dep = dli();  // advance iterator
        } // while more dir entries
    } // if (!isEmpty)
    return bytesProcessed;
} // DirList::toISO

//----------------------------------------------------------------------
//  METHOD:  DirList::append( DirEntry )                        --JLK
//      
//  _TERSE_DESC_
//  _LONG_DESC_if_needed_
//
//  ARGS:
//  reallyBigArgumentName   IN/OUT  Your_description_goes_here
//
//  RETURNS:
//  Explain_returned_value
// 
//  PRE-CONDS:  (E.g., assumes)
//  List_preconds
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List_postconds
// 
//  ERRORS:
//  error_No_free_store
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Notes_go_here
//
//----------------------------------------------------------------------

void 
NSR::DirList::append( const DirEntry & de, Error & err )
{
    err = error_None;
    DirEntry *dep = new DirEntry;
    if (dep == NULL) {
        err = NSR::error_No_free_store;
    }
    else {
        *dep = de;
        if (dlist_.insert(dep) == NULL) 
            err = NSR::error_No_free_store;
    }
} // DirList::append()

//----------------------------------------------------------------------
//  METHOD:  DirList::print()                                   --JLK
//    Prints out a DirList
//
//  ARGS:
//  indent                  IN      Number of spaces to indent
//  header                  IN      String to print as a header title
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  0 <= indent < 160
// 
//  POST-CONDS: (E.g., changes to class data members)
//  none
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Must compile with DEBUG compiler flag.
//
//----------------------------------------------------------------------

#ifdef DEBUG
void 
NSR::DirList::print(UINT32 indent,
                    const String & header)
{
    CHAR_PTR ind = (CHAR_PTR) NSR::space(indent);               // cast to CHAR_PTR
    printf("%s------ DirList -------------------------\n", ind);
    printf("%s<%s>\n", ind, (CHAR_PTR) header);
    printf("%s Num entries: %ld\n", ind, dlist_.entries());

    UINT32 i = 0;
    UINT32 numEnt = dlist_.entries();
    String entryTitle;  
    DirEntry * de;
    while ( i < numEnt) {
        entryTitle = "Entry #";
        entryTitle += (String) ::ultostr( i, 10 ); //unsigned long to str, base 10
        de = dlist_.at(i++);
        de->print(indent + 1, entryTitle);
    } // while
} // DirList::print()
#endif
