REPOST v23i068: TRN, version of RN that follows conversation threads, Part09/14

Rich Salz rsalz at bbn.com
Thu Dec 6 08:17:17 AEST 1990


Submitted-by: Wayne Davison <davison at dri.com>
Posting-number: Volume 23, Issue 68
Archive-name: trn/part09

#! /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:  makekit newsetup.1 newsetup.SH newsgroups.1 newsgroups.SH
#   newsnews.SH ng.c
# Wrapped by rsalz at litchi.bbn.com on Wed Dec  5 16:14:50 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."'
if test -f 'makekit' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makekit'\"
else
  echo shar: Extracting \"'makekit'\" \(745 characters\)
  sed "s/^X//" >'makekit' <<'END_OF_FILE'
X#!/bin/sh
X# $Header: makekit,v 4.3 85/05/01 11:42:38 lwall Exp $
X#
X# $Log:	makekit,v $
X# Revision 4.3  85/05/01  11:42:38  lwall
X# Baseline for release with 4.3bsd.
X# 
X
Xnumkits=$#
Xfor kitlist in $*; do
X    kit=`basename $kitlist .list`
X    kitnum=`expr "$kit" : 'kit\([0-9][0-9]*\)'`
X    echo "*** Making $kit ***"
X    kitleader "$kit" "$kitnum" "$numkits"
X    for file in `/bin/cat $kitlist`; do
X	echo $file
X	echo "echo Extracting $file" >> $kit
X	if egrep '^\.$' $file; then
X	    echo "sed >$file <<'!STUFFY!FUNK!' -e 's/X//'" >> $kit
X	    sed <$file >>$kit -e 's/^/X/'
X	else
X	    echo "cat >$file <<'!STUFFY!FUNK!'" >> $kit
X	    /bin/cat $file >> $kit
X	fi
X	echo "!STUFFY!FUNK!" >> $kit
X    done
X    kittrailer "$kit" "$kitnum" "$numkits"
Xdone
END_OF_FILE
  if test 745 -ne `wc -c <'makekit'`; then
    echo shar: \"'makekit'\" unpacked with wrong size!
  fi
  chmod +x 'makekit'
  # end of 'makekit'
fi
if test -f 'newsetup.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newsetup.1'\"
else
  echo shar: Extracting \"'newsetup.1'\" \(1599 characters\)
  sed "s/^X//" >'newsetup.1' <<'END_OF_FILE'
X''' $Header: newsetup.1,v 4.3 85/05/01 11:43:22 lwall Exp $
X''' 
X''' $Log:	newsetup.1,v $
X''' Revision 4.3  85/05/01  11:43:22  lwall
X''' Baseline for release with 4.3bsd.
X''' 
X''' 
X.de Sh
X.br
X.ne 5
X.PP
X\fB\\$1\fR
X.PP
X..
X.de Sp
X.if t .sp .5v
X.if n .sp
X..
X'''
X'''     Set up \*(-- to give an unbreakable dash;
X'''     string Tr holds user defined translation string.
X'''     Bell System Logo is used as a dummy character.
X'''
X.ie n \{\
X.tr \(bs-\*(Tr
X.ds -- \(bs-
X.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
X.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
X.ds L" ""
X.ds R" ""
X.ds L' '
X.ds R' '
X'br\}
X.el\{\
X.ds -- \(em\|
X.tr \*(Tr
X.ds L" ``
X.ds R" ''
X.ds L' `
X.ds R' '
X'br\}
X.TH NEWSETUP 1 LOCAL
X.SH NAME
Xnewsetup - a program to set up a .newsrc file
X.SH SYNOPSIS
X.B newsetup
X.SH DESCRIPTION
XThe
X.I newsetup
Xprogram creates a new .newsrc file containing all of the currently active
Xnewsgroups.
XIt tries to put them in a reasonable order, i.e. local newsgroups earlier,
Xbut you'll probably want to change the ordering anyway (if you use
X.IR rn )
Xin order to put interesting newsgroups first.
XIf you already have a .newsrc, it will be backed up with the name
X\*(L".oldnewsrc\*(R".
X.SH ENVIRONMENT
X.IP DOTDIR 8
XWhere to put your .newsrc, if not in your home directory.
X.Sp
XDefault: $HOME
X.IP HOME 8
XYour home directory.
X.Sp
XDefault: $LOGDIR
X.IP LOGDIR 8
XYour home directory if HOME is undefined.
X.SH FILES
X/usr/lib/news/active or a reasonable facsimile
X.br
X${DOTDIR-{$HOME-$LOGDIR}}/.newsrc
X.SH SEE ALSO
Xrn(1), newsrc(5)
X.SH DIAGNOSTICS
X.SH BUGS
END_OF_FILE
  if test 1599 -ne `wc -c <'newsetup.1'`; then
    echo shar: \"'newsetup.1'\" unpacked with wrong size!
  fi
  # end of 'newsetup.1'
fi
if test -f 'newsetup.SH' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newsetup.SH'\"
else
  echo shar: Extracting \"'newsetup.SH'\" \(5105 characters\)
  sed "s/^X//" >'newsetup.SH' <<'END_OF_FILE'
Xcase $CONFIG in
X    '') . ./config.sh ;;
Xesac
Xecho "Extracting newsetup (with variable substitutions)"
X$spitshell >newsetup <<!GROK!THIS!
X$startsh
X
X# $Header: newsetup.SH,v 4.3.2.7 90/03/17 20:34:48 sob Exp $
X# 
X# $Log:	newsetup.SH,v $
X# Revision 4.3.2.7  90/03/17  20:34:48  sob
X# Remove the /tmp/n.* files created when generating the .newsrc.
X# 
X# Revision 4.3.2.6  90/03/17  17:26:42  sob
X# Some versions of sed only take 9 wfiles.
X# 
X# Revision 4.3.2.5  89/11/28  01:15:32  sob
X# Fixed a bug that caused the active file to be removed on non-NNTP-based
X# systems.
X# 
X# Revision 4.3.2.4  89/11/28  00:31:20  sob
X# Changed news.announce.newuser to news.announce.newusers.
X# 
X# Revision 4.3.2.3  89/11/08  01:13:15  sob
X# Finished modifications to make work with RN and RRN
X#
X# Revision 4.3.2.2  89/11/07  23:25:50  sob
X#
X# Added support such that RN and RRN can be made from the same sources.
X# 
X# Revision 4.3.1.2  86/09/05  15:41:04  lwall
X# Changes for newsgroup renaming.
X# 
X# Revision 4.3.1.1  85/05/10  11:35:43  lwall
X# Branch for patches.
X# 
X# Revision 4.3  85/05/01  11:43:05  lwall
X# Baseline for release with 4.3bsd.
X# 
X
Xexport PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)
X
X: syntax: newsetup
X
X: System dependencies
X: You will want to change the definitions below to reflect the distribution
X: areas around you.  If you have more areas than this you will need to modify
X: the sed below.
X
Xlocorg="$locpref"
Xorganization="$orgpref"
Xcity="$citypref"
Xstate="$statepref"
Xcntry="$cntrypref"
Xcont="$contpref"
X#NORMALactive="${active-/usr/lib/news/active}"
X#NNTPactive="/tmp/active.\$\$"
X
Xdotdir="\${DOTDIR-\${HOME-\$LOGDIR}}"
X$rm -f \$dotdir/.oldnewsrc
X$echo "Creating .newsrc in \$dotdir to be used by news programs."
X#NNTPrnlib=$rnlib
X#NNTPcase \$rnlib in
X#NNTP~*) rnlib=\`$filexp \$rnlib\` ;;
X#NNTPesac
X#NNTP\$rnlib/getactive \$active
X#NORMALcase \$active in
X#NORMAL~*) active=\`$filexp \$active\` ;;
X#NORMALesac
X
X: NOTE: SED WILL NOT TAKE MORE THAN 9 WFILES, SO BEWARE   
X
X$sort <\$active | $sed >/tmp/n.tmp\$\$	\\
X	-e 's/^\([^ ]*\) .*\$/\1:/'	\\
X	-e '/^control:/{'		\\
X 	-e "  s/:/!/"	\\
X	-e "  w /tmp/n.test\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e '/^junk:/{'			\\
X 	-e "  s/:/!/"	\\
X	-e "  w /tmp/n.test\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e '/test:/{'			\\
X 	-e "  s/:/!/"	\\
X	-e "  w /tmp/n.test\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^\$locorg\./{"		\\
X	-e "  w /tmp/n.\$locorg\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^\$organization\./{"	\\
X	-e "  w /tmp/n.\$organization\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^\$city\./{"		\\
X	-e "  w /tmp/n.\$city\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^\$cntry\./{" 		\\
X	-e "  w /tmp/n.\$cntry\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^\$state\./{" 		\\
X	-e "  w /tmp/n.\$state\$\$"	\\
X	-e '  d'			\\
X	-e '}'
X
X$sed </tmp/n.tmp\$\$ >/tmp/n.local\$\$	\\
X	-e "/^\$cont\./{" 		\\
X	-e "  w /tmp/n.\$cont\$\$"	\\
X	-e '  d'			\\
X	-e '}'
X
X$sed </tmp/n.tmp\$\$ >/tmp/n.local\$\$	\\
X	-e "/^to\./{"			\\
X	-e "  s/:/!/"	\\
X	-e "  w /tmp/n.to\$\$"		\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^comp\./{"			\\
X	-e "  w /tmp/n.comp\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^news\./{"			\\
X	-e "  w /tmp/n.news\$\$"	\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^rec\./{"			\\
X	-e "  w /tmp/n.rec\$\$"		\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^sci\./{"			\\
X	-e "  w /tmp/n.sci\$\$"		\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/^soc\./{"			\\
X	-e "  w /tmp/n.soc\$\$"		\\
X	-e '  d'			\\
X	-e '}'				\\
X	-e "/\./{"			\\
X	-e "  w /tmp/n.misc\$\$"	\\
X	-e '  d'			\\
X	-e '}'
X
X
Xif $test -s \$dotdir/.newsrc ; then
X    $echo "Saving your current .newsrc as .oldnewsrc..."
X    $mv -f \$dotdir/.newsrc \$dotdir/.oldnewsrc
Xfi
X
X: newsrc order determined here
X
X$sed <\$active '
X	/^to\./d
X	s/ .*//
X	s/^/ /
X	s/^ '\$locorg'\./01&/
X	s/^ '\$organization'\./02&/
X	s/^ '\$city'\./03&/
X	s/^ '\$state'\./04&/
X	s/^ '\$cntry'\./05&/
X	s/^ '\$cont'\./06&/
X	s/^ news\./07&/
X	s/^ comp\./08&/
X	s/^ sci\./09&/
X	s/^ rec\./10&/
X	s/^ soc\./11&/
X	s/^ talk\./13&/
X	s/^ control\$/14&/
X	s/^ junk\$/14&/
X	s/^ test\$/14&/
X	/\.test\$/s/^[0-9]*/14/
X	s/^ .*\./12&/
X	s/^ /00&/
X' |
X$sort -u |
X$sed '
X	/^14 /!s/\$/:/
X	/^14 /s/\$/!/
X	s/^[0-9][0-9] //
X' >\$dotdir/.newsrc
X
X#NNTP$rm -f \$active
X$rm -f /tmp/n.misc\$\$ /tmp/n.sci\$\$ /tmp/n.soc\$\$ /tmp/n.news\$\$ /tmp/n.comp\$\$
X$rm -f /tmp/n.\$cont\$\$ /tmp/n.\$city\$\$ /tmp/n.\$cntry\$\$ /tmp/n.\$state\$\$
X$rm -f /tmp/n.to\$\$ /tmp/n.\$organization\$\$ /tmp/n.\$locorg\$\$ /tmp/n.test\$\$
X$rm -f /tmp/n.tmp\$\$ /tmp/n.local\$\$ /tmp/n.rec\$\$
X
X$cat <<'EOH'
XDone.
X
XIf you have never used the news system before, you may find the articles
Xin news.announce.newusers to be helpful.  There is also a manual entry for rn.
X
XTo get rid of newsgroups you aren't interested in, use the 'u' command.
XType h for help at any time while running rn.
XEOH
X!GROK!THIS!
Xcase "$isrrn" in
Xdefine)  sed < newsetup -e '/^#NNTP/s/^#NNTP//' -e '/^#NORMAL/d' > newsetup.new ;;
X*) sed < newsetup -e '/^#NNTP/d' -e '/^#NORMAL/s/^#NORMAL//' > newsetup.new ;;
Xesac
Xmv newsetup.new newsetup
X$eunicefix newsetup
Xchmod 755 newsetup
END_OF_FILE
  if test 5105 -ne `wc -c <'newsetup.SH'`; then
    echo shar: \"'newsetup.SH'\" unpacked with wrong size!
  fi
  chmod +x 'newsetup.SH'
  # end of 'newsetup.SH'
fi
if test -f 'newsgroups.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newsgroups.1'\"
else
  echo shar: Extracting \"'newsgroups.1'\" \(1706 characters\)
  sed "s/^X//" >'newsgroups.1' <<'END_OF_FILE'
X''' $Header: newsgroups.1,v 4.3 85/05/01 11:43:32 lwall Exp $
X''' 
X''' $Log:	newsgroups.1,v $
X''' Revision 4.3  85/05/01  11:43:32  lwall
X''' Baseline for release with 4.3bsd.
X''' 
X''' 
X.de Sh
X.br
X.ne 5
X.PP
X\fB\\$1\fR
X.PP
X..
X.de Sp
X.if t .sp .5v
X.if n .sp
X..
X'''
X'''     Set up \*(-- to give an unbreakable dash;
X'''     string Tr holds user defined translation string.
X'''     Bell System Logo is used as a dummy character.
X'''
X.ie n \{\
X.tr \(bs-\*(Tr
X.ds -- \(bs-
X.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
X.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
X.ds L" ""
X.ds R" ""
X.ds L' '
X.ds R' '
X'br\}
X.el\{\
X.ds -- \(em\|
X.tr \*(Tr
X.ds L" ``
X.ds R" ''
X.ds L' `
X.ds R' '
X'br\}
X.TH NEWSGROUPS 1 LOCAL
X.SH NAME
Xnewsgroups - a program to list unsubscribed newsgroups.
X.SH SYNOPSIS
X.B newsgroups pattern flag
X.SH DESCRIPTION
XThe
X.I newsgroups
Xprogram compares your .newsrc file with the file of active newsgroups,
Xand prints a list of unsubscribed newsgroups matching pattern.
XIf the second argument \*(L"flag\*(R" is present, only newsgroups not
Xfound in your .newsrc are listed, and the display is not paged.
XIf the second argument is missing, the display is paged, and an additional
Xlist of unsubscribed newsgroups occurring in your .newsrc is printed.
X.SH ENVIRONMENT
X.IP DOTDIR 8
XWhere to find your .newsrc, if not in your home directory.
X.Sp
XDefault: $HOME
X.IP HOME 8
XYour home directory.
X.Sp
XDefault: $LOGDIR
X.IP LOGDIR 8
XYour home directory if HOME is undefined.
X.SH FILES
X/usr/lib/news/active or a reasonable facsimile
X.br
X${DOTDIR-{$HOME-$LOGDIR}}/.newsrc
X.SH SEE ALSO
Xrn(1), newsrc(5)
X.SH DIAGNOSTICS
X.SH BUGS
XThe flag argument is a kludge.
END_OF_FILE
  if test 1706 -ne `wc -c <'newsgroups.1'`; then
    echo shar: \"'newsgroups.1'\" unpacked with wrong size!
  fi
  # end of 'newsgroups.1'
fi
if test -f 'newsgroups.SH' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newsgroups.SH'\"
else
  echo shar: Extracting \"'newsgroups.SH'\" \(2710 characters\)
  sed "s/^X//" >'newsgroups.SH' <<'END_OF_FILE'
Xcase $CONFIG in
X    '') . ./config.sh ;;
Xesac
Xecho "Extracting newsgroups (with variable substitutions)"
X$spitshell >newsgroups <<!GROK!THIS!
X$startsh
X# $Header: newsgroups.SH,v 4.3.2.7 90/04/23 19:30:41 sob Exp $
X# 
X# $Log:	newsgroups.SH,v $
X# Revision 4.3.2.7  90/04/23  19:30:41  sob
X# Extra space removed to make second part of newsgroups work.
X# Sigh.
X# 
X# Revision 4.3.2.6  90/04/06  20:35:37  sob
X# Added fixes for SCO Xenix sent by ronald at robobar.co.uk.
X# 
X# Revision 4.3.2.5  90/03/17  17:27:00  sob
X# System V sed and BSD sed don't work quite alike. This change uses a
X# syntax that is common between them.
X# 
X# Revision 4.3.2.4  89/11/08  01:13:35  sob
X# Finished modifications to make work with RN and RRN
X# 
X# Revision 4.3.2.3  89/11/07  22:58:04  sob
X# Added final changes to allow rn and rrn to be built from same sources.
X# 
X# Revision 4.3.2.2  89/11/07  22:52:22  sob
X# Added prelimiary support to allow both rrn and rn to be built from same
X# sources.
X# 
X# Revision 4.3.2.1  89/11/06  01:04:39  sob
X# Added RRN support from NNTP 1.5
X# 
X# Revision 4.3  85/05/01  11:43:27  lwall
X# Baseline for release with 4.3bsd.
X# 
X
Xexport PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)
X
X: syntax: newsgroups [pattern] [pipeflag]
X
X: System Dependencies
X
X: You might want to change pager to a "make column" program if you have one.
X: On the other hand, if your kernel does paging, cat would probably do.
Xpager="${pager-/usr/ucb/more}"
X#NORMALactive="${active-/usr/lib/news/active}"
X
X#NORMALcase \$active in
X#NORMAL~*) active=\`$filexp \$active\` ;;
X#NORMALesac
X
X#NNTPactive="/tmp/active.\$\$"
X
X: End of system dependencies, hopefully
X
X#NNTP$rnlib/getactive \$active
X
Xif $test \$# -ge 2 ; then
X    pager=$cat
Xelse
X    $echo "Completely unsubscribed newsgroups:"
Xfi
X
Xdotdir=\${DOTDIR-\${HOME-\$LOGDIR}}
X
X: Throwing .newsrc into the pot twice is a lovely hack to prevent
X: bogus newsgroups from showing up as unsubscribed.
X
X$cat \$dotdir/.newsrc \$dotdir/.newsrc \$active | \\
X$sed -n	-e '/^options/d' \\
X	-e '/^[	 ]/d' \\
X	-e '/^control/d' \\
X	-e '/^to\./d' \\
X	-e 's/^\([^ !:]*\)[ !:].*\$/\1/' \\
X	-e "/.*\$1/p" | \\
X$sort | $uniq -u | \$pager
Xif $test \$# -ge 2 ; then
X    exit
Xfi
X$echo $n "[Type return to continue] $c"
Xread tmp
X$echo ""
X$echo "Unsubscribed but mentioned in .newsrc:"
X$sed -n < \$dotdir/.newsrc \\
X	-e "/\$1.*!/"'s/^\([^!]*\)!.*\$/\1/p' | \\
X$sort | \$pager
X!GROK!THIS!
Xcase "$isrrn" in
Xdefine)  sed < newsgroups -e '/^#NNTP/s/^#NNTP//' -e '/^#NORMAL/d' > newsgroups.new ;;
X*) sed < newsgroups -e '/^#NNTP/d' -e '/^#NORMAL/s/^#NORMAL//' > newsgroups.new ;;
Xesac
Xmv newsgroups.new newsgroups
X$eunicefix newsgroups
Xchmod 755 newsgroups
END_OF_FILE
  if test 2710 -ne `wc -c <'newsgroups.SH'`; then
    echo shar: \"'newsgroups.SH'\" unpacked with wrong size!
  fi
  chmod +x 'newsgroups.SH'
  # end of 'newsgroups.SH'
fi
if test -f 'newsnews.SH' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'newsnews.SH'\"
else
  echo shar: Extracting \"'newsnews.SH'\" \(1307 characters\)
  sed "s/^X//" >'newsnews.SH' <<'END_OF_FILE'
Xcase $CONFIG in
X    '') . ./config.sh ;;
Xesac
Xecho "Extracting newsnews (with variable substitutions)"
Xcat >newsnews <<!GROK!THIS!
X			*** NEWS NEWS ***
X
XWelcome to trn.  There are more options to trn than you want to think about,
Xso we won't list them here.  If you want to find out about them, read the
Xtrn manual page.  There are some important things to remember, though:
X
X  * Trn is an extention to rn.  Where possible, the command syntax is the same.
X  * To access all the new features, specify the options -x and -X.  These
X    options MAY be on by default, but it won't hurt to be redundant.
X  * At ANY prompt in trn, you may type 'h' for help.  There are many different
X    help menus, depending on where you are.  Typing <esc>h in the
X    middle of a multi-character command will list escape substitutions.
X  * Typing a space to any prompt means to do the normal thing.  You could
X    spend all day reading news and never hit anything but the space bar.
X
XThis particular message comes from $rnlib/newsnews.  You will only
Xsee it once.  Your news administrator should feel free to substitute his or
Xher own message whenever something new happens to trn, and then the file
Xwill again be displayed, just once for each person.
X
XWayne Davison	davison%drivax at uunet.uu.net
X!GROK!THIS!
X$eunicefix newsnews
END_OF_FILE
  if test 1307 -ne `wc -c <'newsnews.SH'`; then
    echo shar: \"'newsnews.SH'\" unpacked with wrong size!
  fi
  chmod +x 'newsnews.SH'
  # end of 'newsnews.SH'
fi
if test -f 'ng.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ng.c'\"
else
  echo shar: Extracting \"'ng.c'\" \(34823 characters\)
  sed "s/^X//" >'ng.c' <<'END_OF_FILE'
X/* $Header: ng.c,v 4.3.3.1 90/07/21 20:27:17 davison Trn $
X *
X * $Log:	ng.c,v $
X * Revision 4.3.3.1  90/07/21  20:27:17  davison
X * Initial Trn Release
X * 
X * Revision 4.3.2.7  90/04/21  14:44:23  sob
X * Revised previous patch insure that it does not decrement below zero.
X * 
X * Revision 4.3.2.6  90/03/22  23:04:49  sob
X * Fixes provided by Wayne Davison <drivax!davison>
X * 
X * Revision 4.3.2.5  89/12/09  01:18:42  sob
X * Fixed a bad call to nntpopen().
X * 
X * Revision 4.3.2.4  89/11/28  01:51:20  sob
X * Removed redundant #include directive.
X * 
X * Revision 4.3.2.3  89/11/27  01:31:03  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.2  89/11/26  22:53:35  sob
X * Add new patches to make RRN be faster.
X * 
X * Revision 4.3.2.1  89/11/06  00:54:27  sob
X * Added RRN support from NNTP 1.5
X * 
X * Revision 4.3.1.6  85/09/10  11:03:42  lwall
X * Improved %m in in_char().
X * 
X * Revision 4.3.1.5  85/09/05  12:34:37  lwall
X * Catchup command could make unread article count too big.
X * 
X * Revision 4.3.1.4  85/07/23  18:19:46  lwall
X * Added MAILCALL environment variable.
X * 
X * Revision 4.3.1.3  85/05/16  16:48:09  lwall
X * Fixed unsubsubscribe.
X * 
X * Revision 4.3.1.2  85/05/13  09:29:28  lwall
X * Added CUSTOMLINES option.
X * 
X * Revision 4.3.1.1  85/05/10  11:36:00  lwall
X * Branch for patches.
X * 
X * Revision 4.3  85/05/01  11:43:43  lwall
X * Baseline for release with 4.3bsd.
X * 
X */
X
X#include "EXTERN.h"
X#include "common.h"
X#include "rn.h"
X#include "term.h"
X#include "final.h"
X#include "util.h"
X#include "artsrch.h"
X#include "cheat.h"
X#include "help.h"
X#include "kfile.h"
X#include "rcstuff.h"
X#include "head.h"
X#include "bits.h"
X#include "art.h"
X#include "artio.h"
X#include "ngstuff.h"
X#include "intrp.h"
X#include "respond.h"
X#include "ngdata.h"
X#include "backpage.h"
X#include "rcln.h"
X#include "last.h"
X#include "search.h"
X#ifdef SERVER
X#include "server.h"
X#endif
X#ifdef USETHREADS
X#include "rthreads.h"
X#endif
X#include "uudecode.h"
X#include "INTERN.h"
X#include "ng.h"
X#include "artstate.h"			/* somebody has to do it */
X
X/* art_switch() return values */
X
X#define AS_NORM 0
X#define AS_INP 1
X#define AS_ASK 2
X#define AS_CLEAN 3
X
XART_NUM recent_art = -1;	/* previous article # for '-' command */
XART_NUM curr_art = -1;		/* current article # */
Xint exit_code = NG_NORM;
X
Xvoid
Xng_init()
X{
X
X#ifdef KILLFILES
X    open_kfile(KF_GLOBAL);
X#endif
X#ifdef CUSTOMLINES
X    init_compex(&hide_compex);
X    init_compex(&page_compex);
X#endif
X}
X
X/* do newsgroup on line ng with name ngname */
X
X/* assumes that we are chdir'ed to SPOOL, and assures that that is
X * still true upon return, but chdirs to SPOOL/ngname in between
X *
X * If you can understand this routine, you understand most of the program.
X * The basic structure is:
X *	for each desired article
X *		for each desired page
X *			for each line on page
X *				if we need another line from file
X *					get it
X *					if it's a header line
X *						do special things
X *				for each column on page
X *					put out a character
X *				end loop
X *			end loop
X *		end loop
X *	end loop
X *
X *	(Actually, the pager is in another routine.)
X *
X * The chief problem is deciding what is meant by "desired".  Most of
X * the messiness of this routine is due to the fact that people want
X * to do unstructured things all the time.  I have used a few judicious
X * goto's where I thought it improved readability.  The rest of the messiness
X * arises from trying to be both space and time efficient.  Have fun.
X */
X
Xint
Xdo_newsgroup(start_command)
Xchar *start_command;			/* command to fake up first */
X{
X#ifdef SERVER
X    char ser_line[256];
X    char artname[32];
X    static long our_pid;
X#endif /* SERVER */
X    char oldmode = mode;
X    register long i;			/* scratch */
X    int skipstate;			/* how many unavailable articles */
X					/*   have we skipped already? */
X    
X    char *whatnext = "%sWhat next? [%s]";
X
X#ifdef SERVER
X    if (our_pid == 0)           /* Agreed, this is gross */
X        our_pid = getpid();
X#endif /* SERVER */
X
X#ifdef ARTSEARCH
X    srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0);
X					/* did they say -S? */
X#endif
X    
X    mode = 'a';
X#ifdef USETHREADS
X    recent_p_art = curr_p_art = Nullart;
X#endif
X    recent_art = curr_art = -1;
X    exit_code = NG_NORM;
X
X#ifdef SERVER
X    sprintf(ser_line, "GROUP %s", ngname);
X    put_server(ser_line);
X    if (get_server(ser_line, sizeof(ser_line)) < 0) {
X	fprintf(stderr, "rrn: Unexpected close of server socket.\n");
X	finalize(1);
X    }
X    if (*ser_line != CHAR_OK) {
X	if (atoi(ser_line) != ERR_NOGROUP)
X		fprintf(stderr, "rrn: server response to GROUP %s:\n%s\n",
X			ngname, ser_line);
X	return (-1);
X    }
X#else /* not SERVER */
X    if (eaccess(ngdir,5)) {		/* directory read protected? */
X	if (eaccess(ngdir,0)) {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nNewsgroup %s does not have a spool directory!\n",
X		    ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\nNo spool for %s!\n",ngname) FLUSH;
X#endif
X#ifdef CATCHUP
X	    catch_up(ng);
X#endif
X	    toread[ng] = TR_NONE;
X	}
X	else {
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\nNewsgroup %s is not currently accessible.\n",
X		    ngname) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\n%s not readable.\n",ngname) FLUSH;
X#endif
X	    toread[ng] = TR_NONE;	/* make this newsgroup invisible */
X					/* (temporarily) */
X	}
X	mode = oldmode;
X	return -1;
X    }
X
X    /* chdir to newsgroup subdirectory */
X
X    if (chdir(ngdir)) {
X	printf(nocd,ngdir) FLUSH;
X	mode = oldmode;
X	return -1;
X    }
X#endif /* SERVER */
X
X#ifdef CACHESUBJ
X    subj_list = Null(char **);		/* no subject list till needed */
X#endif
X    
X    /* initialize control bitmap */
X
X    if (initctl()) {
X	mode = oldmode;
X	return -1;
X    }
X
X    /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */
X
X    /* grab threaded data */
X
X#ifdef USETHREADS
X    if (ThreadedGroup && (ThreadedGroup = use_data(thread_name(ngname)))) {
X	/* check if thread file is newer than the active2 entry (this is
X	** possible when mthreads is still running.) */
X	if (total.last > lastart) {
X	    grow_ctl(total.last);	/* sets lastart */
X	}
X	else if (total.last < lastart) {
X	    /* If the active2 entry is newer than the data file, something
X	    ** bad is going on.  Forget using the thread data. */
X	    unuse_data(0);
X	    ThreadedGroup = FALSE;
X	}
X    }
X#endif
X
X    in_ng = TRUE;			/* tell the world we are here */
X    forcelast = TRUE;			/* if 0 unread, do not bomb out */
X
X    /* remember what newsgroup we were in for sake of posterity */
X
X    writelast();
X
X    /* see if there are any special searches to do */
X
X#ifdef KILLFILES
X    open_kfile(KF_LOCAL);
X#ifdef VERBOSE
X    IF(verbose)
X	kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE);
X    ELSE
X#endif
X#ifdef TERSE
X	kill_unwanted(firstart,"Killing...\n\n",TRUE);
X#endif
X#endif
X#ifdef USETHREADS
X    first_art();
X#else
X    art=firstart;
X#endif
X    
X    /* do they want a special top line? */
X
X    firstline = getval("FIRSTLINE",Nullch);
X
X    /* custom line suppression, custom page ending */
X
X#ifdef CUSTOMLINES
X    if (hideline = getval("HIDELINE",Nullch))
X	compile(&hide_compex,hideline,TRUE,TRUE);
X    if (pagestop = getval("PAGESTOP",Nullch))
X	compile(&page_compex,pagestop,TRUE,TRUE);
X#endif
X
X    /* now read each unread article */
X
X    rc_changed = doing_ng = TRUE;	/* enter the twilight zone */
X    skipstate = 0;			/* we have not skipped anything (yet) */
X    checkcount = 0;			/* do not checkpoint for a while */
X    do_fseek = FALSE;			/* start 1st article at top */
X    if (art > lastart)
X#ifdef USETHREADS
X	first_art();
X#else
X	art=firstart;			/* init the for loop below */
X#endif
X    for (; art<=lastart+1; ) {		/* for each article */
X
X	/* do we need to "grow" the newsgroup? */
X
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    if ((art > lastart || forcegrow) && getngsize(ng) > total.last) {
X		unuse_data(1);		/* free data with selections saved */
X		if ((ThreadedGroup = use_data(thread_name(ngname))) != 0) {
X		    grow_ctl(total.last);	/* sets lastart */
X		    find_article(art);
X		    curr_p_art = p_art;
X		}
X		forcegrow = FALSE;
X	    }
X	}
X	else
X#endif
X	if (art > lastart || forcegrow)
X	    grow_ctl(getngsize(ng));
X	check_first(art);		/* make sure firstart is still 1st */
X	if (start_command) {		/* fake up an initial command? */
X	    prompt = whatnext;
X	    strcpy(buf,start_command);
X	    free(start_command);
X	    start_command = Nullch;
X#ifdef USETHREADS
X	    p_art = Nullart;
X#endif
X	    art = lastart+1;
X	    goto article_level;
X	}
X	if (art>lastart) {		/* are we off the end still? */
X	    ART_NUM ucount = 0;		/* count of unread articles left */
X
X	    for (i=firstart; i<=lastart; i++)
X		if (!(ctl_read(i)))
X		    ucount++;		/* count the unread articles */
X#ifdef DEBUGGING
X	    /*NOSTRICT*/
X	    if (debug && ((ART_NUM)toread[ng]) != ucount)
X		printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount)
X		  FLUSH;
X#endif
X	    /*NOSTRICT*/
X	    toread[ng] = (ART_UNREAD)ucount;	/* this is perhaps pointless */
X	    art = lastart + 1;		/* keep bitmap references sane */
X	    if (art != curr_art) {
X#ifdef USETHREADS
X		recent_p_art = curr_p_art;
X		find_article(art);
X		curr_p_art = p_art;
X#endif
X		recent_art = curr_art;
X					/* remember last article # (for '-') */
X		curr_art = art;      /* remember this article # */
X	    }
X	    if (erase_screen)
X		clear();			/* clear the screen */
X	    else
X		fputs("\n\n",stdout) FLUSH;
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("End of newsgroup %s.",ngname);
X					/* print pseudo-article */
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("End of %s",ngname);
X#endif
X	    if (ucount) {
X#ifdef USETHREADS
X		if (selected_root_cnt)
X		    printf("  (%ld + %ld articles still unread)",
X			(long)selected_count,(long)ucount-selected_count);
X		else
X#endif
X		    printf("  (%ld article%s still unread)",
X			(long)ucount,ucount==1?nullstr:"s");
X	    }
X	    else {
X		if (!forcelast)
X		    goto cleanup;	/* actually exit newsgroup */
X	    }
X	    prompt = whatnext;
X#ifdef ARTSEARCH
X	    srchahead = 0;		/* no more subject search mode */
X#endif
X	    fputs("\n\n",stdout) FLUSH;
X	    skipstate = 0;		/* back to none skipped */
X	}
X	else if (!reread && was_read(art)) {
X					/* has this article been read? */
X#ifdef USETHREADS
X	    follow_thread('n');
X#else
X	    art++;			/* then skip it */
X#endif
X	    continue;
X	}
X	else if
X	  (!reread && !was_read(art)
X#ifdef SERVER
X	    && nntpopen(art,GET_HEADER) == Nullfp) { 
X#else
X	    && artopen(art) == Nullfp) { /* never read it, & cannot find it? */
X	    if (errno != ENOENT) {	/* has it not been deleted? */
X#ifdef VERBOSE
X		IF(verbose)
X		    printf("\n(Article %ld exists but is unreadable.)\n",
X			(long)art) FLUSH;
X		ELSE
X#endif
X#ifdef TERSE
X		    printf("\n(%ld unreadable.)\n",(long)art) FLUSH;
X#endif
X		skipstate = 0;
X		sleep(2);
X	    }
X#endif
X	    switch(skipstate++) {
X	    case 0:
X		clear();
X#ifdef VERBOSE
X		IF(verbose)
X		    fputs("Skipping unavailable article",stdout);
X		ELSE
X#endif
X#ifdef TERSE
X		    fputs("Skipping",stdout);
X#endif
X		for (i = just_a_sec/3; i; --i)
X		    putchar(PC);
X		fflush(stdout);
X		sleep(1);
X		break;
X	    case 1:
X		fputs("..",stdout);
X		fflush(stdout);
X		break;
X	    default:
X		putchar('.');
X		fflush(stdout);
X#ifndef SERVER
X#define READDIR
X#ifdef READDIR
X		{			/* fast skip patch */
X		    ART_NUM newart;
X		    
X		    if (! (newart=getngmin(".",art)))
X			newart = lastart+1;
X		    for (i=art; i<newart; i++)
X			oneless(i);
X		    art = newart - 1;
X		}
X#endif
X#else
X		{
X			char	ser_line[256];
X			ART_NUM	newart;
X
X			put_server("NEXT");
X			if (get_server(ser_line, sizeof (ser_line)) < 0) {
X				fprintf(stderr,
X			"rrn: unexpected close of server socket.\n");
X				finalize(1);
X			}
X			if (ser_line[0] != CHAR_OK)
X				newart = lastart + 1;
X			else
X				newart = atoi(ser_line+4);
X		        for (i=art; i<newart; i++)
X				oneless(i);
X		        art = newart - 1;
X		}
X#endif /* SERVER */
X		break;
X	    }
X	    oneless(art);		/* mark deleted as read */
X#ifdef USETHREADS
X	    count_roots(FALSE);		/* Keep selected_count accurate */
X	    find_article(art);
X	    follow_thread('n');
X#else
X	    art++;			/* try next article */
X#endif
X	    continue;
X	}
X	else {				/* we have a real live article */
X	    skipstate = 0;		/* back to none skipped */
X	    if (art != curr_art) {
X#ifdef USETHREADS
X		recent_p_art = curr_p_art;
X		find_article(art);
X		curr_p_art = p_art;
X#endif
X		recent_art = curr_art;
X					/* remember last article # (for '-') */
X		curr_art = art;      /* remember this article # */
X	    }
X	    if (!do_fseek) {		/* starting at top of article? */
X		artline = 0;		/* start at the beginning */
X		topline = -1;		/* and remember top line of screen */
X					/*  (line # within article file) */
X	    }
X	    clear();			/* clear screen */
X	    if (!artopen(art)) {	/* make sure article is found & open */
X#ifdef USETHREADS
X		char tmpbuf[256];
X		/* see if we have tree data for this article anyway */
X		init_tree();
X		sprintf(tmpbuf,"%s #%ld is not available.",ngname,(long)art);
X		tree_puts(tmpbuf,0,0);
X		vwtary((ART_LINE)0,(ART_POS)0);
X		finish_tree(1);
X		prompt = whatnext;
X#else
X		printf("Article %ld of %s is not available.\n\n",
X		    (long)art,ngname) FLUSH;
X		prompt = whatnext;
X#endif
X#ifdef ARTSEARCH
X		srchahead = 0;
X#endif
X	    }
X	    else {			/* found it, so print it */
X		switch (do_article()) {
X		case DA_CLEAN:		/* quit newsgroup */
X		    goto cleanup;
X		case DA_TOEND:		/* do not mark as read */
X		    goto reask_article; 
X		case DA_RAISE:		/* reparse command at end of art */
X		    goto article_level;
X		case DA_NORM:		/* normal end of article */
X		    break;
X		}
X	    }
X	    if (art >= absfirst)	/* don't mark non-existant articles */
X		mark_as_read();		/* mark current article as read */
X	    reread = FALSE;
X	    do_hiding = TRUE;
X#ifdef ROTATION
X	    rotate = FALSE;
X#endif
X	}
X
X/* if these gotos bother you, think of this as a little state machine */
X
Xreask_article:
X#ifdef MAILCALL
X	setmail();
X#endif
X	setdfltcmd();
X#ifdef CLEAREOL
X	if (erase_screen && can_home_clear)
X	    clear_rest();
X#endif /* CLEAREOL */
X	unflush_output();		/* disable any ^O in effect */
X	standout();			/* enter standout mode */
X	printf(prompt,mailcall,dfltcmd);/* print prompt, whatever it is */
X	un_standout();			/* leave standout mode */
X	putchar(' ');
X	fflush(stdout);
Xreinp_article:
X	eat_typeahead();
X#ifdef PENDING
X	look_ahead();			/* see what we can do in advance */
X	if (!input_pending())
X	    collect_subjects();		/* loads subject cache until */
X					/* input is pending */
X#endif
X	getcmd(buf);
X	if (errno || *buf == '\f') {
X	    if (LINES < 100 && !int_count)
X		*buf = '\f';		/* on CONT fake up refresh */
X	    else {
X		putchar('\n') FLUSH;		/* but only on a crt */
X		goto reask_article;
X	    }
X	}
Xarticle_level:
X
X	/* parse and process article level command */
X
X	switch (art_switch()) {
X	case AS_INP:			/* multichar command rubbed out */
X	    goto reinp_article;
X	case AS_ASK:			/* reprompt "End of article..." */
X	    goto reask_article;
X	case AS_CLEAN:			/* exit newsgroup */
X	    goto cleanup;
X	case AS_NORM:			/* display article art */
X	    break;
X	}
X    }					/* end of article selection loop */
X    
X/* shut down newsgroup */
X
Xcleanup:
X    uud_end();
X#ifdef KILLFILES
X    kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE);
X					/* do cleanup from KILL file, if any */
X#endif
X#ifdef USETHREADS
X    if (ThreadedGroup)
X	unuse_data(0);			/* free article thread data */
X#endif
X    in_ng = FALSE;			/* leave newsgroup state */
X    if (artfp != Nullfp) {		/* article still open? */
X	fclose(artfp);			/* close it */
X	artfp = Nullfp;			/* and tell the world */
X#ifdef SERVER
X        sprintf(artname, "/tmp/rrn%ld.%ld", (long) openart, our_pid);
X        UNLINK(artname);
X#endif /* SERVER */
X	openart = 0;
X    }
X    putchar('\n') FLUSH;
X    yankback();				/* do a Y command */
X    restore_ng();			/* reconstitute .newsrc line */
X    doing_ng = FALSE;			/* tell sig_catcher to cool it */
X    free(ctlarea);			/* return the control area */
X#ifdef CACHESUBJ
X    if (subj_list) {
X	for (i=OFFSET(lastart); i>=0; --i)
X	    if (subj_list[i])
X		free(subj_list[i]);
X#ifndef lint
X	free((char*)subj_list);
X#endif /* lint */
X    }
X#endif
X    write_rc();				/* and update .newsrc */
X    rc_changed = FALSE;			/* tell sig_catcher it is ok */
X    if (chdir(spool)) {
X	printf(nocd,spool) FLUSH;
X	sig_catcher(0);
X    }
X#ifdef KILLFILES
X    if (localkfp) {
X	fclose(localkfp);
X	localkfp = Nullfp;
X    }
X#endif
X    mode = oldmode;
X    return exit_code;
X}					/* Whew! */
X
X/* decide what to do at the end of an article */
X
Xint
Xart_switch()
X{
X    register ART_NUM i;
X      
X    setdef(buf,dfltcmd);
X#ifdef VERIFY
X    printcmd();
X#endif
X
X    switch (*buf) {
X#ifdef USETHREADS
X    case '<':			/* goto previous thread */
X	if (!ThreadedGroup) {
X	    goto group_unthreaded;
X	}
X	prev_root();
X	return AS_NORM;
X    case '>':			/* goto next thread */
X	if (!ThreadedGroup) {
X	    goto group_unthreaded;
X	}
X	next_root();
X	return AS_NORM;
X    case 'U': {			/* unread some articles */
X	char *u_prompt, *u_help_thread;
X
X	if (!ThreadedGroup) {
X	    dfltcmd = "a";
X	    u_help_thread = nullstr;
X#ifdef VERBOSE
X	    IF(verbose)
X		u_prompt = "\nSet unread: all articles? [an] ";
X	    ELSE
X#endif
X#ifdef TERSE
X		u_prompt = "\nUnread? [an] ";
X#endif
X	}
X	else if (!p_art || art > lastart) {
X	    dfltcmd = "+";
X	    u_help_thread = nullstr;
X#ifdef VERBOSE
X	    IF(verbose)
X		u_prompt = "\nSet unread: +select or all? [+an] ";
X	    ELSE
X#endif
X#ifdef TERSE
X		u_prompt = "\nUnread? [+an] ";
X#endif
X	}
X	else {
X	    dfltcmd = "t";
X#ifdef VERBOSE
X	    IF(verbose) {
X		u_prompt = "\n\
XSet unread: thread, subthread, +select, or all? [ts+an] ";
X		u_help_thread = "\
XType t or SP to mark this thread's articles as unread.\n\
XType s to mark the current article and its descendants as unread.\n";
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		u_prompt = "\nUnread? [ts+an] ";
X		u_help_thread = "\
Xt or SP to mark thread unread.\n\
Xs to mark subthread unread.\n";
X	    }
X#endif
X	}
X      reask_unread:
X	in_char(u_prompt,'u');
X	putchar('\n') FLUSH;
X	setdef(buf,dfltcmd);
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X	    fputs(u_help_thread,stdout);
X#ifdef VERBOSE
X	    IF(verbose)
X	    {
X		if (ThreadedGroup)
X		    fputs("\
XType + to enter select thread mode using all the unread articles.\n\
X(The selected threads will be marked as unread and displayed as usual.)\n\
X",stdout) FLUSH;
X		fputs("\
XType a to mark all articles in this group as unread.\n\
XType n to change nothing.\n\
X",stdout) FLUSH;
X	    }
X	    ELSE
X#endif
X#ifdef TERSE
X	    {
X		if (ThreadedGroup)
X		    fputs("\
X+ to select threads from the unread.\n\
X",stdout) FLUSH;
X		fputs("\
Xa to mark all articles unread.\n\
Xn to change nothing.\n\
X",stdout) FLUSH;
X	    }
X#endif
X	    goto reask_unread;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    return AS_ASK;
X	}
X	else if (*buf == 't' && *dfltcmd == 't')
X	    follow_thread('u');
X	else if (*buf == 's' && *dfltcmd == 't') {
X	    follow_thread('U');
X	}
X	else if (*buf == 'a') {
X	    check_first(absfirst);
X	    for (i = absfirst; i <= lastart; i++) {
X		onemore(i);		/* mark as unread */
X	    }
X	    scan_all_roots = FALSE;
X	    count_roots(FALSE);
X	    if (art > lastart) {
X		first_art();
X	    }
X	}
X	else if (ThreadedGroup && *buf == '+') {
X	    *buf = 'U';
X	    goto select_threads;
X	}
X	else {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_unread;
X	}
X	return AS_NORM;
X    }
X    case '[':			/* goto parent article */
X    case '{':			/* goto thread's root article */
X	if (p_art) {
X	    if (!p_art->parent) {
X		if (p_art == p_articles + p_roots[p_art->root].articles) {
X		    register char *cp = (*buf=='['?"parent":"root");
X#ifdef VERBOSE
X		    IF(verbose)
X			fprintf(stdout,"\n\
XThere is no %s article prior to this one.\n",cp) FLUSH;
X		    ELSE
X#endif
X#ifdef TERSE
X			fprintf(stdout,"\nNo prior %s.\n",cp) FLUSH;
X#endif
X		    return AS_ASK;
X		}
X		*buf = '{';
X		p_art--;
X	    }
X	    else
X		p_art += p_art->parent;
X
X	    if (*buf == '{')
X		while (p_art->parent)
X		    p_art += p_art->parent;
X
X	    art = p_art->num;
X	    reread = TRUE;
X	    return AS_NORM;
X	}
Xnot_threaded:
X	if (ThreadedGroup) {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\nThis article is not threaded.\n",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\nUnthreaded article.\n",stdout) FLUSH;
X#endif
X	    return AS_ASK;
X	}
Xgroup_unthreaded:
X#ifdef VERBOSE
X	IF(verbose)
X	    fputs("\nThis group is not threaded.\n",stdout) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    fputs("\nUnthreaded group.\n",stdout) FLUSH;
X#endif
X	return AS_ASK;
X    case ']':			/* goto child article */
X    case '}':			/* goto thread's leaf article */
X	if (p_art) {
X	    if (!(p_art++)->child_cnt) {
X		PACKED_ARTICLE *root_limit = upper_limit(p_art-1,FALSE);
X
X		if (p_art == root_limit) {
X#ifdef VERBOSE
X		    IF(verbose)
X			fputs("\n\
XThis is the last leaf in this tree.\n",stdout) FLUSH;
X		    ELSE
X#endif
X#ifdef TERSE
X			fputs("\nLast leaf.\n",stdout) FLUSH;
X#endif
X		    p_art--;
X		    return AS_ASK;
X		}
X		if (*buf == ']')
X		    *buf = '}';
X		else {
X		    while (++p_art != root_limit && p_art->parent)
X			;
X		    p_art--;
X		    *buf = ' ';
X		}
X	    }
X	    if( *buf == '}' )
X		while (p_art->child_cnt)
X		    p_art++;
X
X	    art = p_art->num;
X	    reread = TRUE;
X	    return AS_NORM;
X	}
X	goto not_threaded;
X    case 'T':
X	if (p_art) {
X	    sprintf(buf,"T%ld\t# %s",(long)p_roots[p_art->root].root_num,
X		subject_ptrs[p_art->subject]);
X	    fputs(buf,stdout);
X	    kf_append(buf);
X	    *buf = 'J';
X	    goto follow_threads;
X	}
X	goto not_threaded;
X    case 'K':
X	if (p_art) {
X	    /* first, write kill-subject command */
X	    (void)art_search(buf, (sizeof buf), TRUE);
X	    art = curr_art;
X	    p_art = curr_p_art;
X	    *buf = 'k';			/* then take care of any prior subjs */
X	    goto follow_threads;
X	}
X	goto normal_search;
X    case ',':		/* kill this node and all descendants */
X	mark_as_read();
X	*buf = 'K';
X    case 'k':		/* kill current subject # (e.g. [1]) */
X    case 'J':		/* Junk all nodes in this thread */
X	if (!ThreadedGroup) {
X	    *buf = 'k';
X	    goto normal_search;
X	}
Xfollow_threads:
X	follow_thread(*buf);
X	if (!reread && !toread[ng])
X	    return AS_CLEAN;
X	if (!reread && selected_root_cnt && !selected_count)
X	    goto select_threads;
X	return AS_NORM;
X    case 't':
X	carriage_return();
X#ifndef CLEAREOL
X	erase_eol();		/* erase the prompt */
X#else
X	if (erase_screen && can_home_clear)
X	    clear_rest();
X	else
X	    erase_eol();	/* erase the prompt */
X#endif /* CLEAREOL */
X	fflush(stdout);
X	page_line = 1;
X	p_art = curr_p_art;
X	entire_tree();
X	return AS_ASK;
X    case ':':			/* execute command on selected articles */
X	if (!ThreadedGroup) {
X	    goto group_unthreaded;
X	}
X	page_line = 1;
X	if (!use_selected())
X	    return AS_INP;
X	putchar('\n');
X	art = curr_art;
X	p_art = curr_p_art;
X	return AS_ASK;
X#endif /* USETHREADS */
X    case 'p':			/* find previous unread article */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto backtrack_threads;
X	}
X#endif
X	do {
X	    if (art <= firstart)
X		break;
X	    art--;
X#ifdef SERVER
X	} while (was_read(art) || nntpopen(art,GET_HEADER) == Nullfp);
X#else
X	} while (was_read(art) || artopen(art) == Nullfp);
X#endif
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case 'P':		/* goto previous article */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
Xbacktrack_threads:
X	    backtrack_thread( *buf );
X	    art++;		/* prepare for art-- below */
X	}
X#endif
X	if (art > absfirst)
X	    art--;
X	else {
X#ifdef VERBOSE
X	    IF(verbose)
X		fprintf(stdout,"\n\
XThere are no%s articles prior to this one.\n\
X",*buf=='P'?nullstr:" unread") FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fprintf(stdout,"\n\
XNo previous%s articles\n\
X",*buf=='P'?nullstr:" unread") FLUSH;
X#endif
X	    art = curr_art;
X#ifdef USETHREADS
X	    p_art = curr_p_art;
X#endif
X	    return AS_ASK;
X	}
X	reread = TRUE;
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '-':
X	if (recent_art >= 0) {
X#ifdef USETHREADS
X	    p_art = recent_p_art;
X#endif
X	    art = recent_art;
X	    reread = TRUE;
X#ifdef ARTSEARCH
X	    srchahead = -(srchahead != 0);
X#endif
X	    return AS_NORM;
X	}
X	else {
X	    exit_code = NG_MINUS;
X	    return AS_CLEAN;
X	}
X    case 'n':		/* find next unread article? */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto follow_threads;
X	}
X#endif
X	if (art > lastart) {
X	    if (!toread[ng])
X		return AS_CLEAN;
X	    art = firstart;
X	}
X#ifdef ARTSEARCH
X	else if (scanon && srchahead) {
X	    *buf = Ctl('n');
X	    goto normal_search;
X	}
X#endif
X	else
X	    art++;
X
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case 'N':			/* goto next article */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto follow_threads;
X	}
X#endif
X	if (art > lastart)
X	    art = absfirst;
X	else
X	    art++;
X	if (art <= lastart)
X	    reread = TRUE;
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '$':
X	art = lastart+1;
X	forcelast = TRUE;
X#ifdef USETHREADS
X	p_art = Nullart;
X#endif
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X    case '1': case '2': case '3':	/* goto specified article */
X    case '4': case '5': case '6':	/* or do something with a range */
X    case '7': case '8': case '9': case '.':
X	forcelast = TRUE;
X	switch (numnum()) {
X	case NN_INP:
X	    return AS_INP;
X	case NN_ASK:
X	    return AS_ASK;
X	case NN_REREAD:
X	    reread = TRUE;
X#ifdef ARTSEARCH
X	    if (srchahead)
X		srchahead = -1;
X#endif
X	    break;
X	case NN_NORM:
X	    if (was_read(art)) {
X#ifdef USETHREADS
X		first_art();
X#else
X		art = firstart;
X#endif
X		pad(just_a_sec/3);
X	    }
X	    else {
X		putchar('\n');
X		return AS_ASK;
X	    }
X	    break;
X	}
X	return AS_NORM;
X    case Ctl('k'):
X	edit_kfile();
X	return AS_ASK;
X#ifndef USETHREADS
X    case 'K':
X    case 'k':
X#endif
X    case Ctl('n'):	/* search for next article with same subject */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto follow_threads;
X	}
X#endif
X    case Ctl('p'):	/* search for previous article with same subject */
X#ifdef USETHREADS
X	if (ThreadedGroup) {
X	    goto backtrack_threads;
X	}
X#endif
X    case '/': case '?':
Xnormal_search:
X#ifdef ARTSEARCH
X    {		/* search for article by pattern */
X	char cmd = *buf;
X	
X	reread = TRUE;		/* assume this */
X	page_line = 1;
X	switch (art_search(buf, (sizeof buf), TRUE)) {
X	case SRCH_ERROR:
X	    art = curr_art;
X	    return AS_ASK;
X	case SRCH_ABORT:
X	    art = curr_art;
X	    return AS_INP;
X	case SRCH_INTR:
X#ifdef VERBOSE
X	    IF(verbose)
X		printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		printf("\n(Intr at %ld)\n",(long)art) FLUSH;
X#endif
X	    art = curr_art;
X			    /* restore to current article */
X	    return AS_ASK;
X	case SRCH_DONE:
X	    fputs("done\n",stdout) FLUSH;
X	    pad(just_a_sec/3);	/* 1/3 second */
X	    if (!srchahead) {
X		art = curr_art;
X		return AS_ASK;
X	    }
X#ifdef USETHREADS
X	    first_art();
X#else
X	    art = firstart;
X#endif
X	    reread = FALSE;
X	    return AS_NORM;
X	case SRCH_SUBJDONE:
X#ifdef UNDEF
X	    fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH;
X	    pad(just_a_sec/3);	/* 1/3 second */
X#endif
X#ifdef USETHREADS
X	    first_art();
X#else
X	    art = firstart;
X#endif
X	    reread = FALSE;
X	    return AS_NORM;
X	case SRCH_NOTFOUND:
X	    fputs("\n\n\n\nNot found.\n",stdout) FLUSH;
X	    art = curr_art;  /* restore to current article */
X	    return AS_ASK;
X	case SRCH_FOUND:
X	    if (cmd == Ctl('n') || cmd == Ctl('p'))
X		oldsubject = TRUE;
X	    break;
X	}
X	return AS_NORM;
X    }
X#else
X    buf[1] = '\0';
X    notincl(buf);
X    return AS_ASK;
X#endif
X    case 'u':			/* unsubscribe from this newsgroup? */
X	rcchar[ng] = NEGCHAR;
X	return AS_CLEAN;
X    case 'M':
X#ifdef DELAYMARK
X	if (art <= lastart) {
X	    delay_unmark(art);
X	    printf("\nArticle %ld will return.\n",(long)art) FLUSH;
X	}
X#else
X	notincl("M");
X#endif
X	return AS_ASK;
X    case 'm':
X	if (art <= lastart) {
X	    unmark_as_read();
X	    printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH;
X	}
X	return AS_ASK;
X    case 'c':			/* catch up */
X      reask_catchup:
X#ifdef VERBOSE
X	IF(verbose)
X	    in_char("\nDo you really want to mark everything as read? [yn] ",
X		'C');
X	ELSE
X#endif
X#ifdef TERSE
X	    in_char("\nReally? [ynh] ", 'C');
X#endif
X	putchar('\n') FLUSH;
X	setdef(buf,"y");
X#ifdef VERIFY
X	printcmd();
X#endif
X	if (*buf == 'h') {
X#ifdef VERBOSE
X	    IF(verbose)
X		fputs("\
XType y or SP to mark all articles as read.\n\
XType n to leave articles marked as they are.\n\
XType u to mark everything read and unsubscribe.\n\
X",stdout) FLUSH;
X	    ELSE
X#endif
X#ifdef TERSE
X		fputs("\
Xy or SP to mark all read.\n\
Xn to forget it.\n\
Xu to mark all and unsubscribe.\n\
X",stdout) FLUSH;
X#endif
X	    goto reask_catchup;
X	}
X	else if (*buf == 'n' || *buf == 'q') {
X	    return AS_ASK;
X	}
X	else if (*buf != 'y' && *buf != 'u') {
X	    fputs(hforhelp,stdout) FLUSH;
X	    settle_down();
X	    goto reask_catchup;
X	}
X	for (i = firstart; i <= lastart; i++) {
X	    oneless(i);		/* mark as read */
X	}
X#ifdef USETHREADS
X	selected_root_cnt = selected_count = 0;
X#endif
X#ifdef DELAYMARK
X	if (dmfp)
X	    yankback();
X#endif
X	if (*buf == 'u') {
X	    rcchar[ng] = NEGCHAR;
X	    return AS_CLEAN;
X	}
X#ifdef USETHREADS
X	p_art = Nullart;
X	selected_count = 0;
X#endif
X	art = lastart+1;
X	forcelast = FALSE;
X	return AS_NORM;
X    case 'Q':
X	exit_code = NG_ASK;
X	/* FALL THROUGH */
X    case 'q':			/* go back up to newsgroup level? */
X	return AS_CLEAN;
X    case 'j':
X	putchar('\n') FLUSH;
X	if (art <= lastart)
X	    mark_as_read();
X	return AS_ASK;
X    case 'h': {			/* help? */
X	int cmd;
X
X	if ((cmd = help_art()) > 0)
X	    pushchar(cmd);
X	return AS_ASK;
X    }
X    case '&':
X	if (switcheroo()) /* get rest of command */
X	    return AS_INP;	/* if rubbed out, try something else */
X	return AS_ASK;
X    case '#':
X#ifdef VERBOSE
X	IF(verbose)
X	    printf("\nThe last article is %ld.\n",(long)lastart) FLUSH;
X	ELSE
X#endif
X#ifdef TERSE
X	    printf("\n%ld\n",(long)lastart) FLUSH;
X#endif
X	return AS_ASK;
X#ifdef USETHREADS
X    case '+':			/* enter thread selection mode */
X	if (ThreadedGroup) {
Xselect_threads:
X	    *buf = select_thread(*buf);
X	    if (*buf == 'q') {
X		putchar( '\n' ) FLUSH;
X		return AS_ASK;
X	    }
X	    if (*buf == 'Q') {
X		exit_code = NG_ASK;
X		return AS_CLEAN;
X	    }
X	    if (*buf == 'N' || !toread[ng])
X		return AS_CLEAN;
X	    return AS_NORM;
X	}
X	/* FALLTHROUGH */
X#endif
X    case '=': {			/* list subjects */
X	char tmpbuf[256];
X	ART_NUM oldart = art;
X	int cmd;
X	char *subjline = getval("SUBJLINE",Nullch);
X#ifndef CACHESUBJ
X	char *s;
X#endif
X
X	page_init();
X#ifdef CACHESUBJ
X	if (!subj_list)
X	    fetchsubj(art,TRUE,FALSE);
X#endif
X	for (i=firstart; i<=lastart && !int_count; i++) {
X#ifdef CACHESUBJ
X	    if (!was_read(i) &&
X	      (subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)) &&
X	      *subj_list[OFFSET(i)] ) {
X		sprintf(tmpbuf,"%5ld ", i);
X		if (subjline) {
X		    art = i;
X		    interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
X		}
X		else
X		    safecpy(tmpbuf + 6, subj_list[OFFSET(i)],
X			(sizeof tmpbuf) - 6);
X		if (cmd = print_lines(tmpbuf,NOMARKING)) {
X		    if (cmd > 0)
X			pushchar(cmd);
X		    break;
X		}
X	    }
X#else
X	    if (!was_read(i) && (s = fetchsubj(i,FALSE,FALSE)) && *s) {
X		sprintf(tmpbuf,"%5ld ", i);
X		if (subjline) {	/* probably fetches it again! */
X		    art = i;
X		    interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
X		}
X		else
X		    safecpy(tmpbuf + 6, s, (sizeof tmpbuf) - 6);
X		if (cmd = print_lines(tmpbuf,NOMARKING)) {
X		    if (cmd > 0)
X			pushchar(cmd);
X		    break;
X		}
X	    }
X#endif
X	}
X	int_count = 0;
X	art = oldart;
X	return AS_ASK;
X    }
X    case '^':
X#ifdef USETHREADS
X	first_art();
X#else
X	art = firstart;
X#endif
X#ifdef ARTSEARCH
X	srchahead = 0;
X#endif
X	return AS_NORM;
X#if defined(CACHESUBJ) && defined(DEBUGGING)
X    case 'D':
X	printf("\nFirst article: %ld\n",(long)firstart) FLUSH;
X	if (!subj_list)
X	    fetchsubj(art,TRUE,FALSE);
X	if (subj_list != Null(char **)) {
X	    for (i=1; i<=lastart && !int_count; i++) {
X		if (subj_list[OFFSET(i)])
X		    printf("%5ld %c %s\n",
X			i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]) FLUSH;
X	    }
X	}
X	int_count = 0;
X	return AS_ASK;
X#endif
X    case 'v':
X	if (art <= lastart) {
X	    reread = TRUE;
X	    do_hiding = FALSE;
X	}
X	return AS_NORM;
X#ifdef ROTATION
X    case Ctl('x'):
X#endif
X    case Ctl('r'):
X#ifdef ROTATION
X	rotate = (*buf==Ctl('x'));
X#endif
X	if (art <= lastart)
X	    reread = TRUE;
X	return AS_NORM;
X#ifdef ROTATION
X    case 'X':
X	rotate = !rotate;
X	/* FALL THROUGH */
X#else
X    case Ctl('x'):
X    case 'x':
X    case 'X':
X	notincl("x");
X	return AS_ASK;
X#endif
X    case 'l': case Ctl('l'):		/* refresh screen */
X	if (art <= lastart) {
X	    reread = TRUE;
X	    clear();
X	    do_fseek = TRUE;
X	    artline = topline;
X	    if (artline < 0)
X		artline = 0;
X	}
X	return AS_NORM;
X    case 'b': case Ctl('b'):		/* back up a page */
X	if (art <= lastart) {
X	    ART_LINE target;
X
X	    reread = TRUE;
X	    clear();
X	    do_fseek = TRUE;
X	    target = topline - (LINES - 2);
X	    artline = topline;
X	    while (artline >= 0 && artline > target && vrdary(artline-1) >= 0)
X		artline--;
X	    topline = artline;
X	    if (artline < 0)
X		artline = 0;
X	}
X	return AS_NORM;
X    case '!':			/* shell escape */
X	if (escapade())
X	    return AS_INP;
X	return AS_ASK;
X    case 'C': {
X	cancel_article();
X	return AS_ASK;
X    }
X    case 'R':
X    case 'r': {			/* reply? */
X	reply();
X	return AS_ASK;
X    }
X    case 'F':
X    case 'f': {			/* followup command */
X	followup();
X	forcegrow = TRUE;		/* recalculate lastart */
X	return AS_ASK;
X    }
X    case '|':
X    case 'w': case 'W':
X    case 's': case 'S':		/* save command */
X    case 'e':			/* extract command */
X	if (save_article() == SAVE_ABORT)
X	    return AS_INP;
X	int_count = 0;
X	return AS_ASK;
X    case 'E':
X	if (uu_out != Nullfp) {
X	    uud_end();
X	}
X	return AS_ASK;
X#ifdef DELAYMARK
X    case 'Y':				/* yank back M articles */
X	yankback();
X#ifdef USETHREADS
X	first_art();
X#else
X	art = firstart;			/* from the beginning */
X#endif
X	return AS_NORM;			/* pretend nothing happened */
X#endif
X#ifdef STRICTCR
X    case '\n':
X	fputs(badcr,stdout) FLUSH;
X	return AS_ASK;
X#endif
X    default:
X	printf("\n%s",hforhelp) FLUSH;
X	settle_down();
X	return AS_ASK;
X    }
X}
X
X#ifdef MAILCALL
X/* see if there is any mail */
X
Xvoid
Xsetmail()
X{
X    if (! (mailcount++)) {
X	char *mailfile = filexp(getval("MAILFILE",MAILFILE));
X	
X	if (stat(mailfile,&filestat) < 0 || !filestat.st_size
X	    || filestat.st_atime > filestat.st_mtime)
X	    mailcall = nullstr;
X	else
X	    mailcall = getval("MAILCALL","(Mail) ");
X    }
X    mailcount %= 10;			/* check every 10 articles */
X}
X#endif
X
Xvoid
Xsetdfltcmd()
X{
X#ifdef USETHREADS
X    if (toread[ng] == unthreaded) {
X#else
X    if (!toread[ng]) {
X#endif
X	if (art > lastart)
X	    dfltcmd = "qnp";
X	else
X	    dfltcmd = "npq";
X    }
X    else {
X#ifdef USETHREADS
X	if (selected_root_cnt && !selected_count)
X	    dfltcmd = "+npq";
X	else
X# ifdef ARTSEARCH
X	if (!ThreadedGroup && srchahead)
X	    dfltcmd = "^Nnpq";
X	else
X# endif
X#else
X# ifdef ARTSEARCH
X	if (srchahead)
X	    dfltcmd = "^Nnpq";
X	else
X# endif
X#endif
X	    dfltcmd = "npq";
X	}
X}
END_OF_FILE
  if test 34823 -ne `wc -c <'ng.c'`; then
    echo shar: \"'ng.c'\" unpacked with wrong size!
  fi
  # end of 'ng.c'
fi
echo shar: End of archive.
exit 0
-- 
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