#!/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 '<Key-'^$nl^'>' {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...
|