#! /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