#! /bin/sh
# use tclsh in the path \
exec tclsh "$0" "$@"
#
#    send stdin to a remote socket,
#    echo the reply from that socket to stdout
#    then exit.
#
#    usage:
#	echo "gtos move detector_z 100" | sock_exchange.tcl host port count separator timeout
#
#
# read command line
set host [lindex $argv 0]
set port [lindex $argv 1]
set lines [lindex $argv 2]
set separator [subst -nocommands -novariables [lindex $argv 3]]
set timeout [lindex $argv 4]
# impose defaults
if { "$timeout" == ""} { set timeout 30 }
if { "$separator" == ""} { set separator "\n" }
if { "$lines" == ""} { set lines -1}
if { "$port" == ""}  { set port 80 }
if { "$host" == ""}  { set host localhost }
# alternate interpretations of the command line
if { ! [string is digit $lines] } {
    set lines -1
}
# initialize common variables
set status "normal"
# open the socket connection
set sock [socket -async $host $port]
#set sock [socket $host $port]
fconfigure $sock  -buffering none -translation binary -blocking 0
fconfigure stdin  -buffering none -translation binary
fconfigure stdout -buffering none -translation binary
#-translation {auto auto}
# check for failed connection
# send everything from stdin to the remote socket
#while { ! [eof stdin] } {
#    # read data from std input and send it to the remote computer
#    set tosend [read stdin]
#    puts -nonewline $sock $tosend
#}
# now read stuff back
set reply ""
set cmplen [string length $separator]
set window [expr $cmplen - 1]
proc bgerror { code } {
    global sock
    puts stderr "bgerror: $code"
    set status "bgerror"
    flush $sock
    close $sock
    exit 2
}
proc timeout {} {
    global timeout
#    puts stderr "idle"
    after [expr int( $timeout * 1000 ) ] {
#	puts stderr "timeout"
	set status "timeout"
    }
}
# send everything from stdin to the remote socket
proc send_request {sock} {
    global status
    # cancel this procedure when there is no more input
    if { [eof stdin] } {
	fileevent $sock writable ""
	rename send_request ""
	return
    }
    # postpone timeout if there is input to process
    after cancel [after info]
    after idle timeout
    # read data from std input and send it to the remote computer
    set tosend [read stdin]
    puts -nonewline $sock $tosend
    #puts "sending: $tosend"
}
proc read_reply {sock} {
    # almost everyhting is global (sock_exchange will only use one socket)
    global lines reply cmplen window separator status
    # postpone timeout if there is input to process
    after cancel [after info]
    after idle timeout
    # echo every last byte of reply to std output
    set byte [read $sock 1]
    # accumulate "reply"
    append reply "$byte"
    # print out the character
    if { [catch {puts -nonewline "$byte"} foo] } {
	# must be a broken pipe
	#puts stderr "broken pipe "
	set status "broken pipe"
	return
    }
    # why do we need to do this?
    set cmp [string range "$reply" end-$window end]
    # count down the number of lines read (separators "seen")
    if { [string equal -length $cmplen [string range "$reply" end-$window end] "$separator"] } {
	incr lines -1
    }
    # check for completed read
    if { $lines == 0} {
	set status "normal"
	return
    }
    # check for broken socket
    if { [eof $sock] } {
	#puts stderr "socket broken by remote host "
	set status "closed"
	return
    }
}
fileevent $sock readable [list read_reply $sock]
fileevent $sock writable [list send_request $sock]
after idle timeout
vwait status
# close up
flush $sock
close $sock
if {$status != "normal"} {
#    puts stderr "$status"
    exit 9
}
exit