rsalz@bbn.com (Rich Salz) (01/05/91)
Submitted-by: Wayne Davison <0004475895@mcimail.com> Posting-number: Volume 23, Issue 67 Archive-name: trn/part08 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: Pnews.SH bits.c cheat.c rt-rn.c # Wrapped by rsalz@litchi.bbn.com on Thu Dec 27 11:34:07 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 8 (of 14)."' if test -f 'Pnews.SH' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Pnews.SH'\" else echo shar: Extracting \"'Pnews.SH'\" \(16703 characters\) sed "s/^X//" >'Pnews.SH' <<'END_OF_FILE' Xcase $CONFIG in X '') . ./config.sh ;; Xesac Xecho "Extracting Pnews (with variable substitutions)" X$spitshell >Pnews <<!GROK!THIS! X$startsh X# $Header: Pnews.SH,v 4.3.3.2 90/08/20 16:24:09 davison Trn $ X# X# $Log: Pnews.SH,v $ X# Revision 4.3.3.2 90/08/20 16:24:09 davison X# Use mbox.saver for AUTHORCOPY. Fixed sitename handling. X# X# Revision 4.3.3.1 90/07/24 22:01:25 davison X# Initial Trn Release X# X# Revision 4.3.2.7 90/05/04 23:14:45 sob X# Expires: line removed from Pnews.header. X# X# Revision 4.3.2.6 90/04/21 16:53:43 sob X# Corrected a typo pointed out by Bill Aten. X# X# Revision 4.3.2.5 89/12/17 01:53:48 sob X# Changed from using ypcat to using ypmatch X# X# Revision 4.3.2.4 89/12/09 01:48:30 sob X# Reply-To: field removed. This makes all the headers created by rn X# look roughly the same. X# X# Revision 4.3.2.3 89/11/26 22:20:04 sob X# Added support for some of the top level names that are not part of X# the mainstream. X# X# Revision 4.3.2.2 89/11/06 00:25:39 sob X# Some minor changes to make the help messages more accurate X# X# Revision 4.3.2.1 89/11/06 00:21:30 sob X# Added RRN support from NNTP 1.5 X# X# Revision 4.3.1.4 86/09/05 15:21:10 lwall X# Changes for new top-level newsgroup names. X# X# Revision 4.3.1.3 85/08/01 14:24:06 lwall X# Added AUTHORCOPY. Temp file is no longer in /tmp. 'e editor' added. X# X# Revision 4.3.1.2 85/05/17 10:36:46 lwall X# Removed some extra backslashes. X# X# Revision 4.3.1.1 85/05/10 11:30:21 lwall X# Branch for patches. X# X# Revision 4.3 85/05/01 12:20:33 lwall X# Baseline for release with 4.3bsd. X# X# X# syntax: Pnews -h headerfile or X# Pnews -h headerfile oldarticle or X# Pnews newsgroup title or just X# Pnews X Xexport PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh \$0; kill \$\$) X X# System dependencies X Xmailer="${mailer-/bin/mail}" X# if you change this to something that does signatures, take out signature code X Xcase $portable in Xdefine) X# your site name Xcase "$hostcmd" in X'') sitename="$sitename" ;; X*) sitename=\`$hostcmd\` ;; Xesac Xcase \$sitename in X *.*) X ;; X *) X sitename=\${sitename}.$domain X ;; Xesac X# where recordings, distributions and moderators are kept Xlib=\`$filexp $lib\` X# where important rn things are kept Xrnlib=\`$filexp $rnlib\` X;; Xundef) X# your site name Xsitename="$sitename" X# where recordings, distributions and moderators are kept Xlib="$lib" X# where important rn things are kept Xrnlib="$rnlib" X;; Xesac X X# your organization name Xorgname="$orgname" X# what pager you use--if you have kernal paging use cat Xpager="\${PAGER-$pager}" X# how you derive full names, bsd, usg, or other Xnametype="$nametype" X# default editor Xdefeditor="$defeditor" X# how not to echo with newline Xn="$n" Xc="$c" X X# You should also look at the distribution warnings below marked !DIST! X# to make sure any distribution regions you are a member of are included. X# The following are some prototypical distribution groups. If you do not X# use them all set the unused ones to a non-null string such as 'none'. Xloc="$locpref" Xorg="$orgpref" Xcity="$citypref" Xstate="$statepref" Xcntry="$cntrypref" Xcont="$contpref" X Xtest=${test-test} Xsed=${sed-sed} Xecho=${echo-echo} Xcat=${cat-cat} Xegrep=${egrep-egrep} Xgrep=${grep-grep} Xrm=${rm-rm} Xtr=${tr-tr} Xinews=${inews-inews} Xypmatch=${ypmatch} X X!GROK!THIS! X$spitshell >>Pnews <<'!NO!SUBS!' Xdotdir=${DOTDIR-${HOME-$LOGDIR}} Xtmpart=$dotdir/.article X Xif $test -f $dotdir/.pnewsexpert; then X expertise=expert Xelse X $cat <<'EOM' XI see you've never used this version of Pnews before. I will give you extra Xhelp this first time through, but then you must remember what you learned. XIf you don't understand any question, type h and a CR (carriage return) for Xhelp. X XIf you've never posted an article to the net before, it is HIGHLY recommended Xthat you read the netiquette document found in news.announce.newusers so Xthat you'll know to avoid the commonest blunders. To do that, interrupt XPnews, and get to the top-level prompt of rn. Type "g news.announce.newusers" Xand you are on your way. X XEOM X expertise=beginner Xfi X Xcase $cntry in X can) stpr=Province ;; X *) stpr=State ;; Xesac X Xheaderfile="" Xcase $# in X0) ;; X*) case $1 in X -h) X headerfile="$2" X shift X shift X case $# in X 0) X oldart="" X ;; X *) X oldart="$1" X shift X ;; X esac X ;; X esac X ;; Xesac X Xcase $headerfile in X'') X . $rnlib/Pnews.header X ;; X*) X $cat < $headerfile > $tmpart X ;; Xesac X rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article ; $echo Article appended to ${HOME-$LOGDIR}/dead.article ; exit" X trap "$rescue" 1 X trap "$rescue" 2 X X$echo "" X X# extract the newsgroups list and distribution Xhdr_newsgroups=`$sed -n -e '/^Newsgroups:/{' -e 's///' -e 's/,/ /g' -e p -e q -e '}' $tmpart` Xhdr_distribution=`$sed -n -e '/^Distribution:/{' -e 's///' -e p -e q -e '}' $tmpart` X X# check for "poster" magic cookie Xflag=0 Xfor ng in $hdr_newsgroups ; do X case "$ng" in X poster) flag=1 ;; X *) ;; X esac Xdone Xcase $flag in X1) X $echo " " X $echo "The original author has requested that messages be sent back via" X $echo "mail rather than posting to news. Do you want to jump out of this" X $echo $n "and mail your reply instead? [yn] $c" X read ans X case $ans in X n*) ;; X *) exit ;; X esac X $echo " " X $echo "OK, but you will have to edit the 'Newsgroups:' line in the message." X ;; Xesac X X# play recorded message Xif $test -s ${lib}/recording ; then X for ng in $hdr_newsgroups ; do X _rec1=${lib}/`$sed -n "/^$ng/s/^.* //p" ${lib}/recording` X _tmp=`$echo $ng |$sed "s/\..*//"` X _rec2=${lib}/`$cat -s ${lib}/recording|$grep ${_tmp}.all|$sed "s/^.* //"` X if $test -f ${_rec1} ; then X $cat -s ${_rec1} X fi X if $test -f ${_rec2} ; then X $cat -s ${_rec2} X fi X done Xfi X X# determine the distribution of this message Xset X $hdr_distribution Xshift Xif $test $# -gt 0 ; then X dist=$1 Xelse X set X $hdr_newsgroups X shift X if $test $# -gt 0 ; then X dist=$1 X else X dist=misc.whatever X fi Xfi X X# tell them what we think they are doing... !DIST! Xcase $dist in Xworld.*|comp.*|news.*|sci.*|rec.*|misc.*|soc.*|talk.*|alt.*) X $cat <<'EOM' XThis program posts news to thousands of machines throughout the entire Xcivilized world. Your message will cost the net hundreds if not thousands of Xdollars to send everywhere. Please be sure you know what you are doing. X XEOM X ;; Xvmsnet.*) X $echo 'This program posts news to many machines.' X ;; Xbit.*) X $echo 'This program posts news to many machines on BITNET.' X ;; Xddn.*) X $echo 'This program posts news to many machines throughout the internet.' X ;; X$cont.*) X $echo 'This program posts news to many machines throughout the continent.' X ;; X$cntry.*) X $echo 'This program posts news to many machines throughout the country.' X ;; X$state.*) X $echo 'This program posts news to many machines throughout the state.' X ;; X$city.*) X $echo 'This program posts news to many machines throughout the city.' X ;; X$org.*) X $echo 'This program posts news to machines throughout the organization.' X ;; X$loc.*) X $echo 'This program posts news to machines throughout the local organization.' X ;; X*.*) X $echo 'This program may post news to many machines.' X ;; Xto.*) X $echo 'This program may post news to a partcular machine.' X ;; X*) X $echo 'This program posts news to everyone on the machine.' X ;; Xesac Xans="" Xwhile $test "$ans" = "" ; do X $echo $n "Are you absolutely sure that you want to do this? [ny] $c" X read ans X case $ans in X y*) ;; X f*) ;; X h*) $cat <<'EOH' X XType n or CR to exit, y to post. X XEOH X ans="" ;; X *) exit ;; X esac Xdone X Xfile=h Xwhile $test "$file" = h ; do X $echo "" X $echo $n "Prepared file to include [none]: $c" X read file X case $file in X h) X $cat <<'EOH' X XIf you have already produced the body of your article, type the filename Xfor it here. If you just want to proceed directly to the editor, type a XRETURN. In any event, you will be allowed to edit as many times as you Xwant before you send off the article. XEOH X ;; X '') X $echo "" >> $tmpart X state=edit X ;; X *) X $cat $file >>$tmpart X state=ask X ;; X esac Xdone X X$echo "" X Xwhile true ; do X case $state in X edit) X case $expertise in X beginner) X $cat </dev/null >$dotdir/.pnewsexpert X $cat <<'EOMessage' XA temporary file has been created for you to edit. Be sure to leave at Xleast one blank line between the header and the body of your message. X(And until a certain bug is fixed all over the net, don't start the body of Xyour message with any indentation, or it may get eaten.) X XWithin the header may be fields that you don't understand. If you don't Xunderstand a field (or even if you do), you can simply leave it blank, and Xit will go away when the article is posted. X XType return to get the default editor, or type the name of your favorite Xeditor. X XEOMessage X ;; X esac X case "${VISUAL-${EDITOR-}}" in X '') X tmp=h X ;; X *) X tmp='' X ;; X esac X while $test "$tmp" = h ; do X $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c" X read tmp X case $tmp in X h) X $cat <<'EOH' X XType a return to get the default editor, or type the name of the editor you Xprefer. The default editor depends on the VISUAL and EDITOR environment Xvariables. X XEOH X ;; X '') X ;; X *) X VISUAL=$tmp X export VISUAL X ;; X esac X done X trap : 2 X ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart X trap "$rescue" 2 X state=ask X ;; X X ask) X $echo "" X $echo $n "Send, abort, edit, or list? $c" X read ans X X case "$ans" in X a*) X state=rescue X ;; X e*) X set $ans X case $# in X 2) VISUAL="$2" ;; X esac X state=edit X ;; X l*) X $pager $tmpart X state=ask X ;; X s*) X state=send X ;; X h*) X $cat <<'EOH' X XType s to send the article, a to abort and append the article to dead.article, Xe to edit the article again, or l to list the article. X XTo invoke an alternate editor, type 'e editor'. XEOH X esac X ;; X X send) X set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'` X shift X case $# in X 2) X state=cleanup X if $test -f $lib/moderators; then X tryinews=no X shift X case "$1" in X *,*) set `$echo $1 | tr ',' ' '`;; X esac X for newsgroup in $*; do X# the following screwy sed should prevent Eunice from hanging on no match X moderator=`$sed <$lib/moderators \ X -e "/^$newsgroup[ ]/!s/.*//" \ X -e "s/^$newsgroup[ ]//"` X case ${moderator}X in X X) tryinews=yes X ;; X *) X $echo Mailing to moderator $moderator X case "$sign" in X n*) ;; X *) X if $test -f $dotdir/.signature; then X echo $n "Append .signature file? [y] $c" X read ans X case $ans in X ''|y*) X echo "-- " >> $tmpart X cat $dotdir/.signature >> $tmpart X ;; X esac X fi X sign=no X ;; X esac X case "$mailer" in X *recmail) X $echo To: $moderator | $cat - $tmpart | $mailer X ;; X *) X $mailer $moderator < $tmpart X ;; X esac X case $? in X 0) ;; X *) X $echo Unable to mail to moderator $moderator X state=rescue X ;; X esac X ;; X esac X done X else X tryinews=yes X fi X case "$tryinews" in X yes) X if $inews -h < $tmpart ; then X : null X else X state=rescue X fi X ;; X esac X ;; X *) X $echo "" X $echo "Malformed Newsgroups line." X $echo "" X sleep 1 X state=edit X ;; X esac X ;; X rescue) X if $test -s $tmpart; then X $cat $tmpart >> ${HOME-$LOGDIR}/dead.article X $echo "Article appended to ${HOME-$LOGDIR}/dead.article" X $echo "A copy may be temporarily found in $tmpart" X else X $echo "Null article discarded." X fi X exit X ;; X cleanup) X case "${AUTHORCOPY-none}" in X none) X ;; X *) X set X ${USER-${LOGNAME-`who am i`}} unknown X shift X $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $AUTHORCOPY "From $1 `date`" X if $test $? -eq 0 ; then X $echo "Article appended to $AUTHORCOPY" X else X $echo "Cannot append to $AUTHORCOPY" X fi X ;; X esac X exit X ;; X esac Xdone X!NO!SUBS! X$eunicefix Pnews Xchmod 755 Pnews X$spitshell >Pnews.header <<'!NO!SUBS!' Xcase $# in X0) X ng=h X while $test "$ng" = h ; do X $echo "" X $echo $n "Newsgroup(s): $c" X read ng X case $ng in X h) X $cat <<'EOH' X XType the name of one or more newsgroups to which you wish to post an article. XIf you want to post to multiple newsgroups, it is better to do them all at Xonce than to post to each newsgroup individually, which defeats the news Xreading programs' strategies of eliminating duplicates. X XSeparate multiple newsgroup names with commas. XEOH X ;; X esac X done X ;; X*) X ng=$1 X shift X ;; Xesac Xcase $ng in X*\ *) X ng=`$echo "$ng" | $sed 's/[, ] */,/g'` X ;; Xesac Xcase $ng in Xbit.*|pubnet.*|bionet.*|vmsnet.*|comp.*|news.*|sci.*|rec.*|misc.*|soc.*|talk.*|alt.*) X defdist=world X dist=h X ;; Xddn.*) X defdist=inet X dist=h X ;; Xto.*) X defdist='' X dist='' X ;; X*.*) X defdist=`expr "X$ng" : 'X\([a-z0-9]*\)'` X dist=h X ;; X*) X defdist='' X dist='' X ;; Xesac X Xwhile $test "$dist" = h ; do X if $test -f $lib/distributions; then X $echo " " X $echo "Your local distribution prefixes are:" X $cat $lib/distributions X $echo " " X else X $egrep -v '[ ]none$' <<EOM X XYour local distribution prefixes are: X Local organization: $loc X Organization: $org X City: $city X $stpr: $state X Country: $cntry X Continent: $cont X Everywhere: world X XEOM X fi X $echo $n "Distribution ($defdist): $c" X read dist X case $dist in X '') dist=$defdist ;; X esac X case $dist in X h) X $cat <<'EOH' X XThe Distribution line may be used to limit the distribution of an article Xto some subset of the systems that would receive the article based only on Xthe Newsgroups line. For example, if you want to sell your car in talk.auto, Xand you live in New Jersey, you might want to put "nj" on the Distribution Xline to avoid advertising in California, which has enough problems of its own. XThe actual area designators to use depend on where you are, of course. XEOH X ;; X ''|$loc*|$org*|$city*|$state*|$cntry*|$cont*|$defdist) X ;; X world*|comp*|news*|sci*|rec*|misc*|soc*|talk*|alt*) X dist='' X ;; X *) X if $test -f $lib/distributions && \ X $egrep "^$dist[ ]" $lib/distributions >$tmpart && \ X $test -s $tmpart; then X : null X else X $echo "Unrecognized distribution prefix--type h for help, CR to use anyway." X defdist=$dist X dist=h X fi X ;; X esac Xdone X Xfollow="" X Xcase $# in X0) X title=h X while $test "$title" = h ; do X $echo "" X $echo $n "Title/Subject: $c" X read title X case $title in X h) X $cat <<'EOH' X XType the title for your article. Please make it as informative as possible X(within reason) so that people who aren't interested won't have to read the Xarticle to find out they aren't interested. This includes marking movie Xspoilers as (spoiler), and rotated jokes as (rot 13). XEOH X ;; X esac X done X ;; X*) X title="$*" X ;; Xesac X X# now build a file with a header for them to edit X Xset X ${USER-${LOGNAME-`who am i`}} Xshift Xlogname=$1 Xcase $logname in X*!*) logname=`expr "$logname" : '!\(.*\)$'` ;; Xesac Xcase ${NAME-$nametype} in Xbsd) X if $test "$ypmatch" != ""; then X fullname=`$ypmatch $logname passwd 2>/dev/null | $sed "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/"` X fi X if $test "$fullname" = ""; then X fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"` X fi X case $fullname in X *'&'*) : GACK X lname=`$echo $logname | $tr 'a-z' 'A-Z'` X lname=`$echo $lname $logname | $sed 's/^\(.\)[^ ]* ./\1/'` X fullname=`$echo "$fullname" | $sed "s/&/${lname}/"` X ;; X esac X ;; Xusg) X if $test "$ypmatch" != ""; then X fullname=`$ypmatch $logname passwd 2>/dev/null | $sed "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q"` X fi X if $test "$fullname" = ""; then X fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q" -e "}" -e "d"` X fi X ;; X*) X fullname=${NAME-`$cat $dotdir/.fullname`} X ;; Xesac X Xorgname=${ORGANIZATION-$orgname} Xcase $orgname in X/*) orgname=`$cat $orgname` ;; Xesac X X$sed -e '/^Reply-To: $/d' > $tmpart <<EOHeader XNewsgroups: $ng XSubject: $title XReply-To: $REPLYTO XFollowup-To: $follow XDistribution: $dist XOrganization: $orgname XKeywords: X XEOHeader X X!NO!SUBS! Xcase "$isrrn" in Xdefine) sed < Pnews.header -e '/^#NORMAL/d' > Pnews.h.new ;; X*) sed < Pnews.header -e '/^#NORMAL/s/^#NORMAL//' > Pnews.h.new ;; Xesac Xmv Pnews.h.new Pnews.header X$eunicefix Pnews.header END_OF_FILE if test 16703 -ne `wc -c <'Pnews.SH'`; then echo shar: \"'Pnews.SH'\" unpacked with wrong size! fi chmod +x 'Pnews.SH' # end of 'Pnews.SH' fi if test -f 'bits.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'bits.c'\" else echo shar: Extracting \"'bits.c'\" \(17397 characters\) sed "s/^X//" >'bits.c' <<'END_OF_FILE' X/* $Header: bits.c,v 4.3.3.1 90/06/20 22:36:24 davison Trn $ X * X * $Log: bits.c,v $ X * Revision 4.3.3.1 90/06/20 22:36:24 davison X * Initial Trn Release X * X * Revision 4.3.2.3 89/11/28 01:52:02 sob X * Removed some lint. X * X * Revision 4.3.2.2 89/11/27 01:30:04 sob X * Altered NNTP code per ideas suggested by Bela Lubkin X * <filbo@gorn.santa-cruz.ca.us> X * X * Revision 4.3.1.4 86/10/31 15:23:53 lwall X * Separated firstart into two variables so KILL on new articles won't X * accidentally mark articles read. X * X * Revision 4.3.1.3 86/09/09 16:01:43 lwall X * Fixed 'n more articles' bug. X * X * Revision 4.3.1.2 86/07/24 14:40:23 lwall X * Gets host name from path instead of relay-version for news 2.10.3. X * X * Revision 4.3.1.1 85/05/10 11:31:41 lwall X * Branch for patches. X * X * Revision 4.3 85/05/01 11:36:15 lwall X * Baseline for release with 4.3bsd. X * X */ X X#include "EXTERN.h" X#include "common.h" X#include "rcstuff.h" X#include "head.h" X#include "util.h" X#include "final.h" X#include "rn.h" X#include "cheat.h" X#include "ng.h" X#include "artio.h" X#include "intrp.h" X#include "ngdata.h" X#include "rcln.h" X#include "kfile.h" X#ifdef USETHREADS X#include "rthreads.h" X#endif X#include "INTERN.h" X#include "bits.h" X X#ifdef DBM X# ifdef NULL X# undef NULL X# endif X# include <dbm.h> X#endif XMEM_SIZE ctlsize; /* size of bitmap in bytes */ X Xvoid Xbits_init() X{ X#ifdef DELAYMARK X dmname = savestr(filexp(RNDELNAME)); X#else X ; X#endif X} X X/* checkpoint the .newsrc */ X Xvoid Xcheckpoint_rc() X{ X#ifdef DEBUGGING X if (debug & DEB_CHECKPOINTING) { X fputs("(ckpt)",stdout); X fflush(stdout); X } X#endif X if (doing_ng) X restore_ng(); /* do not restore M articles */ X if (rc_changed) X write_rc(); X#ifdef DEBUGGING X if (debug & DEB_CHECKPOINTING) { X fputs("(done)",stdout); X fflush(stdout); X } X#endif X} X X/* reconstruct the .newsrc line in a human readable form */ X Xvoid Xrestore_ng() X{ X register char *s, *mybuf = buf; X register ART_NUM i; X ART_NUM count=0; X int safelen = LBUFLEN - 16; X X strcpy(buf,rcline[ng]); /* start with the newsgroup name */ X s = buf + rcnums[ng] - 1; /* use s for buffer pointer */ X *s++ = rcchar[ng]; /* put the requisite : or !*/ X *s++ = ' '; /* put the not-so-requisite space */ X for (i=1; i<=lastart; i++) { /* for each article in newsgroup */ X if (s-mybuf > safelen) { /* running out of room? */ X safelen *= 2; X if (mybuf == buf) { /* currently static? */ X *s = '\0'; X mybuf = safemalloc((MEM_SIZE)safelen + 16); X strcpy(mybuf,buf); /* so we must copy it */ X s = mybuf + (s-buf); X /* fix the pointer, too */ X } X else { /* just grow in place, if possible */ X char *newbuf; X X newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16); X s = newbuf + (s-mybuf); X mybuf = newbuf; X } X } X if (!was_read(i)) /* still unread? */ X count++; /* then count it */ X else { /* article was read */ X ART_NUM oldi; X X sprintf(s,"%ld",(long)i); /* put out the min of the range */ X s += strlen(s); /* keeping house */ X oldi = i; /* remember this spot */ X do i++; while (i <= lastart && was_read(i)); X /* find 1st unread article or end */ X i--; /* backup to last read article */ X if (i > oldi) { /* range of more than 1? */ X sprintf(s,"-%ld,",(long)i); X /* then it out as a range */ X s += strlen(s); /* and housekeep */ X } X else X *s++ = ','; /* otherwise, just a comma will do */ X } X } X if (*(s-1) == ',') /* is there a final ','? */ X s--; /* take it back */ X *s++ = '\0'; /* and terminate string */ X#ifdef DEBUGGING X if (debug & DEB_NEWSRC_LINE && !panic) { X printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) FLUSH; X printf("%s\n",mybuf) FLUSH; X } X#endif X free(rcline[ng]); /* return old rc line */ X if (mybuf == buf) { X rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1); X /* grab a new rc line */ X strcpy(rcline[ng], buf); /* and load it */ X } X else { X mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1); X /* be nice to the heap */ X rcline[ng] = mybuf; X } X *(rcline[ng] + rcnums[ng] - 1) = '\0'; X if (rcchar[ng] == NEGCHAR) { /* did they unsubscribe? */ X printf(unsubto,ngname) FLUSH; X toread[ng] = TR_UNSUB; /* make line invisible */ X } X else X /*NOSTRICT*/ X toread[ng] = (ART_UNREAD)count; /* remember how many unread there are */ X} X X/* mark an article unread, keeping track of toread[] */ X Xvoid Xonemore(artnum) XART_NUM artnum; X{ X#ifdef DEBUGGING X if (debug && artnum < firstbit) { X printf("onemore: %d < %d\n",artnum,firstbit) FLUSH; X return; X } X#endif X if (ctl_read(artnum)) { X ctl_clear(artnum); X ++toread[ng]; X } X} X X/* mark an article read, keeping track of toread[] */ X Xvoid Xoneless(artnum) XART_NUM artnum; X{ X#ifdef DEBUGGING X if (debug && artnum < firstbit) { X printf("oneless: %d < %d\n",artnum,firstbit) FLUSH; X return; X } X#endif X if (!ctl_read(artnum)) { X ctl_set(artnum); X if (toread[ng] > TR_NONE) X --toread[ng]; X } X} X X/* mark an article as unread, making sure that firstbit is properly handled */ X/* cross-references are left as read in the other newsgroups */ X Xvoid Xunmark_as_read() X{ X check_first(art); X#ifdef USETHREADS X /* Keep selected_count accurate */ X if (ctl_read(art)) { X find_article(art); X if (p_art) { X if (selected_roots[p_art->root]) { X selected_count++; X } X root_article_cnts[p_art->root] = 1; X } else X unthreaded++; X } X scan_all_roots = FALSE; X#endif X onemore(art); X#ifdef MCHASE X if (!parse_maybe(art)) X chase_xrefs(art,FALSE); X#endif X} X X#ifdef USETHREADS X/* mark an article as read in this newsgroup only */ X Xvoid Xset_read(artnum,sel) XART_NUM artnum; Xint sel; X{ X if (!ctl_read(artnum)) { X oneless(artnum); X if( p_art->subject != -1 ) X selected_count -= sel; X } X} X X/* mark an article as unread in this newsgroup only */ X Xvoid Xset_unread(artnum,sel) XART_NUM artnum; Xint sel; X{ X if (artnum >= absfirst) { X check_first(artnum); X if (ctl_read(artnum)) { X onemore(artnum); X selected_count += sel; X root_article_cnts[p_art->root] = 1; X scan_all_roots = FALSE; X } X } X} X#endif X X#ifdef DELAYMARK X/* temporarily mark article as read. When newsgroup is exited, articles */ X/* will be marked as unread. Called via M command */ X Xvoid Xdelay_unmark(artnum) XART_NUM artnum; X{ X if (dmfp == Nullfp) { X dmfp = fopen(dmname,"w+"); X if (dmfp == Nullfp) { X printf(cantcreate,dmname) FLUSH; X sig_catcher(0); X } X } X#ifdef USETHREADS X /* Keep selected_count accurate */ X if (!ctl_read(artnum)) { X find_article(artnum); X if (p_art) { X if (selected_roots[p_art->root]) X selected_count--; X } else X unthreaded--; X } X#endif X oneless(artnum); /* set the correct bit */ X dmcount++; X fprintf(dmfp,"%ld\n",(long)artnum); X} X#endif X X/* mark article as read. If article is cross referenced to other */ X/* newsgroups, mark them read there also. */ X Xvoid Xmark_as_read() X{ X#ifdef USETHREADS X /* Keep selected_count accurate */ X if (!ctl_read(art)) { X find_article(art); X if (p_art) { X if (selected_roots[p_art->root]) X selected_count--; X } else X unthreaded--; X } X#endif X oneless(art); /* set the correct bit */ X checkcount++; /* get more worried about crashes */ X chase_xrefs(art,TRUE); X} X X/* make sure we have bits set correctly down to firstbit */ X Xvoid Xcheck_first(min) XART_NUM min; X{ X register ART_NUM i = firstbit; X X if (min < absfirst) X min = absfirst; X if (min < i) { X for (i--; i>=min; i--) X ctl_set(i); /* mark as read */ X firstart = firstbit = min; X } X} X X/* bring back articles marked with M */ X X#ifdef DELAYMARK Xvoid Xyankback() X{ X if (dmfp) { /* delayed unmarks pending? */ X#ifdef VERBOSE X printf("\nReturning %ld Marked article%s...\n",(long)dmcount, X dmcount == 1 ? nullstr : "s") FLUSH; X#endif X rewind(dmfp); X while (fgets(buf,sizeof buf,dmfp) != Nullch) { X art = (ART_NUM)atol(buf); X unmark_as_read(); X } X fclose(dmfp); X dmfp = Nullfp; X UNLINK(dmname); /* and be tidy */ X } X dmcount = 0; X} X#endif X X/* run down xref list and mark as read or unread */ X Xint Xchase_xrefs(artnum,markread) XART_NUM artnum; Xint markread; X{ X#ifdef ASYNC_PARSE X if (parse_maybe(artnum)) /* make sure we have right header */ X return -1; X#endif X#ifdef DBM X { X datum lhs, rhs; X datum fetch(); X register char *idp; X char *ident_buf; X static FILE * hist_file = Nullfp; X#else X if ( X#ifdef DEBUGGING X debug & DEB_FEED_XREF || X#endif X htype[XREF_LINE].ht_minpos >= 0) { X /* are there article# xrefs? */ X#endif /* DBM */ X char *xref_buf, *curxref; X register char *xartnum; X char *rver_buf = Nullch; X static char *inews_site = Nullch; X register ART_NUM x; X char tmpbuf[128]; X X#ifdef DBM X rver_buf = fetchlines(artnum,NGS_LINE); X /* get Newsgroups */ X if (!index(rver_buf,',')) /* if no comma, no Xref! */ X return 0; X if (hist_file == Nullfp) { /* Init. file accesses */ X#ifdef DEBUGGING X if (debug) X printf ("chase_xref: opening files\n"); X#endif X dbminit(filexp(ARTFILE)); X if ((hist_file = fopen (filexp(ARTFILE), "r")) == Nullfp) X return 0; X } X xref_buf = safemalloc((MEM_SIZE)BUFSIZ); X ident_buf = fetchlines(artnum,MESSID_LINE); X /* get Message-ID */ X#ifdef DEBUGGING X if (debug) X printf ("chase_xref: Message-ID: %s\n", ident_buf); X#endif X idp = ident_buf; X while (*++idp) /* make message-id case insensitive */ X if (isupper(*idp)) X *idp = tolower (*idp); X lhs.dptr = ident_buf; /* look up article by id */ X lhs.dsize = strlen(lhs.dptr) + 1; X rhs = fetch(lhs); /* fetch the record */ X if (rhs.dptr == NULL) /* if null, nothing there */ X goto wild_goose; X fseek (hist_file, *((long *)rhs.dptr), 0); X /* datum returned is position in hist file */ X fgets (xref_buf, BUFSIZ, hist_file); X#ifdef DEBUGGING X if (debug) X printf ("Xref from history: %s\n", xref_buf); X#endif X curxref = cpytill(tmpbuf, xref_buf, '\t') + 1; X curxref = cpytill(tmpbuf, curxref, '\t') + 1; X#ifdef DEBUGGING X if (debug) X printf ("chase_xref: curxref: %s\n", curxref); X#endif X#else /* !DBM */ X#ifdef DEBUGGING X if (htype[XREF_LINE].ht_minpos >= 0) X#endif X xref_buf = fetchlines(artnum,XREF_LINE); X /* get xrefs list */ X#ifdef DEBUGGING X else { X xref_buf = safemalloc((MEM_SIZE)100); X printf("Give Xref: ") FLUSH; X gets(xref_buf); X } X#endif X#ifdef DEBUGGING X if (debug & DEB_XREF_MARKER) X printf("Xref: %s\n",xref_buf) FLUSH; X#endif X curxref = cpytill(tmpbuf,xref_buf,' ') + 1; X X /* Make sure site name on Xref matches what inews thinks site is. X * Check first against last inews_site. If it matches, fine. X * If not, fetch inews_site from current Relay-Version line and X * check again. This is so that if the new administrator decides X * to change the system name as known to inews, rn will still do X * Xrefs correctly--each article need only match itself to be valid. X */ X if (inews_site == Nullch || strNE(tmpbuf,inews_site)) { X#ifndef NORELAY X char *t; X#endif X if (inews_site != Nullch) X free(inews_site); X#ifndef NORELAY X rver_buf = fetchlines(artnum,RVER_LINE); X if ((t = instr(rver_buf,"; site ")) == Nullch) X#else /* NORELAY */ X /* In version 2.10.3 of news or afterwards, the Relay-Version X * and Posting-Version header lines have been removed. For X * the code below to work as intended, I have modified it to X * extract the first component of the Path header line. This X * should give the same effect as did the old code with respect X * to the use of the Relay-Version site name. X */ X rver_buf = fetchlines(artnum,PATH_LINE); X if (instr(rver_buf,"!") == Nullch) X#endif /* NORELAY */ X inews_site = savestr(nullstr); X else { X char new_site[128]; X X#ifndef NORELAY X cpytill(new_site,t + 7,'.'); X#else /* NORELAY */ X cpytill(new_site,rver_buf,'!'); X#endif /* NORELAY */ X inews_site = savestr(new_site); X } X if (strNE(tmpbuf,inews_site)) { X#ifdef DEBUGGING X if (debug) X printf("Xref not from %s--ignoring\n",inews_site) FLUSH; X#endif X goto wild_goose; X } X } X#endif /* DBM */ X while (*curxref) { X /* for each newsgroup */ X curxref = cpytill(tmpbuf,curxref,' '); X#ifdef DBM X xartnum = index(tmpbuf,'/'); X#else X xartnum = index(tmpbuf,':'); X#endif /* DBM */ X if (!xartnum) /* probably an old-style Xref */ X break; X *xartnum++ = '\0'; X if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */ X x = atol(xartnum); X if (x) X if (markread) { X if (addartnum(x,tmpbuf)) X goto wild_goose; X } X#ifdef MCHASE X else X subartnum(x,tmpbuf); X#endif X } X while (*curxref && isspace(*curxref)) X curxref++; X } X wild_goose: X free(xref_buf); X#ifdef DBM X free(ident_buf); X#endif /* DBM */ X if (rver_buf != Nullch) X free(rver_buf); X } X return 0; X} X Xint Xinitctl() X{ X char *mybuf = buf; /* place to decode rc line */ X register char *s, *c, *h; X register long i; X register ART_NUM unread; X X#ifdef DELAYMARK X dmcount = 0; X#endif X if ((lastart = getngsize(ng)) < 0) /* this cannot happen (laugh here) */ X return -1; X X absfirst = getabsfirst(ng,lastart); /* remember first existing article */ X if (!absfirst) /* no articles at all? */ X absfirst = 1; /* pretend there is one */ X#ifndef lint X ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20); X#endif /* lint */ X ctlarea = safemalloc(ctlsize); /* allocate control area */ X X /* now modify ctlarea to reflect what has already been read */ X X for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ; X /* find numbers in rc line */ X i = strlen(s); X#ifndef lint X if (i >= LBUFLEN-2) /* bigger than buf? */ X mybuf = safemalloc((MEM_SIZE)(i+2)); X#endif /* lint */ X strcpy(mybuf,s); /* make scratch copy of line */ X mybuf[i++] = ','; /* put extra comma on the end */ X mybuf[i] = '\0'; X s = mybuf; /* initialize the for loop below */ X if (strnEQ(s,"1-",2)) { /* can we save some time here? */ X firstbit = atol(s+2)+1; /* ignore first range thusly */ X s=index(s,',') + 1; X } X else X firstbit = 1; /* all the bits are valid for now */ X if (absfirst > firstbit) { /* do we know already? */ X firstbit = absfirst; /* no point calling getngmin again */ X } X else if (artopen(firstbit) == Nullfp) { X /* first unread article missing? */ X i = getngmin(".",firstbit); /* see if expire has been busy */ X if (i) { /* avoid a bunch of extra opens */ X firstbit = i; X } X } X firstart = firstbit; /* firstart > firstbit in KILL */ X#ifdef PENDING X# ifdef CACHESUBJ X subj_to_get = firstbit; X# endif X#endif X unread = lastart - firstbit + 1; /* assume this range unread */ X for (i=OFFSET(firstbit)/BITSPERBYTE; i<ctlsize; i++) X ctlarea[i] = 0; /* assume unread */ X#ifdef DEBUGGING X if (debug & DEB_CTLAREA_BITMAP) { X printf("\n%s\n",mybuf) FLUSH; X for (i=1; i <= lastart; i++) X if (! was_read(i)) X printf("%ld ",(long)i) FLUSH; X } X#endif X for ( ; (c = index(s,',')) != Nullch; s = ++c) { X /* for each range */ X ART_NUM min, max; X X *c = '\0'; /* do not let index see past comma */ X if ((h = index(s,'-')) != Nullch) { /* is there a -? */ X min = atol(s); X max = atol(h+1); X if (min < firstbit) /* make sure range is in range */ X min = firstbit; X if (max > lastart) X max = lastart; X if (min <= max) /* non-null range? */ X unread -= max - min + 1;/* adjust unread count */ X for (i=min; i<=max; i++) /* for all articles in range */ X ctl_set(i); /* mark them read */ X } X else if ((i = atol(s)) >= firstbit && i <= lastart) { X /* is single number reasonable? */ X ctl_set(i); /* mark it read */ X unread--; /* decrement articles to read */ X } X#ifdef DEBUGGING X if (debug & DEB_CTLAREA_BITMAP) { X printf("\n%s\n",s) FLUSH; X for (i=1; i <= lastart; i++) X if (! was_read(i)) X printf("%ld ",(long)i) FLUSH; X } X#endif X } X#ifdef DEBUGGING X if (debug & DEB_CTLAREA_BITMAP) { X fputs("\n(hit CR)",stdout) FLUSH; X gets(cmd_buf); X } X#endif X if (mybuf != buf) X free(mybuf); X toread[ng] = unread; X return 0; X} X Xvoid Xgrow_ctl(newlast) XART_NUM newlast; X{ X ART_NUM tmpfirst; X MEM_SIZE newsize; X register ART_NUM i; X X forcegrow = FALSE; X if (newlast > lastart) { X ART_NUM tmpart = art; X#ifndef lint X newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2); X#else X newsize = Null(MEM_SIZE); X#endif /* lint */ X if (newsize > ctlsize) { X newsize += 20; X ctlarea = saferealloc(ctlarea,newsize); X ctlsize = newsize; X } X toread[ng] += (ART_UNREAD)(newlast-lastart); X for (i=lastart+1; i<=newlast; i++) X ctl_clear(i); /* these articles are unread */ X#ifdef CACHESUBJ X if (subj_list != Null(char**)) { X#ifndef lint X subj_list = (char**)saferealloc((char*)subj_list, X (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) ); X#endif /* lint */ X for (i=lastart+1; i<=newlast; i++) X subj_list[OFFSET(i)] = Nullch; X } X#endif X tmpfirst = lastart+1; X lastart = newlast; X#ifdef KILLFILES X#ifdef VERBOSE X IF(verbose) X sprintf(buf, X "%ld more article%s arrived--looking for more to kill...\n\n", X (long)(lastart - tmpfirst + 1), X (lastart > tmpfirst ? "s have" : " has" ) ); X ELSE /* my, my, how clever we are */ X#endif X#ifdef TERSE X strcpy(buf, "More news--killing...\n\n"); X#endif X kill_unwanted(tmpfirst,buf,TRUE); X#endif X art = tmpart; X } X} X END_OF_FILE if test 17397 -ne `wc -c <'bits.c'`; then echo shar: \"'bits.c'\" unpacked with wrong size! fi # end of 'bits.c' fi if test -f 'cheat.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'cheat.c'\" else echo shar: Extracting \"'cheat.c'\" \(3117 characters\) sed "s/^X//" >'cheat.c' <<'END_OF_FILE' X/* $Header: cheat.c,v 4.3.3.1 90/07/21 20:14:01 davison Trn $ X * X * $Log: cheat.c,v $ X * Revision 4.3.3.1 90/07/21 20:14:01 davison X * Initial Trn Release X * X * Revision 4.3.2.2 89/11/27 01:30:18 sob X * Altered NNTP code per ideas suggested by Bela Lubkin X * <filbo@gorn.santa-cruz.ca.us> X * X * Revision 4.3.2.1 89/11/26 22:54:21 sob X * Added RRN support X * X * Revision 4.3 85/05/01 11:36:46 lwall X * Baseline for release with 4.3bsd. X * X */ X X#include "EXTERN.h" X#include "common.h" X#include "intrp.h" X#include "search.h" X#include "ng.h" X#include "bits.h" X#include "artio.h" X#include "term.h" X#include "artsrch.h" X#include "head.h" X#include "INTERN.h" X#include "cheat.h" X X/* see what we can do while they are reading */ X X#ifdef PENDING X# ifdef ARTSEARCH X COMPEX srchcompex; /* compiled regex for searchahead */ X# endif X#endif X Xvoid Xcheat_init() X{ X ; X} X X#ifdef PENDING Xvoid Xlook_ahead() X{ X#ifdef ARTSEARCH X register char *h, *s; X X#ifdef DEBUGGING X if (debug && srchahead) { X printf("(%ld)",(long)srchahead); X fflush(stdout); X } X#endif X if (srchahead && srchahead < art) { /* in ^N mode? */ X char *pattern; X X pattern = buf+1; X strcpy(pattern,": *"); X h = pattern + strlen(pattern); X interp(h,(sizeof buf) - (h-buf),"%s"); X h[24] = '\0'; /* compensate for notesfiles */ X while (*h) { X if (index("\\[.^*$'\"",*h) != Nullch) X *h++ = '.'; X else X h++; X } X#ifdef DEBUGGING X if (debug & DEB_SEARCH_AHEAD) { X fputs("(hit CR)",stdout); X fflush(stdout); X gets(buf+128); X printf("\npattern = %s\n",pattern); X } X#endif X if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != Nullch) { X /* compile regular expression */ X printf("\n%s\n",s); X srchahead = 0; X } X if (srchahead) { X srchahead = art; X for (;;) { X srchahead++; /* go forward one article */ X if (srchahead > lastart) { /* out of articles? */ X#ifdef DEBUGGING X if (debug) X fputs("(not found)",stdout); X#endif X break; X } X if (!was_read(srchahead) && X wanted(&srchcompex,srchahead,0)) { X /* does the shoe fit? */ X#ifdef DEBUGGING X if (debug) X printf("(%ld)",(long)srchahead); X#endif X#ifdef SERVER X nntpopen(srchahead,GET_HEADER); X#else X artopen(srchahead); X#endif X break; X } X if (input_pending()) X break; X } X fflush(stdout); X } X } X else X#endif X { X if (art+1 <= lastart)/* how about a pre-fetch? */ X#ifdef SERVER X nntpopen(art+1,GET_HEADER); /* look for the next article */ X#else X artopen(art+1); /* look for the next article */ X#endif X } X} X#endif X X/* see what else we can do while they are reading */ X Xvoid Xcollect_subjects() X{ X#ifdef PENDING X# ifdef CACHESUBJ X ART_NUM oldart = openart; X ART_POS oldartpos; X X if (!in_ng || !srchahead) X return; X if (oldart) /* remember where we were in art */ X oldartpos = ftell(artfp); X if (srchahead >= subj_to_get) X subj_to_get = srchahead+1; X while (!input_pending() && subj_to_get <= lastart) X fetchsubj(subj_to_get++,FALSE,FALSE); X if (oldart) { X artopen(oldart); X fseek(artfp,oldartpos,0); /* do not screw the pager */ X } X# endif X#endif X} X END_OF_FILE if test 3117 -ne `wc -c <'cheat.c'`; then echo shar: \"'cheat.c'\" unpacked with wrong size! fi # end of 'cheat.c' fi if test -f 'rt-rn.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'rt-rn.c'\" else echo shar: Extracting \"'rt-rn.c'\" \(21485 characters\) sed "s/^X//" >'rt-rn.c' <<'END_OF_FILE' X/* $Header: rt-rn.c,v 4.3.3.1 90/07/28 18:07:55 davison Trn $ X** X** $Log: rt-rn.c,v $ X** Revision 4.3.3.1 90/07/28 18:07:55 davison X** Initial Trn Release X** X*/ X X#include "EXTERN.h" X#include "common.h" X#include "term.h" X#include "final.h" X#include "util.h" X#include "bits.h" X#include "artio.h" X#include "ng.h" X#include "ngdata.h" X#include "search.h" X#include "artstate.h" X#include "backpage.h" X#include "rthreads.h" X X#ifdef USETHREADS X Xstatic void find_depth(), cache_tree(), display_tree(); Xstatic char letter(); X X/* Find the article structure information based on article number. X*/ Xvoid Xfind_article( artnum ) XART_NUM artnum; X{ X register PACKED_ARTICLE *article; X register int i; X X if( !p_articles ) { X p_art = Nullart; X return; X } X X if( !p_art ) { X p_art = p_articles; X } X /* Start looking for the article num from our last known spot in the array. X ** That way, if we already know where we are, we run into ourselves right X ** away. X */ X for( article=p_art, i=p_art-p_articles; i < total.article; article++,i++ ) { X if( article->num == artnum ) { X p_art = article; X return; X } X } X /* Didn't find it, so search the ones before our current position. X */ X for( article = p_articles; article != p_art; article++ ) { X if( article->num == artnum ) { X p_art = article; X return; X } X } X p_art = Nullart; X} X Xstatic char tree_indent[] = { X ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0, X ' ', ' ', ' ', ' ', 0, ' ', ' ', ' ', ' ', 0 X}; X Xchar letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?"; X Xstatic PACKED_ARTICLE *tree_article; X Xstatic int max_depth, max_line = -1; Xstatic int first_depth, first_line; Xstatic int my_depth, my_line; Xstatic bool node_on_line; Xstatic int node_line_cnt; X Xstatic int line_num; Xstatic int header_indent; X Xstatic char *tree_lines[11]; Xstatic char tree_buff[128], *str; X X/* Prepare tree display for inclusion in the article header. X*/ Xvoid Xinit_tree() X{ X register PACKED_ARTICLE *article; X X#if 000 /* don't do this, since read-status may change */ X if( curr_p_art == tree_article ) { X return; X } X#endif X while( max_line >= 0 ) { /* free any previous tree data */ X free( tree_lines[max_line--] ); X } X tree_article = curr_p_art; X X if( !curr_p_art ) { X return; X } X article = p_articles + p_roots[curr_p_art->root].articles; X X max_depth = max_line = my_depth = my_line = node_line_cnt = 0; X find_depth( article, 0 ); X X if( max_depth <= 5 ) { X first_depth = 0; X } else { X if( my_depth+2 > max_depth ) { X first_depth = max_depth - 5; X } else if( (first_depth = my_depth - 3) < 0 ) { X first_depth = 0; X } X max_depth = first_depth + 5; X } X if( --max_line < max_tree_lines ) { X first_line = 0; X } else { X if( my_line + max_tree_lines/2 > max_line ) { X first_line = max_line - (max_tree_lines-1); X } else if( (first_line = my_line - (max_tree_lines-1)/2) < 0 ) { X first_line = 0; X } X max_line = first_line + max_tree_lines-1; X } X X str = tree_buff; /* initialize first line's data */ X *str++ = ' '; X node_on_line = FALSE; X line_num = 0; X /* cache our portion of the tree */ X cache_tree( article, 0, tree_indent ); X X max_depth = (max_depth-first_depth) * 5; /* turn depth into char width */ X max_line -= first_line; /* turn max_line into count */ X /* shorten tree if lower lines aren't visible */ X if( node_line_cnt < max_line ) { X max_line = node_line_cnt + 1; X } X} X X/* A recursive routine to find the maximum tree extents and where we are. X*/ Xstatic void Xfind_depth( article, depth ) XPACKED_ARTICLE *article; X{ X if( depth > max_depth ) { X max_depth = depth; X } X for( ;; ) { X if( article == tree_article ) { X my_depth = depth; X my_line = max_line; X } X if( article->child_cnt ) { X find_depth( article+1, depth+1 ); X } else { X max_line++; X } X if( !article->siblings ) { X break; X } X article += article->siblings; X } X} X X/* Place the tree display in a maximum of 11 lines x 6 nodes. X*/ Xstatic void Xcache_tree( article, depth, cp ) XPACKED_ARTICLE *article; Xint depth; Xchar *cp; X{ X int depth_mode; X X cp[1] = ' '; X if( depth >= first_depth && depth <= max_depth ) { X cp += 5; X depth_mode = 1; X } else if( depth+1 == first_depth ) { X depth_mode = 2; X } else { X cp = tree_indent; X depth_mode = 0; X } X for( ;; ) { X switch( depth_mode ) { X case 1: { X char ch; X X *str++ = (article->flags & ROOT_ARTICLE)? ' ' : '-'; X if( article == tree_article ) { X *str++ = '*'; X } X if( was_read( article->num ) ) { X *str++ = '('; X ch = ')'; X } else { X *str++ = '['; X ch = ']'; X } X if( article == recent_p_art && article != tree_article ) { X *str++ = '@'; X } X *str++ = letter( article ); X *str++ = ch; X if( article->child_cnt ) { X *str++ = (article->child_cnt == 1)? '-' : '+'; X } X if( article->siblings ) { X *cp = '|'; X } else { X *cp = ' '; X } X node_on_line = TRUE; X break; X } X case 2: X *tree_buff = (!article->child_cnt)? ' ' : X (article->child_cnt == 1)? '-' : '+'; X break; X default: X break; X } X if( article->child_cnt ) { X cache_tree( article+1, depth+1, cp ); X cp[1] = '\0'; X } else { X if( !node_on_line && first_line == line_num ) { X first_line++; X } X if( line_num >= first_line ) { X if( str[-1] == ' ' ) { X str--; X } X *str = '\0'; X tree_lines[line_num-first_line] X = safemalloc( str-tree_buff + 1 ); X strcpy( tree_lines[line_num - first_line], tree_buff ); X if( node_on_line ) { X node_line_cnt = line_num - first_line; X } X } X line_num++; X node_on_line = FALSE; X } X if( !article->siblings || line_num > max_line ) { X break; X } X article += article->siblings; X if( !article->siblings ) { X *cp = '\\'; X } X if( !first_depth ) { X tree_indent[5] = ' '; X } X strcpy( tree_buff, tree_indent+5 ); X str = tree_buff + strlen( tree_buff ); X } X} X X/* Output a header line with possible tree display on the right hand side. X** Does automatic wrapping of lines that are too long. X*/ Xint Xtree_puts( orig_line, header_line, use_underline ) Xchar *orig_line; XART_LINE header_line; Xint use_underline; X{ X char *buf; X register char *line, *cp, *end; X int pad_cnt, wrap_at; X ART_LINE start_line = header_line; X int i; X char ch; X X /* Make a modifiable copy of the line */ X buf = safemalloc( strlen( orig_line ) + 2 ); /* yes, I mean "2" */ X strcpy( buf, orig_line ); X line = buf; X X /* Change any embedded control characters to spaces */ X for( end = line; *end && *end != '\n'; end++ ) { X if( (unsigned char)*end < ' ' ) { X *end = ' '; X } X } X *end = '\0'; X X if( !*line ) { X strcpy( line, " " ); X end = line+1; X } X X /* If this is the first subject line, output it with a preceeding [1] */ X if( use_underline && curr_p_art && (unsigned char)*line > ' ' ) { X#ifdef NOFIREWORKS X no_sofire(); X#endif X standout(); X putchar( '[' ); X putchar( letter( curr_p_art ) ); X putchar( ']' ); X un_standout(); X putchar( ' ' ); X header_indent = 4; X line += 9; X i = 0; X } else { X if( *line != ' ' ) { X /* A "normal" header line -- output keyword and set header_indent X ** _except_ for the first line, which is a non-standard header. X */ X if( !header_line || !(cp = index( line, ':' )) || *++cp != ' ' ) { X header_indent = 0; X } else { X *cp = '\0'; X fputs( line, stdout ); X putchar( ' ' ); X header_indent = ++cp - line; X line = cp; X } X i = 0; X } else { X /* Skip whitespace of continuation lines and prepare to indent */ X while( *++line == ' ' ) { X ; X } X i = header_indent; X } X } X for( ; *line; i = header_indent ) { X#ifdef CLEAREOL X maybe_eol(); X#endif X if( i ) { X putchar( '+' ); X while( --i ) { X putchar( ' ' ); X } X } X /* If no (more) tree lines, wrap at COLS-1 */ X if( max_line < 0 || header_line > max_line+1 ) { X wrap_at = COLS-1; X } else { X wrap_at = COLS - max_depth - 5 - 3; X } X /* Figure padding between header and tree output, wrapping long lines */ X pad_cnt = wrap_at - (end - line + header_indent); X if( pad_cnt <= 0 ) { X cp = line + wrap_at - header_indent - 1; X pad_cnt = 1; X while( cp > line && *cp != ' ' ) { X if( *--cp == ',' || *cp == '.' || *cp == '-' || *cp == '!' ) { X cp++; X break; X } X pad_cnt++; X } X if( cp == line ) { X cp += wrap_at - header_indent; X pad_cnt = 0; X } X ch = *cp; X *cp = '\0'; X /* keep rn's backpager happy */ X vwtary( artline, vrdary( artline - 1 ) ); X artline++; X } else { X cp = end; X ch = '\0'; X } X if( use_underline ) { X underprint( line ); X } else { X fputs( line, stdout ); X } X *cp = ch; X /* Skip whitespace in wrapped line */ X while( *cp == ' ' ) { X cp++; X } X line = cp; X /* Check if we've got any tree lines to output */ X if( wrap_at != COLS-1 && header_line <= max_line ) { X char *cp1, *cp2; X X do { X putchar( ' ' ); X } while( pad_cnt-- ); X /* Check string for the '*' flagging our current node X ** and the '@' flagging our prior node. X */ X cp = tree_lines[header_line]; X cp1 = index( cp, '*' ); X cp2 = index( cp, '@' ); X if( cp1 != Nullch ) { X *cp1 = '\0'; X } X if( cp2 != Nullch ) { X *cp2 = '\0'; X } X fputs( cp, stdout ); X /* Handle standout output for '*' and '@' marked nodes, then X ** continue with the rest of the line. X */ X while( cp1 || cp2 ) { X standout(); X if( cp1 && (!cp2 || cp1 < cp2) ) { X cp = cp1; X cp1 = Nullch; X *cp++ = '*'; X putchar( *cp++ ); X putchar( *cp++ ); X } else { X cp = cp2; X cp2 = Nullch; X *cp++ = '@'; X } X putchar( *cp++ ); X un_standout(); X if( *cp ) { X fputs( cp, stdout ); X } X }/* while */ X }/* if */ X putchar( '\n' ) FLUSH; X header_line++; X }/* for remainder of line */ X X /* free allocated copy of line */ X free( buf ); X X /* return number of lines displayed */ X return header_line - start_line; X} X X/* Output any parts of the tree that are left to display. Called at the X** end of each header. X*/ Xint Xfinish_tree( last_line ) XART_LINE last_line; X{ X ART_LINE start_line = last_line; X X while( last_line <= max_line ) { X artline++; X last_line += tree_puts( "+", last_line, 0 ); X vwtary( artline, artpos ); /* keep rn's backpager happy */ X } X return last_line - start_line; X} X X/* Output the entire article tree for the user. X*/ Xvoid Xentire_tree() X{ X int j, root; X X if( check_page_line() ) { X return; X } X if( !p_art ) { X#ifdef VERBOSE X IF( verbose ) X fputs( "\nNo article tree to display.\n", stdout ); X ELSE X#endif X#ifdef TERSE X fputs( "\nNo tree.\n", stdout ); X#endif X } else { X root = p_art->root; X#ifdef NOFIREWORKS X no_sofire(); X#endif X standout(); X printf( "T%ld:\n", (long)p_roots[root].root_num ); X un_standout(); X if( check_page_line() ) { X return; X } X putchar( '\n' ); X for( j = 0; j < p_roots[root].subject_cnt; j++ ) { X sprintf( buf, "[%c] %s\n", letters[j > 9+26+26 ? 9+26+26 : j], X subject_ptrs[root_subjects[root]+j] ); X if( check_page_line() ) { X return; X } X fputs( buf, stdout ); X } X if( check_page_line() ) { X return; X } X putchar( '\n' ); X if( check_page_line() ) { X return; X } X putchar( ' ' ); X buf[3] = '\0'; X display_tree( p_articles+p_roots[p_art->root].articles, tree_indent ); X X if( check_page_line() ) { X return; X } X putchar( '\n' ); X } X} X X/* A recursive routine to output the entire article tree. X*/ Xstatic void Xdisplay_tree( article, cp ) XPACKED_ARTICLE *article; Xchar *cp; X{ X if( cp - tree_indent > COLS || page_line < 0 ) { X return; X } X cp[1] = ' '; X cp += 5; X for( ;; ) { X putchar( (article->flags & ROOT_ARTICLE)? ' ' : '-' ); X if( was_read( article->num ) ) { X buf[0] = '('; X buf[2] = ')'; X } else { X buf[0] = '['; X buf[2] = ']'; X } X buf[1] = letter( article ); X if( article == curr_p_art ) { X standout(); X fputs( buf, stdout ); X un_standout(); X } else if( article == recent_p_art ) { X putchar( buf[0] ); X standout(); X putchar( buf[1] ); X un_standout(); X putchar( buf[2] ); X } else { X fputs( buf, stdout ); X } X X if( article->siblings ) { X *cp = '|'; X } else { X *cp = ' '; X } X if( article->child_cnt ) { X putchar( (article->child_cnt == 1)? '-' : '+' ); X display_tree( article+1, cp ); X cp[1] = '\0'; X } else { X putchar( '\n' ) FLUSH; X } X if( !article->siblings ) { X break; X } X article += article->siblings; X if( !article->siblings ) { X *cp = '\\'; X } X tree_indent[5] = ' '; X if( check_page_line() ) { X return; X } X fputs( tree_indent+5, stdout ); X } X} X Xint Xcheck_page_line() X{ X if( page_line < 0 ) { X return -1; X } X if( page_line >= LINES || int_count ) { X register int cmd = -1; X if( int_count || (cmd = get_anything()) ) { X page_line = -1; /* disable further printing */ X if( cmd > 0 ) { X pushchar( cmd ); X } X return cmd; X } X } X page_line++; X return 0; X} X X/* Calculate the subject letter representation. "Place-holder" nodes X** are marked with a ' ', others get a letter in the sequence: X** ' ', '1'-'9', 'A'-'Z', 'a'-'z', '?' X*/ Xstatic char Xletter( article ) XPACKED_ARTICLE *article; X{ X register int subj = article->subject; X X if( subj < 0 ) { X return ' '; X } X subj -= root_subjects[article->root]; X if( subj < 9+26+26 ) { X return letters[subj]; X } X return '?'; X} X X/* Find the first unread article in the (possibly selected) root order. X*/ Xvoid Xfirst_art() X{ X register int r; X X if( !ThreadedGroup ) { X art = firstart; X return; X } X p_art = Nullart; X art = lastart+1; X follow_thread( 'n' ); X} X X/* Perform a command over all or a section of the article tree. Most of X** the option letters match commands entered from article mode: X** n - find the next unread article after current article. X** ^N - find the next unread article with the same subject. X** N - goto the next article in the thread. X** J - junk the entire thread. X** k - junk all articles with this same subject. X** K - kill all this article's descendants (we know that the caller X** killed the current article on the way here). X** u - mark entire thread as "unread". X** U - mark this article and its descendants as "unread". X** f - follow the thread (like 'n'), but don't attempt to find a new thread X** if we run off the end. X*/ Xvoid Xfollow_thread( cmd ) Xchar cmd; X{ X int curr_subj = -1, sel; X PACKED_ARTICLE *root_limit, *p_art_old = Nullart; X bool subthread_flag; X X reread = FALSE; X X if( !p_art ) { X if( ThreadedGroup && art > lastart ) { X p_art = root_limit = p_articles; X goto follow_root; X } X art++; X return; X } X if( cmd == 'k' || cmd == Ctl('n') ) { X if( (curr_subj = p_art->subject) == -1) { X return; X } X p_art_old = p_art; X } X sel = (selected_roots[p_art->root] & 1); X if( cmd == 'U' || cmd == 'K' ) { X subthread_flag = TRUE; X p_art_old = p_art; X } else { X subthread_flag = FALSE; X } X /* The current article is already marked as read for 'K' */ X if( cmd == 'k' || cmd == 'J' || cmd == 'u' ) { X p_art = p_articles + p_roots[p_art->root].articles; X art = p_art->num; X if( cmd == 'u' ) { X p_art_old = p_art; X cmd = 'U'; X } else { X if( !was_read( art ) X && (curr_subj < 0 || curr_subj == p_art->subject) ) { X set_read( art, sel ); X } X cmd = 'K'; X } X } X if( cmd == 'U' ) { X if( p_art->subject != -1 ) { X set_unread( art, sel ); X } X root_article_cnts[p_art->root] = 1; X scan_all_roots = FALSE; X } X follow_again: X sel = (selected_roots[p_art->root] & 1); X root_limit = upper_limit( p_art, subthread_flag ); X for( ;; ) { X if( ++p_art == root_limit ) { X break; X } X if( !(art = p_art->num) ) { X continue; X } X if( cmd == 'K' || p_art->subject == -1 ) { X if( !was_read( art ) X && (curr_subj < 0 || curr_subj == p_art->subject) ) { X set_read( art, sel ); X } X } else if( cmd == 'U' ) { X set_unread( art, sel ); X } else if( !was_read( art ) X && (curr_subj < 0 || curr_subj == p_art->subject) ) { X return; X } else if( cmd == 'N' ) { X reread = TRUE; X return; X } X }/* for */ X if( p_art_old ) { X p_art = p_art_old; X if( cmd == 'U' && p_art->subject != -1 ) { X art = p_art->num; X return; X } X p_art_old = Nullart; X cmd = 'n'; X curr_subj = -1; X subthread_flag = FALSE; X goto follow_again; X } X if( cmd == 'f' ) { X p_art = Nullart; X art = lastart+1; X return; X } X follow_root: X if( root_limit != p_articles + total.article ) { X register int r; X X for( r = p_art->root; r < total.root; r++ ) { X if( !selected_root_cnt || selected_roots[r] ) { X p_art = p_articles + p_roots[r].articles; X art = p_art->num; X if( p_art->subject == -1 || (cmd != 'N' && was_read( art )) ) { X if( cmd != 'N' ) { X cmd = 'n'; X } X curr_subj = -1; X subthread_flag = FALSE; X goto follow_again; X } X return; X } X } X } X if( !count_roots( FALSE ) && unthreaded ) { X /* No threaded articles left -- blow everything else away */ X for( art = firstart; art <= lastart; art++ ) { X oneless( art ); X } X unthreaded = 0; X } X p_art = Nullart; X art = lastart+1; X} X X/* Go backward in the article tree. Options match commands in article mode: X** p - previous unread article. X** ^P - previous unread article with same subject. X** P - previous article. X*/ Xvoid Xbacktrack_thread( cmd ) Xchar cmd; X{ X int curr_subj = -1, sel; X PACKED_ARTICLE *root_limit, *p_art_old = Nullart; X X if( art > lastart ) { X p_art = p_articles + total.article - 1; X root_limit = Nullart; X goto backtrack_root; X } X if( !p_art ) { X art--; X return; X } X if( cmd == Ctl('p') ) { X if( (curr_subj = p_art->subject) == -1) { X return; X } X p_art_old = p_art; X } X backtrack_again: X sel = (selected_roots[p_art->root] & 1); X root_limit = p_articles + p_roots[p_art->root].articles; X for( ;; ) { X if( p_art-- == root_limit ) { X break; X } X if( !(art = p_art->num) ) { X continue; X } X if( p_art->subject == -1 ) { X set_read( art, sel ); X } else if( !was_read( art ) X && (curr_subj < 0 || curr_subj == p_art->subject) ) { X return; X } else if( cmd == 'P' ) { X reread = TRUE; X return; X } X }/* for */ X if( p_art_old ) { X p_art = p_art_old; X p_art_old = Nullart; X curr_subj = -1; X goto backtrack_again; X } X backtrack_root: X if( root_limit != p_articles ) { X register int r; X X for( r = p_art->root; r >= 0; r-- ) { X if( !selected_root_cnt || selected_roots[r] ) { X art = p_art->num; X if( cmd != 'P' && was_read( art ) ) { X goto backtrack_again; X } X return; X } X p_art = p_articles + p_roots[r].articles - 1; X } X } X p_art = Nullart; X art = absfirst-1; X} X X/* Find the next root (first if p_art == NULL). If roots are selected, X** only choose from selected roots. X*/ Xvoid Xnext_root() X{ X register int r; X X reread = FALSE; X X if( p_art ) { X r = p_art->root+1; X } else { X r = 0; X } X for( ; r < total.root; r++ ) { X if( !selected_root_cnt || selected_roots[r] ) { X try_again: X p_art = p_articles + p_roots[r].articles; X art = p_art->num; X if( p_art->subject == -1 || (!reread && was_read( art )) ) { X follow_thread( reread ? 'N' : 'f' ); X if( art == lastart+1 ) { X if( scan_all_roots || selected_root_cnt X || root_article_cnts[r] ) { X reread = TRUE; X goto try_again; X } X continue; X } X } X return; X } X } X p_art = Nullart; X art = lastart+1; X forcelast = TRUE; X} X X/* Find previous root (or last if p_art == NULL). If roots are selected, X** only choose from selected roots. X*/ Xvoid Xprev_root() X{ X register int r; X X reread = FALSE; X X if( p_art ) { X r = p_art->root - 1; X } else { X r = total.root - 1; X } X for( ; r >= 0; r-- ) { X if( !selected_root_cnt || selected_roots[r] ) { X try_again: X p_art = p_articles + p_roots[r].articles; X art = p_art->num; X if( p_art->subject == -1 || (!reread && was_read( art )) ) { X follow_thread( reread ? 'N' : 'f' ); X if( art == lastart+1 ) { X if( scan_all_roots || selected_root_cnt X || root_article_cnts[r] ) { X reread = TRUE; X goto try_again; X } X continue; X } X } X return; X } X } X p_art = Nullart; X art = lastart+1; X forcelast = TRUE; X} X X/* Return a pointer value that we will equal when we've reached the end of X** the current (sub-)thread. X*/ XPACKED_ARTICLE * Xupper_limit( artp, subthread_flag ) XPACKED_ARTICLE *artp; Xbool subthread_flag; X{ X if( subthread_flag ) { X for( ;; ) { X if( artp->siblings ) { X return artp + artp->siblings; X } X if( !artp->parent ) { X break; X } X artp += artp->parent; X } X } X return p_articles + (artp->root == total.root-1 ? X total.article : p_roots[artp->root+1].articles); X} X X#endif /* USETHREADS */ END_OF_FILE if test 21485 -ne `wc -c <'rt-rn.c'`; then echo shar: \"'rt-rn.c'\" unpacked with wrong size! fi # end of 'rt-rn.c' fi echo shar: End of archive 8 \(of 14\). cp /dev/null ark8isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 14 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.