From: davison@borland.com (Wayne Davison)
Subject: Patch #3 for trn 2 (PART 1 of 2)
Organization: Borland International
Date: Wed, 30 Dec 1992 22:29:00 GMT

I've just released trn 2.3  This is the official patch split up into two
pieces for posting.  You can ftp this patch or the entire 2.3 tar file
from ftp.uu.net:

	ftp.uu.net:/news/trn/trn-2.3.patch.Z
	ftp.uu.net:/news/trn/trn-2.3.tar.Z

I'm currently working on version 3.0, but as it won't be out for a
couple months(?), this should tide you over.  Enjoy!

..wayne..

------------------------------------------------------------------------

Here's patch #3 for trn 2 -- this creates version 2.3.  To apply it, you
need either a unified version of Larry Wall's patch program (such as 12u8),
or the unidiff filter (included with trn) and an old version of patch.

To apply this from within trn, you would type:

	| patch -p -d /trn/source/dir
or
	| unipatch | patch -p -d /trn/source/dir

After installing the patch you should do the following:

	Configure             # (optional if you're not an INN site)
	make Makefile
	make depend
	make
	make install

Also, if you're using NNTP, see the new NNTP patches in the nntp subdir.

Additions to trn:
   o	Added the 'Z' command in article mode to supersede an article and
	include its text for easy modification.
   o	Added thread-movement commands to go to an article's next/previous
	sibling with '(' and ')', including "cousin" siblings.
   o	The thread selector now lets you press 'c'+'y' to catch up without
	chasing the xref'ed articles (easier than having to first leave the
	thread selector).  Purists will want to set their own SELECTCHARS.
   o	The Makefile knows how to use INN's nntp client library.
   o	Added -b option to force trn to read threads in a breadth-first
	manner rather than depth first.
   o	Added -j option to have trn pass-through control characters in
	articles.

Fixes for trn:
   o	Trn no longer puts bogus Reference lines in new articles.
   o	Fixed a problem in the KILL file processing of new articles
	that arrive while within the group.
   o	The default extract directory is now correctly set to a
	group's SAVEDIR when entering a group.
   o	Relative paths that contain a slash are not bumped out of
	SAVEDIR when saving articles.
   o	You can now 'g'o to a group name that starts with a number.
   o	Trn opens the correct article when it is pre-fetching.
   o	Turned off subject-prefetch in a threaded group.
   o	Fixed a bug in the subject-search code that would result in
	very slow search times if a group had lots of missing articles.
   o	When using the 'k'ill thread command in the thread selector, it
	no longer tries to chases xrefs for each keypress -- instead it
	batches all the killing until after you're done selecting.
   o	Added Geoff Collyer's hdbm hasing routines to make scaning the
	active file faster (e.g. when bogus groups are discovered).
   o	Various small fixes, including all the missing tweaks from
	rn's last patch.

Fixes/enhancements for mthreads:
   o	Fixed a couple bugs in the free code when attempting to ignore a
	damaged thread file.
   o	Added an intelligent name-squishing algorithm.
   o	The NNTP version knows how to use the LISTGROUP command for the
	extra-expire pass.
   o	Other small fixes and minor speed tweaking.

Configure updates:
   o	Added detection/query for INN.
   o	Fixes to support more nm output formats.
   o	Configure now checks for DGUX.
   o	Fixed Configure's understanding of Domain/OS (hopefully).

Misc:
   o	The HINTS file was corrected and an additional hint added.

NNTP changes for v1.5.11[t]:
   o	Added LISTGROUP command (for mthreads).
   o	The NEWGROUPS command is now filtered to remove groups you don't
	have permission to access.
   o	Fixed LONG_THREAD_NAMES support.
   o	All clientlib changes have been moved into trn's own code.
	This allows trn to use the XTHREAD call using a wider variety of
	clientlibs, including the one that comes with INN.

NOTE: use nntp/xthread.patch to upgrade v1.5.11 to v1.5.11t2.  Use the
nntp/xthread-11t.to.11t2 to upgrade v1.5.11t to v1.5.11t2.

Wayne Davison
davison@borland.com
---8<------8<------8<------8<---cut here--->8------>8------>8------>8---
Index:patchlevel
Prereq: v2.2
@@ -1,1 +1,1 @@
-trn v2.2
+trn v2.3
Index:Configure
@@ -16,10 +16,13 @@
 # shell scripts, Configure will trim # comments from them for you.
 #
-# $Id: Configure,v 4.4.3.1 1991/11/22 04:11:10 davison Trn $
+# $Id: Configure,v 2.3 1992/03/01 02:06:01 davison Trn $
 #
 # $Log: Configure,v $
-# Revision 4.4.3.1  1991/11/22  04:11:10  davison
-# Trn Release 2.0
-# 
+# Revision 4.4.4.1  1992/02/23  21:25:39  sob
+# Patch level 4
+#
+# Revision 4.4.3.1  1992/02/01  03:09:32  sob
+# Release 4.4 Patchlevel 3
+#
 # Revision 4.4.2.1  1991/12/01  18:05:42  sob
 # Patchlevel 2 changes
@@ -165,4 +168,5 @@
 hostcmd=''
 norelay=''
+haveinn=''
 isrrn=''
 xthread=''
@@ -320,5 +324,5 @@
 	echo "Your C library is in /lib/libc.a.  You're normal."
 	if test -f /lib/libc_s.a; then
-	    echo "You also have AT&T shared libraries, we'll use those."
+	    echo "You also have AT&T shared libraries, we'll use those too."
 	    sharedclib=-lc_s
 	fi
@@ -326,11 +330,7 @@
     fi
 else
-  if test -f /lib/clib; then
-    echo "Your C library is in /lib/clib.  How nonstandard, must be Apollo."
-    libc=/lib/clib
-  else
-    if test -f /lib/libc; then
-	echo "Your C library is in /lib/libc.  This might be Domain/OS."
-	libc=/lib/libc
+    if test -f /lib/clib; then
+	echo "Your C library is in /lib/clib.  How nonstandard, must be Apollo."
+	libc='/lib/clib /lib/libc'
     else
 	if test -f /lib/386/Slibc.a; then
@@ -368,5 +368,4 @@
 	fi
     fi
-  fi
 fi
 
@@ -391,28 +390,48 @@
 echo $n "Extracting names from $libc for later perusal...$c"
 nm $bopt $libc > nm.list 2>/dev/null
-sed -n -e 's/^.* T _//p' -e 's/^.* T //p' < nm.list > libc.list
+sed -n -e 's/^.* [ATDS]  *__*//p' -e 's/^.* [ATDS] //p' <nm.list >libc.list
+if $contains '^printf$' libc.list >/dev/null 2>&1; then
+    echo done
+else
+sed -n -e 's/^__*//' -e 's/^\([a-zA-Z_0-9$]*\).*xtern.*/\1/p' <nm.list >libc.list
+if $contains '^printf$' libc.list >/dev/null 2>&1; then
+    echo done
+else
+sed -n -e '/|UNDEF/d' -e '/FUNC..GL/s/^.*|__*//p' <nm.list >libc.list
+if $contains '^printf$' libc.list >/dev/null 2>&1; then
+    echo done
+else
+sed -n -e 's/^_//' -e 's/^\([a-zA-Z_0-9]*\).*xtern.*text.*/\1/p' <nm.list >libc.list
+if $contains '^printf$' libc.list >/dev/null 2>&1; then
+    echo done
+else
+grep '|' <nm.list | sed -n -e '/|COMMON/d' -e '/|DATA/d' -e '/ file/d' \
+                           -e 's/^\([^     ]*\).*/\1/p' >libc.list
+if $contains '^printf$' libc.list >/dev/null 2>&1; then
+    echo done
+else
+sed -n -e 's/^.*|FUNC |GLOB .*|//p' -e 's/^.*|FUNC |WEAK .*|//p' <nm.list >libc.list
+if $contains '^printf$' libc.list >/dev/null 2>&1; then
+    echo done
+else
+: This is supposed to have a space and a tab in the three brackets
+sed -n -e 's/^[ 	]*[0-9][0-9a-f]*[ 	]*Def. Text[ 	]*//p' <nm.list | sed -e 's/\[.*\]//' >libc.list
+if $contains '^printf$' libc.list >/dev/null 2>&1; then
+    echo done
+else
+$contains .text nm.list | grep -v static | awk '{ print $1 }' > libc.list
 if $contains '^printf$' libc.list >/dev/null 2>&1; then
-    echo "done"
+    echo done
 else
-    sed -n -e 's/^.* [AD] _//p' -e 's/^.* [AD] //p' < nm.list > libc.list
-    if $contains '^printf$' libc.list >/dev/null 2>&1; then
-	echo "done"
-    else
-	 $contains .text nm.list | grep -v static | awk '{ print $1 }' > libc.list
-    	if $contains '^printf$' libc.list >/dev/null 2>&1; then
-		echo "done"
-    	else
-	     echo " "
-	     echo "nm didn't seem to work right."
-	     echo "Trying ar instead..."
-	     if ar t $libc | sed -e 's/\.o$//' > libc.list; then
-		echo "Ok."
-	     else
-		echo "That didn't work either.  Giving up."
-		exit 1
-	     fi
-	 fi
+    echo " "
+    echo "nm didn't seem to work right."
+    echo "Trying ar instead..."
+    if ar t $libc | sed -e 's/\.o$//' > libc.list; then
+	echo "Ok."
+    else
+	echo "That didn't work either.  Giving up."
+	exit 1
     fi
-fi
+fi ; fi ; fi ; fi ; fi ; fi ; fi ; fi
 rm nm.list
 : make some quick guesses about what we are up against
@@ -638,4 +657,21 @@
 	socketlib=
 fi
+
+: check for DGUX
+cat <<'EOT' >DGUX.c
+#ifdef DGUX
+exit 0
+#else
+exit 1
+#endif
+EOT
+$cpp DGUX.c | grep exit >DGUX
+chmod +x DGUX
+$eunicefix DGUX
+rm DGUX.c
+if DGUX; then
+    echo "This appears to be a DG AViiON..."
+fi
+
 : check for sgi
 cat <<'EOT' >sgi.c
@@ -1339,5 +1375,9 @@
 case "$mtmansrc" in
 '') case "$manext" in
-    '1') dflt="`dirname $mansrc`/`basename $mansrc 1`8" ;;
+    '1') if pyr; then
+	     dflt="`att dirname $mansrc`/`basename $mansrc 1`8"
+	 else
+	     dflt="`dirname $mansrc`/`basename $mansrc 1`8"
+	 fi ;;
     *)   dflt="$mansrc"
     esac
@@ -1854,4 +1894,9 @@
     getwd=define
 else
+if DGUX; then
+    $echo "I know DG/UX has getwd()."
+    getcwd=undef
+    getwd=define
+else
 : see if the system has getcwd
 if $contains '^getcwd$' libc.list >/dev/null 2>&1 ; then
@@ -1864,4 +1909,5 @@
 fi
 fi
+fi
 
 : see if there is a vfork
@@ -2190,5 +2236,5 @@
 	n*) syslog='' ;;
 	*)  case $syslog in
-	    LOG_*) dflt="$dflt" ;;
+	    LOG_*) dflt="$syslog" ;;
 	    *)     dflt=LOG_NEWS ;;
 	    esac
@@ -2210,4 +2256,21 @@
 fi
 
+: check for inn
+$echo " "
+if test -f $lib/newsfeeds; then
+    dflt="y"
+else
+    dflt="n"
+fi
+$echo $n "Is your system or news server running inn news software? [${dflt}] $c"
+. myread
+case "$ans" in
+'') ans=$dflt;;
+esac
+case "$ans" in
+y*) haveinn='define';;
+*)  haveinn='undef';;
+esac
+
 : locate spool directory and check if rrn
 case "$isrrn" in
@@ -2337,5 +2400,8 @@
 	while $test ! -d "$ans"; do
 	    $echo " "
-	    $echo $n "Enter the pathname of the NNTP source directory: [$dflt] $c"
+	    case "$haveinn" in
+	    undef) $echo $n "Enter the pathname of the NNTP source directory: [$dflt] $c" ;;
+	    *) $echo $n "Enter the pathname of the inn source directory: [$dflt] $c" ;;
+	    esac
 	    . myread
 	    case "$ans" in
@@ -2343,8 +2409,14 @@
 	    esac
 	    ans=`filexp "$ans"`
-	    if $test ! -r $ans/common/nntp.h ; then
-		$echo "Can't find $ans/common/nntp.h!"
-		ans='blurfl/dyick'
-	    fi
+	    case "$haveinn" in
+	    undef) if $test ! -r $ans/common/nntp.h ; then
+		    $echo "Can't find $ans/common/nntp.h!"
+		    ans='blurfl/dyick'
+		fi;;
+	    *)  if $test ! -r $ans/include/nntp.h ; then
+		    $echo "Can't find $ans/include/nntp.h!"
+		    ans='blurfl/dyick'
+		fi;;
+	    esac
 	done
 	NNTPSRC="$ans"
@@ -2577,6 +2649,8 @@
 
 : check for 2.10.2 and above
-$echo " "
-if $contains ' [0-9][0-9]* [0-9]' "$myactive" >/dev/null 2>&1; then
+case "$haveinn" in
+undef)
+  $echo " "
+  if $contains ' [0-9][0-9]* [0-9]' "$myactive" >/dev/null 2>&1; then
     case "$norelay" in
     undef) dflt="y";;
@@ -2610,6 +2684,9 @@
     esac
     norelay='undef'
-fi
-
+  fi ;;
+define)
+  norelay='define' 
+  mininact='define' ;;
+esac
 
 : check for void type
@@ -3155,4 +3232,5 @@
 norelay='$norelay'
 rdchk='$rdchk'
+haveinn='$haveinn'
 isrrn='$isrrn'
 xthread='$xthread'
Index:HINTS
@@ -12,7 +12,5 @@
 you want to read on the current page, press space, and you will read the
 selected articles immediately (if there were any).  All other articles on the
-current page will be marked as read.  (Don't use -XXD, because new articles
-might have arrived while reading, and they could be marked as read without
-ever seeing them.)
+current page will be marked as read.
 ----------
 I like to use the -xls command-line option to only have the long and short
@@ -29,6 +27,6 @@
 unread?" prompt (except at the thread selector), 'U' goes directly to the
 already-read article selector, and Ctrl-U unsubscribes from the group (even
-while in the thread selector).  Put the following 3 macros in your .trninit
-file to accomplish this:
+while in the thread selector).  Put the following 3 macros in your .rnmac
+file to accomplish this (or change RNMACROS to .trnmac and put them there):
 
 u	%(%m=[anp]?U:u)
@@ -37,7 +35,12 @@
 ----------
 If you like the way that 'q' worked in the thread selector in trn 1.x, put
-the following macro in your .trninit file:
+the following macro in your .rnmac file:
 
 q	%(%m=t?+:q)
+----------
+If you would like the 'f' command to always answer yes to the "Are you start-
+ing an unreleated topic?" question, put this line into your .rnmac file:
+
+f	%(%m=[ap]?f%(%m=F?y:^@):f)
 ----------
 If you want to be able to save your shar headers in a file as they are
Index:Makefile.SH
@@ -4,5 +4,5 @@
 echo "Extracting Makefile (with variable substitutions)"
 $cat >Makefile <<!GROK!THIS!
-# $Id: Makefile.SH,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+# $Id: Makefile.SH,v 2.3 1992/03/01 02:13:32 davison Trn $
 #
 # This software is Copyright 1991 by Stan Barber. 
@@ -19,4 +19,7 @@
 #
 # $Log: Makefile.SH,v $
+# Revision 4.4.3.1  1992/02/01  03:09:32  sob
+# Release 4.4 Patchlevel 3
+#
 # Revision 4.4.2.1  1991/12/01  18:05:42  sob
 # Changed clientlib to be built in the rn tree instead of the nntp tree.
@@ -59,7 +62,7 @@
 
 h1 = addng.h art.h artio.h artsrch.h autosub.h backpage.h bits.h cheat.h 
-h2 = common.h decode.h final.h head.h help.h init.h intrp.h kfile.h last.h
-h3 = ng.h ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h
-h4 = respond.h rn.h search.h sw.h term.h util.h
+h2 = common.h decode.h final.h hdbm.h hdbmint.h head.h help.h init.h
+h3 = intrp.h kfile.h last.h ng.h ngdata.h ngsrch.h ngstuff.h only.h rcln.h
+h4 = rcstuff.h respond.h rn.h search.h sw.h term.h util.h
 
 h = $(h1) $(h2) $(h3) $(h4)
@@ -66,9 +69,10 @@
 
 c1 = addng.c art.c artio.c artsrch.c autosub.c backpage.c bits.c cheat.c
-c2 = decode.c final.c head.c help.c init.c intrp.c kfile.c last.c $(NDIRC)
-c3 = ng.c ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c respond.c rn.c
-c4 = rthreads.c rt-rn.c rt-select.c search.c sw.c term.c threads.c util.c
-c5 = unship.c uudecode.c
-#NNTPc6 = server.c $(NNTPDIR)/common/clientlib.c
+c2 = decode.c final.c hdbm.c head.c help.c init.c intrp.c kfile.c last.c
+c3 = $(NDIRC) ng.c ngdata.c nghash.c ngsrch.c ngstuff.c only.c rcln.c
+c4 = rcstuff.c respond.c rn.c rthreads.c rt-rn.c rt-select.c search.c sw.c
+c5 = term.c threads.c util.c unship.c uudecode.c
+#INN#NNTPc6 = server.c
+#NO_INN#NNTPc6 = server.c $(NNTPDIR)/common/clientlib.c
 
 c = $(c1) $(c2) $(c3) $(c4) $(c5) $(c6)
@@ -81,9 +85,10 @@
 
 obj1 = addng.o art.o artio.o artsrch.o autosub.o backpage.o bits.o cheat.o
-obj2 = decode.o final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO)
-obj3 = ng.o ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o respond.o rn.o
-obj4 = rthreads.o rt-rn.o rt-select.o search.o sw.o term.o threads.o util.o
-obj5 = unship.o uudecode.o
-#NNTPobj6 = server.o clientlib.o
+obj2 = decode.o final.o hdbm.o head.o help.o init.o intrp.o kfile.o last.o
+obj3 = $(NDIRO) ng.o ngdata.o nghash.o ngsrch.o ngstuff.o only.o rcln.o
+obj4 = rcstuff.o respond.o rn.o rthreads.o rt-rn.o rt-select.o search.o sw.o
+obj5 = term.o threads.o util.o unship.o uudecode.o
+#INN#NNTPobj6 = server.o $(NNTPDIR)/libinn.a
+#NO_INN#NNTPobj6 = server.o clientlib.o
 
 obj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5) $(obj6)
@@ -101,8 +106,8 @@
 
 add1 = Makefile.old Pnews Rnmail mt.check
-add2 = bsd config.h config.sh eunice filexp 
+add2 = bsd config.h eunice filexp 
 add3 = loc makedepend makedir mbox.saver ndir.h newsetup
 add4 = newsgroups newsnews norm.saver
-add5 = pdp11 usg v7 ultrix sun hp-ux sgi xenix next server.h mips uts
+add5 = pdp11 usg v7 ultrix sun hp-ux sgi xenix next server.h mips uts DGUX
 add6 = all pyr grimble .falseactive Pnews.header s5uniq sigtest stardent
 
@@ -131,9 +136,11 @@
 	$(CC) $(LDFLAGS) mtgroups.o mt-misc.o -lcurses $(libs) -o mtgroups
 
-#NNTPgetactive: getactive.o server.o clientlib.o
-#NNTP	$(CC) $(LDFLAGS) getactive.o server.o clientlib.o -o getactive $(libs)
+#INN#NNTPgetactive: getactive.o server.o $(NNTPDIR)/libinn.a
+#INN#NNTP	$(CC) $(LDFLAGS) getactive.o server.o -o getactive $(libs) $(NNTPDIR)/libinn.a
+#NO_INN#NNTPgetactive: getactive.o server.o clientlib.o
+#NO_INN#NNTP	$(CC) $(LDFLAGS) getactive.o server.o clientlib.o -o getactive $(libs)
 
-#NNTPclientlib.o:
-#NNTP	$(CC) -c $(CFLAGS) $(NNTPINC) $(NNTPDIR)/common/clientlib.c
+#NO_INN#NNTPclientlib.o:
+#NO_INN#NNTP	$(CC) -c $(CFLAGS) $(NNTPINC) $(NNTPDIR)/common/clientlib.c
 
 # if a .h file depends on another .h file...
@@ -149,4 +156,5 @@
 	- if test `pwd` != $(rnbin); then cd $(rnbin); chmod 755 $(public); strip trn tmpthread ; fi
 	- ./makedir `./filexp $(rnlib)`
+	- chmod 755 `./filexp $(rnlib)`
 #MT	- cd `./filexp $(rnlib)`; mv mthreads mthreads.old
 	- if test `pwd` != `./filexp $(rnlib)`; then cp INIT $(private) `./filexp $(rnlib)`; fi
@@ -158,4 +166,5 @@
 for page in $(manpages); do \
 cp $$page $(mansrc)/`basename $$page .1`.$(manext); \
+chmod 444 $(mansrc)/`basename $$page .1`.$(manext); \
 done; \
 #NNTPecho ".so man$(manext)/trn.$(manext)" > $(mansrc)/trrn.$(manext) ; \
@@ -163,12 +172,18 @@
 #MT	- if test `pwd` != $(mtmansrc); then \
 #MTcp mthreads.8 $(mtmansrc)/mthreads.$(mtmanext); \
+#MTchmod 444 $(mtmansrc)/mthreads.$(mtmanext); \
 #MTfi
 
 clean:
+	@ echo 'Use "make realclean" to remove the executables and Configure droppings.'
+	@ echo 'Use "make spotless" to also remove config.sh'
 	rm -f *.o
 
-realclean:
-	rm -f trn mthreads tmpthread *.o core $(addedbyconf) 
-#NNTP	rm -f clientlib.o getactive
+realclean: clean
+	rm -f trn mthreads tmpthread core $(addedbyconf)
+#NNTP	rm -f getactive
+
+spotless: realclean
+	rm -f config.sh
 
 # The following lint has practically everything turned on.  Unfortunately,
@@ -200,4 +215,11 @@
 	@ echo "You haven't done a "'"make depend" yet!'; exit 1
 !NO!SUBS!
+case "$haveinn" in
+undef) sed < Makefile -e '/^#NO_INN/s/^#NO_INN//
+			  /^#INN/d' > Makefile.new ;;
+define) sed < Makefile -e '/^#INN/s/^#INN//
+			  /^#NO_INN/d' > Makefile.new ;;
+esac
+mv Makefile.new Makefile
 case "$isrrn" in
 define) if $test "$serverspool " != " " ; then
Index:NEW
@@ -1,3 +1,3 @@
-			TRN 2.0 vs TRN 1.0.3:
+			TRN 2.3 vs TRN 1.0.3:
 
 [If you're upgrading from trn 1.x, at least read the ** LOOK ** section.]
@@ -115,5 +115,5 @@
 	resources to destroy the old database and rebuild it with the new
 	mthreads, put the -o2 option in your global INIT file for a few
-	weeks -- you can remove it once the old article data has expired
+	days -- you can remove it once the old article data has expired
 	(if you begin using the new mthreads, of course).
    o	NOTE that the new mthreads puts its db.init file in the root of the
Index:Pnews.1
@@ -1,5 +1,8 @@
-.\" $Id: Pnews.1,v 4.4.1.1 1991/09/25 19:36:48 sob Exp sob $
+.\" $Id: Pnews.1,v 4.4.3.1 1992/02/01 03:17:20 sob PATCH_3 sob $
 .\" 
 .\" $Log: Pnews.1,v $
+.\" Revision 4.4.3.1  1992/02/01  03:17:20  sob
+.\" Version 4.4 Patchlevel 3
+.\"
 .\" Revision 4.4.1.1  1991/09/25  19:36:48  sob
 .\" Changed quote macro to "standard" one
Index:Pnews.SH
@@ -5,7 +5,13 @@
 $spitshell >Pnews <<!GROK!THIS!
 $startsh
-# $Id: Pnews.SH,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+# $Id: Pnews.SH,v 4.4.4.1 1992/02/23 21:25:39 sob PATCH_4 sob $
 #
 # $Log: Pnews.SH,v $
+# Revision 4.4.4.1  1992/02/23  21:25:39  sob
+# Patch level 4
+#
+# Revision 4.4.3.1  1992/02/01  03:09:32  sob
+# Release 4.4 Patchlevel 3
+#
 # Revision 4.4.2.1  1991/12/01  18:05:42  sob
 # Patchlevel 2
@@ -87,4 +93,5 @@
 loc="$locpref"
 org="$orgpref"
+multistate="$multistatepref"
 city="$citypref"
 state="$statepref"
@@ -139,6 +146,11 @@
 
 case $cntry in
-  can) stpr=Province ;;
-  *)   stpr=State ;;
+  can) Stpr=Province ; stpr=province ;;
+  *)   Stpr=State ; stpr=state ;;
+esac
+
+case $multistate in
+  pnw) multistpr="Pacific NorthWest" ;;
+  *)   multistpr="Multi-State Area" ;;
 esac
 
@@ -269,6 +281,9 @@
     $echo 'This program posts news to many machines throughout the country.'
     ;;
+$multistate.*)
+    $echo "This program posts news to many machines throughout the ${multistpr}."
+    ;;
 $state.*)
-    $echo 'This program posts news to many machines throughout the state.'
+    $echo "This program posts news to many machines throughout the ${stpr}."
     ;;
 $city.*)
@@ -523,5 +538,5 @@
 	    set X ${USER-${LOGNAME-`who am i`}} unknown
 	    shift
-	    $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $AUTHORCOPY "From $1 `date`"
+	    $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $AUTHORCOPY "From $1 `LANG= date`"
 	    if $test $? -eq 0 ; then
 		$echo "Article appended to $AUTHORCOPY"
@@ -607,5 +622,6 @@
     Organization:	$org
     City:		$city
-    $stpr:  		$state
+    $Stpr:  		$state
+    $multistpr:	$multistate
     Country:		$cntry
     Continent:		$cont
@@ -631,5 +647,5 @@
 EOH
 	;;
-    ''|$loc*|$org*|$city*|$state*|$cntry*|$cont*|$defdist)
+    ''|$loc*|$org*|$city*|$state*|$multistate*|$cntry*|$cont*|$defdist)
 	;;
     world*|comp*|news*|sci*|rec*|misc*|soc*|talk*|alt*)
Index:README
@@ -1,3 +1,3 @@
-			  Trn Kit, Version 2.0
+			  Trn Kit, Version 2.x
 
 		    Copyright (c) 1991, Wayne Davison
@@ -158,5 +158,5 @@
 
 11) IMPORTANT!  Help save the world!  Communicate any problems and suggested
-    patches to Wayne Davison (davison@borland.com) so we can keep the world
+    patches to Wayne Davison <davison@borland.com> so we can keep the world
     in sync.  If you have a problem, there's someone else out there who either
     has had or will have the same problem.  If the problem affects regular rn,
Index:Rnmail.1
@@ -1,5 +1,8 @@
-.\" $Id: Rnmail.1,v 4.4.1.1 1991/09/25 19:36:48 sob Exp sob $
+.\" $Id: Rnmail.1,v 4.4.3.1 1992/02/01 03:17:20 sob PATCH_3 sob $
 .\" 
 .\" $Log: Rnmail.1,v $
+.\" Revision 4.4.3.1  1992/02/01  03:17:20  sob
+.\" Version 4.4 Patchlevel 3
+.\"
 .\" Revision 4.4.1.1  1991/09/25  19:36:48  sob
 .\" Changed quote macro to "standard" one
Index:Rnmail.SH
@@ -5,7 +5,13 @@
 $spitshell >Rnmail <<!GROK!THIS!
 $startsh
-# $Id: Rnmail.SH,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+# $Id: Rnmail.SH,v 4.4.4.1 1992/02/23 21:25:39 sob PATCH_4 sob $
 # 
 # $Log: Rnmail.SH,v $
+# Revision 4.4.4.1  1992/02/23  21:25:39  sob
+# Patch level 4
+#
+# Revision 4.4.3.1  1992/02/01  03:09:32  sob
+# Release 4.4 Patchlevel 3
+#
 # Revision 4.4  1991/09/09  20:18:23  sob
 # release 4.4
@@ -325,5 +331,5 @@
 	    set X ${USER-${LOGNAME-`who am i`}} unknown
 	    shift
-	    $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $MAILRECORD "From $1 `date`"
+	    $rnlib/mbox.saver $tmpart "." "." 0 0 Pnews $MAILRECORD "From $1 `LANG= date`"
 	    if $test $? -eq 0 ; then
 		$echo "Message appended to $MAILRECORD"
Index:addng.c
@@ -1,5 +1,8 @@
-/* $Id: addng.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: addng.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: addng.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
@@ -39,11 +42,4 @@
 #include "INTERN.h"
 #include "addng.h"
-
-#ifdef TZSET
-#include <time.h>
-#else
-#include <sys/time.h>
-#include <sys/timeb.h>
-#endif
 
 void
Index:addng.h
@@ -1,5 +1,8 @@
-/* $Id: addng.h,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: addng.h,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: addng.h,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:art.c
@@ -1,3 +1,3 @@
-/* $Id: art.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: art.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  *
@@ -508,14 +508,18 @@
 #endif
 			{
-			    putchar('^');
-			    if (highlight == artline && *UC && marking == 1) {
-				backspace();
-				underchar();
-				putchar(*bufptr+64);
-				backspace();
-				underchar();
+			    if (dont_filter_control)
+				putchar(*bufptr);
+			    else {
+				putchar('^');
+				if (highlight == artline && *UC && marking == 1) {
+				    backspace();
+				    underchar();
+				    putchar(*bufptr+64);
+				    backspace();
+				    underchar();
+				}
+				else
+				    putchar(*bufptr+64);
 			    }
-			    else
-				putchar(*bufptr+64);
 			}
 			bufptr++;
@@ -875,4 +879,5 @@
     case '[':	case ']':
     case '{':	case '}':
+    case '(':   case ')':
     case '+':   case ':':
 #endif
@@ -905,5 +910,5 @@
 #endif
     case Ctl('x'):
-    case 'z':
+    case 'z':	case 'Z':
     case '^':
 
Index:artio.c
@@ -1,5 +1,8 @@
-/* $Id: artio.c,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: artio.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: artio.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:artsrch.c
@@ -1,5 +1,8 @@
-/* $Id: artsrch.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: artsrch.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: artsrch.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
@@ -296,7 +299,6 @@
 	
 #ifdef USETHREADS
-	if (ThreadedGroup)
+	if (ThreadedGroup) {
 	    find_article(art);
-	if (p_art) {
 	    if (mode != 't')
 		strcpy(subj_buf, "Subject: ");
@@ -303,6 +305,9 @@
 	    else
 		*subj_buf = '\0';
-	    if (p_art->subject != -1)
+	    if (p_art && p_art->subject != -1) {
+		if (!(p_art->flags & ROOT_ARTICLE))
+		    strcat(subj_buf,"Re: ");
 		strcat(subj_buf,subject_ptrs[p_art->subject]);
+	    }
 	}
 	else
Index:artsrch.h
@@ -1,5 +1,8 @@
-/* $Id: artsrch.h,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: artsrch.h,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: artsrch.h,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:autosub.c
@@ -1,6 +1,9 @@
 /*
- * $Id: autosub.c,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+ * $Id: autosub.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: autosub.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:bits.c
@@ -1,5 +1,8 @@
-/* $Id: bits.c,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: bits.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: bits.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:cheat.c
@@ -1,5 +1,8 @@
-/* $Id: cheat.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: cheat.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: cheat.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
@@ -29,4 +32,5 @@
 #include "search.h"
 #include "ng.h"
+#include "ngdata.h"
 #include "bits.h"
 #include "artio.h"
@@ -34,4 +38,8 @@
 #include "artsrch.h"
 #include "head.h"
+#ifdef USETHREADS
+#include "threads.h"
+#include "rthreads.h"
+#endif
 #include "INTERN.h"
 #include "cheat.h"
@@ -64,4 +72,23 @@
     }
 #endif
+#endif
+
+#ifdef USETHREADS
+    if (ThreadedGroup) {
+	int save_art = art;
+	PACKED_ARTICLE *save_p_art = p_art;
+
+	follow_thread('n');
+#ifdef SERVER
+	nntpopen(art,GET_HEADER);
+#else
+	artopen(art);
+#endif
+        art = save_art;
+	p_art = save_p_art;
+    }
+    else
+#endif /* USETHREADS */
+#ifdef ARTSEARCH
     if (srchahead && srchahead < art) {	/* in ^N mode? */
 	char *pattern;
@@ -123,5 +150,5 @@
     }
     else
-#endif
+#endif /* ARTSEARCH */
     {
 	if (art+1 <= lastart)/* how about a pre-fetch? */
@@ -133,5 +160,5 @@
     }
 }
-#endif
+#endif /* PENDING */
 
 /* see what else we can do while they are reading */
@@ -147,4 +174,8 @@
     if (!in_ng || !srchahead)
 	return;
+#ifdef USETHREADS
+    if (ThreadedGroup)
+	return;
+#endif
     if (oldart)			/* remember where we were in art */
 	oldartpos = ftell(artfp);
Index:common.h
@@ -1,5 +1,11 @@
-/* $Id: common.h,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: common.h,v 4.4.4.1 1992/02/23 21:25:39 sob PATCH_4 sob $
  * 
  * $Log: common.h,v $
+ * Revision 4.4.4.1  1992/02/23  21:25:39  sob
+ * Patch level 4
+ *
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
@@ -69,4 +75,11 @@
 #endif
 
+#ifdef TZSET
+# include <time.h>
+#else
+# include <sys/time.h>
+# include <sys/timeb.h>
+#endif
+
 #define BITSPERBYTE 8
 #ifdef pdp11
@@ -456,5 +469,5 @@
 /* default characters to use in the selection menu */
 #ifndef SELECTCHARS
-#   define SELECTCHARS "abcdefgijlorstuvwxz1234567890"
+#   define SELECTCHARS "abdefgijlorstuvwxz1234567890"
 #endif
 
@@ -638,15 +651,7 @@
 #ifndef NEWSHEADER		/* % */
 #   ifdef CONDSUB
-#ifdef INTERNET
-#	define NEWSHEADER "Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \nDistribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\n\n"
-#else
 #	define NEWSHEADER "Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \nDistribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\n\n"
-#endif
 #   else
-#	ifdef INTERNET
-#	    define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
-#	else
-#	    define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
-#	endif
+#	define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
 #   endif
 #endif
@@ -691,7 +696,7 @@
 #   ifdef MININACT		/* 2.10.2 site? */
 #       ifdef SERVER
-#           define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %`date`\""
+#           define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %`LANG= date`\""
 #       else
-#	    define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %`date`\""
+#	    define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %`LANG= date`\""
 #	endif
 #   else
@@ -752,18 +757,10 @@
 /* how to cancel an article, continued */
 #ifndef CANCELHEADER
-#ifdef INTERNET
-#   define CANCELHEADER "From: %L@%H (%N)\nNewsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nDistribution: %D\nOrganization: %o\n\n%i was cancelled from within trn.\n"
-#else
-#   define CANCELHEADER "From:%L@%H.UUCP (%N)\nNewsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nDistribution: %D\nOrganization: %o\n\n%i was cancelled from within trn.\n"
+#   define CANCELHEADER "Newsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nDistribution: %D\nOrganization: %o\n\n%i was cancelled from within trn.\n"
 #endif
-#endif
 
 /* how to supersede an article */
 #ifndef SUPERSEDEHEADER
-#ifdef INTERNET
-#   define SUPERSEDEHEADER "From: %L@%H (%N)\nNewsgroups: %n\nSupersedes: %i\nSubject: %S\nDistribution: %D\nOrganization: %o\n"
-#else
-#   define SUPERSEDEHEADER "From:%L@%H.UUCP (%N)\nNewsgroups: %n\nSupersedes: %i\nSubject: %S\nDistribution: %D\nOrganization: %o\n"
-#endif
+#	define SUPERSEDEHEADER "Newsgroups: %n\nSubject: %S\nSummary: %[summary]\nExpires: %[expires]\nReferences: %[references]\nSupersedes: %i\nSender: %[sender]\nFollowup-To: %[followup-to]\nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
 #endif
 
@@ -874,9 +871,11 @@
 #endif
 
+EXT bool dont_filter_control INIT(FALSE);		/* -j */
 EXT bool mbox_always INIT(FALSE);			/* -M */
 EXT bool norm_always INIT(FALSE);			/* -N */
 EXT bool thread_always INIT(FALSE);			/* -a */
+EXT bool breadth_first INIT(FALSE);			/* -b */
 EXT bool olden_days INIT(FALSE);			/* -o */
-EXT bool checkflag INIT(FALSE);			/* -c */
+EXT bool checkflag INIT(FALSE);				/* -c */
 EXT bool suppress_cn INIT(FALSE);			/* -s */
 EXT int countdown INIT(5);	/* how many lines to list before invoking -s */
Index:config.h.SH
@@ -35,7 +35,10 @@
  * that running config.h.SH again will wipe out any changes you've made.
  * For a more permanent change edit config.sh and rerun config.h.SH.
- * $Id: config.h.SH,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+ * $Id: config.h.SH,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: config.h.SH,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ *Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Fixed problems with CTRLA flag.
@@ -193,3 +196,4 @@
 #$syslog2 USESYSLOG $syslog	/* use syslog for mthreads' log messages */
 #$xthread XTHREAD 	/* get thread files via NNTP */
+#$haveinn HAVE_INN 	/* are we running inn? */
 EOT
Index:final.c
@@ -1,5 +1,8 @@
-/* $Id: final.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: final.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: final.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
@@ -35,4 +38,5 @@
 #include "ngdata.h"
 #include "artio.h"
+#include "intrp.h"
 #ifdef SERVER
 #include "server.h"
@@ -41,4 +45,8 @@
 #include "final.h"
 
+#ifndef sigmask
+#define sigmask(m)	(1 << ((m)-1))
+#endif
+
 void
 final_init()
@@ -112,4 +120,7 @@
 	chdir("/usr/tmp");
 	sigset(SIGILL,SIG_DFL);
+#ifdef SIGBLOCK
+	sigsetmask(sigblock(0) & ~(sigmask(SIGILL) | sigmask(SIGIOT)));
+#endif
 	abort();
     }
@@ -186,6 +197,10 @@
     }
 #endif
-    if (panic)
+    if (panic) {
+#ifdef SIGBLOCK
+      sigsetmask(sigblock(0) & ~(sigmask(SIGILL) | sigmask(SIGIOT)));
+#endif
 	abort();
+      }
     (void) sigset(SIGILL,SIG_DFL);
     panic = TRUE;			/* disable terminal I/O */
Index:final.h
@@ -1,5 +1,8 @@
-/* $Id: final.h,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: final.h,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  * 
  * $Log: final.h,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:hdbm.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 1992 Geoffrey Collyer
+ * All rights reserved.
+ * Written by Geoffrey Collyer.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company, the Regents of the University of California, or
+ * the Free Software Foundation.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ *    software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ *    explicit claim or by omission.  Since few users ever read sources,
+ *    credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.  Since few users
+ *    ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+/*
+ * general-purpose in-core hashing, dbm interface
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hdbm.h"
+#include "hdbmint.h"
+
+/* tunable parameters */
+#define RETAIN 300		/* retain & recycle this many HASHENTs */
+
+/* fundamental constants */
+#define YES 1
+#define NO 0
+
+static HASHENT *hereuse = NULL;
+static int reusables = 0;
+
+static hefree();
+
+
+static unsigned				/* not yet taken modulus table size */
+hdbmdef(key)
+HDBMDATUM key;
+{
+	register char *s = key.dat_ptr;
+	register unsigned len = key.dat_len;
+	register unsigned hash = 0;
+
+	while (len-- > 0)
+		hash += *s++;
+	return hash;
+}
+
+HASHTABLE *
+hdbmcreate(size, hashfunc)
+register unsigned size;			/* a crude guide to size */
+unsigned (*hashfunc)();
+{
+	register HASHTABLE *tbl;
+	register HASHENT **hepp;
+	/*
+	 * allocate HASHTABLE and (HASHENT *) array together to reduce the
+	 * number of malloc calls.  this idiom ensures correct alignment of
+	 * the array.
+	 * dmr calls the one-element array trick `unwarranted chumminess with
+	 * the compiler' though.
+	 */
+	register struct alignalloc {
+		HASHTABLE ht;
+		HASHENT *hepa[1];	/* longer than it looks */
+	} *aap;
+
+	aap = (struct alignalloc *)
+		malloc(sizeof *aap + (size-1)*sizeof(HASHENT *));
+	if (aap == NULL)
+		return NULL;
+	tbl = &aap->ht;
+	tbl->ht_size = (size == 0? 1: size);	/* size of 0 is nonsense */
+	tbl->ht_magic = HASHMAG;
+	tbl->ht_hash = (hashfunc == NULL? hdbmdef: hashfunc);
+	tbl->ht_addr = hepp = aap->hepa;
+	while (size-- > 0)
+		hepp[size] = NULL;
+	return tbl;
+}
+
+/*
+ * free all the memory associated with tbl, erase the pointers to it, and
+ * invalidate tbl to prevent further use via other pointers to it.
+ */
+hdbmdestroy(tbl)
+register HASHTABLE *tbl;
+{
+	register unsigned idx;
+	register HASHENT *hp, *next;
+	register HASHENT **hepp;
+	register int tblsize;
+
+	if (tbl == NULL || BADTBL(tbl))
+		return;
+	tblsize = tbl->ht_size;
+	hepp = tbl->ht_addr;
+	for (idx = 0; idx < tblsize; idx++) {
+		for (hp = hepp[idx]; hp != NULL; hp = next) {
+			next = hp->he_next;
+			hp->he_next = NULL;
+			hefree(hp);
+		}
+		hepp[idx] = NULL;
+	}
+	tbl->ht_magic = 0;		/* de-certify this table */
+	tbl->ht_addr = NULL;
+	free((char *)tbl);
+}
+
+/*
+ * The returned value is the address of the pointer that refers to the
+ * found object.  Said pointer may be NULL if the object was not found;
+ * if so, this pointer should be updated with the address of the object
+ * to be inserted, if insertion is desired.
+ */
+static HASHENT **
+hdbmfind(tbl, key)
+register HASHTABLE *tbl;
+HDBMDATUM key;
+{
+	register HASHENT *hp, *prevhp = NULL;
+	register char *hpkeydat, *keydat = key.dat_ptr;
+	register int keylen = key.dat_len;
+	register HASHENT **hepp;
+	register unsigned size; 
+
+	if (BADTBL(tbl))
+		return NULL;
+	size = tbl->ht_size;
+	if (size == 0)			/* paranoia: avoid division by zero */
+		size = 1;
+	hepp = &tbl->ht_addr[(*tbl->ht_hash)(key) % size];
+	for (hp = *hepp; hp != NULL; prevhp = hp, hp = hp->he_next) {
+		hpkeydat = hp->he_key.dat_ptr;
+		if (hp->he_key.dat_len == keylen && hpkeydat[0] == keydat[0] &&
+		    memcmp(hpkeydat, keydat, keylen) == 0)
+			break;
+	}
+	/* assert: *(returned value) == hp */
+	return (prevhp == NULL? hepp: &prevhp->he_next);
+}
+
+static HASHENT *
+healloc()					/* allocate a hash entry */
+{
+	register HASHENT *hp;
+
+	if (hereuse == NULL)
+		return (HASHENT *)malloc(sizeof(HASHENT));
+	/* pull the first reusable one off the pile */
+	hp = hereuse;
+	hereuse = hereuse->he_next;
+	hp->he_next = NULL;			/* prevent accidents */
+	reusables--;
+	return hp;
+}
+
+static
+hefree(hp)					/* free a hash entry */
+register HASHENT *hp;
+{
+	if (reusables >= RETAIN)		/* compost heap is full? */
+		free((char *)hp);		/* yup, just pitch this one */
+	else {					/* no, just stash for reuse */
+		++reusables;
+		hp->he_next = hereuse;
+		hereuse = hp;
+	}
+}
+
+int
+hdbmstore(tbl, key, data)
+register HASHTABLE *tbl;
+HDBMDATUM key, data;
+{
+	register HASHENT *hp;
+	register HASHENT **nextp;
+
+	if (BADTBL(tbl))
+		return NO;
+	nextp = hdbmfind(tbl, key);
+	if (nextp == NULL)
+		return NO;
+	hp = *nextp;
+	if (hp == NULL) {			/* absent; allocate an entry */
+		hp = healloc();
+		if (hp == NULL)
+			return NO;
+		hp->he_next = NULL;
+		hp->he_key = key;
+		*nextp = hp;			/* append to hash chain */
+	}
+	hp->he_data = data;	/* supersede any old data for this key */
+	return YES;
+}
+
+/* return any existing entry for key; otherwise call allocator to make one */
+HDBMDATUM
+hdbmentry(tbl, key, allocator)
+register HASHTABLE *tbl;
+HDBMDATUM key;
+HDBMDATUM (*allocator)();
+{
+	register HASHENT *hp;
+	register HASHENT **nextp;
+	static HDBMDATUM errdatum = { NULL, 0 };
+
+	if (BADTBL(tbl))
+		return errdatum;
+	nextp = hdbmfind(tbl, key);
+	if (nextp == NULL)
+		return errdatum;
+	hp = *nextp;
+	if (hp == NULL) {			/* absent; allocate an entry */
+		hp = healloc();
+		if (hp == NULL)
+			return errdatum;
+		hp->he_next = NULL;
+		hp->he_key = key;
+		hp->he_data = (*allocator)(key);
+		*nextp = hp;			/* append to hash chain */
+	}
+	return hp->he_data;
+}
+
+int
+hdbmdelete(tbl, key)
+register HASHTABLE *tbl;
+HDBMDATUM key;
+{
+	register HASHENT *hp;
+	register HASHENT **nextp;
+
+	nextp = hdbmfind(tbl, key);
+	if (nextp == NULL)
+		return NO;
+	hp = *nextp;
+	if (hp == NULL)				/* absent */
+		return NO;
+	*nextp = hp->he_next;			/* skip this entry */
+	hp->he_next = NULL;
+	hp->he_data.dat_ptr = hp->he_key.dat_ptr = NULL;
+	hefree(hp);
+	return YES;
+}
+
+HDBMDATUM					/* data corresponding to key */
+hdbmfetch(tbl, key)
+register HASHTABLE *tbl;
+HDBMDATUM key;
+{
+	register HASHENT *hp;
+	register HASHENT **nextp;
+	static HDBMDATUM errdatum = { NULL, 0 };
+
+	if (BADTBL(tbl))
+		return errdatum;
+	nextp = hdbmfind(tbl, key);
+	if (nextp == NULL)
+		return errdatum;
+	hp = *nextp;
+	if (hp == NULL)				/* absent */
+		return errdatum;
+	else
+		return hp->he_data;
+}
+
+/*
+ * visit each entry by calling nodefunc at each, with key, data and hook as
+ * arguments.  hook is an attempt to allow side-effects and reentrancy at
+ * the same time.
+ */
+hdbmwalk(tbl, nodefunc, hook)
+HASHTABLE *tbl;
+register int (*nodefunc)();
+register char *hook;				/* (void *) really */
+{
+	register unsigned idx;
+	register HASHENT *hp;
+	register HASHENT **hepp;
+	register int tblsize;
+
+	if (BADTBL(tbl))
+		return;
+	hepp = tbl->ht_addr;
+	tblsize = tbl->ht_size;
+	for (idx = 0; idx < tblsize; idx++)
+		for (hp = hepp[idx]; hp != NULL; hp = hp->he_next)
+			(*nodefunc)(hp->he_key, hp->he_data, hook);
+}
Index:hdbm.h
@@ -0,0 +1,18 @@
+/*
+ * general-purpose in-core hashing, dbm interface
+ */
+
+#define HDBMDATUM struct hdbmdatum
+HDBMDATUM {
+	char	*dat_ptr;
+	unsigned dat_len;
+};
+
+#ifndef HASHTABLE
+#define HASHTABLE struct hashtable
+#endif
+
+extern HASHTABLE *hdbmcreate();
+extern hdbmdestroy(), hdbmwalk();
+extern int hdbmstore(), hdbmdelete();
+extern HDBMDATUM hdbmfetch(), hdbmentry();
Index:hdbmint.h
@@ -0,0 +1,25 @@
+/*
+ * general-purpose in-core hashing, dbm interface (internals)
+ */
+
+#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
+
+#define BADTBL(tbl)	(((tbl)->ht_magic&BYTEMASK) != HASHMAG)
+
+#define HASHMAG  0257
+#define BYTEMASK 0377
+
+#define HASHENT struct hashent
+
+HASHENT {
+	HASHENT	*he_next;		/* in hash chain */
+	HDBMDATUM he_key;		/* to verify a match */
+	HDBMDATUM he_data;
+};
+
+HASHTABLE {
+	HASHENT **ht_addr;		/* array of HASHENT pointers */
+	unsigned ht_size;
+	char	ht_magic;
+	unsigned (*ht_hash)();
+};
Index:head.c
@@ -1,5 +1,11 @@
-/* $Id: head.c,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: head.c,v 4.4.4.1 1992/02/23 21:25:39 sob PATCH_4 sob $
  *
  * $Log: head.c,v $
+ * Revision 4.4.4.1  1992/02/23  21:25:39  sob
+ * Patch level 4
+ *
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
@@ -171,4 +177,6 @@
     if (parsed_art == artnum)
 	return 0;
+    if (artnum > lastart)
+	return -1;
     /* no maybe about it now */
 #ifdef SERVER
@@ -217,5 +225,5 @@
 	    subj_list[i] = Nullch;
     }
-    if (!artnum || artnum > lastart)
+    if (artnum < absfirst || artnum > lastart)
 	s = nullstr;
     else
Index:head.h
@@ -1,5 +1,8 @@
-/* $Id: head.h,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: head.h,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: head.h,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:help.c
@@ -1,5 +1,8 @@
-/* $Id: help.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: help.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: help.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
@@ -91,4 +94,5 @@
 	selected for browsing.  Entering an empty group browses all threads.\n\
 [,],{,} Go to parent/child/root/leaf in thread.\n\
+(,)	Go to article's previous/next sibling.\n\
 \n\
 ",NOMARKING)) ||
@@ -149,4 +153,5 @@
 [,]	Go to article's parent/child.\n\
 {,}	Go to tree's root/leaf.\n\
+(,)	Go to article's previous/next sibling.\n\
 t	Display the entire article tree and all its subjects.\n\
 ",NOMARKING)) ||
@@ -474,4 +479,5 @@
 	reading if articles were selected, else go to next page.\n\
 J	Junk all selected articles (mark them as read).\n\
+c	Catch up -- marks ALL articles as read without chasing xrefs.\n\
 ^K	Edit local KILL file (the one for this newsgroup).\n\
 :cmd	Perform a command on all the selected articles.\n\
Index:init.c
@@ -1,5 +1,8 @@
-/* $Id: init.c,v 4.4 1991/09/09 20:18:23 sob Exp sob $
+/* $Id: init.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: init.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:18:23  sob
  * release 4.4
Index:intrp.c
@@ -1,5 +1,8 @@
-/* $Id: intrp.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: intrp.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: intrp.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
@@ -91,10 +94,4 @@
 {
     char *getlogin();
-
-    spool = savestr(filexp(SPOOL));	/* usually /usr/spool/news */
-#ifdef USETHREADS
-    mtlib = savestr(filexp(MTLIB));
-    threaddir = savestr(filexp(THREAD_DIR));
-#endif
     
     /* get environmental stuff */
@@ -136,4 +133,10 @@
     if (logname == Nullch)
 	logname = savestr(getlogin());
+#endif
+
+    spool = savestr(filexp(SPOOL));	/* usually /usr/spool/news */
+#ifdef USETHREADS
+    mtlib = savestr(filexp(MTLIB));
+    threaddir = savestr(filexp(THREAD_DIR));
 #endif
 
Index:intrp.h
@@ -1,5 +1,8 @@
-/* $Id: intrp.h,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
+/* $Id: intrp.h,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: intrp.h,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  * Patchlevel 2 changes
Index:kfile.c
@@ -143,5 +143,5 @@
 		break;
 	    case SRCH_SUBJDONE:
-		fputs("\tsubject not found (???)\n",stdout) FLUSH;
+		fputs("\tsubject not found (?)\n",stdout) FLUSH;
 		break;
 	    case SRCH_NOTFOUND:
Index:last.c
@@ -1,5 +1,8 @@
-/* $Id: last.c,v 4.4 1991/09/09 20:23:31 sob Exp sob $
+/* $Id: last.c,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: last.c,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:23:31  sob
  * release 4.4
Index:last.h
@@ -1,5 +1,8 @@
-/* $Id: last.h,v 4.4 1991/09/09 20:23:31 sob Exp sob $
+/* $Id: last.h,v 4.4.3.1 1992/02/01 03:09:32 sob PATCH_3 sob $
  *
  * $Log: last.h,v $
+ * Revision 4.4.3.1  1992/02/01  03:09:32  sob
+ * Release 4.4 Patchlevel 3
+ *
  * Revision 4.4  1991/09/09  20:23:31  sob
  * release 4.4
Index:mt-lint.h
@@ -1,8 +1,4 @@
-/* $Id: mt-lint.h,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
+/* $Id: mt-lint.h,v 2.3 1992/12/14 00:14:01 davison Trn $
 **
-** $Log: mt-lint.h,v $
-** Revision 4.4.3.1  1991/11/22  04:12:15  davison
-** Trn Release 2.0
-** 
 ** Handle brain-dead lints that only have 6 significant-character names.
 */
Index:mt-misc.c
@@ -1,8 +1,3 @@
-/* $Id: mt-misc.c,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
-**
-** $Log: mt-misc.c,v $
-** Revision 4.4.3.1  1991/11/22  04:12:15  davison
-** Trn Release 2.0
-** 
+/* $Id: mt-misc.c,v 2.3 1992/12/14 00:14:02 davison Trn $
 */
 
@@ -160,4 +155,5 @@
 	    wrap_it_up(1);
 	}
+	break;
     }
     default:			/* all "normal" names are relative to SPOOL */
Index:mt-process.c
@@ -1,8 +1,3 @@
-/* $Id: mt-process.c,v 4.4.3.1 1991/11/22 04:12:15 davison Trn $
-**
-** $Log: mt-process.c,v $
-** Revision 4.4.3.1  1991/11/22  04:12:15  davison
-** Trn Release 2.0
-** 
+/* $Id: mt-process.c,v 2.3 1992/12/14 00:14:03 davison Trn $
 */
 
@@ -18,13 +13,4 @@
 #include "bits.h"
 
-#include <time.h>
-#ifndef TZSET
-# include <sys/timeb.h>
-#endif
-
-#if defined(SERVER) && !defined(USLEEP)
-# include <sys/time.h>
-#endif
-
 #define buff buf
 
@@ -330,7 +316,29 @@
 	/* List all articles and use ctl_set() to keep track of what's there. */
 #ifdef SERVER
-	sprintf(buff, "XHDR message-id %ld-%ld", (long)absfirst, (long)lastart);
-	put_server(buff);
-	if (get_server(buff, sizeof buff) == 0 && *buff == CHAR_OK) {
+	/* Try my after-market LISTGROUP command before trying XHDR. */
+	put_server("LISTGROUP");
+	if (get_server(buff, sizeof buff) != 0) {
+	    *buff = CHAR_ERR;
+	}
+	/* If it was a command syntax error (not an unrecongnized command),
+	** then they have the old LISTGROUP that requires a newsgroup name.
+	*/
+	if (atoi(buff) == ERR_CMDSYN) {
+	    extern char line[];
+	    sprintf(buff, "LISTGROUP %s", line);
+	    put_server(buff);
+	    if (get_server(buff, sizeof buff) != 0) {
+		*buff = CHAR_ERR;
+	    }
+	}
+	if (*buff != CHAR_OK) {
+	    sprintf(buff, "XHDR message-id %ld-%ld",
+				(long)absfirst, (long)lastart);
+	    put_server(buff);
+	    if (get_server(buff, sizeof buff) != 0) {
+		*buff = CHAR_ERR;
+	    }
+	}
+	if (*buff == CHAR_OK) {
 	    while (1) {
 		if (get_server(buff, sizeof buff) < 0) {
@@ -535,5 +543,5 @@
 order_roots()
 {
-    register ROOT *root, *next, *search;
+    register ROOT *root, *next, *search, *link;
 
     /* If we don't have at least two roots, we're done! */
@@ -551,9 +559,11 @@
 	    root_root = root;
 	} else {
-	    while (search->link
-	     && search->link->articles->date < root->articles->date) {
-		search = search->link;
+	    register time_t radate = root->articles->date;
+
+	    while ((link = search->link) != NULL
+	     && link->articles->date < radate) {
+		search = link;
 	    }
-	    root->link = search->link;
+	    root->link = link;
 	    search->link = root;
 	}
@@ -636,4 +646,337 @@
 }
 
+#ifndef OLD_AUTHOR_CODE
+/* Name-munging routines written by Ross Ridge.  Public Domain.
+** Enhanced by Wayne Davison.
+*/
+
+/* If necessary, compress a net user's full name by playing games with
+** initials and the middle name(s).  If we start with "Ross Douglas Ridge"
+** we try "Ross D Ridge", "Ross Ridge", "R D Ridge" and finally "R Ridge"
+** before simply truncating the thing.  We also turn "R. Douglas Ridge"
+** into "Douglas Ridge" if it fits, otherwise it goes through the normal
+** modification route.
+*/
+static char *
+compress_name(name, max)
+char *name;
+int max;
+{
+    register char *s, *last, *mid, *d;
+    register int len, namelen, midlen;
+    int notlast;
+
+    /* First remove white space from both ends. */
+    while (isspace(*name)) {
+	name++;
+    }
+    if ((len = strlen(name)) == 0) {
+	return name;
+    }
+    s = name + len - 1;
+    while (isspace(*s)) {
+	s--;
+    }
+    s[1] = '\0';
+    if (s - name + 1 <= max) {
+	return name;
+    }
+
+    /* Look for characters that likely mean the end of the name
+    ** and the start of some hopefully uninteresting additional info.
+    ** Spliting at a comma is somewhat questionalble, but since
+    ** "Ross Ridge, The Great HTMU" comes up much more often than 
+    ** "Ridge, Ross" and since "R HTMU" is worse than "Ridge" we do
+    ** it anyways.
+    */
+    for (d = name + 1; *d; d++) {
+	if (*d == ',' || *d == ';' || *d == '(' || *d == '@'
+	 || (*d == '-' && (d[1] == '-' || d[1] == ' '))) {
+	    *d-- = '\0';
+	    s = d;
+	    break;
+	}
+    }
+
+    /* Find the last name */
+    do {
+	notlast = 0;
+	while (isspace(*s)) {
+	    s--;
+	}
+	s[1] = '\0';
+	len = s - name + 1;
+	if (len <= max) {
+	    return name;
+	}
+	while (!isspace(*s)) {
+	    if (s == name) {	/* only one name */
+		name[max] = '\0';
+		return name;
+	    }
+	    if (isdigit(*s)) {	/* probably a phone number */
+		notlast = 1;	/* so chuck it */
+	    }
+	    s--;
+	}
+    } while (notlast);
+
+    last = s-- + 1;
+
+    /* Look for a middle name */
+    while (isspace(*s)) {	/* get rid of any extra space */
+	len--;	
+	s--;
+    }
+    mid = name;
+    while (!isspace(*mid)) {
+	mid++;
+    }
+    namelen = mid - name + 1;
+    if (mid == s+1) {	/* no middle name */
+	mid = 0;
+    } else {
+	*mid++ = '\0';
+	while (isspace(*mid)) {
+	    len--;
+	    mid++;
+	}
+	midlen = s - mid + 2;
+	/* If first name is an initial and middle isn't and it all fits
+	** without the first initial, drop it. */
+	if (len > max && mid != s && mid[1] != '.'
+	 && (!name[1] || (name[1] == '.' && !name[2]))
+	 && len - namelen <= max) {
+	    len -= namelen;
+	    name = mid;
+	    mid = 0;
+	}
+    }
+    s[1] = '\0';
+    if (mid && len > max) {
+	/* Turn middle names into intials */
+	len -= s - mid + 2;
+	d = s = mid;
+	while (*s) {
+	    if (isalpha(*s)) {
+		if (d != mid) {
+		    *d++ = ' ';
+		}
+		*d++ = *s++;
+	    }
+	    while (*s && !isspace(*s)) {
+		s++;
+	    }
+	    while (isspace(*s)) {
+		s++;
+	    }
+	}
+	if (d != mid) {
+	    *d = '\0';
+	    midlen = d - mid + 1;
+	    len += midlen;
+	} else {
+	    mid = 0;
+	}
+    }
+    if (len > max) {
+	/* If the first name fits without the middle initials, drop them */
+	if (mid && len - midlen <= max) {
+	    len -= midlen;
+	    mid = 0;
+	} else {
+	    /* Turn the first name into an initial */
+	    len -= namelen - 2;
+	    name[1] = '\0';
+	    namelen = 2;
+	    if (len > max) {
+		/* Dump the middle initials (if present) */
+		if (mid) {
+		    len -= midlen;
+		    mid = 0;
+		}
+		if (len > max) {
+		    /* Finally just truncate the last name */
+		    last[max - 2] = '\0';
+		}
+	    }
+	}
+    }
+
+    /* Paste the names back together */
+    d = name + namelen;
+    if (mid) {
+	d[-1] = ' ';
+	strcpy(d, mid);
+	d += midlen;
+    }
+    d[-1] = ' ';
+    strcpy(d, last);
+    return name;
+}
+
+/* Compress an email address, trying to keep as much of the local part of
+** the addresses as possible.  The order of precence is @ ! %, but
+** @ % ! may be better...
+*/
+static char *
+compress_address(name, max)
+char *name;
+int max;
+{
+    char *s, *at, *bang, *hack, *start;
+    int len;
+
+    /* Remove white space from both ends. */
+    while (isspace(*name)) {
+	name++;
+    }
+    if ((len = strlen(name)) == 0) {
+	return name;
+    }
+    s = name + len - 1;
+    while (isspace(*s)) {
+	s--;
+    }
+    s[1] = '\0';
+    if (*name == '<') {
+	name++;
+	if (*s == '>') {
+	    *s-- = '\0';
+	}
+    }
+    if ((len = s - name + 1) <= max) {
+	return name;
+    }
+
+    at = bang = hack = NULL;
+    for (s = name + 1; *s; s++) {
+	/* If there's whitespace in the middle then it's probably not
+	** really an email address. */
+	if (isspace(*s)) {
+	    name[max] = '\0';
+	    return name;
+	}
+	switch (*s) {
+	case '@':
+	    if (at == NULL) {
+		at = s;
+	    }
+	    break;
+	case '!':
+	    if (at == NULL) {
+		bang = s;
+		hack = NULL;
+	    }
+	    break;
+	case '%':
+	    if (at == NULL && hack == NULL) {
+		hack = s;
+	    }
+	    break;
+	}
+    }
+    if (at == NULL) {
+	at = name + len;
+    }
+
+    if (hack != NULL) {
+	if (bang != NULL) {
+	    if (at - bang - 1 >= max) {
+		start = bang + 1;
+	    } else if (at - name >= max) {
+		start = at - max;
+	    } else {
+		start = name;
+	    }
+	} else {
+	    start = name;
+	}
+    } else if (bang != NULL) {
+	if (at - name >= max) {
+	    start = at - max;
+	} else {
+	    start = name;
+	}
+    } else {
+	start = name;
+    }
+    if (len - (start - name) > max) {
+	start[max] = '\0';
+    }
+    return start;
+}
+
+/* Extract the full-name part of an email address, returning NULL if not
+** found.
+*/
+static char *
+extract_name(name)
+char *name;
+{
+    char *s;
+    char *lparen, *rparen;
+    char *langle;
+
+    while (isspace(*name)) {
+	name++;
+    }
+
+    lparen = index(name, '(');
+    rparen = rindex(name, ')');
+    langle = index(name, '<');
+    if (!lparen && !langle) {
+	return NULL;
+    } else
+    if (langle && (!lparen || !rparen || lparen > langle || rparen < langle)) {
+	if (langle == name) {
+	    return NULL;
+	}
+	*langle = '\0';
+    } else {
+	name = lparen;
+	*name++ = '\0';
+	while (isspace(*name)) {
+	    name++;
+	}
+	if (name == rparen) {
+	    return NULL;
+	}
+	if (rparen != NULL) {
+	    *rparen = '\0';
+	}
+    }
+
+    if (*name == '"') {
+	name++;
+	while (isspace(*name)) {
+	    name++;
+	}
+	if ((s = rindex(name, '"')) != NULL) {
+	    *s = '\0';
+	}
+    }
+    return name;
+}
+
+/* Try to fit the author name in 16 bytes.  Use the comment portion if
+** present.
+*/
+void
+get_author_str(addr)
+char *addr;
+{
+    char *s;
+
+    if ((s = extract_name(addr)) != NULL) {
+	s = compress_name(s, 16);
+    } else {
+	s = compress_address(addr, 16);
+    }
+    strcpy(author_str, s);
+}
+
+#else  /* Here's the old, simple method in case someone wants it. */
+
 /* Try to fit the author name in 16 bytes.  Use the comment portion in
 ** parenthesis if present.  Cut off non-commented names at the '@' or '%'.
@@ -640,5 +983,4 @@
 ** Then, put as many characters as we can into the 16 bytes, packing multiple
 ** whitespace characters into a single space.
-** We might want to implement a nice name shortening algorithm sometime.
 */
 void
@@ -676,4 +1018,5 @@
     *cp2 = '\0';
 }
+#endif
 
 /* Take a message-id and see if we already know about it.  If so, return it.
@@ -836,6 +1179,7 @@
 	** chain and process our references as usual.
 	*/
-	for (node = article->parent; node; node = node->parent) {
+	for (node = article->parent; node; node = last) {
 	    unlink_child(node);
+	    last = node->parent;
 	    free_article(node);
 	}
@@ -1102,5 +1446,5 @@
 void
 make_root(article)
-ARTICLE *article;
+register ARTICLE *article;
 {
     register ROOT *new, *node;
---8<------8<------8<------8<---cut here--->8------>8------>8------>8---
Xref: sparky news.software.readers:2625 news.software.b:3663