/*
 *	osread( mode,recsiz,ioptr,scptr ) reads the next record from
 *	the designated input file into the scblk. recsiz determines
 *	whether the read should be line or raw mode. line mode records
 *	are teminated with a new-line character (the new-line is not
 *	put in the scblk though). raw records are simply the next recsiz
 *	characters.
 */

#include "spitblks.h"
#include "spitio.h"

int	osread( mode, recsiz, ioptr, scptr )

int	mode;
int	recsiz;
struct	ioblk	*ioptr;
struct	scblk	*scptr;

{
	register struct bfblk	*bfptr = ioptr -> buf;
	register char	*cp = scptr -> str;
	register char *bp;
	register int	cnt = 0;
	int	fdn = ioptr -> fdn;
	register int	n;

	/*
	 *	Handle unbuffered reads.
	 */

	if ( ioptr -> flg & IO_WRC ) {

		/*	no line mode with unbuffered	*/
		if ( mode > 0 )
			return	-2;

		/*	attempt to read in scblk	*/
		n	= read( fdn,cp,recsiz );

		/*	if read error then take action	*/
		if ( n < 0 )
			return	-2;

		/*	check for eof			*/
		if ( n == 0 )
			return	-1;

		/*	everything ok, so return	*/
		return	n;
	}

	/*
	 *	Handle buffered reads.
	 */

	if ( mode > 0 ) {

		/* line mode */
		register char	*savecp;
		char	savechar;

		/*
		 *	First phase:  copy characters to the result
		 *	buffer either until recsiz is exhausted or
		 *	we have copied the last character of a line.
		 *	This loop is speeded up by pretending that
		 *	the input line is no longer than the result.
		 */
		
		do {
			char	*oldbp;

			/* if the buffer is empty, try to fill it */
			if ( bfptr -> rem == 0 ) {
				n = read ( fdn, bfptr -> buf, bfptr -> siz );

				/* eof is only ok at the beginning of a line */
				if ( n == 0 )
					return cnt > 0? -2: -1;

				/* input errors are never ok */
				if ( n < 0 )
					return -2;

				bfptr -> off = 0;
				bfptr -> rem = n;
			}

			/* set n to max # chars we can process this time */
			n = recsiz - cnt;
			if ( n > bfptr -> rem )
				n = bfptr -> rem;
			
			/* point bp and oldbp at the first char to be copied */
			oldbp = bp = bfptr -> buf + bfptr -> off;

			/* plant a newline at the end of the valid input */
			savecp = bp + n;
			savechar = *savecp;
			*savecp = '\n';

			/* copy characters until we hit a newline */
			while ( *bp != '\n' )
				*cp++ = *bp++;
			
			/* restore the stolen character */
			*savecp = savechar;

			/* calculate how many characters were moved */
			n = bp - oldbp;
			cnt += n;
			bfptr -> off += n;
			bfptr -> rem -= n;

		} while ( bp == savecp && cnt < recsiz );
		/* loop until we hit a real \n or recsiz is exhausted */

		/*
		 *	Second phase: discard characters up to and
		 *	including the next newline in the input.
		 *	This loop is optimized to miminize startup
		 *	overhead, because it will usually be executed
		 *	only once (but never less than once!)
		 */
		do {
			/*
			 *	decrement count of characters remaining
			 *	in the buffer, check for buffer underflow
			 */
			if ( --bfptr -> rem < 0 ) {
				n = read ( fdn, bfptr -> buf, bfptr -> siz );

				/* eof and input errors are never ok */
				if ( n < 0 )
					return -2;

				bfptr -> off = 0;
				bfptr -> rem = n - 1;
			}

			/*
			 *	The buffer is guaranteed non-empty,
			 *	and the count of characters remaining
			 *	has already been decremented by 1.
			 *	Pick up a character and bump the offset.
			 */
			n = bfptr -> buf [bfptr -> off++];
		} while (n != '\n');
		/* loop until we see a newline */

	} else {
		/* raw mode, try to read exactly "recsiz" characters */
		while ( recsiz > 0 ) {
			/* if the buffer is empty, try to fill it */
			if ( bfptr -> rem == 0 ) {
				n = read ( fdn, bfptr -> buf, bfptr -> siz );

				/* input error, no good */
				if ( n < 0 )
					return -2;

				/* eof, return what we got so far */
				if ( n == 0 )
					break;

				bfptr -> rem = n;
				bfptr -> off = 0;
			}

			/* calculate how many chars we can move */
			n = bfptr -> rem;
			if ( n > recsiz )
				n = recsiz;
			bp = bfptr -> buf + bfptr -> off;

			/* update pointers to move n characters */
			cnt += n;
			recsiz -= n;
			bfptr -> off += n;
			bfptr -> rem -= n;

			/* move n characters from bp to cp */
			if ( n & 1 )
				*cp++ = *bp++;
			n >>= 1;
			if ( n > 0 ) {
				do {
					*cp++ = *bp++;
					*cp++ = *bp++;
				} while ( --n > 0 );
			}
		}

		/* if we couldn't make any progress, signal end of file */
		if ( cnt == 0 )
			return -1;	
	}

	return cnt;
}
