#/usr/local/bin/perl require "sys/termio.ph"; # ********************************************************************* # threaded newsreader 0.1 beta, august 1992 # programmed by vic spicer (vspicer@ccu.umanitoba.ca) with design # and optimization help by budi rahardjo (rahardj@ccu.umanitoba.ca) # ********************************************************************* # these parameters should be set by user or operator $sun=1; # set =1 for UNIX, 0 for DOS # these are configuration variables, needed for both versions # fully domainized internet address $environment_site="feenix.metronet.com"; # organization name $environment_organization="feenix"; # directory for UNIX news $newsdir_sun="/news"; # directory for MSDOS news $newsdir_dos="\\news"; # news agents $dos_post="rnews <"; $sun_post="inews "; # these MUST be set for DOS if not found in environment # as HOME, VISUAL and USER $environment_home="c:\\nn"; $environment_visual="c:\\tools\\vi.exe"; $environment_user="vspicer"; # ************************************************************************** # code starts here.... &INVERSE(); print "budi and vic's threaded newsreader"; &NORMAL(); print "\n"; # sun users MUST have these defined in environment, generally a given if ($sun==1) { if ($ENV{"HOME"} eq "") { die "error. environment variable HOME not found\n"; } if ($ENV{"USER"} eq "") { die "error. environment variable USER not found\n"; } if ($ENV{"VISUAL"} eq "") { die "error. environment variable VISUAL not found\n"; } $HOME=$ENV{"HOME"}; $USER=$ENV{"USER"}; $VISUAL=$ENV{"VISUAL"}; } # dos users can have either if ($sun==0) { if ($ENV{"HOME"} ne "") { $HOME=$ENV{"HOME"}; } else { $HOME=$environment_home; } if ($ENV{"VISUAL"} ne "") { $VISUAL=$ENV{"VISUAL"}; } else { $VISUAL=$environment_visual; } if ($ENV{"USER"} ne "") { $USER=$ENV{"USER"}; } else { $USER=$environment_user; } } # check if posting only if ($ARGV[0] eq "+p") { if ($sun==0) { do KILLALLKEYS(); do SETKEYS(); } else { $SIG{'INT'}='Quit'; } $group_look=0; $BRGV[0]=""; $look=1; while ($ARGV[$look] ne "") { $BRGV[0]="$BRGV[0],$ARGV[$look]"; $look=$look+1; } &CLS(); $BRGV[0]=substr($BRGV[0],1); # note we allow posting to groups that may not exist on this site &INVERSE(); print "Posting to: $BRGV[0]\n"; &NORMAL(); if ($sun==0) { do RAK(); } print "\nSubject: "; $subpost=; chop($subpost); if ($sun==0) { do KILLALLKEYS(); do SETKEYS(); } if ($subpost eq "") { print "\nNo subject given. Aborted.\n"; if ($sun==0) { do RAK(); } die "\n"; } &MAKEHEADER(); &EDITMESSAGE(); print "\e[23;1H\e[K"; &POSTORNOT(); print "\e[23;1H\e[K$message\n\n"; if ($sun==0) { do RAK(); } die "\n"; } if ($ARGV[0] eq "") { &GETKNOWN(); } else { $k=0; while ($ARGV[$k] ne "") { $BRGV[$k]="\L$ARGV[$k]\E"; $k++; } $BRGV[$k]=""; } $group_look=0; if ($sun==0) { do KILLALLKEYS(); do SETKEYS(); } else { $SIG{'INT'}='Quit'; } # loop for every group in command line while ($BRGV[$group_look] ne "") { $crop="$BRGV[$group_look]"; if ($sun==1) { $crop="$newsdir_sun/$crop"; $crop =~ s/\./\//g; } else { $crop="$newsdir_dos\\$crop"; $crop =~ s/\./\\/g; } # get current group status do CURRENT(); print "scanning: $BRGV[$group_look]... building threads\n"; # grab the threads... do THREAD($crop); if ($articles==0) { print "no new articles since last read\n"; goto nextgroup; } if ($sun==1) { $crop="$crop/"; } else { $crop="$crop\\"; } do SORTEM(); # set up stuff $current=0; $message=""; # show page &CLS(); show_page: # show header stuff &INVERSE(); print "\e[1;1H\e[K"; print " " x 80; print "\e[1;1H"; print "group: $BRGV[$group_look]"; # update screen when not cleared upda_scrn: &INVERSE(); $page=$current/15; $pages=int(($threads-1)/15); print "\e[1;50H"; print "pg: $page/$pages"; print "\e[1;64H "; print "\e[1;64H"; print "$message"; &NORMAL(); print "\n\n"; if ($dont_redraw==1) { $dont_redraw=0; goto cmd_in; } $label="a"; $ll=3; for ($i=$current;$i<$threads & $i<$current+15;$i++) { $grok=$label; $disp=substr($thread[$i],0,70); if ($read[$i]==1) { $readit="* "; } else { $readit=" "; } print "\e[$ll;1H\e[K\e[$ll;1H"; $ll++; if ($marked[$i]==1) { &INVERSE(); print "$label. $readit$disp [$msgs[$i]]\n"; &NORMAL(); } if ($marked[$i]==0) { print "$label. $readit$disp [$msgs[$i]]\n"; } $label++; } for ($kup=$ll;$kup<19;$kup++) { print "\e[$kup;1H\e[K"; } cmd_in: $command=""; print "\e[22;1H"; &INVERSE(); print "cmd:[a..$grok] (+)(-)page (/)scan (u)nmark (r)ead (q)uit group e(x)it (p)ost:"; &NORMAL(); print " "; print "\e[22;74H"; if ($sun==0) { $command=; chop($command); } else { $command=&keypress(); print "\n"; } # 1- accept selection toggle commands if ($command ge "a" & $command lt $label) { $lnum=ord($command)-ord("a"); $select=$current+$lnum; $state=$marked[$select]; if ($state ==0) { $rp=$lnum+3; $marked[$select]=1; if ($read[$select]==1) { $readit="* "; } else { $readit=" "; } print "\e[$rp;1H"; &INVERSE(); $moo=substr($thread[$select],0,70); print "$command. $readit$moo [$msgs[$select]]\n"; &NORMAL(); } if ($state ==1) { $rp=$lnum+3; $marked[$select]=0; if ($read[$select]==1) { $readit="* "; } else { $readit=" "; } print "\e[$rp;1H"; $moo=substr($thread[$select],0,70); print "$command. $readit$moo [$msgs[$select]]\n"; } goto cmd_in; } # forwards a page if ($command eq "+") { $temp=$current+15; if ($temp< $threads) { $current=$temp; goto upda_scrn; } $message=""; goto cmd_in; } # backwards a page if ($command eq "-") { $temp=$current-15; if ($temp >=0) { $current=$temp; goto upda_scrn; } $message=""; goto cmd_in; } if ($command eq "p") { if ($sun==0) { do RAK(); } &INVERSE(); print "\e[23;1HSubject: "; &NORMAL(); $subpost=; chop($subpost); if ($sun==0) { do KILLALLKEYS(); do SETKEYS(); } if ($subpost eq "") { $message="no subject"; print "\e[23;1H\e[K"; $dont_redraw=1; goto show_page; } &MAKEHEADER(); print "\e[23;1H\e[K"; &EDITMESSAGE(); &POSTORNOT(); print "\e[23;1H\e[K"; if ($sun==1) { &CLS(); } else { $dont_redraw=1; } goto show_page; } if ($command eq "/") { if ($sun==0) { do RAK(); } &INVERSE(); print "\e[23;1HScan for: "; &NORMAL(); $scanfor=; chop($scanfor); if ($sun==0) { do KILLALLKEYS(); do SETKEYS(); } $found=0; print "\e[23;1H\e[K"; if ($scanfor eq "") { $message="scan aborted"; $dont_redraw=1; goto show_page; } $dont_redraw=1; for ($i=0;$i<$threads;$i++) { $check="$thread[$i]"; if ($check =~ /$scanfor/i) { $marked[$i]=1; $found++; if ($i>=$current & $i<=$current+15) { $dont_redraw=0; } } } if ($found==1) { $message="$found match found "; } if ($found>1) { $message="$found matches found"; } if ($found==0) { $message="no matches found "; $dont_redraw=1; } goto upda_scrn; } # un-mark stuff if ($command eq "u") { $dont_redraw=1; for ($i=0;$i<$threads;$i++) { if ($marked[$i]==1 & $i>=$current & $i<=$current+15) { $dont_redraw=0; } $marked[$i]=0; $read[$i]=0; } $message="all marks cleared"; goto upda_scrn; } # quit if ($command eq "q") { &WRITEGROUPS(); for ($i=0;$i<$threads;$i++) { $marked[$i]=0; $class[$i]=""; $thread[$i]=""; $read[$i]=0; $thread_list[$i]=""; $msgs[$i]=0; } goto nextgroup; } # end program if ($command eq "x") { &WRITEGROUPS(); &CLS(); if ($sun==0) { do RAK(); } print "bye for now...\n\n"; exit(0); } # read marked if ($command eq "r" | $command eq " ") { $markcount=0; for ($i=0;$i<$threads;$i++) { $markcount=$markcount+$marked[$i]; } if ($markcount==0) { $message="none marked "; $dont_redraw=1; goto show_page; } &CLS(); $quit=0; for ($i=0;$i<$threads & $quit==0;$i++) { if ($marked[$i]==1) { $read[$i]=1; $marked[$i]=0; @article=split(/ /,$thread_list[$i]); $j=0; $abort=0; $quit=0; while ($article[$j] ne "" & $abort==0 & $quit==0) { &READARTICLE($article[$j]); } } } &CLS(); #$message=""; goto show_page; } goto cmd_in; # end of command structure nextgroup: $group_look++; } if ($sun==0) { do RAK(); } # end of main... # display an article... sub READARTICLE { local($peep)=@_; $art1="$crop$peep"; $death=open(VIEW,$art1); if ($death==0) { if ($sun==0) { &RAK(); } die "article file not found: $art1\n"; } top_page: &INVERSE(); for ($cl=1;$cl<3;$cl++) { print "\e[$cl;1H\e[K"; } $arp=$j+1; $subjectd=substr($thread[$i],0,60); &INVERSE(); print "\e[1;1H\e[K"; print " " x 80; print "\e[1;1H$peep"; print "\e[1;10H$subjectd\e[1;72H[$arp/$msgs[$i]]\n"; $msg_lines=0; # pick off sender and date $inli=; while ($inli ne "\n") { $inli=; $store="$inli"; if ($store =~ /Date:/) { $dmsg="$inli"; } $store="$inli"; if ($store =~ /From:/) { $from="$inli"; } $store="$inli"; if ($store =~ /Lines:/) { $msg_lines="$inli"; $msg_lines =~ s/Lines: //; chop($msg_lines); } $store="$inli"; if ($store =~ /Message-ID:/) { $reference="$inli"; $reference =~ s/Message-ID: //; chop($reference); } } $from =~ s/From: //; $dmsg =~ s/Date: //; chop($from); chop($dmsg); $fromd=substr($from,0,48); $dmsgd=substr($dmsg,0,30); print "\e[2;1H\e[K"; print " " x 80; print "\e[2;1H$fromd "; print "\e[2;49H|$dmsgd"; &NORMAL(); print "\e[3;1H["; print "-" x 78; print "]\n"; # display rest of stuff $in_lines=0; $show="moof"; $dropout=0; while ($show ne "" & $dropout==0) { $dline=0; while ($dline<18 & $show ne "") { $show=; $in_lines++; $dl=$dline+4; $s1=substr($show,0,80); print "\e[$dl;1H\e[K"; print "\e[$dl;1H$s1"; $dline++; } for ($k=$dline;$k<18;$k++) { $dp=$k+4; print "\e[$dp;1H\e[K"; } print "\e[22;1H["; print "-"; if ($msg_lines!=0) { $percent=int($in_lines*100/$msg_lines); if ($percent>100) { $percent=100; } print " ("; if ($percent<10) { print "0"; } if ($percent<100) { print "0"; } print "$percent)% "; } else { print "--------"; } print "-" x 69; print "]"; $cmd2=""; # process user input while ($cmd2 ne "q" & $cmd2 ne " " & $cmd2 ne "n" & $cmd2 ne "b" & $cmd2 ne "s" & $cmd2 ne "f") { print "\e[23;1H"; &INVERSE(); print "cmd: (n)ext (space) (b)ack (s)kip thread (f)ollow (q)uit: "; &NORMAL(); if ($sun==0) { $cmd2=; chop($cmd2); } else { $cmd2=&keypress(); print "\n"; } # interpret commands if ($cmd2 eq "b") { $j=$j-2; if ($j<-1) { $j=-1; } $dropout=1; } if ($cmd2 eq "n") { $dropout=1; } if ($cmd2 eq "s") { $abort=1; $dropout=1; } if ($cmd2 eq "q") { $quit=1; $dropout=1; } if ($cmd2 eq "f") { $subpost="Re: $thread[$i]"; do MAKEHEADER(); print "\e[23;1H\e[KQuote message [n]: "; $rep1=""; if ($sun==0) { $rep1=; chop($rep1); } else { $rep1=&keypress(); print "\n"; } if ($rep1 eq "y") { do QUOTE($art1); } do EDITMESSAGE(); do POSTORNOT(); $cmd2="q"; $quit=1; $dropout=1; } } # wend for input interpeting } # wend for no dropout and not eof $j++; ret1: close(VIEW); $dropout=0; } # threaded news scanning sub THREAD { local($group)=@_; $notgroup=0; $check=$group; stat($check); $notgroup=1 if !(-d _); if ($notgroup==1) { if ($sun==0) { &RAK(); } die "group $BRGV[$group_show] not found.\n"; } if ($sun ==1) { $group="$group/"; } else { $group="$group\\"; } opendir(ALL,$group); $ent="moof"; $threads=0; $articles=0; $lastout=0; while ($ent ne "") { $ent=readdir(ALL); $ok=1; $found=0; $check="$group$ent"; stat($check); $ok=0 if -d _; $ok=0 if ($ent eq ".") | ($ent eq "..") | ($ent eq ""); if ($ent <= $lastmessage) { $ok=0; } if ($ent > $lastout) { $lastout=$ent; } # if it's a file.... if ($ok ==1 ) { print "\rreading: $ent"; $|=1; open(article,$check); # look for subject line $ins=""; $ins=
; while (($ins ne "") & (!($ins =~ /Subject:/))) { $ins=
; $line1="$ins"; } $articles++; chop($line1); $line1 =~ s/Subject: //; $line1 =~ s/Re: //; $line1 =~ s/RE: //; $line1 =~ s/^[ \t]+//; $line1 =~ s/[ \t]+$//; $found=0; for ($scan=0;($scan<$threads) & (found!=1);$scan++) { if ($line1 eq $thread[$scan]) { $thread_list[$scan]="$thread_list[$scan] $ent"; $found=1; $msgs[$scan]++; } } if ($found==0) { $thread[$threads]="$line1"; $thread_list[$threads]="$ent"; $msgs[$threads]=1; $threads++; } $found=0; close(article); } # close file } # no more directory entries closedir(ALL); } sub CLS { print "\e[;H\e[2J"; } sub INVERSE { print "\e[7m"; } sub NORMAL { print "\e[0m"; } # scan newsgroup file, if it exists, for know groups sub GETKNOWN { $pathlook=$HOME; if ($sun==1) { $current="$pathlook/brn.dat"; } else { $current="$pathlook\\brn.dat"; } $status=open(allfiles,$current); if ($status ==0) { if ($sun==0) { &RAK(); } die "no subscription list.\n"; } $line="grok"; $k=0; while ($line ne "") { $line=; chop($line); ($a,$b)=split(/:/,$line); $BRGV[$k++]=$a; } close(allfiles); } sub SORTEM { undef @class2; undef @class; for ($i=0;$i<$threads;$i++) { $class[$i]="$thread[$i]SpliTTER$thread_list[$i]SpliTTER$msgs[$i]"; } @class2=sort @class; for ($i=0;$i<$threads;$i++) { ($thread[$i],$thread_list[$i],$msgs[$i])=split(/SpliTTER/,$class2[$i]); } undef @class2; undef @class; } # get newsgroup info sub CURRENT { $pathlook=$HOME; if ($sun==1) { $current="$pathlook/brn.dat"; } else { $current="$pathlook\\brn.dat"; } $status=open (group,$current); $lastmessage=0; $lists=0; if ($status ==0) { $new_write=1; } if ($status !=0) { $line="grok"; while ($line ne "") { $line=; $belong[$lists]="$line"; $lists++; ($newsgroup,$last)=split(/:/,$line); if ($newsgroup eq $BRGV[$group_look]) { $lastmessage=$last; } } } if ($lastmessage==0) { $new_write=1; } close(group); } # write out group info... sub WRITEGROUPS { $pathlook=$HOME; if ($sun==1) { $current=">$pathlook/brn.dat"; } else { $current=">$pathlook\\brn.dat"; } $status=open (group,"$current"); if ($status==0) { if (sun==0) { &RAK(); } die "can not write to $current\n"; } $kout=0; while ($kout<=$lists) { ($stuff,$extra)=split(/:/,$belong[$kout]); $feed="$belong[$kout]"; if ($kout==$lists & $new_write==1) { $feed="$BRGV[$group_look]:$lastout\n"; } if ($stuff eq $BRGV[$group_look]) { $feed="$BRGV[$group_look]:$lastout\n"; } if ($feed ne "\n") { print (group "$feed"); } $kout++; } close(group); } sub KILLALLKEYS { for ($key=0;$key<256;$key++) { print "\033[$key;0p"; } } sub RAK { for ($key=0;$key<256;$key++) { print "\033[$key;$key"; print "p"; } } sub SETKEYS { for ($key=97;$key<121;$key++) { print "\033[$key;$key;13p"; } print "\033[43;43;13p"; print "\033[45;45;13p"; print "\033[47;47;13p"; print "\033[32;32;13p"; print "\033[121;121;13p"; } sub keypress { # $bsd= -f '/vmunix'; # # if ($bsd) { system "stty cbreak /dev/tty 2>&1"; } # else { system "stty",'cbreak', system "stty",'eol','^A'; }; # local($key); $key = `../kr -o`; $key; } # ^C trap for UNIX sub Quit { &WRITEGROUPS(); &CLS(); print "bye for now...\n"; exit(0); } # make header based on environment variables and other stuff sub MAKEHEADER { $messfile=$HOME; if ($sun==0) { $messfile="$messfile\\mess.tmp"; } else { $messfile="$messfile/mess.tmp"; } $mess2=">$messfile"; $status=open(groupx,"$mess2"); if ($status==0) { if (sun==0) { &RAK(); } die "can not write to $messfile\n"; } $userid=$USER; $frominfo=$environment_site; $frominfo="$userid@$frominfo"; $newsgroup="$BRGV[$group_look]"; $frominfo="From: $frominfo\n"; $newsgroup="Newsgroups: $newsgroup\n"; $subject="Subject: $subpost\n"; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$off)=gmtime(time); $month_word=("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug", "Sept","Oct","Nov","Dec")[$mon]; $day_word=("Sun","Mon","Tue","Wed","Thur","Fri","Sat")[$wday]; print (groupx "$frominfo"); print (groupx "$newsgroup"); print (groupx "$subject"); print (groupx "Date: $day_word, $month_word"); print (groupx " $mday $year $hour:$min:$sec GMT\n"); print (groupx "Organization: $environment_organization\n"); print (groupx "Distribution: world\n"); print (groupx "\n\n"); close(groupx); } # edit message sub EDITMESSAGE { $editor=$VISUAL; $cmd="$editor $messfile"; system $cmd; } # post message or not? sub POSTORNOT { $cmd3=""; while ($cmd3 ne "a") { print"\e[23;1H\e[K"; &INVERSE(); print "CMD: (s)end, (e)dit, (a)bort: "; &NORMAL(); if ($sun==0) { $cmd3=; chop($cmd3); } else { $cmd3=&keypress(); print "\n"; } if ($cmd3 eq "s") { if ($sun==0) { $operation="$dos_post$messfile"; } else { $operation="$sun_post$messfile"; } system $operation; $message="posted"; return; } if ($cmd3 eq "e") { &EDITMESSAGE; } if ($cmd3 eq "a") { $message="post aborted"; return; } } } # quote message sub QUOTE { local($quoted)=@_; $status=open(messout,">>$messfile"); $status2=open(messin,"$quoted"); if ($status==0) { if ($sun==0) { &RAK(); } die "can not open $messfile\n"; } $in1=""; while ($in1 ne "\n") { $in1=; } print (messout "In $reference $from writes:\n"); while ($in1 ne "") { $in1=; print (messout ">$in1"); } print (messout "\n"); close(messin); close(messout); } @REM=(qq! :end !) if 0;