v23i067: Complete reposting of TRN at patchlevel 1, Part08/14

Rich Salz rsalz at bbn.com
Sat Jan 5 08:05:50 AEST 1991


Submitted-by: Wayne Davison <0004475895 at 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 at uunet.uu.net if you want that tool.
# Contents:  Pnews.SH bits.c cheat.c rt-rn.c
# Wrapped by rsalz at 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 at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.



More information about the Comp.sources.unix mailing list