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

extern  jmp_buf mainloop;

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

static void
updateenv(File *f)
{
       static int fd = -1;
       int n;
       char buf[64], *p, *e;

       if(f == nil){
               putenv("%", "");
               putenv("%dot", "");
               return;
       }

       p = Strtoc(&f->name);
       putenv("%", p);
       free(p);

       p = buf;
       e = buf+sizeof(buf);
       p = seprint(p, e, "%lud", 1+nlcount(f, 0, f->dot.r.p1));
       p = seprint(p+1, e, "%lud", f->dot.r.p1);
       p = seprint(p+1, e, "%lud", f->dot.r.p2);
       n = p - buf;
       if(fd == -1)
       if((fd = create("/env/%dot", OWRITE, 0666)) < 0)
               fprint(2, "updateenv create: %r\n");
       if(write(fd, buf, n) != n)
               fprint(2, "updateenv write: %r\n");
}

int
plan9(File *f, int type, String *s, int nest)
{
       long l;
       int m;
       int pid, fd;
       int retcode;
       char *retmsg;
       int pipe1[2], pipe2[2];

       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(pipe1)==-1)
               error(Epipe);
       if(type=='|' || type=='_')
               snarf(f, addr.r.p1, addr.r.p2, &plan9buf, 1);
       if((pid=fork()) == 0){
               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(type == '>')
                               dup(2, 1);
                       else if(type=='!'){
                               dup(2, 1);
                               fd = open("/dev/null", 0);
                               dup(fd, 0);
                               close(fd);
                       }
               }
               if(type != '!') {
                       if(type == '>')
                               dup(pipe1[0], 0);
                       else
                               dup(pipe1[1], 1);
                       close(pipe1[0]);
                       close(pipe1[1]);
               }
               if(type == '|' || type == '_'){
                       if(pipe(pipe2) == -1)
                               exits("pipe");
                       if((pid = fork())==0){
                               /*
                                * It's ok if we get SIGPIPE here
                                */
                               close(pipe2[0]);
                               io = pipe2[1];
                               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(pipe2[1], c, strlen(c));
                                               free(c);
                                       }
                               }
                               exits(retcode? "error" : 0);
                       }
                       if(pid==-1){
                               fprint(2, "Can't fork?!\n");
                               exits("fork");
                       }
                       dup(pipe2[0], 0);
                       close(pipe2[0]);
                       close(pipe2[1]);
               }
               if(type=='<' || type=='^'){
                       close(0);       /* so it won't read from terminal */
                       open("/dev/null", 0);
               }
               updateenv(f);
               execl(SHPATH, SH, "-c", Strtoc(&plan9cmd), nil);
               exits("exec");
       }
       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(pipe1[1]);
               io = 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(pipe1[0]);
               io = pipe1[1];
               bpipeok = 1;
               writeio(f);
               bpipeok = 0;
               closeio((Posn)-1);
       }else if(type == '^' || type == '_'){
               int nulls;
               close(pipe1[1]);
               bufload(&cmdbuf, cmdbufpos, pipe1[0], &nulls);
               close(pipe1[0]);
       }
       retmsg = waitfor(pid);
       if(type=='|' || type=='<' || type=='_' || type=='^')
               if(retmsg[0]!=0)
                       warn_s(Wbadstatus, retmsg);
       if(downloaded)
               checkerrs();
       if(!nest)
               dprint("!\n");
       return retmsg[0] ? -1 : 0;
}

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);
}