#  Copyright (c) 1997-2004
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#-----------------------------------------------------------------------------
#  $Project: polymake $$Id: ProgramPipe.pm,v 1.6.2.1 2004/12/21 14:43:07 gawrilow Exp $

use strict;
use namespaces;

package Poly::Pipe::Process;
use Struct (
   [ new => '$$' ],
   [ '$pid' => '#1' ],
   [ '$program' => '#2' ],
);
use POSIX qw(:sys_wait_h);

sub DESTROY {
   my $self=shift;
   waitpid $self->pid, 0;
   if (!$@  &&  $?  &&  $program) {
      my $rc=$?>>8;
      my ($prog)= $self->program =~ m'([^/\s]+)(?:\s|$)';
      die $rc > 128 ? ("$prog exited with code ", $rc>>8)
	            : ("$prog killed by signal ", $rc&127),
          "\n";
   }
}

package Poly::ProgramPipe;
use IPC::Open3;
use POSIX qw(:signal_h);
use Fcntl;
use Poly::Pipe;

#####################################################################################
#
#  Constructor:  new Poly::ProgramPipe('command', 'arg', ... );
#
#  Supports only one special, although very common communication "protocol":
#  send the source data to the program, then switch into the input mode,
#  and read all the results it has produced.
#
use Struct (
   [ '@ISA' => 'Poly::CollaborativePipe' ],
   [ '$proc' => 'undef' ],
   [ '$in' => 'undef' ],
);

sub construct {
   my $self=&_new;
   dbg_print( "running '@_'" ) if $Switches::d;
   my $out;
   my $pid=eval { open3($out, $self->in, ">&STDERR", @_) };
   if ($@) {
      $@ =~ s/^.*?://;
      $@ =~ s/ at \(eval.*$/\n/;
      if ($$ != getpgrp) {	# we are in the fork!!!
	 print STDERR $@;
	 exec "false";	# avoid executing global destructors
      }
      die $@;
   }
   $self->init($out);
   $self->proc=new Poly::Pipe::Process($pid, $_[0]);
   my $rfd=fileno($self->in);
   $channels[$rfd]=$self;
   vec($rmask,$rfd,1)=1;
   $out;
}

sub new {
   my $out=&construct;
   fcntl($out, F_SETFL, O_NONBLOCK);
   $out;
}

sub shared {
   my $out=&construct;
   fcntl($out, F_SETFD, 0);	# by default the pipe is closed in child processes (due to the implementation of Open3.)
   $out;
}

#####################################################################################
#  
#  The source data (output for us) are complete, start receiving the results
#
sub switch_to_input {
   my $self=shift;
   # flush the output buffer
   $self->WRITE while length($self->wbuffer);
   vec($wmask,$self->fd,1)=0;
   POSIX::close($self->fd);
   undef $channels[$self->fd];
   $self->fd=fileno($self->in);
   bless $self, keys(%active) ? "Poly::Pipe" : "Poly::CollaborativePipe";
}

sub alone { }

sub READ {
   (shift)->switch_to_input->READ(@_);
}

sub READLINE {
   (shift)->switch_to_input->READLINE;
}

# seems to die before started to consume input: shoot the program down
sub CLOSE {
   my $self=shift;
   $self->SUPER::CLOSE;
   kill SIGINT, $self->proc->pid;
   undef $self->proc->program;
   close($self->in);
}

1

# Local Variables:
# c-basic-offset:3
# End:
