/*static char *SCCSID = "@(#)fs_cfptr.c	6.2 91/01/14";*/
#define SCCSID "@(#)fs_cfptr.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_CFPTR.C
 *
 *    DESCRIPTION
 *    
 *      Move a file's position pointer.
 *
 *
 *    FUNCTIONS DEFINED
 *
 *      FS_CHFILEPTR () -   Move a files's position pointer.
 *
 * 
 *  REVISION HISTORY (in reverse chronological order)
 *  -------------------------------------------------------------------------
 *  4/20/90     Brian Andrew    Multiple thread support.
 * 
 *  12/11/89    Brian Andrew    Revised to match Pinball except that pointer
 *                              can't be pushed past the end of the file.
 * 
 *  9/10/89     Brian Andrew    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 "pr_memin.h"
#include "stdlib.h"

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

/***    FS_CHFILEPTR () -   Move a file's logical read position pointer.
 *      
 *  DESCRIPTION
 *
 *      We mathematically adjust the pointer using either the current position,
 *      the beginning of the file or the end of the pointer.
 *
 *      If the offset is from the end of the file we treat the offset as
 *      a positive value and subtract it from the file length.
 *
 *      If a new position is beyond either the beginning or end of the file
 *      we set the position to the end of the file.  This is to allow for
 *      3x compatibility which allows negative seeks.
 *
 *      
 *  ENTRY
 *
 *      psffsi      = Pointer to file independent area.  Contains the file's
 *                    time stamps, size and position information.
 *      psffsd      = Pointer to file dependent area.  Contains the
 *                    directory entry for the first and most recently accessed
 *                    extents.  It also contains the semaphores and flags
 *                    which control the access to this data.
 *      offlSeek    = Offset to adjust pointer.  Subtract this from end of
 *                    file if type is from end of file.
 *      type        = Flag indicating if move is from current position,
 *                    beginning of file or end of file.
 *      IOflag      = Indicates information about the handle.  (not used)
 *
 *
 *  LOCAL
 *
 *      usRet           = Return value.
 *      cbCurrentPos    =   Local copy of current position.
 *
 *
 *  EXIT
 *
 *      NO_ERROR   (unless memory initialization called )
 */

int far pascal
FS_CHGFILEPTR (PSFFSI   psffsi,
			   PSFFSD   psffsd,
			   LONG     offlSeek,
			   USHORT   type,
			   USHORT   IOflag)
{
	USHORT  usRet;
	ULONG   cbCurrentPos;

#ifdef DEBUG
	dprintf ("FS_CHFLPTR: Offset=%ld, Type=%d, Current Pos=%d\n",
			 offlSeek, type, psffsi->sfi_position);
#endif

#ifdef STATS_H
		CurSt.st_cfptr++;
#endif

	if (usRet = CHECK_INIT)
		return usRet;

	/* Wait on the file semaphore */
	SEM_REQ (((PFILE_DEP_DATA) psffsd)->sFileSem);

	/* Check if a close has started */
	if (!((FILE_DEP_DATA FAR *) psffsd)->fCloseFlag)
	{
		/* Register as a reader */
		((PFILE_DEP_DATA) psffsd)->usCountRead++;

		/* Make a local copy of the file position */
		cbCurrentPos = psffsi->sfi_position;

		/* Give up the semaphore */
		SEM_CLEAR (((PFILE_DEP_DATA) psffsd)->sFileSem);

		switch (type)
		{
			/*
			 *  For the start of the file, if the offset is positive we check
			 *  that it isn't beyond the end of the file.  If so then we set
			 *  the position to either the end of the file or the offset.
			 *
			 *  For a negative offset, we use position at end of file.
			 */

			case CFP_RELBEGIN :
				/* Check for negative seek */
				cbCurrentPos = (offlSeek < 0) ?
										psffsi->sfi_size : offlSeek;

				break;

			/*
			 *  For the end of the file.  We assume the offset is positive
			 *  and add it from the end of the file.  If the result offset is
			 *  rolls over, the new position will be the end of the file.
			 *
			 *  Otherwise we subtract the offset from the file size.  If the
			 *  result is a negative offset, we set the position to the end
			 *  of the file.
			 */

			case CFP_RELEND :
				/* Check for negative offset */
				if (offlSeek < 0)
				{
					/* Check for negative file position */
					cbCurrentPos =  ((ULONG)(-offlSeek) > psffsi->sfi_size) ?
									psffsi->sfi_size :
									(psffsi->sfi_size + offlSeek);

				
				}

				else
				{
					/* Check first if rollover */
					if (psffsi->sfi_size > (psffsi->sfi_size + offlSeek))
					{
						cbCurrentPos = psffsi->sfi_size;
					}

					else
					{
						cbCurrentPos = psffsi->sfi_size + offlSeek;

					}
				}

				break;

			/*
			*  For the relative position, we have to worry about going beyond
			*  the beginning or end of the file.  We also have to worry
			*  about wrapping from positive to negative or the reverse.
			*/

			case CFP_RELCUR :

				/* Check for negative offset */
				if (offlSeek < 0)
				{
					/* Check for negative file position */
					cbCurrentPos = ((ULONG) (-offlSeek) > cbCurrentPos) ?
											psffsi->sfi_size :
											(cbCurrentPos + offlSeek);
				}

				else
				{
					/* Check first if rollover */
					if (cbCurrentPos > (cbCurrentPos + offlSeek))
					{
						cbCurrentPos = psffsi->sfi_size;

					}

					else
					{
						cbCurrentPos += offlSeek;

					}
				}

				break;

			default:

				usRet = ERROR_INVALID_FUNCTION;
				break;

		} /* end switch */

		/* Grab the file semaphore */
		SEM_REQ (((PFILE_DEP_DATA) psffsd)->sFileSem);

		/* Update the file position */
		psffsi->sfi_position = cbCurrentPos;

		/* Decrease the number of file readers */
		((PFILE_DEP_DATA) psffsd)->usCountRead--;

		/* Give up the semaphore */
		SEM_CLEAR (((PFILE_DEP_DATA) psffsd)->sFileSem);


	} /* end if close has not started */

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

		SEM_CLEAR (((PFILE_DEP_DATA) psffsd)->sFileSem);

	}

#ifdef DEBUG
	dprintf ("    FS_CHFLPTR: Position= %ld, Exit  --  %d\n",
			 cbCurrentPos, usRet);
#endif

	return usRet;

	/* no warnings */
	psffsi; psffsd; offlSeek; type; IOflag;

}   /* end FS_CHFILEPTR */
