/*static char *SCCSID = "@(#)fs_fnext.c 6.3 92/01/31";*/
#define _BTXT _BTXT

#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <cdib.h>

#include "heap.h"
#include "api.h"
#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "find.h"
#include "storage.h"
#include "volume.h"
#include "tables.h"
#include "pb_ifs.h"
#include "pbmacros.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "tefti.h"
#include "ea.h"
#include "assert.h"
#include "log.h"
#include "codepage.h"
#include "pr_fsd.h"
#include "pr_CACHE.h"
#include "pr_FIND.h"
#include "pr_DIR.h"
#include "pr_MISC.h"
#include "pr_HEAP.h"
#include "pr_DEBUG.h"


/*
 ***    FS_FINDNEXT - Continue File Search
 *
 *      FindNext continues a file search sequence.
 *
 *      FindNext locates the continuation spot and then calls FindMatch
 *      to fill up the return buffer; see FindMatch for additional information.
 *
 *      There are two methods for locating our continuation spot:
 *      a) Sequential FindNext - use pointers to the last visited
 *              entry (dir block sector pointer and dir entry offset).
 *              We must ensure that the dir block is in the cache.
 *      b) Replay Find - use the last name returned to relocate the our
 *              position in the directory.  This involves calling
 *              FindDir and LDE to locate the last entry returned.
 *              We resume our search starting with the next entry.
 *
 *      During FindFirst and FindNext the directory is held so that
 *      it will not change while we are looking at it.  However, the
 *      directory may change between find calls.  If the directory
 *      has not changed then we can do a sequential FindNext, otherwise
 *      we must replay find.
 *
 *      Findnext doesn't need to hold directory entrys since it doesn't
 *      ever yield while it's working on a directory block.  We yield
 *      when we move to a new block, but at that point we're done with the
 *      old block.  If we need it again we'll read it again.  We also yield
 *      when we need to probe the return buffer address but we'll hold
 *      the block before and after we probe.
 *
 *      HISTORY
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *          26-May-1989 by JR (John Rogers, JohnRo@Microsoft)
 *              - DCR 259.2: changed names of macros in "frame.h".
 */

int far pascal
FS_FINDNEXT (
    struct fsfsi far *  pfsfsi,
    struct fsfsd far *  pfsfsd,
    PCHAR               pbData,     /* ptr to data buffer */
    USHORT              cbData,     /* size of data buffer */
    PUSHORT             pcMatch,     /* number of matching entries returned */
    USHORT              usLevel,
    USHORT              usFlags
    )
{
    PFWORK      pfw;        /* ptr to FWORK structure (search handle) */
    PBUFNODE    pbuf;       /* ptr to buffer node */
    PDIRBLK     pdb;        /* ptr to dir block of next dir entry */
    PDIRENT     pde;        /* ptr to next dir entry */
    PSUBDIR     psbd;       /* ptr to subdirectory from previous find */
    USHORT      usError;    /* return code */
    PEAOP       peaop;      /* eaop from caller's buffer */
    FRAME       frm;

    frm.usHardErr = NO_ERROR;
    frm.fRecheckAddr = TRUE;
    frm.fFlag = 0;               // Clear Frame flag  @132506

#ifdef TEFTI
    ClearCounter (0);
    StartCounter (0);
#endif

    if ((frm.pvol = FindPVol (pfsfsi->fsi_hVPB)) == NULL) {
        return (ERROR_INVALID_HANDLE);
    }

    pfw = (PFWORK) ((PPB_FSFSD)pfsfsd)->hfind;
    psbd = frm.psbd = pfw->psbd;

    if ( pfw->fFindDone )
    {
        usError = ERROR_NO_MORE_FILES;
        goto FindNextExit;
    }

    /* map into pinball internal flag bits */
    if (usFlags == FF_GETPOS) {
        usFlags = CFR_GETPOS | CFR_1STINBUF;
    } else {
        usFlags = CFR_1STINBUF;
    }

    /* remember NewNames attr for CFR */
    if ((pfw->attr_union.wordattr.usSearchAttr & ATTR_NEWNAME) != 0) {
        usFlags |= CFR_NEWNAME;
    }

    /*
     * If we are to do EA work, get a pointer to the EAOP, bump the
     * data area pointer so we preserve the EAOP in the caller's
     * buffer, and decrease the size of the data that is left.
     */
    if (usLevel == 3) {
        if (cbData >= sizeof (EAOP)) {
            peaop = (PEAOP) pbData;
            pbData += sizeof (EAOP);
            cbData -= sizeof (EAOP);
        } else {
            usError = ERROR_BUFFER_OVERFLOW;
            goto FindNextExit;
        }
    } else {
        peaop = NULL;
    }


    if (psbd != NULL)
    {
        /*  Hold subdir
         *  We do this because FindDir is not called.
         */

        HoldDir (psbd,&frm);         /* subdir is locked - wait before holding */

        /*
         * If dir blocks have been set free since the last find then
         * the dir block (which contains the next dir entry) may not
         * be valid.
         */
        if ( pfw->culFreeDB == psbd->culFreeDB )
        {
            /*
             * Sequential FindNext:
             *   Use the sector pointer (from previous find) to
             *   locate the dir block which contains the next dir entry
             *   to be returned.
             */

                                                                    //#85759
            if ((pdb = LSDDB(&pfw->scpDir, BP_KEEP, &pbuf, &frm)) == NULL) {
              usError = ERROR_READ_FAULT;
              goto FindNextExit;
            }

            ASSERT_DB (pdb);

            if (pfw->culChange == pdb->culChange)
            {
                /*
                 * This dir block has not changed since the last find
                 * so continue from where we left off.
                 */

                pde = (PDIRENT) ((PCHAR)pdb + pfw->offusDirEnt);

                IncrHoldsBUF(pbuf);  // @76193

                usError = FindMatch ( pdb, pde, pbuf, usLevel, usFlags,
                                      cbData, pbData, pfw, pcMatch, TRUE,
                                      peaop, &frm);

                DecrHoldsBUF(pbuf);  // @76193

                DecrHoldsSBD (psbd);          /* unhold it the fast way */
                if ( psbd->bFlags & SDF_PND )
                    ChkHoldDir (psbd);

                goto FindNextExit;
            }
        }
        /*
         * Get ready to replay find.
         */
        pfw->psbd = NULL;
        psbd->cusFinds--;
        UnHoldDir_(frm.psbd);

    }

    /*
     * Replay Find:
     *   Using the path name, search for the directory again.
     *   Then use the last name (returned from previous find)
     *   to locate the place where the search is resumed.
     */

#ifdef DEBUG1
        dprintf ("  *pfw->pspLastName: ");

    {   PSZ psz;
        USHORT cchar;

        psz = (PSZ)pfw->pspLastName;
        cchar = (UCHAR) *psz++;
        while (--cchar)
            dprintf ("%c", *psz++);
    }
    dprintf ("\n");
    Int3 ();
#endif
    NameUpCase (pfw->pspLastName->b,
                pfw->pspLastName->c - sizeof (pfw->pspLastName->c),
                &paCodePageEnt [pfw->iCP],
                pfw->pspLastName->b);

    frm.iCP = (BYTE)pfw->iCP;   //#90495

    usError = FindFromName (pfw->pspLastName, usLevel, usFlags,
                cbData, pbData, pfw, pcMatch, peaop, &frm);

FindNextExit:

#ifdef DEBUG
    VBS ();
#endif

    /* Get Hard Error (if any) */
    usError = FS_GetHardErr( usError );

#ifdef TEFTI
    StopCounter (0);
    LatchCounter (0);
    dprintf (__FILE__ ": %dus\n", Get_cntCounter (0));
#endif

    /*
     *  BUGBUG -- we set the match count to zero if
     *  any error was encountered.  This is not the right
     *  thing to do for EA errors, but we don't handle those
     *  correctly anyhow.
     */
    if( usError != NO_ERROR && usError != ERROR_EAS_DIDNT_FIT ) {
        *pcMatch = 0;
    }

    return usError;

    /* suppress warning messages */
    pfsfsi;
}
