[news.software.b] 14-Sep-1989 patch for C News, corrected version

henry@utzoo.uucp (Henry Spencer) (09/17/89)

(See accompanying article for explanation of correction.)

Well, dbz is still "in the works" -- I've been busy -- but here's the latest
batch of (mostly) small stuff.  The big one here is that (with the exception
of superkludge, whose days are numbered anyway), we now 100.000% comply with
RFC822's rules about case-sensitivity in header keywords and message-IDs.
(Note that B News is not RFC822-compliant on message-IDs, which are only
*partly* [!] case-insensitive.)  Unless you want duplicate checking to fail
temporarily, you will need to rebuild your dbm/dbz indexes to the history
file when installing this.  (The history file itself is unchanged.)
There are some #ifdefs in libcnews/case.c to give you B News behavior, if
you really want it.

Other changes:  The usual minor cleanup and fixes for small annoyances.
A new transmitter for the batcher, viarsh.  Robustness fixes in history
rebuilding.  A warning message when configuration mismatches (or testing)
cause programs to renounce setuid status.  Robustness improvements in the
fast-stdio stuff, which may let it run on more systems.  Fix for a bug in
change-moderation-status's newsgroup matching.  Slightly less noise from
checkgroups.  Inews now passes formfeeds.  Inews -p now works better.
A new program, recovact, to help recover an active file after problems.
And inews's rsh handling has been cleaned up and revised, which should
cure assorted minor problems.

start of patch 14-Sep-1989
(suggested archive name: `pch14Sep89.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
*** tmp.dates.with.really.long.filename.for.patch	Thu Sep 14 15:51:07 1989
--- PATCHDATES	Thu Sep 14 15:51:07 1989
***************
*** 1,5 ****
--- 1,6 ----
  23-Jun-1989
  7-Jul-1989
  23-Jul-1989
  22-Aug-1989
  24-Aug-1989
+ 14-Sep-1989

Changed files, if any:

*** cnpatch/tmp.file	Thu Sep 14 15:51:12 1989
--- batch/Makefile	Sat Aug 26 22:48:38 1989
***************
*** 17,24 ****
  PGMS=batcher batchih batchsm batchsplit comp compcun nocomp viainews viauux \
  	sendbatches compc7 c7encode viamail viapmail bencode compb viauuxz \
! 	viaemail
  DTR=Makefile batcher.c batchih batchsplit c7encode.c comp compc7 compcun \
  	newsbatch.8 nocomp sendbatches viainews viauux viamail \
! 	bencode.c coder.h crctab.c compb viapmail
  ALL = $(PGMS) batchparms
  
--- 17,24 ----
  PGMS=batcher batchih batchsm batchsplit comp compcun nocomp viainews viauux \
  	sendbatches compc7 c7encode viamail viapmail bencode compb viauuxz \
! 	viaemail viarsh
  DTR=Makefile batcher.c batchih batchsplit c7encode.c comp compc7 compcun \
  	newsbatch.8 nocomp sendbatches viainews viauux viamail \
! 	bencode.c coder.h crctab.c compb viapmail viarsh
  ALL = $(PGMS) batchparms
  

*** cnpatch/tmp.file	Thu Sep 14 15:51:22 1989
--- conf/build	Tue Aug 29 16:43:42 1989
***************
*** 994,997 ****
--- 994,999 ----
  	echo "echo 'general 0000000000 0000000001 y' >active"
  	echo "echo 'news.announce.newusers 0000000000 0000000001 y' >>active"
+ 	echo "echo 'control 0000000000 0000000001 y' >>active"
+ 	echo "echo 'junk 0000000000 0000000001 y' >>active"
  	echo ">errlog"
  	echo ">history"
***************
*** 1083,1086 ****
--- 1085,1089 ----
  	echo "	chmod +x $newsconfig"
  	echo "fi"
+ 	echo "$newsbin/maint/adddirs"
  	echo ": It is virtually certain that some of those control files"
  	echo ": need modifying to suit your system.  In particular, you"

*** cnpatch/tmp.file	Thu Sep 14 15:51:30 1989
--- conf/spacefor.proto	Tue Aug 29 19:22:20 1989
***************
*** 22,26 ****
  	if test " $server" != " $me"
  	then
! 		exec rsh $server "PATH=$PATH `basename $0` $*"
  		# does not return
  	fi
--- 22,26 ----
  	if test " $server" != " $me"
  	then
! 		exec rsh $server /bin/sh -c "'PATH=$PATH `basename $0` $*'"
  		# does not return
  	fi

*** cnpatch/tmp.file	Thu Sep 14 15:51:32 1989
--- conf/subst.hs	Wed Sep 13 16:50:55 1989
***************
*** 42,43 ****
--- 42,45 ----
  notebook/config
  batch/batchih
+ expire/recovact
+ misc/adddirs

*** cnpatch/tmp.file	Thu Sep 14 15:51:32 1989
--- conf/sys.proto	Tue Aug 29 19:30:01 1989
***************
*** 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/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)
--- 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)

*** cnpatch/tmp.file	Thu Sep 14 15:51:34 1989
--- contrib/dbz	Thu Sep 14 15:36:16 1989
***************
*** 174,177 ****
--- 174,179 ----
  
  		/* lcase(buffer, key.dsize);	lcase is a B news botch */
+ 		buffer[key.dsize] = '\0';
+ 		(void) rfc822ize(buffer);
  		if (buffer[key.dsize - 1] == '\t') {
  			buffer[key.dsize - 1] = '\0';

*** cnpatch/tmp.file	Thu Sep 14 15:51:48 1989
--- expire/Makefile	Thu Sep 14 15:50:17 1989
***************
*** 7,14 ****
  LIBS= ../libcnews.a
  THEM = expire histdups histinfo histslash mkdbm mkhistory \
! 	superkludge upact doexpire mkadir
  DTR = README Makefile dircheck doexpire expire.c histdups histinfo.c \
  	histslash.c mkdbm.c mkhistory pgood superkludge tgood upact \
! 	mkadir updatemin.c
  UPACT=upact
  # =()<NEWSARTS = @<NEWSARTS>@>()=
--- 7,14 ----
  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 \
! 	mkadir updatemin.c recovact
  UPACT=upact
  # =()<NEWSARTS = @<NEWSARTS>@>()=
***************
*** 212,219 ****
  	test ! -f arts/urp/99 ;
  	cmp history history.after
! 	: "that's it for expire, on to upact"
  	$(D) ./$(UPACT)
  	cmp active active.after
  	$(D) ./$(UPACT)
  	cmp active active.after
  	: "and for upact, on to mkhistory"
--- 212,224 ----
  	test ! -f arts/urp/99 ;
  	cmp history history.after
! 	: "that's it for expire, on to upact and recovact"
  	$(D) ./$(UPACT)
  	cmp active active.after
  	$(D) ./$(UPACT)
+ 	cmp active active.after
+ 	$(D) ./recovact
+ 	cmp active active.after
+ 	sed '/^foo /s/99/09/' active.after >active
+ 	$(D) ./recovact
  	cmp active active.after
  	: "and for upact, on to mkhistory"

*** cnpatch/tmp.file	Thu Sep 14 15:51:51 1989
--- expire/expire.c	Wed Sep 13 21:02:42 1989
***************
*** 77,80 ****
--- 77,81 ----
  int verbose = 0;		/* report statistics */
  int rebuild = 1;		/* rebuild history files */
+ int violate = 0;		/* non-rfc822 case-sensitive messageIDs */
  
  long nkept = 0;			/* count of articles not expired */
***************
*** 167,171 ****
  	ftime(&ftnow);
  
! 	while ((c = getopt(argc, argv, "pa:sF:cn:tlvrd")) != EOF)
  		switch (c) {
  		case 'p':	/* print info line for archived articles */
--- 168,172 ----
  	ftime(&ftnow);
  
! 	while ((c = getopt(argc, argv, "pa:sF:cn:tlvrVd")) != EOF)
  		switch (c) {
  		case 'p':	/* print info line for archived articles */
***************
*** 199,202 ****
--- 200,206 ----
  			rebuild = 0;
  			break;
+ 		case 'V':	/* non-rfc822 dualcase messageIDs */
+ 			violate = 1;
+ 			break;
  		case 'd':	/* debug */
  			expdebug = 1;
***************
*** 215,220 ****
  		setbuf(stderr, (char *)NULL);
  
- 	(void) cistrncmp("a", "a", 1);		/* prime case.c */
- 
  	if (optind < argc) {
  		cf = eufopen(argv[optind], "r");
--- 219,222 ----
***************
*** 433,436 ****
--- 435,439 ----
  	datum rhs;
  	register int ret;
+ 	register char *id;
  
  	cd(ctlfile((char *)NULL));
***************
*** 464,469 ****
  			/* make the DBM entry */
  			*nameend = '\0';
! 			lhs.dptr = line;
! 			lhs.dsize = strlen(line)+1;
  			here = ftell(new);
  			rhs.dptr = (char *)&here;
--- 467,476 ----
  			/* make the DBM entry */
  			*nameend = '\0';
! 			if (!violate)
! 				lhs.dptr = rfc822ize(strsave(line));
! 			else
! 				lhs.dptr = strsave(line);
! 			*nameend = '\t';
! 			lhs.dsize = strlen(lhs.dptr)+1;
  			here = ftell(new);
  			rhs.dptr = (char *)&here;
***************
*** 473,477 ****
  			if (ret < 0)
  				fail("dbm failure on `%s'", line);
! 			*nameend = '\t';
  
  			/* and the history entry */
--- 480,484 ----
  			if (ret < 0)
  				fail("dbm failure on `%s'", line);
! 			free(lhs.dptr);
  
  			/* and the history entry */
***************
*** 909,913 ****
  	limit = buf + bsize - len;
  	for (scan = buf; scan < limit; scan++)
! 		if (TOLOW(*scan) == 's' && cistrncmp(scan, sline, len) == 0 &&
  				(scan == buf || *(scan-1) == '\n')) {
  			scan += len;
--- 916,920 ----
  	limit = buf + bsize - len;
  	for (scan = buf; scan < limit; scan++)
! 		if (CISTREQN(scan, sline, len) &&
  				(scan == buf || *(scan-1) == '\n')) {
  			scan += len;
***************
*** 915,919 ****
  				if (*limit == '\n')
  					break;
! 			while (scan < limit && isspace(*scan))
  				scan++;
  			len = limit-scan;
--- 922,926 ----
  				if (*limit == '\n')
  					break;
! 			while (scan < limit && isascii(*scan) && isspace(*scan))
  				scan++;
  			len = limit-scan;
***************
*** 1143,1147 ****
   */
  void
! unprivileged()
  {
  }
--- 1150,1155 ----
   */
  void
! unprivileged(reason)
! char *reason;
  {
  }

*** cnpatch/tmp.file	Thu Sep 14 15:51:56 1989
--- expire/histinfo.c	Mon Sep 11 17:38:15 1989
***************
*** 87,95 ****
  	while ((line = fgetms(in)) != NULL && strcmp(line, "\n") != 0) {
  		line[strlen(line)-1] = '\0';		/* trim newline */
! 		if (cistrncmp(line, msgidnm, STRLEN(msgidnm)) == 0) {
  			if (msgid != NULL)
  				free(msgid);
  			msgid = strsave(line+STRLEN(msgidnm));
! 		} else if (cistrncmp(line, expnm, STRLEN(expnm)) == 0) {
  			free(expiry);
  			expiry = strsave(line+STRLEN(expnm));
--- 87,95 ----
  	while ((line = fgetms(in)) != NULL && strcmp(line, "\n") != 0) {
  		line[strlen(line)-1] = '\0';		/* trim newline */
! 		if (CISTREQN(line, msgidnm, STRLEN(msgidnm))) {
  			if (msgid != NULL)
  				free(msgid);
  			msgid = strsave(line+STRLEN(msgidnm));
! 		} else if (CISTREQN(line, expnm, STRLEN(expnm))) {
  			free(expiry);
  			expiry = strsave(line+STRLEN(expnm));
***************
*** 109,113 ****
  
  	/* deal with empty and trash articles */
! 	if (msgid == NULL || strchr(msgid, '\t') != NULL) {
  		if (msgid != NULL)
  			free(msgid);
--- 109,116 ----
  
  	/* deal with empty and trash articles */
! 	if (msgid == NULL || strchr(msgid, '\t') != NULL || msgid[0] != '<' ||
! 					msgid[strlen(msgid)-1] != '>' ||
! 					strchr(expiry, '\t') != NULL ||
! 					strchr(expiry, '~') != NULL) {
  		if (msgid != NULL)
  			free(msgid);
***************
*** 134,138 ****
   */
  void
! unprivileged()
  {
  }
--- 137,142 ----
   */
  void
! unprivileged(reason)
! char *reason;
  {
  }

*** cnpatch/tmp.file	Thu Sep 14 15:51:57 1989
--- expire/histslash.c	Tue Aug 29 19:24:44 1989
***************
*** 32,36 ****
  			scan++;
  		}
! 		fputs(line, stdout);
  		free(line);
  	}
--- 32,37 ----
  			scan++;
  		}
! 		if (fputs(line, stdout) == EOF)
! 			error("fputs failed", "");
  		free(line);
  	}

*** cnpatch/tmp.file	Thu Sep 14 15:51:58 1989
--- expire/mkdbm.c	Wed Sep 13 16:36:31 1989
***************
*** 7,12 ****
  #include <stdio.h>
  #include <string.h>
- #include <ctype.h>
  #include "fgetmfs.h"
  
  
--- 7,12 ----
  #include <stdio.h>
  #include <string.h>
  #include "fgetmfs.h"
+ #include "case.h"
  
  
***************
*** 40,43 ****
--- 40,44 ----
  		*scan = '\0';
  
+ 		rfc822ize(line);
  		lhs.dptr = line;
  		lhs.dsize = strlen(line) + 1;

*** cnpatch/tmp.file	Thu Sep 14 15:51:59 1989
--- expire/mkhistory	Wed Sep 13 20:49:00 1989
***************
*** 8,12 ****
  umask $NEWSUMASK
  
! lock="$NEWSCTL/LOCK"		# modify name as appropriate
  ltemp="$NEWSCTL/L.$$"
  echo $$ >$ltemp
--- 8,12 ----
  umask $NEWSUMASK
  
! lock="$NEWSCTL/LOCK"
  ltemp="$NEWSCTL/L.$$"
  echo $$ >$ltemp

*** cnpatch/tmp.file	Thu Sep 14 15:52:00 1989
--- expire/superkludge	Wed Sep 13 16:56:22 1989
***************
*** 32,36 ****
  			END { print FILENAME, mid, sup ; exit }' $f >>/tmp/sup$$
  	done
! 	awk 'NF > 3 || $2 !~ /^<.*>$/ || $3 !~ /^(<.*>)?$/' /tmp/sup$$ >/tmp/supx$$
  	if test -s /tmp/supx$$
  	then
--- 32,36 ----
  			END { print FILENAME, mid, sup ; exit }' $f >>/tmp/sup$$
  	done
! 	awk 'NF > 3 || $2 !~ /^<.*>$/ || (NF == 3 && $3 !~ /^(<.*>)?$/)' /tmp/sup$$ >/tmp/supx$$
  	if test -s /tmp/supx$$
  	then

*** cnpatch/tmp.file	Thu Sep 14 15:52:01 1989
--- expire/updatemin.c	Tue Sep  5 17:29:35 1989
***************
*** 211,215 ****
   */
  void
! unprivileged()
  {
  }
--- 211,216 ----
   */
  void
! unprivileged(reason)
! char *reason;
  {
  }

*** cnpatch/tmp.file	Thu Sep 14 15:52:04 1989
--- h/case.h	Sun Aug 27 18:39:49 1989
***************
*** 1,5 ****
  extern int cistrncmp();
  
- /* must call the above once before invoking the macro below */
  extern char casemap[];
! #define	TOLOW(c)	(casemap[(c)])
--- 1,12 ----
  extern int cistrncmp();
+ extern char *rfc822ize();
  
  extern char casemap[];
! 
! /* must call cistrncmp before invoking TOLOW... */
! #define	TOLOW(c)	(casemap[(c)+128])	/* see case.c for why 128 */
! 
! /* ...but the use of it in CISTREQN is safe without the preliminary call (!) */
! /* CISTREQN is an optimised case-insensitive strncmp(a,b,n)==0; n > 0 */
! #define CISTREQN(a, b, n) \
! 	(TOLOW((a)[0]) == TOLOW((b)[0]) && cistrncmp(a, b, n) == 0)

*** cnpatch/tmp.file	Thu Sep 14 15:52:05 1989
--- h/libc.h	Sat Aug 26 18:34:25 1989
***************
*** 56,61 ****
  extern FILE *popen();			/* stdio.h */
  #ifdef __STDC__
! extern int printf(char *fmt, ...), fprintf(FILE *, char *fmt, ...); /* stdio.h */
! extern sprvalue sprintf(char *buf, char *fmt, ...);		/* stdio.h */
  #else					/* __STDC__ */
  extern int printf(), fprintf();		/* stdio.h */
--- 56,62 ----
  extern FILE *popen();			/* stdio.h */
  #ifdef __STDC__
! extern int printf(char *fmt, ...)	/* stdio.h */
! extern int fprintf(FILE *fp, char *fmt, ...);		/* stdio.h */
! extern sprvalue sprintf(char *buf, char *fmt, ...);	/* stdio.h */
  #else					/* __STDC__ */
  extern int printf(), fprintf();		/* stdio.h */

*** cnpatch/tmp.file	Thu Sep 14 15:52:11 1989
--- input/newsspool.c	Tue Sep  5 17:20:37 1989
***************
*** 285,291 ****
   */
  void
! unprivileged()
  {
  	setgid(getgid());
  	setuid(getuid());
  }
--- 285,294 ----
   */
  void
! unprivileged(reason)
! char *reason;
  {
  	setgid(getgid());
  	setuid(getuid());
+ 	fprintf(stderr, "%s: renouncing setuid due to nonstandard `%s'\n",
+ 							progname, reason);
  }

*** cnpatch/tmp.file	Thu Sep 14 15:52:26 1989
--- libcnews/case.c	Mon Aug 28 14:16:11 1989
***************
*** 5,10 ****
   * headers are limited to the ASCII characters by RFC822.  It is barely
   * possible that we might be dealing with a translation into another
!  * character set, but in particular it's very difficult for a header
!  * character to be negative without blowing standard compliance.
   */
  #include <stdio.h>
--- 5,13 ----
   * headers are limited to the ASCII characters by RFC822.  It is barely
   * possible that we might be dealing with a translation into another
!  * character set, but in particular it's very unlikely for a header
!  * character to be outside -128..255.
!  *
!  * Life would be a whole lot simpler if tolower() could safely and portably
!  * be applied to any char.
   */
  #include <stdio.h>
***************
*** 12,16 ****
  #include "case.h"
  
! #define	MAPSIZE	256		/* overkill for ASCII */
  char casemap[MAPSIZE];		/* relies on init to '\0' */
  static int primed = 0;		/* has casemap been set up? */
--- 15,21 ----
  #include "case.h"
  
! /* note that case.h knows the value of OFFSET */
! #define	OFFSET	128		/* avoid trouble with negative chars */
! #define	MAPSIZE	(256+OFFSET)
  char casemap[MAPSIZE];		/* relies on init to '\0' */
  static int primed = 0;		/* has casemap been set up? */
***************
*** 25,28 ****
--- 30,34 ----
  	register char *up;
  	register int c;
+ 	register int i;
  	static char lower[] = "abcdefghijklmnopqrstuvwxyz";
  	static char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
***************
*** 30,39 ****
  	for (lp = lower, up = upper; *lp != '\0'; lp++, up++) {
  		c = *lp;
! 		casemap[c] = c;
! 		casemap[*up] = c;
  	}
! 	for (c = 0; c < MAPSIZE; c++)
! 		if (casemap[c] == '\0')
! 			casemap[c] = c;
  	primed = 1;
  }
--- 36,45 ----
  	for (lp = lower, up = upper; *lp != '\0'; lp++, up++) {
  		c = *lp;
! 		casemap[c+OFFSET] = c;
! 		casemap[*up+OFFSET] = c;
  	}
! 	for (i = 0; i < MAPSIZE; i++)
! 		if (casemap[i] == '\0')
! 			casemap[i] = (char)(i-OFFSET);
  	primed = 1;
  }
***************
*** 78,80 ****
--- 84,120 ----
  	else
  		return(TOLOW(*p1) - TOLOW(*p2));
+ }
+ 
+ /*
+  - rfc822ize - do the bizarre case conversion needed for rfc822 message-ids
+  */
+ char *				/* returns the argument */
+ rfc822ize(s)
+ char *s;
+ {
+ 	register char *p;
+ 	static char post[] = "postmaster";
+ 	static int postlen = sizeof(post)-1;
+ 
+ 	if (!primed)
+ 		prime();
+ 
+ 	p = strrchr(s, '@');
+ 	if (p == NULL)			/* no local/domain split */
+ 		p = "";			/* assume all local */
+ 	else if	(p - (s+1) == postlen && CISTREQN(s+1, post, postlen)) {
+ 		/* crazy special case -- "postmaster" is case-insensitive */
+ 		p = s;
+ 	}
+ #ifdef NONSTANDARD
+ #ifdef RFCVIOLATION
+ #ifdef B_2_11_MISTAKE
+ 	p = s;				/* all case-insensitive */
+ #endif
+ #endif
+ #endif
+ 	for (; *p != '\0'; p++)
+ 		*p = TOLOW(*p);
+ 
+ 	return(s);
  }

*** cnpatch/tmp.file	Thu Sep 14 15:52:28 1989
--- libcnews/config.c	Tue Sep  5 17:17:40 1989
***************
*** 61,67 ****
  {
  	register char *p;
- 	register int nonstd = NO;
  	register int mask;
  	register char *scan;
  
  	if (dirsset)
--- 61,68 ----
  {
  	register char *p;
  	register int mask;
  	register char *scan;
+ 	register int ns = 0;
+ #	define	NONSTD(reason)	{ if (!ns) { unprivileged(reason); ns = 1; } }
  
  	if (dirsset)
***************
*** 70,75 ****
  	p = getenv("NEWSARTS");
  	if (p != NULL && !STREQ(p, arts)) {
- 		nonstd = YES;
  		arts = p;
  	}
  
--- 71,76 ----
  	p = getenv("NEWSARTS");
  	if (p != NULL && !STREQ(p, arts)) {
  		arts = p;
+ 		NONSTD("NEWSARTS");
  	}
  
***************
*** 77,81 ****
  	if (p != NULL && !STREQ(p, ctl)) {
  		ctl = p;
! 		nonstd = YES;
  	}
  
--- 78,82 ----
  	if (p != NULL && !STREQ(p, ctl)) {
  		ctl = p;
! 		NONSTD("NEWSCTL");
  	}
  
***************
*** 83,87 ****
  	if (p != NULL && !STREQ(p, path)) {
  		path = p;
! 		nonstd = YES;
  	}
  
--- 84,88 ----
  	if (p != NULL && !STREQ(p, path)) {
  		path = p;
! 		NONSTD("NEWSPATH");
  	}
  
***************
*** 89,93 ****
  	if (p != NULL && !STREQ(p, bin)) {
  		bin = p;
! 		nonstd = YES;
  	}
  
--- 90,94 ----
  	if (p != NULL && !STREQ(p, bin)) {
  		bin = p;
! 		NONSTD("NEWSBIN");
  	}
  
***************
*** 104,108 ****
  		if (mask != numask) {
  			numask = mask;
! 			nonstd = YES;
  		}
  	}
--- 105,109 ----
  		if (mask != numask) {
  			numask = mask;
! 			NONSTD("NEWSUMASK");
  		}
  	}
***************
*** 111,120 ****
  	if (p != NULL && !STREQ(p, nmaster)) {
  		nmaster = p;
! 		nonstd = YES;
  	}
  
  	dirsset = YES;
- 	if (nonstd)
- 		unprivileged();
  }
  
--- 112,119 ----
  	if (p != NULL && !STREQ(p, nmaster)) {
  		nmaster = p;
! 		NONSTD("NEWSMASTER");
  	}
  
  	dirsset = YES;
  }
  

*** cnpatch/tmp.file	Thu Sep 14 15:52:48 1989
--- libstdio/fgets.c	Wed Sep  6 16:33:39 1989
***************
*** 30,33 ****
--- 30,35 ----
  			register int bytesleft = fp->_cnt;	/* to EOB */
  
+ 			if (bytesleft < 0)
+ 				bytesleft = 0;
  			if (bytesleft > lim)	/* buffer longer than s */
  				bytesleft = lim; /* only copy to s's end */

*** cnpatch/tmp.file	Thu Sep 14 15:52:50 1989
--- libstdio/rdwr.c	Wed Sep  6 16:33:38 1989
***************
*** 29,32 ****
--- 29,34 ----
  			register int copy = fp->_cnt;
  
+ 			if (copy < 0)
+ 				copy = 0;
  			if (copy > bytes)	/* buffer longer than ptr */
  				copy = bytes;	/* only fill ptr */
***************
*** 76,79 ****
--- 78,83 ----
  			register int copy = fp->_cnt;
  
+ 			if (copy < 0)
+ 				copy = 0;
  			if (copy > bytes)	/* buffer longer than ptr */
  				copy = bytes;	/* only empty ptr */

*** cnpatch/tmp.file	Thu Sep 14 15:53:01 1989
--- man/expire.8	Thu Sep 14 15:50:22 1989
***************
*** 7,11 ****
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH EXPIRE 8 "23 Aug 1989" "C News"
  .SH NAME
  expire, doexpire \- expire old news
--- 7,11 ----
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH EXPIRE 8 "13 Sept 1989" "C News"
  .SH NAME
  expire, doexpire \- expire old news
***************
*** 15,18 ****
--- 15,20 ----
  upact \- update news active file
  .br
+ recovact \- partially recover news active file
+ .br
  superkludge \- implement stupid Supersedes header in news
  .SH SYNOPSIS
***************
*** 53,56 ****
--- 55,60 ----
  .B \*b/expire/upact
  .br
+ .B \*b/expire/recovact
+ .br
  .B \*b/expire/superkludge
  [
***************
*** 226,230 ****
  updates the third fields of the \fIactive\fR file to match the articles
  in \fI\*a\fR (for historical reasons, \fIexpire\fR does not do this).
! These programs are both fairly slow and they both lock the whole news
  system for the duration of the run, so they should not be run casually.
  .PP
--- 230,238 ----
  updates the third fields of the \fIactive\fR file to match the articles
  in \fI\*a\fR (for historical reasons, \fIexpire\fR does not do this).
! .I Recovact
! updates the second fields of the \fIactive\fR file to match the articles
! in \fI\*a\fR,
! for use in disaster recovery based on an outdated \fIactive\fR file.
! 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
***************
*** 266,270 ****
  `Supersedes' header.
  .PP
! Some of the more obscure options of \fIexpire\fR have not been tested well.
  .PP
  One cannot put more than one newsgroup into a single archiving directory
--- 274,280 ----
  `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

*** cnpatch/tmp.file	Thu Sep 14 15:53:03 1989
--- man/inews.1	Tue Aug 29 12:20:37 1989
***************
*** 7,11 ****
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH INEWS 1 "6 Aug 1989" "C News"
  .SH NAME
  inews \- `user-friendly' news-posting front-end for relaynews
--- 7,11 ----
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH INEWS 1 "29 Aug 1989" "C News"
  .SH NAME
  inews \- `user-friendly' news-posting front-end for relaynews
***************
*** 117,124 ****
  .PP
  .B "inews -p"
  is exactly equivalent to
  invoking
! .BR rnews (8);
! this is seldom useful in C News.
  Normal usage is
  .BR "inews -h" ,
--- 117,133 ----
  .PP
  .B "inews -p"
+ .I files
  is exactly equivalent to
  invoking
! .B "cat"
! .I files
! .BR "| relaynews -r" ,
! where
! .I files
! may be an empty list;
! there is no automatic recovery of the input
! file(s) in case of errors,
! full disks
! or other problems.
  Normal usage is
  .BR "inews -h" ,
***************
*** 209,212 ****
--- 218,224 ----
  (no line-printer graphics, please)
  .TP
+ .IB $HOME /dead.article
+ saved article in case of errors or problems
+ .TP
  .BR /tmp/in *
  temporaries
***************
*** 214,219 ****
  .SH "SEE ALSO"
  .IR mail (1),
! .IR rnews (1),
! .IR newsaux (8)
  .SH DIAGNOSTICS
  .I inews
--- 226,232 ----
  .SH "SEE ALSO"
  .IR mail (1),
! .IR news (5),
! .IR newsaux (8),
! .IR relaynews (8)
  .SH DIAGNOSTICS
  .I inews

*** cnpatch/tmp.file	Thu Sep 14 15:53:04 1989
--- man/newsaux.8	Wed Sep 13 20:49:45 1989
***************
*** 7,11 ****
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH NEWSAUX 8 "19 Aug 1989" "C News"
  .SH NAME
  spacefor \- check available space for news
--- 7,11 ----
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH NEWSAUX 8 "13 Sept 1989" "C News"
  .SH NAME
  spacefor \- check available space for news
***************
*** 21,24 ****
--- 21,26 ----
  gngp \- search text using a newsgroup pattern
  .br
+ canonhdr \- extract header and canonicalize case in header keywords
+ .br
  newshist \- extract history line for news article(s)
  .br
***************
*** 57,63 ****
--- 59,70 ----
  [
  .B \-a
+ ] [
+ .B \-r
  ]
  ngpattern file ...
  .br
+ .B \*b/canonhdr
+ [ file ] ...
+ .br
  .B \*b/maint/newshist
  msgid ...
***************
*** 133,136 ****
--- 140,156 ----
  flag is given, the eligible substrings start at the beginning of the
  line and end at white space or the end of the line.
+ The
+ \fB\-r\fR flag reverses the inputs, with patterns coming from
+ the file and the argument taken as the line(s).
+ .PP
+ .I Canonhdr
+ takes the concatenation of its input \fIfile\fR(s) (standard input if none)
+ as an article,
+ and outputs the header from the article with header keywords canonicalized
+ for easier processing.
+ Canonicalization forces all alphabetics to lower case except the first
+ letter of the keyword, which is forced to upper case.
+ (One exception:
+ ``Message-ID'' is the canonical form of [e.g.] ``message-id''.)
  .PP
  .I Newshist

*** cnpatch/tmp.file	Thu Sep 14 15:53:06 1989
--- man/newsbatch.8	Wed Sep 13 16:12:45 1989
***************
*** 7,11 ****
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH NEWSBATCH 8 "19 Aug 1989" "C News"
  .SH NAME
  sendbatches, batchsplit \- news batching to other sites
--- 7,11 ----
  .\" =()<.ds m @<NEWSMASTER>@>()=
  .ds m usenet
! .TH NEWSBATCH 8 "26 Aug 1989" "C News"
  .SH NAME
  sendbatches, batchsplit \- news batching to other sites
***************
*** 17,21 ****
  c7encode, bencode \- compressed-news-batch encoding
  .br
! viauux, viauuxz, viainews \- news-batch transmission
  .br
  viamail, viaemail, viapmail \- news-batch transmission via mail
--- 17,21 ----
  c7encode, bencode \- compressed-news-batch encoding
  .br
! viauux, viauuxz, viainews, viarsh \- news-batch transmission
  .br
  viamail, viaemail, viapmail \- news-batch transmission via mail
***************
*** 72,75 ****
--- 72,78 ----
  site
  .br
+ .B \&.../viarsh
+ site
+ .br
  .B \&.../viamail
  site
***************
*** 216,219 ****
--- 219,227 ----
  feed the batch back to \fIinews\fR, ignoring the \fIsite\fR argument
  (normally useful only for ihave/sendme)
+ .IP viarsh
+ use \fIrsh\fR to run \fIrnews\fR on the \fIsite\fR via Ethernet,
+ Internet, etc.
+ (the directory containing \fIrnews\fR
+ must be in the default PATH on \fIsite\fR)
  .IP viamail
  mail the batch to \fIsite\fB!rnews\fR

*** cnpatch/tmp.file	Thu Sep 14 15:53:09 1989
--- misc/Makefile	Tue Aug 29 12:18:05 1989
***************
*** 13,17 ****
  MAINTBIN=newshist
  MAINT = $(MAINTBIN) newsdaily newswatch newsboot locknews addgroup delgroup adddirs
! UTILBIN = gngp newslock ctime getdate
  UTILS = $(UTILBIN) sizeof newshostname
  THEM = $(MAINT) $(UTILS)
--- 13,17 ----
  MAINTBIN=newshist
  MAINT = $(MAINTBIN) newsdaily newswatch newsboot locknews addgroup delgroup adddirs
! UTILBIN = gngp newslock ctime getdate canonhdr
  UTILS = $(UTILBIN) sizeof newshostname
  THEM = $(MAINT) $(UTILS)
***************
*** 45,48 ****
--- 45,51 ----
  newslock:	newslock.o $(LIBS)
  	$(CC) $(CFLAGS) $(LDFLAGS) newslock.o $(PRE) $(LIBS) $(POST) -o $@
+ 
+ canonhdr:	canonhdr.o $(LIBS)
+ 	$(CC) $(CFLAGS) $(LDFLAGS) canonhdr.o $(PRE) $(LIBS) $(POST) -o $@
  
  NHCFLAGS = -I$(RN) $(CFLAGS)

*** cnpatch/tmp.file	Thu Sep 14 15:53:13 1989
--- misc/gngp.c	Wed Sep  6 16:29:05 1989
***************
*** 1,4 ****
  /*
!  * gngp - globally find newsgroup and print
   *	like grep, but for newsgroup patterns instead of regular expressions
   */
--- 1,4 ----
  /*
!  * gngp - globally match newsgroup pattern and print
   *	like grep, but for newsgroup patterns instead of regular expressions
   */
***************
*** 13,16 ****
--- 13,17 ----
   */
  int anchored = 0;
+ int reverse = 0;	/* iff true, reverse argument & file roles */
  
  FILE *efopen();
***************
*** 30,34 ****
  
  	progname = argv[0];
! 	while ((c = getopt(argc, argv, "ad")) != EOF)
  		switch (c) {
  		case 'a':		/* anchored at start of line */
--- 31,35 ----
  
  	progname = argv[0];
! 	while ((c = getopt(argc, argv, "adr")) != EOF)
  		switch (c) {
  		case 'a':		/* anchored at start of line */
***************
*** 39,42 ****
--- 40,46 ----
  			debug++;
  			break;
+ 		case 'r':	/* reverse roles: ngs in arg., patterns in file */
+ 			reverse++;
+ 			break;
  		default:
  			errflg++;
***************
*** 44,48 ****
  		}
  	if (errflg || optind == argc) {
! 		(void) fprintf(stderr, "usage: %s [-ad] pattern [file...]\n",
  			progname);
  		exit(2);
--- 48,52 ----
  		}
  	if (errflg || optind == argc) {
! 		(void) fprintf(stderr, "usage: %s [-adr] ng_pattern [file...]\n",
  			progname);
  		exit(2);
***************
*** 102,106 ****
  		}
  	}
! 	returned = ngmatch(pattern, text);
  	if (anchored) {
  		if (whitesp != NULL)
--- 106,110 ----
  		}
  	}
! 	returned = (!reverse? ngmatch(pattern, text): ngmatch(text, pattern));
  	if (anchored) {
  		if (whitesp != NULL)

*** cnpatch/tmp.file	Thu Sep 14 15:53:27 1989
--- notebook/config	Tue Sep  5 17:31:30 1989
***************
*** 1,3 ****
! .DA "18 Feb 1989"
  .TL
  Configuration Mechanisms in C News
--- 1,3 ----
! .DA "5 Sept 1989"
  .TL
  Configuration Mechanisms in C News
***************
*** 93,100 ****
  .LP
  .RS
! .IP "void unprivileged(void);"
  Drop any special powers that should not be available to a normal user
  program,
  e.g. those obtained by set-uid bit.
  .RE
  .LP
--- 93,102 ----
  .LP
  .RS
! .IP "void unprivileged(char *reason);"
  Drop any special powers that should not be available to a normal user
  program,
  e.g. those obtained by set-uid bit.
+ .I Reason
+ is the name of the first environment variable that caused trouble.
  .RE
  .LP

*** cnpatch/tmp.file	Thu Sep 14 15:53:42 1989
--- relay/aux/chamod	Tue Sep 12 12:53:54 1989
***************
*** 19,26 ****
  esac
  
! aflag=`awk "/$1/"' { print $4 }' $NEWSCTL/active`
  if test "$aflag" != "$flag" -a \( "$aflag" = m -o "$aflag" = y \); then
  	# old & new flags differ & old flag is m or y
- 	# watch closely - shell quoting is tricky here
  	awk '
  $1 == "'$1'"	{			# this line is for first arg.
--- 19,26 ----
  esac
  
! # watch closely - shell quoting is tricky in the next two awk invocations
! aflag=`awk '$1 == "'$1'" { print $4 }' $NEWSCTL/active`
  if test "$aflag" != "$flag" -a \( "$aflag" = m -o "$aflag" = y \); then
  	# old & new flags differ & old flag is m or y
  	awk '
  $1 == "'$1'"	{			# this line is for first arg.

*** cnpatch/tmp.file	Thu Sep 14 15:53:46 1989
--- relay/aux/newsreply	Tue Aug 29 12:16:17 1989
***************
*** 10,23 ****
  umask $NEWSUMASK
  
! art=/tmp/nc$$
  mroute=/tmp/ncm$$
  
! cat >$art
  
  if test -r $NEWSCTL/replyusepath
  then
! 	sender="`grep '^Path:' $art | sed 's/^[^:]*://' `"
! 	echo "$sender"
! 	rm -f $art
  	exit 0
  fi
--- 10,22 ----
  umask $NEWSUMASK
  
! hdr=/tmp/nc$$
  mroute=/tmp/ncm$$
  
! canonhdr >$hdr
  
  if test -r $NEWSCTL/replyusepath
  then
! 	grep '^Path:' $hdr | sed 's/^[^:]*:[	 ]*//'
! 	rm -f $hdr
  	exit 0
  fi
***************
*** 24,30 ****
  
  # pick out the appropriate header
! sender="` grep '^Reply-To:' $art `"
  case "$sender" in
! "")	sender="` grep '^From:' $art `" ;;
  esac
  
--- 23,29 ----
  
  # pick out the appropriate header
! sender="` grep '^Reply-To:' $hdr `"
  case "$sender" in
! "")	sender="` grep '^From:' $hdr `" ;;
  esac
  
***************
*** 55,57 ****
  fi
  
! rm -f $art $mroute
--- 54,56 ----
  fi
  
! rm -f $hdr $mroute

*** cnpatch/tmp.file	Thu Sep 14 15:53:51 1989
--- relay/ctl/checkgroups	Wed Sep 13 13:18:50 1989
***************
*** 19,26 ****
  sort -u -o $NEWSCTL/newsgroups $NEWSCTL/newsgroups.bac
  
  # generate list of approved newsgroups from $nl/newsgroups
- # [^.]*\. in next two egreps was net.|mod.|fa., which is inadequate - geoff
  (echo junk; echo control; sed 's/[ \	].*//' $NEWSCTL/newsgroups |
! 	egrep "^([^.]*\.|general)") | sort -u >/tmp/$$a
  
  # generate list of locally-present newsgroups from $nl/active
--- 19,36 ----
  sort -u -o $NEWSCTL/newsgroups $NEWSCTL/newsgroups.bac
  
+ # locate our subscription list
+ me="`newshostname`"
+ gngppat=`awk -f $NEWSBIN/relay/canonsys.awk $NEWSCTL/sys |
+ 	egrep "^($me|ME):" |
+ 	awk -F: '
+ {
+ 	fields = split($2, field2, "/")	# split ngs/dists
+ 	print field2[1]			# print only ngs
+ 	exit
+ }' `
+ 
  # generate list of approved newsgroups from $nl/newsgroups
  (echo junk; echo control; sed 's/[ \	].*//' $NEWSCTL/newsgroups |
! 	egrep "^([^.]*\.|general)") | gngp -a "$gngppat" | sort -u >/tmp/$$a
  
  # generate list of locally-present newsgroups from $nl/active

*** cnpatch/tmp.file	Thu Sep 14 15:53:52 1989
--- relay/ctl/newgroup	Tue Aug 29 12:16:19 1989
***************
*** 9,23 ****
  umask $NEWSUMASK
  
! F=/tmp/nc$$
  
! trap "rm -f $F; exit 0" 0
! cat >$F
  
  # unapproved ctl msg? then quit
! grep -s '^Approved:' $F >/dev/null || { rm -f $F; exit 0; }
  
! SENDER="`grep '^Sender:' $F | sed 's/^[^:]*: *//'`"
  case "$SENDER" in
! "")	SENDER="`grep '^From:' $F | sed 's/^[^:]*: *//' `" ;;
  esac
  
--- 9,23 ----
  umask $NEWSUMASK
  
! hdr=/tmp/nc$$
  
! trap "rm -f $hdr; exit 0" 0
! canonhdr >$hdr
  
  # unapproved ctl msg? then quit
! grep -s '^Approved:' $hdr >/dev/null || { rm -f $hdr; exit 0; }
  
! SENDER="`grep '^Sender:' $hdr | sed 's/^[^:]*: *//'`"
  case "$SENDER" in
! "")	SENDER="`grep '^From:' $hdr | sed 's/^[^:]*: *//' `" ;;
  esac
  

*** cnpatch/tmp.file	Thu Sep 14 15:53:53 1989
--- relay/ctl/rmgroup	Tue Aug 29 12:16:20 1989
***************
*** 8,27 ****
  umask $NEWSUMASK
  
! F=/tmp/nc$$
  
! cat >$F
  
  # unapproved ctl msg? then quit
! egrep '^Approved:' $F >/dev/null || { rm -f $F; exit 0; }
  
  # quit if no active entry
  egrep "^`echo $1 | sed 's/\./\\\\./g'` " $NEWSCTL/active >/dev/null ||
! 	{ rm -f $F; exit 0; }
  
! SENDER="`grep '^Sender:' $F | sed 's/^[^:]*: *//'`"
  case "$SENDER" in
! "")
! 	SENDER="`grep '^From:' $F | sed 's/^[^:]*: *//'`"
! 	;;
  esac
  
--- 8,25 ----
  umask $NEWSUMASK
  
! hdr=/tmp/nc$$
  
! canonhdr >$hdr
  
  # unapproved ctl msg? then quit
! egrep '^Approved:' $hdr >/dev/null || { rm -f $hdr; exit 0; }
  
  # quit if no active entry
  egrep "^`echo $1 | sed 's/\./\\\\./g'` " $NEWSCTL/active >/dev/null ||
! 	{ rm -f $hdr; exit 0; }
  
! SENDER="`grep '^Sender:' $hdr | sed 's/^[^:]*: *//'`"
  case "$SENDER" in
! "")	SENDER="`grep '^From:' $hdr | sed 's/^[^:]*: *//'`" ;;
  esac
  
***************
*** 29,31 ****
  echo "rmgroup $1 says $SENDER" | mail $NEWSMASTER
  
! rm -f $F*
--- 27,29 ----
  echo "rmgroup $1 says $SENDER" | mail $NEWSMASTER
  
! rm -f $hdr

*** cnpatch/tmp.file	Thu Sep 14 15:54:01 1989
--- relay/hdrmunge.c	Tue Aug 29 12:18:03 1989
***************
*** 22,25 ****
--- 22,26 ----
  #include "libc.h"
  #include "news.h"
+ #include "case.h"
  #include "fileart.h"
  #include "headers.h"
***************
*** 92,98 ****
  	if (headdebug)
  		(void) fputs(buffer, stderr);
! 	for (vhp = hdrlst; *vhp != NULL; vhp++)
! 		if (STREQN(buffer, (*vhp)->hdrnm, (int)(*vhp)->hdrlen))
  			return;			/* don't save this header */
  	hdrsave(art, buffer, hdrlen);
  }
--- 93,102 ----
  	if (headdebug)
  		(void) fputs(buffer, stderr);
! 	for (vhp = hdrlst; *vhp != NULL; vhp++) {
! 		register char *hdrnm = (*vhp)->hdrnm;
! 
! 		if (CISTREQN(buffer, hdrnm, (int)(*vhp)->hdrlen))
  			return;			/* don't save this header */
+ 	}
  	hdrsave(art, buffer, hdrlen);
  }
***************
*** 172,176 ****
  register int hdrlen;
  {
! 	if (STREQN(hdr, pathhdr.hdrnm, (int)pathhdr.hdrlen)) {
  		register char *oldpath, *hostnm = hostname();
  
--- 176,180 ----
  register int hdrlen;
  {
! 	if (CISTREQN(hdr, pathhdr.hdrnm, (int)pathhdr.hdrlen)) {
  		register char *oldpath, *hostnm = hostname();
  

*** cnpatch/tmp.file	Thu Sep 14 15:54:05 1989
--- relay/hdrparse.c	Tue Aug 29 12:18:04 1989
***************
*** 4,11 ****
  
  #include <stdio.h>
- #include <ctype.h>
  #include <sys/types.h>
  #include "libc.h"
  #include "news.h"
  #include "headers.h"
  #include "hdrint.h"
--- 4,11 ----
  
  #include <stdio.h>
  #include <sys/types.h>
  #include "libc.h"
  #include "news.h"
+ #include "case.h"
  #include "headers.h"
  #include "hdrint.h"
***************
*** 22,30 ****
  
  /*
!  * Parse RFC822/850/1036 header into "hdrs".  Retain significant values.
!  * Assumes ishdr has been called first.
!  *
!  * If a keyword matches one in hdrlst, store the value in *malloc'ed memory*
!  * (N.B.).  freeheader() will free this memory.
   */
  void
--- 22,31 ----
  
  /*
!  * Parse (assumed) RFC822/850/1036 header in "line" (ishdr(line) can
!  * verify this) into "hdrs" using hdrlst set of keywords by retaining the
!  * value of any matching keyword.  Keyword matching is case-insensitive.
!  * If the keyword in "line" matches one in hdrlst, store the value in
!  * *malloc'ed memory* (N.B.) pointed to by a member of "hdrs".
!  * freeheader() will free this memory.
   */
  void
***************
*** 36,51 ****
  	register struct hdrdef **hpp;
  
! 	for (hpp = hdrlst; *hpp != NULL; hpp++)
! 		if (STREQN(line, (*hpp)->hdrnm, (int)(*hpp)->hdrlen) &&
! 		    (*hpp)->hdroff >= 0) {	/* paranoia */
! 			register char **ptrp =
! 				(char **)((char *)hdrs+(*hpp)->hdroff);
  
! 			nnfree(ptrp);	/* free prev. val. in this art. */
! 			*ptrp = strsave(skipsp(&line[(*hpp)->hdrlen]));
! 			if (*ptrp != NULL)
! 				trim(*ptrp);	/* cut trailing \n */
  			break;
! 		}
  }
  
--- 37,55 ----
  	register struct hdrdef **hpp;
  
! 	for (hpp = hdrlst; *hpp != NULL; hpp++) {
! 		register char *hdrnm = (*hpp)->hdrnm;
  
! 		if (CISTREQN(line, hdrnm, (int)(*hpp)->hdrlen) &&
! 		    (*hpp)->hdroff >= 0)	/* paranoia */
  			break;
! 	}
! 	if (*hpp != NULL) {
! 		register char **ptrp = (char **)((char *)hdrs+(*hpp)->hdroff);
! 
! 		nnfree(ptrp);		/* free prev. value in this article */
! 		*ptrp = strsave(skipsp(&line[(*hpp)->hdrlen]));
! 		if (*ptrp != NULL)
! 			trim(*ptrp);		/* cut trailing \n */
! 	}
  }
  

*** cnpatch/tmp.file	Thu Sep 14 15:54:08 1989
--- relay/history.c	Wed Sep  6 16:28:09 1989
***************
*** 23,26 ****
--- 23,27 ----
  #include "libc.h"
  #include "news.h"
+ #include "case.h"
  #include "config.h"
  #include "fgetmfs.h"
***************
*** 101,105 ****
  	if (!openhist())
  		return msgidkey;
! 	clnmsgid = strsave(msgid);
  	sanitise(clnmsgid);
  	msgidkey.dptr = clnmsgid;
--- 102,106 ----
  	if (!openhist())
  		return msgidkey;
! 	clnmsgid = rfc822ize(strsave(msgid));
  	sanitise(clnmsgid);
  	msgidkey.dptr = clnmsgid;
***************
*** 233,243 ****
  		fulldisk(art, filename);
  
! 	msgidkey.dptr = msgid;
  	msgidkey.dsize = strlen(msgid) + 1;	/* include NUL */
  	posdatum.dptr = (char *)&pos;
  	posdatum.dsize = sizeof pos;
! #ifdef NOSTOREVAL
! 	/* original v7 dbm store() returned no value */
! 	(void) store(msgidkey, posdatum);
  #else
  	if (store(msgidkey, posdatum) < 0)
--- 234,243 ----
  		fulldisk(art, filename);
  
! 	msgidkey.dptr = rfc822ize(strsave(msgid));
  	msgidkey.dsize = strlen(msgid) + 1;	/* include NUL */
  	posdatum.dptr = (char *)&pos;
  	posdatum.dsize = sizeof pos;
! #ifdef NOSTOREVAL		/* original v7 dbm store() returned no value */
! 	store(msgidkey, posdatum);
  #else
  	if (store(msgidkey, posdatum) < 0)
***************
*** 244,247 ****
--- 244,248 ----
  		fulldisk(art, filename);
  #endif
+ 	free(msgidkey.dptr);
  }
  

*** cnpatch/tmp.file	Thu Sep 14 15:54:14 1989
--- relay/makefile	Wed Sep 13 20:48:34 1989
***************
*** 76,80 ****
  	cp aux/* $(NEWSBIN)/relay
  	rm -f $(BIN)/inews
! 	ln $(NEWSBIN)/inject/inews $(BIN)/inews || cp sh/inews $(BIN)
  
  cmp:	relaynews
--- 76,80 ----
  	cp aux/* $(NEWSBIN)/relay
  	rm -f $(BIN)/inews
! 	ln $(NEWSBIN)/inject/inews $(BIN)/inews 2>/dev/null || cp sh/inews $(BIN)
  
  cmp:	relaynews

*** cnpatch/tmp.file	Thu Sep 14 15:54:20 1989
--- relay/procart.c	Wed Sep  6 16:34:22 1989
***************
*** 214,218 ****
  		art->a_charswritten += bodylen;
  	}
! 	for (; art->a_unread > 0 && !(art->a_status&ST_DISKFULL) &&
  	    (readcnt=fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) > 0;
  	    art->a_unread -= readcnt, art->a_charswritten += readcnt)
--- 214,218 ----
  		art->a_charswritten += bodylen;
  	}
! 	for (; art->a_unread > 0 && !(art->a_status&ST_DISKFULL) && !feof(in) &&
  	    (readcnt=fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) > 0;
  	    art->a_unread -= readcnt, art->a_charswritten += readcnt)

*** cnpatch/tmp.nonexistent.file.with.very.long.name.for.stupid.patch	Thu Sep 14 15:54:41 1989
--- relay/regress/out/stderr	Wed Sep 13 20:35:17 1989
***************
*** 0 ****
--- 1 ----
+ ./relaynews: warning: renouncing setuid due to nonstandard `NEWSARTS'

*** cnpatch/tmp.file	Thu Sep 14 15:54:45 1989
--- relay/relaynews.c	Wed Sep  6 16:25:32 1989
***************
*** 250,257 ****
  }
  
  void
! unprivileged()		/* called if NEWSARTS, NEWSCTL or NEWSBIN present */
  {
  	userealids = YES;
  }
  
--- 250,267 ----
  }
  
+ /*
+  * called if NEWSARTS, NEWSCTL, NEWSBIN, etc. are non-standard.
+  * this may be due to legitimate testing, but we can't tell.
+  * the error message will at least be seen by a human trying to
+  * track down a problem, even if stderr isn't normally seen.
+  */
  void
! unprivileged(reason)
! char *reason;
  {
  	userealids = YES;
+ 	(void) fprintf(stderr,
+ 		"%s: warning: renouncing setuid due to nonstandard `%s'\n",
+ 							progname, reason);
  }
  

*** cnpatch/tmp.file	Thu Sep 14 15:54:47 1989
--- relay/sh/anne.jones	Fri Sep  8 13:20:58 1989
***************
*** 110,115 ****
  	# strip invisible chars, a la B news; turn tabs to spaces (RFC1036)
  	case "$trversion" in
! 	v7)	tr -d '\1-\7\13\14\16-\37';;
! 	v6)	tr -d '[\1-\7]\13\14[\16-\37]' ;;
  	esac |
  	sed 's/:	/: /' |
--- 110,115 ----
  	# strip invisible chars, a la B news; turn tabs to spaces (RFC1036)
  	case "$trversion" in
! 	v7)	tr -d  '\1-\7\13\15-\37' ;;
! 	v6)	tr -d '[\1-\7]\13[\15-\37]' ;;
  	esac |
  	sed 's/:	/: /' |

*** cnpatch/tmp.file	Thu Sep 14 15:54:48 1989
--- relay/sh/ctlrun	Tue Aug 29 12:16:21 1989
***************
*** 14,23 ****
  for file in *
  do
! 	grep '^Control:' $file |
! 		sed 's;^Control:[	 ]*;'$NEWSBIN/ctl/';' |
! 		grep -v '/cancel ' >/tmp/ctl$$	# cancel needs dbm(3)
  	sh -x /tmp/ctl$$ <$file
  done
  
! rm -f /tmp/ctl$$	
! rm -f LOCK
--- 14,23 ----
  for file in *
  do
! 	# cancel, ihave and sendme are internal to relaynews and use < > metachars.
! 	canonhdr $file | 
! 		sed -n 's;^Control:[	 ]*;'$NEWSBIN/ctl/';p' |
! 		egrep -v '/(cancel|ihave|sendme) ' >/tmp/ctl$$
  	sh -x /tmp/ctl$$ <$file
  done
  
! rm -f /tmp/ctl$$ LOCK

*** cnpatch/tmp.file	Thu Sep 14 15:54:49 1989
--- relay/sh/inews	Fri Sep  8 13:20:58 1989
***************
*** 39,43 ****
  -p)
  	shift
! 	exec rnews $*		# rnews, bailing out at or near line 1
  	;;
  esac
--- 39,44 ----
  -p)
  	shift
! 	cat $* | relaynews -r	# feed directly to relaynews, seat belts off
! 	exit
  	;;
  esac
***************
*** 159,162 ****
--- 160,167 ----
  trap "$cleanup" 0
  tear /tmp/in$$ <$input		# output in $inhdrs and $inbody
+ # canonicalise header keyword capitalisation.
+ # greps for Control: and Approved: later assume this, as does defhdrs.awk.
+ canonhdr <$inhdrs >/tmp/in$$realtmp
+ mv /tmp/in$$realtmp $inhdrs
  # pad zero-line articles, since old B [ir]news are confused by them
  # and the news readers generate zero-line control messages, alas.
***************
*** 184,191 ****
  # echo Lines: $lines
  
!  # strip invisible chars from body, a la B news
   case "$trversion" in
!  v7)	tr -d '\1-\7\13\14\16-\37' ;;
!  v6)	tr -d '[\1-\7]\13\14[\16-\37]' ;;
   esac <$inbody
  
--- 189,196 ----
  # echo Lines: $lines
  
!  # strip invisible chars from body, a la B news.  bells and escapes are right out.
   case "$trversion" in
!  v7)	tr -d  '\1-\7\13\15-\37' ;;
!  v6)	tr -d '[\1-\7]\13[\15-\37]' ;;
   esac <$inbody
  
***************
*** 234,265 ****
  		m)
  			if grep -s '^Approved:[	 ]' $inhdrs >/dev/null; then
! 				rm -f $modroute		# just post normally
  			else
! 				# un-Approved: mail it to the moderator(s).
! 				echo "%s" >$modroute	# in case no route
! 				# look for route for this group
! 				cat $NEWSCTL/mailpaths |
! 					while read ngpat route junk
! 					do
! 						# a dreadful B 2.11 hack:
! 						# backbone == all
! 						case "$ngpat" in
! 						backbone) ngpat="all" ;;
! 						esac
! 						if gngp -a "$ngpat" $nglist >/dev/null; then
! 							echo "$route" >$modroute
! 							break	# take only 1st match
! 						fi
! 					done
! 			fi
! 			# ngpat and route are not set here, damn it!
! 			if test -s $modroute; then
! 				# an unapproved article in a mod group:
! 				# mail the article to this moderator.
! 				moderator=`
! 				 sed "s/%s/\` echo $ng | tr . - \`/" $modroute
! 				`
  				echo "$0: mailing your article to $moderator" >&2
! 				mail $moderator <$censart
  				trap 0	# this is a child process - no cleanup here
  				echo 0 >$exitflag
--- 239,257 ----
  		m)
  			if grep -s '^Approved:[	 ]' $inhdrs >/dev/null; then
! 				:		# just post normally
  			else
! 				# un-Approved article: mail it to the moderator(s).
! 				# look for a route for this group.
! 				# a dreadful B 2.11 hack: backbone == all
! 				(sed 's/^backbone[	 ]/all /' \
! 							$NEWSCTL/mailpaths |
! 						gngp -a -r "`cat $nglist`";
! 						echo 'default	%s') |
! 					sed -n "1{s/^[^	 ]*[	 ][	 ]*//
! 						s/%s/` echo $ng | tr . - `/p;q;}" \
! 						>$modroute
! 				moderator="`cat $modroute `"
  				echo "$0: mailing your article to $moderator" >&2
! 				mail "$moderator" <$censart
  				trap 0	# this is a child process - no cleanup here
  				echo 0 >$exitflag
***************
*** 335,341 ****
  	;;
  *)
! 	status=`rsh $server \
! "PATH=$PATH relaynews $relayopts -s $exclusion -d \"$debug\"; echo status $?" \
! 		<$censart >$outfile; sed -n '/^status /s///p' $outfile `
  	sed '/^status /d' $outfile	# print relaynews's stdout
  	;;
--- 327,342 ----
  	;;
  *)
! 	# send article+commands to remote shell, including clean up
! 	(echo "sed 's/^-//' >/tmp/irsh\$\$ <<'!'"	# remove guard
! 	 sed 's/^[^A-EG-Za-z0-9]/-&/' $censart	# prepend guard
! 	 echo !
! 	 cat <<!
! PATH=$PATH relaynews $relayopts -s $exclusion -d "$debug" </tmp/irsh\$\$
! !
! 	 echo 'echo status $?'
! 	 echo 'rm -f /tmp/irsh$$') |
! 		rsh $server /bin/sh >$outfile
! 
! 	status=`sed -n '/^status /s///p' $outfile `
  	sed '/^status /d' $outfile	# print relaynews's stdout
  	;;

Files that are new:

new batch/viarsh (patch can't create, so diff against null):
Index: batch/viarsh
*** cnpatch/tmp.preposterously.long.name.to.make.patch.behave.right	Thu Sep 14 15:51:06 1989
--- batch/viarsh	Sat Aug 26 22:48:03 1989
***************
*** 0 ****
--- 1,5 ----
+ #! /bin/sh
+ # Transmit batch to $1 by rshing rnews.  This does assume that rnews is
+ # in a user-searched directory.
+ 
+ exec rsh $1 rnews

new expire/recovact (patch can't create, so diff against null):
Index: expire/recovact
*** cnpatch/tmp.preposterously.long.name.to.make.patch.behave.right	Thu Sep 14 15:51:06 1989
--- expire/recovact	Thu Sep 14 15:50:17 1989
***************
*** 0 ****
--- 1,62 ----
+ #! /bin/sh
+ # Try to recover 2nd field of active file.
+ 
+ # =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
+ . ${NEWSCONFIG-/usr/lib/news/bin/config}
+ 
+ PATH=$NEWSCTL/bin:$NEWSBIN/expire:$NEWSBIN:$NEWSPATH ; export PATH
+ umask $NEWSUMASK
+ 
+ cd $NEWSCTL || { echo "$0: can't cd to $NEWSCTL" >&2; exit 1; }
+ 
+ # check active file format
+ set ""`sed 1q active`
+ case $# in
+ 4)	;;
+ *)	echo "$0: active file has other than 4 fields" >&2
+ 	exit 1 ;;
+ esac
+ 
+ # lock news system
+ lock="$NEWSCTL/LOCK"
+ ltemp="$NEWSCTL/L.$$"
+ echo $$ >$ltemp
+ trap "rm -f $ltemp ; exit 0" 0 1 2 15
+ while true
+ do
+ 	if newslock $ltemp $lock
+ 	then
+ 		trap "rm -f $ltemp $lock ; exit 0" 0 1 2 15
+ 		break
+ 	fi
+ 	sleep 30
+ done
+ 
+ while read group max min fourth
+ do
+ 	dir=`echo $group | tr . / `	# map ng name to directory name
+ 	new=
+ 	if test -d $NEWSARTS/$dir
+ 	then
+ 		new=`ls $NEWSARTS/$dir | egrep '^[0-9]+$' | sort -n | tail -1`
+ 	fi
+ 	case "$new" in
+ 	"")	new=$max	;;	# no files -- preserve old value
+ 	*)	if test "$new" -lt "$max"	# old value more recent (!)
+ 		then
+ 			new="$max"
+ 		fi
+ 		;;
+ 	esac
+ 	dots="`echo $max | tr 0123456789 ..........`"
+ 	max="`expr 0000000000$new : '.*\('$dots'\)$'`"	# preserve length
+ 
+ 	echo $group $max $min $fourth
+ done <active >active.new
+ 
+ # replace active, carefully
+ rm -f active.old
+ ln active active.old
+ mv active.new active
+ 
+ exit 0

new misc/canonhdr.c (patch can't create, so diff against null):
Index: misc/canonhdr.c
*** cnpatch/tmp.preposterously.long.name.to.make.patch.behave.right	Thu Sep 14 15:51:06 1989
--- misc/canonhdr.c	Tue Aug 29 12:18:06 1989
***************
*** 0 ****
--- 1,93 ----
+ /*
+  * canonhdr - canonicalise capitalisation of header keywords
+  */
+ 
+ #include <stdio.h>
+ #include <ctype.h>
+ #include <sys/types.h>
+ #include "news.h"
+ #include "libc.h"
+ 
+ char *progname;
+ int debug;
+ 
+ /*
+  * main - parse arguments and handle options
+  */
+ main(argc, argv)
+ int argc;
+ char *argv[];
+ {
+ 	int c;
+ 	int errflg = 0;
+ 	extern int optind;
+ 	extern char *optarg;
+ 	extern FILE *efopen();
+ 
+ 	progname = argv[0];
+ 	while ((c = getopt(argc, argv, "d")) != EOF)
+ 		switch (c) {
+ 		case 'd':
+ 			++debug;
+ 			break;
+ 		default:
+ 			errflg++;
+ 			break;
+ 		}
+ 	if (errflg) {
+ 		(void) fprintf(stderr, "usage: %s [-d] [file]...\n", progname);
+ 		exit(2);
+ 	}
+ 
+ 	if (optind >= argc)
+ 		process(stdin, "stdin");
+ 	else
+ 		for (; optind < argc; optind++)
+ 			if (STREQ(argv[optind], "-"))
+ 				process(stdin, "-");
+ 			else {
+ 				FILE *in = efopen(argv[optind], "r");
+ 
+ 				process(in, argv[optind]);
+ 				(void) fclose(in);
+ 			}
+ 	exit(0);
+ }
+ 
+ /*
+  * process - process input file
+  */
+ process(in, inname)
+ FILE *in;
+ char *inname;
+ {
+ 	register char *hdr;
+ 	int ishdr = YES;
+ 	long nolimit = -1;
+ 	static int washdr = YES;
+ 
+ 	if (!washdr)
+ 		return;
+ 	while ((hdr = gethdr(in, &nolimit, &ishdr)) != NULL && ishdr) {
+ 		register char *cp;
+ 		static char canonmsgid[] = "Message-Id:";
+ 		static char magicmsgid[] = "Message-ID:";
+ 
+ 		/* capitalise initial letter of each word, Message-ID: special */
+ 		for (cp = hdr; *cp != ':' && *cp != '\0'; cp++)
+ 			if (cp == hdr || cp[-1] == '-') {
+ 				if (isascii(*cp) && islower(*cp))
+ 					*cp = toupper(*cp);
+ 			} else
+ 				if (isascii(*cp) && isupper(*cp))
+ 					*cp = tolower(*cp);
+ 		if (STREQN(hdr, canonmsgid, sizeof canonmsgid - 1))
+ 			(void) strncpy(hdr, magicmsgid, sizeof magicmsgid - 1);
+ 		(void) fputs(hdr, stdout);
+ 		/* must not free hdr; gethdr will do so automatically */
+ 	}
+ 	if (hdr != NULL)
+ 		free(hdr);
+ 	if (!ishdr)
+ 		washdr = NO;
+ }


end of patch 14-Sep-1989
-- 
V7 /bin/mail source: 554 lines.|     Henry Spencer at U of Toronto Zoology
1989 X.400 specs: 2200+ pages. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu