#!/bin/rc # 2024-03-23T23:27:02-04:00 # EXT. NOSTROMO rfork en ramfs -p 9pid=$pid myself=`{echo $0 | sed 's/.*\///'} 00a0=' ' if(~ $#nostromoname 0) nostromoname=glenda@$sysname argv0=$0 if(~ $#editor 0) editor='sam -d' fn changepassword{ echo -n $* 'password: ' password=`{read} if(~ $#password 0) terminate hungup password=`{echo $"password | nstrip} while(! ~ $#password 1){ echo echo !empty passwords not allowed, try again. echo -n $* 'password: ' password=`{read} if(~ $#password 0) terminate hungup password=`{echo $"password | nstrip} } echo $"password >$mnt/etc/users/$username/password } fn checkroom{ croom=`{echo $1 | sed 's/\//\\\//g'} cn=`{diff <{basename `{grep -e $croom'\/[0-9]+$' $mnt/etc/rooms}} <{basename `{grep -e $croom'\/[0-9]+$' $mnt/etc/users/$logged_user/rooms}}} if(~ $#cn 0) echo $#posts of $#posts messages if not if(~ $cn(6) '') echo 1 of $cn(3) messages if not echo $cn(6) of $cn(3) messages } fn delete{ if(test -f $mnt/rooms/$room/$1){ if(~ $logged_user `{sed '1q; s/From: //g' $mnt/rooms/$room/$1}){ { awk '/^$/{exit} {print}' $mnt/rooms/$room/$1 echo echo DELETED `{date} } >/tmp/$1 cat /tmp/$1 >$mnt/rooms/$room/$1 } if not echo !permission denied } if not echo !$1 does not exist } fn dumpuser{ echo $biff >$mnt/etc/users/$logged_user/biff printD >$mnt/etc/users/$logged_user/delimiter echo $editor >$mnt/etc/users/$logged_user/editor echo $plines >$mnt/etc/users/$logged_user/plines echo $room >$mnt/etc/users/$logged_user/room printsig >$mnt/etc/users/$logged_user/sig echo $wrap >$mnt/etc/users/$logged_user/wrap } fn edit{ @{ rfork en { dirs=(lib rc/lib sys/lib sys/man tmp) programs=(echo sam) mkdir -p /tmp/root builtin cd /tmp/root mkdir -p $dirs for(i in $dirs) bind /$i $i mkdir -p bin touch bin/$programs for(i in $programs) bind /bin/$i bin/$i mkdir -p dev >dev/null bind /dev/null dev/null mkdir -p env bind -c '#e' env bind -c /tmp /tmp/root/tmp bind /tmp/root / } && { rfork m $editor $* echo !now, hit enter } } } fn editpost{ echo yes >/tmp/busy daten=`{date -n} { echo From: $logged_user echo To: $room echo Date: `{echo $daten | seconds2rfc822} echo X-Date-N: $daten echo if(! ~ $#reply 0){ echo re $reply echo } } >/tmp/post.header >/tmp/post.body nstrip | edit /tmp/post.body >[2=1] if(! ~ `{cat /tmp/post.body} '') postpost if not echo !message is empty echo no >/tmp/busy } fn getpost{ groom=`{basename `{grep -e '^'^$1^'\/[0-9]+$' $mnt/etc/users/$logged_user/rooms}} if(~ $#groom 0) groom=1 echo $groom } fn getposts{ ls | grep -e '^[0-9]+$' | sort -n | tail -$maxposts } fn goto{ nroom=`{grep $1 $mnt/etc/roomlist | sed 1q} # partial matches ok if(~ $#nroom 1 && grep -s $nroom $mnt/etc/roomlist){ if(grep -s -e '^[0-9]+$' <{ls -p $mnt/rooms/$nroom}){ syncroom $room $post $mnt/etc/users/$logged_user/rooms room=$nroom builtin cd $mnt/rooms/$room posts=`{getposts} post=`{getpost $room} checkroom $room prompt=$room/$post go=() } if not echo !$nroom contains no messages } if not echo !$1 does not exist } fn loaduser{ biff=`{cat $mnt/etc/users/$logged_user/biff} delimiter=`{cat $mnt/etc/users/$logged_user/delimiter} editor=`{cat $mnt/etc/users/$logged_user/editor} plines=`{cat $mnt/etc/users/$logged_user/plines} room=`{cat $mnt/etc/users/$logged_user/room} sig=`{cat $mnt/etc/users/$logged_user/sig} wrap=`{cat $mnt/etc/users/$logged_user/wrap} builtin cd $mnt/rooms/$room roomlist=`{cat $mnt/etc/roomlist} posts=`{getposts} post=`{getpost $room} checkroom $room } fn login{ echo login as new to create an account echo echo -n 'username: ' username=`{read} if(~ $#username 0) terminate hungup username=`{echo $"username | nstrip} if(~ $username new) newuser if not{ echo -n 'password: ' password=`{read} if(~ $#password 0) terminate hungup password=`{echo $"password | nstrip} if(test -d $mnt/etc/users/$username && ~ `{cat $mnt/etc/users/$username/password} $password){ logged_user=$username loaduser date=`{date -n} lfile=`{echo `{ls $mnt/etc/users/$logged_user/ | grep logged_in | wc -l} + 1 | bc} lfile=logged_in^$lfile echo $date >$mnt/etc/users/$logged_user/$lfile upasname=$nostromoname { echo `{date $date} $logged_user login echo } | mail -s '9bbs: '$"logged_user' login' $nostromoname echo `{date $date} $logged_user login >[1=2] } if not{ upasname=$nostromoname { echo `{date} $username login failed echo } | mail -s '9bbs: '$"username' login failed' $nostromoname terminate $username login failed } } } fn logout{ syncroom $room $post $mnt/etc/users/$logged_user/rooms dumpuser rm $mnt/etc/users/$logged_user/$lfile cat $mnt/etc/goodbye } fn newuser{ invalid='[^a-zA-Z0-9_\-\.]' echo echo -n 'new username: ' username=`{read} if(~ $#username 0) terminate hungup username=`{echo $"username | nstrip} if(! ~ $#username 1 || grep -s -e $invalid <{echo $username}){ echo '!invalid username, valid characters are: a-z A-Z 0-9 _ - .' newuser } if not if(test -d $mnt/etc/users/$username){ echo echo '!username already exists' newuser } if not{ mkdir $mnt/etc/users/$username changepassword echo no >$mnt/etc/users/$username/biff date >$mnt/etc/users/$username/created >$mnt/etc/users/$username/delimiter echo sam -d >$mnt/etc/users/$username/editor echo 22 >$mnt/etc/users/$username/plines echo lobby >$mnt/etc/users/$username/room sed 's/$/\/1/g' $mnt/etc/roomlist >$mnt/etc/users/$username/rooms echo $username >$mnt/etc/users/$username/sig echo 70 >$mnt/etc/users/$username/wrap logged_user=$username upasname=$nostromoname { echo `{date} $logged_user create echo } | mail -s '9bbs: '$"logged_user' create' $nostromoname echo `{date} $logged_user create >[1=2] loaduser >/dev/null lfile=logged_in1 date >$mnt/etc/users/$logged_user/$lfile echo `{date} $logged_user login >[1=2] cat $mnt/etc/welcome | np -$plines } } #fn nstrip{ tr -cd '\11\12\40-\176' } # Allow ASCII only. fn nstrip{ tr -d '\001-\010\013-\037' } # Cute, but let's allow UTF-8. fn postpost{ ppost=`{echo `{ls -p $mnt/rooms/$room | grep -e '^[0-9]+$' | sort -n | tail -1}^+1 | bc } # POLE POSITION { cat /tmp/post.header /tmp/post.body echo printsig } >$mnt/rooms/$room/$ppost # WELCOME 2 THE RATRACE syncroom $room $ppost $mnt/etc/rooms } fn printD{ if(! ~ $#delimiter 0) echo $"delimiter} fn printg{ printD glines=`{diff $mnt/etc/users/$logged_user/rooms $mnt/etc/rooms | grep -e '^(<|>)' | sort -t' ' +1} while(! ~ $#glines 0){ groom=`{basename -d $glines(2)} gupost=`{basename $glines(2)} gpost=`{basename $glines(4)} echo ' g '$"groom' # '^`{echo $gpost^-$gupost | bc | sed 's/-//g'} unread messages for(i in `{seq 1 4}) glines=$glines(2-) } printD } fn printgr{ printD builtin cd $mnt/rooms walk -demp * | sort -n | sed 's/^[^ ]+ //' | sed 's/^/ g /g' | tail -10 printD } fn printhelp{ echo yes >/tmp/busy printD echo '[0-9]+ print specified message b back B toggle biff C change password d [...] delete message D [...] view, set message delimiter >D truncate message delimiter e enter message (http://sam.cat-v.org) f jump to first message g [...] list rooms with unread messages, go to specified room gr list ten most recently active rooms grep [...] grep messages in current room for regexp h, help print this help message k [...] print roomlist or grep for specified regexp l jump to last message L [...] view, set number of lines to print before engaging pager (enter to continue, q to quit) n next p, re [...] print message with minimal headers P [...] print message with full headers r [...] reply to message q quit S [...] view, set signature >S truncate signature t print current date and time u list users w who is online wr list ten most recently active users W [...] view, set line wrap width y synchronize message list for current room z mark all messages (on the entire bbs) as read " [...] print message in quoted form, suitable for reply ? print debug information' printD echo no >/tmp/busy } fn printp{ echo yes >/tmp/busy if(test -f $mnt/rooms/$room/$1){ syncroom $room $1 $mnt/etc/users/$logged_user/rooms printD grep -v -e '^X-.*: ' $mnt/rooms/$room/$1 | fmt -j -l $wrap | np -$plines || echo !try again echo prompt=$room/$1 } echo no >/tmp/busy } fn printpp{ echo yes >/tmp/busy if(test -f $mnt/rooms/$room/$1){ syncroom $room $1 $mnt/etc/users/$logged_user/rooms printD <$1 fmt -j -l $wrap | np -$plines echo prompt=$room/$1 } echo no >/tmp/busy } fn printq{ echo yes >/tmp/busy if(test -f $mnt/rooms/$room/$1){ printD <$1 fmt -j -l $wrap | sed 's/^/> /g; s/^> >/>>/g' | grep -v -e '^X-Date-N' | np -$plines || echo !try again echo prompt=$room/$1 } echo no >/tmp/busy } fn printsig{ if(! ~ $#sig 0) echo $"sig} fn printw{ echo yes >/tmp/busy printD for(i in `{ls -t $mnt/etc/users/*/ | grep logged_in}){ who=`{basename `{basename -d `{echo $i | sed 's/\logged_in.*//g'}}} echo -n $who echo -n ' ' echo `{date `{cat $i}} } printD echo no >/tmp/busy } fn printwr{ printD builtin cd $mnt/etc/users walk -demp * | sort -n | sed 's/^[^ ]+ //' | tail -10 printD } fn seconds2rfc822{ date `{cat} | awk '{print $1 ", " $3 " " $2 " " $6 " " $4 " " $5}' } fn syncroom{ { sroom=`{echo $1 | sed 's/\//\\\//g'} echo ',s/^'$sroom'\/[0-9]+$/'$sroom'\/'$2'/g' echo w echo q } | sam -d $3 >[2]/dev/null } fn terminate{ if(! ~ $#logged_user 0) logout echo `{date} $logged_user $* >[1=2] echo echo $* echo Kill sleep | rc echo interrupt >/proc/$9pid/notepg exit terminate } fn yn{ echo -n $1 '(y, n) ' yn=`{read} if(~ $#yn 0) terminate hungup yn=`{echo $"yn | nstrip} switch($yn){ case y eval $*(2-) case n ; case * eval yn $* } } fn usage{ echo usage: $argv0 '[ -f room ] [ -m mountpoint ] [ -p maxposts ]' >[1=2] exit usage } while(~ $1 -*){ switch($1){ case -d debug=1 case -f room=`{echo $2 | sed 's/\./\//g'} shift case -m mnt=$2 shift case -p maxposts=$2 shift case * usage } shift } if(! ~ $#* 0) usage if(~ $#mnt 0) usage if(~ $#maxposts 0) maxposts=9000 if(~ $#room 0) room=lobby cat $mnt/etc/motd login if(~ $#plines 0) plines=22 if(~ $#wrap 0) wrap=70 prompt=$room/$post go=() if(~ $#post 0) post=$posts(1) echo no >/tmp/busy while(test -r $mnt/etc/users/$logged_user/$lfile){ # biff sleep 300 if(~ `{cat $mnt/etc/users/$logged_user/biff} yes && ~ `{cat /tmp/busy} no){ echo date printg } } & while(! ~ $status terminate){ echo -n $"prompt': ' cmd=`{read} if(~ $#cmd 0){ echo `{date} $logged_user XXX >>[1=2] terminate hungup } # command input ignores everything after unicode 00a0 cmd=`{echo $"cmd | sed 's/'^$00a0^'.*$//g' | nstrip } if(~ $debug 1) echo `{date} $logged_user $"cmd >>[1=2] & switch($cmd){ case [0-9]* if(grep -s $cmd(1) <{echo $posts}){ post=$cmd(1) printp $post } if not echo !no such message case b if(! ~ $post $posts(1)){ post=`{echo $post^-1 | bc} printp $post } case B if(~ $biff no){ echo !biff enabled biff=yes } if not if(~ $biff yes){ echo !biff disabled biff=no } echo $biff >$mnt/etc/users/$logged_user/biff case C echo -n 'new ' changepassword case d yn 'delete '$"post'?' delete $post case d' '* if(grep -s -e '^[0-9]+$' <{echo $cmd(2)}){ post=$cmd(2) yn 'delete '$"post'?' delete $post } if not echo !no such message case D printD case D' '* echo `{echo $cmd(2-)} >$mnt/etc/users/$logged_user/delimiter && delimiter=$cmd(2-) case '>D' >$mnt/etc/users/$logged_user/delimiter && delimiter=() case e editpost posts=`{getposts} case f post=$posts(1) printp $post case g printg | np -$plines case g' '* goto $cmd(2) case gr printgr | np -$plines case grep' '* regexp=`{echo $cmd(2-)} printD grep -e $"regexp [0-9]* | sed 's/^/ /' | sed 's/:/'^$00a0^'# /' | sort -n | np -$plines printD case h help printhelp | np -$plines case k sed 's/^/ g /g' $mnt/etc/roomlist | sort | np -$plines case k' '* printD grep -e `{echo $cmd(2)} $mnt/etc/roomlist | sed 's/^/ g /g' | sort | np -$plines printD case l post=$posts($#posts) printp $post case L echo $plines case L' '* lines=`{echo $cmd(2)} if(grep -s -e '^[0-9]+$' <{echo $lines}) plines=$lines case p re printp $post case p' '* re' '* Re:' '* post=`{echo $cmd(2) | sed 's/://g'} printp $post case P printpp $post case P' '* post=$cmd(2) printpp $post case q yn quit? 'terminate logout' case r' '* post=$cmd(2) reply=$post editpost posts=`{getposts} case r reply=$post editpost posts=`{getposts} case S printsig case S' '* echo `{echo $cmd(2-)} >$mnt/etc/users/$logged_user/sig && sig=$cmd(2-) case '>S' >$mnt/etc/users/$logged_user/sig && sig=() case u printD ls -p $mnt/etc/users | sort | np -$plines printD case w printw case W echo $wrap case W' '* nwrap=`{echo $cmd(2)} if(grep -s -e '^[0-9]+$' <{echo $nwrap}) wrap=$nwrap case wr printwr case y syncroom $room $post $mnt/etc/users/$logged_user/rooms posts=`{getposts} checkroom $room case z yn 'zoom all rooms to the latest message?' cp $mnt/etc/rooms $mnt/etc/users/$logged_user/rooms case '"' printq $post case '" '* post=$cmd(2) printq $post case '?' { echo biff: $biff echo mnt: $mnt echo room: $room echo maxposts: $maxposts echo posts: $posts echo post: $post echo delimiter: `{printD} echo editor: `{echo $editor} echo plines: $plines echo wrap: $wrap echo sig: `{printsig} } | np -$plines case t date case n * if(~ $post $posts(1) && ~ $#go 0){ go=1 printp $post } if not if(! ~ $post $posts($#posts)){ go=1 post=`{echo $post^+1 | bc} if(test $post -gt $posts($#posts)) post=$posts($#posts) printp $post } } }