#!/dis/sh # distributed chat application. # not fully debugged... load std file2chan tk run net.sh nl=' ' fn debug { echo $* > /dev/cons } fn startwindow { wid = ${tk window $"*} fn x { a := $*; or {tk $wid $a} {echo error on tk cmd $"a':' $status }} x entry .e x frame .f x scrollbar .f.s -orient vertical -command {.f.t yview} x text .f.t -yscrollcommand {.f.s set} x pack .f.s -side left -fill y x pack .f.t -side top -fill both -expand 1 x pack .e -side top -fill x x pack .f -side top -fill both -expand 1 x pack propagate . 0 x bind .e '' {send event enter} x update while {} {tk winctl $wid ${recv $wid}} & # default window handling chan event tk namechan $wid event fn addtext { x .f.t insert end ''''^$"* x update } subfn enteredtext { debug waiting for entered text {} ${recv event} debug got entered text result = ${tk $wid .e get} } } fn chatserver { addr:=$1 name:=$2 memfs -r /tmp mkdir /tmp/^(local remote) blocked := fidl := () (file2chan /tmp/local/chatlocal # pendFID: list of pending chat messages for FID # blocked: list of tags of blocked read requests # blockedFID: "true" if fid currently has a tag blocked. # nameFID: name associated with FID { fid := ${rget fid} p := pend^$fid r := ${hd $$p} (if {~ $#r 1} {rread $r} { rblock blocked=$tag $blocked blocked^$fid=true } ) } { rq := ${unquote ${rget data}} debug 'chatlocal got write:' ${quote $rq} cmd := ${hd $rq} fid := ${rget fid} (if {~ $cmd announce} {debug processing announce; name^$fid = ${tl $rq}; fidl=$fid $fidl; blocked^$fid=''} {~ $cmd msg} { # reply to any blocked requests; add to the pending # lists of those that aren't debug process message. blocked ${quote $blocked} debug fidl ${quote $fidl} msg := $(name^$fid)^': '^${tl $rq}^$nl for i in $blocked {rread $i $msg} blocked=() for i in $fidl { (if {~ $(blocked^$i) ''} {debug adding to pending for fid $i; pend^$i = $msg $(pend^$i)} {blocked^$i = ''} ) } addtext $msg } ) debug done chatlocal write } { fid := ${rget fid} blocked^$fid=() name^$fid=() pend^$fid=() } ) { echo -n announce ${quote $name} while {} { txt := ${enteredtext} echo -n msg ${quote $txt} } } <>[1] /tmp/local/chatlocal & debug announcing on $addr announce $addr { while {} { debug server awaiting connection on $net listen { debug got connection in dir $net from `{cat $net/remote} name := ${pid} # was "{read} debug name is $name { pctl forkns echo -n ${quote announce $name} >[1=3] debug about to do file2chan or {(file2chan /tmp/remote/chat {debug chat read; rblock; {read <>[0=3] | putrdata} &} {debug chat write; echo ${quote msg ${rget data}} >[1=3]} {debug chat close; echo $name exiting; exit} )} {debug file2chan for remote failed} debug done file2chan ls -l /tmp/remote export /tmp/remote & debug export finished pause } <>[3] /tmp/local/chatlocal } echo after listen net is $net } } } fn chatclient { addr := $1 name := $2 mkdir /tmp/chatdir >[2] /dev/null chmod 755 /tmp/chatdir dial $addr { debug 'connected to chat server' # echo -n $name >[1=0] imount $net/data /tmp/chatdir debug done imount ls -l /tmp/chatdir debug done ls { while {} { txt := ${enteredtext} debug got entered text ${quote $txt} echo -n $txt >[1=0] } & eof:='' while {} { txt := "{read; eof=$status} debug got remote ${quote $txt} if {! ~ $eof ''} {echo eof is $eof; raise break} addtext $"txt } } <> /tmp/chatdir/chat } } args:=$* port:=18981 if {! ~ $#args 1 2} {echo 'usage: chat [address] name' >[1=2]; raise usage} pctl newpgrp forkns startwindow $args (if {~ $#args 1} {chatserver 'tcp!*!'^$port $args} {chatclient 'tcp!'^${hd $args}^'!'^$port ${tl $args}} ) echo bye bye...