#include <stdio.h>
#include <stdlib.h>
#include "file2.h"
#include "internal.h"

#include <trace.h>
/************************************************************************
 *
 * Function:    setbuf
 *
 * Description:
 *    Ensure that the file has a buffer when one is needed. This function
 *    is a better version of setbuf as it allows the specification of the
 *    file mode and a variable length buffer.
 *
 * Entry:
 *    stream    - Pointer to the file stream
 *    pvBuffer  - Pointer to the buffer. If this value is NULL and type
 *                calls for a buffered file, the buffer is allocated from
 *                the heap area.
 *    type      - Type of buffering
 *                _IONBF - Not buffered
 *                _IOFBF - Fully buffered
 *                _IOLBF - Line buffered
 *    size      - Size of the buffer area area pointed to by pvBuffer
 *
 * Returns:
 *   0 if successful
 *   1 if there is no size and a buffer was required
 *   2 if the type is invalid
 *   3 if the buffer could not be allocated from the heap.
 */

int setvbuf (FILE *stream, void *pvBuffer, int nType, size_t size)
    {
    FILE2 *stream2 = file2p (stream);
    int   nAnswer  = 0;
    FUNC_ENTRY ("setvbuf");
/*
 *  Validate the parameters
 */
    if (nType != _IONBF)
        {
        if (size == 0)
	    {
	    nAnswer = 1;
	    }
	else
	    {
	    if (nType != _IOFBF && nType != _IOLBF)
	        {
		nAnswer = 2;
	        }
	    }
        }
/*
 *  Flush the buffer and release the previous buffer data.
 */
    if (nAnswer == 0)
        {
        fflush   (stream);
        _freebuf (stream);
/*
 *  If the file is unbuffered then reset the indicator flags
 */
	if (nType == _IONBF)
	    {
	    stream->_flag   |= _IONBF;
	    stream2->_flag2  = 0;
	    pvBuffer         = &(stream2->_charbuf);
	    size             = 1;
	    }  
/*
 *  If there is no buffer then allocate a buffer from the heap.
 */
	else
	    {
	    if (pvBuffer == NULL)
	        {
		pvBuffer = (void *) malloc (size);
		if (pvBuffer == NULL)
		    {
		    nAnswer = 3;
		    } 
/*
 *   One was allocated. Set the appropriate flags to indicate that
 *   the buffer must be free() when the file is closed.
 */
		else
		    {
		    stream->_flag  &= ~(_IONBF | _IOLBF | _IOFBF);
		    stream->_flag  |= _IOMYBUF;
		    stream2->_flag2 = 0;
		    }  
	        }
/*
 *  There is a user buffer.
 */
	    else
	        {
		_cflush++;
		stream->_flag  &= ~(_IOMYBUF | _IONBF | _IOFBF | _IOLBF);
		stream2->_flag2 = _IOYOURBUF;
	        }
/*
 *  Set the proper mode for the file
 */
	    if (nAnswer == 0)
	        {
		stream->_flag |= nType;
	        }
	    }
        }
/*
 *  Reset the count of characters in the buffers and return.
 */
    if (nAnswer == 0)
        {
	stream2->_bufsiz = size;
	stream->_ptr     =
        stream->_base    = (char *) pvBuffer;
	stream->_cnt     = 0;
        }
/*
 *  Return the status to the caller.
 */
    FUNC_EXIT ("setvbuf");
    return (nAnswer);
    }
