/***
*fseek.c - reposition file pointer on a stream
*
*	Copyright (c) 1985-1992, Microsoft Corporation. All rights reserved.
*
*Purpose:
*	defines fseek() - move the file pointer to new place in file
*
*Revision History:
*	10-13-83  RN	initial version
*	06-26-85  TC	added code to allow variable buffer lengths
*	02-10-87  BCM	fixed '%' mistakenly used for '/'
*	03-04-87  JCR	added errno settings
*	04-16-87  JCR	added _IOUNGETC support for bug fix and changes whence
*			from unsigned int to int (ANSI conformance)
*	04-17-87  JCR	fseek() now clears end-of-file indicator flag _IOEOF
*			(for ANSI conformance)
*	04-21-87  JCR	be smart about lseek'ing to the end of the file and
*			back
*	09-17-87  SKS	handle case of '\n' at beginning of buffer (FCRLF flag)
*	09-24-87  JCR	fixed an incorrect access to flag _IOEOF
*	09-28-87  JCR	Corrected _iob2 indexing (now uses _iob_index() macro).
*	09-30-87  JCR	Fixed buffer allocation bug, now use _getbuf()
*	11-04-87  JCR	Multi-thread support
*	12-11-87  JCR	Added "_LOAD_DS" to declaration
*	01-13-88  JCR	Removed unnecessary calls to mthread fileno/feof/ferror
*	03-04-88  JCR	Return value from read() must be treated as unsigned
*			value
*	05-27-88  PHG	Merged DLL and normal versions
*	06-06-88  JCR	Optimized _iob2[] references
*	06-15-88  JCR	Near reference to _iob[] entries; improve REG variables
*	08-25-88  GJF	Don't use FP_OFF() macro for the 386
*	12-02-88  JCR	Added _IOCTRLZ support (fixes bug pertaining to ^Z at
*			eof)
*	04-12-89  JCR	Ripped out all of the special read-only code.  See the
*			comments in the routine header for more information.
*	08-17-89  GJF	Clean up, now specific to OS/2 2.0 (i.e., 386 flat
*			model). Also fixed copyright and indents.
*	02-15-90  GJF	Fixed copyright
*	03-19-90  GJF	Made calling type _CALLTYPE1, added #include
*			<cruntime.h> and removed #include <register.h>.
*	05-29-90  SBM   Use _flush, not [_]fflush[_lk]
*	07-23-90  SBM	Replaced <assertm.h> by <assert.h>
*	10-02-90  GJF	New-style function declarators.
*	01-21-91  GJF	ANSI naming.
*	03-27-92  DJM	POSIX support.
*	08-08-92  GJF	Use seek method constants!
*	08-26-92  GJF	Include unistd.h for POSIX build.
*
*******************************************************************************/

#include <cruntime.h>
#include <stdio.h>
#include <file2.h>
#include <assert.h>
#ifdef	_POSIX_
#include <unistd.h>
#else
#include <msdos.h>
#endif
#include <errno.h>
#include <malloc.h>
#include <io.h>
#include <stddef.h>
#include <internal.h>
#ifndef _POSIX_
#include <os2dll.h>
#endif

/***
*int fseek(stream, offset, whence) - reposition file pointer
*
*Purpose:
*
*	Reposition file pointer to the desired location.  The new location
*	is calculated as follows:
*				 { whence=0, beginning of file }
*		<offset> bytes + { whence=1, current position  }
*				 { whence=2, end of file       }
*
*	Be careful to coordinate with buffering.
*
*			- - - - - - - - - - - - -
*
*	[NOTE: We used to bend over backwards to try and preserve the current
*	buffer and maintain disk block alignment.  This ended up making our
*	code big and slow and complicated, and slowed us down quite a bit.
*	Some of the things pertinent to the old implimentation:
*
*	(1) Read-only: We only did the special code path if the file was
*	opened read-only (_IOREAD).  If the file was writable, we didn't
*	try to optimize.
*
*	(2) Buffering:	We'd assign a buffer, if necessary, since the
*	later code might need it (i.e., call _getbuf).
*
*	(3) Ungetc: Fseek had to be careful NOT to save the buffer if
*	an ungetc had ever been done on the buffer (flag _IOUNGETC).
*
*	(4) Control ^Z: Fseek had to deal with ^Z after reading a
*	new buffer's worth of data (flag _IOCTRLZ).
*
*	(5) Seek-to-end-and-back: To determine if the new seek was within
*	the current buffer, we had to 'normalize' the desired location.
*	This means that we sometimes had to seek to the end of the file
*	and back to determine what the 0-relative offset was.  Two extra
*	lseek() calls hurt performance.
*
*	(6) CR/LF accounting - When trying to seek within a buffer that
*	is in text mode, we had to go account for CR/LF expansion.  This
*	required us to look at every character up to the new offset and
*	see if it was '\n' or not.  In addition, we had to check the
*	FCRLF flag to see if the new buffer started with '\n'.
*
*	Again, all of these notes are for the OLD implimentation just to
*	remind folks of some of the issues involving seeking within a buffer
*	and maintaining buffer alignment.  As an aside, I think this may have
*	been a big win in the 'old days' on floppy-based systems but on newer
*	fast hard disks, the extra code/complexity overwhelmed any gain.
*
*			- - - - - - - - - - - - -
*
*Entry:
*	FILE *stream - file to reposition file pointer on
*	long offset - offset to seek to
*	int whence - origin offset is measured from (0=beg, 1=current pos,
*		     2=end)
*
*Exit:
*	returns 0 if succeeds
*	returns -1 and sets errno if fails
*	fields of FILE struct will be changed
*
*Exceptions:
*
*******************************************************************************/

#ifdef MTHREAD	/* multi-thread; define both fseek() and _lk_fseek() */

int _CALLTYPE1 fseek (
	FILE *stream,
	long offset,
	int whence
	)
{
	int retval;
#ifdef MTHREAD
	int index;
#endif

	assert(stream != NULL);

#ifdef MTHREAD
	index = _iob_index(stream);
#endif

	_lock_str(index);

	retval = _fseek_lk (stream, offset, whence);

	_unlock_str(index);

	return(retval);
}


/***
*_fseek_lk() - Core fseek() routine (stream is locked)
*
*Purpose:
*	Core fseek() routine; assumes that caller has the stream locked.
*
*	[See fseek() for more info.]
*
*Entry: [See fseek()]
*
*Exit:	[See fseek()]
*
*Exceptions:
*
*******************************************************************************/

int _CALLTYPE1 _fseek_lk (

#else	/* non multi-thread; just define fseek() */

int _CALLTYPE1 fseek (

#endif	/* rejoin common code */

	FILE *str,
	long offset,
	int whence
	)
{


	REG1 FILE *stream;

	assert(str != NULL);

	/* Init stream pointer */
	stream = str;

	if ( !inuse(stream) || ((whence != SEEK_SET) && (whence != SEEK_CUR) &&
	    (whence != SEEK_END)) ) {
		errno=EINVAL;
		return(-1);
	}

	/* Clear EOF flag */

	stream->_flag &= ~_IOEOF;

	/* If seeking relative to current location, then convert to
	   a seek relative to beginning of file.  This accounts for
	   buffering, etc. by letting fseek() tell us where we are. */

	if (whence == SEEK_CUR) {
		offset += _ftell_lk(stream);
		whence = SEEK_SET;
	}

	/* Flush buffer as necessary */

#ifdef _POSIX_
	/*
	 * If the stream was last read, we throw away the buffer so
	 * that a possible subsequent write will encounter a clean
	 * buffer.  (The Win32 version of fflush() throws away the
	 * buffer if it's read.)  Write buffers must be flushed.
	 */
	
	if ((stream->_flag & (_IOREAD | _IOWRT)) == _IOREAD) {
		stream->_ptr = stream->_base;
		stream->_cnt = 0;
	} else {
		_flush(stream);
	}
#else
	_flush(stream);
#endif


	/* If file opened for read/write, clear flags since we don't know
	what the user is going to do next. */

	if (stream->_flag & _IORW)
		stream->_flag &= ~(_IOWRT|_IOREAD);

	/* Seek to the desired locale and return. */

#ifdef _POSIX_
	return(lseek(fileno(stream), offset, whence) == -1L ? -1 : 0);
#else
	return(_lseek(_fileno(stream), offset, whence) == -1L ? -1 : 0);
#endif
}
