#ifndef _NSRDIR_ // -*-c++-*-
#define _NSRDIR_

//----------------------------------------------------------------------
//  nsrDir.h
//   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.
//
//  ENUMS:
//   DirEntType
//  CLASSES:
//   DirEntry
//   DirList
//   DirListIter
//
//----------------------------------------------------------------------
// $Source: /projects/HMS/nsr/udfCore/ss/nsrDir.h,v $
// $Revision: 1.51 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/21 23:15:35 $

//----------------------------------------------------------------------
//  ENUM:  DirEntType                                           --JLK           
//   Directory entries may be a Parent, Subdir, or File.
//
//  NOTES: 
//  DirEntType is an abstraction of the Directory and Parent bits of the File 
//  characteristics field of the FID.
//   Parent  == PARENT  & DIRECTORY 
//   Subdir  == !PARENT & DIRECTORY
//   File    == !PARENT & !DIRECTORY 
//
//  The Parent bit is assumed to be more reliable than the directory bit.
//  On fromISO, if we see PARENT & !DIRECTORY (labeled 'Parent?' above),
//  then we return 'Parent' and don't set an error.
//----------------------------------------------------------------------
enum DirEntType { 
    de_Parent = 3, 
    de_Subdir = 1, 
    de_File   = 0 
};

//----------------------------------------------------------------------
//  CLASS:  DirEntry                                            --JLK
//
//   Directory Entry.  An abstraction of a FID.
//
//  PUBLIC METHODS:
//
// DirEntry()
//      Creates a null DirEntry for default constructor. 
// fidError()
//      Returns error state of contained fid.
// hidden()
//      Returns or sets Existence bit.  (Yeah, we renamed it
//      Hidden, to make it sound more like English.)
// deleted()
//      Returns or sets the Deleted bit.
// getICBAddress()
// setICBAddress()
//      Gets or sets the Extent that has the address of the ICB.
// entryType()
//      Can be parent, subdir, or file.  Returns or sets type.
//      Abstraction of Directory and Parent bits.
// name()
// name()
//      Gets or sets File Identifier field.
// print()
//      Prints out information held in the DirEntry.  For debugging purposes.
//      Prints 'header' as a title for the DirEntry.
// operator!=()
//      Another debugging method.  Returns true if the fids are not equal.
// ISOlen()
//      Returns len of fid when written to ISO.
// toISO()
//      Writes fid to a buffer in ISO form.
// fromISO()
//      Reads DirEntry from buffer at offset.  Expects to see a FID at the 
//      start of buffer.  Checks tag location for consistency. Returns
//      length of DirEntry on disk (i.e., length of the FID).
// isNull()
//      Returns true if getICBAddress(...).infoLen() == 0.
//
//  NOTES:  (E.g., special cases, heads-up, and algo discussion) 
//  DE is contained in:
//    DirList  -- a list of ptrs to DEs
//----------------------------------------------------------------------
//  DATA MEMBERS:
//  String     name_       holds the name of the DirEntry.
//  Boolean    hidden_     True if not to be displayed. (normally)
//  Boolean    deleted_    True if the entry has been deleted.
//  LogExtent  icbAddress_ Holds location of node associated with entry.
//  DirEntType entryType_  Enum for which type of entry it is: Parent,
//                         subdir, file.
//
//----------------------------------------------------------------------

class DirEntry {
  private:
    String     name_       ;
    Boolean    hidden_     ;
    Boolean    deleted_    ;  
    LogExtent  icbAddress_ ;
    DirEntType entryType_  ;

  public:
    DirEntry() {}
    DirEntry( const DirEntry & d ) :
            name_      (d.name_       ),
            hidden_    (d.hidden_     ),
            deleted_   (d.deleted_    ),
            icbAddress_(d.icbAddress_ ),
            entryType_ (d.entryType_  )
    {}
    //~DirEntry();                          // use c++ default
    Boolean operator!=(DirEntry & other); 
// INLINE: DirEntry::hidden()
    void hidden(const Boolean hid) { hidden_ = hid; }
    Boolean hidden() const { return hidden_; } 
// INLINE: DirEntry::deleted()
    void deleted(const Boolean del) { deleted_ = del; }
    Boolean deleted() const { return deleted_;} 
    Boolean isNull();
// INLINE: DirEntry::getICBAddress()
    void getICBAddress( LogExtent & outExtent, 
                        Error & err)
    {
        outExtent = icbAddress_;
        err       = error_None;
    }
// INLINE: DirEntry::setICBAddress()
    void setICBAddress(LogExtent & inExtent) {
        icbAddress_ = inExtent;
    }
    void name( const String & name );
    String name(void) const { return name_;}
    void entryType(const DirEntType det) { entryType_ = det; }  
    DirEntType entryType() const { return entryType_; }
    UINT32 ISOlen(void) const {
        FileIdentifierDesc fid(name_);
        return fid.descLen();
    }
    UINT32 toISO(ByteArray & buffer, 
                 UINT32 offset, 
                 UINT32 tagLoc,
                 Error & err 
    ); 
    UINT32 fromISO(const ByteArray & buffer, 
                   UINT32 offset, 
                   UINT32 tagLoc,
                   LogicalVolume * lvp,
                   Error & err
    );
#ifdef DEBUG
    void print(UINT32 indent, const String & header ) ;
#endif
}; //class DirEntry

//----------------------------------------------------------------------
//  METHOD:  DirEntry::hidden() Setter                          --JLK
//    Sets existence/hidden bit
//
//  ARGS:
//  Boolean hid             IN      True if file/dir is hidden          
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  fid_ will have existence bit of file characteristics flags set/cleared.
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  METHOD:  DirEntry::deleted()  // Set                        --JLK
//    Sets deleted bit.
//
//  Set deleted bit to value of input parameter.  You can set this bit to
//  indicate a file or dir is deleted.  You could  
//
//  ARGS:
//  Boolean del             IN      True if file/dir is deleted
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  fid_ will have deleted bit of file characteristics flags set/cleared.
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  METHOD:  DirEntry::getICBAddress()                          --JLK
//     Gets ICB Address 
//  
//  Gets the ICB Address field of the fid.  Translates it to an extent.
//  User constructs the extent and gives it to us as an argument.
// 
//  ARGS:
//  LogExtent & outExtent   OUT     ICBAddress is returned here.
//  LogicalVolume * lvp     IN      LogicalVolume where ICB lives.
//  Error                   OUT
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  lvp is the LV where ICB lives.  otherwise the partition ptr in the 
//      extent won't point to the right partition.
//  fid_.icbAddr holds valid data.
// 
//  POST-CONDS: (E.g., changes to class data members)
//  outExtent contains the ICB Address
// 
//  ERRORS:
//  ISSUE:  add error doc here.  DE get icb address()   
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  Uses lvp to make a LogExtent.  On disk, ICBAddr is a longAD, which
//  stores a ref number to the surface (== partition, for OSTA).  We take the 
//  the ref number and using the lvp, fromLongAD sets the extent's partition 
//  pointer to point to the correct surface.
//
//
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  METHOD:  DirEntry::setICBAddress()                          --JLK
//    Sets the fid's ICB Address field.
//
//  Takes an extent, translates it to a LongAD, and sets the fid's 
//  ICB Address field.
//
//  ARGS:
//  LogExtent inExtent      IN      ICB Address in LogExtent form
//
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  inExtent holds ICB Address
// 
//  POST-CONDS: (E.g., changes to class data members)
//  fid_.icbAddr will be set to LongAD translation of inExtent
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  CLASS:  DirList                                             --JLK
//    A list of DirEntry objects.
//
//  PUBLIC METHODS:
//
// DirList()
//      Default makes an empty DirList.  Given a DirEntry, makes a list
//      with one entry, which is a copy of the entry passed in.  
//      Sets error if no memory.  Copy constructor makes a deep copy.
//      USE WITH CARE:  
//      Check error() after the constructor.
//      If you use the constructors that pass a DirEntry or 
//      a DirList in, it's possible for c++ to make the new DirList
//      and then run out of free store for the copy of the input param.
// ~DirList()
//      Destroys DirList and all DirEntries inside.  Releases all list
//      memory and all entries' memory.
// clear()
//      Removes all items in the list.  Releases all list memory and all
//      entries' memory.  Doesn't destroy the DirList object.  Resets error.
// error()
//      Returns error state of the DirList.  
// nullPointersError()
//      True if list contains null pointers.
// operator!=()
//
//
// fromISO()
//      Clears the DirList object before it starts appending
//      dirEntries.  This means you have to use an algo that goes
//      through a directory like this:  read a buffer, process, 
//      write a buffer, repeat.  You can't read all the 
//      dirEntries into the same dirList just by repeated calls
//      to fromISO (like you could if fromISO didn't do a clear).  
//      However you can create a big dirList with the whole directory
//      (if you have the memory for it) by reading in small DirLists
//      and appending onto bigDirList.  All the copying would
//      by time expensive.
//
//      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.  Resets error at start and sets 
//      error based on status of fromISO() procedure at exit.
//      Uses dirEL to verify correctness of tag location field in FID.
//      Assumes start of first extent maps to start of buffer.
//
// FUTURE: JLK. Possible enhancement, add a fromISO that doesn't clear first.
//      Requires a toISO that specifies index in dirList to start writing
//      and returns last index written.
//
// toISO()
//      Returns number of bytes written in buffer.  Starts writing at
//      offset.  Converts DirEntries to FIDs and writes them to buffer.
//      Uses dirEL to write tag location in FID.  Assumes
//      start of first extent maps to start of buffer.
//      If imp. doesn't want to overwrite or underwrite on a toISO(),
//      it needs to compare the return values of fromISO() and ISOlen().
//      Resets error at start and sets error based on status of toISO at exit.
//      ASSUMES:  ToISO assumes buffer.length >= dirList.ISOlen()
//
// USE WITH CARE: Should be noted that we use UINT32 in all UINT64 fields.
// This has repercussions like smaller file lengths.
//
// ISOlen()
//      Returns the number of bytes it would take to write the DirList
//      to a buffer.  That is, returns length of DirList in ISO form.
//      Use this to calculate if you have enough room for toISO().
//      This is useful when you do length changing alterations to 
//      the filename. (...or to the embedded data or the embedded
//      EAs in the Node).  
//
//      NOTE:  If you are rewriting an extent of the directory (a packed
//      file of fids), then unless you are changing the very last dirEntry 
//      you want to make sure that you write the same len of bytes
//      as you read.  This way, you won't overwrite the next dirEntry
//      (if the current entry grows) or make it so that the next entry
//      doesn't start at the next byte after the current entry (if the
//      current entry shrinks).  To avoid this you could write a dir
//      that needed massive changes to a diff location than it was read from.
// operator=()
//      Does a deep copy.
// append()
//      Adds an entry to end of list.  Makes a copy of the entry passed in.
// at()
//      Returns a pointer to an item in the list.
//      Error if index greater than entries().  Starts counting at 0.
//
// FUTURE:  In ver 2.0 we could do a first(), last(), or find() for DirList.
//      Return a ref to the first, last, or specifed item in list.
//
// entries()
//      Returns number of items in list.
// isEmpty()
//      True if there are no entries.
//
//----------------------------------------------------------------------
//
// ISSUE: Possibly Move DirList::free(), DirList::at() to nsrDir.cpp
//
//  DATA MEMBERS:
//  dlist_      the list 
//  allocError_ allocation error
//
//  PRIVATE METHODS:
//  free()
//  copy()
//
//
//  NOTES:  (E.g., special cases, heads-up, and algo discussion) 
//    List functions are based on RWGSList, Rogue Wave Singly Linked List
//    except while RW uses a list of pointers that is referenced-based,
//    we take that list of pointers and make it look value-based.  
//    This means we do deep copies and deep deletes.
//    It also means that during appends to the list we add copies
//    of what you pass in.  
//
//    We won't implement list queries like first().  Use at()
//    or use the iterator object.  The iterator returns pointers to 
//    DirEntries, so you can change stuff in DirEntries.  
//    USE WITH CARE:  Do not delete entries that are pointed to by a list.  
// 
// FUTURE:  Add remove() to DirList?
//    We may want to provide some sort of remove functionality,
//    although this could only properly be used in the last extent of
//    a directory. --DVM  
//    True if you were going to write a directory in the same place
//    you read it from. If, however, you freed that space and rewrote
//    the directory in a diff location, you could do wholesale removes
//    as a form of garbage collection.  To do an algo like this, you
//    would have to read in the whole dir into a huge DirList (instead
//    of reading and flushing it in small buffer-size portions).  Or,
//    you could do some fancy footwork if you wanted to do it in buffers.
//    --JLK
//----------------------------------------------------------------------

declare (RWGSlist, DirEntry)

class DirList {
  public:
    RWGSlist(DirEntry) dlist_;
  private:
    Error allocError_;                  // allocation error on copy
    void free() { // deep deletes, called by destructor & copy 
        static DirEntry *dep;
        const UINT32 listMax = this->entries();
        for (UINT32 i=0; i < listMax; i++) {
            dep = dlist_.get();
            delete dep; 
        } // for
    } // DirList::free()
    void copy( const DirList & dl, Error & err );   // DEEP copy

  public:
    DirList()  : allocError_( error_None )  {}
    DirList(const DirEntry & first)         {append(first, allocError_);}
    DirList( const DirList & dl)            {copy(dl, allocError_);} 
    ~DirList()                              {free();}   // DEEP deletes
// INLINE: DirList::clear()
    void clear() {
        allocError_ = error_None;       
        free();
    } // clear()
    Error error() const                     {return allocError_;}
    Error nullPointersError() const;
    Boolean operator!=(const DirList & other);
    UINT32 fromISO( 
        const ByteArray & buffer, 
        UINT32 offset, 
        const LogExtentList & dirEL, 
        Error & err);
    UINT32 toISO( 
        ByteArray & buffer, 
        UINT32 offset, 
        const LogExtentList &  dirEL, 
        Error & err); 
    UINT32 ISOlen() const;
    DirList & operator=( const DirList & obj) { 
        copy(obj, allocError_);
        return *this;
    }
    void append( const DirEntry & de, Error & err ); 
// INLINE: DirList::at()
    DirEntry* at(UINT32 i, Error & err) {
        err = error_None;
        if (i < dlist_.entries()) {
            DirEntry * tmp = dlist_.at(i);
            if (tmp != NULL) {
                return tmp;
            } // if
            else {
                err = error_Null_pointer;
                return NULL;
            } // else
        } //if
         else {
            err = error_Index_out_of_bounds;
            return NULL;
        }
    } // DirList::at()
    const DirEntry* at(UINT32 i, Error & err) const {
        err = error_None;
        if (i < dlist_.entries()) {
            DirEntry * tmp = dlist_.at(i);
            if (tmp != NULL) {
                return tmp;
            } // if
            else {
                err = error_Null_pointer;
                return NULL;
            } // else
        } //if
         else {
            err = error_Index_out_of_bounds;
            return NULL;
        }
    } // DirList::at()
    UINT32 entries() const      {return dlist_.entries();}
    Boolean isEmpty() const     {return dlist_.isEmpty()? True : False;}

#ifdef DEBUG
    void print(UINT32 indent, const String & header) ;
#endif
#ifdef FUTURE
    // FUTURE:  DE.operator[] as an lvalue..or rather, setAt()
    const DirEntry* first() const;                              // FUTURE
    const DirEntry* last() const;                               // FUTURE
    const DirEntry* find( const DirEntry &  de,err ) const;     // FUTURE
#endif
}; //class DirList

//----------------------------------------------------------------------
//  METHOD:  DirList::clear()                                   --JLK
//    Clears the DirList.
// 
//  Removes all items in the list.  Releases all list memory and all
//  entries' memory.  Doesn't destroy the DirList object.  Resets error.
//
//  ARGS:
//  none
//
//  RETURNS:
//  none
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  List is empty.  All of its items were deleted.  
//  err_ == error_None
//  dlist_ will contain 0 entries.
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  METHOD:  DirList::at()                                      --JLK
//    Returns ptr to DirEntry at given location.
//
//  ARGS:
//  i                       IN      index. 0 <= i < dlist_.entries()
//  err                     OUT     error
//
//  RETURNS:
//  Ptr to DirEntry at location i in the DirList.
//  Or, returns NULL if index is out of bounds or entry is deleted.
// 
//  PRE-CONDS:  (E.g., assumes)
//  0 <= i < dlist_.entries() 
// 
//  POST-CONDS: (E.g., changes to class data members)
//  Returns non-null ptr. if no error.
//  Or, returns NULL if error.
// 
//  ERRORS:
//  error_Index_out_of_bounds   
//  error_Null_pointer
//  error_None
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//  To see if there is an error, check err parameter after return
//  or check to see if return value == NULL.  
// 
//  Algo note:  We could code this to be quicker if we took out the 
//  error_Null_pointer if() statement, but then error would return
//  as error_None for that case.  This is probably okay.
//  For now, the code is written on the safe side (and slow side).
// 
//  Note diff betw DirList dl; dl.at(i, err) and dl.dlist_.at(i)
//  The diff betw our implentation of at() and RogueWave's is that
//  we set an explicit error of our Error object, while they throw an
//  exception of type TOOL_INDEX.
//  
//  Example code:
//  DirList dl;         // dl is a DirList
//  dl.at(i, err);      // at() is in dl's public interface.  HP's at().
//  dl.dlist_.at(i);    // dlist_ is an RWGSlist private to dl.  RW's at().
//
//----------------------------------------------------------------------

//----------------------------------------------------------------------
//  CLASS:  DirListIter                                         --JLK
//   DirList Iterator -- Based on RogueWave GSlist Iter.
//
//  PUBLIC METHODS:
//
// DirListIter
//      Constructs an iterator.  
//      NOTE:  Initially the current item is undefined.
//      You need to use operator()() or next() to get to the first item.
// operator()()
//      Advances the iterator to the next item and returns a pointer to it.  
//      Returns nil if at end of the list.
// operator++()
//      Like next() but doesn't check for end of list.  Returns void.
// next()
//      Advances the iterator to the next item.  Returns FALSE if there
//      is no next item (i.e., at End of List).
// atFirst()
//      Returns TRUE if current position is first element.  Else returns
//      FALSE.  Example:  After 'reset(); next();' atFirst() is True. 
// atLast()
//      Returns TRUE if current position is last element.  
// key()
//      Returns a ptr to the item at the current position.  Returns NULL if at 
//      end of list.  Returns NULL if you use key() right after 
//      the constructor.
// reset()
//      Resets the iterator to its initial state.  
//      NOTE:  You need to use operator()() or next() to get to the first item.
//
//  NOTES:  (E.g., special cases, heads-up, and algo discussion) 
//
//----------------------------------------------------------------------
//  DATA MEMBERS:
//  sli_        the RW slist iterator
//
//----------------------------------------------------------------------
class DirListIter {
    RWGSlistIterator(DirEntry) sli_;

  public:
    DirListIter( DirList & list ) 
        : sli_( list.dlist_ )       {}
    ~DirListIter()                  {}
    DirEntry* operator()()          { return sli_(); }
    void operator++()               { ++sli_; }

// INLINE: DirListIter::next()
    Boolean next() {
        if ( sli_.atLast())
            return False;
        else {
            ++sli_;
            return True;
        }
    } // DirListIter::next()
    Boolean atFirst()       {return (sli_.atFirst() ? True : False);}
    Boolean atLast()        {return (sli_.atLast() ? True : False);}

    DirEntry* key()                 {return sli_.key();}
    void reset()                    {sli_.reset();}
}; //class DirListIter

//----------------------------------------------------------------------
//  METHOD:  DirListIter::next()                                --JLK
//      Advances the iterator to the next item.  Returns False if there
//      is no next item (i.e., at End of List).
//
//  ARGS:
//  none
//
//  RETURNS:
//  False if at end of list when called.
//  True if not at end of list when called.
// 
//  PRE-CONDS:  (E.g., assumes)
//  none
// 
//  POST-CONDS: (E.g., changes to class data members)
//  Iterator is advanced to next item.
// 
//  ERRORS:
//  none        
// 
//  NOTES:  (E.g., special cases, heads-up, & algo discussion)
//
//----------------------------------------------------------------------

#endif











