+##
+## File parsing
+##
+my($sort)=0;
+my($nb_arg)=0;
+my($timeout)=0;
+my($old_buffer);
+my($linebis);
+my($SIGABRT)=0;
+my($no_output_ignore)=1;
+my($verbose)=0;
+my($return)=-1;
+my($pid);
+my($result);
+my($result_err);
+my($forked);
+my($config)="";
+
+my($tesh_command)=0;
+my(@buffer_tesh)=();
+
+eval {
+ use POSIX;
+ sub exit_status {
+ my $status = shift;
+ if (POSIX::WIFEXITED($status)) {
+ return "returned code ".POSIX::WEXITSTATUS($status);
+ } elsif (POSIX::WIFSIGNALED($status)) {
+ return "got signal ".$SIG{POSIX::WTERMSIG($status)};
+ }
+ return "Unparsable status. Is the process stopped?";
+ }
+};
+if ($@) { # no POSIX available?
+ warn "POSIX not usable to parse the return value of forked child: $@\n";
+ sub exit_status {
+ return "returned code 0";
+ }
+}
+
+sub exec_cmd {
+ my %cmd = %{$_[0]};
+ if ($opts{'debug'}) {
+ print "IN BEGIN\n";
+ map {print " $_"} @{$cmd{'in'}};
+ print "IN END\n";
+ print "OUT BEGIN\n";
+ map {print " $_"} @{$cmd{'out'}};
+ print "OUT END\n";
+ print "CMD: $cmd{'cmd'}\n";
+ }
+
+ # cleanup the command line
+ if($OS eq "WIN"){
+ $cmd{'cmd'} =~ s/\${EXEEXT:=}/.exe/g;
+ $cmd{'cmd'} =~ s/\${EXEEXT}/.exe/g;
+ $cmd{'cmd'} =~ s/\$EXEEXT/.exe/g;
+ }
+ else{
+ $cmd{'cmd'} =~ s/\${EXEEXT:=}//g;
+ }
+ $cmd{'cmd'} =~ s/\${bindir:=}/$bindir/g;
+ $cmd{'cmd'} =~ s/\${srcdir:=}/$srcdir/g;
+ $cmd{'cmd'} =~ s/\${bindir:=.}/$bindir/g;
+ $cmd{'cmd'} =~ s/\${srcdir:=.}/$srcdir/g;
+ $cmd{'cmd'} =~ s/\${bindir}/$bindir/g;
+ $cmd{'cmd'} =~ s/\${srcdir}/$srcdir/g;
+ $cmd{'cmd'} =~ s|^\./||g;
+# $cmd{'cmd'} =~ s|tesh|tesh.pl|g;
+ $cmd{'cmd'} =~ s/\(%i:%P@%h\)/\\\(%i:%P@%h\\\)/g;
+ $cmd{'cmd'} .= " $opts{'cfg'}" if (defined($opts{'cfg'}) && length($opts{'cfg'}));
+
+ print "[$cmd{'file'}:$cmd{'line'}] $cmd{'cmd'}\n";
+
+ ###
+ # exec the command line
+ ###
+ $pid = open3(\*IN, \*OUT, \*OUT, $cmd{'cmd'} );
+
+ # if timeout specified, fork and kill executing child at the end of timeout
+ if ($timeout){
+ $forked = fork();
+ die "fork() failed: $!" unless defined $forked;
+ if ( $forked == 0 ) { # child
+ sleep $timeout;
+ kill(9, $pid);
+ exit;
+ }
+ }
+
+ # push all provided input to executing child
+ map { print IN "$_\n" } $cmd{'in'};
+ close IN;
+
+ # pop all output from executing child
+ my @got;
+ while(defined(my $got=<OUT>)) {
+ $got =~ s/\r//g;
+ #$got =~ s/^( )*//g;
+ chomp $got;
+ push @got, "$got";
+ }
+ close OUT;
+
+ # Cleanup the executing child, and kill the timeouter brother on need
+ $cmd{'return'} = 0 unless defined($cmd{'return'});
+ my $wantret = "returned code ".(defined($cmd{'return'})? $cmd{'return'} : 0);
+ waitpid ($pid, 0);
+ my $gotret = exit_status($?);
+ if($gotret ne $wantret) {
+ my $msg = "Test suite `$cmd{'file'}': NOK (<$cmd{'file'}:$cmd{'line'}> $gotret)\n".
+ "Output of <$cmd{'file'}:$cmd{'line'}> so far:\n";
+ map {$msg .= "|| $_\n"} @got;
+ print STDERR "$msg";
+ exit(1);
+ }
+ if($timeout){kill(9, $forked);$timeout=0;}
+ $timeout = 0;
+
+ ###
+ # Check the result of execution
+ ###
+ my $diff = build_diff(\@{$cmd{'out'}}, \@got);
+ if (length $diff) {
+ print color("red")."[TESH/CRITICAL$$] Output mismatch\n";
+ map { print "[TESH/CRITICAL] $_\n" } split(/\n/,$diff);
+ print color("reset");
+ die "Tesh failed\n";
+ }