#include	"config.h"
#include	<sys/types.h>
#if HAVE_SYS_STAT_H
#include	<sys/stat.h>
#endif
#include	<errno.h>

#include	"mio.h"
#include	"funcs.h"
#include	"buffer.h"
#include	"rfc2045/rfc2045.h"

int mopen(const char *fname, int flags, mode_t mode)
{
int	rc;

	while ((rc=open(fname, flags, mode)) < 0 && errno == EINTR)
		;
	return (rc);
}

int mread(int fd, void *buf, size_t count)
{
int	n;

	while ((n=read(fd, buf, count)) < 0 && errno == EINTR)
		;
	return (n);
}

int mwrite(int fd, const void *buf, size_t count)
{
int	n;

	while ((n=write(fd, buf, count)) < 0 && errno == EINTR)
		;
	return (n);
}

off_t mseek(int fd, off_t off, int whence)
{
off_t	n;

	while ((n=lseek(fd, off, whence)) < 0 && errno == EINTR)
		;
	return (n);
}

int mclose(int fd)
{
int	n;

	while ((n=close(fd)) < 0 && errno == EINTR)
		;
	return (n);
}

//-------------------------------------------------------------------------

Mio::Mio() : fd_(-1), readcnt(0), writecnt(0), err(0), rfc2045p(0)
{
}

Mio::~Mio() { Close(); }

void Mio::Close()
{
	if (fd_ < 0)	return;
	if (writeptr > buf)	flush(-1);
	mclose(fd_);
	fd_= -1;
}

int Mio::Open(const char *filename, int flags, mode_t m)
{
	fd(mopen(filename, flags, m));
	return (fd_);
}

int Mio::fill()
{
	readcnt=0;
	if (fd_ < 0 || err)	return (-1);
	if (writeptr > buf && flush(-1) < 0)	return (-1);

	readstartpos=tell();
	readcnt=mread(fd_, buf, sizeof(buf));
	readsize=readcnt;

	if (readcnt <= 0)
	{
		readcnt=0;
		readsize=0;
		err= -1;
		return (-1);
	}
	if (rfc2045p)
		rfc2045_parse(rfc2045p, (const char *)buf, readcnt);

	--readcnt;
	return (*(readptr=buf)++);
}

int Mio::flush(int c)
{
	if (fd_ < 0 || err)	return (-1);
	if (readcnt && mseek(fd_, -readcnt, SEEK_CUR) < 0)	return (-1);
	readcnt=0;
	readptr=buf;

	while (writeptr > buf)
	{
	int	n;

		if ((n=mwrite(fd_, buf, writeptr-buf)) <= 0)
		{
			err= -1;
			return (-1);
		}
		memorycopy(buf, buf+n, writeptr-buf-n);
		writeptr -= n;
	}

	writecnt=sizeof(buf);
	if (c >= 0)
	{
		--writecnt;
		*writeptr++ = c;
	}
	return (0);
}

off_t Mio::Rewind()
{
	readstartpos= -1;

	if (pubseekpos(0) == static_cast<off_t>(-1))
		return static_cast<off_t>(-1);

	return 0;
}

off_t Mio::pubseekpos(off_t p)
{
	return seek(p, SEEK_SET);
}

off_t Mio::seek(off_t off, int whence)
{
	if (fd_ < 0)	return (-1);
	if (writeptr > buf && flush(-1) < 0)	return (-1);
	if (whence == SEEK_CUR && err == 0)	off -= readcnt;

	// Optimize - seek can be satisfied within the read buffer.

	if (whence == SEEK_SET && readstartpos != -1 &&
		off >= readstartpos && off <= readstartpos + readsize)
	{
		readptr = buf + (off - readstartpos);
		readcnt = readsize - (off - readstartpos);
		return (off);
	}

	if (mseek(fd_, off, whence) < 0)
	{
		err= -1;
		return (-1);
	}
	err=0;
	readcnt=0;
	readptr=buf;
	return (off);
}

int Mio::write(const void *p, int cnt)
{
const unsigned char *cp=(const unsigned char *)p;
int	done=0;

	if (fd_ < 0 || err)	return (-1);

	while (cnt)
	{
	int	l;

		if (writecnt == 0 && flush() < 0)
		{
			if (done == 0)	done= -1;
			break;
		}

		if (writeptr > buf || cnt <= writecnt)
		{
			l=writecnt;

			if (l > cnt)	l=cnt;
			memcpy(writeptr, cp, l);
			writecnt -= l;
			writeptr += l;
		}
		else
		{
			l=mwrite(fd_, cp, cnt);
			if (l <= 0)
			{
				if (done == 0)	done= -1;
				break;
			}
		}

		cp += l;
		done += l;
		cnt -= l;
	}
	return (done);
}

off_t Mio::tell()
{
	if (fd_ < 0)	return (0);

off_t	p=mseek(fd_, 0L, SEEK_CUR);

	if (p == -1)	return (-1);
	return (p + (writeptr - buf) - readcnt);
}

Mio &Mio::operator<<(const std::string &b)
{
	write( b.c_str(), b.size() );
	return (*this);
}

MioStdio mout(1), merr(2);

MioStdio::MioStdio(int f)
{
	fd(f);
}

MioStdio::~MioStdio()
{
	fd(-1);
}

int MioStdio::write(const void *p, int cnt)
{
int n;

	n=Mio::write(p, cnt);
	if (n > 0)	Mio::flush();
	return (n);
}

extern MioStdio mout, merr;
