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

/**************************************************************************
 *
 * Function:   write_nbuf
 *
 * Description:
 *    Write a buffer to the file table 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 write
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The remainder of the data to be write or EOF to indicate an error.
 *
 *  This is a local function and is not exported.
 */

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

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

/**************************************************************************
 *
 * Function:   write_nblk
 *
 * Description:
 *    Write from the buffer to the file table 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 write
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The remainder of the data to be write or EOF to indicate an error.
 *
 *  This is a local function and is not exported.
 */

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

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

/**************************************************************************
 *
 * Function:   write_blk
 *
 * Description:
 *    Write from the buffer to a file table 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 write
 *    stream     - Pointer to the stream to be used for the input
 *
 * Returns:
 *    The remainder of the data to be write or EOF to indicate an error.
 *
 *  This is a local function and is not exported.
 */

static int write_blk (unsigned char *puchBuffer, unsigned int pending, FILE *stream)
    {
    size_t tmpcnt;
/*
 *  Process the complete request
 */
    while (pending != 0)
        {
/*
 *  If there is space in the buffer then move the first block to the buffer
 *  area.
 */
	tmpcnt = (unsigned int) stream->_cnt;
	if (tmpcnt != 0)
	    {
	    if (pending < (unsigned int) stream->_cnt)
	        {
		tmpcnt = pending;
	        }
	    
	    memcpy (stream->_ptr, puchBuffer, tmpcnt);
	    
	    stream->_ptr += tmpcnt;
	    stream->_cnt -= tmpcnt;

	    puchBuffer   += tmpcnt;
	    pending      -= tmpcnt;
	    continue;
	    }
/*
 *  The buffer is full. Use the "putc" macro to write the next character
 *  to the buffer and it should flush the pending buffer to the output
 *  file.
 */
	if (putc (*puchBuffer, stream) == EOF)
	    {
	    pending = EOF;
	    break;
	    }
	++puchBuffer;
	--pending;
        }
/*
 *  Return the pending count to the caller.
 */
    return (pending);
    }

/**************************************************************************
 *
 * Function:   write_buf
 *
 * Description:
 *    Write from a buffer to the file table when a buffer has been
 *    allocated to the file table.
 *
 * 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 write or EOF to indicate an error
 */

static int write_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.
 */
    result = putc (*puchBuffer, stream);
    if (result != EOF)
        {
	++puchBuffer;
	--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 write routine.
 */
	if (!bigbuf(stream))
	    {
	    result = write_nblk (puchBuffer, pending, stream);
	    }
	else
	    {
	    result = write_blk (puchBuffer, pending, stream);
	    }
        }
    return (result);
    }

/**************************************************************************
 *
 * Function:   fwrite
 *
 * Description:
 *    Write from a buffer to the open file.
 *
 * 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 written or EOF to indicate an error
 */

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


