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

#include <trace.h>
/**************************************************************************
 *
 * Function:   read_nbuf
 *
 * Description:
 *    Read from a file table into a buffer when the file table has not
 *    been assigned a buffer at the present time.
 *
 * Entry:
 *    puchBuffer - Pointer to the buffer to be used for input
 *    pending    - Number of characters to be read
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The remainder of the data to be read or EOF to indicate an error.
 *
 *  This is a local function and is not exported.
 */

static int read_nbuf (unsigned char *puchBuffer, unsigned int pending, FILE *stream)
    {
    int   result;

    FUNC_ENTRY ("read_nbuf");
    while (pending != 0)
        {
	result = read (fileno(stream), puchBuffer, pending);
	
	if ((result == 0) || (result == -1))
	    {
	    stream->_flag |= ( result == -1 ? _IOERR : _IOEOF );
	    break;
	    }
	else
	    {
	    pending    -= result;
	    puchBuffer += result;
	    }
        }
    FUNC_EXIT ("read_nbuf");
    return (pending);
    }

/**************************************************************************
 *
 * Function:   read_nblk
 *
 * Description:
 *    Read from a file table into a buffer when the file is not to be
 *    buffered for normal character input processing.
 *
 * Entry:
 *    puchBuffer - Pointer to the buffer to be used for input
 *    pending    - Number of characters to be read
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The remainder of the data to be read or EOF to indicate an error.
 *
 *  This is a local function and is not exported.
 */

static int read_nblk (unsigned char *puchBuffer, unsigned int pending, FILE *stream)
    {
    int   result;

    FUNC_ENTRY ("read_nblk");
    while (pending != 0)
        {
	result = read (fileno (stream), puchBuffer, pending);
	if (result == 0)
	    {
	    stream->_flag |= _IOEOF;
	    break;
	    }
	if (result == -1)
	    {
	    stream->_flag |= _IOERR;
	    break;
	    }
	pending  -= result;
	puchBuffer += result;
        }
    FUNC_EXIT ("read_nblk");
    return (pending);
    }

/**************************************************************************
 *
 * Function:   read_nblk
 *
 * Description:
 *    Read from a file table into a buffer when the file has a pending
 *    buffer area assigned to it.
 *
 * Entry:
 *    puchBuffer - Pointer to the buffer to be used for input
 *    pending    - Number of characters to be read
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The remainder of the data to be read or EOF to indicate an error.
 *
 *  This is a local function and is not exported.
 */

static int read_blk (unsigned char *puchBuffer, unsigned int pending, FILE *stream)
    {
    size_t tmpcnt;
    int    result, orig_pending;
    int    blk_size;
    FUNC_ENTRY ("read_blk");
    orig_pending = pending;
/*
 *  Compute the size of the buffer associated with the file.
 *  There is no "buffer" for a string file.
 */
    if (stream->_flag & _IOSTRG)
        {
	blk_size = BUFSIZ;
        }
    else
        {
	FILE2 *stream2 = file2p (stream);
	blk_size = stream2->_bufsiz;
	if (blk_size <= 0)
	    {
	    blk_size = BUFSIZ;      /* Punt if the value is illegal */
	    }
        }
/*
 *  Process the complete request
 */
    while (pending != 0)
        {
/*
 *  Move the partial buffer to the caller's area.
 */
	tmpcnt = (unsigned int) stream->_cnt;
	if (tmpcnt != 0)
	    {
	    if (pending < (unsigned int) stream->_cnt)
	        {
		tmpcnt = pending;
	        }
	    
	    memcpy (puchBuffer, stream->_ptr, tmpcnt);
	    
	    stream->_ptr += tmpcnt;
	    stream->_cnt -= tmpcnt;

	    puchBuffer   += tmpcnt;
	    pending      -= tmpcnt;
	    continue;
	    }
/*
 *  The buffer is empty. If the request is larger than the buffer size
 *  then use the "unbuffered" procedure to fill the gap.
 */
	if (pending >= blk_size)
	    {
	    int blocks = pending / blk_size;
	    tmpcnt     = blocks * blk_size;
	    
	    result = read (fileno(stream), puchBuffer, tmpcnt);
	    if (result == 0)
	        {
		stream->_flag |= _IOEOF;
		break;
	        }
	    
	    if (result == -1)
	        {
		stream->_flag |= _IOERR;
		pending = EOF;
		break;
	        }
	    pending    -= result;
	    puchBuffer += result;
	    continue;
	    }
/*
 *  Fill the buffer on the stream. This will return the first character
 *  in the buffer. (It is basically the "getc" call.)
 */
	result = _filbuf(stream);
	if (result == EOF)
	    {
	    /* Return EOF only if we have not read anything */
	    if(pending == orig_pending) pending = EOF;
	    break;
	    }
/*
 *  Store the character and continue processing. It will normally transfer
 *  the remainder of the buffer at the top of the loop.
 */
	*puchBuffer++ = (unsigned char) result;
	--pending;
      }
    FUNC_EXIT ("read_blk");
    return (pending);
    }

/**************************************************************************
 *
 * Function:   fread
 *
 * Description:
 *    Read from an open file into the user buffer.
 *
 * Entry:
 *    puchBuffer - Pointer to the buffer to be used for input
 *    nSize      - Size of an item in the buffer
 *    nCount     - Number of items in the buffer
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The number of items actually read or EOF to indicate an error
 */

static int read_buf (unsigned char *puchBuffer, unsigned int pending, FILE *stream)
    {
    int   result;
/*
 *  Fetch the first character. This will force the buffer to be allocated
 *  should it be possible.
 */
    FUNC_ENTRY ("read_buf");
    result = getc (stream);
    if (result != EOF)
        {
	*puchBuffer++ = result;
	--pending;
/*
 *  If the file is still un-buffered then it will never be buffered. Do
 *  the I/O based upon the file being un-buffered. This is done with the
 *  low level read routine.
 */
	if (!bigbuf(stream))
	    {
	    result = read_nblk (puchBuffer, pending, stream);
	    }
	else
	    {
	    result = read_blk (puchBuffer, pending, stream);
	    }
        }
    FUNC_EXIT ("read_buf");
    return (result);
    }

/**************************************************************************
 *
 * Function:   fread
 *
 * Description:
 *    Read from an open file into the user buffer.
 *
 * Entry:
 *    pvBuffer   - Pointer to the buffer to be used for input
 *    nSize      - Size of an item in the buffer
 *    nCount     - Number of items in the buffer
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The number of items actually read or EOF to indicate an error
 */

size_t fread (void *pvBuffer, size_t size, size_t count, FILE * stream)
    {
    unsigned int total;
    int          result;
    FUNC_ENTRY ("fread");
/*
 *  Compute the total byte count to be read
 */
    total = size * count;
    if (total == 0)
        {
	result = 0;
        }
/*
 *  If the file does not have a buffer assigned to it at the present time
 *  then read the file in multiple blocks using a simple (fast) algorithm.
 */
    else
        {
	if (!anybuf (stream) && (total % BUFSIZ) == 0)
	    {
	    result = read_nbuf ((unsigned char *) pvBuffer, total, stream);
	    }
/*
 *  Otherwise the processing is more complicated.
 */
	else
	    {
	    result = read_buf ((unsigned char *) pvBuffer, total, stream);
	    }
/*
 *  Compute the number of items read into the buffer
 */
	if (result == EOF)
	    {
	    result = 0;
	    }
	else
	    {
	    result = (total - result) / size;
	    }
        }
/*
 *  Return the number of items actually read to the buffer or 0 to
 *  indicate an abnormal condition.
 */
    FUNC_EXIT ("fread");
    return (result);
    }


