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

/*
 * FS_FNEXT.C
 *
 *  DESCRIPTION
 *    
 *      Perform further searches within a directory for subsequent file
 *      matches.
 *
 *  FUNCTIONS DEFINED
 *
 *      FS_FINDNEXT() - Find next occurrence(s) of a file name in a directory.
 *
 * 
 *  REVISION HISTORY (in reverse chronological order)
 *  --------------------------------------------------------------------------
 *  12/6/89 Brian Andrew    Shifted test for PROBEBUF to FNextInfoFit since
 *                          PROBEBUF isn't valid across FSH_YIELD.
 * 
 *  9/20/89 Paul Bingman    Added pbDataStart variable, needed by FNextInfoFit
 *                          to know where the EAOP is on each call.
 * 
 *  9/2/89  Brian Andrew    Outline, including directory traversal
 *
 *
 *  TO DO
 *  --------------------------------------------------------------------------
 *  Add EA support to FNextInfoFit().
 *
 */

#define INCL_DOSERRORS
#define INCL_NOPMAPI
#include <os2.h>
#include <fsd.h>
#include <fsh.h>

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

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

#include "fsdata.h"
#include "pr_memin.h"
#include "pr_file.h"
#include "memory.h"

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



/***    FS_FINDNEXT () - Find next occurrence(s) of a file name in a directory.
 *      
 *  DESCRIPTION
 *
 *      Use the data in the SEARCH_DEP_DATA as a starting point for a file
 *      search.  Search through consecutive sectors until past the last
 *      sector in the data area.  Return as many matches as found provided
 *      they fit in the user's data area.
 *
 *      
 *  ENTRY
 *
 *      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
 *
 *      pvpfsi      = Pointer to volume information (not used by us)
 *
 *      pvddVolData = Pointer to our volume information.  This is
 *                      used whenever the disk is accessed in
 *                      'FindNextFile'
 *      pdeTempDE   = Pointer to DIR_ENTRY structure.  Used to keep
 *                      hold of a DE structure during a swap.
 *      pdePrevDE   = Pointer to DIR_ENTRY structure.  Stores the last
 *                      file found.
 *      pdeThisDE   = Pointer to DIR_ENTRY structure.  Stores the current
 *                      file found.
 *      usMatchesLeft = Number of matches still to be made.  Used as
 *                      the loop guard.
 *      cbDataSize  = Location to store result of checking if the
 *                      the data for this file will fit in the user's
 *                      area.
 *      usRet       = Return value.
 *      pbDataStart = Pointer to start of EAOP area.
 *
 *
 *  EXIT
 *
 *      NO_ERROR if all matches made.
 *      ERROR_FILE_NOT_FOUND if search close has started.
 *      Return value from allocation functions if they fail.
 *      Return value from FindNextFile if not ERROR_NO_MORE_FILES.
 *      ERROR_NO_MORE_FILES if not 1 match made.
 *      Return value from FSH_PROBEBUF if user's area is invalid.
 *      Return value from routine that checks data area when file found, if
 *          not enough data area.
 *  
 */


int far pascal
FS_FINDNEXT (
	PFSFSI              pfsfsi,
	PFSFSD              pfsfsd,
	PCHAR               pbData,     
	USHORT              cbData,     
	PUSHORT             pcMatch,    
	USHORT              usLevel,
	USHORT              usFlags
	)
{

	PVPFSI              pvpfsi;
	PVOL_DEP_DATA       pvddVolData;
	PDIR_ENTRY          pdeTempDE;
	PDIR_ENTRY          pdeThisDE;
	PDIR_ENTRY          pdePrevDE;
	USHORT              usMatchesLeft;
	USHORT              cbDataSize;
	USHORT              usRet;
	PCHAR               pbDataStart;

	/*
	 *  We check to see if a close has started or if someone else is using
	 *  the directory entry.  If a close has started, we exit returning
	 *  ERROR_FILE_NOT_FOUND.  If someone else is using the search
	 *  work area, we wait for a semaphore to clear.
	 */

#ifdef DEBUG
	dprintf ("FS_FNEXT: Flag=%d, Level=%d, Match=%d\n", usFlags, usLevel,
			 *pcMatch);

#endif

#ifdef STATS_H
	CurSt.st_fnext++;
#endif

	/*
	 * Get a copy of the start of the buffer and save.
	 * This will be used by the level 3 code.
	 */

	pbDataStart = pbData;

	/* Initialize the return values */
	usMatchesLeft = *pcMatch;
	*pcMatch = 0;

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

	/*
	 *  We perform the necessary structure allocations and obtain the
	 *  the volume structures.
	 */
	if (!(usRet = FSH_GETVOLPARM (  pfsfsi->fsi_hVPB, &pvpfsi,
									(PPVPFSD) &pvddVolData)))
	{
		/* Get the DE structure for the current DE */
		if (!(usRet = GetDirEntry (&pdeThisDE)))
		{
			/* Get the DE structure for previous DE */
			if (!(usRet = GetDirEntry (&pdePrevDE)))
			{
				/* Wait on the search semaphore */
				SEM_REQ (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

				/* Check if a close has started */
				if (!((PSEARCH_DEP_DATA) pfsfsd)->fSearchClose)
				{
					/* Register as a reader */
					((PSEARCH_DEP_DATA) pfsfsd)->usCountSearch++;

					*pdePrevDE = *((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir;

					/* Give up the semaphore */
					SEM_CLEAR (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

					/*
					 *  If no errors have occurred we are ready to look for
					 *  matches.
					 *  The search will end on three conditions
					 *
					 *      1) We have found all the matches asked for.
					 *
					 *      2) We have a match, but can't fit the data in the users
					 *         area.
					 *
					 *      3) We couldn't get all the matches requested.
					 *
					 *  There is some doubt about what constitutes an error
					 *  for FINDNEXT.  We will return an error on the
					 *  following conditions.
					 *
					 *      1) Couldn't find as many files as requested
					 *         (although the number of files found will be
					 *         returned).
					 *
					 *      2) Ran out of buffer space before ran out of file
					 *         matches (although the number of buffers found
					 *         is returned).
					 *
					 *  We will always save the last file directory entry
					 *  found in the search dependent work to continue the
					 *  search.
					 */


					/*
					 *  Basic algorithm
					 *
					 *      While not all users matches requested not found
					 *
					 *          If find another match
					 *
					 *              If the data fits into the data area
					 *
					 *                  Store the data in the area
					 *                  Decrement the space left in the area
					 *                  Increment the number of matches made
					 *                  Swap the last file found into search area
					 *
					 *              Else Break
					 *
					 *          Else Break
					 */
					while (usMatchesLeft--)
					{
						/*
						 *  We check to see if we are searching for the root directory.
						 *  If we are, there are two possibilities
						 *
						 *      1) We have not returned the root directory
						 *
						 *      2) We have returned the root.
						 *
						 *  In the first case we store the root directory information
						 *  in 'pdeNextDE' and set the 'fRetRootDir' flag.
						 *
						 *  In the second case we set usRet to ERROR_NO_MORE FILES
						 *
						 *  If we are not looking for the root directory, we use the
						 *  directory entry in the search dependent data as a starting
						 *  point to search for a match.
						 */

						/* Wait on the search semaphore */
						SEM_REQ (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

						if (((PSEARCH_DEP_DATA) pfsfsd)->fRootDir)
						{
							/* Check if we have already returned the directory */
							if(((PSEARCH_DEP_DATA) pfsfsd)->fRetRootDir)
							{
								usRet = ERROR_NO_MORE_FILES;

							} /* end if already returned */

							/*
							 *  Else copy the root dir into pdeThisDE and set the
							 *  the fRetRootDir flag.
							 */
							else
							{

								*pdeThisDE = *(pvddVolData->pdeRootDir);

								((PSEARCH_DEP_DATA) pfsfsd)->fRetRootDir = TRUE;

							} /* end if use previous DE */

						} /* end if search for root directory */

						/* Else use the existing directory as a start point */
						else
						{
							usRet = FindNextFile (
										((PSEARCH_DEP_DATA) pfsfsd)->pssSearchString->szName,
										((PSEARCH_DEP_DATA) pfsfsd)->afAttr,
										pdeThisDE,
										pdePrevDE,
										((PSEARCH_DEP_DATA) pfsfsd)->fFirstSearch,
										pvddVolData,
										pfsfsi->fsi_hVPB);
					
							((PSEARCH_DEP_DATA) pfsfsd)->fFirstSearch = FALSE;

						} /* end else use previous directory */

						/* Give up the semaphore */
						SEM_CLEAR (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

						/*
						 *  If there are no errors, the matching file is in
						 *  pdeThisDE.  We look to see if the returned data
						 *  will fit.
						 */

						if (!usRet)
						{
							/* Check if the data will fit */
							if (!(usRet = FNextInfoFit (
											pbDataStart,
											pbData, cbData, usLevel,
											usFlags, pdeThisDE,
											pvddVolData->pvdVolDesc->cbLogBlk,
											&cbDataSize,
											((PSEARCH_DEP_DATA) pfsfsd)->afAttr)))
								/*
								 *  'pdeNextDE' contains the data on the next
								 *  file, it's possible with EA support that
								 *  we will have to hit the disk to find out
								 *  about EA's.  The directory entry has the
								 *  location and length of the XAR.
								 *
								 *  FSH_PROBEBUF uses
								 *  ERROR_PROTECTION_VIOLATION as its return
								 *  value when invalid access, if you
								 *  want to do the same.
								 *
								 *  For 'usFlags' = 1, we always leave the
								 *  'position' field blank.
								 */
							{

								/*
								 *  Increment the pointer into the user's
								 *  area by the amount of data transfered,
								 *  then decrement the amount left for the
								 *  users
								 *
								 *      pbData += DataSize;
								 *      cbData -= DataSize; (remember cbData
								 *                          is unsigned
								 *                          don't let it go
								 *                          below zero)
								 */
								pbData += cbDataSize;
								cbData -= cbDataSize;

								/* Wait on the search semaphore */
								SEM_REQ (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

								*((PSEARCH_DEP_DATA) pfsfsd)->pdeLastDir =
																	*pdeThisDE;

								/* Give up the semaphore */
								SEM_CLEAR (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

								/* Increment matches made */

								(*pcMatch)++;

							} /* end if data fits in the data area */

							/* Else we exit */
							else
							{
#ifdef DEBUG                                                                                                        
								dprintf ("\tFS_FNEXT:  FNextInfoFit  --  %d\n",
										usRet);
#endif                                                                                                              
								break;

							} /* else data didn't fit */

						} /* end if found another match */

						/* Else break out of while loop */
						else
						{
							break;
						}

						/* Move to the next DE */
						pdeTempDE = pdeThisDE;
						pdeThisDE = pdePrevDE;
						pdePrevDE = pdeTempDE;

					} /* end while more matches */

					/* Clean up the search dependent area */
					/* Wait on the search semaphore */
					SEM_REQ (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

					/* Exit as a searcher */
					((PSEARCH_DEP_DATA) pfsfsd)->usCountSearch--;

					/* Give up the semaphore */
					SEM_CLEAR (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

				} /* end if close has not started */

				/* Else return invalid access */
				else
				{
					usRet = ERROR_FILE_NOT_FOUND;

					SEM_CLEAR (((PSEARCH_DEP_DATA) pfsfsd)->sSearchSem);

				}


				FREE_DIR_ENTRY (pdePrevDE);

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

#ifdef DEBUG
			/* Else print error message */
			else
			{
				dprintf ("\tFS_FINDNEXT:  GetDirEntry (PrevDE)  %d\n", usRet);
			}
#endif

			/* Give up this DE */
			FREE_DIR_ENTRY (pdeThisDE);

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

#ifdef DEBUG
		/* Else print error message */
		else
		{
			dprintf ("\tFS_FINDNEXT:  GetDirEntry (ThisDE)  %d\n", usRet);
		}
#endif

	} /* end if volume parameters obtained */

#ifdef DEBUG
	/* Else print error message */
	else
	{

		dprintf ("\tFS_FINDNEXT:  GETVOLPARM  --  %d\n", usRet);
	}
#endif

	/*
	 *  We check in the case where multiple files were being searched for.
	 *  If we found at least one file, we don't return NO_MORE_FILES.
	 */

	if ((usRet) && (usRet != ERROR_EAS_DIDNT_FIT) && (*pcMatch))
	{
		usRet = NO_ERROR;

	}

#ifdef DEBUG
	dprintf ("    FS_FNEXT:  Exit  --  %d, Matches  --  %d\n", usRet, *pcMatch);

#endif


	return usRet;

	/* No warnings */
	usLevel; usFlags;

}   /* end FS_FINDNEXT */
