;
#
# Standard client - for communication with the server.
#

use Socket;
require "flush.pl";

#
# Default/misc. variables
#

$alarm = -1;
$file = "-";                      # output to stdout by default
$| = 1;                           # don't buffer stdout
$progName = $0;                   # progname
$progName =~ s/(.*\/)(.+)/$2/g;   # strip path from progname
$SIG{'ALRM'} = \&handler;
$SIG{'PIPE'} = \&piper;

$sendBufferSize = 8192;
$recvBufferSize = 8192;

#
# Process command line switches 
#

while ($_ = shift) {
  if (/^-silent$/) {
    $silent = 1;
  } elsif (/^-fork$/) {
    $fork = 1;
  } elsif (/^-statSend$/) {
    $statSend = 1;
    $silent = 1;
  } elsif (/^-statRecv$/) {
    $statRecv = 1;
    $silent = 1;
  } elsif (/^-server$/) {
    $server = shift;
    unless ($server =~ /(.+):(\d+)/) {
      print "\n$0: Invalid server name: \"$server\"\n";
      &usage;
      exit(-1);
    } else {
      $host = $1;
      $port = $2;
    }
  } elsif (/^-file$/) {
    $file = shift;
  } elsif (/^-request$/) {
    $request = shift;
  } elsif (/^-sendBuffer$/) {
    $sendBufferSize = shift;
  } elsif (/^-recvBuffer$/) {
    $recvBufferSize = shift;
  } elsif (/^-alarm$/) {
    $alarm = shift;
  } elsif (/^-manual/) {
    # for compatability with older clients
  } elsif (/^-u/) {
    &usage;
    exit(0);
  } else {
    #print "$progName: ERROR: unknown flag \"$_\"\n";
    #print "$progName: -u for usage\n";
    #exit(-1);
  }
}

#
# Data check
#

unless ($host) {
  print "$progName: ERROR: missing hostname\n";
  print "$progName: -u for usage\n";
  exit(-1);
}
unless ($port =~ /^\d+$/) {
  print "$progName: ERROR: missing port number\n";
  print "$progName: -u for usage\n";
  exit(-1);
}
unless ($sendBufferSize > 0) {
  print "$progName: send buffer must be > 0\n";
  exit(-1);
}
unless ($recvBufferSize > 0) {
  print "$progName: recv buffer must be > 0\n";
  exit(-1);
}

#
# Create local socket and connect to server
#

if ($alarm > 0) {
  print "$progName: alarm in $alarm seconds\n" unless $silent;
  alarm($alarm);
}
$sockaddr = 'S n a4 x8';
$iaddr   = gethostbyname($host)         or &Die ("$progName: ERROR: gethostbyname: no such host: $host");
print "$progName: connecting to $host:$port\n" unless $silent; 
$paddr   = pack($sockaddr, PF_INET, $port, $iaddr);
$proto   = getprotobyname('tcp')        or &Die ("$progName: ERROR: getprotobyname: $!");
socket(S, PF_INET, SOCK_STREAM, $proto) or &Die ("$progName: ERROR: socket: $!");
connect(S, $paddr)                      or &Die ("$progName: ERROR: connect to $port with host $host: $!");
$old = select; 
select(S); 
$| = 1; select($old); 
print "$progName: connected\n" unless $silent; 

#
# forks if the Tcl/TK submit client invokes it
#

if ($fork) {
  if (fork() != 0) {
    close(S);
    exit(0);
  }
}

#
# Send username to server
#

$USER = getlogin || (getpwuid($<))[0] || "INTRUDER!!";
print S "$USER\n"; 

#
# Send message to server.
#

unless ($silent) {
  print "$progName: sending request\n"; 
}

if (!$request) {
  $_ = <STDIN>;
} else {
  $_ = "$request\n";
}

if (/^begin (job|results) (\d+)\s*(\d+)?/) {
  $which = $1;
  $size  = $2;
  $job   = $3;
  print S $_;
  $count=1;
  for ($i=0;($i+$sendBufferSize)<=$size;$i+=$sendBufferSize) {
    $numSent = read(STDIN, $_, $sendBufferSize);  
    $totalSent += $numSent;
    print S $_;
    if ($statSend) {
      $percent = (($count)*$sendBufferSize)/$size;
      print "$percent\n";
      $count++;
    } else {
      print "." unless $silent;
    }
  }
  if (($size%$sendBufferSize)> 0) {
    $numSent = read(STDIN, $_, $size%$sendBufferSize);  
    $totalSent += $numSent;
    print S $_;
    if ($statSend) {
      print "1.0\n";
    } else {
      print "." unless $silent;
    }
  }
} else {
  print S $_;
  $totalSent = length($_);
} 
unless ($silent) {
  print "\n$progName: $totalSent bytes sent\n";
}

#
# Wait for server's reply 
#
# Display server results on standard out unless a file was
# specified. While the file is being built, its name is
# <file>.tmp until the transfer is complete and it is then
# moved to <file>.
#

setsockopt(S,SOL_SOCKET,SO_KEEPALIVE,1);
#
# keepalive should perform checks as to whether the remote server
# is still alive, in case of power outage or similar on the 
# server side.
#

print "$progName: receiving data\n" unless $silent;

if ($file eq "-") {
  print "\n" unless $silent;

  $size = <S>; 
  $fromAddress = recv(S, $_, $recvBufferSize,MSG_PEEK);
  while(1) {
    $numRead = read(S, $_, $recvBufferSize);
    if(!defined($numRead)) {
	print STDERR "\n\nUndefined socket read.\n\nError in remote communication.  Data lost.\n";
	last;
    }
    if ($numRead == 0) {
	last;
    }
    $totalRead += $numRead;
    print $_; 
  }
} else { 
  open (handle, ">$file.tmp");
  $size = <S>; 
  # don't buffer output to file
  $old = select; select(handle); $| = 1; select($old); 
  $count = 0;
  while(1) {
    $numRead = read(S, $_, $recvBufferSize);
    $totalRead += $numRead;
    if(!(defined($numRead))) {
	print "\n\nError in remote communication with solver.  Data lost.\n";
	last;
    }
    last if ($numRead == 0);
    print handle $_; 
    if ($statRecv) {
      $percent = ($count*$recvBufferSize)/$size;
      print "$percent\n";
      $count++;
    } else {
      print "." unless $silent;
    }
  }
  if ($stat) {
    print "1.0\n";
  }
  close(handle);
  system("mv $file.tmp $file");
  unless ($silent) {
    print "\n";
  }
}

unless ($silent) {
  print "\n$progName: $totalRead bytes received\n";
}

#
# turn off alarm
#

alarm(0);

#
# Unzip/uncompress if necessary.
#

if ($file =~ /\S+\.gz$/) {
  print "$progName: uncompressing via gzip\n" unless $silent;
  if (system("gzip -f -d -n $file")) {
    &error("error uncompressing $file"); 
  }
} elsif ($file =~ /\S+\.Z$/) {
  print "$progName: uncompressing via compress\n" unless $silent;
  if (system("uncompress $file")) {
    &error("error uncompressing $file"); 
  }
}

print "$progName: exiting\n" unless $silent;
exit(0);

#
# Misc. subroutines
#

sub error {
  open(handle, ">$file");
  print handle "$progName: ERROR: $_[0]\n"; 
  close(handle);
  if ($file =~ /.*\.gz$/) {
    $file_nogz = $file;
    $file_nogz =~ s/\.gz$//;
    system("mv $file $file_nogz");
  } elsif ($file =~ /.*\.Z$/) {
    $file_noZ = $file;
    $file_noZ =~ s/\.Z$//;
    system("mv $file $file_noZ");
  }
  exit(-1);
}

sub usage {
  print "Usage: $progName\n"; 
  print "      [-server <host:port>]\n";
  print "      [-sendbuffer <integer>]\n"; 
  print "      [-recvbuffer <integer>]\n"; 
  print "      [-file <string>]\n"; 
  print "      [-silent]\n"; 
  print "      [-alarm <integer>]\n"; 
  print "      [-fork]\n"; 
  print "      [-usage]\n"; 
}

sub handler {
  print "$progName: ALARM: connection timed out\n";
  exit(-1);
}

sub Die {
  print STDERR "$_[0]\n";
  print "$_[0]\n";
  exit(-1);
}

sub piper {
    system("echo 'SIGPIPE, communications with remote solver failed.' >> job.results");
    print STDERR "Received a sigpipe.  Dying.\n";
    
    exit(-2);
}
