Newsgroups: comp.sources.unix
From: davison@borland.com (Wayne Davison)
Subject: v27i100: trn-3.3 - threaded newsreader based on RN, V3.3, Part07/12
References: <1.754431075.7231@gw.home.vix.com>
Sender: unix-sources-moderator@gw.home.vix.com
Approved: vixie@gw.home.vix.com

Submitted-By: davison@borland.com (Wayne Davison)
Posting-Number: Volume 27, Issue 100
Archive-Name: trn-3.3/part07

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 7 (of 12)."
# Contents:  Pnews.SH nntp/nntp.patch respond.c rt-mt.c
# Wrapped by vixie@gw.home.vix.com on Sun Nov 21 01:14:06 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Pnews.SH' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Pnews.SH'\"
else
echo shar: Extracting \"'Pnews.SH'\" \(18848 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# $Id: Pnews.SH,v 3.0 1992/02/23 21:25:39 davison Trn $
X# 
X# This software is Copyright 1991 by Stan Barber. 
X#
X# Permission is hereby granted to copy, reproduce, redistribute or otherwise
X# use this software as long as: there is no monetary profit gained
X# specifically from the use or reproduction of this software, it is not
X# sold, rented, traded or otherwise marketed, and this copyright notice is
X# included prominently in any copy made. 
X#
X# The author make no claims as to the fitness or correctness of this software
X# for any use whatsoever, and it is provided as is. Any use of this software
X# is at the user's own risk. 
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="\${RNMAILER-${mailer-/bin/mail}}"
X# if you change this to something that does signatures, take out signature code
X
Xcase $d_portable in
Xdefine)
X    # where recordings, distributions and moderators are kept
X    lib=\`$filexp $newslib\`
X    # where important rn things are kept
X    rnlib=\`$filexp $privlib\`
X    ;;
Xundef)
X    # where recordings, distributions and moderators are kept
X    lib="$newslib"
X    # where important rn things are kept
X    rnlib="$privlib"
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="$locdist"
Xorg="$orgdist"
Xmultistate="$multistatedist"
Xcity="$citydist"
Xstate="$statedist"
Xcntry="$cntrydist"
Xcont="$contdist"
X
Xactive=${active-$lib/active}
Xtest=${test-test}
Xsed=${sed-sed}
Xecho=${echo-echo}
Xcat=${cat-cat}
Xegrep=${egrep-egrep}
Xgrep=${grep-grep}
Xtr=${tr-tr}
Xinews=${inewsloc-inews}
Xnidump=${nidump}
Xypmatch=${ypmatch}
X
X!GROK!THIS!
Xcase "$d_ignoreorg" in
Xdefine) $spitshell >>Pnews <<'!NO!SUBS!'
Xorgname=${NEWSORG-$orgname}
X!NO!SUBS!
X	;;
X*)	$spitshell >>Pnews <<'!NO!SUBS!'
Xorgname=${NEWSORG-${ORGANIZATION-$orgname}}
X!NO!SUBS!
X	;;
Xesac
X$spitshell >>Pnews <<'!NO!SUBS!'
Xdotdir=${DOTDIR-${HOME-$LOGDIR}}
Xtmpart=$dotdir/.article
Xartcheck=$rnlib/artcheck
Xspeller=$rnlib/Speller
X
Xtmp="${TMPDIR-/tmp}"
Xnewsgroups=${NEWSGROUPS-$lib/newsgroups}
Xactive=${NEWSACTIVE-$active}
Xnews_sig=${NEWSSIGNATURE-$dotdir/.news_sig}
X
Xif $test ! -f "$newsgroups" -a -r "$rnlib/getactive"; then
X    newsgroups="$tmp/Pnng.$$";
X    rmlist="$newsgroups";
X    cmdlist="$rnlib/getactive newsgroups $tmp/Pnng.$$ ; ";
Xfi
Xif $test ! -f "$active" -a -r "$rnlib/getactive"; then
X    active="$tmp/Pnact.$$";
X    rmlist="$rmlist $active";
X    cmdlist="$cmdlist $rnlib/getactive active $tmp/Pnact.$$";
Xfi
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, get to the top-level prompt of [t]rn, and use the command
X"g news.announce.newusers" to go to that group.
X
XEOM
X    expertise=beginner
Xfi
X
Xcase $cntry in
X  can) Stpr=Province ; stpr=province ;;
X  *)   Stpr=State ; stpr=state ;;
Xesac
X
Xcase $multistate in
X  pnw) multistpr="Pacific NorthWest" ;;
X  *)   multistpr="Multi-State Area" ;;
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 ; rm -f $rmlist ; $echo Article appended to ${HOME-$LOGDIR}/dead.article ; exit"
X    if $test "$rmlist" ; then
X	trap "rm -f $rmlist" 0
X    fi
X    trap "trap : 1; $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.  Allow erroneous user@site too.
Xflag=0
Xfor ng in $hdr_newsgroups ; do
X    case "$ng" in
X	poster)	flag=1 ;;
X	*@*) 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.whatever
Xelse
X    set X $hdr_newsgroups
X    shift
X    if $test $# -gt 0 ; then
X	dist=$1.whatever
X    else
X	dist=misc.whatever
X    fi
Xfi
Xcase $dist in
X*.*)
X    ;;
X*)
X    dist=$dist.whatever
X    ;;
Xesac
X
Xcase "$FAST_PNEWS" in
Xy*) ;;
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$multistate.*)
X    $echo "This program posts news to many machines throughout the ${multistpr}."
X    ;;
X$state.*)
X    $echo "This program posts news to many machines throughout the ${stpr}."
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 particular machine.'
X    ;;
X*)
X    $echo 'This program posts news to everyone on the machine.'
X    ;;
Xesac
Xans=""
Xwhile $test "X$ans" = X ; 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;;
Xesac
X
X# run getactive in the background, if necessary
Xif $test "$cmdlist"; then
X    ( eval $cmdlist ) &
Xfi
X
Xcase "$FAST_PNEWS" in
Xy*) file=''
X    $echo "" >> $tmpart
X    state=edit
X    ;;
X*)  file=h;;
Xesac
Xwhile $test "X$file" = Xh ; 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=check
X	;;
X    esac
Xdone
X
X$echo ""
X
Xif $test -r $news_sig; then
X    $echo "-- " >> $tmpart
X    $sed 4q $news_sig >> $tmpart
Xfi
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 "X$tmp" = Xh ; 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=check
X	;;
X	
X    check)
X	# wait for possible background getactive
X	$test "$cmdlist" && wait
X
X	# warn about long lines, malformed headers, misspelled newsgroups
X	$artcheck $tmpart 79 $newsgroups $active
X	state=ask
X	;;
X
X    ask)
X	$echo ""
X	$echo $n "Check spelling, Send, Abort, Edit, or List? $c"
X	read ans
X
X	case "$ans" in
X	[aA]*)
X	    state=rescue
X	    ;;
X	[eE]*)
X	    set $ans
X	    case $# in
X	    2)  VISUAL="$2" ;;
X	    esac
X	    state=edit
X	    ;;
X	[lL]*)
X	    $pager $tmpart
X	    state=ask
X	    ;;
X	[cC]*|[sS][pP]*)
X	    $speller $tmpart
X	    state=ask
X	    ;;
X	[sS]*)
X	    state=send
X	    ;;
X	[hH]*)
X	    $cat <<'EOH'
X
XType c to check the article's spelling, s to send the article, a to abort
Xand append the article to dead.article, e to edit the article again, or l
Xto list the article with your pager.
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		headerstrip='1,/^[	 ]*$/{/^[A-Z][-A-Za-z0-9]*:[	 ]*$/d;
X			/^X-ORIGINAL-NEWSGROUPS:.*$/d;
X			/^[Cc][Cc]:/d;
X			/^Distribution: world/d;}'
X		if $sed "$headerstrip" $tmpart | $inews -h ; then
X		    : null
X		else
X		    state=rescue
X		fi
X		cc=`$sed -n '1,/^[	 ]*$/{/^[Cc][Cc]:[	 ][^	 ]/p;}' $tmpart|
X		 $sed 's/^[Cc][Cc]:[	 ][	 ]*//'`
X		if $test "X$cc" != X ; then
X		    set X $cc
X		    shift
X		    case "$mailer" in
X		    *recmail)
X			($echo To: $cc
X			 $sed "$headerstrip" $tmpart) | $mailer
X			;;
X		    *)
X			set X `echo $cc | sed 's/,/ /g'`
X			shift
X			$sed "$headerstrip" $tmpart | $mailer $@
X			;;
X		    esac
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 `LANG= 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 "X$ng" = Xh ; 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
Xddn.*)
X    defdist=inet
X    dist=h
X    ;;
X*.*)
X    defdist=''
X    dist=h
X    ;;
X*)
X    defdist=''
X    dist=''
X    ;;
Xesac
X
Xwhile $test "X$dist" = Xh ; 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    $multistpr:	$multistate
X    Country:		$cntry
X    Continent:		$cont
X    Everywhere:		<null> (not "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    world*|comp*|news*|sci*|rec*|misc*|soc*|talk*|alt*)
X	dist=''
X	;;
X    ''|$loc|$org|$city|$state|$multistate|$cntry|$cont|$defdist)
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
X# LCP 16-Oct-91 Subject line is required.  Make it a little more
X# difficult to omit.  Added "while : ; do", ... "done", and "if"
X# at end of while loop.
Xwhile :
Xdo
X  case $# in
X  0)
X    title=h
X    while $test "X$title" = Xh ; 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    # LCP 16-Oct-91 Added "set" and "shift".  Must insure $# is 0
X    # in case the title is all white space and we make another
X    # pass thru this loop.
X    set X
X    shift
X    ;;
X  esac
X  if expr "X$title" : "^X[    ]*$" > /dev/null 2>&1
X  then
X    $cat <<'EOH'
X
XArticles without a "Subject:" line will not be accepted by the News
Xsystem.  Please give a Title/Subject line for your article.
XEOH
X  else
X     break
X  fi
Xdone
X
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 "X$ypmatch" != X; then
X		fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/"`
X	elif $test "X$nidump" != X; then
X		fullname=`$nidump passwd / | $sed -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
X	fi
X	if $test "X$fullname" = X; 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 "X$ypmatch" != X; then
X	fullname=`$ypmatch $logname passwd 2>/dev/null | $sed -e "s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q"`
X    fi
X    if $test "X$fullname" = X; 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
Xcase $orgname in
X/*) orgname=`$cat $orgname` ;;
Xesac
X
X$sed -e '/^Reply-To: $/d' > $tmpart <<EOHeader
XNewsgroups: $ng
XSubject: $title
XSummary: 
XReply-To: $REPLYTO
XFollowup-To: $follow
XDistribution: $dist
XOrganization: $orgname
XKeywords: 
XCc: 
X
XEOHeader
X
X!NO!SUBS!
Xcase "$d_nntp" 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 18848 -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 'nntp/nntp.patch' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'nntp/nntp.patch'\"
else
echo shar: Extracting \"'nntp/nntp.patch'\" \(20896 characters\)
sed "s/^X//" >'nntp/nntp.patch' <<'END_OF_FILE'
XHere's my latest patch to modify NNTP 1.5.11 to support the transmission
Xof thread databases.  
X
XThis patch supports both mthread's .thread file format (XTHREAD) and News
XOVerview's .overfile file format (XOVER).  It also adds the LISTGROUP
Xcommand for listing the numbers in a group, and the DATE command for
Xfinding out the time on the server.  Finally, the NEWGROUPS command is
Xfiltered based the user's access privileges.
X
XApply this patch from the root directory of your NNTP source.  While it
Xonly affects your server (the clientlib is no longer modified), it does
Xmodify things in the common directory.
X
XTo apply this:
X	cd nntp-1.5.11
X	patch -p < xthread.patch
X  OR	unipatch < xthread.patch | patch -p
X
XThis patch changes the conf.h.dist file, so you will need to edit your
Xconf.h file to add the new defines.  For example, you could:
X
X	cd common
X	diff -c conf.h.dist.orig conf.h.dist | patch conf.h
X
Xand then edit conf.h to make sure the new stuff is configured properly.
X
XThen you are ready to compile the new server with "make server" as usual.
X
XWayne Davison                                        davison@borland.com
X---8<------8<------8<------8<---cut here--->8------>8------>8------>8---
XIndex:common/version.c
XPrereq: "1.5.11
X@@ -2,4 +2,4 @@
X  * Provide the version number of this release.
X  */
X 
X-char	nntp_version[] = "1.5.11 (10 February 1991)";
X+char	nntp_version[] = "1.5.11t3 (10 February 1993)";
XIndex:CHANGES
X@@ -2,6 +2,22 @@
X since the initial release.  Individuals who either reported the bug or
X inspired the bug fix are in square brackets.
X 
X+1.5.11+XOVER1   (Wayne Davison <davison@borland.com> 18 Jan 93)
X+	Fixes, optimizations, and enhancements to the first patch.
X+
X+1.5.11+XOVER0	(Rich $alz <rsalz@uunet.uu.net> 23 dec 92)
X+	This adds the XOVER command to the server.  The XOVER command
X+	is used to retrieve data from the .overview file that is part
X+	of Geoff Collyer's "nov" package (that package is not provided
X+	here; the official archive for it is
X+	world.std.com:pub/src/news/nov.dist.tar.Z).  This command
X+	has the following syntax:
X+		XOVER [first[-last]]
X+	Where first and last are article numbers; the default is to
X+	return data for the current article.  This command is only
X+	valid after a GROUP command.  It returns a 224 code followed
X+	by a multi-line response.
X+
X 1.5.11
X 	Fixes to spawn.c and batch.c for those system that need
X 	execle() to specifically call /bin/sh to exectute a sh script.
XIndex:common/README
X@@ -340,6 +340,10 @@
X Authorization process. Read the file AUTHORIZATION in the root directory of
X the NNTP distribution for more information.
X 
X+XOVER		(defined)
X+     Defines whether we want to include the XOVER command, described
X+in the top-level README file of this distribution.
X+
X SERVER_FILE	("/usr/local/lib/rn/server")
X 
X      This file contains the name of the machine which runs the
XIndex:common/conf.h.dist
X@@ -94,6 +94,21 @@
X 			/* the server more.  If your server is heavily */
X 			/* loaded already, defining this may be a bad idea */
X 
X+/* XTHREAD defines:  if XTHREAD is defined, THREAD_DIR controls where the
X+ * thread files will be read from.
X+ */
X+#define XTHREAD		/* Optional XTHREAD command.  This allows trn to
X+			 * keep all data on the server. */
X+
X+/* Leave this undefined to indicate that thread files go in the spool
X+ * directory.  However, if you want a separate hierarchy of thread files,
X+ * define it here.
X+ */
X+/*#define THREAD_DIR	"/usr/spool/threads"		/* base directory */
X+
X+/* if LONG_THREAD_NAMES & THREAD_DIR are defined, create threads in one dir */
X+#undef LONG_THREAD_NAMES		/* not for short-name systems */
X+
X /* Things that vary in network implementations */
X #define	SUBNET		/* If you have 4.3 subnetting */
X #undef	DAMAGED_NETMASK	/* If your subnet mask is not a multiple of */
X@@ -201,6 +216,10 @@
X /* Things that relate to authentication and access */
X /* Define AUTH to use the proposed NNTP Version 2 authentication protocol. */
X #define	AUTH	
X+
X+/* Various protocol extensions */
X+#define XOVER		/* xover -- Return .overview data */
X+
X /*
X  * A file containing the name of the host which is running
X  * the news server.  This will have to match what rrn thinks,
X@@ -332,6 +351,24 @@
X #			endif
X #		endif
X #	endif
X+#endif
X+
X+#ifdef XTHREAD
X+# ifdef THREAD_DIR
X+#   ifdef LONG_THREAD_NAMES
X+#	undef SUFFIX
X+#   else
X+#     ifndef SUFFIX
X+#	define SUFFIX ".th"
X+#     endif
X+#   endif
X+# else
X+#   define THREAD_DIR	SPOOLDIR
X+#   ifndef SUFFIX
X+#     define SUFFIX	"/.thread"
X+#   endif
X+#   undef LONG_THREAD_NAMES
X+# endif
X #endif
X 
X /*
XIndex:common/nntp.h
X@@ -20,6 +20,7 @@
X  *	x2x	Article selection
X  *	x3x	Distribution
X  *	x4x	Posting
X+ *	x8x	Authorization
X  */
X 
X #define	CHAR_INF	'1'
X@@ -29,6 +30,7 @@
X #define	CHAR_FATAL	'5'
X 
X #define	INF_HELP	100	/* Help text on way */
X+#define	INF_DATE	111	/* Date */
X #define	INF_AUTH	180	/* Authorization capabilities */
X #define	INF_DEBUG	199	/* Debug output */
X 
X@@ -42,6 +44,7 @@
X #define	OK_HEAD		221	/* Head follows */
X #define	OK_BODY		222	/* Body follows */
X #define	OK_NOTEXT	223	/* No text sent -- stat, next, last */
X+#define	OK_OVER		224	/* Overview data follows */
X #define	OK_NEWNEWS	230	/* New articles by message-id follow */
X #define	OK_NEWGROUPS	231	/* New newsgroups follow */
X #define	OK_XFERED	235	/* Article transferred successfully */
X@@ -48,6 +51,7 @@
X #define	OK_POSTED	240	/* Article posted successfully */
X #define	OK_AUTHSYS	280	/* Authorization system ok */
X #define	OK_AUTH		281	/* Authorization (user/pass) ok */
X+#define	OK_BIN		282	/* binary data follows */
X 
X #define	CONT_XFER	335	/* Continue to send article */
X #define	CONT_POST	340	/* Continue to post article */
XIndex:server/Makefile
X@@ -6,13 +6,13 @@
X 	ahbs.o globals.o group.o help.o ihave.o list.o misc.o netaux.o \
X 	newgroups.o newnews.o nextlast.o ngmatch.o post.o parsit.o scandir.o \
X 	slave.o spawn.o strcasecmp.o subnet.o time.o xhdr.o fakesyslog.o \
X-	batch.o auth.o timer.o ../common/version.o
X+	batch.o auth.o timer.o xthread.o ../common/version.o
X 
X SRVRSRC = main.c serve.c access.c access_inet.c access_dnet.c active.c \
X 	ahbs.c globals.c group.c help.c ihave.c list.c misc.c netaux.c \
X 	newgroups.c newnews.c nextlast.c ngmatch.c post.c parsit.c scandir.c \
X 	slave.c spawn.c strcasecmp.c subnet.c time.c xhdr.c fakesyslog.c \
X-	batch.c auth.c timer.c ../common/version.c
X+	batch.c auth.c timer.c xthread.c ../common/version.c
X 
X SRVRINC = common.h ../common/conf.h ../common/nntp.h timer.h
X 
XIndex:server/ahbs.c
X@@ -56,8 +56,9 @@
X 			(void) fclose(fp);
X 			return;
X 		}
X-		printf("%d 0 %s Article retrieved, %s.\r\n",
X-			OK_ARTICLE + method, argv[1], verbiage[method]);
X+		printf("%d %ld %s Article retrieved, %s.\r\n",
X+			OK_ARTICLE + method, group_artnum, argv[1],
X+			verbiage[method]);
X 		spew(fp, method);
X 		(void) fclose(fp);
X #ifdef LOG
XIndex:server/batch.c
X@@ -461,7 +461,7 @@
X #ifdef POSTER
X 	sprintf(user, "USER=%s", POSTER);
X 	sprintf(logname, "LOGNAME=%s", POSTER);
X-	if ((home = (char *)malloc(strlen(home_poster)+5)) != NULL)
X+	if ((home = (char *)malloc(strlen(home_poster)+5+1)) != NULL)
X 		sprintf(home, "HOME=%s", home_poster);
X 	envp[0] = user;
X 	envp[1] = logname;
XIndex:server/common.h
X@@ -164,11 +164,18 @@
X extern	char	inews[];
X extern	char	rnews[];
X 
X+#ifdef	XTHREAD
X+extern	char	*threaddir;
X+extern	char	*threadfile;
X+#endif
X+
X extern	char	**group_array;
X extern	char	*actbuf;
X extern	int	num_groups;
X extern	char	*homedir;
X extern	int	ingroup;
X+extern	char	*group_name;
X+extern	long	group_artnum;
X extern	int	maxgroups;
X #ifdef DYNAMIC_ART_ARRAY
X extern	int	*art_array;
XIndex:server/globals.c
X@@ -26,6 +26,11 @@
X char	inews[] = INEWS;
X char	rnews[] = RNEWS;
X 
X+#ifdef	XTHREAD
X+char	*threaddir = THREAD_DIR;
X+char	*threadfile = NULL;
X+#endif
X+
X /*
X  * Other random externals.
X  */
X@@ -34,6 +39,8 @@
X char 	*actbuf;
X int	num_groups;
X int	ingroup = 0;
X+char	*group_name = NULL;
X+long	group_artnum = 0;
X int	art_ptr;
X int	num_arts;
X #ifdef DYNAMIC_ART_ARRAY
XIndex:server/group.c
X@@ -4,6 +4,12 @@
X 
X #include "common.h"
X 
X+#ifdef	XTHREAD
X+extern char *thread_name();
X+#endif
X+
X+extern char *malloc();
X+
X /*
X  * GROUP newsgroup
X  *
X@@ -67,8 +73,15 @@
X 		return;
X 	}
X 
X+#ifdef XOVER
X+	close_xover();
X+#endif
X 	close_crnt();
X+	if (group_name)
X+		free(group_name);
X 	(void) chdir(spooldir);
X+	if ((group_name = malloc(strlen(argv[1])+1)) != NULL)
X+		strcpy(group_name, argv[1]);
X 
X #ifdef LOG
X 	syslog(LOG_INFO, "%s group %s", hostname, argv[1]);
X@@ -97,6 +110,10 @@
X 
X 	ingroup = 1;
X 
X+#ifdef	XTHREAD
X+	threadfile = thread_name(argv[1]);
X+#endif
X+
X 	while ((cp = index(argv[1], '/')) != (char *) NULL)
X 		*cp = '.';
X 
X@@ -108,3 +125,177 @@
X 		argv[1]);
X 	(void) fflush(stdout);
X }
X+
X+
X+#ifdef XOVER
X+static FILE *xover_fp;
X+static int xover_num;
X+
X+doxover(argc, argv)
X+	int		argc;
X+	char		*argv[];
X+{
X+	register FILE	*fp;
X+	register int	c, first, last;
X+	int		artnum, artp;
X+	char		*p;
X+
X+	if (!canread) {
X+		printf("%d You only have permission to transfer, sorry.\r\n",
X+			ERR_ACCESS);
X+		(void) fflush(stdout);
X+		return;
X+	}
X+
X+	if (!ingroup) {
X+		printf("%d You are not currently in a newsgroup.\r\n",
X+			ERR_NCING);
X+		(void) fflush(stdout);
X+		return;
X+	}
X+	if (argc != 1 && argc != 2) {
X+		printf("%d Usage: XOVER [first[-last]].\r\n", ERR_CMDSYN);
X+		(void) fflush(stdout);
X+		return;
X+	}
X+
X+	if (xover_fp)
X+		fp = xover_fp;
X+	else {
X+		fp = xover_fp = fopen(".overview", "r");
X+		if (fp == NULL) {
X+			printf("%d No overview available.\r\n", ERR_FAULT);
X+			(void) fflush(stdout);
X+#ifdef SYSLOG
X+			syslog(LOG_ERR, "xover: fopen %s: %m", ".overview");
X+#endif
X+			return;
X+		}
X+		xover_num = 0;
X+	}
X+
X+	if (argc == 1) {
X+		if (art_ptr < 0 || art_ptr >= num_arts) {
X+			printf("%d No article is currently selected.\r\n",
X+				ERR_NOCRNT);
X+			(void) fflush(stdout);
X+			return;
X+		}
X+		first = last = art_array[art_ptr];
X+	} else {
X+		p = index(argv[1], '-');
X+		if (p == NULL)
X+			first = last = atoi(argv[1]);
X+		else {
X+			*p++ = '\0';
X+			first = atoi(argv[1]);
X+			last = atoi(p);
X+		}
X+		if (first < 1)
X+		    first = 1;
X+		if (last > art_array[num_arts-1])
X+		    last = art_array[num_arts-1];
X+	}
X+	/* Return the desired data.  This is written carefully to avoid
X+	 * over-long lines. */
X+	printf("%d overview data follows\r\n", OK_OVER);
X+	if (first < xover_num || !xover_num) {
X+		fseek(fp, 0L, 0);
X+		xover_num = 0;
X+	}
X+	if (xover_num) {
X+		artnum = xover_num;
X+		xover_num = 0;
X+	} else
X+		fscanf(fp, "%d", &artnum);
X+	artp = 0;
X+	while (!feof(fp)) {
X+		if (artnum > last) {
X+			xover_num = artnum;
X+			break;
X+		}
X+		while (art_array[artp] < artnum)
X+		    artp++;
X+		if (artnum >= first && artnum == art_array[artp]) {
X+			printf("%d", artnum);
X+			while ((c = getc(fp)) != EOF && c != '\n')
X+				putchar(c);
X+			printf("\r\n");
X+		} else
X+			while ((c = getc(fp)) != EOF && c != '\n')
X+				continue;
X+		fscanf(fp, "%d", &artnum);
X+	}
X+	printf(".\r\n");
X+	(void) fflush(stdout);
X+}
X+
X+close_xover()
X+{
X+	if (xover_fp) {
X+		fclose(xover_fp);
X+		xover_fp = NULL;
X+	}
X+}
X+#endif
X+
X+#ifdef LISTGROUP
X+/*
X+ * LISTGROUP [group]
X+ *
X+ * Lists all article numbers (filenames) in the given group. Used by
X+ * newsreaders such as nn and trn for fast validation of a database.
X+ * If a group name is given it becomes the current group.
X+ *
X+ * This command is an extention, and not included in RFC 977.
X+ */
X+
X+listgroup(argc, argv)
X+	int		argc;
X+	char		*argv[];
X+{
X+	register int i;
X+
X+	if (argc == 2) {
X+		ingroup = 0;
X+		/* This will output a success or failure message */
X+		group(argc, argv);
X+		if (!ingroup) {
X+			return;
X+		}
X+	} else if (argc > 2) {
X+		printf("%d Usage: LISTGROUP [newsgroup].\r\n", ERR_CMDSYN);
X+		(void) fflush(stdout);
X+		return;
X+	} else if (!ingroup) {
X+		printf("%d You are not currently in a newsgroup.\r\n",
X+			ERR_NCING);
X+		(void) fflush(stdout);
X+		return;
X+	} else if (!canread) {
X+		printf("%d You only have permission to transfer, sorry.\r\n",
X+			ERR_ACCESS);
X+		(void) fflush(stdout);
X+		return;
X+	} else {
X+		/* output a success message when no group name is given */
X+		printf("%d %d %d %d (current group)\r\n",
X+			OK_GROUP,
X+			num_arts,
X+			(num_arts > 0 ? art_array[0] : 0),
X+			(num_arts > 0 ? art_array[num_arts-1] : 0));
X+	}
X+
X+#ifdef LOG
X+	syslog(LOG_INFO, "%s listgroup", hostname);
X+#endif
X+	for (i = 0; i < num_arts; i++) {
X+		printf("%d\r\n", art_array[i]);
X+	}
X+	putchar('.');
X+	putchar('\r');
X+	putchar('\n');
X+	(void) fflush(stdout);
X+}
X+
X+#endif /* LISTGROUP */
XIndex:server/help.c
X@@ -21,8 +21,22 @@
X 	printf("NEXT        POST         QUIT\r\n");
X 	printf("STAT        NEWGROUPS    HELP\r\n");
X 	printf("IHAVE       NEWNEWS      SLAVE\r\n");
X-	printf("\r\nAdditionally, the following extention is supported:\r\n\r\n");
X+	printf("DATE\r\n");
X+#if defined(XHDR) || defined(XTHREAD) || defined(LISTGROUP) || defined(XOVER)
X+	printf("\r\nAdditionally, the following extension(s) are supported:\r\n\r\n");
X+# ifdef LISTGROUP
X+	printf("LISTGROUP   Retrieve a list of valid article-numbers.\r\n");
X+# endif
X+# ifdef	XHDR
X 	printf("XHDR        Retrieve a single header line from a range of articles.\r\n");
X+# endif
X+# ifdef	XOVER
X+	printf("XOVER       Return news overview data\r\n");
X+# endif
X+# ifdef	XTHREAD
X+	printf("XTHREAD     Retrieve trn thread file for the current group.\r\n");
X+# endif
X+#endif
X 	printf("\r\n");
X 	printf("Bugs to Stan Barber (Internet: nntp@tmc.edu; UUCP: ...!bcm!nntp)\r\n");
X 	printf(".\r\n");
XIndex:server/misc.c
X@@ -100,7 +100,7 @@
X 	int		lookup;
X {
X 	char		line[MAXBUFLEN];
X-	char		*tmp;
X+	char		*start, *end;
X 	register char	*cp;
X 	long		ltmp;
X 	static char	path[MAXPATHLEN];
X@@ -118,6 +118,7 @@
X #endif USGHIST
X 	static FILE	*hfp = NULL;	/* history file, text version */
X 
X+	group_artnum = 0;
X #ifdef CNEWS
X 	cp = rindex(msg_id,'@');	/* look for @ in message id */
X 	if( cp != NULL)
X@@ -234,17 +235,30 @@
X 			ltmp, msg_id);
X #endif SYSLOG
X 	if (cp == NULL) return(NULL); /* this article has expired */
X-	tmp = cp+1;
X 
X-	if ((cp = index(tmp, ' ')) != NULL)
X+	if (group_name) {
X+		end = cp;
X+		do {
X+			if ((end = index(start = end+1, ' ')) != NULL)
X+				*end = '\0';
X+
X+			if ((cp = index(start, '/')) != NULL) {
X+				*cp = '\0';
X+				if (!strcmp(start, group_name))
X+					group_artnum = atol(cp+1);
X+				*cp = '/';
X+			}
X+		} while (!group_artnum && end != NULL);
X+	}
X+	else if ((cp = index(start = cp+1, ' ')) != NULL)
X 		*cp = '\0';
X-	
X-	while ((cp = index(tmp, '.')) != NULL)
X+
X+	while ((cp = index(start, '.')) != NULL)
X 		*cp = '/';
X 
X 	(void) strcpy(path, spooldir);
X 	(void) strcat(path, "/");
X-	(void) strcat(path, tmp);
X+	(void) strcat(path, start);
X #ifdef USGHIST
X 	(void) fclose(hfp);
X #endif
X@@ -481,9 +495,10 @@
X 
X close_crnt()
X {
X-	if (art_fp != NULL)
X+	if (art_fp != NULL) {
X 		(void) fclose(art_fp);
X-	art_fp = NULL;
X+		art_fp = NULL;
X+	}
X }
X 
X 
XIndex:server/newgroups.c
X@@ -23,6 +23,7 @@
X 	int		i;
X 	long		date;
X 	register FILE	*date_fp;
X+	char		*reqlist[2];
X 
X 	if (argc < 3) {
X printf("%d Usage: NEWGROUPS yymmdd hhmmss [\"GMT\"] [<distributions>].\r\n",
X@@ -96,6 +97,22 @@
X #endif
X 
X 		if (distcount == 0) {
X+#ifdef ACTIVE_TIMES_FILE
X+			reqlist[0] = line;
X+#else
X+			reqlist[0] = cp + 1;
X+#endif
X+			reqlist[1] = NULL;
X+
X+			if (ngpermcount) {
X+			    if (ngmatch(s1strneql, ALLBUT,
X+			        ngpermlist, ngpermcount, reqlist, 1) == 0) {
X+			         continue;
X+			    }
X+			} else if (ALLBUT == 0) {
X+			    continue;
X+			}
X+
X #ifdef ACTIVE_TIMES_FILE
X 			putline(line);
X #else
XIndex:server/serve.c
X@@ -24,9 +24,9 @@
X #include "timer.h"
X #endif
X 
X-extern	int	ahbs(), group(), help(), ihave();
X+extern	int	ahbs(), dodate(), group(), help(), ihave();
X extern	int	list(), newgroups(), newnews(), nextlast(), post();
X-extern	int	slave(), stat(), xhdr();
X+extern	int	slave(), stat(), listgroup(), xhdr(), doxover(), xthread();
X 
X extern int errno;
X 
X@@ -33,6 +33,9 @@
X #ifdef AUTH
X extern	int	doauth();
X #endif AUTH
X+#ifdef XOVER
X+extern	int	doxover();
X+#endif
X 
X static struct cmdent {
X 	char	*cmd_name;
X@@ -46,6 +49,7 @@
X #endif AUTH
X 	"article",	0,	ahbs,
X 	"body",		0,	ahbs,
X+	"date",		0,	dodate,
X 	"group",	0,	group,
X 	"head",		0,	ahbs,
X 	"help",		0,	help,
X@@ -52,6 +56,9 @@
X 	"ihave",	1,	ihave,
X 	"last",		0,	nextlast,
X 	"list",		0,	list,
X+#ifdef LISTGROUP
X+	"listgroup",	0,	listgroup,
X+#endif LISTGROUP
X 	"newgroups",	0,	newgroups,
X 	"newnews",	0,	newnews,
X 	"next",		0,	nextlast,
X@@ -61,6 +68,12 @@
X #ifdef XHDR
X 	"xhdr",		0,	xhdr,
X #endif XHDR
X+#ifdef XOVER
X+	"xover",	0,	doxover,
X+#endif
X+#ifdef XTHREAD
X+	"xthread",	0,	xthread,
X+#endif
X };
X #define NUMCMDS (sizeof(cmdtbl) / sizeof(struct cmdent))
X 
XIndex:server/time.c
X@@ -14,6 +14,28 @@
X #include <sys/time.h>
X #endif not USG
X 
X+dodate(ac, av)
X+	int	ac;
X+	char	*av[];
X+{
X+	struct tm	*gmt;
X+#ifdef USG
X+	time_t		now;
X+
X+	(void) time(&now);
X+	gmt = gmtime(&now);
X+#else /* not USG */
X+	struct timeval	now;
X+
X+	(void) gettimeofday(&now, (struct timezone *)NULL);
X+	gmt = gmtime(&now.tv_sec);
X+#endif /* not USG */
X+	printf("%d %04.4d%02.2d%02.2d%02.2d%02.2d%02.2d\r\n", INF_DATE,
X+		gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
X+		gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
X+	(void) fflush(stdout);
X+}
X+
X /*
X  * dtol -- convert date to long integer.  This is not implicitly
X  * local time, or any other kind of time, for that matter.  If you
XIndex:server/xthread.c
X@@ -0,0 +1,149 @@
X+/* This file (and only this file - not the entire nntp distribution) is
X+ * hereby placed in the public domain.  Use it as you see fit, but if you
X+ * manage to find some wonderful use for this code elsewhere, it would be
X+ * nice to receive mention for it.
X+ *
X+ * - Tim Iverson
X+ *   iverson@xstor.com -/- uunet!xstor!iverson
X+ *   3/28/91
X+ *   modified by Wayne Davison (davison@borland.com) to work with trn 2.0
X+ *   10/6/91
X+ */
X+
X+#include "common.h"
X+
X+#ifdef XTHREAD
X+
X+# ifdef __GNUC__
X+#  define alloca __builtin_alloca
X+# endif
X+
X+char *thread_name();
X+
X+/* Usage: XTHREAD [DBINIT|THREAD]
X+ *
X+ * DBINIT	dump the contents of the db.init file to stdout
X+ * THREAD	dump the contents of the thread file for the current
X+ *		newsgroup to stdout (default if no argument).
X+ *
X+ * N.B. These two files are not ascii and no attempt is made at converting
X+ *	native byte size to any type of standard whatsoever.  This leaves it
X+ *	up to the receiver (i.e. trn) to translate the data based on db.init.
X+ *
X+ * This command is not documented in rfc977.
X+ */
X+
X+void
X+xthread(argc, argv)
X+int	argc;
X+char	*argv[];
X+{
X+	register FILE	*fp;
X+	struct stat	s;
X+	char		*buf, *file, *what;
X+
X+	/* can't transfer threads, only read 'em */
X+	if (!canread)
X+	{
X+		printf("%d You only have permission to transfer, sorry.\r\n",
X+			ERR_ACCESS);
X+		(void) fflush(stdout);
X+		return;
X+	}
X+
X+	/* "parse" the argument */
X+	if (argc == 2 && !strcasecmp(argv[1], "dbinit"))
X+	{
X+		file = thread_name("*******");
X+		what = "db.init";
X+		strcpy(index(file, '*'), what);
X+	}
X+	else if (argc == 1 || argc == 2 && !strcasecmp(argv[1], "thread"))
X+	{
X+		if (!threadfile)
X+		{
X+			printf("%d You are not currently in a newsgroup.\r\n",
X+				ERR_NCING);
X+			(void) fflush(stdout);
X+			return;
X+		}
X+		file = threadfile;
X+		what = "thread";
X+	}
X+	else
X+	{
X+		printf("%d Usage: XTHREAD [DBINIT|THREAD]\r\n", ERR_CMDSYN);
X+		(void) fflush(stdout);
X+		return;
X+	}
X+
X+	/* try to open the file to be transfered */
X+	if (!(fp = fopen(file, "r")))
X+	{
X+		printf("%d %s file is not available.\r\n", ERR_FAULT, what);
X+		(void) fflush(stdout);
X+#ifdef SYSLOG
X+		if (!strcmp(what, "db.init"))
X+		    syslog(LOG_ERR, "xthread: fopen %s: %m", file);
X+#endif
X+		return;
X+	}
X+
X+	/* tell 'em how much binary data is coming down the pike */
X+	fstat(fileno(fp), &s);
X+	printf("%d %u bytes of %s file follows verbatim (binary!)\r\n",
X+		OK_BIN, s.st_size, what);
X+
X+	/* copy the file verbatim to stdout */
X+#ifdef __GNUC__
X+	if (buf = alloca(s.st_size))
X+	{
X+		/* ah-so! got lotsa memoree */
X+		read(fileno(fp), buf, s.st_size);
X+		fwrite(buf, s.st_size, 1, stdout);
X+	}
X+	else
X+#endif
X+	{
X+		int		bytes;
X+		char		buf[BUFSIZ];
X+
X+		while (bytes = fread(buf, 1, sizeof buf, fp))
X+			fwrite(buf, bytes, 1, stdout);
X+	}
X+
X+	fputs("\r\n.\r\n", stdout);
X+	fflush(stdout);
X+	fclose(fp);
X+}
X+
X+/* Change a newsgroup name into the name of the thread data file.  We
X+** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES
X+** is defined), prepend the path, and append the '/.thread' (or '.th') on to
X+** the end.
X+*/
X+char *
X+thread_name(group)
X+char *group;
X+{
X+	static char name_buff[MAXPATHLEN];
X+#ifdef LONG_THREAD_NAMES
X+	char group_buff[512];
X+	register char *ptr;
X+
X+	strcpy(group_buff, group);
X+	ptr = group = group_buff;
X+	while ((ptr = index(ptr, '/'))) {
X+		*ptr = '.';
X+	}
X+#endif
X+#ifdef SUFFIX
X+	sprintf(name_buff, "%s/%s%s", threaddir, group, SUFFIX);
X+#else
X+	sprintf(name_buff, "%s/%s", threaddir, group);
X+#endif
X+
X+	return name_buff;
X+}
X+
X+#endif /* not XTHREAD */
X---8<------8<------8<------8<---cut here--->8------>8------>8------>8---
END_OF_FILE
if test 20896 -ne `wc -c <'nntp/nntp.patch'`; then
    echo shar: \"'nntp/nntp.patch'\" unpacked with wrong size!
fi
# end of 'nntp/nntp.patch'
fi
if test -f 'respond.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'respond.c'\"
else
echo shar: Extracting \"'respond.c'\" \(20348 characters\)
sed "s/^X//" >'respond.c' <<'END_OF_FILE'
X/* $Id: respond.c,v 3.0 1992/02/01 03:09:32 davison Trn $
X */
X/* This software is Copyright 1991 by Stan Barber. 
X *
X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
X * use this software as long as: there is no monetary profit gained
X * specifically from the use or reproduction or this software, it is not
X * sold, rented, traded or otherwise marketed, and this copyright notice is
X * included prominently in any copy made. 
X *
X * The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "intrp.h"
X#include "cache.h"
X#include "head.h"
X#include "term.h"
X#include "ngdata.h"
X#include "ng.h"
X#include "util.h"
X#include "trn.h"
X#include "artio.h"
X#include "final.h"
X#include "decode.h"
X#include "INTERN.h"
X#include "respond.h"
X
Xstatic char nullart[] = "\nNull article\n";
X
Xbool cut_line();
X
Xvoid
Xrespond_init()
X{
X    export("NEWSACTIVE", filexp(ACTIVE));
X}
X
Xint
Xsave_article()
X{
X    bool_int use_pref;
X    register char *s, *c;
X    char altbuf[CBUFLEN];
X    int iter;
X    bool interactive = (buf[1] == FINISHCMD);
X    char cmd = *buf;
X    
X    if (!finish_command(interactive))	/* get rest of command */
X	return SAVE_ABORT;
X    if ((use_pref = isupper(cmd)) != 0)
X	cmd = tolower(cmd);
X    parseheader(art);
X#ifdef MIME_SUPPORT
X    savefrom = (!mime_article && (cmd == 'w' || cmd == 'e'))
X#else
X    savefrom = (cmd == 'w' || cmd == 'e')
X#endif
X		? htype[PAST_HEADER].ht_minpos : 0;
X    if (artopen(art) == Nullfp) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\n\
XSaving null articles is not very productive!  :-)\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs(nullart,stdout) FLUSH;
X#endif
X	return SAVE_DONE;
X    }
X    if (chdir(cwd)) {
X	printf(nocd,cwd) FLUSH;
X	sig_catcher(0);
X    }
X    if (cmd == 'e') {		/* is this an extract command? */
X	static bool custom_extract = FALSE;
X	int cnt = 0;
X	bool found_cut = FALSE;
X	char art_buf[LBUFLEN], *cmdstr;
X
X	s = buf+1;		/* skip e */
X	while (*s == ' ') s++;	/* skip leading spaces */
X	safecpy(altbuf,filexp(s),sizeof altbuf);
X	s = altbuf;
X	if (*s) {
X	    cmdstr = cpytill(buf,s,'|');	/* check for | */
X	    s = buf + strlen(buf)-1;
X	    while (*s == ' ') s--;		/* trim trailing spaces */
X	    *++s = '\0';
X	    if (*cmdstr) {
X		s = cmdstr+1;			/* skip | */
X		while (*s == ' ') s++;
X		if (*s)	{			/* if new command, use it */
X		    if (extractprog)
X			free(extractprog);
X		    extractprog = savestr(s);	/* put extracter in %e */
X		}
X		else
X		    cmdstr = extractprog;
X	    }
X	    else
X		cmdstr = Nullch;
X	    s = buf;
X	}
X	else {
X	    if (extractdest)
X		strcpy(s, extractdest);
X	    if (custom_extract)
X		cmdstr = extractprog;
X	    else
X		cmdstr = Nullch;
X	}
X	if (cmdstr) {
X	    if (strEQ(extractprog,"-"))
X		cmdstr = Nullch;
X	    else if (decode_fp != Nullfp)
X		decode_end();
X	}
X	custom_extract = (cmdstr != 0);
X
X	fseek(artfp,savefrom,0);
X	if (*s != '/') {		/* relative path? */
X	    c = (s==buf ? altbuf : buf);
X	    interp(c, (sizeof buf), getval("SAVEDIR",SAVEDIR));
X	    if (makedir(c,MD_DIR))	/* ensure directory exists */
X		strcpy(c,cwd);
X	    if (*s) {
X		while (*c) c++;
X		*c++ = '/';
X		strcpy(c,s);		/* add filename */
X	    }
X	    s = (s==buf ? altbuf : buf);
X	}
X	if (*s != '/') {		/* path still relative? */
X	    c = (s==buf ? altbuf : buf);
X	    sprintf(c, "%s/%s", cwd, s);
X	    s = c;			/* absolutize it */
X	}
X	if (decode_fp != Nullfp) {
X	    printf("Continuing %s:%s\n", decode_fname,
X		cmd != '\0' && strNE(extractdest,s) ?
X		 " (Ignoring conflicting directory)" : nullstr ) FLUSH;
X	    if (decode_type == UUDECODE)
X		uudecode(artfp);
X	    else
X		unship(artfp);
X	}
X	else {
X	    if (extractdest)
X		free(extractdest);
X	    s = extractdest = savestr(s); /* make it handy for %E */
X	    if (makedir(s, MD_DIR)) {	/* ensure directory exists */
X		int_count++;
X		return SAVE_DONE;
X	    }
X	    if (chdir(s)) {
X		printf(nocd,s) FLUSH;
X		sig_catcher(0);
X	    }
X	    s = getwd(buf);		/* simplify path for output */
X	    while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
X		if (*art_buf <= ' ')
X		    continue;	/* Ignore empty or initially-whitespace lines */
X#ifdef MIME_SUPPORT
X		if (mime_article) {
X		    char oldmode = mode;
X		    if (!custom_extract) {
X			printf("Extracting MIME article into %s:\n", s) FLUSH;
X			extractprog = savestr(filexp(getval("MIMESTORE",MIMESTORE)));
X		    }
X		    else
X			printf("Extracting MIME article into %s using %s\n",
X			       s, extractprog) FLUSH;
X		    cnt = 0;
X		    interp(cmd_buf, sizeof cmd_buf,
X			   getval("EXMIMESAVER",EXMIMESAVER));
X		    termlib_reset();
X		    mode = 'x';
X		    resetty();		/* restore tty state */
X		    doshell(SH,cmd_buf);
X		    noecho();		/* revert to cbreaking */
X		    crmode();
X		    termlib_init();
X		    mode = oldmode;
X		    break;
X		}
X#endif
X		if (found_cut && custom_extract) {
X		    printf("Extracting data into %s using %s:\n",
X			s, extractprog) FLUSH;
X		    goto extract_it;
X		}
X		if (((*art_buf == '#' || *art_buf == ':')
X		  && (strnEQ(art_buf+1, "! /bin/sh", 9)
X		   || strnEQ(art_buf+1, "!/bin/sh", 8)
X		   || strnEQ(art_buf+2, "This is ", 8)))
X		 || strnEQ(art_buf, "sed ", 4)
X		 || strnEQ(art_buf, "cat ", 4)
X		 || strnEQ(art_buf, "echo ", 5)) {
X		    fseek(artfp,-(long)strlen(art_buf)-NL_SIZE+1,1);
X		    savefrom = ftell(artfp);
X		    if (custom_extract) {
X			printf("Extracting shar into %s using %s:\n",
X				s, extractprog) FLUSH;
X			goto extract_it;
X		    }
X		    /* Check for special-case of shar'ed-uuencoded file */
X		    while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
X			if (*art_buf == '#' || *art_buf == ':'
X			 || strnEQ(art_buf, "echo ", 5)
X			 || strnEQ(art_buf, "sed ", 4))
X			    continue;
X			if (strnEQ(art_buf, "Xbegin ", 7)) {
X			    decode_type = UUDECODE;
X			    goto decode_it;
X			}
X			break;
X		    }
X		    printf("Extracting shar into %s:\n", s) FLUSH;
X		    if (extractprog)
X			free(extractprog);
X		    extractprog = savestr(filexp(getval("UNSHAR",UNSHAR)));
X		  extract_it:
X		    cnt = 0;
X		    interp(cmd_buf,(sizeof cmd_buf),getval("EXSAVER",EXSAVER));
X		    termlib_reset();
X		    resetty();		/* restore tty state */
X		    doshell(SH,cmd_buf);
X		    noecho();		/* revert to cbreaking */
X		    crmode();
X		    termlib_init();
X		    break;
X		}
X		else
X		if (!custom_extract
X		 && (strEQ(art_buf,"$\n")
X		  || strEQ(art_buf,"$ f\n"))) {
X		    savefrom = ftell(artfp)-strlen(art_buf)-NL_SIZE+1;
X		    if (found_cut
X		     || (fgets(art_buf,LBUFLEN,artfp) != Nullch
X		      && (strnEQ(art_buf, "ship ", 5)
X		       || strnEQ(art_buf, "cont ", 5)))) {
X			decode_type = UNSHIP;
X			goto decode_it;
X		    }
X		}
X		else
X		if (!custom_extract
X		 && (strEQ(art_buf,"table\n")
X		  || strnEQ(art_buf,"begin ", 6))) {
X		    decode_type = UUDECODE;
X		    savefrom = ftell(artfp)-strlen(art_buf)-NL_SIZE+1;
X		 decode_it:
X		    printf("Extracting %s file into %s:\n",
X			decode_type == UNSHIP? "shipped":"uuencoded", s) FLUSH;
X		    if (extractprog)
X			free(extractprog);
X		    extractprog = savestr("-");
X		    fseek(artfp, savefrom, 0);
X		    cnt = 0;
X		    if (decode_type == UUDECODE) {
X			uud_start();
X			uudecode(artfp);
X		    } else
X			unship(artfp);
X		    break;
X		}
X		else {
X		    if (cut_line(art_buf)) {
X			savefrom = ftell(artfp);
X			found_cut = TRUE;
X		    }
X		    else if (found_cut || ++cnt == 300) {
X			break;
X		    }
X		}
X	    }/* while */
X	    if (cnt) {
X		if (custom_extract)
X		    printf("Didn't find cut line for extraction to '%s'.\n",
X			extractprog) FLUSH;
X		else
X		    printf("Unable to determine type of file.\n") FLUSH;
X	    }
X	}/* if */
X    }
X    else if ((s = index(buf,'|')) != Nullch) {
X				/* is it a pipe command? */
X	s++;			/* skip the | */
X	while (*s == ' ') s++;
X	safecpy(altbuf,filexp(s),sizeof altbuf);
X	if (savedest)
X	    free(savedest);
X	savedest = savestr(altbuf);
X	interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER));
X				/* then set up for command */
X	termlib_reset();
X	resetty();		/* restore tty state */
X	if (use_pref)		/* use preferred shell? */
X	    doshell(Nullch,cmd_buf);
X				/* do command with it */
X	else
X	    doshell(sh,cmd_buf);	/* do command with sh */
X	noecho();		/* and stop echoing */
X	crmode();		/* and start cbreaking */
X	termlib_init();
X    }
X    else {			/* normal save */
X	bool there, mailbox;
X	char *savename = getval("SAVENAME",SAVENAME);
X
X	s = buf+1;		/* skip s or S */
X	if (*s == '-') {	/* if they are confused, skip - also */
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("Warning: '-' ignored.  This isn't readnews.\n",stdout)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("'-' ignored.\n",stdout) FLUSH;
X#endif
X	    s++;
X	}
X	for (; *s == ' '; s++);	/* skip spaces */
X	safecpy(altbuf,filexp(s),sizeof altbuf);
X	s = altbuf;
X	if (*s != '/') {
X	    interp(buf, (sizeof buf), getval("SAVEDIR",SAVEDIR));
X	    if (makedir(buf,MD_DIR))	/* ensure directory exists */
X		strcpy(buf,cwd);
X	    if (*s) {
X		for (c = buf; *c; c++) ;
X		*c++ = '/';
X		strcpy(c,s);		/* add filename */
X	    }
X	    s = buf;
X	}
X	for (iter = 0;
X	    (there = stat(s,&filestat) >= 0) &&
X	    (filestat.st_mode & S_IFDIR);
X	    iter++) {			/* is it a directory? */
X
X	    c = (s+strlen(s));
X	    *c++ = '/';			/* put a slash before filename */
X	    interp(c, s==buf?(sizeof buf):(sizeof altbuf),
X		iter ? "News" : savename );
X				/* generate a default name somehow or other */
X	}
X	makedir(s,MD_FILE);
X	if (*s != '/') {		/* relative path? */
X	    c = (s==buf ? altbuf : buf);
X	    sprintf(c, "%s/%s", cwd, s);
X	    s = c;			/* absolutize it */
X	}
X	if (savedest)
X	    free(savedest);
X	s = savedest = savestr(s);	/* doesn't move any more */
X					/* make it handy for %b */
X	if (!there) {
X	    if (mbox_always)
X		mailbox = TRUE;
X	    else if (norm_always)
X		mailbox = FALSE;
X	    else {
X		char *dflt = (instr(savename,"%a", TRUE) ? "nyq" : "ynq");
X		
X		sprintf(cmd_buf,
X		"\nFile %s doesn't exist--\n	use mailbox format? [%s] ",
X		  s,dflt);
X	      reask_save:
X		in_char(cmd_buf, 'M');
X		putchar('\n') FLUSH;
X		setdef(buf,dflt);
X#ifdef VERIFY
X		printcmd();
X#endif
X		if (*buf == 'h') {
X#ifdef VERBOSE
X		    IF(verbose)
X			printf("\n\
XType y to create %s as a mailbox.\n\
XType n to create it as a normal file.\n\
XType q to abort the save.\n\
X",s) FLUSH;
X		    ELSE
X#endif
X#ifdef TERSE
X			fputs("\n\
Xy to create mailbox.\n\
Xn to create normal file.\n\
Xq to abort.\n\
X",stdout) FLUSH;
X#endif
X		    goto reask_save;
X		}
X		else if (*buf == 'n') {
X		    mailbox = FALSE;
X		}
X		else if (*buf == 'y') {
X		    mailbox = TRUE;
X		}
X		else if (*buf == 'q') {
X		    goto s_bomb;
X		}
X		else {
X		    fputs(hforhelp,stdout) FLUSH;
X		    settle_down();
X		    goto reask_save;
X		}
X	    }
X	}
X	else if (filestat.st_mode & S_IFCHR)
X	    mailbox = FALSE;
X	else {
X	    int tmpfd;
X	    
X	    tmpfd = open(s,0);
X	    if (tmpfd == -1)
X		mailbox = FALSE;
X	    else {
X		if (read(tmpfd,buf,LBUFLEN)) {
X		    c = buf;
X		    if (!isspace(MBOXCHAR))   /* if non-zero, */
X			while (isspace(*c))   /* check the first character */
X			    c++;
X		    mailbox = (*c == MBOXCHAR);
X		} else {
X		    mailbox = mbox_always;    /* if zero length, recheck -M */
X		}
X		close(tmpfd);
X	    }
X	}
X
X	safecpy(cmd_buf, filexp(mailbox ?
X	    getval("MBOXSAVER",MBOXSAVER) :
X	    getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf);
X				/* format the command */
X	termlib_reset();
X	resetty();		/* make terminal behave */
X	if (doshell(use_pref?Nullch:SH,cmd_buf)) {
X	    termlib_init();
X	    fputs("Not saved",stdout);
X	} else {
X	    termlib_init();
X	    printf("%s to %s %s",
X	      there?"Appended":"Saved",
X	      mailbox?"mailbox":"file",
X	      s);
X	}
X	if (interactive)
X	    putchar('\n') FLUSH;
X	noecho();		/* make terminal do what we want */
X	crmode();
X    }
Xs_bomb:
X#ifdef USE_NNTP
X    if (chdir(spool)) {
X#else
X    if (chdir(spool) || chdir(ngdir)) {
X#endif
X	printf(nocd,ngdir) FLUSH;
X	sig_catcher(0);
X    }
X    return SAVE_DONE;
X}
X
Xint
Xcancel_article()
X{
X    char *ngs_buf;
X    char *from_buf;
X    char *reply_buf;
X    int myuid = getuid();
X    int r = -1;
X
X    if (artopen(art) == Nullfp) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\n\
XCancelling null articles is your idea of fun?  :-)\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs(nullart,stdout) FLUSH;
X#endif
X	return r;
X    }
X    reply_buf = fetchlines(art,REPLY_LINE);
X    from_buf = fetchlines(art,FROM_LINE);
X    ngs_buf = fetchlines(art,NGS_LINE);
X    if (!instr(from_buf,phostname,FALSE) ||
X	(!instr(from_buf,loginName,TRUE) &&
X	 !instr(reply_buf,loginName,TRUE) &&
X#ifdef NEWS_ADMIN
X	 myuid != newsuid &&
X#endif
X	 myuid != ROOTID ) ) {
X#ifdef DEBUG
X	    if (debug)
X		printf("\n%s@%s != %s\n",loginName,phostname,from_buf) FLUSH;
X#endif
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nYou can't cancel someone else's article\n",stdout)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\nNot your article\n",stdout) FLUSH;
X#endif
X    }
X    else {
X	tmpfp = fopen(headname,"w");	/* open header file */
X	if (tmpfp == Nullfp) {
X	    printf(cantcreate,headname) FLUSH;
X	    goto no_cancel;
X	}
X	interp(buf, (sizeof buf), getval("CANCELHEADER",CANCELHEADER));
X	fputs(buf,tmpfp);
X	fclose(tmpfp);
X	fputs("\nCanceling...\n",stdout) FLUSH;
X	r = doshell(sh,filexp(getval("CANCEL",CANCEL)));
X    }
Xno_cancel:
X    free(ngs_buf);
X    free(from_buf);
X    free(reply_buf);
X    return r;
X}
X
Xint
Xsupersede_article()		/* Supersedes: */
X{
X    char *ngs_buf;
X    char *from_buf;
X    char *reply_buf;
X    int myuid = getuid();
X    int r = -1;
X    bool incl_body = (*buf == 'Z');
X
X    if (artopen(art) == Nullfp) {
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\n\
XSuperceding null articles is your idea of fun?  :-)\n\
X",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs(nullart,stdout) FLUSH;
X#endif
X	return r;
X    }
X    reply_buf = fetchlines(art,REPLY_LINE);
X    from_buf = fetchlines(art,FROM_LINE);
X    ngs_buf = fetchlines(art,NGS_LINE);
X    if (!instr(from_buf,phostname,FALSE) ||
X	(!instr(from_buf,loginName,TRUE) &&
X	 !instr(reply_buf,loginName,TRUE) &&
X#ifdef NEWS_ADMIN
X	 myuid != newsuid &&
X#endif
X	 myuid != ROOTID ) ) {
X#ifdef DEBUG
X	    if (debug)
X		printf("\n%s@%s != %s\n",loginName,phostname,from_buf) FLUSH;
X#endif
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nYou can't supersede someone else's article\n",stdout)
X		  FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\nNot your article\n",stdout) FLUSH;
X#endif
X    }
X    else {
X	tmpfp = fopen(headname,"w");	/* open header file */
X	if (tmpfp == Nullfp) {
X	    printf(cantcreate,headname) FLUSH;
X	    goto no_commute;
X	}
X	interp(buf, (sizeof buf), getval("SUPERSEDEHEADER",SUPERSEDEHEADER));
X	fputs(buf,tmpfp);
X	if (incl_body && artfp != Nullfp) {
X	    parseheader(art);
X	    fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
X	    while (fgets(buf,LBUFLEN,artfp) != Nullch) {
X		fputs(buf,tmpfp);
X	    }
X	}
X	fclose(tmpfp);
X    	safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),
X		sizeof cmd_buf);
X    	invoke(cmd_buf,origdir);
X	r = 0;
X    }
Xno_commute:
X    free(ngs_buf);
X    free(from_buf);
X    free(reply_buf);
X    return r;
X}
X
Xvoid
Xreply()
X{
X    bool incl_body = (*buf == 'R');
X    char *maildoer = savestr(filexp(getval("MAILPOSTER",MAILPOSTER)));
X
X    artopen(art);
X    tmpfp = fopen(headname,"w");	/* open header file */
X    if (tmpfp == Nullfp) {
X	printf(cantcreate,headname) FLUSH;
X	goto no_reply;
X    }
X    interp(buf, (sizeof buf), getval("MAILHEADER",MAILHEADER));
X    fputs(buf,tmpfp);
X    if (!instr(maildoer,"%h",TRUE))
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\n%s\n(Above lines saved in file %s)\n",buf,headname)
X	      FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH;
X#endif
X    if (incl_body && artfp != Nullfp) {
X	interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID));
X	fprintf(tmpfp,"%s\n",buf);
X	parseheader(art);
X	fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
X	while (fgets(buf,LBUFLEN,artfp) != Nullch) {
X	    fprintf(tmpfp,"%s%s",indstr,buf);
X	}
X	fprintf(tmpfp,"\n");
X    }
X    fclose(tmpfp);
X    interp(cmd_buf, (sizeof cmd_buf), maildoer);
X    invoke(cmd_buf,origdir);
X    UNLINK(headname);		/* kill the header file */
Xno_reply:
X    free(maildoer);
X}
X
Xvoid
Xfollowup()
X{
X    bool incl_body = (*buf == 'F');
X    char hbuf[4*LBUFLEN];	/* four times the old size */
X    ART_NUM oldart = art;
X
X    if (!incl_body && art <= lastart) {
X	in_answer("\n\nAre you starting an unrelated topic? [ynq] ", 'F');
X	setdef(buf,"y");
X	if (*buf == 'q')  /*TODO: need to add 'h' also */
X	    return;
X	if (*buf != 'n')
X	    art = lastart + 1;
X    }
X    artopen(art);
X    tmpfp = fopen(headname,"w");
X    if (tmpfp == Nullfp) {
X	printf(cantcreate,headname) FLUSH;
X	art = oldart;
X	return;
X    }
X    interp(hbuf, (sizeof hbuf), getval("NEWSHEADER",NEWSHEADER));
X    fprintf(tmpfp,"%s",hbuf);
X    if (incl_body && artfp != Nullfp) {
X#ifdef VERBOSE
X	if (verbose)
X	    fputs("\n\
X(Be sure to double-check the attribution against the signature, and\n\
Xtrim the quoted article down as much as possible.)\n\
X",stdout) FLUSH;
X#endif
X	interp(buf, (sizeof buf), getval("ATTRIBUTION",ATTRIBUTION));
X	fprintf(tmpfp,"%s\n",buf);
X	parseheader(art);
X	fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
X	while (fgets(buf,LBUFLEN,artfp) != Nullch) {
X	    fprintf(tmpfp,"%s%s",indstr,buf);
X	}
X	fprintf(tmpfp,"\n");
X    }
X    fclose(tmpfp);
X    safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf);
X    invoke(cmd_buf,origdir);
X    UNLINK(headname);
X    art = oldart;
X}
X
Xvoid
Xinvoke(cmd,dir)
Xchar *cmd,*dir;
X{
X    char oldmode = mode;
X    if (chdir(dir)) {
X	printf(nocd,dir) FLUSH;
X	return;
X    }
X    termlib_reset();
X    mode = 'x';
X#ifdef VERBOSE
X    IF(verbose)
X	printf("\n(leaving cbreak mode; cwd=%s)\nInvoking command: %s\n\n",
X	    dir,cmd) FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	printf("\n(-cbreak; cwd=%s)\nInvoking: %s\n\n",dir,cmd) FLUSH;
X#endif
X    resetty();			/* make terminal well-behaved */
X    doshell(sh,cmd);		/* do the command */
X    noecho();			/* set no echo */
X    crmode();			/* and cbreak mode */
X#ifdef VERBOSE
X    IF(verbose)
X	fputs("\n(re-entering cbreak mode)\n",stdout) FLUSH;
X    ELSE
X#endif
X#ifdef TERSE
X	fputs("\n(+cbreak)\n",stdout) FLUSH;
X#endif
X    termlib_init();
X    mode = oldmode;
X#ifdef USE_NNTP
X    if (chdir(spool)) {
X#else
X    if (chdir(spool) || chdir(ngdir)) {
X#endif
X	printf(nocd,ngdir) FLUSH;
X	sig_catcher(0);
X    }
X}
X
X/*
X** cut_line() determines if a line is meant as a "cut here" marker.
X** Some examples that we understand:
X**
X**  BEGIN--cut here--cut here
X**
X**  ------------------ tear at this line ------------------
X**
X**  #----cut here-----cut here-----cut here-----cut here----#
X*/
Xbool
Xcut_line(str)
Xchar *str;
X{
X    char *cp, got_flag;
X    char word[80];
X    int  dash_cnt, equal_cnt, other_cnt;
X
X    /* Disallow any single-/double-quoted, parenthetical or c-commented
X    ** string lines.  Make sure it has the cut-phrase and at least six
X    ** '-'s or '='s.  If only four '-'s are present, check for a duplicate
X    ** of the cut phrase.  If over 20 unknown characters are encountered,
X    ** assume it isn't a cut line.  If we succeed, return TRUE.
X    */
X    for (cp = str, dash_cnt = equal_cnt = other_cnt = 0; *cp; cp++) {
X	switch (*cp) {
X	case '-':
X	    dash_cnt++;
X	    break;
X	case '=':
X	    equal_cnt++;
X	    break;
X	case '/':
X	    if(*(cp+1) != '*') {
X		break;
X	    }
X	case '"':
X	case '\'':
X	case '(':
X	case ')':
X	case '[':
X	case ']':
X	case '{':
X	case '}':
X	    return FALSE;
X	default:
X	    other_cnt++;
X	    break;
X	}
X    }
X    if (dash_cnt < 4 && equal_cnt < 6)
X	return FALSE;
X
X    got_flag = 0;
X
X    for (*(cp = word) = '\0'; *str; str++) {
X	if (islower(*str))
X	    *cp++ = *str;
X	else if (isupper(*str))
X	    *cp++ = tolower(*str);
X	else {
X	    if (*word) {
X		*cp = '\0';
X		switch (got_flag) {
X		case 2:
X		    if (!strcmp(word, "line")
X		     || !strcmp(word, "here"))
X			if ((other_cnt -= 4) <= 20)
X			    return TRUE;
X		    break;
X		case 1:
X		    if (!strcmp(word, "this")) {
X			got_flag = 2;
X			other_cnt -= 4;
X		    }
X		    else if (!strcmp(word, "here")) {
X			other_cnt -= 4;
X			if ((dash_cnt >= 6 || equal_cnt >= 6)
X			 && other_cnt <= 20)
X			    return TRUE;
X			dash_cnt = 6;
X			got_flag = 0;
X		    }
X		    break;
X		case 0:
X		    if (!strcmp(word, "cut")
X		     || !strcmp(word, "snip")
X		     || !strcmp(word, "tear")) {
X			got_flag = 1;
X			other_cnt -= strlen(word);
X		    }
X		    break;
X		}
X		*(cp = word) = '\0';
X	    }
X	}
X    } /* for *str */
X
X    return FALSE;
X}
END_OF_FILE
if test 20348 -ne `wc -c <'respond.c'`; then
    echo shar: \"'respond.c'\" unpacked with wrong size!
fi
# end of 'respond.c'
fi
if test -f 'rt-mt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rt-mt.c'\"
else
echo shar: Extracting \"'rt-mt.c'\" \(18249 characters\)
sed "s/^X//" >'rt-mt.c' <<'END_OF_FILE'
X/* $Id: rt-mt.c,v 3.0 1992/12/14 00:14:13 davison Trn $
X*/
X/* The authors make no claims as to the fitness or correctness of this software
X * for any use whatsoever, and it is provided as is. Any use of this software
X * is at the user's own risk. 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "intrp.h"
X#include "trn.h"
X#include "cache.h"
X#include "bits.h"
X#include "ng.h"
X#include "ngdata.h"
X#include "rcln.h"
X#include "util.h"
X#include "hash.h"
X#include "nntp.h"
X#include "rthread.h"
X#include "rt-process.h"
X#include "INTERN.h"
X#include "rt-mt.h"
X
Xextern HASHTABLE *msgid_hash;
Xextern bool try_mt;
X
X#ifndef USE_XTHREAD
Xstatic FILE *fp;
X#endif
Xstatic bool word_same, long_same;
Xstatic BMAP my_bmap, mt_bmap;
X
Xstatic char *strings = Nullch;
Xstatic WORD *author_cnts = 0;
Xstatic WORD *ids = 0;
X
Xstatic ARTICLE **article_array = 0;
Xstatic SUBJECT **subject_array = 0;
Xstatic char **author_array = 0;
X
Xstatic TOTAL total;
Xstatic PACKED_ROOT p_root;
Xstatic PACKED_ARTICLE p_article;
X
X/* Initialize our thread code by determining the byte-order of the thread
X** files and our own current byte-order.  If they differ, set flags to let
X** the read code know what we'll need to translate.
X*/
Xbool
Xmt_init()
X{
X    int i;
X#ifdef USE_XTHREAD
X    long size;
X#endif
X    bool success = TRUE;			/* I'm an optimist */
X
X    word_same = long_same = TRUE;
X#ifdef USE_XTHREAD
X    sprintf(ser_line, "XTHREAD DBINIT");
X    nntp_command(ser_line);
X    size = nntp_readcheck();
X    if (size < 0)
X	return FALSE;
X    size = nntp_read((char*)&mt_bmap, (long)sizeof (BMAP));
X    if (size >= sizeof (BMAP) - 1) {
X#else /* !USE_XTHREAD */
X    if ((fp = fopen(filexp(DBINIT), FOPEN_RB)) != Nullfp
X     && fread((char*)&mt_bmap, 1, sizeof (BMAP), fp) >= sizeof (BMAP) - 1) {
X#endif
X	if (mt_bmap.version != DB_VERSION) {
X	    printf("\nMthreads database is the wrong version -- ignoring it.\n")
X		FLUSH;
X	    return FALSE;
X	}
X	mybytemap(&my_bmap);
X	for (i = 0; i < sizeof (LONG); i++) {
X	    if (i < sizeof (WORD)) {
X		if (my_bmap.w[i] != mt_bmap.w[i]) {
X		    word_same = FALSE;
X		}
X	    }
X	    if (my_bmap.l[i] != mt_bmap.l[i]) {
X		long_same = FALSE;
X	    }
X	}
X    } else
X	success = FALSE;
X#ifdef USE_XTHREAD
X    while (nntp_read(ser_line, (long)sizeof ser_line))
X	;		/* trash any extraneous bytes */
X#else
X    if (fp != Nullfp)
X	fclose(fp);
X#endif
X    return success;
X}
X
X/* Open and process the data in the group's thread file.  Returns TRUE unless
X** we discovered a bogus thread file, destroyed the cache, and re-built it.
X*/
Xbool
Xmt_data()
X{
X    bool success = TRUE;
X#ifdef USE_XTHREAD		/* use remote thread file? */
X    long size;
X
X    sprintf(ser_line, "XTHREAD THREAD");
X    nntp_command(ser_line);
X    size = nntp_readcheck();
X    if (size < 0)
X	return TRUE;
X
X    printf("\nGetting thread file."), fflush(stdout);
X    if (nntp_read((char*)&total, (long)sizeof (TOTAL)) < sizeof (TOTAL))
X	goto exit;
X
X#else /* !USE_XTHREAD */
X    if ((fp = fopen(mt_name(ngname), FOPEN_RB)) == Nullfp)
X	return TRUE;
X    printf("\nReading thread file."), fflush(stdout);
X
X    if (fread((char*)&total, 1, sizeof (TOTAL), fp) < sizeof (TOTAL))
X	goto exit;
X
X#endif /* !USE_XTHREAD */
X
X    lp_bmap(&total.first, 4);
X    wp_bmap(&total.root, 5);
X    if (!total.root) {
X	tweak_data();
X	goto exit;
X    }
X    if (total.last > lastart)
X#ifdef USE_NNTP
X	total.last = lastart;
X#else
X	grow_cache(total.last);
X#endif
X
X    if (read_authors()
X     && read_subjects()
X     && read_roots()
X     && read_articles()
X     && read_ids())
X    {
X	tweak_data();
X	first_cached = absfirst;
X	last_cached = (total.last < absfirst ? absfirst-1: total.last);
X	cached_all_in_range = TRUE;
X	goto exit;
X    }
X    /* Something failed.  Safefree takes care of checking if some items
X    ** were already freed.  Any partially-allocated structures were freed
X    ** before we got here.  All other structures are cleaned up now.
X    */
X    close_cache();
X    safefree(&strings);
X    safefree((char**)&article_array);
X    safefree((char**)&subject_array);
X    safefree((char**)&author_array);
X    safefree((char**)&ids);
X    try_mt = FALSE;
X    build_cache();
X    try_mt = TRUE;
X    success = FALSE;
X
Xexit:
X#ifdef USE_XTHREAD
X    while (nntp_read(ser_line, (long)sizeof ser_line))
X	;		/* trash any extraneous bytes */
X#else
X    fclose(fp);
X#endif
X    return success;
X}
X
X#ifndef USE_XTHREAD
X/* Change a newsgroup name into the name of the thread data file.  We
X** subsitute any '.'s in the group name into '/'s (unless LONG_THREAD_NAMES
X** is defined), prepend the path, and append the '/.thread' or '.th' on to
X** the end.
X*/
Xstatic char *
Xmt_name(group)
Xchar *group;
X{
X#ifdef LONG_THREAD_NAMES
X    sprintf(buf, "%s/%s", threaddir, group);
X#else
X    register char *cp;
X
X    cp = strcpy(buf, threaddir) + strlen(threaddir);
X    *cp++ = '/';
X    strcpy(cp, group);
X    while ((cp = index(cp, '.')))
X	*cp = '/';
X    if (threaddir == spool)
X	strcat(buf, "/.thread");
X    else
X	strcat(buf, ".th");
X#endif
X    return buf;
X}
X#endif
X
Xstatic char *subject_strings, *string_end;
X
X/* The author information is an array of use-counts, followed by all the
X** null-terminated strings crammed together.  The subject strings are read
X** in at the same time, since they are appended to the end of the author
X** strings.
X*/
Xstatic int
Xread_authors()
X{
X    register int count;
X    register char *string_ptr, **author_ptr;
X
X    if (!read_item((char**)&author_cnts, (MEM_SIZE)total.author*sizeof (WORD)))
X	return 0;
X    safefree((char**)&author_cnts);   /* we don't need these */
X
X    if (!read_item(&strings, (MEM_SIZE)total.string1))
X	return 0;
X
X    string_ptr = strings;
X    string_end = string_ptr + total.string1;
X    if (string_end[-1] != '\0') {
X	/*error("first string table is invalid.\n");*/
X	return 0;
X    }
X
X    /* We'll use this array to point each article at its proper author
X    ** (the packed values were saved as indexes).
X    */
X    author_array = (char**)safemalloc(total.author * sizeof (char*));
X    author_ptr = author_array;
X
X    for (count = total.author; count; count--) {
X	if (string_ptr >= string_end)
X	    break;
X	*author_ptr++ = string_ptr;
X	string_ptr += strlen(string_ptr) + 1;
X    }
X    subject_strings = string_ptr;
X
X    if (count) {
X	/*error("author unpacking failed.\n");*/
X	return 0;
X    }
X    return 1;
X}
X
X/* The subject values consist of the crammed-together null-terminated strings
X** (already read in above) and the use-count array.  They were saved in the
X** order that the roots require while being unpacked.
X*/
Xstatic int
Xread_subjects()
X{
X    register int count;
X    register char *string_ptr;
X    register SUBJECT **subj_ptr;
X    WORD *subject_cnts;
X
X    if (!read_item((char**)&subject_cnts,
X		   (MEM_SIZE)total.subject * sizeof (WORD))) {
X	/* (Error already logged.) */
X	return 0;
X    }
X    free((char*)subject_cnts);		/* we don't need these */
X
X    /* Use this array when unpacking the article's subject offset. */
X    subject_array = (SUBJECT**)safemalloc(total.subject * sizeof (SUBJECT*));
X    subj_ptr = subject_array;
X
X    string_ptr = subject_strings;	/* string_end is already set */
X
X    for (count = total.subject; count; count--) {
X	int len;
X	ARTICLE arty;
X	if (string_ptr >= string_end)
X	    break;
X	len = strlen(string_ptr);
X	arty.subj = 0;
X	set_subj_line(&arty, string_ptr, len);
X	if (len == 72)
X	    arty.subj->flags |= SF_SUBJTRUNCED;
X	string_ptr += len + 1;
X	*subj_ptr++ = arty.subj;
X    }
X    if (count || string_ptr != string_end) {
X	/*error("subject data is invalid.\n");*/
X	return 0;
X    }
X    return 1;
X}
X
X/* Read in the packed root structures to set each subject's thread article
X** offset.  This gets turned into a real pointer later.
X*/
Xstatic int
Xread_roots()
X{
X    register int count, i;
X    register SUBJECT **subj_ptr;
X    int ret;
X
X    subj_ptr = subject_array;
X
X    for (count = total.root; count--; ) {
X#ifdef USE_XTHREAD
X	ret = nntp_read((char*)&p_root, (long)sizeof (PACKED_ROOT));
X#else
X	ret = fread((char*)&p_root, 1, sizeof (PACKED_ROOT), fp);
X#endif
X	if (ret != sizeof (PACKED_ROOT)) {
X	    /*error("failed root read -- %d bytes instead of %d.\n",
X		ret, sizeof (PACKED_ROOT));*/
X	    return 0;
X	}
X	wp_bmap(&p_root.articles, 3);	/* converts subject_cnt too */
X	if (p_root.articles < 0 || p_root.articles >= total.article) {
X	    /*error("root has invalid values.\n");*/
X	    return 0;
X	}
X	i = p_root.subject_cnt;
X	if (i <= 0 || (subj_ptr - subject_array) + i > total.subject) {
X	    /*error("root has invalid values.\n");*/
X	    return 0;
X	}
X	subj_ptr[i-1]->thread_link = subj_ptr[0];
X	while (i--) {
X	    union { ARTICLE *ap; int num; } uni;
X	    if (i)
X		subj_ptr[0]->thread_link = subj_ptr[1];
X	    subj_ptr[0]->flags &= ~SF_THREAD;
X	    uni.num = p_root.articles;
X	    (*subj_ptr++)->thread = uni.ap;
X	}
X    }
X    return 1;
X}
X
Xstatic bool invalid_data;
X
X/* A simple routine that checks the validity of the article's subject value.
X** A -1 means that it is NULL, otherwise it should be an offset into the
X** subject array we just unpacked.
X*/
Xstatic SUBJECT *
Xthe_subject(num)
XWORD num;
X{
X    if (num == -1)
X	return Nullsubj;
X    if (num < 0 || num >= total.subject) {
X	/*printf("Invalid subject in thread file: %d [%ld]\n", num, art_num);*/
X	invalid_data = TRUE;
X	return Nullsubj;
X    }
X    return subject_array[num];
X}
X
X/* Ditto for author checking. */
Xstatic char *
Xthe_author(num)
XWORD num;
X{
X    if (num == -1)
X	return Nullch;
X    if (num < 0 || num >= total.author) {
X	/*error("invalid author in thread file: %d [%ld]\n", num, art_num);*/
X	invalid_data = TRUE;
X	return Nullch;
X    }
X    return savestr(author_array[num]);
X}
X
X/* Our parent/sibling information is a relative offset in the article array.
X** zero for none.  Child values are always found in the very next array
X** element if child_cnt is non-zero.
X*/
Xstatic ARTICLE *
Xthe_article(relative_offset, num)
XWORD relative_offset;
Xint num;
X{
X    union { ARTICLE *ap; int num; } uni;
X
X    if (!relative_offset)
X	return Nullart;
X    num += relative_offset;
X    if (num < 0 || num >= total.article) {
X	/*error("invalid article offset in thread file.\n");*/
X	invalid_data = TRUE;
X	return Nullart;
X    }
X    uni.num = num+1;
X    return uni.ap;		/* slip them an offset in disguise */
X}
X
X/* Read the articles into their trees.  Point everything everywhere. */
Xstatic int
Xread_articles()
X{
X    register int count;
X    register ARTICLE *article, **art_ptr;
X    int ret;
X
X    /* Build an array to interpret interlinkages of articles. */
X    article_array = (ARTICLE**)safemalloc(total.article * sizeof (ARTICLE*));
X    art_ptr = article_array;
X
X    invalid_data = FALSE;
X    for (count = 0; count < total.article; count++) {
X#ifdef USE_XTHREAD
X	ret = nntp_read((char*)&p_article, (long)sizeof (PACKED_ARTICLE));
X#else
X	ret = fread((char*)&p_article, 1, sizeof (PACKED_ARTICLE), fp);
X#endif
X	if (ret != sizeof (PACKED_ARTICLE)) {
X	    /*error("failed article read -- %d bytes instead of %d.\n",
X		ret, sizeof (PACKED_ARTICLE));*/
X	    return 0;
X	}
X	lp_bmap(&p_article.num, 2);
X	wp_bmap(&p_article.subject, 8);
X
X#ifdef USE_NNTP
X	article = *art_ptr++ = allocate_article(p_article.num > lastart?
X						0 : p_article.num);
X#else
X	article = *art_ptr++ = allocate_article(p_article.num);
X#endif
X	article->date = p_article.date;
X#ifndef DBM_XREFS
X	if (olden_days < 2 && !(p_article.flags & HAS_XREFS))
X	    article->xrefs = nullstr;
X#endif
X	article->from = the_author(p_article.author);
X	article->parent = the_article(p_article.parent, count);
X	article->child1 = the_article(p_article.child_cnt ? 1 : 0, count);
X	article->sibling = the_article(p_article.sibling, count);
X	article->subj = the_subject(p_article.subject);
X	if (invalid_data) {
X	    /* (Error already logged.) */
X	    return 0;
X	}
X	/* This is ok because parent articles precede their children */
X	if (article->parent) {
X	    union { ARTICLE *ap; int num; } uni;
X	    uni.ap = article->parent;
X	    article->parent = article_array[uni.num-1];
X	}
X	if (article->subj) {
X	    if (!(article->flags & AF_MISSING)) {
X		article->flags |= AF_FROMTRUNCED | AF_THREADED
X		    | ((p_article.flags & ROOT_ARTICLE)? 0 : AF_HAS_RE);
X	    }
X	    /* Give this subject to any faked parent articles */
X	    while (article->parent && !article->parent->subj) {
X		article->parent->subj = article->subj;
X		article = article->parent;
X	    }
X	} else
X	    article->flags |= AF_FAKE;
X    }
X
X    /* We're done with most of the pointer arrays at this point. */
X    safefree((char**)&subject_array);
X    safefree((char**)&author_array);
X    safefree(&strings);
X
X    return 1;
X}
X
X/* Read the message-id strings and attach them to each article.  The data
X** format consists of the mushed-together null-terminated strings (a domain
X** name followed by all its unique-id prefixes) and then the article offsets
X** to which they belong.  The first domain name was omitted, as it is a null
X** domain for those truly weird message-id's without '@'s.
X*/
Xstatic int
Xread_ids()
X{
X    register ARTICLE *article;
X    register char *string_ptr;
X    register int i, count, len, len2;
X
X    if (!read_item(&strings, (MEM_SIZE)total.string2)
X     || !read_item((char**)&ids,
X		(MEM_SIZE)(total.article+total.domain+1) * sizeof (WORD))) {
X	return 0;
X    }
X    wp_bmap(ids, total.article + total.domain + 1);
X
X    string_ptr = strings;
X    string_end = string_ptr + total.string2;
X
X    if (string_end[-1] != '\0') {
X	/*error("second string table is invalid.\n");*/
X	return 0;
X    }
X
X    for (i = 0, count = total.domain + 1; count--; i++) {
X	if (i) {
X	    if (string_ptr >= string_end) {
X		/*error("error unpacking domain strings.\n");*/
X		return 0;
X	    }
X	    sprintf(buf, "@%s", string_ptr);
X	    len = strlen(string_ptr) + 1;
X	    string_ptr += len;
X	} else {
X	    *buf = '\0';
X	    len = 0;
X	}
X	if (ids[i] != -1) {
X	    if (ids[i] < 0 || ids[i] >= total.article) {
X		/*error("error in id array.\n");*/
X		return 0;
X	    }
X	    article = article_array[ids[i]];
X	    for (;;) {
X		if (string_ptr >= string_end) {
X		    /*error("error unpacking domain strings.\n");*/
X		    return 0;
X		}
X		len2 = strlen(string_ptr);
X		article->msgid = safemalloc(len2 + len + 2 + 1);
X		sprintf(article->msgid, "<%s%s>", string_ptr, buf);
X		string_ptr += len2 + 1;
X		if (msgid_hash)
X		{
X		    HASHDATUM data;
X		    if ((article->flags & AF_TMPMEM) == AF_TMPMEM) {
X			data.dat_ptr = (char*)article;
X			data.dat_len = 0;
X		    } else {
X			data.dat_ptr = Nullch;
X			data.dat_len = article_num(article);
X		    }
X		    hashstore(msgid_hash, article->msgid, len2+len+2, data);
X		}
X		if (++i >= total.article + total.domain + !count) {
X		    /*error("overran id array unpacking domains.\n");*/
X		    return 0;
X		}
X		if (ids[i] != -1) {
X		    if (ids[i] < 0 || ids[i] >= total.article)
X			return 0;
X		    article = article_array[ids[i]];
X		} else
X		    break;
X	    }
X	}
X    }
X    safefree((char**)&ids);
X    safefree(&strings);
X
X    return 1;
X}
X
X/* And finally, turn all the links into real pointers and mark missing
X** articles as read.
X*/
Xstatic void
Xtweak_data()
X{
X    register int count;
X    register ARTICLE *ap, **art_ptr;
X    register SUBJECT *sp;
X    register ART_NUM i;
X    union { ARTICLE *ap; int num; } uni;
X
X    for (sp = first_subject; sp; sp = sp->next) {
X	uni.ap = sp->thread;
X	sp->thread = article_array[uni.num];
X	sp->thread->subj->flags |= SF_THREAD;
X    }
X    art_ptr = article_array;
X    for (count = total.article; count--; ) {
X	ap = *art_ptr++;
X	if (ap->child1) {
X	    uni.ap = ap->child1;
X	    ap->child1 = article_array[uni.num-1];
X	}
X	if (ap->sibling) {
X	    uni.ap = ap->sibling;
X	    ap->sibling = article_array[uni.num-1];
X	}
X	if (!(ap->flags & AF_MISSING))
X	    cache_article(ap);
X    }
X
X    /* Mark any missing articles as read */
X    for (i = absfirst, ap = article_ptr(i); i <= total.last; i++, ap++) {
X	if ((ap->flags & (AF_CACHED|AF_MISSING)) == AF_CACHED)
X	    check_poster(ap);
X	else
X	    onemissing(ap);
X    }
X    safefree((char**)&article_array);
X}
X
X/* A shorthand for reading a chunk of the file into a malloc'ed array.
X*/
Xstatic int
Xread_item(dest, len)
Xchar **dest;
XMEM_SIZE len;
X{
X    long ret;
X
X    *dest = safemalloc(len);
X#ifdef USE_XTHREAD
X    ret = nntp_read(*dest, (long)len);
X#else
X    ret = fread(*dest, 1, (int)len, fp);
X#endif
X    if (ret != len) {
X	free(*dest);
X	*dest = Nullch;
X	return 0;
X    }
X    putchar('.'), fflush(stdout);
X    return 1;
X}
X
X/* Free some memory if it hasn't already been freed.
X*/
Xstatic void
Xsafefree(pp)
Xchar **pp;
X{
X    if (*pp) {
X	free(*pp);
X	*pp = Nullch;
X    }
X}
X
X/* Determine this machine's byte map for WORDs and LONGs.  A byte map is an
X** array of BYTEs (sizeof (WORD) or sizeof (LONG) of them) with the 0th BYTE
X** being the byte number of the high-order byte in my <type>, and so forth.
X*/
Xstatic void
Xmybytemap(map)
XBMAP *map;
X{
X    union {
X	BYTE b[sizeof (LONG)];
X	WORD w;
X	LONG l;
X    } u;
X    register BYTE *mp;
X    register int i, j;
X
X    mp = &map->w[sizeof (WORD)];
X    u.w = 1;
X    for (i = sizeof (WORD); i > 0; i--) {
X	for (j = 0; j < sizeof (WORD); j++) {
X	    if (u.b[j] != 0)
X		break;
X	}
X	if (j == sizeof (WORD))
X	    goto bad_news;
X	*--mp = j;
X	while (u.b[j] != 0 && u.w)
X	    u.w <<= 1;
X    }
X
X    mp = &map->l[sizeof (LONG)];
X    u.l = 1;
X    for (i = sizeof (LONG); i > 0; i--) {
X	for (j = 0; j < sizeof (LONG); j++) {
X	    if (u.b[j] != 0)
X		break;
X	}
X	if (j == sizeof (LONG)) {
X	  bad_news:
X	    /* trouble -- set both to *something* consistent */
X	    for (j = 0; j < sizeof (WORD); j++)
X		map->w[j] = j;
X	    for (j = 0; j < sizeof (LONG); j++)
X		map->l[j] = j;
X	    return;
X	}
X	*--mp = j;
X	while (u.b[j] != 0 && u.l)
X	    u.l <<= 1;
X    }
X}
X
X/* Transform each WORD's byte-ordering in a buffer of the designated length.
X*/
Xstatic void
Xwp_bmap(buf, len)
XWORD *buf;
Xint len;
X{
X    union {
X	BYTE b[sizeof (WORD)];
X	WORD w;
X    } in, out;
X    register int i;
X
X    if (word_same)
X	return;
X
X    while (len--) {
X	in.w = *buf;
X	for (i = 0; i < sizeof (WORD); i++)
X	    out.b[my_bmap.w[i]] = in.b[mt_bmap.w[i]];
X	*buf++ = out.w;
X    }
X}
X
X/* Transform each LONG's byte-ordering in a buffer of the designated length.
X*/
Xstatic void
Xlp_bmap(buf, len)
XLONG *buf;
Xint len;
X{
X    union {
X	BYTE b[sizeof (LONG)];
X	LONG l;
X    } in, out;
X    register int i;
X
X    if (long_same)
X	return;
X
X    while (len--) {
X	in.l = *buf;
X	for (i = 0; i < sizeof (LONG); i++)
X	    out.b[my_bmap.l[i]] = in.b[mt_bmap.l[i]];
X	*buf++ = out.l;
X    }
X}
END_OF_FILE
if test 18249 -ne `wc -c <'rt-mt.c'`; then
    echo shar: \"'rt-mt.c'\" unpacked with wrong size!
fi
# end of 'rt-mt.c'
fi
echo shar: End of archive 7 \(of 12\).
cp /dev/null ark7isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 12 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
