[news.software.b] C News patch of 10-Jan-1990

henry@utzoo.uucp (Henry Spencer) (01/11/90)

This one introduces one major change:  relaynews now completely implements
the silly "Supersedes" header.  We still think that most current uses of
Supersedes do not justify it, but Brad Templeton (always the nuisance :-))
has found a real use for it, and there may be more.  Actually, to head off
yet more silly new headers, we implement an "Also-Control" header that
lets any control-message function be piggybacked on a normal article, and
do "Supersedes" by s/Supersedes:/Also-Control: cancel/.

We've trying to clear our fairly lengthy low-priority-to-be-looked-at
lists, some of it long overdue.  A spacefor variant for Xenix and Microport
Unixes.  Revision of expire and mkdbm to invoke dbmclose(), in preparation
for dbz, and a fake dbmclose() for systems that have dbm but not dbmclose().
Expire's reporting of bad expiry dates is now optional, invoked by -g.
Everything has been checked over to be sure that files under $NEWSARTS
with dots in their names are always ignored, so people who want to annotate
the tree can do so.  Memory leak in histinfo fixed.  Upact and updatemin
use a more sensible name for their temporary file, and are a bit more
paranoid about it.  Spurious system noises in error messages removed.
More fixes for rsh misbehavior.  expire -t output format documented.
Reporting of articles arriving in newsgroups you don't subscribe to.
Beginnings of a new document, notebook/problems, discussing common porting
problems.  And the usual bunch of minor cleanups.

start of patch 10-Jan-1990
(suggested archive name: `pch10Jan90.Z')
this should be run with   patch -p0 <thisfile

The following is a complete list of patches to date.

Prereq: 23-Jun-1989
Prereq: 7-Jul-1989
Prereq: 23-Jul-1989
Prereq: 22-Aug-1989
Prereq: 24-Aug-1989
Prereq: 14-Sep-1989
Prereq: 13-Nov-1989
*** PATCHDATES.old	Wed Jan 10 17:56:27 1990
--- PATCHDATES	Wed Jan 10 17:56:27 1990
***************
*** 1,7 ****
--- 1,8 ----
  23-Jun-1989
  7-Jul-1989
  23-Jul-1989
  22-Aug-1989
  24-Aug-1989
  14-Sep-1989
  13-Nov-1989
+ 10-Jan-1990

Changed files, if any:

*** cnpatch/old/COPYRIGHT	Tue Jun 20 18:55:22 1989
--- COPYRIGHT	Sat Dec 30 23:11:40 1989
***************
*** 1,4 ****
  /*
!  * Copyright (c) University of Toronto 1985, 1986, 1987, 1988, 1989.
   * All rights reserved.
   * Written mostly by Geoffrey Collyer and Henry Spencer.
--- 1,4 ----
  /*
!  * Copyright (c) University of Toronto 1985, 1986, 1987, 1988, 1989, 1990.
   * All rights reserved.
   * Written mostly by Geoffrey Collyer and Henry Spencer.

*** cnpatch/old/ROADMAP	Tue Jun 20 18:55:27 1989
--- ROADMAP	Sat Dec 30 23:11:26 1989
***************
*** 6,10 ****
  	an official part of C News.
  doc	User documentation, including "install" which discusses how to
! 	install C News.
  expire	Expire and friends, including history rebuilding and active-file
  	updating (neither of which is done by expire in C News).  Ought
--- 6,14 ----
  	an official part of C News.
  doc	User documentation, including "install" which discusses how to
! 	install C News.  Although the documentation is supplied as troff
! 	sources, no complex formatting is used and it should be quite
! 	readable even if you don't have a troff to format it.  We supply
! 	preformatted output for doc/install, as doc/install.out, just
! 	as a precaution.
  expire	Expire and friends, including history rebuilding and active-file
  	updating (neither of which is done by expire in C News).  Ought

*** cnpatch/old/batch/Makefile	Mon Nov 13 17:39:42 1989
--- batch/Makefile	Sat Dec 30 02:13:07 1989
***************
*** 44,47 ****
--- 44,50 ----
  	for f in $(PGMS) ; do cmp $(PGMDIR)/$$f $$f ; done
  
+ check:	$(PGMS)
+ 	for f in $(PGMS) ; do cmp $(PGMDIR)/$$f $$f || true ; done
+ 
  newsinstall:	batchparms
  	-if test ! -d $(OUTGOING) ; then mkdir $(OUTGOING) ; fi

*** cnpatch/old/conf/Makefile	Thu Aug 24 16:39:47 1989
--- conf/Makefile	Sun Dec 31 01:07:31 1989
***************
*** 16,19 ****
--- 16,20 ----
  DIRS = batch conf expire h hfake input $(LIBDIRS) misc relay rna
  CMPDIRS = batch conf expire input man misc relay rna
+ RDIRS = batch expire input relay
  SHS = doit.root doit.bin doit.news again.root
  
***************
*** 46,51 ****
  	./subst -f substitutions `sed 's;^;../;' subst.hs subst.gc`
  
- spacefors:	spacefor.sysv spacefor.v7 spacefor.sgi spacefor.bsd spacefor.ultrix spacefor.null
- 
  spacefor.bsd:	spacefor.proto Makefile
  	cp spacefor.proto $@
--- 47,50 ----
***************
*** 57,60 ****
--- 56,62 ----
  	sed '/dfunit=/s/1024/512/;/awk/s~|~| sed "s/.*:/: :/" |~;/nf = 4/s//nf = 3/;/stupid/s/4BSD/System V/' spacefor.proto >$@
  
+ spacefor.xenix:	spacefor.proto Makefile
+ 	sed '/dfunit=/s/1024/512/;/awk/s~|~| sed "s/.*:/: :/" |~;/nr = 2/s//nr = 1/;/nf = 4/s//nf = 3/;/stupid/s/4BSD/Xenix/' spacefor.proto >$@
+ 
  spacefor.sgi:	spacefor.proto Makefile
  	sed '/dfunit=/s/1024/512/;/nf = 4/s//nf = 5/;/stupid/s/4BSD/SGI/' spacefor.proto >$@
***************
*** 86,90 ****
  clean:
  	rm -f spacefor.sysv spacefor.v7 spacefor.null queuelen.null
! 	rm -f spacefor.sgi
  	rm -f config mailname organization server whoami hostname errlog
  	rm -f substitutions history history.pag history.dir active localgroups
--- 88,92 ----
  clean:
  	rm -f spacefor.sysv spacefor.v7 spacefor.null queuelen.null
! 	rm -f spacefor.sgi spacefor.xenix
  	rm -f config mailname organization server whoami hostname errlog
  	rm -f substitutions history history.pag history.dir active localgroups
***************
*** 108,111 ****
--- 110,116 ----
  cmps:
  	for d in $(CMPDIRS) ; do cd ../$$d ; make cmp RBIN=/usr/lib/uucp/bin ; done
+ 
+ rs:
+ 	for d in $(RDIRS) ; do cd ../$$d ; make r ; done
  
  save:	$(SHS)

*** cnpatch/old/conf/build	Mon Nov 13 17:39:43 1989
--- conf/build	Mon Jan  1 00:25:38 1990
***************
*** 377,382 ****
  		esac
  		dbm="DBM=$answer"
  		;;
! *)	fake="$fake dbm.o"
  	dbm='DBM='
  	echo "Okay, we'll fake it for you.  You might want to look at"
--- 377,387 ----
  		esac
  		dbm="DBM=$answer"
+ 		./query 'Does your dbm have a dbmclose() function [no]? '
+ 		read answer
+ 		case "$answer" in
+ 		n*|N*|'')	fake="$fake dbmclose.o"	;;
+ 		esac
  		;;
! *)	fake="$fake dbm.o dbmclose.o"
  	dbm='DBM='
  	echo "Okay, we'll fake it for you.  You might want to look at"
***************
*** 392,398 ****
  echo 'are faster than those in any stdio we know; they are compatible with'
  echo 'most AT&T-derived stdios.  If they work on your system, they are a'
! echo 'major performance win for C News.  There is a simple compatibility'
! echo 'check run after the library is built.  The only system we know of'
! echo 'where the test works but the functions do not is SunOS 4.0.'
  ./query 'Do you want to use our fast stdio library [yes]? '
  read libstdio
--- 397,403 ----
  echo 'are faster than those in any stdio we know; they are compatible with'
  echo 'most AT&T-derived stdios.  If they work on your system, they are a'
! echo 'major performance win for C News.  There is a fairly thorough'
! echo 'compatibility check run after the library is built; as far as we'
! echo 'know, if the test works, the functions do (even on SunOS 4.0).'
  ./query 'Do you want to use our fast stdio library [yes]? '
  read libstdio
***************
*** 602,605 ****
--- 607,611 ----
  echo '	sgi	Silicon Graphics Iris systems'
  echo '	ultrix	DEC Ultrix 3.0 (and later) (and earlier??)'
+ echo '	xenix	some (all?) Xenixes, notably SCO'
  echo '	v7	plain old style:  no headers or fluff, just name and number'
  echo "	null	don't know or don't care how much space is available"
***************
*** 610,614 ****
  	case "$dftype" in
  	'')	dftype=bsd	;;
! 	sysv)	echo 'Beware -- test "spacefor" to make sure it works.'
  		echo 'System V "df" formats vary widely, indeed wildly.'
  		echo '"Consider it standard".  Sure.'
--- 616,621 ----
  	case "$dftype" in
  	'')	dftype=bsd	;;
! 	sysv|xenix)
! 		echo 'Beware -- test "spacefor" to make sure it works.'
  		echo 'System V "df" formats vary widely, indeed wildly.'
  		echo '"Consider it standard".  Sure.'
***************
*** 616,620 ****
  	esac
  	case "$dftype" in
! 	bsd|sysv|sgi|ultrix|v7|null)	break	;;
  	esac
  	echo 'Sorry, no such choice is available.'
--- 623,627 ----
  	esac
  	case "$dftype" in
! 	bsd|sysv|sgi|ultrix|xenix|v7|null)	break	;;
  	esac
  	echo 'Sorry, no such choice is available.'

*** cnpatch/old/conf/spacefor.proto	Thu Sep 14 16:03:27 1989
--- conf/spacefor.proto	Thu Nov 16 17:07:30 1989
***************
*** 22,26 ****
  	if test " $server" != " $me"
  	then
! 		exec rsh $server /bin/sh -c "'PATH=$PATH `basename $0` $*'"
  		# does not return
  	fi
--- 22,26 ----
  	if test " $server" != " $me"
  	then
! 		exec rsh $server -n /bin/sh -c "'PATH=$PATH `basename $0` $*'"
  		# does not return
  	fi
***************
*** 33,37 ****
  
  # argument to df, df units, and free space desired (in df units)
! dfunit=1024			# default unit (bytes)
  case "$2" in
  incoming)	arg="$NEWSARTS/in.coming" ; desire=5000 ;;
--- 33,37 ----
  
  # argument to df, df units, and free space desired (in df units)
! dfunit=1024			# default df unit (bytes)
  case "$2" in
  incoming)	arg="$NEWSARTS/in.coming" ; desire=5000 ;;
***************
*** 43,46 ****
--- 43,53 ----
  		exit 2 ;;
  esac
+ 
+ # In the following, the initialization of nf determines which field the
+ # block count comes from, and the one for nr determines which line.  For
+ # System V, the Makefile edits in a sed which tries to strip silliness
+ # off in a reasonably System-V-variant-indepedent way.  Expr would be
+ # faster than awk, but on a 16-bit machine, expr does 16-bit arithmetic,
+ # which isn't enough.
  
  # this is set up for the stupid 4BSD df

*** cnpatch/old/conf/sys.proto	Thu Sep 14 16:03:28 1989
--- conf/sys.proto	Wed Jan  3 15:11:50 1990
***************
*** 1,6 ****
  # line indicating what we are willing to receive; note local groups on end
  ME:comp,news,sci,rec,misc,soc,talk,to,can,ont,tor,ut
  
! # sample insignificant feed not using batching
  huey:news.config,to.huey/all::uux - -r -gd huey!rnews
  
--- 1,10 ----
+ # Only the ME line is mandatory; the others are just samples of how to do
+ # things.  Virtually everything will need modifying for your local feeds
+ # and newsgroups.
+ 
  # line indicating what we are willing to receive; note local groups on end
  ME:comp,news,sci,rec,misc,soc,talk,to,can,ont,tor,ut
  
! # sample insignificant feed not using batching (for special situations only)
  huey:news.config,to.huey/all::uux - -r -gd huey!rnews
  
***************
*** 20,24 ****
  # Send ihave telling louie what we have -- batcher turns the batch into a
  # giant control message and posts it to "to.louie".  (#1)
! louie:rec.music.synth,!to/all,!sendme,!ihave:I:louie.ihave/togo
  # Send sendme in response to ihave from louie -- again, turned by batcher
  # into giant control message posted to "to.louie".  (#3)
--- 24,28 ----
  # Send ihave telling louie what we have -- batcher turns the batch into a
  # giant control message and posts it to "to.louie".  (#1)
! louie:comp,news,sci,rec,misc,soc,talk,!to/all,!sendme,!ihave:I:louie.ihave/togo
  # Send sendme in response to ihave from louie -- again, turned by batcher
  # into giant control message posted to "to.louie".  (#3)
***************
*** 29,32 ****
  louie-real:to.louie/sendme:f:louie/togo
  # Actually the last two could be combined.
! # and send local postings to louie without waiting (beware ihave/sendme)
  louie-local:comp,news,sci,rec,misc,soc,talk/all,!sendme,!ihave:L:
--- 33,38 ----
  louie-real:to.louie/sendme:f:louie/togo
  # Actually the last two could be combined.
! 
! # also, since ihave/sendme is slow, send local postings to louie without
! # waiting (beware ihave/sendme)
  louie-local:comp,news,sci,rec,misc,soc,talk/all,!sendme,!ihave:L:

*** cnpatch/old/doc/interface	Tue Aug 22 14:47:44 1989
--- doc/interface	Wed Jan  3 14:06:13 1990
***************
*** 1,3 ****
! .DA "19 Aug 1989"
  .TL
  The Interface Between C News And The Outside World
--- 1,3 ----
! .DA "3 Jan 1990"
  .TL
  The Interface Between C News And The Outside World
***************
*** 332,349 ****
  such an update.
  It should be run as \fInews\fR.
! .PP
! \fIRelay/relaynews\fR
! does not implement the ``Supersedes:'' header, which we loathe
! as a crude solution to the wrong problem.
! Alas, the network-map distribution in
! \fIcomp.mail.maps\fR relies heavily on it.
! Our preferred approach is to process map articles as they arrive and
! then expire them normally (using \fIexpire\fR's expiry-date-override
! features to insist that they expire promptly).
! However, for those who don't do this, and don't want to have megabytes
! of obsolete map data piling up,
! \fIexpire/superkludge\fR will remove superseded files in specified
! newsgroups.
! It should be run as \fInews\fR by \fIcron\fR, perhaps nightly.
  .SH
  Reboots and Administration
--- 332,337 ----
  such an update.
  It should be run as \fInews\fR.
! A much faster, but somewhat less portable, C implementation is
! supplied as \fIexpire/updatemin\fR.
  .SH
  Reboots and Administration

*** cnpatch/old/expire/Makefile	Thu Sep 14 16:03:29 1989
--- expire/Makefile	Wed Jan  3 14:03:47 1990
***************
*** 7,11 ****
  LIBS= ../libcnews.a
  THEM = expire histdups histinfo histslash mkdbm mkhistory \
! 	superkludge upact doexpire mkadir recovact
  DTR = README Makefile dircheck doexpire expire.c histdups histinfo.c \
  	histslash.c mkdbm.c mkhistory pgood superkludge tgood upact \
--- 7,11 ----
  LIBS= ../libcnews.a
  THEM = expire histdups histinfo histslash mkdbm mkhistory \
! 	upact doexpire mkadir recovact
  DTR = README Makefile dircheck doexpire expire.c histdups histinfo.c \
  	histslash.c mkdbm.c mkhistory pgood superkludge tgood upact \
***************
*** 31,34 ****
--- 31,37 ----
  	for f in $(THEM) ; do cmp $(NEWSBIN)/expire/$$f $$f ; done
  
+ check:	$(THEM)
+ 	for f in $(THEM) ; do cmp $(NEWSBIN)/expire/$$f $$f || true ; done
+ 
  newsinstall:	explist.proto
  	-if test ! -r $(NEWSCTL)/explist ; then cp explist.proto $(NEWSCTL)/explist ; fi
***************
*** 187,191 ****
  
  # the regression test proper
! r:	$(THEM) $(UPACT) dircheck setup tgood pgood
  	chmod +x dircheck $(THEM)
  	$(RUN) -c explist
--- 190,194 ----
  
  # the regression test proper
! r:	$(THEM) $(UPACT) dircheck setup tgood pgood superkludge
  	chmod +x dircheck $(THEM)
  	$(RUN) -c explist

*** cnpatch/old/expire/README	Tue Aug 22 14:47:45 1989
--- expire/README	Wed Jan  3 14:02:58 1990
***************
*** 30,31 ****
--- 30,33 ----
  pgood and tgood are regression-test output-should-look-like-this files.
  mkadir is what expire invokes to create archiving subdirectories.
+ superkludge is an old implementation of the Supersedes header, now
+ 	superseded :-) by the full implementation in relaynews.

*** cnpatch/old/expire/expire.c	Mon Nov 13 17:39:44 1989
--- expire/expire.c	Sun Dec 31 01:39:56 1989
***************
*** 78,81 ****
--- 78,82 ----
  int rebuild = 1;		/* rebuild history files */
  int violate = 0;		/* non-rfc822 case-sensitive messageIDs */
+ int getdgrump = 0;		/* report un-getdate()able expiry dates */
  
  long nkept = 0;			/* count of articles not expired */
***************
*** 168,172 ****
  	ftime(&ftnow);
  
! 	while ((c = getopt(argc, argv, "pa:sF:cn:tlvrVd")) != EOF)
  		switch (c) {
  		case 'p':	/* print info line for archived articles */
--- 169,173 ----
  	ftime(&ftnow);
  
! 	while ((c = getopt(argc, argv, "pa:sF:cn:tlvrVgd")) != EOF)
  		switch (c) {
  		case 'p':	/* print info line for archived articles */
***************
*** 203,206 ****
--- 204,210 ----
  			violate = 1;
  			break;
+ 		case 'g':	/* report getdate() failures on expiry dates */
+ 			getdgrump = 1;
+ 			break;
  		case 'd':	/* debug */
  			expdebug = 1;
***************
*** 491,496 ****
  
  	(void) close(old);
! 	if (rebuild)
  		eufclose(new, "history.n");
  
  	if (testing)
--- 495,502 ----
  
  	(void) close(old);
! 	if (rebuild) {
  		eufclose(new, "history.n");
+ 		dbmclose();
+ 	}
  
  	if (testing)
***************
*** 565,569 ****
  	else {
  		expdate = readdate(subfield[1]);
! 		if (expdate == NODATE) {
  			errno = 0;
  			warning("bad expiry date in `%.40s...',", line);
--- 571,575 ----
  	else {
  		expdate = readdate(subfield[1]);
! 		if (expdate == NODATE && getdgrump) {
  			errno = 0;
  			warning("bad expiry date in `%.40s...',", line);

*** cnpatch/old/expire/histinfo.c	Thu Sep 14 16:03:31 1989
--- expire/histinfo.c	Sat Dec 30 01:52:05 1989
***************
*** 6,9 ****
--- 6,10 ----
  #include <sys/types.h>
  #include <sys/stat.h>		/* for modified time (date received) */
+ #include <string.h>
  #include "config.h"
  #include "fgetmfs.h"
***************
*** 55,61 ****
  	while ((inname = fgetms(stdin)) != NULL) {
  		inname[strlen(inname)-1] = '\0';	/* kill newline */
! 		in = efopen(inname, "r");
! 		process(in, inname);
! 		(void) fclose(in);
  		free(inname);
  	}
--- 56,64 ----
  	while ((inname = fgetms(stdin)) != NULL) {
  		inname[strlen(inname)-1] = '\0';	/* kill newline */
! 		if (strchr(inname, '.') == NULL) {	/* skip dot names */
! 			in = efopen(inname, "r");
! 			process(in, inname);
! 			(void) fclose(in);
! 		}
  		free(inname);
  	}
***************
*** 79,83 ****
  	static char expnm[] =    "Expires: ";
  	register char *p;
- 	extern char *strchr();
  
  	expiry = strsave("-");
--- 82,85 ----
***************
*** 97,100 ****
--- 99,104 ----
  		free(line);
  	}
+ 	if (line != NULL)
+ 		free(line);
  
  	/* generate the file name */

*** cnpatch/old/expire/mkdbm.c	Thu Sep 14 16:03:31 1989
--- expire/mkdbm.c	Tue Nov 28 19:24:46 1989
***************
*** 1,10 ****
  /*
   * mkdbm - rebuild dbm file for a history file
-  *
-  * History file on standard input; the dbm database is generated as
-  * hist.dir and hist.pag.
   */
  #include <stdio.h>
  #include <string.h>
  #include "fgetmfs.h"
  #include "case.h"
--- 1,8 ----
  /*
   * mkdbm - rebuild dbm file for a history file
   */
  #include <stdio.h>
  #include <string.h>
+ #include "alloc.h"
  #include "fgetmfs.h"
  #include "case.h"
***************
*** 14,18 ****
  typedef struct { char *dptr; int dsize; } datum;
  
! main()
  {
  	register char *scan;
--- 12,18 ----
  typedef struct { char *dptr; int dsize; } datum;
  
! main(argc, argv)
! int argc;
! char *argv[];
  {
  	register char *scan;
***************
*** 21,33 ****
  	datum rhs;
  	register char *line;
  
! 	(void) close(creat("hist.dir", 0666));
! 	(void) close(creat("hist.pag", 0666));
! 	if (dbminit("hist") < 0)
! 		error("unable to do dbminit(\"hist\")", "");
  
  	for (;;) {
! 		place = ftell(stdin);
! 		line = fgetms(stdin);
  		if (line == NULL)
  			break;
--- 21,40 ----
  	datum rhs;
  	register char *line;
+ 	FILE *f;
+ 	extern FILE *efopen();
  
! 	if (argc < 2) {
! 		fprintf(stderr, "Usage: mkdbm historyfile\n");
! 		exit(2);
! 	}
! 	f = efopen(argv[1], "r");
! 	(void) close(creat(str3save(argv[1], ".", "dir"), 0666));
! 	(void) close(creat(str3save(argv[1], ".", "pag"), 0666));
! 	if (dbminit(argv[1]) < 0)
! 		error("unable to do dbminit(`%s')", argv[1]);
  
  	for (;;) {
! 		place = ftell(f);
! 		line = fgetms(f);
  		if (line == NULL)
  			break;
***************
*** 49,52 ****
--- 56,71 ----
  		free(line);
  	}
+ 
+ 	dbmclose();
  	exit(0);
+ }
+ 
+ /*
+  - unprivileged - needed due to library interdependencies
+  */
+ /* ARGSUSED */
+ void
+ unprivileged(reason)
+ char *reason;
+ {
  }

*** cnpatch/old/expire/mkhistory	Thu Sep 14 16:03:32 1989
--- expire/mkhistory	Sun Nov 26 01:32:54 1989
***************
*** 32,36 ****
  	echo "$0:     (grep history file for '@trash' to see them)" >&2
  fi
! mkdbm <history.n
  mv history history.o &&		# install new ASCII history file
  mv history.n history &&
--- 32,36 ----
  	echo "$0:     (grep history file for '@trash' to see them)" >&2
  fi
! mkdbm history.n
  mv history history.o &&		# install new ASCII history file
  mv history.n history &&
***************
*** 37,39 ****
  rm -f history.pag &&		# and related dbm files
  rm -f history.dir &&
! mv hist.pag history.pag && mv hist.dir history.dir
--- 37,39 ----
  rm -f history.pag &&		# and related dbm files
  rm -f history.dir &&
! mv history.n.pag history.pag && mv history.n.dir history.dir

*** cnpatch/old/expire/upact	Tue Aug 22 14:47:46 1989
--- expire/upact	Wed Dec 20 16:03:32 1989
***************
*** 18,21 ****
--- 18,28 ----
  esac
  
+ rm -f active.tmp
+ if test -f active.tmp
+ then
+ 	echo "$0: active.tmp exists and can't be removed; aborting" >&2
+ 	exit 1
+ fi
+ 
  # lock news system
  lock="$NEWSCTL/LOCK"
***************
*** 50,54 ****
  
  	echo $group $max $min $fourth
! done <active >active.new
  
  # replace active, carefully
--- 57,61 ----
  
  	echo $group $max $min $fourth
! done <active >active.tmp
  
  # replace active, carefully
***************
*** 55,59 ****
  rm -f active.old
  ln active active.old
! mv active.new active
  
  exit 0
--- 62,66 ----
  rm -f active.old
  ln active active.old
! mv active.tmp active
  
  exit 0

*** cnpatch/old/expire/updatemin.c	Thu Sep 14 16:03:33 1989
--- expire/updatemin.c	Wed Dec 20 16:04:50 1989
***************
*** 24,28 ****
  long lowest();
  
! char newname[] = "active.new";
  
  char *progname;
--- 24,28 ----
  long lowest();
  
! char newname[] = "active.tmp";
  
  char *progname;

*** cnpatch/old/hfake/Makefile	Tue Jun 20 18:58:03 1989
--- hfake/Makefile	Wed Jan 10 17:12:27 1990
***************
*** 2,6 ****
  
  # beware -- build knows about NEEDED
! NEEDED =  ../include/stdlib.h
  
  all:	$(NEEDED)
--- 2,6 ----
  
  # beware -- build knows about NEEDED
! NEEDED =  ../include/stdlib.h ../include/string.h
  
  all:	$(NEEDED)

*** cnpatch/old/input/Makefile	Thu Aug 24 16:39:52 1989
--- input/Makefile	Sat Dec 30 02:14:57 1989
***************
*** 41,44 ****
--- 41,50 ----
  	ls -lg $(NEWSBIN)/input/newsspool | egrep -s '^-rwsrwsr-x  1 news     news'
  
+ check:	$(THEM)
+ 	for f in $(THEM) ; do cmp $(NEWSBIN)/input/$$f $$f || true ; done
+ 	cmp rnews $(RBIN)/rnews || true
+ 	cmp rnews $(RBIN)/cunbatch || true
+ 	ls -lg $(NEWSBIN)/input/newsspool | egrep -s '^-rwsrwsr-x  1 news     news'
+ 
  newsinstall:
  	: nothing

*** cnpatch/old/input/newsrun	Tue Aug 22 14:47:46 1989
--- input/newsrun	Wed Nov 15 15:14:08 1989
***************
*** 116,120 ****
  		else
  			# N.B.: rsh always returns exit status 0!
! 			rsh $server "PATH=$PATH relaynews -r -n" <$text
  		fi
  		st=$?
--- 116,120 ----
  		else
  			# N.B.: rsh always returns exit status 0!
! 			rsh $server /bin/sh -c "PATH=$PATH relaynews -r -n" <$text
  		fi
  		st=$?

*** cnpatch/old/libc/warning.c	Tue Jun 20 18:58:41 1989
--- libc/warning.c	Sat Dec 30 01:46:05 1989
***************
*** 11,14 ****
--- 11,15 ----
  {
  	char *cmdname;
+ 	int saverrno;
  	extern int errno, sys_nerr;
  	extern char *sys_errlist[];
***************
*** 16,19 ****
--- 17,21 ----
  	extern char *getenv();
  
+ 	saverrno = errno;		/* so isatty(3) won't clobber it */
  	(void) fflush(stdout);				/* hack */
  	cmdname = getenv("CMDNAME");
***************
*** 23,28 ****
  		fprintf(stderr, "%s: ", progname);
  	fprintf(stderr, s1, s2);
! 	if (errno > 0 && errno < sys_nerr)
! 		fprintf(stderr, " (%s)", sys_errlist[errno]);
  	fprintf(stderr, "\n");
  	errno = 0;
--- 25,30 ----
  		fprintf(stderr, "%s: ", progname);
  	fprintf(stderr, s1, s2);
! 	if (saverrno > 0 && saverrno < sys_nerr)
! 		fprintf(stderr, " (%s)", sys_errlist[saverrno]);
  	fprintf(stderr, "\n");
  	errno = 0;

*** cnpatch/old/libfake/Makefile	Thu Aug 24 16:39:55 1989
--- libfake/Makefile	Wed Jan 10 17:15:59 1990
***************
*** 7,11 ****
  ALL = dbm.o fsync.o getopt.o ldiv.o memchr.o memcmp.o memcpy.o \
  memset.o mkdir.o putenv.o strchr.o strcspn.o strpbrk.o strrchr.o \
! strspn.o strtok.o symlink.o
  
  # beware -- build knows about NEEDED
--- 7,11 ----
  ALL = dbm.o fsync.o getopt.o ldiv.o memchr.o memcmp.o memcpy.o \
  memset.o mkdir.o putenv.o strchr.o strcspn.o strpbrk.o strrchr.o \
! strspn.o strtok.o symlink.o dbmclose.o
  
  # beware -- build knows about NEEDED

*** cnpatch/old/man/expire.8	Thu Sep 14 16:03:40 1989
--- man/expire.8	Wed Jan  3 14:01:37 1990
***************
*** 7,11 ****
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH EXPIRE 8 "13 Sept 1989" "C News"
  .SH NAME
  expire, doexpire \- expire old news
--- 7,11 ----
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH EXPIRE 8 "3 Jan 1990" "C News"
  .SH NAME
  expire, doexpire \- expire old news
***************
*** 16,21 ****
  .br
  recovact \- partially recover news active file
- .br
- superkludge \- implement stupid Supersedes header in news
  .SH SYNOPSIS
  .B \*b/expire/expire
--- 16,19 ----
***************
*** 45,48 ****
--- 43,48 ----
  ] [
  .B \-r
+ ] [
+ .B \-g
  ]
  [ controlfile ]
***************
*** 56,65 ****
  .br
  .B \*b/expire/recovact
- .br
- .B \*b/expire/superkludge
- [
- .B \-v
- ]
- newsgroup ...
  .SH DESCRIPTION
  .I Expire
--- 56,59 ----
***************
*** 182,185 ****
--- 176,188 ----
  print (on standard error) a shell-script-like description of what would
  be done, but don't do it.
+ In the absence of archiving, all output lines will be of the form
+ ``\fBremove\fR\ \fIname\fR'', where \fIname\fR is a pathname relative
+ to \fI\*a\fR.
+ If an article is to be archived, this will be preceded (on the same
+ line) by ``\fBcopy\fR\ \fIname\fR\ \fIdir\fR\ \fB;\fR\ '', where \fIname\fR
+ is as in \fBremove\fR and \fIdir\fR is an archiving directory
+ (including any `=' prefix)
+ as specified by the control file
+ or the \fB\-a\fR option.
  .TP
  .BR \-l
***************
*** 199,202 ****
--- 202,211 ----
  report some statistics after termination.
  .TP
+ .BR \-g
+ report expiry dates that \fIgetdate\fR(3) does not like.
+ .I Expire
+ ignores such dates, treating the article as if it had no explicit
+ expiry date.
+ .TP
  .BR \-d
  turn on (voluminous and cryptic) debugging output.
***************
*** 236,247 ****
  These programs are all fairly slow and they all lock the whole news
  system for the duration of the run, so they should not be run casually.
- .PP
- .I Superkludge
- inspects the files in \fI\*a\fR for the \fInewsgroup\fR(s)
- given, and removes any that have been superseded, according to the
- `Supersedes' header, by newer ones.
- The \fIhistory\fR file is not altered; it's not worth the trouble.
- The \fB\-v\fR option produces a report of how many articles have been
- superseded in each \fInewsgroup\fR.
  .SH FILES
  .ta 6c
--- 245,248 ----
***************
*** 270,282 ****
  although such lines are rare.
  .PP
! \fIUpact\fR and \fIsuperkludge\fR are distasteful kludges,
! but then, so are the third field of the \fIactive\fR file and the
! `Supersedes' header.
  .PP
- .I Superkludge
- does not understand RFC822's complex case-sensitivity rules for message-IDs,
- and insists on an exact case-sensitive match.
- .PP
  One cannot put more than one newsgroup into a single archiving directory
  with the `=' feature, since the article numbers will collide with each
  other and expire doesn't do anything about this.
--- 271,284 ----
  although such lines are rare.
  .PP
! \fIUpact\fR is a distasteful kludge,
! but then, so is the third field of the \fIactive\fR file.
  .PP
  One cannot put more than one newsgroup into a single archiving directory
  with the `=' feature, since the article numbers will collide with each
  other and expire doesn't do anything about this.
+ .PP
+ .I Mkhistory
+ is inherently incapable of reconstructing history-file lines corresponding
+ to expired articles.
+ Protection against old articles reappearing is thus somewhat limited for
+ a while after the history file is rebuilt.

*** cnpatch/old/man/newsaux.8	Mon Nov 13 17:39:47 1989
--- man/newsaux.8	Sun Dec 31 01:34:07 1989
***************
*** 7,11 ****
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH NEWSAUX 8 "15 Sept 1989" "C News"
  .SH NAME
  spacefor \- check available space for news
--- 7,11 ----
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH NEWSAUX 8 "30 Dec 1989" "C News"
  .SH NAME
  spacefor \- check available space for news
***************
*** 263,264 ****
--- 263,268 ----
  \fIDelgroup\fR does not remove files or directories from \*a, although it
  prints a reminder to do so.
+ .PP
+ Various nuisances can result if the maintenance utilities are run as
+ \fIroot\fR rather than as the owner of the news database.
+ It's difficult to defend against this.

*** cnpatch/old/man/relaynews.8	Fri Jul  7 15:40:08 1989
--- man/relaynews.8	Wed Jan  3 14:07:31 1990
***************
*** 7,11 ****
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH RELAYNEWS 8 "4 July 1989" "C News"
  .SH NAME
  relaynews \- store and forward netnews articles
--- 7,11 ----
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH RELAYNEWS 8 "2 January 1990" "C News"
  .SH NAME
  relaynews \- store and forward netnews articles
***************
*** 98,101 ****
--- 98,120 ----
  is not a real newsgroup.
  .PP
+ An article which contains an
+ .B Also-Control:
+ header is treated normally
+ except that the contents of the header
+ are executed as if they were the contents of a
+ .B Control:
+ header.
+ Such an article is a form of
+ .I "hybrid message"
+ since it functions as an ordinary article
+ yet also causes control functions to be executed.
+ The
+ .B Supersedes:
+ header is a special case
+ and is rewritten internally
+ .I only
+ to
+ .BR "Also-Control: cancel" .
+ .PP
  Articles which contain no locally-known
  (to the
***************
*** 222,228 ****
  (e.g. something like Eunice but on a PDP-11)
  will be filed incorrectly:
! any symbolic links under \*a will point at a non-existent
! filename.
! .br
  A control message which cannot be filed in the
  .B control
--- 241,248 ----
  (e.g. something like Eunice but on a PDP-11)
  will be filed incorrectly:
! any symbolic links under
! .B \*a
! will point at a non-existent filename.
! .PP
  A control message which cannot be filed in the
  .B control
***************
*** 241,245 ****
  .B control
  pseudo-group.
! .br
  .I Relaynews
  could run faster in some circumstances
--- 261,265 ----
  .B control
  pseudo-group.
! .PP
  .I Relaynews
  could run faster in some circumstances
***************
*** 246,253 ****
  and would be simpler
  if
! .I Control:
  were required to be the first header,
  if present,
  and if
! .I Newsgroups:
  were required to be the next.
--- 266,285 ----
  and would be simpler
  if
! .B Control:
  were required to be the first header,
  if present,
  and if
! .B Newsgroups:
  were required to be the next.
+ .PP
+ The whole control message and hybrid message situation
+ is a festering bug.
+ Either control messages should be eliminated,
+ or all forms of backward compatibility should be dropped
+ (including
+ .B Control:
+ and
+ .BR Supersedes: )
+ and only
+ .B Also-Control:
+ should be supported.

*** cnpatch/old/misc/Makefile	Thu Sep 14 16:03:43 1989
--- misc/Makefile	Sat Dec 30 02:17:11 1989
***************
*** 31,34 ****
--- 31,38 ----
  	for f in $(UTILS) ; do cmp $(NEWSBIN)/$$f $$f ; done
  
+ check:	$(THEM)
+ 	for f in $(MAINT) ; do cmp $(NEWSBIN)/maint/$$f $$f || true ; done
+ 	for f in $(UTILS) ; do cmp $(NEWSBIN)/$$f $$f || true ; done
+ 
  newsinstall:
  	: nothing
***************
*** 50,54 ****
  
  NHCFLAGS = -I$(RN) $(CFLAGS)
! RNEWSOBJS = $(RN)/history.o $(RN)/article.o $(RN)/hdrcommon.o \
   $(RN)/io.o $(RN)/msgs.o
  NHLIBS = $(LIBS) $(DBM)
--- 54,58 ----
  
  NHCFLAGS = -I$(RN) $(CFLAGS)
! RNEWSOBJS = $(RN)/history.o $(RN)/article.o $(RN)/hdrdefs.o \
   $(RN)/io.o $(RN)/msgs.o
  NHLIBS = $(LIBS) $(DBM)

*** cnpatch/old/misc/newsdaily	Mon Nov 13 17:39:49 1989
--- misc/newsdaily	Tue Dec 19 18:52:41 1989
***************
*** 116,119 ****
--- 116,129 ----
  	) >>$gripes
  fi
+ egrep '`' log.o | egrep 'no subscribed' | sed 's/.*`\(.*\)'"'"'.*/\1/' | sort |
+ 	uniq -c | sort -nr | sed 5q >$tmp
+ if test -s $tmp
+ then
+ 	(
+ 		echo 'leading five unsubscribed newsgroups:'
+ 		cat $tmp
+ 		echo
+ 	) >>$gripes
+ fi
  
  # and send it

*** cnpatch/old/misc/newshist.c	Tue Jun 20 19:00:05 1989
--- misc/newshist.c	Mon Nov 27 19:45:35 1989
***************
*** 11,15 ****
  static char *histfile;		/* unused */
  int remote;			/* to satisfy rnews code */
- int headdebug = 0;		/* no debugging */
  
  /*
--- 11,14 ----

*** cnpatch/old/notebook/bdiffs	Tue Jun 20 18:56:13 1989
--- notebook/bdiffs	Tue Jan  2 19:03:00 1990
***************
*** 39,43 ****
  .I rnews
  by linking to the name
! .I /usr/lib/news/LOCK
  and repeating until successful;
  there is no time-out
--- 39,43 ----
  .I rnews
  by linking to the name
! .B /usr/lib/news/LOCK
  and repeating until successful;
  there is no time-out
***************
*** 157,163 ****
  (e.g.
  .B usenet ).
! The superfluous
  .I Supersedes:
! header is not honoured.
  .PP
  .I "Newsgroup aliases."
--- 157,167 ----
  (e.g.
  .B usenet ).
! C news
! adds an
! .I Also-Control:
! header,
! of which
  .I Supersedes:
! is a special case.
  .PP
  .I "Newsgroup aliases."

*** cnpatch/old/notebook/ctlmsg	Tue Jun 20 18:56:24 1989
--- notebook/ctlmsg	Tue Jan  2 19:03:31 1990
***************
*** 62,66 ****
  .I relaynews
  by executing the command following
! .B Control: ,
  with a search path of
  .I $NEWSCTL/bin:$NEWSBIN/ctl
--- 62,66 ----
  .I relaynews
  by executing the command following
! .B Control:
  with a search path of
  .I $NEWSCTL/bin:$NEWSBIN/ctl
***************
*** 68,71 ****
--- 68,73 ----
  the control message article.
  The command inherits
+ the standard news search path
+ and
  .I relaynews 's
  user and group ids,

*** cnpatch/old/notebook/log	Sun Jul 23 00:48:12 1989
--- notebook/log	Sun Dec 31 01:30:54 1989
***************
*** 85,86 ****
--- 85,92 ----
  s	generated in response to a \fIsendme\fR control message	list of sites to which this article was relayed
  .TE
+ .PP
+ Beware that control-message handlers inherit
+ .I relaynew 's
+ standard output, so if any of them natters on standard output
+ (we believe none of ours does), the nattering will appear in
+ .B log .

*** cnpatch/old/notebook/rfcerrata	Mon Nov 13 17:39:49 1989
--- notebook/rfcerrata	Tue Jan  2 19:04:10 1990
***************
*** 77,81 ****
  B 2.11 interprets absence of the target article as ``unable to cancel''.
  It would improve the efficacy and reliability of
! .IR cancel s
  to propagate them anyway, given that feed anomalies are widespread.
  There have been verified instances in which cancellations did not achieve
--- 77,81 ----
  B 2.11 interprets absence of the target article as ``unable to cancel''.
  It would improve the efficacy and reliability of
! \fIcancel\fRs
  to propagate them anyway, given that feed anomalies are widespread.
  There have been verified instances in which cancellations did not achieve
***************
*** 84,88 ****
  C News interprets absence of the target article as deferred cancellation
  rather than failure to cancel, and propagates the
! .IR cancel .
  .SH
  ihave/sendme not documented
--- 84,88 ----
  C News interprets absence of the target article as deferred cancellation
  rather than failure to cancel, and propagates the
! \fIcancel\fR.
  .SH
  ihave/sendme not documented
***************
*** 105,109 ****
  .I remotesys .
  .SH
! case-sensitivity in message-ids
  .PP
  RFC 1036 says nothing about whether message-ids are case-sensitive or not,
--- 105,109 ----
  .I remotesys .
  .SH
! Case-sensitivity in message-ids
  .PP
  RFC 1036 says nothing about whether message-ids are case-sensitive or not,
***************
*** 118,119 ****
--- 118,129 ----
  matters appear to be extremely rare.)
  Simplification appears necessary.
+ .SH
+ New headers
+ .PP
+ The B news
+ .B Supersedes:
+ header needs to be documented in the next revision of the RFC,
+ as does the C news generalisation,
+ .B Also-Control:
+ (see
+ .I relaynews (8)).

*** cnpatch/old/relay/control.c	Thu Aug 24 16:39:59 1989
--- relay/control.c	Mon Nov 27 19:47:53 1989
***************
*** 22,26 ****
--- 22,28 ----
  #include "libc.h"
  #include "news.h"
+ #include "case.h"
  #include "config.h"
+ #include "control.h"
  #include "headers.h"
  #include "article.h"
***************
*** 30,33 ****
--- 32,39 ----
  #define NO_FILES ""
  #define SUBDIR binfile("ctl")		/* holds shell scripts */
+ #ifdef SLOWCTLMATCH
+ #define OLDCNTRL "all.all.ctl"
+ #endif
+ #define SFXOLDCNTRL ".ctl"
  
  /*
***************
*** 58,66 ****
  void
  ctlmsg(art)
! struct article *art;
  {
  	int pid, deadpid;
  	int wstatus;
- 	char *inname = art->a_tmpf, *ctlcmd = art->h.h_ctlcmd;
  	static char nmcancel[] = "cancel ";
  	static char nmihave[] = "ihave ";
--- 64,72 ----
  void
  ctlmsg(art)
! register struct article *art;
  {
+ 	register char *inname = art->a_tmpf, *ctlcmd = art->h.h_ctlcmd;
  	int pid, deadpid;
  	int wstatus;
  	static char nmcancel[] = "cancel ";
  	static char nmihave[] = "ihave ";
***************
*** 67,70 ****
--- 73,80 ----
  	static char nmsendme[] = "sendme ";
  
+ 	if (ctlcmd == NULL)
+ 		ctlcmd = art->h.h_etctlcmd;
+ 	if (ctlcmd == NULL)
+ 		return;
  	if (STREQN(ctlcmd, nmcancel, STRLEN(nmcancel))) {
  		art->a_status |= cancelart(ctlcmd + STRLEN(nmcancel));
***************
*** 102,106 ****
  STATIC boolean
  safecmd(cmd)			/* true if it's safe to system(3) cmd */
! char *cmd;
  {
  	register char *s;
--- 112,116 ----
  STATIC boolean
  safecmd(cmd)			/* true if it's safe to system(3) cmd */
! register char *cmd;
  {
  	register char *s;
***************
*** 245,247 ****
--- 255,300 ----
  	free(mailcmd);
  	_exit(1);
+ }
+ 
+ STATIC boolean
+ oldctl(hdrs)				/* true iff ngs match OLDCNTRL */
+ register struct headers *hdrs;
+ {
+ #ifdef SLOWCTLMATCH
+ 	return ngmatch(OLDCNTRL, hdrs->h_ngs);
+ #else
+ 	register int ngslen = strlen(hdrs->h_ngs);
+ 
+ 	if (ngslen < STRLEN(SFXOLDCNTRL))	/* ngs too short */
+ 		return NO;
+ 	else					/* check for .ctl suffix */
+ 		/*
+ 		 * This is more general than RFC 850 specifies, but this
+ 		 * generality seems harmless.  This doesn't work for e.g.
+ 		 * x.y.ctl,z.q, which is a darn shame, but that's a violation
+ 		 * of common sense.
+ 		 */
+ 		return STREQ(&hdrs->h_ngs[ngslen-STRLEN(SFXOLDCNTRL)],
+ 			SFXOLDCNTRL);
+ #endif						/* SLOWCTLMATCH */
+ }
+ 
+ hackoldctl(hdrs)			/* Handle the all.all.ctl hack. */
+ register struct headers *hdrs;
+ {
+ 	if (hdrs->h_ctlcmd == NULL && oldctl(hdrs))
+ 		hdrs->h_ctlcmd = strsave(hdrs->h_subj);
+ }
+ 
+ char *
+ hackhybrid(line)
+ register char *line;
+ {
+ 	static char stupersedes[] = "Supersedes:";
+ 	static char alsocan[] =     "Also-Control: cancel ";
+ 
+ 	if (CISTREQN(line, stupersedes, STRLEN(stupersedes)))
+ 		return str3save(alsocan, "", &line[STRLEN(stupersedes)]);
+ 	else
+ 		return strsave(line);
  }

*** cnpatch/old/relay/control.h	Tue Jun 20 19:01:03 1989
--- relay/control.h	Mon Nov 27 19:47:53 1989
***************
*** 1,2 ****
--- 1,5 ----
  /* imports from control.c */
  extern void ctlmsg();
+ extern char *hackhybrid();
+ 
+ #define CONTROL "control"		/* control message pseudo-ng. */

*** cnpatch/old/relay/fileart.c	Thu Aug 24 16:39:59 1989
--- relay/fileart.c	Mon Nov 27 19:47:53 1989
***************
*** 25,29 ****
--- 25,31 ----
  #include "news.h"
  #include "config.h"
+ #include "control.h"
  #include "active.h"
+ #include "fileart.h"
  #include "mkdirs.h"
  #include "headers.h"
***************
*** 32,38 ****
  #include "system.h"
  
- #define JUNK "junk"			/* lost+found pseudo-ng. */
- #define CONTROL "control"		/* control message pseudo-ng. */
- 
  static long artnum;			/* asgnartnum sets artnum */
  static int goodngs;			/* asgnartnum reads goodngs */
--- 34,37 ----
***************
*** 98,102 ****
  	register char *comma;
  
! 	ngs = (art->h.h_ctlcmd != NULL? CONTROL: art->h.h_ngs);
  	if (art->a_status&ST_REFUSED)
  		(void) fprintf(stderr,
--- 97,101 ----
  	register char *comma;
  
! 	ngs = (art->h.h_ctlcmd != NULL? CONTROL: art->h.h_ngs);	/* NCMP */
  	if (art->a_status&ST_REFUSED)
  		(void) fprintf(stderr,

*** cnpatch/old/relay/fileart.h	Tue Jun 20 19:01:12 1989
--- relay/fileart.h	Mon Nov 27 19:47:53 1989
***************
*** 2,3 ****
--- 2,5 ----
  extern void filedebug();
  extern void fileart();
+ 
+ #define JUNK "junk"			/* lost+found pseudo-ng. */

*** cnpatch/old/relay/hdrdefs.c	Tue Jun 20 19:01:16 1989
--- relay/hdrdefs.c	Mon Nov 27 19:47:53 1989
***************
*** 13,16 ****
--- 13,17 ----
  #include <stdlib.h>
  #endif				/* REALSTDC */
+ #include "libc.h"
  #include "news.h"
  #include "headers.h"
***************
*** 29,33 ****
  /* optional headers */
  static const char appnm[] =	"Approved:";	/* for mod. groups */
! static const char ctlnm[] =	"Control:";	/* ctl. msg. */
  static const char expnm[] =	"Expires:";	/* for history */
  static const char distrnm[] =	"Distribution:";	/* for transmission */
--- 30,35 ----
  /* optional headers */
  static const char appnm[] =	"Approved:";	/* for mod. groups */
! static const char ctlnm[] =	"Control:";	/* ctl. msg.; NCMP */
! static const char etctlnm[] =	"Also-Control:";	/* hybrid ctl. msg.; NCMP */
  static const char expnm[] =	"Expires:";	/* for history */
  static const char distrnm[] =	"Distribution:";	/* for transmission */
***************
*** 57,62 ****
  static const struct hdrdef apphdr = {
  	appnm, STRLEN(appnm), offsetof(struct headers, h_approved) };
! static const struct hdrdef ctlhdr = {
! 	ctlnm, STRLEN(ctlnm), offsetof(struct headers, h_ctlcmd) };
  static const struct hdrdef exphdr = {
  	expnm, STRLEN(expnm), offsetof(struct headers, h_expiry) };
--- 59,66 ----
  static const struct hdrdef apphdr = {
  	appnm, STRLEN(appnm), offsetof(struct headers, h_approved) };
! static const struct hdrdef ctlhdr = {					/* NCMP */
! 	ctlnm, STRLEN(ctlnm), offsetof(struct headers, h_ctlcmd) };	/* NCMP */
! static const struct hdrdef etctlhdr = {					/* NCMP */
! 	etctlnm, STRLEN(etctlnm), offsetof(struct headers, h_etctlcmd) }; /* NCMP */
  static const struct hdrdef exphdr = {
  	expnm, STRLEN(expnm), offsetof(struct headers, h_expiry) };
***************
*** 85,89 ****
  	/* start optional headers */
  	&apphdr,
! 	&ctlhdr,
  	&distrhdr,
  	&exphdr,
--- 89,94 ----
  	/* start optional headers */
  	&apphdr,
! 	&ctlhdr,		/* NCMP */
! 	&etctlhdr,		/* NCMP */
  	&distrhdr,
  	&exphdr,
***************
*** 109,110 ****
--- 114,156 ----
  
  boolean headdebug = NO;
+ 
+ void
+ hdrdebug(state)
+ int state;
+ {
+ 	headdebug = state;
+ }
+ 
+ void
+ hdrinit(hdrs)			/* zero all elements of hdrs */
+ register struct headers *hdrs;
+ {
+ 	hdrs->h_subj = NULL;
+ 	hdrs->h_ngs = NULL;
+ 	hdrs->h_distr = NULL;
+ 	hdrs->h_ctlcmd = NULL;		/* NCMP */
+ 	hdrs->h_etctlcmd = NULL;	/* NCMP */
+ 	hdrs->h_approved = NULL;
+ 	hdrs->h_msgid = NULL;
+ 	hdrs->h_artid = NULL;
+ 	hdrs->h_expiry = NULL;
+ 	hdrs->h_path = NULL;
+ 	hdrs->h_sender = NULL;
+ }
+ 
+ void
+ freeheaders(hdrs)		/* free (assumed) malloced storage */
+ register struct headers *hdrs;
+ {
+ 	nnfree(&hdrs->h_subj);
+ 	nnfree(&hdrs->h_ngs);
+ 	nnfree(&hdrs->h_distr);
+ 	nnfree(&hdrs->h_ctlcmd);	/* NCMP */
+ 	nnfree(&hdrs->h_etctlcmd);	/* NCMP */
+ 	nnfree(&hdrs->h_approved);
+ 	nnfree(&hdrs->h_msgid);
+ 	nnfree(&hdrs->h_artid);
+ 	nnfree(&hdrs->h_expiry);
+ 	nnfree(&hdrs->h_path);
+ 	nnfree(&hdrs->h_sender);
+ }

*** cnpatch/old/relay/hdrint.h	Thu Aug 24 16:39:59 1989
--- relay/hdrint.h	Mon Nov 27 19:47:53 1989
***************
*** 22,33 ****
  #define DEFMSGID ""		/* must be empty string or contain whitespace */
  
- #define JUNK "junk"
- #define ALL "all"
- 
- #ifdef SLOWCTLMATCH
- #define OLDCNTRL "all.all.ctl"
- #endif
- #define SFXOLDCNTRL ".ctl"
- 
  struct hdrdef {
  	const char *hdrnm;	/* ascii name */
--- 22,25 ----

*** cnpatch/old/relay/hdrparse.c	Mon Nov 13 17:39:50 1989
--- relay/hdrparse.c	Mon Nov 27 19:47:53 1989
***************
*** 7,11 ****
--- 7,13 ----
  #include "libc.h"
  #include "news.h"
+ #include "control.h"
  #include "case.h"
+ #include "fileart.h"
  #include "headers.h"
  #include "hdrint.h"
***************
*** 12,25 ****
  
  /*
-  * Reset internal state of header parser.
-  * (Empty the stomach of partially-digested headers.)
-  */
- void
- hdrwretch()
- {
- 	/* historical stub */
- }
- 
- /*
   * Parse (assumed) RFC822/850/1036 header in "line" (ishdr(line) can
   * verify this) into "hdrs" using hdrlst set of keywords by retaining the
--- 14,17 ----
***************
*** 32,39 ****
  hdrparse(hdrs, line, hdrlst)
  register struct headers *hdrs;
! register char *line;
  hdrlist hdrlst;				/* headers of positive utility */
  {
  	register struct hdrdef **hpp;
  
  	for (hpp = hdrlst; *hpp != NULL; hpp++) {
--- 24,32 ----
  hdrparse(hdrs, line, hdrlst)
  register struct headers *hdrs;
! char *line;
  hdrlist hdrlst;				/* headers of positive utility */
  {
  	register struct hdrdef **hpp;
+ 	register char *hackline = hackhybrid(line);
  
  	for (hpp = hdrlst; *hpp != NULL; hpp++) {
***************
*** 40,44 ****
  		register char *hdrnm = (*hpp)->hdrnm;
  
! 		if (CISTREQN(line, hdrnm, (int)(*hpp)->hdrlen) &&
  		    (*hpp)->hdroff >= 0)	/* paranoia */
  			break;
--- 33,37 ----
  		register char *hdrnm = (*hpp)->hdrnm;
  
! 		if (CISTREQN(hackline, hdrnm, (int)(*hpp)->hdrlen) &&
  		    (*hpp)->hdroff >= 0)	/* paranoia */
  			break;
***************
*** 48,55 ****
  
  		nnfree(ptrp);		/* free prev. value in this article */
! 		*ptrp = strsave(skipsp(&line[(*hpp)->hdrlen]));
  		if (*ptrp != NULL)
  			trim(*ptrp);		/* cut trailing \n */
  	}
  }
  
--- 41,49 ----
  
  		nnfree(ptrp);		/* free prev. value in this article */
! 		*ptrp = strsave(skipsp(&hackline[(*hpp)->hdrlen]));
  		if (*ptrp != NULL)
  			trim(*ptrp);		/* cut trailing \n */
  	}
+ 	free(hackline);
  }
  
***************
*** 90,94 ****
  	if (hdrs->h_subj == NULL)
  		hdrs->h_subj = strsave("");
! 	if (hdrs->h_ctlcmd == NULL && oldctl(hdrs))
! 		hdrs->h_ctlcmd = strsave(hdrs->h_subj);
  }
--- 84,87 ----
  	if (hdrs->h_subj == NULL)
  		hdrs->h_subj = strsave("");
! 	hackoldctl(hdrs);				/* NCMP */
  }

*** cnpatch/old/relay/headers.h	Tue Jun 20 19:01:19 1989
--- relay/headers.h	Mon Nov 27 19:47:54 1989
***************
*** 19,23 ****
  	char *h_ngs;	/* newsgroups: used in filing, sys matching & all.all.ctl matching */
  	char *h_distr;	/* distribution for transmit */
! 	char *h_ctlcmd;	/* control command */
  	char *h_approved;	/* needed for acceptance in moderated groups */
  	char *h_msgid;	/* needed for history & rejection */
--- 19,24 ----
  	char *h_ngs;	/* newsgroups: used in filing, sys matching & all.all.ctl matching */
  	char *h_distr;	/* distribution for transmit */
! 	char *h_ctlcmd;	/* control command (NCMP) */
! 	char *h_etctlcmd;	/* also-control command (NCMP) */
  	char *h_approved;	/* needed for acceptance in moderated groups */
  	char *h_msgid;	/* needed for history & rejection */
***************
*** 36,39 ****
  
  /* parse */
! extern void hdrwretch(), hdrparse(), hdrdeflt();
  extern boolean ishdr(), contin();
--- 37,40 ----
  
  /* parse */
! extern void hdrparse(), hdrdeflt();
  extern boolean ishdr(), contin();

*** cnpatch/old/relay/makefile	Mon Nov 13 17:39:53 1989
--- relay/makefile	Sat Dec 30 02:20:17 1989
***************
*** 33,41 ****
  LIBOBJS=../libcnews.a
  SRC=relaynews.c active.c article.c caches.c mkdirs.c control.c fileart.c \
! 	hdrdefs.c hdrcommon.c hdrparse.c hdrmunge.c \
  	history.c io.c msgs.c procart.c \
  	sys.c transmit.c trbatch.c ihave.c $(LIBSRCS)
  OBJ=relaynews.o active.o article.o caches.o mkdirs.o control.o fileart.o \
! 	hdrdefs.o hdrcommon.o hdrparse.o hdrmunge.o \
  	history.o io.o msgs.o procart.o \
  	sys.o transmit.o trbatch.o ihave.o
--- 33,41 ----
  LIBOBJS=../libcnews.a
  SRC=relaynews.c active.c article.c caches.c mkdirs.c control.c fileart.c \
! 	hdrdefs.c hdrparse.c hdrmunge.c \
  	history.c io.c msgs.c procart.c \
  	sys.c transmit.c trbatch.c ihave.c $(LIBSRCS)
  OBJ=relaynews.o active.o article.o caches.o mkdirs.o control.o fileart.o \
! 	hdrdefs.o hdrparse.o hdrmunge.o \
  	history.o io.o msgs.o procart.o \
  	sys.o transmit.o trbatch.o ihave.o
***************
*** 80,84 ****
  cmp:	relaynews
  	cmp $(NEWSBIN)/relay/relaynews relaynews
- 	ls -lg $(NEWSBIN)/relay/relaynews | egrep -s '^-rwsrwsr-x  1 news     news'
  	for f in `ls sh` ; do cmp $(NEWSBIN)/inject/$$f sh/$$f ; done
  	for f in `ls ctl` ; do cmp $(NEWSBIN)/ctl/$$f ctl/$$f ; done
--- 80,83 ----
***************
*** 85,89 ****
--- 84,97 ----
  	for f in `ls aux` ; do cmp $(NEWSBIN)/relay/$$f aux/$$f ; done
  	cmp $(BIN)/inews sh/inews
+ 	ls -lg $(NEWSBIN)/relay/relaynews | egrep -s '^-rwsrwsr-x  1 news     news'
  
+ check:	relaynews
+ 	cmp $(NEWSBIN)/relay/relaynews relaynews || true
+ 	for f in `ls sh` ; do cmp $(NEWSBIN)/inject/$$f sh/$$f || true ; done
+ 	for f in `ls ctl` ; do cmp $(NEWSBIN)/ctl/$$f ctl/$$f || true ; done
+ 	for f in `ls aux` ; do cmp $(NEWSBIN)/relay/$$f aux/$$f || true ; done
+ 	cmp $(BIN)/inews sh/inews || true
+ 	ls -lg $(NEWSBIN)/relay/relaynews | egrep -s '^-rwsrwsr-x  1 news     news'
+ 
  TODO.grep: TODO
  	-egrep TODO ../include/*.h *.h *.c sh/* | tr -s " \11" " " >$@
***************
*** 124,128 ****
  fileart.o: ../include/libc.h ../include/news.h ../include/config.h
  fileart.o: active.h mkdirs.h headers.h article.h history.h system.h
- hdrcommon.o: ../include/news.h headers.h hdrint.h
  hdrdefs.o: ../include/news.h headers.h hdrint.h
  hdrmunge.o: ../include/libc.h ../include/news.h fileart.h headers.h
--- 132,135 ----

*** cnpatch/old/relay/procart.c	Mon Nov 13 17:39:53 1989
--- relay/procart.c	Mon Nov 27 19:47:54 1989
***************
*** 136,144 ****
  {
  	register char *hdr = NULL;
! 	long limit;
  	int is_hdr = NO;
  
- 	hdrwretch();				/* reset the header parser */
- 	limit = (art->a_blvmax? art->a_unread+1: art->a_unread); /* 1 for NUL */
  	/* 1 is again for NUL */
  	while (limit > 1 && (hdr = gethdr(in, &limit, &is_hdr)) != NULL && is_hdr) {
--- 136,142 ----
  {
  	register char *hdr = NULL;
! 	long limit = (art->a_blvmax? art->a_unread+1: art->a_unread); /* 1 for NUL */
  	int is_hdr = NO;
  
  	/* 1 is again for NUL */
  	while (limit > 1 && (hdr = gethdr(in, &limit, &is_hdr)) != NULL && is_hdr) {
***************
*** 274,279 ****
  		transmit(art, exclude);		/* writes systems on stdout */
  		(void) putchar('\n');		/* ends the log line */
! 		if (art->h.h_ctlcmd != NULL)
! 			ctlmsg(art);
  #ifdef notdef					/* it's only a log file! */
  		(void) fflush(stdout);		/* crash-proofness */
--- 272,276 ----
  		transmit(art, exclude);		/* writes systems on stdout */
  		(void) putchar('\n');		/* ends the log line */
! 		ctlmsg(art);
  #ifdef notdef					/* it's only a log file! */
  		(void) fflush(stdout);		/* crash-proofness */

*** cnpatch/old/relay/transmit.c	Thu Aug 24 16:40:07 1989
--- relay/transmit.c	Tue Dec 19 16:07:22 1989
***************
*** 178,182 ****
  		STRLEN(":") + strlen(bincmd) + STRLEN(":") + strlen(newspath()) +
  		STRLEN(";<") + strlen(filename) + STRLEN(" (") +
! 		strlen(syscmd) + strlen(filename) + STRLEN(")") + 1));
  	(void) strcpy(cmd, "PATH=");
  	(void) strcat(cmd, ctlcmd);
--- 178,182 ----
  		STRLEN(":") + strlen(bincmd) + STRLEN(":") + strlen(newspath()) +
  		STRLEN(";<") + strlen(filename) + STRLEN(" (") +
! 		strlen(syscmd) + strlen(filename) + STRLEN(")") + SIZENUL));
  	(void) strcpy(cmd, "PATH=");
  	(void) strcat(cmd, ctlcmd);
***************
*** 199,207 ****
  			art->a_status |= ST_DROPPED;
  			(void) fprintf(stderr, "%s: `%s' contains two %%'s\n",
! 				progname, cmd);
  		} else if (*percent != 's' && *percent != '%') {
  			art->a_status |= ST_DROPPED;
  			(void) fprintf(stderr, "%s: `%s' contains %%%c, not %%s\n",
! 				progname, cmd, *percent);
  		} else
  			(void) sprintf(cmd+strlen(cmd), syscmd, filename);
--- 199,207 ----
  			art->a_status |= ST_DROPPED;
  			(void) fprintf(stderr, "%s: `%s' contains two %%'s\n",
! 				progname, syscmd);
  		} else if (*percent != 's' && *percent != '%') {
  			art->a_status |= ST_DROPPED;
  			(void) fprintf(stderr, "%s: `%s' contains %%%c, not %%s\n",
! 				progname, syscmd, *percent);
  		} else
  			(void) sprintf(cmd+strlen(cmd), syscmd, filename);
***************
*** 211,216 ****
  	if (exitstat != 0) {
  		art->a_status |= ST_DROPPED;
! 		(void) fprintf(stderr, "%s: `%s' returned exit status 0%o\n",
! 			progname, cmd, exitstat);
  	}
  	free(cmd);
--- 211,217 ----
  	if (exitstat != 0) {
  		art->a_status |= ST_DROPPED;
! 		(void) fprintf(stderr, "%s: `", progname);
! 		(void) fputs(cmd, stderr);
! 		(void) fprintf(stderr, "' returned exit status 0%o\n", exitstat);
  	}
  	free(cmd);

Files that are new:

new libfake/dbmclose.c (patch can't create, so diff against null):
Index: libfake/dbmclose.c
*** cnpatch/old/libfake/dbmclose.c	Wed Jan 10 18:03:50 1990
--- libfake/dbmclose.c	Tue Nov 28 19:20:22 1989
***************
*** 0 ****
--- 1 ----
+ dbmclose(){}

new notebook/problems (patch can't create, so diff against null):
Index: notebook/problems
*** cnpatch/old/notebook/problems	Wed Jan 10 18:03:51 1990
--- notebook/problems	Wed Jan 10 17:54:42 1990
***************
*** 0 ****
--- 1,145 ----
+ .DA "31 Dec 1989"
+ .TL
+ Known Porting Problems With C News
+ .AU
+ Henry Spencer
+ .AI
+ Dept. of Zoology
+ University of Toronto
+ .SH
+ Intro
+ .PP
+ C News in general is pretty portable.
+ People have got it to run on a very wide range of systems with little
+ trouble.
+ Difficulties are usually problems in the system, not C News.
+ Some of them, however, are widespread enough to be worth comment, for
+ the guidance of people having problems.
+ If you run into a novel problem,
+ we are always interested in hearing about such things.
+ .SH
+ Unix Dependencies
+ .PP
+ The biggest portability glitch in C News is that it depends a lot on Unix
+ utilities.
+ The extensive use of complex shell files, \fIsed\fR and \fIawk\fR programs,
+ and a wide range of lesser Unix utilities would make it quite difficult
+ to move C News to a system that is seriously non-Unix-like.
+ The actual C programs seldom depend on Unix in major ways.
+ (An exception is the use
+ of \fIread\fR system calls in \fIexpire\fR, to avoid difficulties with
+ stdio end-of-file behavior;
+ we now know how to avoid this but haven't implemented the fixes yet.)
+ .PP
+ We know that \fIawk\fR and the colon (:) operator of \fIexpr\fR are
+ problem areas under Minix.
+ .SH
+ Shell Problems
+ .PP
+ C News seriously stress-tests shells.
+ The current Minix shell is not robust enough
+ in the face of complex inputs, botches some constructs entirely,
+ and can run out of memory on the complex shell files.
+ Any shell that is too old to implement comments begun
+ by ``#'' is big trouble, since we use such comments everywhere.
+ .PP
+ Any system/shell combination that thinks that a shell script starting
+ with ``#!\ /bin/sh'' should be run by the C Shell (because it starts
+ with `#') is also big trouble:
+ you will have to change that line to ``:\ use\ /bin/sh'' everywhere.
+ We know that at least some releases of Xenix have this problem.
+ It is not necessary that your kernel understand the ``#!'' feature\(emwe
+ believe that nothing in C News relies on it\(embut it is essential that
+ it not cause invocation of the C Shell.
+ .PP
+ We know that some Hewlett-Packard Unixes have broken shells, probably
+ the result of mistakes in
+ HP's efforts to make
+ the shell 8-bit-clean; the symptom is
+ that something like:
+ .DS
+ x=y
+ if test " $x" != " y"
+ then
+ 	echo oops
+ fi
+ .DE
+ prints ``oops''.
+ This is, again, big trouble, because we do that a lot.
+ .PP
+ Many people using 3B1s, aka UNIX PCs,
+ run the Korn shell as their \fI/bin/sh\fR.
+ Some other folks may do this too.
+ Beware that \fIksh\fR was not fully \fIsh\fR-compatible for a
+ long time, with some subtle differences in the
+ ill-documented behavior of backquotes and backslashes.
+ Some of the C News shell scripts,
+ notably \fIinews\fR,
+ are known to hit these bugs.
+ We are \fItold\fR that current \fIksh\fRs have fixed them.
+ .SH
+ Make vs. Test
+ .PP
+ There is a persistent problem on 3B2s with implementations of \fImake\fR
+ that violate the SVID in a subtle way.
+ They attempt to execute makefile commands directly, rather than via the
+ shell, if the commands do not contain metacharacters.
+ This means that if\(emas on many 3B2s\(em\fItest\fR is a shell builtin
+ \fIand there is no /bin/test program\fR, the makefile line
+ ``test\ \-s\ file'' will cause \fImake\fR to complain about an unknown
+ command.
+ (The SVID says that makefile commands must be executed as if by
+ the shell, and the shell will execute this line correctly.)
+ We've added `;' on the ends of such lines, which suffices to convince
+ \fImake\fR to run a shell on the systems we've encountered, but AT&T
+ is good at finding ways to break such workarounds.
+ This problem is also known to occur in A/UX.
+ .SH
+ Offsetof
+ .PP
+ ANSI C requires C compilers to supply a macro \fIoffsetof\fR, which can
+ be used to find the offset of a structure member within the structure.
+ \fIRelaynews\fR's header-parsing code uses it,
+ defining it if the system has not supplied it.
+ Unfortunately, it is really hard to write a portable version of this.
+ The implementation we currently use is:
+ .DS
+ #define offsetof(type, mem) ((char *)&((type *)NULL)\->mem \- (char *)NULL)
+ .DE
+ The table in \fIrelay/hdrdefs.c\fR
+ puts invocations of \fIoffsetof\fR in initializers.
+ This turns out to be a severe stress test for C compilers.
+ A compilation error in \fIhdrdefs.c\fR is almost certain
+ to be problems with this macro.
+ Some compilers,
+ notably the one in Microport System V Release 2.3,
+ reject it.
+ We have heard a report that System V Release 2 on the VAX silently
+ miscompiles it!
+ If you have trouble with \fIoffsetof\fR, you might try this version instead:
+ .DS
+ #define offsetof(type, mem) ((int)&((type *)NULL)\->mem)
+ .DE
+ .SH
+ Fast Stdio Routines
+ .PP
+ We supply a set of fast standard-I/O routines that are compatible with
+ most AT&T-derived implementations of \fIstdio\fR.
+ They speed up C News quite a bit.
+ However, they don't work on all Unixes.
+ The tester program we supply, which the library-build procedure runs,
+ is thought to diagnose such problems 100% of the time.
+ It has been reported in the past that A/UX and Microport 386 stdios
+ flunk the test.
+ SunOS 4.0 used to pass the test falsely, but improvements in both
+ the test and the routines
+ seem to have cured the problems:
+ 4.0.3 passes the test and
+ as far as we can tell,
+ the routines run correctly under it.
+ .PP
+ In any case, if you are feeling nervous or are having mysterious problems,
+ telling \fIbuild\fR that you don't want to use the fast-stdio stuff is
+ always safe.
+ .PP
+ xxx more to come...


end of patch 10-Jan-1990
-- 
1972: Saturn V #15 flight-ready|     Henry Spencer at U of Toronto Zoology
1990: birds nesting in engines | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

brad@looking.on.ca (Brad Templeton) (01/11/90)

In article <1990Jan11.000427.29455@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>This one introduces one major change:  relaynews now completely implements
>the silly "Supersedes" header.  We still think that most current uses of
>Supersedes do not justify it, but Brad Templeton (always the nuisance :-))
>has found a real use for it, and there may be more.

I found a use for it indeed.  But I had to stop using it.  It wasn't just
C News's fault -- there are enough bad B newses out there as well.

And the Supersedes concept, as currently designed, is broken when it
runs into batching, at least if you supersede so quickly that two supersedes
go in the same batch.

So right now I just send out lots of control messages.

An another note, that causes a problem with RN.  Really active ClariNet
groups with major stories have lots of holes in them, created by stories
that were updated, cancelling the old version.

RN has code to deliberately sleep for 1.3 seconds when it finds such a hole,
which makes an annoying delay.  Never bothered me much on my fast console,
but it's a big pain when using RRN, which considers the holes as
'unreadable articles'

Easy fix -- tone down or remove the sleep and pause calls in ng.c.
Who cares about a pause to show where a cancelled article was, anyway?
-- 
Brad Templeton, ClariNet Communications Corp. -- Waterloo, Ontario 519/884-7473

jbrown@herron.uucp (Jordan Brown) (01/16/90)

In article <75308@looking.on.ca>, brad@looking.on.ca (Brad Templeton) writes:
> [clarinet groups often have big holes]
> RN has code to deliberately sleep for 1.3 seconds when it finds such a hole,
> which makes an annoying delay.  Never bothered me much on my fast console,
> but it's a big pain when using RRN, which considers the holes as
> 'unreadable articles'

This is a bug in RRN.  I've sent a patch to Stan Barber that fixes it.
The problem is that the higher-level code that doesn't know it's RRN
expects errno to reflect the cause of the error.  The low-level RN
code leaves it set properly (or rather, open does), but the RRN code
doesn't.  The high-level code sees a reported error, but since errno
isn't ENOENT, it assumes that something wierd is going on and so sleeps
so you can see it.  My patch makes the low-level RRN code set errno
to ENOENT when the problem occurs.

Unfortunately, the code is on another machine do I can't just post a diff.
I believe it was in artio.c that I added a simple "errno = ENOENT;".

The result is an enormous improvement in RRN performance in the face
of holes.  (Another big source is "Welcome to XXX" messages with expires:
headers that keep them around long after you've tossed articles after
them.)
-- 
Jordan Brown
jbrown@jato.jpl.nasa.gov