#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include "file2.h"
#include "internal.h"

#include <trace.h>
/************************************************************************
 *
 * Function:     fseek
 *
 * Description:
 *     Reposition the file stream
 *
 * Input:
 *     foStrean  - pointer to the file stream
 *     lOffset   - delta for the offset
 *     nWhence   - Base address for the offset
 *                 = 0, relative to the start of the file
 *                 = 1, relative to the current location
 *                 = 2, relative to the end of the file
 * Returns:
 *   returns 0 if succeeds
 *   returns -1 and sets errno if fails
 *   fields of FILE struct will be changed
 */

int fseek (FILE *fpStream, long lOffset, int nWhence)
    {
    long  lPresentLoc;
    long  lEndLoc;
    long  lSectorNum;
    int   nNewLOffset;
    int   nBufferSize;
    int   fdFile;
    FILE2 *fp2Stream;
    int   fUngetc;
    int   nAnswer = -2;
    FUNC_ENTRY ("fseek");
/*
 *  If the file is not open or the parameters are invalid then reject
 *  the request.
 */
    if (!inuse (fpStream) || nWhence > SEEK_END || nWhence < SEEK_SET)
        {
	nAnswer = -1;
	errno   = EINVAL;
        }
/*
 *  Construct the various pointers to the data
 */
    if (nAnswer == -2)
        {
	fdFile           = fileno(fpStream);
	fp2Stream        = file2p (fpStream);
	fpStream->_flag &= ~_IOEOF;
/*
 *  If the file was written and the lOffset is relative to the location then
 *  try to find the position and make the position absolute.
 */
	if (fpStream->_flag & (_IOWRT | _IORW))
	    {
	    if (nWhence == SEEK_CUR)
	        {
		lOffset += ftell (fpStream);
		nWhence  = SEEK_SET;
	        }
	    }
/*
 *  Allocate a buffer for the file if this is the first request for the
 *  file.
 */
        if (!anybuf(fpStream))
	    {
            _getbuf(fpStream);
	    }
/*
 *  Turn off the "ungetc" indication for the file.
 */
	fUngetc            = fp2Stream->_flag2 & _IOUNGETC;
	fp2Stream->_flag2 &= ~_IOUNGETC;
/*
 *  If the seek lOffset is relative to the current location then find the
 *  current location.
 */
	lPresentLoc = lseek (fdFile, 0L, SEEK_CUR);
	if (lPresentLoc == -1L)
	    {
	    nAnswer = -1;
	    }
        }

    if (nAnswer == -2 && nWhence == SEEK_CUR)
        {
	if (lOffset == 0L && !fUngetc)
	    {
	    nAnswer = 0;
	    }
	lOffset += lPresentLoc - fpStream->_cnt;
        }
/*
 *  Seek to the end of the file to determine the relative size of the
 *  file.
 */
    if (nAnswer == -2)
        {
        if ((nWhence == SEEK_END) || (fp2Stream->_flag2 & _IOFEOF)
            || (lOffset > lPresentLoc))
	    {
            lEndLoc = lseek(fdFile, 0L, SEEK_END);
	    if (lEndLoc < 0L)
	        {
		nAnswer = -1;
	        }
	    else
	        {
		if (lseek (fdFile, lPresentLoc, SEEK_SET) < 0)
		    {
		    nAnswer = -1;
		    }
	        }
/*
 *  Update the current position relative to the end of the file.
 */
	    if (nAnswer == -2)
	        {
		if (nWhence == SEEK_END)
		    {
		    lOffset += lEndLoc;
		    }
/*
 *   Set/Clear the EOF indication based upon the seek relative location
 *   within the file boundary.
 */
		fp2Stream->_flag2 &= ~_IOFEOF;
		if (lOffset > lEndLoc)
		    {
		    fp2Stream->_flag2 ^= _IOFEOF;
		    }
	        }
	    }
        }
/*
 *  Compute the buffer lOffset and determine if the new position will
 *  be within the current buffer.
 */
    if (nAnswer == -2)
        {
	nBufferSize = fp2Stream->_bufsiz;
    
	if (lOffset < 0L)
	    {
	    errno   = EINVAL;
	    nAnswer = -1;
	    }
        }
/*
 *  Attempt to find the buffer boundry for the block containing the position
 *  desired. Process this request only if this is *not* enf of file and the
 *  file is buffered.
 */
    if (nAnswer == -2)
        {
        if (!(fp2Stream->_flag2 & _IOFEOF) && !nbuf(fpStream))
	    {
            nNewLOffset = lOffset % nBufferSize;
            lSectorNum  = lOffset - nNewLOffset;
/*
 *  If the buffer is currently in memory then simply adjust the position.
 */
            if (fpStream->_cnt && !fUngetc &&
                (lSectorNum == ((lPresentLoc - 1L)/nBufferSize)*nBufferSize))
	        {
                fpStream->_cnt += (fpStream->_ptr - fpStream->_base);
	        }
/*
 *  seek to the starting location for the buffer.
 */
            else
	        {
		if (lseek (fdFile, lSectorNum, SEEK_SET) == -1L)
		    {
		    nAnswer = -1;
		    }
		else
		    {
		    fpStream->_cnt = read (fdFile,
					   fpStream->_base,
					   fp2Stream->_bufsiz);

		    if (fpStream->_cnt < 0)
		        {
			nAnswer        = -1;
			fpStream->_cnt = 0;
		        }
		    }
	        }
/*
 *  Adjust the file poitner to the proper lOffset
 */
	    if (nAnswer == -2)
	        {
		fpStream->_ptr  = &fpStream->_base[nNewLOffset];
		fpStream->_cnt -= nNewLOffset;
		nAnswer = 0;
	        }
	    }
/*
 *  The file is unbuffered or attempting to position beyond the end of the
 *  file.
 */
	else
	    {
	    nWhence = SEEK_SET;
	    }
        }
/*
 *  Flush the fpStream at this point and indicate that the buffer contents
 *  have not been read nor written.
 */
    if (nAnswer == -2)
        {
	fflush(fpStream);

	if (fpStream->_flag & _IORW)
	    {
	    fpStream->_flag &= ~(_IOWRT|_IOREAD);
	    }
/*
 *  Seek to the file location.
 */
	nAnswer = lseek(fdFile, lOffset, nWhence);
	if (nAnswer >= 0)
	    {
	    nAnswer = 0L;
	    }
        }
/*
 *  Return the answer to the caller.
 */
    FUNC_EXIT ("fseek");
    return (nAnswer);
    }
