/*static char *SCCSID = "@(#)dir.c      6.2 91/01/14";*/
#define SCCSID "@(#)dir.c       6.2 91/01/14"
/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 */

/*
 *  DIR.C
 *
 *    DESCRIPTION
 *
 *      Operations on directories and directory entries.
 *
 *
 *    FUNCTIONS DEFINED
 *
 *      FillDE ()   -   Fill in a directory entry from the previous entry
 *                      and the location of the new entry
 *
 *      CopyRawDEtoDE ()    -   Copy the fields from a directory on the disk
 *                              to one of our directory structures.
 *
 *      MakePseudoDE () -   Creates a directory for a non-existant file to
 *                          use as a starting point in a search.
 *
 *      GetParentDE ()  -   This function is called by the path searching
 *                          routines to find the directory entry for the
 *                          search directory.
 *
 *      LastSector ()   -   Compute the last sector containing entries in a
 *                          given directory.
 *
 *      GetNextDE ()    -   Given a directory entry as a starting point,
 *                          find the next entry in the same directory if
 *                          it exists.
 *
 *      GetFirstDE ()   -   Given the Path Entry for a given directory find
 *                          the first directory entry in the file.
 *
 *      FindNextFile () -   Search for the next (or first) file in a
 *                          directory.
 *
 *
 *    REVISION HISTORY (in reverse chronological order)
 *    -----------------------------------------------------------------------
 *   1/21/93    Kathy Muzzio    Added local RamSem for sDirSem to
 *                              correct semaphore hang on return from
 *                              subdirectory to root on Subst/Joined
 *                              drive is mvdm session
 *
 *    9/26/92   Frank Schroeder Modified GetFirstDE and GetNextDE to accept
 *                              flag and ptr parameters.  If flag specified,
 *                              routines will now return RAW_DIR_REC.
 *
 *    4/19/19   Brian Andrew    Modified calls to 'TracePath' to use reduced
 *                              parameters.
 *
 *    4/9/90    Brian Andrew    Removed global string buffers in FillDE.
 *
 *    4/4/90    Brian Andrew    Code review cleanup.  Some cosmetic changes.
 *                                FillDE : check that multiple extents don't
 *                                  exceed maximum file size.
 *                                GetNextDE : Combined test at beginning,
 *                                  replaced forever loop with do loop and
 *                                  exit loop at while by testing usRet.
 *                                FindNextFile : Cleaned up main loop.
 *
 *    3/29/90   Brian Andrew    Bug in 'GetParentDE' : didn't free allocated
 *                              PATH_ENTRY structure.
 *
 *    3/8/90    Brian Andrew    Fixed error in releasing buffer in
 *                              GetNextDE.
 *
 *    1/4/89    Brian Andrew    Reviewed file and cleaned up comments.
 *
 *    12/14/89  Brian Andrew    Added parameter to CanonFileDBCS
 *
 *    11/30/89  Brian Andrew    CopyRawDEtoDE maps ISO associated file
 *                              to DOS system file (is this a good idea)
 *
 *    11/30/89  Brian Andrew    FillDE can generate an error within
 *                              GetNextDE.
 *
 *    11/30/89  Brian Andrew    Return error from FillDE using value
 *                              returned from CopyRawDEtoDE.
 *
 *    11/30/89  Brian Andrew    Returned error code from CopyRawDEtoDE.
 *                              This checks for a directory entry
 *                              which overlaps a sector boundary.
 *
 *    11/14/89  Brian Andrew    Added support for DBCS (CanonFileDBCS,
 *                              StrCmpDBCS, ForwardDirDBCS)
 *
 *    11/2/89   Brian Andrew    Used self directory in GetParentDE.
 *
 *    10/19/89  Brian Andrew    Added use of PE/DE in current directory
 *                              in call to GetParentDE.
 *
 *    10/19/89  Brian Andrew    Removed pdeLastDE from GetParentDE
 *
 *    10/9/89   Brian Andrew    Added last sector for FindBuf
 *
 *    9/25/89   Brian Andrew    Added STAT parameter to 'FindBuf'.
 *
 *    9/19/89   Brian Andrew    Freed structure in GetFirstDE.
 *
 *    9/18/89   Brian Andrew    Specifically freed DirEntry in FindNextFile.
 *
 *    9/17/89   Brian Andrew    Corrected CopyRawDEtoDE to copy 4 bytes not
 *                              3.
 *
 *    9/7/89    Brian Andrew    Modified GetNextDE to return ERROR_NO_MORE_
 *                              FILES
 *
 *    9/2/89    Brian Andrew    File finding functions:
 *                              LastSector, FindFirstFile, GetParentDE
 *
 *    8/24/89   Brian Andrew    Initial working functions:
 *                              GetFirstDE, GetNextDE, CopyRawDEtoDE,
 *                              MakePseudoDE
 *
 *    8/ 4/89   Brian Andrew    Initial Blank Template
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 */

#define INCL_DOSERRORS
#define INCL_DOS
#define INCL_NOPMAPI

#include <os2.h>
#include <fsd.h>
#include <fsh.h>

#include "cdfs.h"
#include "sem.h"
#include "path.h"
#include "vol.h"
#include "file.h"
#include "buffer.h"
#include "mem.h"
#include "heap.h"
#include "message.h"

#ifdef STATS_H
#include "stats.h"
#endif

#include "fsdata.h"
#include "pr_memin.h"
#include "pr_dir.h"
#include "pr_strng.h"
#include "pr_misc.h"
#include "pr_file.h"
#include "pr_path.h"
#include "pr_dbcs.h"
#include "pr_cache.h"
#include "memory.h"

#ifdef DEBUG
#include "pr_DEBUG.h"
#endif

/***    FillDE ()   -   Fill in a directory entry from the previous entry
 *                      and the location of the new entry
 *
 *  DESCRIPTION
 *
 *    We are passed the location of this entry on the disk.  We copy this
 *    location into pdeDstDE.  We then call CopyRawDEtoDE to copy the raw
 *    data to the new directory entry.  We use the previous directory entry
 *    (pdePrevDE) to keep track of multi-extent files.
 *
 *    The return value is the return value from CopyRawDEtoDE, indicating
 *    whether the entire raw directory entry fits in the sector.
 *
 *    We compare the filename from the previous DE with this DE to determine
 *    whether this is a later version of the same file.  Two STRING_BUF
 *    structures are allocated to store the uppercased versions of the
 *    strings.
 *
 *
 *  ENTRY
 *
 *      pdeDstDE    = directory entry to fill in
 *      pdePrevDE   = previous directory entry on the disk
 *      prdeRawDE   = pointer to raw sector data
 *      snThisSec   = sector where directory entry found
 *      soOffset    = offset in sector
 *      fISOVol     = flag indicating if this is an ISO volume
 *
 *
 *  LOCAL
 *
 *      usRet       = Return value
 *      psbPrevStr  = Buffer to uppercase previous DE filename.
 *      psbDestStr  = Buffer to uppercase destination DE filename.
 *
 *
 *  EXIT
 *
 *      pdeDstDE is filled in with the appropriate values.
 *
 *
 *  RETURN
 *
 *      Error code from GetStringBuf if error.
 *      Return value from CopyRawDEtoDE
 */

USHORT FillDE (pdeDstDE, pdePrevDE, prdeRawDE, snThisSector, soOffset, fISOVol)
PDIR_ENTRY      pdeDstDE;
PDIR_ENTRY      pdePrevDE;
PRAW_DIR_REC    prdeRawDE;
SECT_NUM        snThisSector;
SECT_OFFSET     soOffset;
BOOL            fISOVol;
{
        USHORT      usRet;

        /*  Copy the location information, must be done before calling
         *  CopyRawDEtoDE
         */
        pdeDstDE->snSectNum = snThisSector;
        pdeDstDE->soSectOff = soOffset;
        pdeDstDE->snLastSectorDE = pdePrevDE->snLastSectorDE;

        /* Copy the data from the raw directory entry */
        if (!(usRet = CopyRawDEtoDE (pdeDstDE, prdeRawDE, fISOVol)))
        {
                /* Determine the extent information */
                /* If the previous entry was not the last extent, add this to it */
                if (pdePrevDE->fMultiExt)
                {
                        pdeDstDE->cbStartOffset = pdePrevDE->cbStartOffset + pdePrevDE->cbDataLen;
                        pdeDstDE->rsnRecSeq = pdePrevDE->rsnRecSeq + 1;
                        pdeDstDE->cbFileSize = pdePrevDE->cbFileSize + prdeRawDE->cbDataLen;

                        /* Check that maximum file size is not exceeded */
                        if (prdeRawDE->cbDataLen &&
                                (pdeDstDE->cbFileSize <= pdePrevDE->cbFileSize))
                        {
                                /* File size is full 32 bits */
                                pdeDstDE->cbFileSize = MAX_FILE_SIZE;
                        }

                } /* end if add to the last extent */

                /* Else the starting offset is 0, the extent sequence is 1 */
                else
                {
                        pdeDstDE->cbStartOffset = 0;
                        pdeDstDE->rsnRecSeq = 0;
                        pdeDstDE->cbFileSize = prdeRawDE->cbDataLen;

                }

                /*  If this name is the same as the last then remember the most recent
                 *  version for this file.
                 *
                 *  The test condition is that the new file is not a directory,
                 *  and the filenames match after uppercasing.
                 */
                /* If directory then remember this file version as most recent */
                if (pdeDstDE->afFileAttr & DOS_ATTR_DIRECTORY)
                {
                        pdeDstDE->sRecentVer = pdeDstDE->sFileVer;

                } /* end if this is a directory */

                /* Else compare filenames */
                else
                {
                        PSTRING_BUF psbPrevStr;
                        PSTRING_BUF psbDestStr;

                        /* Try to allocate the STRING_BUF structure for the prev DE */
                        if (!(usRet = GetStringBuf (&psbPrevStr)))
                        {
                                /* Try to allocate the STRING_BUF structure for the dest DE */
                                if (!(usRet = GetStringBuf (&psbDestStr)))
                                {
                                        /*
                                         *  If this is a directory or the filename is not the same
                                         *  as the previous filename, then the most recent version
                                         *  of this file is this file.
                                         */
                                        FSH_UPPERCASE ( pdePrevDE->szFileID, sizeof (STRING_BUF),
                                                                        psbPrevStr);

                                        FSH_UPPERCASE ( pdeDstDE->szFileID, sizeof (STRING_BUF),
                                                                        psbDestStr);

                                        /* Check if the file names don't match */
                                        if (StrCmpDBCS (psbPrevStr, psbDestStr))
                                        {
                                                /* Different files, use this version */
                                                pdeDstDE->sRecentVer = pdeDstDE->sFileVer;

                                        }

                                        /* Else filenames match, use the previous version */
                                        else
                                        {
                                                pdeDstDE->sRecentVer = pdePrevDE->sRecentVer;
                                        }

                                        /* Free the string buffer for the destination DE */
                                        FREE_STRING_BUF (psbDestStr);

                                } /* end if successful allocate for dest DE */

#ifdef DEBUG
                                /* Else display error message */
                                else
                                {
                                        dprintf ("\tFillDE : GetStringBuf failed, prev -- %d\n",
                                                                usRet);
                                }
#endif

                                /* Free the string buffer for the previous DE */
                                FREE_STRING_BUF (psbPrevStr);

                        } /* end if successful allocation for prev DE */

#ifdef DEBUG
                        /* Else print debug message */
                        else
                        {
                                dprintf ("\tFillDE : GetStringBuf failed, dest -- %d\n",
                                                        usRet);

                        } /* end else first STRING_BUF alloc failed */
#endif

                } /* end else compare filenames */

        } /* end if no error in CopyRawDEtoDE */

        return usRet;

} /* end FillDE */


/***    CopyRawDEtoDE ()    -   Copy the fields from a directory on the
 *                              disk to one of our directory structures.
 *
 *  DESCRIPTION
 *
 *    We copy the appropriate fields taking into account whether this is
 *    an ISO or HSG volume.  We convert the attributes to OS/2 format
 *    as well as the creation time and date.  We also check whether the
 *    raw directory entry fits entirely within its sector and that the
 *    file name length is a legal value.
 *
 *    The raw directory entry is indicated by a pointer.  The entire
 *    entry has to be contained between this pointer and the end of the
 *    sector.  To find out if the entry fits, we need the length of the
 *    filename and the length of the rest of the entry.  If this is added
 *    to the pointer to the start of the entry, it shouldn't extend
 *    beyond the end of the sector.
 *
 *    The offset of the entry in the sector is already stored in the
 *    destination directory entry.  We use that as a starting point in
 *    the sector.  We first check that the raw directory field which
 *    contains the length of the filename is contained in the sector.
 *    If so, we do the same math to figure if the entire filename is
 *    contained in the sector.  We also check that the filename length
 *    is not beyond the legal limit.
 *
 *    NOTE: The sector number and offset fields in pdeDst must already be
 *    filled in.
 *
 *
 *  ENTRY
 *
 *      pdeDst      = Directory Entry to fill.
 *      prdeRawSrc  = Source raw directory entry.
 *      fISOVol     = Flag indicating if ISO volume.
 *
 *
 *  LOCAL
 *
 *      usRet           = Return value.
 *      soCurOffset     = Current offset in the sector.
 *      pcDateArray     = Pointer to recording time in raw entry.
 *      usYear          = Conversion of ISO/HSG year to DOS/OS2 year format.
 *      afISOFlags      = File attributes flags.
 *      usTempDateTime  = Temp storage for date and time.
 *      fEightThree     = Flag indicating if name is 8.3.
 *
 *
 *  EXIT
 *
 *      The destination directory entry is filled in if no error occurred.
 *
 *
 *  RETURN
 *
 *      NO_ERROR            if copy is successful.
 *      ERROR_INVALID_DATA  if the raw directory entry is invalid.
 */

USHORT CopyRawDEtoDE (pdeDst, prdeRawSrc, fISOVol)
PDIR_ENTRY       pdeDst;
PRAW_DIR_REC     prdeRawSrc;
BOOL             fISOVol;
{
        USHORT      usRet;
        USHORT      soCurOffset;
        UCHAR   FAR *pcDateArray;
        USHORT      usYear;
        UCHAR       afISOFlags;
        USHORT      usTempDateTime;
        BOOL        fEightThree;

        /* Assume that the result is invalid data */
        usRet = ERROR_INVALID_DATA;

        /* Check if the filename length field is within this sector */
        if ((soCurOffset = pdeDst->soSectOff + RAW_DIR_LEN) <= SECTOR_SIZE)
        {
                /* Check if the filename length is legal */
                if (prdeRawSrc->cbFileIDLen < MAX_FILE_ID_LEN)
                {
                        /* Check if the filename is completely within the sector */
                        if ((soCurOffset + prdeRawSrc->cbFileIDLen) <= SECTOR_SIZE)
                        {
                                /* Clear the error condition */
                                usRet = NO_ERROR;

                                /*
                                 *  Call CanonFileDBCS to copy the the file name and strip
                                 *  off the version numbers and store them separately.
                                 *
                                 *  The return value indicates if the name is 8.3
                                 */

                                fEightThree = CanonFileDBCS (
                                                                                pdeDst->szFileID,
                                                                                prdeRawSrc->sFileID,
                                                                                &pdeDst->sFileVer,
                                                                                prdeRawSrc->cbFileIDLen,
                                                                                &(pdeDst->cbVerLen));

                                pdeDst->cbFileIDLen = istrlen (pdeDst->szFileID);

                                /* Check for a legal version number or treat as string */
                                if ((pdeDst->sFileVer == NO_VERSION) && pdeDst->cbVerLen)
                                {
                                        /* Put the ';' character back in */
                                        pdeDst->szFileID[pdeDst->cbFileIDLen] = ';';

                                        /* Adjust both the file length and version length */
                                        pdeDst->cbFileIDLen += (1 + pdeDst->cbVerLen);
                                        pdeDst->cbVerLen = 0;
                                }

                                /* Clean this string */
                                pdeDst->cbFileIDLen = CleanStringDBCS (pdeDst->szFileID,
                                                                                                                pdeDst->cbFileIDLen);

                                /*
                                 *  Fill out the directory entry size/location values from
                                 *  the raw entry
                                 */
                                pdeDst->cbDirLen = prdeRawSrc->cbDirLen;
                                pdeDst->clbXARLen = prdeRawSrc->clbXARLen;
                                pdeDst->lbFileLoc = prdeRawSrc->lbFileLoc;
                                pdeDst->cbDataLen = prdeRawSrc->cbDataLen;

                                /*
                                 *  Store the date and time each into a 16 bit word.
                                 */

                                /* Point to the start of the recording time array */
                                pcDateArray = prdeRawSrc->abRecordTime;

                                /*
                                 *  HSG/ISO year is years from 1900, DOS/OS2 is years from
                                 *  1980.  If the HSG/ISO year is before 1980, we convert
                                 *  it to 1980
                                 */
                                usYear = *pcDateArray++;
                                usYear = usYear > 80 ? usYear - 80 : 0;

                                usTempDateTime = 0;

                                SET_YEAR (usTempDateTime, usYear);
                                SET_MONTH (usTempDateTime, *pcDateArray++);
                                SET_DAY (usTempDateTime, *pcDateArray++);

                                pdeDst->abDirDate = usTempDateTime;

                                usTempDateTime = 0;

                                SET_HOUR (usTempDateTime, *pcDateArray++);
                                SET_MINUTE (usTempDateTime, *pcDateArray++);
                                SET_SECOND (usTempDateTime, *pcDateArray);

                                pdeDst->abDirTime = usTempDateTime;

                                /*
                                 *  File attributes, in writing the DOS/OS2 file attributes
                                 *  the following bit positions are used
                                 *
                                 *  Bit 0 -- Unconditionally set  (READ ONLY)
                                 *  Bit 1 -- Copy Bit 0 ISO       (Hidden/Existance)
                                 *  Bit 2 -- Copy Bit 2 ISO       (Associated/System)
                                 *  Bit 3 -- Cleared
                                 *  Bit 4 -- Copy Bit 1 ISO       (Directory)
                                 *  Bit 5 -- Cleared
                                 *  Bit 6 -- Result of CanonFileDBCS (Determines if name
                                 *                                      exceeds 8.3)
                                 */

                                /* Get the correct file attribute flags */
                                afISOFlags = R_FILE_FLAGS (fISOVol, prdeRawSrc);

                                pdeDst->afFileAttr = (DOS_ATTR_READ_ONLY
                                                                | (afISOFlags & ISO_ATTR_ASSOC)
                                                                | ((afISOFlags & ISO_ATTR_HIDDEN) ?
                                                                        DOS_ATTR_HIDDEN : 0)
                                                                | ((afISOFlags & ISO_ATTR_DIRECTORY) ?
                                                                                DOS_ATTR_DIRECTORY : 0)
                                                                | (fEightThree ? 0 : DOS_ATTR_NEWNAME));

                                pdeDst->fMultiExt =  afISOFlags & ISO_ATTR_MULTI;

                                /*
                                 *  Copy the interleave skip/size and volume serial number.
                                 */

                                pdeDst->clbIntLeaveSize = prdeRawSrc->clbIntLeaveSize;
                                pdeDst->clbIntLeaveSkip = prdeRawSrc->clbIntLeaveSkip;
                                pdeDst->vsnVSSN = prdeRawSrc->vsnVSSN;

                        } /* end if filename is completely within sector */

#ifdef DEBUG
                        else
                        {
                                dprintf ("\tCopyRawDEtoDE:  Filename is beyond sector\n");
                        }
#endif

                } /* end if filename length is legal */

#ifdef DEBUG
                else
                {
                        dprintf ("\tCopyRawDEtoDE:  Filename length is illegal\n");
                }
#endif

        } /* end if length field is within sector */

#ifdef DEBUG
        else
        {
                dprintf ("\tCopyRawDEtoDE:  Filename length is not is sector\n");
        }
#endif

        return usRet;

} /* end CopyRawDEtoDE */


/***    MakePseudoDE () -  Creates a directory for a non-existant file to
 *                         use as a starting point in a search.
 *
 *  DESCRIPTION
 *
 *    This function is used by GetFirstDE.  GetNextDE uses
 *    a directory entry as a starting point of the search.  Rather
 *    than deal with a special case for GetNextDE we create
 *    this pseudo directory entry, which when used as a starting
 *    point with GetNextDE will return the first directory entry
 *    in the directory.
 *
 *    We use a path entry to generate a pseudo entry that will cause the
 *    the search to begin at the start of the entries in the directory.
 *    The created entry is located at the start of the directory and has
 *    a data length of 0.
 *
 *
 *  ENTRY
 *
 *      pdeDst   = directory entry to fill in
 *      ppeSrc   = path entry structure for directory
 *      cbLbSize = logical block size
 *
 *
 *  LOCAL
 *
 *      None
 *
 *
 *  EXIT
 *
 *      No return value.
 */

VOID MakePseudoDE (pdeDst, ppeSrc, cbLbSize)
PDIR_ENTRY   pdeDst;
PPATH_ENTRY  ppeSrc;
USHORT       cbLbSize;
{
        /* Set starting position at the position in the path entry */
        pdeDst->snSectNum = lb_to_ls (ppeSrc->lbLocDir + ppeSrc->clbXAR_Len,
                                                                 cbLbSize);
        pdeDst->soSectOff = lb_offset (ppeSrc->lbLocDir + ppeSrc->clbXAR_Len,
                                                                 cbLbSize);

        /* Show that the length of this entry is 0 */
        pdeDst->cbDirLen = 0;
        pdeDst->cbFileSize = 0;

        /* Set the filename length to 0 */
        pdeDst->cbFileIDLen = 0;

        /* Show that this is the last of a multi-extent */
        pdeDst->afFileAttr = 0;
        pdeDst->fMultiExt = FALSE;

} /* end MakePseudoDE */


/***    GetParentDE ()  -   Find directory entry for parent directory.
 *
 *  DESCRIPTION
 *
 *      We're given a path name and current directory information.  We
 *      look for the directory entry and the path entry for the parent of
 *      the full pathname.
 *
 *      NOTE : We know that the pathname does not represent the
 *      root directory, but the parent directory of the target file can
 *      be the root directory.
 *
 *
 *  ENTRY
 *
 *      pvddVolData = Pointer to volume dependent data.
 *      pcdfsi      = Pointer to directory independent information.
 *      pdeFirstDE  = Directory entry to fill in.
 *      cbBaseOffset = Index into the path name of the end of the path.
 *      pszName     = Full path name.
 *      cbNameLen   = Length of string.
 *      iCurDirEnd  = Index in path name of end of current directory.
 *      pdddDirData = Pointer to directory dependent structure.
 *
 *
 *  LOCAL
 *
 *      pnSearchRoot    = Path entry number of the parent directory.
 *      cbPathOffset    = Offset in the path name where the Path Table
 *                        search will start.
 *      fUseCurDirPE    = Flag indicating whether PE in DirDepData may
 *                        be used.
 *      pdeNewDE        = DE used if attaching DE to current directory PE
 *      ppeThisDirPE    = Path Entry structure to fill in for parent.
 *
 *
 *  EXIT
 *
 *      Error returned from allocation routines if failure.
 *      Error returned from PathEntryNum if failure.
 *      Error returned from TracePath if failure.
 *      Error returned from FindNextFile if failure.
 *
 */

USHORT GetParentDE (pvddVolData, pcdfsi, pdeFirstDE, cbBaseOffset, pszName,
                                                cbNameLen, iCurDirEnd, pdddDirData)
PVOL_DEP_DATA       pvddVolData;
PCDFSI              pcdfsi;
PDIR_ENTRY          pdeFirstDE;
USHORT              cbBaseOffset;
UCHAR           FAR *pszName;
USHORT              cbNameLen;
USHORT              iCurDirEnd;
PDIR_DEP_DATA       pdddDirData;
{
        USHORT          usRet;
        PATH_NUMBER     pnSearchRoot;
        USHORT          cbPathOffset;
        BOOL            fUseCurDirPE;
        PDIR_ENTRY      pdeNewDE;
        PPATH_ENTRY     ppeThisDirPE;
        SEM             sDirSem = 0;            // @5235

        /* Get the Path Entry structure */
        if (!(usRet = GetPathEntry (&ppeThisDirPE)))
        {

                /*
                 *  In order to search we must know the location of the directory
                 *  and the length of the directory.  The location can be found
                 *  by looking up the directory to search in the Path Table.
                 *  This uses the same technique as FS_CHDIR.
                 *
                 *  The length of the directory (or more important, the last
                 *  sector to search in) is a bit more complicated.
                 *
                 *  If the directory to search in is the root, then we can use
                 *  the entry for the root directory in the VOL_DEP_DATA area.
                 *
                 *  If it's not the root directory, then we need to examine the
                 *  first entry in the search directory.  It will be the self
                 *  entry.
                 *
                 *  With the Path Entry for the directory, we have the
                 *  information we need to perform a GetFirstDE.
                 *
                 *  If we have the root then simply copy the root directory
                 *  from the volume.
                 *
                 */

                /* If we can use the current directory, save the parent number */

                if ((iCurDirEnd != NO_PREV_DIR) &&
                        (pcdfsi->cdi_flags & CDI_ISVALID))
                {
                        pnSearchRoot = pdddDirData->ppePathEntry->pnThisEntry;
                        cbPathOffset = iCurDirEnd - 1;
                        fUseCurDirPE = TRUE;

                        /* Grab the semaphore */
                        SEM_REQ (sDirSem);      // @5235  use local stack RamSEM

                } /* end if can use current directory */

                /* Else start searches from root */
                else
                {
                        pnSearchRoot = ROOT_DIR_NUM;
                        cbPathOffset = ForwardDirDBCS (pszName, cbNameLen);
                        fUseCurDirPE = FALSE;

                } /* end else start at root of path */

                /*
                 *  At this point the variables have the following values
                 *
                 *      cbPathOffset = offset in name where the search path begins
                 *          (will always step over drive name, but can use current
                 *           directory).  Will always point to a '\' character.
                 *
                 *      cbBaseOffset = offset in name where the basic search string
                 *          is.  This points to the '\' before the final name
                 *          in the path (may include wildcards).
                 *
                 *      pnSearchRoot = directory number in Path Table where search
                 *          will begin.  If we are searching in the root or the
                 *          current directory then this is the directory number
                 *          of the search directory.  This condition is true if
                 *          (cbPathOffset == cbBaseOffset)
                 *
                 *      cbNameLen = total length of the name (includes drive)
                 */

                /* Check for special cases where we know the search directory */
                /* Modify UseCurDir flag to indicate that search DE is found */

                /* Check if the search directory is the root directory */
                if ((cbPathOffset == cbBaseOffset) && (pnSearchRoot == ROOT_DIR_NUM))
                {
                        /*
                         *  Use the root DE in the volume descriptor.
                         */

                        /* Copy the root DE */
                        *pdeFirstDE = *(pvddVolData->pdeRootDir);

                } /* end if parent is root directory */


                /* Else find the path entry in the Path Table, use that to find dir */
                else
                {
                        /*
                         *  The first thing we need is the path entry from the Path Table.
                         *
                         *  If we know the path number we can look it up directly.
                         */
                        if (cbPathOffset == cbBaseOffset)
                        {
                                /* Store the search dir PE in ppeThisDirPE */
                                *ppeThisDirPE = *(pdddDirData->ppePathEntry);

                        } /* end if know number of parent directly */

                        /* Else look up in Path Table */
                        else
                        {
                                usRet = TracePath (pvddVolData,
                                                                        pnSearchRoot, pszName + cbPathOffset,
                                                                        cbBaseOffset - cbPathOffset,
                                                                        ppeThisDirPE,
                                                                        pcdfsi->cdi_hVPB);

                                /* Show that we can't use the CurDirPE */
                                /* Clear the semaphore if we have set it */
                                if (fUseCurDirPE)
                                {
                                        SEM_CLEAR (sDirSem);    // @5235  use local stack RamSEM
                                        fUseCurDirPE = FALSE;

                                } /* end if holding semaphore */

                        } /* end else search through table */

                        /*
                         *  We now have the Path Entry for the search directory.
                         *  It is guaranteed not to be the root directory or we
                         *  would have discovered that already.
                         *
                         *
                         *  If the search directory is the current directory, a copy of
                         *  of the DE may be stored with the current directory information.
                         *  If not, we update with the DE we now get.
                         *
                         *  The directory entry we return is the self directory, the
                         *  first entry in a directory.
                         */

                        /* Make sure no errors have occurred */
                        if (!usRet)
                        {
                                /*
                                 *  If we are searching through the current directory
                                 *  and the DE is stored.  Then we can use it directly.
                                 */
                                if (fUseCurDirPE && pdddDirData->ppePathEntry->pdeDirEntry)
                                {
                                        *pdeFirstDE = *(pdddDirData->ppePathEntry->pdeDirEntry);

                                } /* end if required DE already exists */

                                /* Look up the first entry in the directory */
                                else if (!(usRet = GetFirstDE (
                                                                                pdeFirstDE,
                                                                                ppeThisDirPE,
                                                                                pvddVolData,
                                                                                pcdfsi->cdi_hVPB,
                                                                                NULL,
                                                                                NULL)))
                                {
                                        /*
                                         *  If no error has occurred and fUseCurDir is set,
                                         *  we want to save the DE we just found.  We attempt
                                         *  to allocate a DE, copy into it and attach to
                                         *  the CurDirPE.
                                         */
                                        if (fUseCurDirPE &&
                                                !(GetDirEntry (&pdeNewDE)))
                                        {
                                                *pdeNewDE = *pdeFirstDE;
                                                pdddDirData->ppePathEntry->pdeDirEntry = pdeNewDE;

                                        } /* end if attempt to attach new DE */

                                        /* Free the semaphore if it is being held */
                                        if (fUseCurDirPE)
                                        {
                                                SEM_CLEAR (sDirSem);    // @5235  use local stack RamSEM

                                        } /* end if semaphore is being held */

                                } /* end else if get first DE in directory */

#ifdef DEBUG
                                else
                                {
                                        dprintf ("\tGetParentDir:  Can't get the DE  --  %d\n",
                                                                usRet);
                                }
#endif

                        } /* end if no errors */

                } /* end else find directory through path entry */

                /* Free the path entry */
                FREE_PATH_ENTRY (ppeThisDirPE);

        } /* end if get path entry */

#ifdef DEBUG
        else
        {
                dprintf ("\tGetParentDir:  Can't get the DE  --  %d\n",
                                        usRet);

        }
#endif

       /* Free the semaphore if it is being held */
       if (sDirSem)                     // @5235 use loacl stack RamSEM
       {
               SEM_CLEAR (sDirSem);    // @5235  use local stack RamSEM

       } /* end if semaphore is being held */

        return usRet;

} /* end GetParentDE */


/***    LastSector ()   -   Compute the last sector containing entries in
 *                          a given directory.
 *
 *  DESCRIPTION
 *
 *      Directories end on sector boundaries.  This function computes
 *      the last sector for a directory specified by a DIR_ENTRY
 *      structure.
 *
 *      The algorithm is to take the starting logical block of the
 *      directory, and add any logical blocks in the XAR.
 *
 *      Then compute the sector and offset of that starting logical
 *      block.  Add to the offset, the length in bytes of the directory
 *      data.
 *
 *      To find the number of sectors to add to the base sectors, subtract
 *      1 from the total bytes in data and offset, then divide by the
 *      sector size.
 *
 *      Store the last sector in the DE as well.
 *
 *
 *  ENTRY
 *
 *      pdeDirEntry = Compute the last sector of this directory entry.
 *      cbLBSize    = Logical block size of this volume.
 *
 *
 *
 *  LOCAL
 *
 *      lbLogicalBlock  = First logical block after any XAR logical blocks.
 *      cbBytes     = This contains the number of bytes after the last
 *                    sector boundary in the XAR section.  It is the sum
 *                    of the size of the directory and any spillover bytes
 *                    in the XAR blocks.
 *
 *
 *  EXIT
 *
 *      Return the last sector for this entry.
 */

VOID LastSector (pdeDirEntry, cbLBSize)
PDIR_ENTRY  pdeDirEntry;
USHORT      cbLBSize;
{
        LOGICAL_BLK lbLogicalBlock;
        ULONG       cbBytes;

        lbLogicalBlock = pdeDirEntry->lbFileLoc + pdeDirEntry->clbXARLen;

        pdeDirEntry->snLastSectorDE = lb_to_ls (lbLogicalBlock, cbLBSize);

        cbBytes = lb_offset (lbLogicalBlock, cbLBSize) + pdeDirEntry->cbFileSize;

        pdeDirEntry->snLastSectorDE += ((cbBytes - 1)/SECTOR_SIZE);

} /* End LastSector */


/***    GetNextDE ()    -   Get the next directory entry if it exists.
 *
 *  DESCRIPTION
 *
 *      This function uses an a previous Directory Entry for a starting
 *      point and finds the next directory entry.  It then condenses the
 *      information and stores it in a previously allocated directory
 *      entry.
 *
 *      One of the fields of a DE is the last sector which can contain a
 *      directory entry.  The end of a directory is reached when all the
 *      remaining bytes in that directory are null bytes.
 *
 *      A directory is a sequence of consecutive sectors on the disk.  The
 *      entire directory may have an XAR block(s).  The XAR length is in
 *      LBN, so the main part of the directory will begin on an LB boundary
 *      after that.
 *
 *      A directory consists of several directory entries, the first two are
 *      for the parent and self directories.  Directory entries can't span
 *      a sector boundary, any unused bytes will be '\0'.
 *
 *      This function uses the sector number, sector offset and directory
 *      entry length of the previous directory entry to compute where the
 *      next possible directory entry may start.
 *
 *      If the next offset is the logical sector size (SECTOR_SIZE) or
 *      the byte at that offset is '\0', then the next sector is needed.
 *      If current sector is the last sector, then the directory has been
 *      exhausted.
 *
 *      If a new directory entry is found, the information in it is stored
 *      in the target directory entry passed.  In addition, the sector
 *      location and offset as well as offset in the file of the first
 *      byte in the extent controlled by this entry.  We will also keep
 *      track of the the most recent version number of this file name.
 *
 *
 *  ENTRY
 *
 *      pdeDstDe        = Directory entry to fill in.
 *      pdePrevDE       = Directory entry to start search from.
 *      pvddVolData     = Pointer to volume dependent handle.
 *      hVPB            = Volume parameter block handle.
 *      snLastSector    = Last sector to search in.
 *      usParmFlags     = Bit 0: 0 = do not return RAW_DIR_REC
 *                               1 = return RAW_DIR_REC from disc
 *      prdrRawDirRec   = Pointer to the RAW_DIR_REC structure to fill in
 *                        (only valid when usParmFlags bit 0 = 1)
 *
 *
 *  LOCAL
 *
 *       usRet          = Return value from this function.
 *       snThisSector   = Current sector being examined.
 *       soOffset       = Offset in current sector.
 *       pbThisBuf      = Current buffer.
 *       prdeRawDE      = Raw directory entry pointer into buffer.
 *
 *
 *  EXIT
 *
 *      NO_ERROR if directory entry found.
 *      ERROR_NO_MORE_FILES if beyond last sector.
 *      ERROR_INVALID_DATA if error returned from FillDE.
 *      Return value from FindBuf if failure.
 *
 */

USHORT GetNextDE (pdeDstDe, pdePrevDE, pvddVolData, hVPB, usParmFlags, prdrRawDirRec)

PDIR_ENTRY      pdeDstDe;
PDIR_ENTRY      pdePrevDE;
PVOL_DEP_DATA   pvddVolData;
SHANDLE         hVPB;
USHORT          usParmFlags;
PRAW_DIR_REC    prdrRawDirRec;
{
        USHORT          usRet;
        SECT_NUM        snThisSector;
        SECT_OFFSET     soOffset;
        PBUF            pbThisBuf;
        PRAW_DIR_REC    prdeRawDE;

        /* Initialize */
        snThisSector = pdePrevDE->snSectNum - 1;
        soOffset = pdePrevDE->soSectOff + pdePrevDE->cbDirLen;

        /* Check overlapping sector */
        if (soOffset >= SECTOR_SIZE)
        {
                soOffset = 0;
                snThisSector++;
        } /* end if overlap at start */

        for (;;)
        {
                /*
                 *  Check if past the last sector, 'snSector' is one less
                 *  than the current sector at this point.
                 */
                if (snThisSector >= pdePrevDE->snLastSectorDE)
                {
                        /* Return an error */
                        usRet = ERROR_NO_MORE_FILES;

                        break;
                }

                /* Get desired sector */
                if (!(usRet = FindBuf (pvddVolData, TRUE, ++snThisSector,
                                                        hVPB, &pbThisBuf,
                                                        pdePrevDE->snLastSectorDE
#ifdef STATS_H
                                                        , DIR_SEC
#endif
                                                                                )))
                {
                        /*
                         *  At this point the following variables have the following values
                         *
                         *      snThisSector  --  The sector in the buffer 'pbThisBuf'
                         *      soOffset      --  The offset to next raw directory
                         *      pbThisBuf     --  The buffer containing the current sector
                         *
                         *  We will keep getting the next buffer if at all possible.  Each
                         *  buffer must be released when finished with.
                         */

                        /* Overlay the sector */
                        prdeRawDE = (RAW_DIR_REC FAR *)
                                        OVERLAY_STRUCT (pbThisBuf, soOffset);

                        /* If the first byte is zero, then no more entries in this sector */
                        if (!*((UCHAR FAR *) prdeRawDE))
                        {
                                /* Release the current buffer */
                                RELEASE_BUF (pbThisBuf);

                                /* Start at beginning of the next sector */
                                soOffset = 0;
                                continue;

                        } /* end if first byte zero */

                        /* Else copy in the new data and exit */
                        else
                        {
                                usRet = FillDE (pdeDstDe, pdePrevDE, prdeRawDE,
                                                                snThisSector, soOffset,
                                                                pvddVolData->pvdVolDesc->fISOVol);

                                if (usParmFlags == 0x0001)     /* return raw directory record */
                                  memcpy (prdrRawDirRec, prdeRawDE, prdeRawDE->cbDirLen);

                                RELEASE_BUF (pbThisBuf);
                                break;

                        }

                } /* end if found sector */

#ifdef DEBUG
                /* Else print error message */
                else
                {
                        dprintf ("\tGetNextDE:  Couldn't get sector %d  --  %d\n",
                                        snThisSector, usRet);

                } /* end else if couldn't find another buffer */
#endif

                /* Exit the loop */
                break;

        } /* end forevever loop */

        return usRet;

} /* end GetNextDE */


/***    GetFirstDE ()   -   Get the first directory entry within a directory.
 *
 *  DESCRIPTION
 *
 *      This function will fill in the pdeDstDE structure with the data
 *      from the first directory entry indicated by the the Path Table
 *      entry 'ppePathEntry'.  Rather than write code to specifically
 *      search for the first directory, we create a pseudo directory
 *      which when passed to 'GetNextDE' will cause the first entry in
 *      the directory to be returned.
 *
 *
 *  ENTRY
 *
 *      pdeDstDE        = Directory entry to fill in
 *      ppePathEntry    = Path entry for the for the directory to search
 *      pvddVolData     = Pointer to the volume dependent data.
 *      hVPB            = Handle for device access
 *      usParmFlags     = Bit 0: 0 = do not return RAW_DIR_REC
 *                               1 = return RAW_DIR_REC from disc
 *      prdrRawDirRec   = Pointer to the RAW_DIR_REC structure to fill in
 *                        (only valid when usParmFlags bit 0 = 1)
 *
 *
 *  LOCAL
 *
 *      usRet           = Return value.
 *      pdePseudoDE     = Pointer to pseudo directory being created.
 *
 *
 *  EXIT
 *
 *      Return value from allocation functions if failure.
 *      Return value from GetNextDE otherwise.
 *
 */

USHORT GetFirstDE (pdeDstDE, ppePathEntry, pvddVolData, hVPB, usParmFlags, prdrRawDirRec)

PDIR_ENTRY      pdeDstDE;
PPATH_ENTRY     ppePathEntry;
PVOL_DEP_DATA   pvddVolData;
SHANDLE         hVPB;
USHORT          usParmFlags;
PRAW_DIR_REC    prdrRawDirRec;
{

        USHORT      usRet;
        PDIR_ENTRY  pdePseudoDE;

        /*
         *  Try to allocate a directory entry structure
         */
        if (!(usRet = GetDirEntry (&pdePseudoDE)))
        {
                /* Create the mock directory entry */
                MakePseudoDE (
                        pdePseudoDE,
                        ppePathEntry,
                        pvddVolData->pvdVolDesc->cbLogBlk);
                pdePseudoDE->snLastSectorDE = MAX_SECTOR;

                /* Call FindNext with the Pseudo directory entry */

                usRet = GetNextDE (pdeDstDE, pdePseudoDE, pvddVolData, hVPB,
                                   usParmFlags, prdrRawDirRec);

                /* Compute the last DE of the sector */
                LastSector (pdeDstDE, pvddVolData->pvdVolDesc->cbLogBlk);

                /* Free the pseudo directory */
                FREE_DIR_ENTRY (pdePseudoDE);

        } /* end if allocate pseudo directory entry */

        /* Else print message */
#ifdef DEBUG
        else
        {
                dprintf ("\tGetFirstDE:  Can't allocate Pseudo directory -- %d\n");

        } /* end else allocation error */
#endif

        return usRet;

} /* end GetFirstDE */


/***    FindNextFile () -   Searches for the next file in a directory.
 *
 *  DESCRIPTION
 *
 *      This function returns the next (or first) directory entry in
 *      a target directory.  For the first directory entry, it uses
 *      the arguement path entry for a starting point.  Otherwise
 *      it uses the previous DE as a starting point.
 *
 *      If the first DE is required, we call 'GetFirstDE' with the
 *      path table as an arguement.  If we have to use the previous
 *      DE, it is possible that the previous DE is but the first entry
 *      of a multi-extent file.  In that case, we swallow all the
 *      subsequent extents in the same file and finally get the first
 *      entry for the next file.
 *
 *      We now have a pointer to the first DE for the next file.  If this
 *      is a multi-extent file, we also march through the subsequent
 *      extents.  We update the size of the file via the pointer to the
 *      first extent.  We now have the DE for the first and last extent
 *      of a file, provided that no error occurred.  It's possible that
 *      this file spans several volumes.  In that case, we step over it
 *      and try to get the next file.
 *
 *
 *  ENTRY
 *
 *      pString     = Search string (with wildcards)
 *      afFileAttr  = Attributes for this search
 *      pdeFirstDE  = Fill in, first entry of next file/dir
 *      pdePrevDE   = Starting location
 *      fFirstFile  = Flag is TRUE if this is first file in directory
 *      pvddVolData = Pointer to volume dependent data
 *      hVPB        = Handle for VPB
 *
 *
 *  LOCAL
 *
 *      usRet           = Return value from this function.
 *      pdeCurrentDE    = Directory entry for previous file.
 *      pdeLastDE       = Last directory entry found.
 *      pdeTempDE       = Temp pointer for performing swap.
 *      pdeCurPermDE    = Permanent pointer to DE.
 *      pdeLastPermDE   = Permanent pointer to DE.
 *      pdeFirstDELoc   = Local pointer to first DE of file.
 *      fDiffVol        = Flag indicating if any DE's for the current file
 *                        are on another volume.
 *      psbStringBuf    = Pointer to STRING_BUF, used in 'FileMatch'.
 *      vsnVSSN         = Volume sequence number for entire volume.
 *
 *
 *  EXIT
 *
 *      Return value from allocation routines if failure.
 *      Return value from either GetFirstDE/GetNextDE otherwise.
 */

USHORT FindNextFile (pString, afFileAttr, pdeFirstDE, pdePrevDE,
                                                 fFirstFile, pvddVolData, hVPB)

UCHAR           FAR *pString;
USHORT              afFileAttr;
PDIR_ENTRY          pdeFirstDE;
PDIR_ENTRY          pdePrevDE;
BOOL                fFirstFile;
PVOL_DEP_DATA       pvddVolData;
SHANDLE             hVPB;

{
        USHORT          usRet;
        PDIR_ENTRY      pdeCurrentDE;
        PDIR_ENTRY      pdeCurPermDE;
        PDIR_ENTRY      pdeLastDE;
        PDIR_ENTRY      pdeLastPermDE;
        PDIR_ENTRY      pdeTempDE;
        PDIR_ENTRY      pdeFirstDELoc;
        BOOL            fDiffVol;
        PSTRING_BUF     psbStringBuf;
#ifdef MULTI_VOLUME
        VOL_SEQ_NUM     vsnVSSN;
#endif

        /* Allocate a DirEntry to store the most recent entry found */
        if (!(usRet = GetDirEntry (&pdeLastPermDE)))
        {
                if (!(usRet = GetDirEntry (&pdeCurPermDE)))
                {
                        /* Get the string buffer structure */
                        if (!(usRet = GetStringBuf (&psbStringBuf)))
                        {
                                pdeTempDE = pdePrevDE;
                                pdeFirstDELoc = pdeFirstDE;

                                /* Remember the DE blocks just allocated */
                                pdeCurrentDE = pdeCurPermDE;
                                pdeLastDE = pdeLastPermDE;

#ifdef MULTI_VOLUME
                                vsnVSSN = pvddVolData->pvdVolDesc->vsnSNum;
#endif

                                /*
                                 *  Determine if should use pdePrevDE as the first DE or if it is
                                 *  from a previous search.
                                 */

                                if (fFirstFile)
                                {
                                        fFirstFile = FALSE;

                                        *pdeFirstDE = *pdePrevDE;

                                        /* Jump to common point in while loop */
                                        goto FindNextFile_HaveFirstDE;

                                } /* end if get the first DE */

                                /*
                                 *  We have a grip on a DE of a previous file, if this is a
                                 *  multi-extent file we need to walk to the last extent.
                                 *  Then to start the next search we need to get the next DE
                                 */
                                else
                                {
                                        while ( pdeTempDE->fMultiExt &&
                                                        !(usRet = GetNextDE (
                                                                                        pdeCurrentDE,
                                                                                        pdeTempDE,
                                                                                        pvddVolData,
                                                                                        hVPB,
                                                                                        NULL,
                                                                                        NULL)))
                                        {
                                                /* Copy the last over to current */
                                                pdeTempDE = pdeCurrentDE;
                                                pdeCurrentDE = pdeLastDE;
                                                pdeLastDE = pdeTempDE;

                                        } /* end while walking through multiextent */

                                } /* end else walk through previous file */

                                /*
                                 *  We loop forever until we can find a complete file contained on
                                 *  this volume or we encounter an error.
                                 */

                                while (!usRet)
                                {
                                        /*
                                         *  If no error, then get the next DE.  This is the first
                                         *  in the next file.
                                         */
                                        if (usRet = GetNextDE (
                                                                                pdeFirstDELoc,
                                                                                pdeTempDE,
                                                                                pvddVolData,
                                                                                hVPB,
                                                                                NULL,
                                                                                NULL))
                                        {
                                                /* Exit from the do loop */
                                                break;
                                        }

                                        /* Else adjust the pointers */
                                        else
                                        {
                                                pdeTempDE = pdeFirstDELoc;

                                        } /* end else adjust the pointers */

/* Common jump point after obtaining first DE for a file */
FindNextFile_HaveFirstDE:

                                        /* Show that next file is not on different volume */
                                        fDiffVol = FALSE;

                                        /*
                                         *  At this point 'pdeTempDE' and 'pdeFirstDELoc' point
                                         *  to the first DE of the next file and 'pdeCurrentDE'
                                         *  and 'pdeLastDE' point to the other local DE structures.
                                         */
#ifdef MULTI_VOLUME
                                        /* Check for different volume */
                                        if (pdeTempDE->vsnVSSN != vsnVSSN)
                                        {
                                                fDiffVol = TRUE;
                                        }
#endif

                                        while ( pdeTempDE->fMultiExt &&
                                                        !(usRet = GetNextDE (
                                                                                        pdeCurrentDE,
                                                                                        pdeTempDE,
                                                                                        pvddVolData,
                                                                                        hVPB,
                                                                                        NULL,
                                                                                        NULL)))
                                        {
                                                /* Check if on this volume */
                                                if (pdeTempDE->vsnVSSN != pdeCurrentDE->vsnVSSN)
                                                {
                                                        fDiffVol = TRUE;

                                                }

                                                /* Copy the last over to current */
                                                pdeTempDE = pdeCurrentDE;
                                                pdeCurrentDE = pdeLastDE;
                                                pdeLastDE = pdeTempDE;

                                        } /* end while more extents */

                                        /*
                                        *  At this point there are three possibilities:
                                        *
                                        *      1) We have the first and last entries for a
                                        *         file on on this volume only.
                                        *
                                        *      2) We have the first and last entries for a
                                        *         file on another volumes besides this one
                                        *
                                        *      3) Some error was generated in searching
                                        *
                                        *  If 3 then we are done, if 2 then we skip over this
                                        *  file and try to find another one.  If 1, then
                                        *  we check to see if the file attributes match.
                                        */

                                        /* Check for errors */
                                        if (usRet)
                                        {
                                                break;

#ifdef DEBUG
                                                dprintf ("\tFindNextFile:  Can't find the first entry  --  %d\n",
                                                                        usRet);
#endif
                                        }

                                        /* Else check for match if this is single volume */
                                        if ((!fDiffVol) &&
                                                FileMatch ( pString, afFileAttr,
                                                                        pdeFirstDELoc, psbStringBuf))
                                        {
                                                /* Copy the file size over */
                                                pdeFirstDELoc->cbFileSize = pdeTempDE->cbFileSize;

                                                /* Copy the DE to the first DE */
                                                if (pdeFirstDE != pdeFirstDELoc)
                                                {
                                                        *pdeFirstDE = *pdeFirstDELoc;
                                                }

                                                break;

                                        } /* end else if found complete file */

#ifdef DEBUG
                                        else if (fDiffVol)
                                        {
                                                dprintf ("\tFindNextFile: Multi-volume file found\n");
                                        }
#endif

                                        /* Prepare to search for the next file */
                                        pdeFirstDELoc = pdeLastDE;
                                        pdeLastDE = pdeTempDE;

                                } /* end forever loop */

                                /* Free the STRING_BUF structure */
                                FREE_STRING_BUF (psbStringBuf);

                        } /* end if psbStringBuf allocation failed */

#ifdef DEBUG
                        /* Else print out error */
                        else
                        {
                                dprintf ("\tFindNextFile: STRING_BUF alloc failed -- %d\n",
                                                        usRet);
                        }
#endif

                        /* Free the local structures */
                        FREE_DIR_ENTRY (pdeCurPermDE);

                } /* end if pdeCurrentDE allocation succeeded */

#ifdef DEBUG
                else
                {
                        dprintf ("\tFindNext:  Can't allocate current entry  --  %d\n",
                                        usRet);

                } /* end if can't get LastDE */
#endif

                FREE_DIR_ENTRY (pdeLastPermDE);

        } /* end if pdeLastDE allocation succeeded */

#ifdef DEBUG
        else
        {
                dprintf ("\tFindNext:  Can't allocate last entry  --  %d\n",
                                 usRet);

        } /* end if can't get LastDE */
#endif

        return usRet;

} /* end FindNextFile */
