
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <limits.h>


/* Note: This def of READING is ok since 1st ungetc puts in buf. */
#define READING(fp) (fp->bufstart < fp->bufread)
#define WRITING(fp) (fp->bufwrite > fp->bufstart)

#define READABLE(fp) (fp->bufread != 0)
#define WRITEABLE(fp) (fp->bufwrite != 0)
#define EOF_OR_ERROR(fp) (fp->mode & (__MODE_EOF | __MODE_ERR))

/***********************************************************************/
/* BUILD TIME OPTIONS                                                  */
/***********************************************************************/
/*
 * FIXED_STREAMS must be >= 3 and FIXED_BUFFERS must be >= 2.
 * As a feature, these can be increased, although this is probably
 * only useful if DISABLE_DYNAMIC is set to 1 below.
 */

#define FIXED_STREAMS 3
#define FIXED_BUFFERS 2

/*
 * As a feature, you can build uClibc with no dynamic allocation done
 * by the stdio package.  Just set DISABLE_DYNAMIC to nonzero.  Note that
 * use of asprintf, getdelim, or getline will pull malloc into the link.
 *
 * Note: You can't trust FOPEN_MAX if DISABLE_DYNAMIC != 0.
 */
#define DISABLE_DYNAMIC 0

/*
 * As a feature, you can try to allow setvbuf calls after file operations.
 * Setting FLEXIBLE_SETVBUF to nonzero will cause setvbuf to try to fflush
 * any buffered writes or sync the file position for buffered reads.  If it
 * is successful, the buffer change can then take place.
 */
#define FLEXIBLE_SETVBUF 0
/***********************************************************************/



FILE *stdin;
FILE *stdout;
FILE *stderr;


int fputc(int c, FILE *fp)
{
	unsigned char buf[1];

	*buf = (unsigned char) c;

	if (fwrite(buf, 1, fp)) {
		return (unsigned char) c;
	}
	return EOF;
}


int fgetc(FILE *fp)
{
	unsigned char buf[1];

	if (fread(buf, 1, fp)) {
		return *buf;
	}
	return EOF;
}


int fflush(FILE *fp)
{
	int rv;

	return rv;
}

/* Nothing special here ... */
char *fgets(char *s, int count, FILE *fp)
{
	int ch;
	char *p;
	
	p = s;
	while (count-- > 1) {		/* Guard against count arg == INT_MIN. */
		ch = getc(fp);
		if (ch == EOF) {
			break;
		}
		*p++ = ch;
		if (ch == '\n') {
			break;
		}
	}
	if (ferror(fp) || (s == p)) {
		return 0;
	}
	*p = 0;
	return s;
}

char *gets(char *str) /* This is an UNSAFE function! */
{
	/* 
	 * Strictly speaking, this implementation is incorrect as the number
	 * of chars gets can read should be unlimited.  However, I can't
	 * imagine anyone wanting to gets() into a buffer bigger than INT_MAX.
	 *
	 * Besides, this function is inherently unsafe and shouldn't be used.
	 */
	return fgets(str, INT_MAX, stdin);
}


int fputs(const char *str, FILE *fp)
{
	int n;

	n = strlen(str);

	fwrite((const unsigned char *)str, n, fp);
	if (fp->mode & __MODE_ERR) {
		n = EOF;
	}
	return n;
}

int puts(const char *str)
{
	int n;

	n = fputs(str, stdout);	/* Let next fputc handle EOF or error. */
	if (fputc('\n', stdout) == EOF) { /* Don't use putc since we want to */
		return EOF;				/* fflush stdout if it is line buffered. */
	}
	return n + 1;
}

/*
 * fread will often be used to read in large chunks of data calling read()
 * directly can be a big win in this case. Beware also fgetc calls this
 * function to fill the buffer.
 */
size_t fread(buf, size, nelm, fp)
void *buf;
size_t size;
size_t nelm;
FILE *fp;
{
	size_t * read;
	
	DL_FsReadFile( buf, size, nelm, fp->fd, &read );
	
	return &read;
}
/*
 * Like fread, fwrite will often be used to write out large chunks of
 * data; calling write() directly can be a big win in this case.
 * 
 * But first we check to see if there's space in the buffer.
 */
size_t fwrite(buf, size, nelm, fp)
const void *buf;
size_t size;
size_t nelm;
FILE *fp;
{
	size_t * written;
	
	DL_FsWriteFile( buf, size, nelm, fp->fd, &written );
	
	return &written;
	
}



void rewind(fp)
FILE *fp;
{
	clearerr(fp);				/* Clear errors first, then seek in case */
	fseek(fp, 0, SEEK_SET);		/* there is an error seeking. */
}


int fseek(FILE *fp, long int offset, int ref)
{

	return DL_FsFSeekFile( fp->fd, offset, ref );
}

long ftell(fp)
FILE *fp;
{
	/* Note: can't do fflush here since it would discard any ungetc's. */
	off_t pos;

    pos = lseek(fp->fd, 0, SEEK_CUR); /* Get kernels idea of position. */
	if (pos < 0) {
		return -1;
	}

	if (WRITING(fp)) {
		pos += (fp->bufpos - fp->bufstart);	/* Adjust for buffer position. */
	} else if (READING(fp)) {
	    pos -= (fp->bufread - fp->bufpos);	/* Adjust for buffer position. */
		if (fp->mode & __MODE_UNGOT) {
			--pos;
		}
		if (pos < 0) {			/* ungetcs at start of file? */
			__set_errno(EIO);
			pos = -1;
		}
	}

	return pos;
}

int fclose(fp)
FILE *fp;
{
	int rv;

	rv = 0;

	return rv;
}


void setbuffer(FILE *fp, char *buf, size_t size)
{
	int mode;

	mode = _IOFBF;
	if (!buf) {
		mode = _IONBF;
	}
	setvbuf(fp, buf, mode, size);
}


int setvbuf(FILE *fp, char *buf, int mode, size_t size)
{
	int allocated_buf_flag;

	if ((mode < 0) || (mode > 2)) {	/* Illegal mode. */
		return EOF;
	}

#if FLEXIBLE_SETVBUF
	/* C89 standard requires no ops before setvbuf, but we can be flexible. */
	/* NOTE: This will trash any chars ungetc'd!!! */
	if (fseek(fp, 0, SEEK_CUR)) {
		return EOF;
	}
#endif

	/* Note: If size == 2 we could use FILE's builting buffer as well, but */
	/* I don't think the benefit is worth the code size increase. */
	if ((mode == _IONBF) || (size < 1)) {
		size = 1;				/* size == 1 _REQUIRED_ for _IONBF!!! */
		buf = fp->unbuf;
	}

	fp->mode &= ~(__MODE_BUF);	/* Clear current mode */
	fp->mode |= mode;			/*   and set new one. */

	allocated_buf_flag = 0;
	if ((!buf) && (size != (fp->bufend - fp->bufstart))) {
		/* No buffer supplied and requested size different from current. */
		allocated_buf_flag = __MODE_FREEBUF;
		if (!(buf = _alloc_stdio_buffer(size))) {
			return EOF;			/* Keep current buffer. */
		}
	}

	if (buf && (buf != (char *) fp->bufstart)) { /* Want different buffer. */
		_free_stdio_buffer_of_file(fp);	/* Free the old buffer. */
		fp->mode |= allocated_buf_flag;	/* Allocated? or FILE's builtin. */
		fp->bufstart = buf;
		fp->bufend = buf + size;
		fp->bufpos = fp->bufstart;
		if (READABLE(fp)) {
			fp->bufread = fp->bufstart;
		}
		if (WRITEABLE(fp)) {
			fp->bufwrite = fp->bufstart;
		}
	}

	return 0;
}

void setbuf(FILE *fp, char *buf)
{
	int mode;

	mode = _IOFBF;
	if (!buf) {
		mode = _IONBF;
	}
	setvbuf(fp, buf, mode, BUFSIZ);
}

void setlinebuf(FILE *fp)
{
	setvbuf(fp, NULL, _IOLBF, BUFSIZ);
}

/*
/* 
 * NOTE: Only one character of pushback is guaranteed, although sometimes
 * it is possible to do more.  You have 1 plus as many characters of pushback
 * as have been read since that last buffer-fill.
 */
int ungetc(c, fp)
int c;
FILE *fp;
{
	unsigned char *p;

	/* If can't read or there's been an error, or c == EOF, or ungot slot
	 * already filled, then return EOF */
		/*
		 * This can only happen if an fgetc triggered a read (that filled
		 * the buffer for case 2 above) and then we ungetc 3 chars.
		 */
	if (!READABLE(fp) || (fp->mode & (__MODE_UNGOT | __MODE_ERR))
		|| (c == EOF) ) {
		return EOF;
	}

	if (WRITING(fp)) {			/* Commit any write-buffered chars. */
		fflush(fp);
	}

	if (fp->bufpos > fp->bufstart) { /* We have space before bufpos. */
		p = --fp->bufpos;
	} else if (fp->bufread == fp->bufpos) { /* Buffer is empty. */
		p = fp->bufread++;
	} else {
		fp->mode |= __MODE_UNGOT;
		p = &(fp->ungot);
	}
	fp->mode &= ~(__MODE_EOF);	/* Clear EOF indicator. */

	if (*p != (unsigned char) c) { /* Don't store if same, because could */
		*p = (unsigned char) c;	/* be sscanf from a const string!!! */
	}

	return c;
}


FILE *fopen(const char *__restrict filename,
			const char *__restrict mode)
{
	return __fopen(filename, -1, NULL, mode, 0);
}

FILE *freopen(__const char *__restrict filename,
			  __const char *__restrict mode, FILE *__restrict fp)
{

	fclose( fp );				/* Close the file. */

	return __fopen(filename, -1, fp, mode, 0);
}

/*
#ifdef L_fsfopen
FILE *fsfopen(__const char *__restrict filename,
			  __const char *__restrict mode, FILE *__restrict fp)
{
	fp->mode = _IOFBF;
	fp->bufstart = fp->unbuf;
	fp->bufend = fp->unbuf + sizeof(fp->unbuf);

	return __fopen(filename, -1, fp, mode, 0);
}
#endif*/

FILE *fdopen(FS_HANDLE_T fd, __const char *mode)
{
	return __fopen(NULL, fd, NULL, mode, 0);
}


int getc(FILE *stream)
{
    return(((stream)->bufpos >= (stream)->bufread)?  fgetc(stream) :
		(*(stream)->bufpos++));
}

int putc(int c, FILE *stream)
{
    return(((stream)->bufpos >= (stream)->bufwrite)?  fputc((c), (stream)) :
		(unsigned char) (*(stream)->bufpos++ = (c)) );
}


int getchar(void)
{
	return getc(stdin);
}

int putchar(int c)
{
	return putc(c, stdout);
}

void clearerr(FILE *fp)
{
	fp->mode &= ~(__MODE_EOF | __MODE_ERR);
}


int feof(FILE *fp)
{
  	return fp->mode & __MODE_EOF;
}


int ferror(FILE *fp)
{
	return fp->mode & __MODE_ERR;
}

int fileno(FILE *fp)
{
	return fp->fd;
}

int fgetpos(FILE *fp, fpos_t *pos)
{
	fpos_t p;

	if (!pos) {					/* NULL pointer. */
		__set_errno(EINVAL);
		return -1;
	}

	if ((p = ftell(fp)) < 0) {	/* ftell failed. */
		return -1;				/* errno set by ftell. */
	}

	*pos = p;
	return 0;
}


int fsetpos(FILE *fp, __const fpos_t *pos)
{
	if (pos) {					/* Pointer ok. */
		return fseek(fp, *pos, SEEK_SET);
	}
	__set_errno(EINVAL);				/* NULL pointer. */
	return EOF;
}

#endif /* __UCLIBC_HAVE_LFS__ */
#endif

