#include "sam.h"
#include "parse.h"

extern	jmp_buf	mainloop;

char	errfile[64];
String	plan9cmd;	/* null terminated */
void	checkerrs(void);
Buffer plan9buf;

static void
procplan9pipe(void *arg)
{
	int io, retcode;
	long l, m;
	struct { int pipe1[2]; int pipe2[2]; Channel *sync; } *a1;

	a1 = arg;
	memset(&plan9buf, 0, sizeof plan9buf);

	close(a1->pipe2[0]);
	io = a1->pipe2[1];
	sendul(a1->sync, 0);
	a1 = nil; USED(a1);

	/*
	 * It's ok if we get SIGPIPE here
	 */
	if(retcode=!setjmp(mainloop)){	/* assignment = */
		char *c;
		for(l = 0; l<plan9buf.nc; l+=m){
			m = plan9buf.nc-l;
			if(m>BLOCKSIZE-1)
				m = BLOCKSIZE-1;
			bufread(&plan9buf, l, genbuf, m);
			genbuf[m] = 0;
			c = Strtoc(tmprstr(genbuf, m+1));
			Write(io, c, strlen(c));
			free(c);
		}
	}
	threadexits(retcode? "error" : 0);
}

static void
procplan9(void *arg)
{
	int fd;
	struct { int pipe1[2]; int type; Channel *pidc; } *a;
	struct { int pipe1[2]; int pipe2[2]; Channel *sync; } a1;

	a = arg;
	if(downloaded){	/* also put nasty fd's into errfile */
		fd = create(errfile, 1, 0666L);
		if(fd < 0)
			fd = create("/dev/null", 1, 0666L);
		dup(fd, 2);
		close(fd);
		/* 2 now points at err file */
		if(a->type == '>')
			dup(2, 1);
		else if(a->type=='!'){
			dup(2, 1);
			fd = open("/dev/null", 0);
			dup(fd, 0);
			close(fd);
		}
	}
	if(a->type != '!') {
		if(a->type=='<' || a->type=='|')
			dup(a->pipe1[1], 1);
		else if(a->type == '>')
			dup(a->pipe1[0], 0);
		close(a->pipe1[0]);
		close(a->pipe1[1]);
	}
	if(a->type == '|'){
		if(pipe(a1.pipe2) == -1){
			sendul(a->pidc, (ulong)-1);
			threadexits("pipe");
		}
		a1.pipe1[0] = a->pipe1[0];
		a1.pipe1[1] = a->pipe1[1];
		a1.sync = chancreate(sizeof(ulong), 0);
		if(proccreate(procplan9pipe, &a1, 8192) == -1){
			fprint(2, "Can't fork?!\n");
			threadexits("fork");
		}
		recvul(a1.sync);
		chanfree(a1.sync);
		dup(a1.pipe2[0], 0);
		close(a1.pipe2[0]);
		close(a1.pipe2[1]);
	}
	if(a->type=='<'){
		close(0);	/* so it won't read from terminal */
		open("/dev/null", 0);
	}
	procexecl(a->pidc, SHPATH, SH, "-c", Strtoc(&plan9cmd), (char *)0);
	threadexits("exec");
}

int
plan9(File *f, int type, String *s, int nest)
{
	int pid, retcode;
	struct { int pipe1[2]; int type; Channel *pidc; } a;

	if(s->s[0]==0 && plan9cmd.s[0]==0)
		error(Enocmd);
	else if(s->s[0])
		Strduplstr(&plan9cmd, s);
	if(downloaded){
		samerr(errfile);
		remove(errfile);
	}
	if(type!='!' && pipe(a.pipe1)==-1)
		error(Epipe);
	if((a.pidc = chancreate(sizeof(ulong), 0)) == nil)
		error(Epipe);	/* not quite right */
	if(type=='|')
		snarf(f, addr.r.p1, addr.r.p2, &plan9buf, 1);
	a.type = type;
	if(proccreate(procplan9, &a, 8192) == -1){
		chanfree(a.pidc);
		error(Efork);
	}
	pid = recvul(a.pidc);
	chanfree(a.pidc);
	if(pid == -1)
		error(Efork);
	if(type=='<' || type=='|'){
		int nulls;
		if(downloaded && addr.r.p1 != addr.r.p2)
			outTl(Hsnarflen, addr.r.p2-addr.r.p1);
		snarf(f, addr.r.p1, addr.r.p2, &snarfbuf, 0);
		logdelete(f, addr.r.p1, addr.r.p2);
		close(a.pipe1[1]);
		io = a.pipe1[0];
		f->tdot.p1 = -1;
		f->ndot.r.p2 = addr.r.p2+readio(f, &nulls, 0, FALSE);
		f->ndot.r.p1 = addr.r.p2;
		closeio((Posn)-1);
	}else if(type=='>'){
		close(a.pipe1[0]);
		io = a.pipe1[1];
		bpipeok = 1;
		writeio(f);
		bpipeok = 0;
		closeio((Posn)-1);
	}
	retcode = waitfor(pid);
	if(type=='|' || type=='<')
		if(retcode!=0)
			warn(Wbadstatus);
	if(downloaded)
		checkerrs();
	if(!nest)
		dprint("!\n");
	return retcode;
}

void
checkerrs(void)
{
	char buf[256];
	int f, n, nl;
	char *p;
	long l;

	if(statfile(errfile, 0, 0, 0, &l, 0) > 0 && l != 0){
		if((f=open((char *)errfile, 0)) != -1){
			if((n=read(f, buf, sizeof buf-1)) > 0){
				for(nl=0,p=buf; nl<3 && p<&buf[n]; p++)
					if(*p=='\n')
						nl++;
				*p = 0;
				dprint("%s", buf);
				if(p-buf < l-1)
					dprint("(sam: more in %s)\n", errfile);
			}
			close(f);
		}
	}else
		remove((char *)errfile);
}
