#include "sig.h"
#include "substdio.h"
#include "strerr.h"
#include "str.h"
#include "byte.h"
#include "readwrite.h"
#include "exit.h"
#include "fmt.h"
#include "select.h"

#define FATAL "recordio: fatal: "

char pid[FMT_ULONG + 10];

char recordbuf[512];
substdio ssrecord = SUBSTDIO_FDBUF(write,2,recordbuf,sizeof recordbuf);

void record(buf,len,direction)
char *buf;
int len; /* 1...256 */
char *direction;
{
  int i;

  while (len) {
    substdio_puts(&ssrecord,pid);
    substdio_puts(&ssrecord,direction);

    i = byte_chr(buf,len,'\n');
    substdio_put(&ssrecord,buf,i);

    if (i == len) {
      substdio_puts(&ssrecord,"+\n");
      substdio_flush(&ssrecord);
      return;
    }

    substdio_puts(&ssrecord," \n");
    substdio_flush(&ssrecord);
    buf += i + 1;
    len -= i + 1;
  }
}

int leftstatus = 0;
char leftbuf[256];
int leftlen;
int leftpos;

int rightstatus = 0;
char rightbuf[256];
int rightlen;
int rightpos;

void doit(fdleft,fdright)
int fdleft; /* copy from 0 to fdleft */
int fdright; /* copy from fdright to 1 */
{
  fd_set rfds;
  fd_set wfds;
  int nfds;
  int r;

  nfds = 2;
  if (nfds <= fdleft) nfds = fdleft + 1;
  if (nfds <= fdright) nfds = fdright + 1;
  /* XXX: will overflow select buf if too many fds are open */

  for (;;) {
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);

    if (leftstatus == 0) FD_SET(0,&rfds);
    if (leftstatus == 1) FD_SET(fdleft,&wfds);

    if (rightstatus == 0) FD_SET(fdright,&rfds);
    if (rightstatus == 1) FD_SET(1,&wfds);

    if (select(nfds,&rfds,&wfds,(fd_set *) 0,(struct timeval *) 0) == -1)
      continue;

    if (FD_ISSET(0,&rfds)) {
      r = read(0,leftbuf,sizeof leftbuf);
      if (r <= 0) {
        leftstatus = -1;
        close(fdleft);
        substdio_puts(&ssrecord,pid);
        substdio_puts(&ssrecord," < [EOF]\n");
        substdio_flush(&ssrecord);
      }
      else {
        leftstatus = 1; leftpos = 0; leftlen = r;
        record(leftbuf,r," < ");
      }
    }

    if (FD_ISSET(fdleft,&wfds)) {
      r = write(fdleft,leftbuf + leftpos,leftlen - leftpos);
      if (r == -1) break;
      leftpos += r;
      if (leftpos == leftlen) leftstatus = 0;
    }

    if (FD_ISSET(fdright,&rfds)) {
      r = read(fdright,rightbuf,sizeof rightbuf);
      if (r <= 0) {
        substdio_puts(&ssrecord,pid);
        substdio_puts(&ssrecord," > [EOF]\n");
        substdio_flush(&ssrecord);
        break;
      }
      else {
        rightstatus = 1; rightpos = 0; rightlen = r;
        record(rightbuf,r," > ");
      }
    }

    if (FD_ISSET(1,&wfds)) {
      r = write(1,rightbuf + rightpos,rightlen - rightpos);
      if (r == -1) break;
      rightpos += r;
      if (rightpos == rightlen) rightstatus = 0;
    }
  }

  _exit(0);
}

void main(argc,argv)
int argc;
char **argv;
{
  int piin[2];
  int piout[2];

  pid[fmt_ulong(pid,(unsigned long) getpid())] = 0;

  if (argc < 2)
    strerr_die1x(100,"recordio: usage: recordio program [ arg ... ]");

  if (pipe(piin) == -1)
    strerr_die2sys(111,FATAL,"unable to create pipe: ");
  if (pipe(piout) == -1)
    strerr_die2sys(111,FATAL,"unable to create pipe: ");

  switch(fork()) {
    case -1:
      strerr_die2sys(111,FATAL,"unable to fork: ");
    case 0:
      sig_pipeignore();
      close(piin[0]);
      close(piout[1]);
      doit(piin[1],piout[0]);
  }

  close(piin[1]);
  close(piout[0]);
  if (fd_move(0,piin[0]) == -1)
    strerr_die2sys(111,FATAL,"unable to move descriptors: ");
  if (fd_move(1,piout[1]) == -1)
    strerr_die2sys(111,FATAL,"unable to move descriptors: ");

  execvp(argv[1],argv + 1);
  strerr_die4sys(111,FATAL,"unable to run ",argv[1],": ");
}
