[news.software.b] fixes to c-news to speed up rn

Dan@dna.lth.se (Dan Oscarsson) (08/04/89)

One of the irretating things in rn is the slow handling of the = command
to show the subject lines in all articles. Also killing is slow.

The following patch file fixes this. rn is getting really good to use.
The speedup is done by creating an index file in each spool directory
(I thing news 3.0 have something like it).

The fixes below includes patches to rn to make it use the index files,
but other news readers can probably also use the index files to be faster.

Unpack the shell archive in a separate directory and read the README file.

   rn is good to use now.

   Dan Oscarsson

-------------------------------------------------------------
#!/bin/sh
# This is a shell archive.  Remove anything before the "#!/bin/sh" line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
#	./README
#	./domakeindex
#	./expire.diff
#	./include.diff
#	./libcnews.diff
#	./makeindex.c
#	./pack_name.c
#	./relay.diff
#	./rn.diff
#	./update_index.c
#
if `test ! -s ./README`
then
echo "writing ./README"
cat > ./README << '\End\of\File\'

        Index files for articles under C-news
------------------------------------------------------
The patches and code here creates and maintains an INDEX file in
each newsgroup spooling directory. The index file contains enought
information to speed up news readers a lot when showing subject lines
of articles. Included is a patch to rn that makes it a lot faster.

The format of the INDEX file is an ascii file with one line per article.
Each line is composed of fields separated by tabs.
The current fields are from the beginning of a line:
- article number
- number of re in subject
- article subject without leading re: and trailng "- (nf)"
- date in time_t format of posted date
- condenced sender name

All "Re: ", "RE: ", "re: " and Re^number is removed from the subject and
included in the re count.

The sender name is condenced to a maximum of 16 characters a la nn-news reader.

-------------
Something like this is implemented in news 3.0


INSTALLATION
------------
Use patch to patch files in the C-news source directories.
 - expire.diff  -> patches for directory expire
 - libcnews.diff-> patches for directory libcnews
 - include.diff -> patches for directory include
 - relay.diff   -> patches for directory relay

copy the file:
 - domakeindex - to expire/domakeindex - make it executable
 - makeindex.c - to expire/makeindex.c
 - pack_name.c - to libcnews/pack_name.c
 - update_index.c - to relay/update_index.c

do make in libcnews, ranlib and install new version of library.

do make makeindex in expire, install makeindex, patched version of doexpire
and domakeindex in $NEWSBIN/expire.

do make in relay, install new version of relaynews.

if you immediately want the INDEX files to me up to date, run domakeindex
as user news. Otherwise the INDEX files will be brought up to date after
the next doexpire is run.

--
patch the source of rn with rn.diff and recompile.
The patched rn will run much faster when showing subject lines if the
INDEX files exist. If the do not exist or are out of date, rn will work
anyway but slower, just like before.

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

The file pack_name.c is from the nn-news reader and
Copyright (c) 1989 by Kim Fabricius Storm.

The rest are my own or modified C-news files.

Please send bugs and comments to:

    Dan@dna.lth.se

Dan Oscarsson
Dept. of Computer Science
Lund Institute of Technology
Lund, Sweden
\End\of\File\
else
  echo "will not over write ./README"
fi
if [ `wc -c ./README | awk '{printf $1}'` -ne 2418 ]
then
echo `wc -c ./README | awk '{print "Got " $1 ", Expected " 2418}'`
fi
if `test ! -s ./domakeindex`
then
echo "writing ./domakeindex"
cat > ./domakeindex << '\End\of\File\'
#! /bin/sh
# Update index files in all spool directories
# called from doexpire

# =()<. ${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; }

while read group max min fourth
do
	dir=`echo $group | tr . / `	# map ng name to directory name
	if test -d $NEWSARTS/$dir
	then
		if cd $NEWSARTS/$dir; then
			makeindex 2>/tmp/mkindex$$
			if test -s /tmp/mkindex$$; then
				export dir
				(echo 'makeindex problems:' "$dir" ; cat /tmp/mkindex$$ ) | mail "$NEWSMASTER"
				rm -f /tmp/mkindex$$
			fi
		fi
	fi
done < active
rm -f /tmp/mkindex$$
exit 0
\End\of\File\
else
  echo "will not over write ./domakeindex"
fi
if [ `wc -c ./domakeindex | awk '{printf $1}'` -ne 717 ]
then
echo `wc -c ./domakeindex | awk '{print "Got " $1 ", Expected " 717}'`
fi
if `test ! -s ./expire.diff`
then
echo "writing ./expire.diff"
cat > ./expire.diff << '\End\of\File\'
*** Makefile.org	Fri Aug  4 10:37:39 1989
--- Makefile	Fri Aug  4 10:41:37 1989
***************
*** 7,16 ****
  DBM = -ldbm
  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 lowest.c mkdbm.c mkhistory pgood superkludge tgood upact \
! 	mkadir
  # =()<NEWSARTS = @<NEWSARTS>@>()=
  NEWSARTS = /usr/spool/news
  # =()<NEWSBIN = @<NEWSBIN>@>()=
--- 7,16 ----
  DBM = -ldbm
  LIBS= ../libcnews.a
  THEM = expire histdups histinfo histslash mkdbm mkhistory \
! 	superkludge upact doexpire mkadir makeindex domakeindex
  DTR = README Makefile dircheck doexpire expire.c histdups histinfo.c \
  	histslash.c lowest.c mkdbm.c mkhistory pgood superkludge tgood upact \
! 	mkadir domakeindex
  # =()<NEWSARTS = @<NEWSARTS>@>()=
  NEWSARTS = /usr/spool/news
  # =()<NEWSBIN = @<NEWSBIN>@>()=
***************
*** 35,40 ****
--- 35,43 ----
  
  expire: expire.o $(LIBS)
  	$(CC) $(LDFLAGS) expire.o $(PRE) $(LIBS) $(DBM) $(POST) -o $@
+ 
+ makeindex: makeindex.o $(LIBS)
+ 	$(CC) $(LDFLAGS) makeindex.o $(PRE) $(LIBS) $(DBM) $(POST) -o $@
  
  histinfo: histinfo.o $(LIBS)
  	$(CC) $(LDFLAGS) histinfo.o $(PRE) $(LIBS) $(POST) -o $@
*** doexpire.org	Fri Jun 30 15:14:30 1989
--- doexpire	Sun Jul  2 12:27:21 1989
***************
*** 49,54 ****
--- 49,57 ----
  done
  
  expire $* $NEWSCTL/explist 2>/tmp/doex$$
+ 
+ domakeindex
+ 
  if test -s /tmp/doex$$
  then
  	(echo 'expire problems:' ; cat /tmp/doex$$ ) | mail "$NEWSMASTER"
\End\of\File\
else
  echo "will not over write ./expire.diff"
fi
if [ `wc -c ./expire.diff | awk '{printf $1}'` -ne 1579 ]
then
echo `wc -c ./expire.diff | awk '{print "Got " $1 ", Expected " 1579}'`
fi
if `test ! -s ./include.diff`
then
echo "writing ./include.diff"
cat > ./include.diff << '\End\of\File\'
*** news.h.org	Fri Jun 30 13:13:36 1989
--- news.h	Fri Jun 30 13:15:41 1989
***************
*** 14,19 ****
--- 14,21 ----
  #define MAXHOST 128		/* max. length of this host's name */
  #define SPOOLTMP ".tmpXXXXXX"	/* template for NEWSARTS temporary link */
  
+ #define ARTICLE_INDEX		/* generate article index files */
+ #define INDEX_FROM_LENGTH 16
  
  /* STATIC & FORWARD must agree to avoid redeclarations(!) */
  #define STATIC	static		/* "static" when not debugging|profiling */
\End\of\File\
else
  echo "will not over write ./include.diff"
fi
if [ `wc -c ./include.diff | awk '{printf $1}'` -ne 487 ]
then
echo `wc -c ./include.diff | awk '{print "Got " $1 ", Expected " 487}'`
fi
if `test ! -s ./libcnews.diff`
then
echo "writing ./libcnews.diff"
cat > ./libcnews.diff << '\End\of\File\'
*** Makefile.org	Fri Jun 30 13:07:31 1989
--- Makefile	Fri Jun 30 13:08:06 1989
***************
*** 10,18 ****
  #RANLIB=:
  SRCS=complain.c config.c fopenclex.c hostname.c \
   lock.c ltoza.c ngmatch.c readline.c \
!  string.c strlower.c strsave.c str3save.c time.c
  OBJS = complain.o config.o fopenclex.o gethdr.o hostname.o lock.o ltoza.o \
! 	nemalloc.o ngmatch.o str3save.o string.o strlower.o strsave.o time.o
  # workaround for System V make bug
  SHELL = /bin/sh
  
--- 10,19 ----
  #RANLIB=:
  SRCS=complain.c config.c fopenclex.c hostname.c \
   lock.c ltoza.c ngmatch.c readline.c \
!  string.c strlower.c strsave.c str3save.c time.c pack_name.c
  OBJS = complain.o config.o fopenclex.o gethdr.o hostname.o lock.o ltoza.o \
! 	nemalloc.o ngmatch.o str3save.o string.o strlower.o strsave.o time.o \
! 	pack_name.o
  # workaround for System V make bug
  SHELL = /bin/sh
  
\End\of\File\
else
  echo "will not over write ./libcnews.diff"
fi
if [ `wc -c ./libcnews.diff | awk '{printf $1}'` -ne 883 ]
then
echo `wc -c ./libcnews.diff | awk '{print "Got " $1 ", Expected " 883}'`
fi
if `test ! -s ./makeindex.c`
then
echo "writing ./makeindex.c"
cat > ./makeindex.c << '\End\of\File\'
/*
 * makeindex - make index file for a news article directory
 *
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <sys/stat.h>
#include <dirent.h>
#include "libc.h"
#include "news.h"
#include "config.h"
#include "fgetmfs.h"

#define HUGE    999999999L      /* Bigger than any valid article number. */

#ifndef EPOCH
#define	EPOCH	((time_t)0)
#endif


/* structure for article index lines */
struct article {
	struct article *next;
	long            no;
	char *		line;
	int		re;
	char *		subject;
	time_t		date;
	char *		from;
};

struct article *inlist = NULL;
struct article *lastin = NULL;
struct article *outlist = NULL;

int new_articles = 0;


char subject[256] = "";		/* Subject line*/

/* Buffer etc. for readline and friends. */
char rlbuf[BUFSIZ];
int rlnleft = 0;
char *rest;
int nlocked = 0;		/* has readline() locked the news system? */

/*
 * Archive-copying buffer.
 * 8KB buffer is large enough to take most articles at one gulp,
 * and also large enough for virtual certainty of getting the
 * Subject: line in the first bufferload.
 */
#ifdef SMALLMEM
char abuf[2*1024];		/* expire reported to be tight on 11 */
#else
char abuf[8*1024];
#endif

char *progname;

struct timeb ftnow;

extern int errno;
extern long atol();
extern char *malloc();
extern time_t time();

extern time_t getdate();

extern pack_name();

/* forwards */
FILE *eufopen();
void eufclose();
void fail();
void doit();
doline();
time_t readdate();
void warning();
char *readline();
getheaders();
void refill();
void fillin();

struct article * make_article();
to_inlist();
struct article * get_art();
sort_in();

/*
 - main - parse arguments and handle options
 */
main(argc, argv)
int argc;
char *argv[];
{
	progname = argv[0];
	ftime(&ftnow);

	(void) umask(newsumask());
	doit();			/* side effect: newslock() */
	newsunlock();

	exit(0);
}

/*
 - doit - file manipulation and master control
 */
void
doit()
{
	register int old;
	register FILE *new;
	char *line;
	DIR *d;
	register struct dirent *dp;
	struct article * art;
	long no;

	old = open("INDEX", 0);
	if (old >= 0) {
		while ((line = readline(old)) != NULL)
			doline(line);
		(void) close(old);
	} else {
		newslock();
		nlocked = 1;
	}
	/* side effect of readline() == NULL:  newslock() */

	d = opendir(".");
	if (d == NULL)
               fail("can't read directory %s\n", ".");
	while ((dp = readdir(d)) != NULL) {
		if (strspn(dp->d_name, "0123456789") == strlen(dp->d_name)) {
			no = atol(dp->d_name);
			if ((art = get_art(no)) == NULL) 
				art = make_article(no,dp->d_name);
			sort_in(art);
		}
	}
	closedir(d);

	if (new_articles || inlist != NULL) {
		(void) unlink("INDEX.new");
		new = eufopen("INDEX.new", "w");
		if (new < 0)
			fail("can't create new INDEX file","");
		for (art = outlist; art != NULL; art = art->next) {
			if (art->line != NULL)
				fprintf(new,"%s\n",art->line);
			else {
				fprintf(new,"%ld\t%d\t%s\t%ld",art->no,art->re,art->subject,art->date);
				if (art->from != NULL)
					fprintf(new,"\t%s",art->from);
				fprintf(new,"\n");
			}
		}
		eufclose(new, "INDEX.new");
		if (rename("INDEX.new", "INDEX") < 0)
			fail("can't rename INDEX.new to INDEX","");
	}
}

/*
 - doline - handle one history line, modifying it if appropriate
 */
doline(line)
	char *line;			/* malloced; freed here */
{
	register int k;
	long no;
	struct article * art;
	char * tab;

	/* pull the incoming line apart */
	tab = strchr(line,'\t');
	if (tab != NULL)
		*tab = NULL;

	if (read_no(&no,line)) {
		art = (struct article *) malloc(sizeof(struct article));
		if (art == NULL)
			fail("can't malloc a new article","");
		if (tab != NULL)
			*tab = '\t';
		art->no = no;
		art->line = line;
		art->next = NULL;
		art->re = 0;
		art->subject = NULL;
		art->date = EPOCH;
		art->from = NULL;
		to_inlist(art);
	} else {
		if (tab != NULL)
			*tab = '\t';
		warning("bad article number in `%.40s...'", line);
		free(line);
	}
}

/*
 - read_no 
*/
read_no(no,text)
	long * no;
	char * text;
{
	if (strspn(text, "0123456789") == strlen(text)) {
		*no = atol(text);
		return 1;
	}
	*no = 0;
	return 0;
}

/*
 - make_article - 
 */
struct article * make_article(no,name)
	long no;
	char * name;
{
	register int ret;
	register int count;
	register int in;
	register struct article * art;
	int re;
	time_t date;
	char * from;

	in = open(name, 0);
	if (in < 0)
		return(NULL);

	if ((count = read(in, abuf, sizeof(abuf))) > 0) {
		getheaders(abuf, count, &re,&date,&from);
	}
	if (count < 0)
		return(NULL);

	(void) close(in);
	art = (struct article *) malloc(sizeof(struct article));
	art->next = NULL;
	art->no = no;
	art->line = NULL;
	art->re = re;
	art->subject = strsave(subject);
	art->date = date;
	art->from = from;
	new_articles = 1;
	return art;
}

/*
 - getheaders - try to find the headers wanted in a buffer
 *
 * Result of subject goes in "subject", and is never empty.  Tabs become spaces,
 * since they are the output delimiters.
 */
getheaders(buf, bsize,r_re,r_date,r_from)
char *buf;
int bsize;
int * r_re;
time_t * r_date;
char ** r_from;
{
	register char *scan;
	register char *limit;
	register char *flimit;
	register int len;
	int slen,flen,dlen;
	register int clipped;
	static char sline[] = "Subject:";
	static char fline[] = "From:";
	static char dline[] = "Date:";
	int re;
	time_t date;
	char * from;
	char savechr;
	int subject_found = 0;
	char to_name[INDEX_FROM_LENGTH+1];

	re = 0;
	date = EPOCH;
	from = NULL;
	slen = strlen(sline);
	flen = strlen(fline);
	dlen = strlen(dline);
	flimit = buf + bsize - slen;
	for (scan = buf; scan < flimit; scan++)
		if (STREQN(scan, sline, slen) &&
				(scan == buf || *(scan-1) == '\n')) {
			scan += slen;
			for (limit = scan; limit < buf+bsize; limit++)
				if (*limit == '\n')
					break;
			while (scan < limit && isspace(*scan))
				scan++;
			/* remove and count leading re: and remove trailing '- (nf)' */
			while (scan < limit && (STREQN(scan,"Re: ",4) ||
						STREQN(scan,"re: ",4) ||
						STREQN(scan,"RE: ",4) ||
						STREQN(scan,"Re^",3))) {
				if (STREQN(scan,"Re^",3)) {
					scan += 3;
					re += atol(scan);
					while (scan < limit && isdigit(*scan))
						scan++;
					if (*scan == ':') scan++;
					if (*scan == ' ') scan++;
				} else {
					scan += 4;
					re++;
				}
			}
			while (scan < limit && isspace(*scan))
				scan++;
			if (limit-scan > 5 && STREQN(limit-5,"- (nf)",5)) 
				limit -= 5;
			len = limit-scan;
			clipped = 0;
			if (len > sizeof(subject)-1) {
				len = sizeof(subject) - 1 - strlen("...");
				clipped = 1;
			}
			if (len > 0) {
				(void) strncpy(subject, scan, len);
				subject[len] = '\0';
			} else
				(void) strcpy(subject, "???");
			if (clipped)
				(void) strcat(subject, "...");
			for (scan = strchr(subject, '\t'); scan != NULL;
					scan = strchr(scan+1, '\t'))
				*scan = ' ';
			subject_found = 1;
			scan = limit;
		} else if (STREQN(scan, dline, dlen) &&
				(scan == buf || *(scan-1) == '\n')) {
			scan += dlen;
			for (limit = scan; limit < buf+bsize; limit++)
				if (*limit == '\n')
					break;
			while (scan < limit && isspace(*scan))
				scan++;
			savechr = *limit;
			*limit = NULL;
			date = getdate(scan,&ftnow);
			if (date == -1)
				date = EPOCH;
			*limit = savechr;
			scan = limit;
		} else if (STREQN(scan, fline, flen) &&
				(scan == buf || *(scan-1) == '\n')) {
			scan += flen;
			for (limit = scan; limit < buf+bsize; limit++)
				if (*limit == '\n')
					break;
			while (scan < limit && isspace(*scan))
				scan++;
			savechr = *limit;
			*limit = NULL;
			pack_name(to_name,scan,INDEX_FROM_LENGTH);
			*limit = savechr;
			scan = limit;
		} else if (*scan == '\n' && scan+1 < flimit && *(scan+1) == '\n')
			break;		/* empty line terminates header */

	/* didn't find one -- fill in *something* */
	if (!subject_found)
		(void) strcpy(subject, "???");
	*r_re = re;
	*r_date = date;
	*r_from = strsave(to_name);
}

/*
 - eufopen - fopen, with fail if doesn't succeed
 */
FILE *
eufopen(name, mode)
char *name;
char *mode;
{
	FILE *f;
	static char grump[50] = "can't open `%s' for `";

	f = fopen(name, mode);
	if (f == NULL) {
		(void) strcat(grump, mode);
		(void) strcat(grump, "'");
		fail(grump, name);
	}
	return(f);
}

/*
 - eufclose - fclose with failure checking
 */
void
eufclose(f, name)
FILE *f;
char *name;
{
	if (nfclose(f) == EOF)
		fail("error in closing file `%s'", name);
}

/*
 - fail - call errunlock, possibly after cleanup
 */
void
fail(s1, s2)
char *s1;
char *s2;
{
	errunlock(s1, s2);
	/* NOTREACHED */
}

void
unprivileged() {}

/*
 - readline - read history line (sans newline), with locking when we hit EOF
 *
 * Minor flaw:  will lose a last line which lacks a newline.
 */
char *				/* NULL is EOF */
readline(fd)
int fd;				/* Note descriptor, not FILE *. */
{
	register char *line;
	register int nline;
	register char *p;
	register int c;
	register int n;
	extern void refill();

	nline = 100;		/* reasonable starter */
	line = malloc(nline);
	if (line == NULL)
		fail("out of space when reading INDEX", "");
	p = line;

	for (;;) {
		if (rlnleft <= 0) {
			refill(fd);
			if (rlnleft <= 0)	/* refill gave up. */
				return(NULL);
		}
		c = *rest++;
		rlnleft--;

		if (c == '\n') {
			*p++ = '\0';
			return(line);
		}
		if (p - line >= nline - 1) {
			nline = (nline * 3) / 2;
			n = p - line;
			line = realloc(line, nline);
			if (line == NULL)
				fail("out of memory in readline", "");
			p = line + n;
		}
		*p++ = c;
	}
	/* NOTREACHED */
}

/*
 - refill - refill readline's buffer, with locking on EOF
 */
void
refill(fd)
int fd;
{
	register int ret;

	/* Just in case... */
	if (rlnleft > 0)
		return;

	/* Try ordinary read. */
	ret = read(fd, rlbuf, (int)sizeof(rlbuf));
	if (ret < 0)
		fail("read error in INDEX", "");
	if (ret > 0) {
		rlnleft = ret;
		rest = rlbuf;
		return;
	}

	/* EOF. */
	if (nlocked)
		return;		/* We're really done. */

	/* EOF but we haven't locked yet.  Lock and try again. */
	(void) signal(SIGINT, (sigarg_t)SIG_IGN);
	(void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
	(void) signal(SIGHUP, (sigarg_t)SIG_IGN);
	(void) signal(SIGTERM, (sigarg_t)SIG_IGN);
	newslock();
	nlocked = 1;
	refill(fd);
}

/*
 - to_inlist
*/
to_inlist(art)
	struct article * art;
{
	if (art != NULL) {
		if (lastin == NULL) 
			inlist = art;
		else
			lastin->next = art;
		lastin = art;
		art->next = NULL;
	}
}

/*
 - get_art - get art from inlist and remove it of the list
*/
struct article * get_art(no)
	long no;
{
	struct article * p;
	struct article * prv;

	prv = NULL;
	for (p = inlist; p != NULL && p->no != no; prv = p,p = p->next) {}
	if (p != NULL) {
		if (prv == NULL)
			inlist = p->next;
		else
			prv->next = p->next;
		p->next = NULL;
	}
	return p;
}

/*
 - sort_in  sort in artcile in outlist
*/
sort_in(art)
	struct article * art;
{
	struct article * p;
	struct article * prv;

	prv = NULL;
	for (p = outlist; p != NULL && p->no < art->no; prv = p,p= p->next) ;
	if (p == NULL) {
		if (prv == NULL) {
			outlist = art;
			art->next = NULL;
		} else {
			art->next = prv->next;
			prv->next = art;
		}
	} else {
		if (prv == NULL) {
			art->next = outlist;
			outlist = art;
		} else {
			art->next = prv->next;
			prv->next = art;
		}
	}
}

\End\of\File\
else
  echo "will not over write ./makeindex.c"
fi
if [ `wc -c ./makeindex.c | awk '{printf $1}'` -ne 11259 ]
then
echo `wc -c ./makeindex.c | awk '{print "Got " $1 ", Expected " 11259}'`
fi
if `test ! -s ./pack_name.c`
then
echo "writing ./pack_name.c"
cat > ./pack_name.c << '\End\of\File\'
/*
 * 	pack_name(packed, name, length)
 *	pack sender's name into something sensible, return in packed
 *
 */

#define NULL 0
#define SP ' '
#define TAB '\t'

#define	SEP_DOT		0	/* . */
#define	SEP_PERCENT	1	/* % */
#define	SEP_SCORE	2	/* _ */
#define	SEP_AMPERSAND	3	/* @ */
#define	SEP_BANG	4	/* ! */
#define	SEP_MAXIMUM	5


#define CL_OK		0x0100		/* letter or digit */
#define CL_SPACE	0x0200		/* cvt to space */
#define CL_IGNORE	0x0400		/* ignore */
#define CL_RANGE(c)	0x0800+c	/* space range, end with c */
#define CL_HYPHEN	0x1000		/* convert to - */
#define CL_STOP		0x2000		/* discard rest of name */
#define	CL_SEP		| 0x4000 +    	/* address separator */

#define	IS_OK(c)	(Class[c] & CL_OK)
#define IS_SPACE(c)	(Class[c] & CL_SPACE)
#define IGNORE(c)	(c & 0x80 || Class[c] & CL_IGNORE)
#define BEGIN_RANGE(c)	(Class[c] & CL_RANGE(0))
#define END_RANGE(c)	(Class[c] & 0xff)
#define IS_HYPHEN(c)	(Class[c] & CL_HYPHEN)
#define IS_STOP(c)	(Class[c] & CL_STOP)
#define	IS_SEPARATOR(c)	(Class[c] & (0 CL_SEP 0))
    
static short Class[128] = {
	/* NUL */		CL_STOP ,
	/* SOH */		CL_IGNORE ,
	/* STX */		CL_IGNORE ,
	/* ETX */		CL_IGNORE ,
	/* EOT */		CL_IGNORE ,
	/* ENQ */		CL_IGNORE ,
	/* ACK */		CL_IGNORE ,
	/* BEL */		CL_IGNORE ,
	/* BS  */		CL_IGNORE ,
	/* TAB */		CL_SPACE ,
	/* NL  */		CL_IGNORE ,
	/* VT  */		CL_IGNORE ,
	/* FF  */		CL_IGNORE ,
	/* CR  */		CL_IGNORE ,
	/* SO  */		CL_IGNORE ,
	/* SI  */		CL_IGNORE ,
	/* DLE */		CL_IGNORE ,
	/* DC1 */		CL_IGNORE ,
	/* DC2 */		CL_IGNORE ,
	/* DC3 */		CL_IGNORE ,
	/* DC4 */		CL_IGNORE ,
	/* NAK */		CL_IGNORE ,
	/* SYN */		CL_IGNORE ,
	/* ETB */		CL_IGNORE ,
	/* CAN */		CL_IGNORE ,
	/* EM  */		CL_IGNORE ,
	/* SUB */		CL_IGNORE ,
	/* ESC */		CL_IGNORE ,
	/* FS  */		CL_IGNORE ,
	/* GS  */		CL_IGNORE ,
	/* RS  */		CL_IGNORE ,
	/* US  */		CL_IGNORE ,
	
	/* space */		CL_SPACE ,
	/*   !   */		CL_SPACE CL_SEP SEP_BANG,
	/*   "   */		CL_RANGE( '"' ) ,
	/*   #   */		CL_OK ,
	/*   $   */		CL_OK ,
	/*   %   */		CL_OK CL_SEP SEP_PERCENT,
	/*   &   */		CL_OK ,
	/*   '   */		CL_OK ,
	/*   (   */		CL_RANGE( ')' ) ,
	/*   )   */		CL_IGNORE ,
	/*   *   */		CL_HYPHEN ,
	/*   +   */		CL_HYPHEN ,
	/*   ,   */		CL_STOP ,
	/*   -   */		CL_HYPHEN ,
	/*   .   */		CL_SPACE CL_SEP SEP_DOT,
	/*   /   */		CL_OK ,
	/*   0   */		CL_OK ,
	/*   1   */		CL_OK ,
	/*   2   */		CL_OK ,
	/*   3   */		CL_OK ,
	/*   4   */		CL_OK ,
	/*   5   */		CL_OK ,
	/*   6   */		CL_OK ,
	/*   7   */		CL_OK ,
	/*   8   */		CL_OK ,
	/*   9   */		CL_OK ,
	/*   :   */		CL_IGNORE ,
	/*   ;   */		CL_STOP ,
	/*   <   */		CL_IGNORE ,
	/*   =   */		CL_HYPHEN ,
	/*   >   */		CL_IGNORE ,
	/*   ?   */		CL_IGNORE ,
	/*   @   */		CL_OK CL_SEP SEP_AMPERSAND,
	/*   A   */		CL_OK ,
	/*   B   */		CL_OK ,
	/*   C   */		CL_OK ,
	/*   D   */		CL_OK ,
	/*   E   */		CL_OK ,
	/*   F   */		CL_OK ,
	/*   G   */		CL_OK ,
	/*   H   */		CL_OK ,
	/*   I   */		CL_OK ,
	/*   J   */		CL_OK ,
	/*   K   */		CL_OK ,
	/*   L   */		CL_OK ,
	/*   M   */		CL_OK ,
	/*   N   */		CL_OK ,
	/*   O   */		CL_OK ,
	/*   P   */		CL_OK ,
	/*   Q   */		CL_OK ,
	/*   R   */		CL_OK ,
	/*   S   */		CL_OK ,
	/*   T   */		CL_OK ,
	/*   U   */		CL_OK ,
	/*   V   */		CL_OK ,
	/*   W   */		CL_OK ,
	/*   X   */		CL_OK ,
	/*   Y   */		CL_OK ,
	/*   Z   */		CL_OK ,
	/*   [   */		CL_OK ,
	/*   \   */		CL_OK ,
	/*   ]   */		CL_OK ,
	/*   ^   */		CL_IGNORE ,
	/*   _   */		CL_SPACE CL_SEP SEP_SCORE,
	/*   `   */		CL_IGNORE ,
	/*   a   */		CL_OK ,
	/*   b   */		CL_OK ,
	/*   c   */		CL_OK ,
	/*   d   */		CL_OK ,
	/*   e   */		CL_OK ,
	/*   f   */		CL_OK ,
	/*   g   */		CL_OK ,
	/*   h   */		CL_OK ,
	/*   i   */		CL_OK ,
	/*   j   */		CL_OK ,
	/*   k   */		CL_OK ,
	/*   l   */		CL_OK ,
	/*   m   */		CL_OK ,
	/*   n   */		CL_OK ,
	/*   o   */		CL_OK ,
	/*   p   */		CL_OK ,
	/*   q   */		CL_OK ,
	/*   r   */		CL_OK ,
	/*   s   */		CL_OK ,
	/*   t   */		CL_OK ,
	/*   u   */		CL_OK ,
	/*   v   */		CL_OK ,
	/*   w   */		CL_OK ,
	/*   x   */		CL_OK ,
	/*   y   */		CL_OK ,
	/*   z   */		CL_OK ,
	/*   {   */		CL_OK ,
	/*   |   */		CL_OK ,
	/*   }   */		CL_OK ,
	/*   ~   */		CL_HYPHEN ,
	/*  DEL  */		CL_IGNORE 
} ;


pack_name(dest, source, length)
char *dest, *source;
int length;
{
    register char *p, *q, *r, c;
    register int n;
    char namebuf[129], *name;
    char *maxq;
    int lname, lfirst, lmiddle, llast, sep, i;
    int drop_space, prev_space;
    char *separator[SEP_MAXIMUM];
    
    dest[0] = NULL;
    
    if (source == NULL || source[0] == NULL)
	return 0;

    p = source, q = namebuf, n = 0;
    
new_partition:
    for (i = SEP_MAXIMUM; --i >= 0; separator[i] = NULL);
    
    while ( c = *p++ ) {
	if (c == '<') {
	    while (q > namebuf && q[-1] == SP) q--;
	    if (q == namebuf) continue;
	    break;
	}
	if (IGNORE(c)) continue;
	if (q == namebuf && IS_SPACE(c)) continue;
	if (c == '(') {
	    if (*p == ')') {
		p++;
		continue;
	    }
	    if (n++ == 0) {
		q = namebuf;
		goto new_partition;
	    }
	    continue;
	}
	if (c == ')') {
	    if (--n == 0) break;
	    continue;
	}
	if (n > 1) continue;
	*q++ = c;
	if (IS_SEPARATOR(c)) {
	    switch (sep = (Class[c] & 0xff)) {
		
	     case SEP_DOT:
		if (separator[SEP_AMPERSAND] && q - namebuf <= length)
		    break;
		continue;

	     case SEP_BANG:
		if (separator[SEP_AMPERSAND]) continue;
		break;
		
	     default:
		if (separator[sep]) continue;
		break;
	    }
	    
	    separator[sep] = q - 1;
	}
    }

    *q = NULL;
    
    if (namebuf[0] == NULL) return 0;
    
    name = namebuf;

    if (name[0] == '"') {
	name++;
	if (q[-1] == '"') *--q = NULL;
    }
    
    if (q - name <= length) goto name_ok;
    
    /* sorry for the many goto's -- the 3B2 C compiler does not */
    /* make correct code for complicated logical expressions!!  */
    /* not even without -O					*/

    /* We must pack the name to make it fit */
    
    /* Name_of_person%... -> Name_of_person */

    if (r = separator[SEP_PERCENT]) {
	if (!(q = separator[SEP_SCORE]) || q > r ) 
	    goto no_percent;
	if ((q = separator[SEP_AMPERSAND]) && q < r)
	    goto no_percent;
	if ((q = separator[SEP_BANG]) && q < r)
	    goto no_percent;
	*r = NULL;
	goto parse_name;
    }

 no_percent:

    /* name@site.domain -> name@site */
 
   if (r = separator[SEP_AMPERSAND]) {

       if ((q = separator[SEP_PERCENT]) && q < r) {
	   *r = NULL;
	   if (r - name <= length) goto name_ok;

	   *q = NULL;

	   if (((p = separator[SEP_BANG]) && p < q)
	     || ((p = (char *) strrchr(name, '!')) && p < q)) {
	       name = p + 1;
	   }

	   if (strchr(name, '.')) 
	       goto parse_name;
	   
	   goto name_ok;
       }

       if (q = separator[SEP_DOT]) {
	   *q = NULL;
	   goto name_ok;
       }	
	
       *r = NULL;
       if (r - name <= length) goto name_ok;
       
       if ((q = separator[SEP_BANG]) && q < r) {
	   name = q + 1;
	   goto name_ok;
       }

#ifdef NOTDEF
       if (strchr(name, '!') == NULL) 
	   goto parse_name; /* take the chance ... */
#endif
	goto name_ok;	/* can't do it any better */
    }
    
    
    /* Phase 1: Normalization (remove superfluous characters) */
    
 parse_name:
    
    for (p = name, lname = 0, prev_space = 0; c = *p; p++) {

/*	
	if (IGNORE(c)) {
	    *p = TAB;
	    if (p == name) name++;
	    continue;
	}
*/
	
	if (IS_OK(c)) {
	    lname++;
	    prev_space = 0;
	    continue;
	}
	
	if (IS_HYPHEN(c)) {
	    if (p == name) {
		name++;
		continue;
	    }
	    if (prev_space)
		*p = TAB;
	    else {
		*p = '-';
		lname++;
	    }
	    continue;
	}
	
	if (BEGIN_RANGE(c)) {
	    
	    if (p == name) {
		name++;
		continue;
	    }
	    
	    c = END_RANGE(c);
	    for (q = p+1; *q && *q != c; q++);
	    if (*q) {
		if (p[-1] != ' ') lname++;
		while (p <= q) *p++ = ' ';
		p--;
		prev_space++;
		continue;
	    }
	    c = ' ';
	}
	
	if (IS_SPACE(c)) {
	    *p = ' ';
	    if (p == name) 
		name++;
	    else
		if (!prev_space) {
		    lname++;
		    prev_space++;
		}
	    continue;
	}
	
	if (IS_STOP(c)) {
	    *p = NULL;
	    break;
	}
    }
 drop_last_name:
    while (p > name && (*--p == ' ' || *p == TAB)) *p = NULL;
    
    if (lname < length) goto name_ok;
    
    
    /* Phase 2: Reduce middle names */
    
    for (r = p, llast = 0; r > name && *r != ' '; r--)
	if (*r != TAB) llast++;
    
    /* r points to space before last name */
    
    if (strncmp(r, " Jr", 3) == 0 || strncmp(r, " II", 3) == 0) {
	p = r+1;
	lname -= llast;
	goto drop_last_name;
    }		
    
    if (r == name) goto phase6;	/* only last name */
    
    for (q = name, lfirst = 0; *q && *q != ' '; q++)
	if (*q != TAB) lfirst++;
    
    /* q points at space after first name */
    
    for (p = q, lmiddle = 0; p < r; ) {
	/* find next middle name */
	while (p < r && (*p == ' ' || *p == TAB)) p++;
	
	if (p >= r) break; /* found last name */
	
	p++; /* skip first char of middle name */
	for (;*p != ' '; p++) { /* remove rest */
	    if (*p == TAB) continue;
	    *p = TAB;
	    lname--;
	}
	lmiddle += 2;	/* initial + space */
    }
    
    if (lname < length) goto name_ok;
    
    /* If removing middle names is not enough, but reducing first name instead is, do it that way */
    
    if (lname - lmiddle >= length && lname - lfirst + 1 < length) goto phase4;
    
    
    /* Phase 3: Remove middle names */
    
    for (p = q; p < r; p++) {
	if (*p == TAB) continue;
	if (*p == ' ') continue;
	*p = TAB;
	lname -= 2;
    }
    
    if (lname < length) goto name_ok;
    
    
    /* Phase 4: Reduce first name */
    
 phase4:
    for (p = name+1; p < q; p++) {
	if (*p == TAB) continue;
	if (*p == ' ') continue;
	*p = TAB;
	lname--;
    }
    
    if (lname < length) goto name_ok;
    
    /* Phase 5: Remove first name */
    
    name = r+1;
    lname--;
    
    if (lname < length) goto name_ok;
    
    /* Phase 6: Cut last name */
 phase6:
    goto name_ok;
    
 name_ok:

    q = dest;
    maxq = q + length;

    drop_space = 1;
    
    for (p = name; *p && q < maxq ; p++) {
	if (*p == TAB) continue;
	
	if ( *p == ' ' ) {
	    if (!drop_space) {
		drop_space = 1;
		*q++ = ' ';
	    }
	    continue;
	}
	drop_space = 0;
	*q++ = *p;
    }
    
    *q = NULL;
    
    return strlen(dest);
}    

\End\of\File\
else
  echo "will not over write ./pack_name.c"
fi
if [ `wc -c ./pack_name.c | awk '{printf $1}'` -ne 10141 ]
then
echo `wc -c ./pack_name.c | awk '{print "Got " $1 ", Expected " 10141}'`
fi
if `test ! -s ./relay.diff`
then
echo "writing ./relay.diff"
cat > ./relay.diff << '\End\of\File\'
*** makefile.org	Fri Aug  4 10:37:40 1989
--- makefile	Fri Aug  4 10:48:00 1989
***************
*** 34,44 ****
  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
  FILES=$(NONCFILES) $(CFILES)
  NONCFILES= TODO* README ads/README ads/[0-9]* \
  	sh/inews sh/tear sh/anne.jones sh/defhdrs.awk \
--- 34,44 ----
  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 update_index.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 update_index.o
  FILES=$(NONCFILES) $(CFILES)
  NONCFILES= TODO* README ads/README ads/[0-9]* \
  	sh/inews sh/tear sh/anne.jones sh/defhdrs.awk \
*** hdrdefs.c.org	Fri Jun 30 14:36:14 1989
--- hdrdefs.c	Fri Jun 30 14:42:17 1989
***************
*** 25,30 ****
--- 25,34 ----
  static const char ngsnm[] =	"Newsgroups:";	/* filing, clone for Xref */
  static const char pathnm[] =	"Path:";	/* rejection, extend (damn) */
  static const char subjnm[] =	"Subject:";	/* for ctl. msgs. */
+ #ifdef ARTICLE_INDEX
+ static const char fromnm[] =	"From:";
+ static const char datenm[] =	"Date:";
+ #endif
  
  /* optional headers */
  static const char appnm[] =	"Approved:";	/* for mod. groups */
***************
*** 53,58 ****
--- 57,68 ----
  	pathnm, STRLEN(pathnm), offsetof(struct headers, h_path) };
  static const struct hdrdef subjhdr = {
  	subjnm, STRLEN(subjnm), offsetof(struct headers, h_subj) };
+ #ifdef ARTICLE_INDEX
+ static const struct hdrdef fromhdr = {
+ 	fromnm, STRLEN(fromnm), offsetof(struct headers, h_from) };
+ static const struct hdrdef datehdr = {
+ 	datenm, STRLEN(datenm), offsetof(struct headers, h_date) };
+ #endif
  
  static const struct hdrdef apphdr = {
  	appnm, STRLEN(appnm), offsetof(struct headers, h_approved) };
***************
*** 82,87 ****
--- 92,101 ----
  	&ngshdr,
  	&pathhdr,		/* modified by hdrmunge.c (emithdr()) */
  	&subjhdr,
+ #ifdef ARTICLE_INDEX
+ 	&fromhdr,
+ 	&datehdr,
+ #endif
  	/* start optional headers */
  	&apphdr,
  	&ctlhdr,
*** hdrcommon.c.org	Fri Jun 30 14:34:42 1989
--- hdrcommon.c	Fri Jun 30 14:36:00 1989
***************
*** 30,35 ****
--- 30,39 ----
  	hdrs->h_expiry = NULL;
  	hdrs->h_path = NULL;
  	hdrs->h_sender = NULL;
+ #ifdef ARTICLE_INDEX
+ 	hdrs->h_from = NULL;
+ 	hdrs->h_date = NULL;
+ #endif
  }
  
  boolean
***************
*** 69,72 ****
--- 73,80 ----
  	nnfree(&hdrs->h_expiry);
  	nnfree(&hdrs->h_path);
  	nnfree(&hdrs->h_sender);
+ #ifdef ARTICLE_INDEX
+ 	nnfree(&hdrs->h_from);
+ 	nnfree(&hdrs->h_date);
+ #endif
  }
*** headers.h.org	Fri Jun 30 14:33:42 1989
--- headers.h	Fri Jun 30 14:39:41 1989
***************
*** 25,30 ****
--- 25,34 ----
  	char *h_expiry;	/* needed for history */
  	char *h_path;	/* needed for transmit - must munge */
  	char *h_sender;	/* needed for transmit in case of moderation */
+ #ifdef ARTICLE_INDEX
+ 	char *h_from;	/* needed for article index */
+ 	char *h_date;	/* needed for article index */
+ #endif
  };
  
  /* common */
*** fileart.c.org	Fri Jun 30 14:19:01 1989
--- fileart.c	Fri Jun 30 14:33:25 1989
***************
*** 42,47 ****
--- 42,51 ----
  /* imports from news */
  extern void prefuse();
  
+ #ifdef ARTICLE_INDEX
+ extern update_index();
+ #endif
+ 
  /* forwards */
  FORWARD void asgnartnum(), gotgoodng(), mkjunklink(), mklinks();
  FORWARD boolean openorlink(), mkonelink(), tryartnum();
***************
*** 228,233 ****
--- 232,241 ----
  	while ((artnum = nxtartnum(ng)) >= 1)
  		if (tryartnum(art, openfirst, slashng, artnumstr))
  			break;
+ #ifdef ARTICLE_INDEX
+ 	if (artnum >= 1 && art->a_status == ST_OKAY)
+ 		update_index(art,slashng,artnumstr);
+ #endif
  	free(slashng);
  }
  
\End\of\File\
else
  echo "will not over write ./relay.diff"
fi
if [ `wc -c ./relay.diff | awk '{printf $1}'` -ne 4245 ]
then
echo `wc -c ./relay.diff | awk '{print "Got " $1 ", Expected " 4245}'`
fi
if `test ! -s ./rn.diff`
then
echo "writing ./rn.diff"
cat > ./rn.diff << '\End\of\File\'
*** head.c.org	Sun Jul  2 14:21:12 1989
--- head.c	Sun Jul  2 14:33:59 1989
***************
*** 171,176 ****
--- 171,217 ----
  }
  #endif
  
+ /* load the subjects */
+ 
+ load_subjects()
+ {
+     ART_NUM no;
+     char line[512];
+     FILE * f;
+     char subject[512];
+     int re,k;
+     char *p,*rep,*sub;
+     
+     if (f = fopen("INDEX","r")) {
+         while (fgets(line,sizeof(line),f)) {
+             subject[0] = NULL;
+             p = index(line,'\t');
+             if (p == NULL)
+                 continue;
+             *p++ = NULL;
+             rep = p;
+             p = index(p,'\t');
+             if (p == NULL)
+                 continue;
+             *p++ = NULL;
+             sub = p;
+             p = index(p,'\t');
+             if (p != NULL)
+                 *p = NULL;
+             re = atol(rep);
+             for (k = 0; k < re && strlen(subject) < sizeof(subject)-5; k++) 
+                 strcat(subject,"Re: ");
+             if (strlen(subject)+strlen(sub) < sizeof(subject)-1)
+                 strcat(subject,sub);
+             no = atol(line);
+             if (no <= lastart && OFFSET(no) >= 0)
+                 subj_list[OFFSET(no)] = savestr(subject);
+         }
+         fclose(f);
+     }
+ }
+ 
+ 
  /* get the subject line for an article */
  
  char *
***************
*** 192,197 ****
--- 233,239 ----
  #endif lint
  	for (i=0; i<=OFFSET(lastart); i++)
  	    subj_list[i] = Nullch;
+         load_subjects();
      }
      if (!artnum || artnum > lastart)
  	s = nullstr;
\End\of\File\
else
  echo "will not over write ./rn.diff"
fi
if [ `wc -c ./rn.diff | awk '{printf $1}'` -ne 1541 ]
then
echo `wc -c ./rn.diff | awk '{print "Got " $1 ", Expected " 1541}'`
fi
if `test ! -s ./update_index.c`
then
echo "writing ./update_index.c"
cat > ./update_index.c << '\End\of\File\'
/*
 * update_index - updates the index file for the article
 *
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>
#include <sys/timeb.h>

#include "libc.h"
#include "news.h"
#include "config.h"
#include "active.h"
#include "mkdirs.h"
#include "headers.h"
#include "article.h"
#include "history.h"
#include "system.h"

extern pack_name();
extern time_t getdate();

update_index(art,slashng,artnumstr)
	struct article *art;
	char * slashng;
	char * artnumstr;
{
	char name[1024];
	char subject[256];
	struct timeb ftnow;
	time_t date;
	char from[INDEX_FROM_LENGTH+1];
	int re;
	FILE * new;
	char *p,*e;

	ftime(&ftnow);

	(void) sprintf(name,"%s/INDEX",slashng);

	re = 0;
	if (art->h.h_subj != NULL ) {
		p = art->h.h_subj;
		e = p+strlen(art->h.h_subj)-4;
		while (p < e && (STREQN(p,"Re: ",4) ||
				 STREQN(p,"re: ",4) ||
				 STREQN(p,"RE: ",4) ||
				 STREQN(p,"Re^",3))) {
			if (STREQN(p,"Re^",3)) {
				p += 3;
				re += atol(p);
				while (p < e && isdigit(*p))
					p++;
				if (*p == ':') p++;
				if (*p == ' ') p++;
			} else {
				p += 4;
				re++;
			}
		}
		while (p < e && isspace(*p))
			p++;
		strncpy(subject,p,sizeof(subject)-1);
		subject[255] = NULL;
		if (p = strchr(subject,'\n'))
			*p = NULL;
		while (p = strchr(subject,'\t'))
			*p = ' ';
		e = subject+strlen(subject)-5;
		if (e > subject && STREQN(e,"- (nf)",5))
			*e = NULL;
	} else
		strcpy(subject,"???");
	if (art->h.h_date != NULL) {
		date = getdate(art->h.h_date,&ftnow);
		if (date == -1)
			date = (time_t) 0;
	} else
		date = (time_t) 0;
	if (art->h.h_from != NULL) {
		pack_name(from,art->h.h_from,INDEX_FROM_LENGTH);
	} else
		*from = NULL;

	new = fopen(name,"a");
	if (new == NULL) {
		fprintf(stderr,"cannot open index file %s\n",name);
		return;
	}
	fprintf(new,"%s\t%d\t%s\t%ld",artnumstr,re,subject,date);
	if (*from != NULL)
		fprintf(new,"\t%s",from);
	fprintf(new,"\n");
	(void) fclose(new);
}
		
\End\of\File\
else
  echo "will not over write ./update_index.c"
fi
if [ `wc -c ./update_index.c | awk '{printf $1}'` -ne 1971 ]
then
echo `wc -c ./update_index.c | awk '{print "Got " $1 ", Expected " 1971}'`
fi
echo "Finished archive 1 of 1"
# if you want to concatenate archives, remove anything after this line
exit
-- 
Dan Oscarsson                              Department of Computer Science
                                           Lund Institute of Technology
e-mail:  Dan@DNA.LTH.Se                    Box 118
                                           S-221 00 Lund, Sweden