/* wasp - Copyright 1991 by Steven Reiz
 * see COPYING and wasp.c for further info
 * io.c, 30/5/91 - 2/6/91, 8/7/91, 8/12/91,
 * 27/12/91 - 1/1/92, 21/3/92
 */

/* all Wasp I/O should go through the functions which are defined
 * in this file, copen_in/out, cseek_in/out, cread, cwrite and wrl
 * for handling the input- and outputimagefiles,
 * cout_tmp, cout_default and ctmp_move for working with temporary
 * output files,
 * printe and pute for messages that cannot be handled by errorx,
 * init_counter, counter and erase_counter for producing a counter,
 * errorx (not called directly, use error0, error1, etc.) for error-
 * messages.
 */

static char *sourcefile=__FILE__;

#include "wasp.h"

#ifdef AMIGA
#define TMP_PREFIX "t:"
#else
#define TMP_PREFIX "/tmp/"
#endif

extern int errno;

/* to make it possible to read the start of the input file multiple
 * times (so each reader can try to recognize it), even when reading
 * from stdin, the first MAXINBUF bytes are stored in a buffer, inbuf.
 */
#define MAXINBUF 256
static char *inbuf;
static int inbufn;		/* actual number of bytes in inbuf */
static long infdpos, incpos;	/* the real and virtual positions of infd */
static long outfdpos, outcpos;  /* and outfd */

/* temporary file stuff */
static int ran_nr;		/* for tmp files, to not clash with other wasps */
static long tmp_ctr=0;
static int tmpfd= -1;
static char tmpfilename[30];


void
copen_in(char *filename)
{
	srand(time(NULL));
	ran_nr=rand();

	if (!strcmp(filename, "-"))
		infd=0;
	else if ((infd=open(filename, O_RDONLY))<0)
		error1(E0_FATAL, E1_IO, E2_OPEN, E3_ERRNO, filename);
	outcpos=0;
	inbuf=Malloc(MAXINBUF);
	inbufn=read(infd, inbuf, MAXINBUF);
	if (inbufn<0)
		error1(E0_FATAL, E1_IO, E2_READ, E3_ERRNO, filename);
	else if (!inbufn)
		error1(E0_FATAL, E1_IO, E2_READ, E3_UNEXPEND, infilename);
	infdpos=inbufn;
}


void
copen_out(char *filename)
{
	if (!strcmp(filename, "-"))
		outfd=1;
	else if ((outfd=open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644))<0)
		error1(E0_FATAL, E1_IO, E2_CREAT, E3_ERRNO, filename);
	outfdpos=0;
	outcpos=0;
}


long
cseek_in(long offset, int whence)
{
	if (whence==0)
		incpos=offset;
	else if (whence==1)
		incpos+=offset;
	else
		assert(0);
	assert(incpos>=0);
	if (incpos!=infdpos && incpos>=inbufn) {
		if (infd>2) {
			if (lseek(infd, incpos, 0)<0)
				error1(E0_FATAL, E1_IO, E2_SEEK, E3_ERRNO, infilename);
			infdpos=incpos;
		} else if (incpos>infdpos) {
			char *m;
			int n;

			n=incpos-infdpos;
			m=Malloc(n);
			if (read(infd, m, n)!=n)
				error1(E0_FATAL, E1_IO, E2_READ, E3_ERRNO, infilename);
			free(m);
			infdpos=incpos;
		} else
			error0(E0_FATAL, E1_IO, E2_SEEK, E3_SEEK_STDIN);
	}
	return incpos;
}


long
cseek_out(long offset, int whence)
{
	assert(tmpfd== -1);
	if (whence==0)
		outcpos=offset;
	else if (whence==1)
		outcpos+=offset;
	else {
		assert(!offset);
		if ((outfdpos=lseek(outfd, 0L, 2))<0)
			error1(E0_FATAL, E1_IO, E2_SEEK, E3_ERRNO, outfilename);
		outcpos=outfdpos;
	}
	assert(outcpos>=0);
	if (outcpos!=outfdpos) {
		if (outfd>2) {
			if (lseek(outfd, outcpos, 0)<0)
				error1(E0_FATAL, E1_IO, E2_SEEK, E3_ERRNO, outfilename);
			outfdpos=outcpos;
		} else
			error0(E0_FATAL, E1_IO, E2_SEEK, E3_SEEK_STDOUT);
	}
	return outcpos;
}


int
read1(void *buf, int len)
{
	int todo, rr;

	todo=len;
	if (incpos<inbufn) {
		int min;

		min=inbufn-incpos;
		if (min>todo)
			min=todo;
		memcpy(buf, inbuf+incpos, min);
		todo-=min;
		incpos+=min;
		if (!todo)
			return min;
	}
	assert(incpos==infdpos);
	rr=read(infd, (char *)buf+(len-todo), todo);
	if (rr<0) {
		if (todo!=len)
			return len-todo;
		return rr;
	}
	infdpos+=rr;
	incpos+=rr;
	todo-=rr;
	return len-todo;
}


/* cread_type       er     eof    varlen  misc
 * CREAD_STRICT     fatal  fatal  fatal   default
 * CREAD_NONFATAL   error  error  error
 * CREAD_VARLEN     error  error
 * CREAD_OPTIONAL   error
 */
void
cread(void *buf, int len)
{
	assert(cread_type>=CREAD_STRICT && cread_type<=CREAD_OPTIONAL);
	if ((cread_result=read1(buf, len))!=len) {
		if (cread_result<0) {
			if (cread_type==CREAD_STRICT)
				error1(E0_FATAL, E1_IO, E2_READ, E3_ERRNO, infilename);
			else
				error1(E0_ERROR, E1_IO, E2_READ, E3_ERRNO, infilename);
		} else if (cread_type==CREAD_STRICT)
			error1(E0_FATAL, E1_IO, E2_READ, E3_UNEXPEND, infilename);
		else if (cread_type==CREAD_NONFATAL
		 || (cread_type==CREAD_VARLEN && !cread_result))
			error1(E0_ERROR, E1_IO, E2_READ, E3_UNEXPEND, infilename);
	}
}


void
cwrite(void *buf, int len)
{
	if (tmpfd== -1) {
		assert(outcpos==outfdpos);
		if (write(outfd, buf, len)!=len)
			error1(E0_FATAL, E1_IO, E2_WRITE, E3_ERRNO, outfilename);
		outfdpos+=len;
		outcpos+=len;
	} else {
		if (write(tmpfd, buf, len)!=len)
			error1(E0_FATAL, E1_IO, E2_WRITE, E3_ERRNO, tmpfilename);
	}
}


void
wrl(unsigned long l)
{
	cwrite(&l, 4);	/* BYTEORDER */
}


void
wrs(unsigned short s)
{
	cwrite(&s, 2);	/* BYTEORDER */
}


void
cout_tmp(void)
{
	assert(tmpfd== -1);
	sprintf(tmpfilename, "%swasp.%04d.%03ld", TMP_PREFIX, ran_nr%10000,
	 tmp_ctr%1000L);
	if ((tmpfd=open(tmpfilename, O_WRONLY|O_CREAT|O_TRUNC, 0644))<0)
		error1(E0_FATAL, E1_IO, E2_CREAT, E3_ERRNO, tmpfilename);
}


void
cout_default(long *tmp_num, long *tmp_size)
{
	assert(tmpfd!= -1);
	*tmp_size=lseek(tmpfd, 0L, 1);
	*tmp_num=tmp_ctr++;
	close(tmpfd);
	tmpfd= -1;
}


void
ctmp_move(long tmp_num)
{
	int fd, n;
	char *buf;
#define CTMP_BUF 16384
	long len=0;

	assert(tmp_ctr>tmp_num && tmpfd== -1);
	buf=Malloc(CTMP_BUF);
	sprintf(tmpfilename, "%swasp.%04d.%03ld", TMP_PREFIX, ran_nr%10000,
	 tmp_num%1000L);
	if ((fd=open(tmpfilename, O_RDONLY))<0)
		error1(E0_FATAL, E1_IO, E2_OPEN, E3_ERRNO, tmpfilename);
	assert(outcpos==outfdpos);
	while ((n=read(fd, buf, CTMP_BUF))>0) {
		if (write(outfd, buf, n)!=n)
			error1(E0_FATAL, E1_IO, E2_WRITE, E3_ERRNO, outfilename);
		len+=n;
	}
	if (n<0)
		error1(E0_FATAL, E1_IO, E2_READ, E3_ERRNO, tmpfilename);
	outfdpos+=len;
	outcpos+=len;
	close(fd);
	unlink(tmpfilename);
	free(buf);
}


int
printe(char *s, ...)
{
	va_list argp;
	int res;

	va_start(argp, s);
	res=vfprintf(stderr, s, argp);
	va_end(argp);
	return res;
}


void
pute(char c)
{
	putc(c, stderr);
}


static int co, cur, step, dir=0, len, last1;

void
init_counter(int start, int end, int instep, char *s, ...)
{
	va_list argp;

	va_start(argp, s);
	if (start<=end)
		dir=1;
	else
		dir= -1;
	step=instep;
	if (step<0)
		step= -step;
	cur=start;
	co=1;
	last1=end;
	len=printe("\r       [%d..%d] ", start, end)-1;
	if (s)
		len+=vfprintf(stderr, s, argp);
	pute('\r');
	fflush(stderr);
	va_end(argp);
}


void
counter(void)
{
	if (--co && (co!=1 || ((dir<0 && cur-step>last1) || (dir>=0 && cur+step<last1))))
		return;
	printe("\r%6d", cur);
	co=step;
	if (dir<0)
		cur-=step;
	else
		cur+=step;
	fflush(stderr);
}


void
erase_counter(char *s, ...)
{
	va_list argp;
	int l1;

	va_start(argp, s);
	pute('\r');
	if (s)
		l1=vfprintf(stderr, s, argp);
	else
		l1=0;
	while (l1++ <len)
		pute(' ');
	pute(s ? '\n' : '\r');
	fflush(stderr);
	dir=0;
	va_end(argp);
}


void
prin1(char *p)
{
	if (p)
		printe("%s ", p);
}


void
errorx(long code, ...)
{
	va_list argp;
	char c0, c1, c2, c3;
	char *p;
	int l1;

	va_start(argp, code);
	if (dir) {
		pute('\r');
		l1=0;
		while (l1++ <len)
			pute(' ');
		pute('\r');
		fflush(stderr);
	}
	c0=code>>24;
	c1=code>>16;
	c2=code>>8;
	c3=code;
	if (c0==E0_INTERNAL) {
		printe("internal error in wasp %s, file %s, line %ld; please report to sreiz@cs.vu.nl\n",
		 version+14, va_arg(argp, char *), code&0x00ffffff);
		exit(1);
	}
	assert(c0>E0_INTERNAL && c0<E0_NUM && c1>0 && c1<E1_NUM && c2>0
	 && c2<E2_NUM && c3>0 && c3<E3_NUM);
	prin1(e0_s[c0].s1);
	prin1(e1_s[c1]);
	prin1(e2_s[c2]);
	p=e0_s[c0].s2;
	if (p)
		printe("%s", p);
	if (c3==E3_ERRNO) {
		printe(" in ");
		perror(va_arg(argp, char *));
	} else {
		p=e3_s[c3];
		if (p) {
			printe(": ");
			vfprintf(stderr, p, argp);
		}
		pute('\n');
	}
	if (c0==E0_FATAL)
		exit(1);
	va_end(argp);
}
