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

#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include "heap.h"
#include "api.h"
#include "const.h"
#include "misc.h"
#include "chain.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 "assert.h"
#include "ea.h"
#include "log.h"
#include "pr_fsd.h"
#include "pr_CACHE.h"
#include "pr_FIND.h"
#include "pr_HEAP.h"
#include "pr_MISC.h"
#include "pr_DIR.h"
#include "pr_WRSUB.h"
#include "pr_DEBUG.h"

// @76193 for TKNOBLOCK checking
// #define MAKE_FP(seg, off) \
//        (((BYTE far *) (((long)(unsigned) (seg)) << 16)) + (unsigned) (off))

/*
 * This is used as the candidate and thus count should not include the count
 * byte itself.
 */
PASCALSTRING spDot = {
        1,      /* spDot.c     */
        '.'     /* spDot.b [0] */
};


/*
 ***    FS_FINDFIRST - Initiate File Search
 *
 *      FindFirst initiates a file search sequence.
 *
 *      We allocate an FWORK record and skip to the first
 *      directory entry.  Then FindMatch is called to fill up
 *      the return buffer with matching directory entries.
 *      The returned search handle is a pointer to the FWORK.
 *
 *      Attribute matching rules:
 *          Normal and Read-Only files are always found.
 *          Hidden, System and Directory entries are found
 *              iff their bit is specified in the mask.
 *
 *      We have to chain our FWORK records so that if a directory
 *      gets deleted the subdir field can be cleared (by RemSbDir).
 *      We do not want to add an FWORK to this chain that has a garbage
 *      subdir field; if we block then it is possible that RemSbDir will
 *      look at this field.
 *
 *      HISTORY
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *          09-Mar-1990 by JR (John Rogers, JohnRo@Microsoft)
 *              - Speedup directory searches.
 *          19-Sep-89 by JR (John Rogers, JohnRo@Microsoft)
 *              - Avoid warnings from Microsoft C 5.2 (on slash-slash comments).
 *          26-May-1989 by JR (John Rogers, JohnRo@Microsoft)
 *              - DCR 259.2: changed names of macros in "frame.h".
 */

int far pascal
FS_FINDFIRST (
    struct cdfsi far *      pcdfsi,
    struct cdfsd far *      pcdfsd,
    CHAR far *              pchName,    /* search pattern (including path) */
    USHORT                  usCurDirEnd,
    USHORT                  usAttr,     /* search attrib (hidden/system/dir) */
    struct fsfsi far *      pfsfsi,
    struct fsfsd far *      pfsfsd,
    CHAR far *              pbData,     /* ptr to data buffer */
    USHORT                  cbData,     /* size of data buffer */
    USHORT far *            pcMatch,    /* # of matching entries returned */
    USHORT                  usLevel,    /* level of info returned */
    USHORT                  usFlags
)
{
    PFWORK      pfw;            /* ptr to FWORK structure (search handle) */
    PDIRBLK     pdbFirst;       /* ptr to dir block of dir entry */
    PDIRENT     pdeFirst;       /* ptr to first dir entry */
    PBUFNODE    pbufFirst;      /* ptr to bufnode of dir block */
    PSUBDIR     psbdFirst;      /* ptr to sub dir */
    USHORT      usError = NO_ERROR;     /* return code */
    PCRACKEDPATH pcp;
    USHORT      usLen;
    PEAOP       peaop;
    FRAME       frm;

#ifdef MINIFSD
    extern USHORT BootStage;

    if (BootStage == 1) {
        return ERROR_NOT_SUPPORTED;
    }
#endif

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

#ifdef DEBUG_FIND
    dprintf(__FILE__": Searching for '%Fs'\n", pchName);
#endif

    /*  Initialize file search structures
     *  (pfsfsd->fsd_hfind is set by FindFirst)
     *  (&frm->pvol is set by FindDir)
     */
//#114986
     ((PPB_FSFSD)pfsfsd)->hfind = NULL;

    /*
     * Because CrackPath now allocs its own space, in this one case
     * use a temp variable to get the cracked path in then copy it to the
     * fwork struct.  All other fs_ guys don't need to do this.
     */
    if ((usError = CrackPath(pcdfsi->cdi_hVPB,pchName,&pcp,&frm)) != NO_ERROR) {
        goto FFirstExit1;
    }

    /*
     * Allocate storage for FWORK buffer which includes the cracked path.
     * Since a FWORK includes a crackedpath, subtract off the size of
     * this (avoid double accounting).
     */
    usLen = sizeof( pcp->hVPB ) + pcp->cbPath;
    usLen += ((PBYTE)pcp)[usLen];

    if ((pfw = GHS (sizeof (FWORK) - sizeof (CRACKEDPATH) + usLen,&frm)) == NULL) {
        usError = ERROR_NOT_ENOUGH_MEMORY;
        RetHeap(pcp);
        goto FFirstExit1;
    }

    /*      dest                  src  count */
    memcpy (&(pfw->cpSearchPath), pcp, usLen);
    RetHeap (pcp);

    pfw->pspLastName = NULL; /* initialize */

    /*
     * Find the directory, skip to the first eligible dir entry,
     * and then call FindMatch to fill up the return buffer with
     * matching dir entries.
     */
    if (FindDir (&pfw->cpSearchPath, &pdbFirst, &pbufFirst,
                 &psbdFirst, &frm)) {
        ASSERT_DB (pdbFirst);
        ASSERT_SBD (psbdFirst);

        /*
         * Initialize FWORK struct
         */
        psbdFirst->cusFinds++;          /* subdir is used for find */
        pfw->psbd = psbdFirst;
        pfw->attr_union.wordattr.usSearchAttr = usAttr;
        usAttr &= 0x00ff     ;          /* Must_Have bits not supported  */
        ADD_FWORK (pfw)                 /* add it to the chain */

        /*
         * We've found the directory that we're to search.
         * Set ourselves up to start at the beginning.
         */
        if (SkipNam(&pdbFirst,&pbufFirst,pfw,&pdeFirst,&frm) == NO_ERROR) {

//          BYTE FAR *NoBlock = MAKE_FP(0x400,0x4fCE);  // @76193 TKNOBLOCK DEBUG ONLY!!!
            PPASCALSTRING pspPattern;
            BOOL fFindDot = FALSE;

//            (*NoBlock)++;  // 76193 TKNOBLOCK DEBUG ONLY!!!
            ASSERT_DB (pdbFirst);

            /*
             * We've located the first file that might be eligible.  If it
             * starts with a wildcard we'll be pointing at the first entry
             * in the directory, which is our ".." surrogate.  See if we
             * start with a wildcard and therefore might match.  If so,
             * we'll check out "." manually since it's not in the directory.
             */

            pspPattern = FILE_NAME(&pfw->cpSearchPath);

            /* 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 ((usAttr & 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;
                    UnHoldDir_(frm.psbd);
                    if( pfw->psbd != NULL ) {

#ifdef DEBUG
                        if( pfw->psbd->cusFinds == 0 ) {
                            INTERR( __FILE__":  subdir cusFinds going negative.\n" );
                        }
#endif
                        pfw->psbd->cusFinds--;
                    }
                    REMOVE_FWORK( pfw );
                    goto FFirstExit2;
                }
            } else {
                peaop = NULL;
            }

            /* see if we can speed things up by determining if we have a '*' */
            if ((pspPattern->c == 2 && pspPattern->b[0] == '*')

#ifdef STAR_DOT_STAR_IS_SPECIAL
                || (pspPattern->c == 4 &&
#pragma message (__FILE__ ": Ignore the following warning")
                  memcmp (pspPattern->b, (PBYTE) "*.*", (USHORT) 3) == 0)
#endif
                                                                          ) {
                /*
                 * Change pattern of * into the equivalent of a null string
                 * (1 length byte, 0 characters).  Doing this will disable
                 * the call to MetaMatch and every name in the directory
                 * is returned.
                 */
                pspPattern->c = 1;

                /*
                 * if we would match on the '.' directory then set
                 * the boolean.
                 */
                if (FATTRFIND (ATTR_DIRECTORY, usAttr))
                        fFindDot = TRUE;
            }

            /* if "." will match the pattern (use code page 0) */
            if (fFindDot ||
                (MetaMatch (pspPattern, &spDot, 0) == METAMATCH_EQUAL)) {

                /* only return '.' if the caller is looking for directories */
                if (FATTRFIND (ATTR_DIRECTORY, usAttr)) {

                    /*
                     * We know that "." will match so generate the find record.
                     */
                    IncrHoldsBUF(pbufFirst);  // @76193

                    usError = CFR (pdeFirst,
                                   pbufFirst,
                                   usLevel,
                                   usFlags | CFR_DOT,
                                   usLevel == 3 ? peaop->fpGEAList : NULL,
                                   &cbData,
                                   &pbData,
                                   &frm);

                    DecrHoldsBUF(pbufFirst);  // @76193
                                              // Code deleted; #92692

                    /* not 1st in buf any more */
                    usFlags &= ~CFR_1STINBUF;

                    /* Since we returned "." we must adjust pcMatch. */
#ifdef DEBUG
                    if (*pcMatch == 0) {
                        dprintf ("FS_FINDFIRST: *pcMatch about to underflow\n");
                    }
#endif
                    (*pcMatch)--;

                    /*
                     * Fix up the fwork struct.  This was taken from the exit
                     * code in FindMatch.  Since we are mocking up our
                     * FindMatch of '.' we need to set up the fwork struct so
                     * that on successive calls to FindMatch there really is an
                     * fwork struct initialized.
                     */
                    pfw->fFindDone = FALSE;
                    pfw->offusDirEnt = (PCHAR) pdeFirst - (PCHAR) pdbFirst;
#ifdef DEBUG
                    if (pbufFirst != BH (pdbFirst)) {
                        dprintf (__FILE__": Wrong buffer\n");
                        Int3 ();
                    }
#endif
                    pfw->scpDir.pbufHint = pbufFirst;
                    pfw->scpDir.lsn = pdbFirst->lsnThisDir;

                    /* used for detecting directory changes */
                    pfw->culChange = pdbFirst->culChange;
                    pfw->culFreeDB = pfw->psbd->culFreeDB;

                    IncrHoldsBUF(pbufFirst);  // @76193
                    IncrHoldsSBD(psbdFirst);

                    /* put the '.' in the fwork struct */
                    if ((pfw->pspLastName = GHS (2 * sizeof (BYTE),&frm)) == NULL) {
                            usError = ERROR_NOT_ENOUGH_MEMORY;
                    } else {
                            pfw->iCP = 0;
                            pfw->pspLastName->c = 2;
                            pfw->pspLastName->b [0] = '.';
                    }

                    DecrHoldsSBD(psbdFirst);
                    DecrHoldsBUF(pbufFirst);  // @76193

                    /*
                     * Set that we found '.' so that we know whether to
                     * re-adjust *pcMatch after our call to FindMatch.
                     */
                    fFindDot = TRUE;
                }
            }

            /* don't call findmatch if error from above call to CFR */
            if (usError == NO_ERROR) {

                if (*pcMatch != 0) {
                    usError = FindMatch (pdbFirst,
                                         pdeFirst,
                                         pbufFirst,
                                         usLevel,
                                         usFlags,
                                         cbData,
                                         pbData,
                                         pfw,
                                         pcMatch,
                                         FALSE,
                                         peaop,
                                         &frm);
                }

                if (fFindDot && ((usError == ERROR_NO_MORE_FILES) ||
                                (usError == ERROR_BUFFER_OVERFLOW)))
                    usError = NO_ERROR ; /* At least '.' was found. */
            }

            /* if we found . earlier, bump pcMatch up 1 */
            if (fFindDot) (*pcMatch)++;

//        (*NoBlock)--;  // @76193 TKNOBLOCK DEBUG ONLY!!!
        } else {
            usError = ERROR_NO_MORE_FILES; /* Zero files found */
        }

        if (usError == NO_ERROR || usError == ERROR_EAS_DIDNT_FIT) {
            /* return search handle */

            ((PPB_FSFSD)pfsfsd)->hfind = (HFIND) pfw;

            /*
             * Subdir is held by FindDir;  unhold it the fast way.
             */
            DecrHoldsSBD (pfw->psbd);
            if (pfw->psbd->bFlags & SDF_PND)
                ChkHoldDir (pfw->psbd);
        } else {
            /*
             * An error occured; search handle is not returned.
             */
            UnHoldDir_(frm.psbd);       /* held by FindDir */
            if (pfw->psbd != NULL)
                pfw->psbd->cusFinds--;
            REMOVE_FWORK (pfw)          /* remove it from the chain */
        }
    } else {
        usError = ERROR_PATH_NOT_FOUND;
    }

    /*
     * If an error occured then release FWORK structure.
     * (the search handle is not returned; FindClose will not be called)
     */
    if (usError != NO_ERROR && usError != ERROR_EAS_DIDNT_FIT) {
        *pcMatch = 0;

FFirstExit2:
        if (pfw->pspLastName)
          RetHeap (pfw->pspLastName);

        RetHeap (pfw);
    }

FFirstExit1:

#ifdef DEBUG
    VBS ();
#endif

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

    return usError;

    /* for the compiler  /W3 */
    usCurDirEnd; pcdfsd; pfsfsi;
}
