#! /usr/local/bin/perl -w
# (c) Louis Granboulan 2000-2004
# tp6: expect, in perl

if (scalar(@ARGV) == 1 and $ARGV[0] eq '-h') {
print << 'EOT';
Usage:
  The first argument is a perl file containing a subroutine named "interact".
  The other arguments are used as a command that will interact with this
  subroutine.
  The subroutine can use CIN, COUT and CERR as the file descriptors for
  the I/O of the command. STDIN, STDOUT and STDERR are the I/O of this
  program.
  The subroutine can write in CIN, STDOUT and STDERR.
  The subroutine gets what is read from STDIN, COUT and CERR.
  Don't forget to close CIN if necessary.
EOT
  exit 0;
}
unless (scalar(@ARGV) >= 2) {
  die "Syntax: $0 [interact] [command]\n"
     ."        $0 -h\n";
}
unless (-r $ARGV[0]) {
  die "Error: $ARGV[0] unreadable.\n";
}

pipe (XIN,CIN);
pipe (COUT,XOUT);
pipe (CERR,XERR);
unless (defined($pid = fork)) {
  die "Fork failed\n";
}
if ($pid == 0) {
  close (CIN);
  close (COUT);
  close (CERR);
  open (STDIN , "<&XIN");
  open (STDOUT, ">&XOUT");
  open (STDERR, ">&XERR");
  shift @ARGV;
  exec @ARGV;
} else {
  $SIG{'CHLD'} = sub { wait };
  $SIG{'HUP'} = $SIG{'TERM'} = sub { kill 'TERM', $pid; exit 0 };
  # We connect childe I/O to father I/O
  close (XIN);
  close (XOUT);
  close (XERR);
  # We import "interact" subroutine
  require $ARGV[0];
  # We wait some something from the user (STDIN) or from the program (COUT/CERR)
  # Infinite loop, until child (maple) dies or all inputs are closed.
  $rin = $win = $ein = '';
  vec($rin,fileno(STDIN),1) = 1;
  vec($rin,fileno(COUT),1) = 1;
  vec($rin,fileno(CERR),1) = 1;
  use IO::Handle;
  CIN->autoflush(1);
  COUT->autoflush(1);
  interact(-1); # Initialisation
  while (0 <= select ($rout=$rin, $wout=$win, $eout=$ein, undef)) {
    if (vec($rout,fileno(STDIN),1)) {
      # Copy STDIN into CIN, closing CIN when STDIN is closed.
      if (eof STDIN) {
        vec($rin,fileno(STDIN),1) = 0;
        interact(-2); # End
      } else {
        $_ = <STDIN>;
        interact(0,$_); # Something came from STDIN
      }
    }
    if (vec($rout,fileno(COUT),1)) {
      if (eof COUT) {
        vec($rin,fileno(COUT),1) = 0;
      } elsif(1) {
        sysread COUT, $_, 80;
        interact(1,$_); # Something came from COUT
      }
    }
    if (vec($rout,fileno(CERR),1)) {
      if (eof CERR) {
        vec($rin,fileno(CERR),1) = 0;
      } else {
        sysread CERR, $_, 80;
        interact(2,$_); # Something came from CERR
      }
    }
  }
  exit 0;
}
