/*static char *SCCSID = "@(#)fs_ffrst.c 6.3 91/05/31";*/
#define SCCSID "@(#)fs_ffrst.c  6.3 91/05/31"
/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 */

/*
 *  FS_FFRST.C
 *
 *    DESCRIPTION
 *
 *      Find the first occurrence of a file in a directory.
 *
 *
 *    FUNCTIONS DEFINED
 *
 *      FS_FINDFIRST () -   Find the first occurrence of a file in a directory.
 *
 *
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *  1/19/94     Doug Barra      @DB65536 - Store the initial directory entry
 *                                  so that we can restart the search if
 *                                  needed in FS_FINDFROMNAME
 *  5/31/91     Greg Simco      Removed Translation of ERROR_NO_MORE_FILES
 *                                  from FINDNEXT as recursive searches were
 *                                  failing from this
 *  11/14/89    Brian Andrew    Added DBCS support (ParentEndLenDBCS)
 *  9/20/89     Brian Andrew    Fixed bug on CloseStarted flag.
 *  9/7/89      Brian Andrew    Translate the return code from FINDNEXT
 *                                  if it is ERROR_NO_MORE_FILES
 *  9/5/89      Brian Andrew    Initial working version
 *  8/4/89      Brian Andrew    Initial Blank Template
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 */


#define INCL_DOSERRORS
#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 "memory.h"
#include "pr_memin.h"
#include "pr_strng.h"
#include "pr_path.h"
#include "pr_dir.h"
#include "pr_misc.h"
#include "pr_dbcs.h"

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


/***    FS_FINDFIRST () -   Find the first occurrence of a file name in a
 *                          directory.
 *
 *  DESCRIPTION
 *
 *      This function uses FINDNEXT to do the actual search for a file.
 *      What we do here is determine a starting point for the search and
 *      store it in the search dependent data area and call FINDNEXT.
 *
 *      We use the same technique as in FS_FILEATTRIBUTE to find the
 *      search directory, making a special note if the search target is
 *      actually the root directory.
 *
 *      If it is the root directory, we set two flags in the search
 *      dependent area indicating that we want the root directory and
 *      that it hasn't been returned yet.
 *
 *
 *  ENTRY
 *
 *      pcdfsi      = Pointer to file system independent directory
 *                      information.  Contains the bits indicating if the
 *                      dependent area is vilaid.
 *      pcdfsd      = Pointer to the directory dependent area.  Contains
 *                      the pathe number of the current directory.
 *      pchName     = Pointer to the full path name of the desired file.
 *
 *      usCurDirEnd = Index of the end of the current directory in 'pchName'.
 *                      If the current directory is not in the path, this
 *                      value is -1.
 *      usAttr      = Pointer to address to store file attributes.
 *      pfsfsi      = Pointer to file search independent area :
 *                      Contains the volume handle
 *      pfsfsd      = Pointer to SEARCH_DEP_DATA area (cast needed) :
 *                    Contains    DIR_ENTRY to start search from,
 *                        SEARCH_STRING with file name,
 *                        Length of search string,
 *                        File attributes for search,
 *                        Last sector to search in,
 *                        Semaphore to control access to data,
 *                        Flag indicating if search close in
 *                        process.
 *      pbData      = Pointer to data area to store file data
 *      cbData      = Size of data area
 *      pcMatch     = Pointer to USHORT, store the number of matches made
 *      usLevel     = Indication of type of data to return, refers to
 *                      structures in data area
 *      usFlags     = Flag indicating whether to return file position
 *                      information.
 *
 *
 *  LOCAL
 *
 *      usRet       = Return value from function.
 *      pffpLocal   = Pointer to structure containing the local parameters.
 *                      This is done to reduce the stack usage.
 *
 *    Fields
 *
 *      pdeFirstDE  = Pointer to directory entry struct.  Used during search,
 *                      first to store the entry for the parent directory,
 *                      then to store the entry for the target file.
 *      pssSearchString = Pointer to search string structure.
 *      pvpfsi      = Pointer to the volume independent information.  Not
 *                      used by us.
 *      pvddVolData = Pointer to volume dependent information.  Contains
 *                      the volume descriptor, the path table and the start
 *                      of all the cached sectors for this volume.
 *      cbNameLen   = Used to store the length of pName.
 *      cbBaseOffset = Offset in 'pName' of the last '\' character before
 *                      the target file name.
 *      usTempMatch = Temp storage for number of matches.
 *
 *
 *  EXIT
 *
 *      Return value from FINDNEXT if no error here
 *      Return value from GETVOLPARM if failure.
 *      Return value from allocation functions if failure.
 *      Return value from GetParentDE if error.
 *      Return value from FindNextFile if error.
 *
 */


int far pascal
FS_FINDFIRST (
                PCDFSI          pcdfsi,
                PCDFSD          pcdfsd,
                PCHAR           pchName,
                USHORT          usCurDirEnd,
                USHORT          usAttr,
                PFSFSI          pfsfsi,
                PFSFSD          pfsfsd,
                PCHAR           pbData,
                USHORT          cbData,
                PUSHORT         pcMatch,
                USHORT          usLevel,
                USHORT          usFlags
)
{

        USHORT              usRet;
        FFRST_PARAMS    FAR *pffpLocal;

#ifdef DEBUG
        dprintf ("FS_FFRST: Index=%d, Root=%d, Valid=%d, Name=%Fs\n",
                                                 usCurDirEnd,
                                                 pcdfsi->cdi_flags & CDI_ISROOT,
                                                 pcdfsi->cdi_flags & CDI_ISVALID,
                                                 pchName);
        dprintf ("FS_FFRST: Flag=%d, Level=%d, Match=%d, Attr=%x\n",
                                                usFlags, usLevel, *pcMatch, usAttr);

#endif

        /* Check initialization */
        if (usRet = CHECK_INIT)
                        return usRet;

#ifdef STATS_H
        CurSt.st_ffirst++;
#endif

        /* Try to get the structure for the local variables, return if failure */
        if (usRet = GetFfrstParams (&pffpLocal))
        {
#ifdef DEBUG
                dprintf ("\tFS_FINDFIRST:   GetFfrstParams  %d\n", usRet);
#endif

                return usRet;

        } /* end if local parameter allocation failed */

        /* Remember the number of matches */
        pffpLocal->usTempMatch = *pcMatch;
        *pcMatch = 0;

        /* Initialize the search structure */
        ((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir = NULL;
        ((PSEARCH_DEP_DATA) pfsfsd)->pssSearchString = NULL;
        ((PSEARCH_DEP_DATA) pfsfsd)->usCountSearch = 0;
        ((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem = 0;

        /* Try to get the volume parameters */
        if (!(usRet = FSH_GETVOLPARM (
                                                pcdfsi->cdi_hVPB,
                                                &pffpLocal->pvpfsi,
                                                (PPVPFSD) &pffpLocal->pvddVolData)))
        {
                /*
                 *  The following structures are going to be needed
                 *
                 *    DIR_ENTRY       for the directory entry for the parent
                 *                    directory (pfsfsd->pdeLastDir).
                 *
                 *
                 *    SEARCH_STRING   used to store the actual search string
                 *                    including wildcards (pfsfsd->pssSearchString).
                 *
                 *  pfsfsd->pdeLastDir is needed to compute the last sector to search in
                 *  for the directory.
                 */

                /* Try to allocate the first extent DIR_ENTRY */
                if (!(usRet = GetDirEntry (&((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir)))
                {
                  if (!(usRet = GetDirEntry (&((PSEARCH_DEP_DATA) pfsfsd)->pdeParentDir))) /* @DB65536 */
                  {                                                                        /* @DB65536 */
                        /* Try to get the search string */
                        if (usRet = GetSearchArray (
                                                                &((PSEARCH_DEP_DATA) pfsfsd)->pssSearchString))
                        {
#ifdef DEBUG
                                dprintf
                                                ("\tFS_FFRST:  GetSearchArray  --  %d\n",
                                                usRet);
#endif
                                FREE_DIR_ENTRY (((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir);
                                FREE_DIR_ENTRY (((PSEARCH_DEP_DATA) pfsfsd)->pdeParentDir); /* @DB65536 */

                        } /* end if failed to get search array */

                  } /* end if allocated directory entry */                      /* @DB65536 */
                  else                                                          /* @DB65536 */
                  {                                                             /* @DB65536 */
                     FREE_DIR_ENTRY (((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir);  /* @DB65536 */
#ifdef DEBUG
                     dprintf ("\tFS_FFRST:  GetDirEntry  --  %d\n",usRet);      /* @DB65536 */
#endif
                  }                                                             /* @DB65536 */
                } /* end if allocated directory entry */

#ifdef DEBUG
                /* Else failed to get Directory entry */
                else
                {
                        dprintf ("\tFS_FFRST:  GetDirEntry  --  %d\n",
                                                                usRet);
                } /* end else failed first DIR_ENTRY */
#endif

        } /* end if get volume parms */

#ifdef DEBUG
        else
        {
                dprintf ("\tFS_FFRST:  GETVOLPARMS  --  %d\n",
                                                         usRet);

        }
#endif

        /* Continue looking for a match if no error has occurred */
        if (!usRet)
        {
                /* Recover the desired match number */
                *pcMatch = pffpLocal->usTempMatch;

                pffpLocal->cbNameLen = CleanStringDBCS (pchName, istrlen (pchName));

                pffpLocal->cbBaseOffset = ParentEndLenDBCS (
                                                                                pchName,
                                                                                pffpLocal->cbNameLen);

                /*
                 *  We check whether this search file is the root directory.
                 *  If the base offset is one less than the string length.
                 *  We just set the flags in the search dependent area.
                 */

                if (pffpLocal->cbBaseOffset == (pffpLocal->cbNameLen - 1))
                {
                        ((PSEARCH_DEP_DATA) pfsfsd)->fRootDir      = TRUE;
                        ((PSEARCH_DEP_DATA) pfsfsd)->fRetRootDir   = FALSE;

                } /* end if root directory */

                /* Else do a detailed search */
                else
                {
                        /* Get the directory entry for the directory to search in */
                        if (!(usRet = GetParentDE (pffpLocal->pvddVolData, pcdfsi,
                                                                                ((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir,
                                                                                pffpLocal->cbBaseOffset, pchName,
                                                                                pffpLocal->cbNameLen, usCurDirEnd,
                                                                                (PDIR_DEP_DATA) pcdfsd)))
                        {
                                /*
                                 *  If no errors have occurred, the directory to search
                                 *  is in pffpLocal->pdeFirstDE.
                                 *
                                 *  We use the directory entry to compute the last sector
                                 *  for the directory.  We use the path entry for the
                                 *  directory to create a mock directory and store it
                                 *  in pffpLocal->pdeFirstDE.
                                 *
                                 *  We will store this mock directory, along with the
                                 *  search string and the last sector into the
                                 *  SEARCH_DEP_DATA. We then call FS_FINDNEXT to do the
                                 *  actual work.
                                 */

                                /* Initialize the SEARCH_DEP_DATA area */
                                ((PSEARCH_DEP_DATA) pfsfsd)->fRootDir      = FALSE;
                                ((PSEARCH_DEP_DATA) pfsfsd)->fRetRootDir   = TRUE;
                                ((PSEARCH_DEP_DATA) pfsfsd)->pssSearchString->cbSearchString =
                                                                                        pffpLocal->cbNameLen -
                                                                                        (pffpLocal->cbBaseOffset + 1);

                                ((PSEARCH_DEP_DATA) pfsfsd)->afAttr =   usAttr |
                                                                                DOS_ATTR_READ_ONLY |
                                                                                DOS_ATTR_ARCHIVE;
                                /* GK Defect 65351 */

                                if ((pffpLocal->cbNameLen - (pffpLocal->cbBaseOffset + 1) >= MAX_FILE_ID_LEN))
                                   usRet = ERROR_FILENAME_EXCED_RANGE;
                                else

                                /* Store the search string and compute its length */
                                CopyWithNull (
                                        ((PSEARCH_DEP_DATA) pfsfsd)->pssSearchString->szName,
                                        pchName + pffpLocal->cbBaseOffset + 1,
                                        pffpLocal->cbNameLen - (pffpLocal->cbBaseOffset + 1));

                                /* Need to save the parent directory so that */  /* @DB65536 */
                                /* we can restart the search in findfromname */  /* @DB65536 */
                                *((PSEARCH_DEP_DATA) pfsfsd)->pdeParentDir =     /* @DB65536 */
                                   *((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir;     /* @DB65536 */

                        } /* end if no error, getting parent directory */

#ifdef DEBUG
                        /* Else we couldn't find the search directory */
                        else
                        {
                                dprintf ("\tFS_FFRST:  GetParentDE  --  %d\n",
                                                                 usRet);

                        } /* end else couldn't find the search DE */
#endif

                } /* end else complex search */

                /*
                 *  We are now ready to call FS_FINDNEXT.  Out return value
                 *  will be the return value from that function.
                 *
                 *  If there is an error where FS_FINDNEXT doesn't make
                 *  a single match we will free all of the structures below.
                 *  We also free all of the structures if the search for
                 *  a directory structure above failed.
                 */
                if (!usRet)
                {
                        /* Set flag indicating that this is first search */
                        ((PSEARCH_DEP_DATA) pfsfsd)->fSearchClose  = FALSE;
                        ((PSEARCH_DEP_DATA) pfsfsd)->fFirstSearch = TRUE;

                        usRet = FS_FINDNEXT (pfsfsi, pfsfsd, pbData, cbData, pcMatch,
                                                                                                         usLevel, usFlags);


                } /* end if no errors, call FINDNEXT */

                if (usRet && (usRet != ERROR_EAS_DIDNT_FIT))
                {
                        FS_FINDCLOSE (pfsfsi, pfsfsd);

                }

        } /* end continue looking */


#ifdef DEBUG
        dprintf ("    FS_FFRST:  Exit  --  %d\n", usRet);

#endif

        /* Free the local parameter structure */
        FREE_FFRST_PARAMS (pffpLocal);

        return usRet;


        /* no warnings */
        pcdfsi; pcdfsd; pchName; usCurDirEnd; usAttr; pfsfsi; pfsfsd;
        pbData; cbData; pcMatch; usLevel; usFlags;

}   /* end FS_FINDFIRST */
