[alt.sources] Cnews Extensions

kehoe@scotty.dccs.upenn.edu (Brendan Kehoe) (11/07/90)

 What follows is my set of replacements for various parts of the Cnews
package (along w/ John Zeeff's rnews), in an effort to help cut down
on the amount of time eaten up by using shell scripts. Enclosed are
replacements for anne.jones, rnews, spacefor, and tear. They've been
tested & built on a number of machines; if you have any problems,
please let me know.  (But before you do, make sure you've exhausted
any local possibilities first.)

 Check out the README then the top of the Makefile for instructions.

-- cut here --
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README FilePlaces Makefile anne.h anne.jones.c
#   anne.jones.sh anne.misc.c getline.c rnews.h rnews.c rnews.readme
#   spacefor.h spacefor.c spacefor.sh tear.c
# Wrapped by brendan@cs.widener.edu on Tue Nov  6 11:00:22 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1717 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
X First, my thanks to all of the folks that helped test this stuff:
X
aem@mthvax.cs.miami.edu		brtmac@maverick.ksu.ksu.edu
ccea3@rivm05.rivm.nl		rdc30med@nmrdc1.nmrdc.nnmc.navy.mil
zeeff@b-tech.ann-arbor.mi.us	ns@iddth.id.dk
ler@lerami.lonestar.org		balilly!bruce@sonyd1.broadcast.sony.com
ns@iddth.id.dk			stealth@engin.umich.edu
blender!herb@uunet.uu.net	slolane!diller@polyslo.calpoly.edu
fk@rci.dk			paulv@logitek.co.uk
root%humpty%symbas.uucp@nac.no	pdn!palan!ckctpa!crash@uunet.uu.net
winans@antares.mcs.anl.gov
X
X Check out the top of the Makefile for how to build all of this.
X
X	1. To install just anne.jones and none of the others, type:
X		make installaj
X	2. To install just rnews & none of the others, type:
X		make installnews
X	3. To install everything, type:
X		make install
X	4. If you want to replace only certain ones, check out the file
X	   'FilePlaces'.
X	5. I fixed the problems with spacefor. It also now lets you
X	   explicitly say how much space you want by doing:
X		spacefor.stub 1 article 3500
X	   or somesuch, per ccea3@rivm05.rivm.nl's suggestion.
X	6. Tear works.
X	7. According to John Zeeff rnews works.
X	8. To use them, you'll want to check out anne.h, spacefor.h, rnews.h,
X	   & the Makefile and customize them for your own site's configuration.
X
X Note: if you find that anne.stub is spitting out blah@<YourOS>.uucp, your
NEWSCTL environ. variable isn't being exported properly. Check out your
config file (/usr/lib/news/bin/config, for me) to see that NEWSCTL is
pointing to the right place. Also, check anne.jones.sh to see if something
got fudged.
X
X Good luck! Any questions, comments, etc. are welcome.
X
X		 Brendan Kehoe (kehoe@scotty.dccs.upenn.edu)
X			(eventually brendan@cs.widener.edu)
X
END_OF_FILE
if test 1717 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'FilePlaces' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'FilePlaces'\"
else
echo shar: Extracting \"'FilePlaces'\" \(1103 characters\)
sed "s/^X//" >'FilePlaces' <<'END_OF_FILE'
X Here's where everything goes, in case you just want to replace a few
of them.
X BINDIR is the newsbin directory (e.g. /usr/lib/newsbin) where all of this
stuff is located. RNEWSDIR is where rnews is currently located (if it shows
up as /bin/rnews, ls -l it to make sure it's not a symbolic link to somewhere
else).
X
X For spacefor:
X	mkdir BINDIR/old if it's not already there
X	move BINDIR/spacefor	->		BINDIR/old/spacefor
X	move spacefor.sh	->		BINDIR/spacefor
X	move spacefor.stub	->		BINDIR/spacefor.stub
X
X For anne.jones: (you could also do 'make installaj')
X	mkdir BINDIR/inject/old if it's not already there
X	move BINDIR/inject/anne.jones	->	BINDIR/inject/old/anne.jones
X	move anne.jones.sh		->	BINDIR/inject/anne.jones
X	move anne.stub			->	BINDIR/inject/anne.stub
X
X For tear:
X	mkdir BINDIR/inject/old if it's not already there
X	move BINDIR/inject/tear	->		BINDIR/inject/old
X	move tear		->		BINDIR/inject/tear
X
X For rnews: (you could also do 'make installnews')
X	mkdir RNEWSDIR/old if it's not already there
X	move RNEWSDIR/rnews	->		RNEWSDIR/old/rnews
X	move rnews		->		RNEWSDIR/rnews
X
X And that's it.
END_OF_FILE
if test 1103 -ne `wc -c <'FilePlaces'`; then
    echo shar: \"'FilePlaces'\" unpacked with wrong size!
fi
# end of 'FilePlaces'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(3301 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# The Cnews Extensions Package - Brendan Kehoe - brendan@cs.widener.edu
X# (can currently be reached at kehoe@scotty.dccs.upenn.edu)
X# Last Updated: 11/04/90
X#
X# Step 1 - Edit anne.h, spacefor.h, and rnews.h to customize them for
X#	   your site. Also see rnews.readme for important information for
X#	   rnews to work properly.
X# Step 2 - If your machine needs the TZ environmental variable, edit the
X#	   line in rnews.c that reads 'environ[2] = "TZ=EST5EDT"' to use
X#	   your local timezone
X# Step 3 - Set up BINDIR, RNEWSDIR, CC, and CFLAGS for your configuration.
X# Step 4 - Do a make.
X# Step 5 - Do a 'make install' -- this will replace the current versions
X#	   of spacefor, tear, and anne.jones with the new ones.
X#[Step 6]- If you want to go back to what you had before, do 'make undo'..
X#	   it'll put the old versions back. (They're in $BINDIR/old)
X#
X# This is your NEWSBIN directory (if it's a sym link, that's ok)
BINDIR	= /usr/lib/newsbin
X#
X# This is the directory where the rnews program currently resides
RNEWSDIR= /usr/local/bin
X#
X# CC:
X# If you don't have Gnu C, have 'cc' uncommented instead.
CC	= cc
X#CC	= gcc
X#
X# CFLAGS:
X# If you have Gnu C, work with the first line (with the -f's in it);
X#  otherwise, use the second. 
X# If you're a BSD system, use -DBSD
X# If you're running AIX 2.2.1 on an IBM RT use -DAIX
X# If you're on a USG system, use -DUSG	(note only rnews uses this)
X# Note for SunOS 4.1: I expressly didn't use -O2 or higher because of
X#  the many bugreports that are logged discounting their accuracy. (Most
X#  notable is -O2, generally accepted, even in the Gnu Project.)
X#
X#CFLAGS       = -O -traditional -finline-functions -fstrength-reduce -DBSD
CFLAGS		= -O -DBSD
X#
X# And that's all she wrote.
X  
all: anne.stub tear spacefor.stub rnews
X
anne.stub: getline.o anne.misc.o anne.h anne.jones.c
X	$(CC) $(CFLAGS) anne.jones.c -o anne.stub getline.o anne.misc.o $(LDFLAGS)
X	strip $@
X
tear: getline.c tear.c
X	$(CC) $(CFLAGS) $@.c -o $@ getline.o $(LDFLAGS)
X	strip $@
X
spacefor.stub: spacefor.c
X	$(CC) $(CFLAGS) spacefor.c -o spacefor.stub $(LDFLAGS)
X	strip $@
X
rnews: rnews.c
X	$(CC) $(CFLAGS) $@.c -o $@ $(LDFLAGS)
X	strip $@
X
install:
X	-mkdir $(BINDIR)/old
X	mv $(BINDIR)/spacefor $(BINDIR)/old
X	cp spacefor.sh $(BINDIR)/spacefor
X	cp spacefor.stub $(BINDIR)
X	-mkdir $(BINDIR)/inject/old
X	mv $(BINDIR)/inject/anne.jones $(BINDIR)/inject/old
X	mv $(BINDIR)/inject/tear $(BINDIR)/inject/old
X	cp anne.jones.sh $(BINDIR)/inject/anne.jones
X	cp anne.stub $(BINDIR)/inject
X	cp tear $(BINDIR)/inject
X	-mkdir $(RNEWSDIR)/old
X	mv $(RNEWSDIR)/rnews $(RNEWSDIR)/old
X	cp rnews $(RNEWSDIR)
X
installaj:
X	-mkdir $(BINDIR)/old
X	mv $(BINDIR)/inject/anne.jones $(BINDIR)/inject/old
X	cp anne.jones.sh $(BINDIR)/inject/anne.jones
X	cp anne.stub $(BINDIR)/inject
X
installnews:
X	mkdir $(RNEWSDIR)/old
X	mv $(RNEWSDIR)/rnews $(RNEWSDIR)/old
X	cp rnews $(RNEWSDIR)
X
undo:
X	rm -f $(BINDIR)/spacefor.stub $(BINDIR)/inject/anne.stub
X	-mv $(BINDIR)/old/* $(BINDIR)/
X	-mv $(BINDIR)/inject/old/* $(BINDIR)/inject
X	-rmdir $(BINDIR)/old $(BINDIR)/inject/old
X	-mv $(RNEWSDIR)/old/rnews $(RNEWSDIR)/
X	-rmdir $(RNEWSDIR)/old
X
shar:
X	shar -i list -o cnews.set.shar
X
clean:
X	rm -f anne.stub tear spacefor.stub rnews getline.o anne.misc.o tear.o
X	rm -f core a.out spacefor.o anne.jones.o *.ln gmon.out cnews.set.shar
X
END_OF_FILE
if test 3301 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'anne.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'anne.h'\"
else
echo shar: Extracting \"'anne.h'\" \(1614 characters\)
sed "s/^X//" >'anne.h' <<'END_OF_FILE'
X/*
X * anne.h - for anne.jones.c
X */
X
X#define	HAVESTRSTR		/* have the strstr() function? */
X#ifndef HAVESTRSTR
char *strstr();
X#endif /* !HAVESTRSTR */
X#define HAVEGETHOSTNAME		/* have the gethostname() function? */
X#undef	NOLINEBUF		/* fprintf() doesn't flush stdout() */
X
X/* you're done */
X
X#define	MAXLINE		1000	/* maximum line length to be read in */
X#define	MAXHEAD		30	/* maximum length from ^ to : of header */
X#define	NUMHEADERS	50	/* max # of headers expected */
X#define	NAMESIZE	30	/* size of mailname */
X
X#define	ORDER		5	/* the range from 1-n that *HAVE* to be in */
X#define	NUMKNOWN	10	/* how many we already know */
X#define	KNOWSTART	1	/* # the below starts at */
X#define	MSGIDLEN	60	/* length of message id string, with <>'s */
X#define	DATELEN		30	/* length of date string */
X#define	ORGLEN		80	/* length of organization's name */
X
X#define	CTLNAMEPOS	1
X#define	NGNAMEPOS	2
X#define	PATHNAMEPOS	3
X#define	FROMPOS		4
X#define	SUBJECTPOS	5
X#define	MSGIDPOS	6
X#define	DATEPOS		7
X#define	ORGPOS		8
X#define	DISTPOS		9
X#define	SENDERPOS	10
X
X/* these aren't used yet */
X#define	CTLNAME		"Control:"
X#define	NGNAME		"Newsgroups:"
X#define	PATHNAME	"Path:"
X#define	FROMNAME	"From:"
X#define	SUBJECTHDR	"Subject:"
X#define	MSGIDNAME	"Message-ID:"
X#define	TYPONAME	"Message-Id:"
X#define	DATENAME	"Date:"
X#define	ORGNAME		"Organization:"
X#define	DISTRNAME	"Distribution:"
X#define	SENDERNAME	"Sender:"
X
X#define	NOTFOUND	-1
X#define	NOTKNOWN	-1
X
static char *allheads="\
Control:.....\
Newsgroups:..\
Path:........\
XFrom:........\
Subject:.....\
Message-ID:..\
Date:........\
Organization:\
Distribution:\
Sender:......";
END_OF_FILE
if test 1614 -ne `wc -c <'anne.h'`; then
    echo shar: \"'anne.h'\" unpacked with wrong size!
fi
# end of 'anne.h'
fi
if test -f 'anne.jones.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'anne.jones.c'\"
else
echo shar: Extracting \"'anne.jones.c'\" \(9002 characters\)
sed "s/^X//" >'anne.jones.c' <<'END_OF_FILE'
X/*
X * anne.jones - anne.jones.c - 11/03/90 - Brendan Kehoe
X * An implementation of Cnews' anne.jones program to parse down articles
X */
X
X#include <stdio.h>
X#include <string.h>
X#include <pwd.h>
X#include <time.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/utsname.h>
X#include "anne.h"
X#include <signal.h>
X
struct passwd *getpwuid(); /* Not included in pwd.h -- a3@rivm.nl */
X
X#ifdef	DEBUG
XFILE *debug = stderr;
X#endif
X
char *get_a_line(), *safemalloc(), *saferealloc();
X
int
main(argc, argv)
X     int argc;
X     char **argv;
X/*ARGSUSED*/
X{
X  char *user, *name, *home, *newsctl, *mailname, *colon, *org, *line,
X  **header, from[100], head[MAXHEAD], path[200], fullname[50];
X  static char *dayname[8] = {  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
X			       "Sat", "Sun",
X			     }, *mon[12] = {
X			       "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
X			       "Aug", "Sep", "Oct", "Nov", "Dec",
X			     };
X  int tmp, i, headcnt = 0, *found, *pme;
X  FILE *fp;
X  time_t t;
X  struct stat statbuf;
X  struct tm *tm;
X  struct utsname ut;
X  struct passwd *pent;
X  
X  extern char *getenv();
X  char *squeeze(), *getmname(), **buildbook();
X  FILE *trypath();
X  void termin(), squeeze2();
X  
X  signal(SIGSEGV, termin);
X  signal(SIGINT, termin);
X  signal(SIGQUIT, termin);
X  signal(SIGHUP, termin);
X  signal(SIGBUS, termin);
X
X  if ((newsctl = getenv("NEWSCTL")) == (char *) NULL) {
X    perror("NEWSCTL");
X    exit(1);
X  }
X  found = (int *) calloc(NUMKNOWN + KNOWSTART, sizeof(int));
X  for (i = KNOWSTART; i < NUMKNOWN + KNOWSTART; i++)
X    found[i] = NOTFOUND;
X  header = (char **) buildbook(NUMHEADERS);
X  
X  /* mailname & host set */
X  mailname = safemalloc(301);
X  if ((fp = trypath(newsctl, "mailname")) != (FILE *) NULL) {
X    mailname = squeeze(getmname(fp));
X  } else if ((fp = trypath(newsctl, "whoami")) != (FILE *) NULL) {
X    mailname = getmname(fp);
X#ifdef HAVEGETHOSTNAME
X  } else if ((gethostname(mailname, 100)) < 0) {
X    perror("ghostname");
X    exit(1);
X#endif /* HAVEGETHOSTNAME */
X  } else if ((fp = fopen("/etc/whoami", "r")) != (FILE *) NULL) {
X    mailname = getmname(fp);
X    /* skipped the uuname -l one, since it does gethostname(2) */
X  } else if (uname(&ut) >= 0) {
X    strcpy(mailname, ut.sysname);
X  } else {
X    strcpy(mailname, "the_unknown_host");
X  }
X  
X  /* if it doesn't have a ., it's probably a uucp host */
X  if (strchr(mailname, '.') == (char *) NULL)
X    strcat(mailname, ".uucp");
X  
X  /* user's name set */
X  pent = getpwuid(geteuid());	/* make it so su'ing someone works */
X  if ((user = getenv("LOGNAME")) == NULL)
X    user = pent->pw_name;
X  
X  /* name set & cleaned up */
X  if (((name = getenv("NAME")) == NULL) &&
X      ((home = getenv("HOME")) != (char *) NULL)) {
X    strcpy(path, home);
X    strcat(path, "/.name");
X    stat(path, &statbuf);
X    if (statbuf.st_size > 0) {
X      if ((fp = fopen(path, "r")) != (FILE *) NULL) {
X	name = safemalloc(101);
X	if ((get_a_line(name, 100, fp)) == (char *) NULL) {
X	  perror("get_a_line .name");
X	  exit(1);
X	}
X	if (*(colon = (name + strlen(name) - 1)) == '\n')
X	  *colon = '\0';
X	fclose(fp);
X      }
X    } else {
X      
X      /*
X	if gecos has '&', use capitalized login name
X	*/
X      if ((strchr(pent->pw_gecos, '&') != (char *) NULL)) {
X	name = safemalloc(strlen(pent->pw_name) + 1);
X	strcpy(name, pent->pw_name);
X	*name = toupper(*name);
X      } else {
X	name = pent->pw_gecos;
X      }
X    }
X  }
X
X  /* fullname test */
X  if (name != NULL) {	/* if no real name, leave it off */
X    strtok(name, ",");  /* preserve only the name field -- ns@iddth.id.dk */
X    sprintf(fullname, " (%s)", name);
X  }
X
X  /* setup the from field */
X  sprintf(from, "%s@%s", user, mailname);
X  if (name != NULL)
X    strcat(from, fullname);
X  
X  /* set up the date */
X  time(&t);
X  tm = gmtime(&t);
X  
X  /* set up the org */
X  if ((org = getenv("ORGANIZATION")) == (char *) NULL) {
X    if (((fp = trypath(newsctl, "organization")) == (FILE *) NULL) &&
X	((fp = trypath(newsctl, "organisation")) == (FILE *) NULL)) {
X      org = "Total lack of organization.";
X    } else {
X      org = safemalloc(ORGLEN + 1);
X      get_a_line(org, ORGLEN, fp);
X      if (*(colon = (org + strlen(org) - 1)) == '\n')
X	*(org + strlen(org) - 1) = '\0';
X      fclose(fp);
X    }
X  }
X
X  /* now pump out a nice clean header */
X  line = safemalloc(MAXLINE + 1);
X  
X  while (get_a_line(line, MAXLINE, stdin) != (char *) NULL) {
X    /* squeeze out (tr) the bad chars */
X#ifdef	DEBUG
X    fprintf(debug, "getline got ->%s<-\n", line);
X#endif
X    if (*line != '\n') {
X      if (*(colon = (line + strlen(line) - 1)) == '\n')
X	*colon = '\0';
X      /* clean out the garbage */
X      squeeze2(line);
X      /* replace :tab with :spc */
X      if (colon = strchr(line, ':'))
X	if (*(colon + 1) == '\t')
X	  *(colon + 1) = ' ';
X    }
X    header[headcnt] = safemalloc(strlen(line) + 1);
X    strcpy(header[headcnt++], line);
X#ifdef	DEBUG
X    fprintf(debug, "getline put ->%s<-\n", line);
X#endif
X  }
X  
X  /* thus begins the defheaders.awk hack */
X  
X  /* nullify all headers with empty contents */
X  for (i = 0; i < headcnt; i++) {
X    if ((header[i][0]) && (colon = strchr(header[i], ':'))) {
X      colon++;	/* move to the chr past the colon */
X      
X      /*
X	strspn -> the last position (the NULL) if there was
X	nothing but tabs & spaces .. in that case, it's an
X	empty header and discard it
X	*/
X      if (strspn(colon, " \011") == strlen(colon)) {
X	header[i][0] = '\0';
X	continue;
X      }
X      strncpy(head, header[i], colon - header[i]);
X      *(head + (colon - header[i])) = '\0';
X      
X      /* fix Message-Id to read Message-ID */
X      if (strcmp(head, TYPONAME) == 0)
X	header[i][9] = 'D';
X      
X      /* mark off the ones we're expecting */
X      if ((tmp = know_head(head)) != NOTKNOWN)
X	found[tmp] = i;
X    }
X  }
X  
X  for (i = KNOWSTART; i < (NUMKNOWN + KNOWSTART); i++) {
X    
X    /*
X      If no header was given for something, then fill it in with
X      what we've come up with above
X      */
X    if (found[i] == NOTFOUND) {
X      switch (i) {
X      case PATHNAMEPOS:
X	header[headcnt] = safemalloc(strlen(user) + 8);
X	sprintf(header[headcnt], "Path: %s", user);
X	found[i] = headcnt++;
X	break;
X      case MSGIDPOS:
X	header[headcnt] = safemalloc(MSGIDLEN + 13);
X	sprintf(header[headcnt],
X		"Message-ID: <19%02d%s%02d.%02d%02d%02d.%i@%s>",
X		tm->tm_year, *(mon + tm->tm_mon),
X		tm->tm_mday, tm->tm_hour,
X		tm->tm_min, tm->tm_sec, getpid(),
X		mailname);
X	found[i] = headcnt++;
X	break;
X      case DATEPOS:
X	
X	/*
X	 * put it in the form Day, DD Mon Yr hh:mm:ss GMT
X	 */
X	header[headcnt] = safemalloc(DATELEN + 5);
X	sprintf(header[headcnt],
X		"Date: %s, %02d %s %02d %02d:%02d:%02d GMT",
X		*(dayname + tm->tm_wday), tm->tm_mday,
X		*(mon + tm->tm_mon), tm->tm_year,
X		tm->tm_hour, tm->tm_min, tm->tm_sec);
X	found[i] = headcnt++;
X	break;
X      case ORGPOS:
X	header[headcnt] = safemalloc(strlen(org) + 16);
X	sprintf(header[headcnt], "Organization: %s",
X		org);
X	found[i] = headcnt++;
X	break;
X      case FROMPOS:
X	header[headcnt] = safemalloc(strlen(from) + 8);
X	sprintf(header[headcnt], "From: %s", from);
X	found[i] = headcnt++;
X	break;
X      }
X    }
X  }
X  
X  /* kill the dist header if it's to 'world' */
X  if (found[DISTPOS] != NOTFOUND)
X    if (strcmp("Distribution: world", header[found[DISTPOS]]) == 0) {
X      header[found[DISTPOS]][0] = '\0';
X      found[DISTPOS] = NOTFOUND;
X    }
X  
X  /*
X   * if chars 1-14 of Subject header is 'Subject: cmesg ', then create
X   * the Control line to read 'Control: 15th_on'
X   * 
X   * if newsgroup line contains '.ctl', build Control: line so it reads
X   * 'Control: 8th_on'
X   */
X  if ((found[SUBJECTPOS] != NOTFOUND) && (found[CTLNAMEPOS] == NOTFOUND)) {
X    if (strncmp(header[found[SUBJECTPOS]],
X		"Subject: cmesg ", 15) == 0) {
X      
X      /*
X	get 'Control: ' (10) plus rest of Subject (-14)
X	*/
X      header[headcnt] = safemalloc(strlen(header[found[SUBJECTPOS]]) - 3);
X      sprintf(header[headcnt], "Control: %s",
X	      header[found[SUBJECTPOS]] + 15);
X      found[CTLNAMEPOS] = headcnt;
X      headcnt++;
X    } else if ((found[NGNAMEPOS] != NOTFOUND) &&
X	       (strstr(header[found[NGNAMEPOS]], ".ctl") != (char *) NULL)) {
X      
X      /*
X       * get 'Control: ' (10) plus rest of Subject (-8)
X       */
X      header[headcnt] = safemalloc(strlen(header[found[SUBJECTPOS]]) + 3);
X      sprintf(header[headcnt],
X	      "Control: %s", header[found[SUBJECTPOS]] + 9);
X      found[CTLNAMEPOS] = headcnt;
X      headcnt++;
X    }
X  }
X
X  /* warn to stderr if there are no newsgroups listed */
X  if (found[NGNAMEPOS] == NOTFOUND)
X    fputs("no newsgroups header!\n", stderr);
X  
X  /* reorder & emit headers */
X  pme = (int *) calloc(headcnt, sizeof(int));
X  
X  for (i = KNOWSTART; i < ORDER + 1; i++) {
X    if ((found[i] != NOTFOUND) && (header[found[i]][0])) {
X      fprintf(stdout, "%s\n", header[found[i]]);
X      pme[found[i]] = 1;
X    }
X  }
X  
X  for (i = 0; i < headcnt; i++) {
X    if ((pme[i] != 1) && (header[i][0]))
X      fprintf(stdout, "%s\n", header[i]);
X  }
X  fflush(stdout);
X  return(0);	/* shut UP lint */
X}
END_OF_FILE
if test 9002 -ne `wc -c <'anne.jones.c'`; then
    echo shar: \"'anne.jones.c'\" unpacked with wrong size!
fi
# end of 'anne.jones.c'
fi
if test -f 'anne.jones.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'anne.jones.sh'\"
else
echo shar: Extracting \"'anne.jones.sh'\" \(553 characters\)
sed "s/^X//" >'anne.jones.sh' <<'END_OF_FILE'
X#! /bin/sh
X# anne.jones [file...] - censor headers: munge locally-generated headers in
X#  files, enforce feeble attempts at Usenet security, generate lots of silly
X#  headers.
X# (after the notorious ring-leader of the Ontario Film and Video Review Board
X# (nee Ontario Board of Censors), Ontario's very own Mrs. Mary Whitehouse.)
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
export NEWSCTL NEWSBIN NEWSARTS
PATH=$NEWSCTL/bin:$NEWSBIN/inject:$NEWSBIN:$NEWSPATH ; export PATH
umask $NEWSUMASK
X
cat $* | exec anne.stub
END_OF_FILE
if test 553 -ne `wc -c <'anne.jones.sh'`; then
    echo shar: \"'anne.jones.sh'\" unpacked with wrong size!
fi
chmod +x 'anne.jones.sh'
# end of 'anne.jones.sh'
fi
if test -f 'anne.misc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'anne.misc.c'\"
else
echo shar: Extracting \"'anne.misc.c'\" \(2912 characters\)
sed "s/^X//" >'anne.misc.c' <<'END_OF_FILE'
X/*
X * anne.jones - anne.misc.c - 10/12/90
X *
X * Miscellaneous routines used by anne.jones
X */
X
X#include <stdio.h>
X#include <string.h>
X#ifndef AIX		/* AIX doesn't have malloc.h.  Oh well. */
X#include <malloc.h>
X#endif
X#include "anne.h"
X  
extern char	*safemalloc(), *saferealloc(), *get_a_line();
X
XFILE *
trypath(route, name)
X     register char *route, *name;
X{
X  register char *s;
X  
X  if ((s = safemalloc(strlen(route) + strlen(name) + 2)) == (char *) NULL) {
X    perror("trypath safemalloc");
X    exit(1);
X  }
X  strcpy(s, route);
X  strcat(s, "/");
X  strcat(s, name);
X  
X  return (fopen(s, "r"));
X}
X
char *
squeeze(str)
X     char *str;
X{
X  register char *s, *t, *u;
X
X#ifdef	DEBUG
X  fprintf(debug, "squeeze got ->%s<-\n", str);
X#endif
X  s = str;
X  
X  /*
X    worst case it'll be as long as s .. never longer
X    */
X  t = u = safemalloc(strlen(s) + 1);
X  
X  do {
X    if (*s != ' ' && *s != '\t')
X      *(t++) = *s;
X  } while (*s++);
X  
X  *t = '\0';
X#ifdef	DEBUG
X  fprintf(debug, "squeeze gave back ->%s<-\n", u);
X#endif
X  return (u);
X}
X
void
squeeze2(s)
X     register char *s;
X{
X  register char *t, *u, *orig;
X
X  t = u = safemalloc(strlen(s) + 8);
X#ifdef	DEBUG
X  fprintf(debug, "squeeze2 got ->%s<-\n", s);
X#endif
X  orig = s;
X  do {
X    if (!((*s < '\040') &&
X	  ((*s >= '\015') || (*s == '\013') || (*s < '\010'))
X	  )) {
X      *(t++) = *s;
X    }
X  } while (*(++s));
X  *t = '\0';
X  
X  strcpy(orig, u);
X#ifdef	DEBUG
X  fprintf(debug, "squeeze2 put ->%s<-\n", orig);
X#endif
X  free(u);
X}
X
char *
getmname(f)
X     FILE *f;
X{
X  char *s, *t;
X  s = safemalloc(NAMESIZE + 1);
X  if ((get_a_line(s, NAMESIZE, f)) == (char *) NULL) {
X    perror("getmname");
X    exit(1);
X  }
X  fclose(f);
X  if (*(t = (s + strlen(s) - 1)) == '\n')
X    *t = '\0';
X  return (s);
X}
X
int
know_head(s)
X     char *s;
X{
X  register char *t;
X  
X  if ((t = strstr(allheads, s)) != (char *) NULL)
X    return (((t - allheads) / 13) + 1);
X  else
X    return (NOTKNOWN);
X}
X
char **
buildbook(dim)
X     int dim;
X{
X  register int i;
X  register char *pages, **book;
X  
X  pages = safemalloc(dim * dim);
X  if (pages == (char *)NULL) {
X    fprintf(stderr, "No heap space for header book items!\n");
X    exit(1);
X  }
X  book = (char **) calloc(dim, sizeof(char *));
X  if (book == (char **)NULL) {
X    fprintf(stderr, "No heap space for header book!\n");
X    exit(1);
X  }
X  for (i = 0; i < dim; i++) {
X    *(book + i) = pages;
X    pages += dim;
X  }
X  return (book);
X}
X
void
termin()
X{
X  creat("/tmp/termin", 0644);
X  exit(0);
X}
X
X/* This was ripped from rn 4.3 */
X
X#ifndef	HAVESTRSTR
X/* return ptr to little string in big string, NULL if not found */
char *
strstr(big, little)
X     char *big, *little;
X{
X  register char *t, *s, *x;
X  
X  for (t = big; *t; t++) {
X    for (x = t, s = little; *s; x++, s++) {
X      if (!*x)
X	return ((char *) NULL);
X      if (*s != *x)
X	break;
X    }
X    if (!*s)
X      return (t);
X  }
X  return ((char *) NULL);
X}
X#endif	/* HAVESTRSTR */
END_OF_FILE
if test 2912 -ne `wc -c <'anne.misc.c'`; then
    echo shar: \"'anne.misc.c'\" unpacked with wrong size!
fi
# end of 'anne.misc.c'
fi
if test -f 'getline.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getline.c'\"
else
echo shar: Extracting \"'getline.c'\" \(1997 characters\)
sed "s/^X//" >'getline.c' <<'END_OF_FILE'
X/*
X * this was mercilessly stolen from rn
X */
X
X#include <stdio.h>
X#ifndef AIX		/* AIX doesn't have malloc.h.  Oh well... */
X#include <malloc.h>
X#endif
X
typedef unsigned int	MEM_SIZE;
X#ifdef NOLINEBUF
X#define FLUSH ,fflush(stdout)
X#else
X#define FLUSH
X#endif
X
X/* just like fgets but will make bigger buffer as necessary */
X
char *
get_a_line(original_buffer, buffer_length, fp)
X     char *original_buffer;
X     register int buffer_length;
X     FILE *fp;
X{
X  register int bufix = 0;
X  register int nextch;
X  register char *some_buffer_or_other = original_buffer;
X  char *safemalloc(), *saferealloc();
X  
X  do {
X    if (bufix >= buffer_length) {
X      buffer_length *= 2;
X      if (some_buffer_or_other == original_buffer) {
X	/* currently static? */
X	some_buffer_or_other = safemalloc((MEM_SIZE) buffer_length + 1);
X	strncpy(some_buffer_or_other, original_buffer, buffer_length / 2);
X	/* so we must copy it */
X      } else {	/* just grow in place, if possible */
X	some_buffer_or_other = saferealloc(some_buffer_or_other,
X					   (MEM_SIZE) buffer_length + 1);
X      }
X    }
X    if ((nextch = getc(fp)) == EOF)
X      return ((char *) NULL);
X    some_buffer_or_other[bufix++] = (char) nextch;
X  } while (nextch && nextch != '\n');
X  some_buffer_or_other[bufix] = '\0';
X  return some_buffer_or_other;
X}
X
static char nomem[] = "annejones: out of memory!\n";
X
X/* paranoid version of safemalloc */
X
char *
safemalloc(size)
X     MEM_SIZE size;
X{
X  char *ptr;
X  extern char *malloc();
X  
X  ptr = malloc(size ? size : 1);	/* safemalloc(0) is NASTY on our
X					   system */
X  if (ptr != (char *) NULL)
X    return ptr;
X  else {
X    fputs(nomem, stdout) FLUSH;
X    exit(0);
X  }
X  /* NOTREACHED */
X}
X
X/* paranoid version of realloc */
char *
saferealloc(where, size)
X     char *where;
X     MEM_SIZE size;
X{
X  char *ptr;
X  extern char *realloc();
X  
X  ptr = realloc(where, size ? size : 1);
X
X  if (ptr != (char *) NULL)
X    return ptr;
X  else {
X    fputs(nomem, stdout) FLUSH;
X    exit(0);
X  }
X  /* NOTREACHED */
X}
END_OF_FILE
if test 1997 -ne `wc -c <'getline.c'`; then
    echo shar: \"'getline.c'\" unpacked with wrong size!
fi
# end of 'getline.c'
fi
if test -f 'rnews.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rnews.h'\"
else
echo shar: Extracting \"'rnews.h'\" \(970 characters\)
sed "s/^X//" >'rnews.h' <<'END_OF_FILE'
X/*
X * rnews.h - for rnews.c
X */
X
X#define NEWS_UID	6   /* important: double check these! */
X#define NEWS_GROUP	6	
X#define RELAYNEWS	"/usr/lib/newsbin/relay/relaynews -r"
X/*
X * note: undef HAVE_STATFS under Ultrix; it's there, but it's not what
X *       we wanna use
X */
X#define HAVE_STATFS 	/* If you have statfs() syscall */
X#define NICE		/* define if you want to slow things down */
X#define LIB_MNT		"/usr" /* mount point (eg, /usr) for /usr/lib/news */
X#define SPOOL_MNT	"/news" /* mount pt for news spool dir */
X#define MIN_INODES	100   /* inodes needed for rnews to run */
X#define LIB_FREE	300   /* free blocks needed for rnews to run */
X#define SPOOL_FREE	1200  /* ditto for the spool area */
X#define EXPIRE		"/usr/lib/newsbin/expire/expire -r"
X#define EXPIRE_DIR      "/usr/lib/news"  /* expire control files dir */
X/* #define C7		/* if you want C7 decoding (7 bit) */
X#define BITS 16         /* For compress.  Try 14 on 16 bit machines */
X			/* or use M_XENIX */
END_OF_FILE
if test 970 -ne `wc -c <'rnews.h'`; then
    echo shar: \"'rnews.h'\" unpacked with wrong size!
fi
# end of 'rnews.h'
fi
if test -f 'rnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rnews.c'\"
else
echo shar: Extracting \"'rnews.c'\" \(38473 characters\)
sed "s/^X//" >'rnews.c' <<'END_OF_FILE'
X/*
X * rnews.c - rnews v1.4
X * Copyright 1989 Jon Zeeff <zeeff@b-tech.ann-arbor.mi.us>
X *
X * rnews.c - rnews v1.5 - 11/04/90
X *  - changed the include & struct setup for the statfs() call for
X *    BSD systems that don't have proper calls (e.g. Ultrix)
X *    kehoe@scotty.dccs.upenn.edu -or- brendan@cs.widener.edu (Brendan Kehoe)
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <fcntl.h>
X#include "rnews.h"
X
X/* external declarations */
extern char **environ;
extern FILE *popen();
void perror();
void exit();
unsigned sleep();
X
X/* forward declarations */
void copyout();
X	
XFILE *relaynews;
X
main()
X{
X	char buffer[20];
X	int ret;
X
X#ifdef USG
X	ulimit(2,60000L);   /* in case the ulimit was too low */
X#endif
X
X	/* get rid of our suid root status completely */
X	if (setgid(NEWS_GROUP) || setuid(NEWS_UID)) {
X           perror("can't set uid or gid\n");
X           exit(1); /* */
X        }
X
X	/* replace environment with a clean one */
X        environ[0] = "IFS= \t\n";
X        environ[1] = "PATH=/bin:/usr/bin";
X        /*environ[2] = "TZ=EST5EDT";      /* some people need this */
X        environ[2] = 0;
X
X	(void) umask(022);
X#ifdef NICE
X        (void) nice(5);
X#endif
X
X        /* Do we have a reasonable number of free blocks? */
X        /* Eliminate this test if you don't have disk space problems */
X
X	if (!have_space(SPOOL_MNT,SPOOL_FREE)) 
X           (void) make_space();
X
X        if (!have_space(LIB_MNT,LIB_FREE)||!have_space(SPOOL_MNT,SPOOL_FREE)) {
X           fprintf(stderr,"Not enough space to run rnews\n");
X
X           /* Why 75?  Because, I use 75 to indicate a temporary  
X              error that the program calling rnews (eg. uuxqt with 
X              the right modification) may be able to use to try again 
X              later.  */ 
X
X           exit(75);   
X        }
X
X	(void) fclose(stdout);
X#ifdef NICE
X	(void) sleep(2);
X#endif
X        relaynews = popen(RELAYNEWS,"w");
X	if (relaynews == NULL) {
X		perror("can't popen relaynews");
X		exit(3);
X	}
X#ifdef NICE
X	(void) sleep(2);
X#endif
X
X	if (fread(buffer, 1, 13, stdin) != 13) {
X		fprintf(stderr, "could not read in first 13 characters\n");
X		exit(4);
X	}
X
X	if (strncmp(buffer, "#! cunbatch\n",12) == 0) {
X	   (void) ungetc(buffer[12], stdin);
X	   uncompress();
X	}
X#ifdef C7
X	else if (strncmp(buffer, "#! c7unbatch\n",13) == 0) 
X		do_c7();
X#endif
X	else if (buffer[0] == 0x1f) {   /* compressed w/o cunbatch */
X                rewind(stdin);          /* won't work on a pipe */
X                uncompress();
X             }
X        else {                                          /* plain text */
X            if (fwrite(buffer,1,13,relaynews) != 13) {
X		fprintf(stderr, "error writing first 13 characters\n");
X		exit(5);
X	    }
X	    copyout();
X	}
X
X	if ((ret = pclose(relaynews)) != 0) {
X           perror("pclose of relaynews failed");
X           exit(ret);
X        }
X
X        /* Leave enough space for local people to post news */
X
X	if (!have_space(SPOOL_MNT,SPOOL_FREE)) 
X           (void) make_space();
X
X        exit(0);
X	return 0;    /* keep lint happy */
X}
X
X/* Copy stdin to relaynews */
X
void
copyout()
X{
X	int count;
X	char buf[8192];
X
X        while ((count = fread(buf,1,8192,stdin)) > 0) {
X               (void) fwrite(buf,1,count,relaynews);
X#ifdef NICE
X		(void) sleep(2);   /* reduce system load (for uncompressed) */
X#endif		
X        }
X
X}
X
X/* 
X   Run progressive expires until there is enough space.  
X   Steve Schonberger gave me the idea of doing this in rnews.
X*/
X
X/* Create these files in EXPIRE_DIR and modify them to suit your system */
X
char *days[]={"explist.A","explist.B","explist.C","explist.D","explist.E"};
X
X#define LOCK_FILE "LOCK.rnews"
X
make_space()
X{
register int i;
int ret;
char string[sizeof(EXPIRE) + 20];
X
X   (void) chdir(EXPIRE_DIR);
X
X   /* Get a lock file to prevent another rnews from wasting cpu time */
X   /* It's not a disaster if we don't, so blow it away after awhile */
X
X   for (i = 0; i < 1000; i++) {
X      if (open(LOCK_FILE, O_CREAT | O_EXCL | O_WRONLY, 0777) >= 0) 
X         break;
X      else
X         (void) sleep(5);
X   }
X
X   if (have_space(SPOOL_MNT,SPOOL_FREE))
X      ret = 0;  /* space appeared while waiting for lock */
X   else
X      for (ret = 2,i = 0; i < sizeof(days) / sizeof(char *); ++i) {
X         sprintf(string,"%s %s",EXPIRE,days[i]);
X         if (system(string)) {
X            fprintf(stderr,"Error in running expire to get space\n");
X            ret =  1;
X            break;
X         }
X
X         if (have_space(SPOOL_MNT,SPOOL_FREE)) {
X            ret = 0;
X            break;
X         }
X      } /* for */
X
X   (void) unlink(LOCK_FILE);
X   return ret;
X
X}
X
X/*
X
X  Space checker.  Based on code by:
X
X  denny@mcmi.uucp (Denny Page)
X
X*/
X
X/*
X * Changed this so it'll deal with BSD systems that don't have proper
X *  statfs() syscalls (e.g. Ultrix).
X *
X * kehoe@scotty.dccs.upenn.edu -or- brendan@cs.widener.edu (Brendan Kehoe)
X */
X#ifdef HAVE_STATFS
X# ifdef USG
X#  include <sys/statfs.h>
X#  define FREEBLOCKS statfsb.f_bfree
X#  define FREENODES statfsb.f_ffree
X# else /* !USG */
X#  include <sys/vfs.h>
X#  define FREEBLOCKS statfsb.f_bavail
X#  define FREENODES statfsb.f_ffree
X# endif /* USG */
X  struct statfs statfsb;
X#else /* !HAVE_STATFS */
X# include <sys/stat.h>
X# include <ustat.h>
X# define FREEBLOCKS ustatb.f_tfree
X# define FREENODES ustatb.f_tinode
X  struct stat statb;
X  struct ustat ustatb;
X#endif /* HAVE_STATFS */
X
have_space(dirname, minblocks)
char *dirname;
int minblocks;
X{
X
X#ifndef BSD 
X#ifndef USG
X   return 1;
X#endif
X#endif
X
X 
X#ifdef HAVE_STATFS
X    if (statfs(dirname, &statfsb, sizeof(statfsb), 0) != 0) {
X	perror("statfs failed");
X	return(1);
X    }
X#else /* HAVE_STATFS */
X    if (stat(dirname, &statb) != 0) {
X	perror("stat failed");
X	return(1);
X    }
X    if (ustat(statb.st_dev, &ustatb) != 0) {
X	perror("ustat failed");
X	return(1);
X    }
X#endif /* HAVE_STATFS */
X
X    if (FREEBLOCKS > minblocks && FREENODES > MIN_INODES)
X       return 1;
X    else
X       return 0;
X
X}
X
X/* The following is a slightly hacked up version of compress */
X/* It reads from stdin and writes to stdout */
X
X/* 
X * Compress - data compression program 
X */
X
X#define UNCOMPRESS_ONLY
X
X#define	min(a,b)	((a>b) ? b : a)
extern char *strcpy(), *strcat();
X/*
X * machine variants which require cc -Dmachine:  pdp11, z8000, pcxt
X */
X/*
X * Set USERMEM to the maximum amount of physical user memory available
X * in bytes.  USERMEM is used to determine the maximum BITS that can be used
X * for compression.
X *
X * SACREDMEM is the amount of physical memory saved for others; compress
X * will hog the rest.
X */
X#ifndef SACREDMEM
X#define SACREDMEM	0
X#endif
X
X#ifndef USERMEM
X# define USERMEM 	450000	/* default user memory */
X#endif
X
X#ifdef interdata		/* (Perkin-Elmer) */
X#define SIGNED_COMPARE_SLOW	/* signed compare is slower than unsigned */
X#endif
X
X#ifdef pdp11
X# define NO_UCHAR	/* also if "unsigned char" functions as signed char */
X# undef USERMEM 
X#endif /* pdp11 */	/* don't forget to compile with -i */
X
X#ifdef z8000
X# undef vax		/* weird preprocessor */
X# undef USERMEM 
X#endif /* z8000 */
X
X#ifdef pcxt
X# undef USERMEM
X#endif /* pcxt */
X
X#ifdef USERMEM
X# if USERMEM >= (433484+SACREDMEM)
X#  define PBITS	16
X# else
X#  if USERMEM >= (229600+SACREDMEM)
X#   define PBITS	15
X#  else
X#   if USERMEM >= (127536+SACREDMEM)
X#    define PBITS	14
X#   else
X#    if USERMEM >= (73464+SACREDMEM)
X#     define PBITS	13
X#    else
X#     define PBITS	12
X#    endif
X#   endif
X#  endif
X# endif
X# undef USERMEM
X#endif /* USERMEM */
X
X/* Preferred BITS for this memory size */
X
X#ifdef PBITS	
X# ifndef BITS
X#  define BITS PBITS
X# endif 
X#endif 
X
X#if BITS == 16
X# define HSIZE	69001		/* 95% occupancy */
X#endif
X#if BITS == 15
X# define HSIZE	35023		/* 94% occupancy */
X#endif
X#if BITS == 14
X# define HSIZE	18013		/* 91% occupancy */
X#endif
X#if BITS == 13
X# define HSIZE	9001		/* 91% occupancy */
X#endif
X#if BITS <= 12
X# define HSIZE	5003		/* 80% occupancy */
X#endif
X
X#ifdef M_XENIX			/* Stupid compiler can't handle arrays with */
X# if BITS == 16			/* more than 65535 bytes - so we fake it */
X#  define XENIX_16
X# else
X#  if BITS > 13			/* Code only handles BITS = 12, 13, or 16 */
X#   define BITS	13
X#  endif
X# endif
X#endif
X
X/*
X * a code_int must be able to hold 2**BITS values of type int, and also -1
X */
X#if BITS > 15
typedef long int	code_int;
X#else
typedef int		code_int;
X#endif
X
X#ifdef SIGNED_COMPARE_SLOW
typedef unsigned long int count_int;
typedef unsigned short int count_short;
X#else
typedef long int	  count_int;
X#endif
X
X#ifdef NO_UCHAR
X typedef char	char_type;
X#else
X typedef	unsigned char	char_type;
X#endif /* UCHAR */
char_type magic_header[] = { "\037\235" };	/* 1F 9D */
X
X/* Defines for third byte of header */
X#define BIT_MASK	0x1f
X#define BLOCK_MASK	0x80
X/* Masks 0x40 and 0x20 are free.  I think 0x20 should mean that there is
X   a fourth header byte (for expansion).
X*/
X#define INIT_BITS 9			/* initial number of bits/code */
X
X/*
X * compress.c - File compression ala IEEE Computer, June 1984.
X *
X * Authors:	Spencer W. Thomas	(decvax!harpo!utah-cs!utah-gr!thomas)
X *		Jim McKie		(decvax!mcvax!jim)
X *		Steve Davies		(decvax!vax135!petsd!peora!srd)
X *		Ken Turkowski		(decvax!decwrl!turtlevax!ken)
X *		James A. Woods		(decvax!ihnp4!ames!jaw)
X *		Joe Orost		(decvax!vax135!petsd!joe)
X */
X
X/* $Log:	rnews.c,v $
X * Revision 1.1  90/10/12  14:24:34  zeeff
X * Initial revision
X * 
X * Revision 4.0  85/07/30  12:50:00  joe
X * Removed ferror() calls in output routine on every output except first.
X * Prepared for release to the world.
X * 
X * Revision 3.6  85/07/04  01:22:21  joe
X * Remove much wasted storage by overlaying hash table with the tables
X * used by decompress: tab_suffix[1<<BITS], stack[8000].  Updated USERMEM
X * computations.  Fixed dump_tab() DEBUG routine.
X *
X * Revision 3.5  85/06/30  20:47:21  jaw
X * Change hash function to use exclusive-or.  Rip out hash cache.  These
X * speedups render the megamemory version defunct, for now.  Make decoder
X * stack global.  Parts of the RCS trunks 2.7, 2.6, and 2.1 no longer apply.
X *
X * Revision 3.4  85/06/27  12:00:00  ken
X * Get rid of all floating-point calculations by doing all compression ratio
X * calculations in fixed point.
X *
X * Revision 3.3  85/06/24  21:53:24  joe
X * Incorporate portability suggestion for M_XENIX.  Got rid of text on #else
X * and #endif lines.  Cleaned up #ifdefs for vax and interdata.
X *
X * Revision 3.2  85/06/06  21:53:24  jaw
X * Incorporate portability suggestions for Z8000, IBM PC/XT from mailing list.
X * Default to "quiet" output (no compression statistics).
X *
X * Revision 3.1  85/05/12  18:56:13  jaw
X * Integrate decompress() stack speedups (from early pointer mods by McKie).
X * Repair multi-file USERMEM gaffe.  Unify 'force' flags to mimic semantics
X * of SVR2 'pack'.  Streamline block-compress table clear logic.  Increase 
X * output byte count by magic number size.
X * 
X * Revision 3.0   84/11/27  11:50:00  petsd!joe
X * Set HSIZE depending on BITS.  Set BITS depending on USERMEM.  Unrolled
X * loops in clear routines.  Added "-C" flag for 2.0 compatibility.  Used
X * unsigned compares on Perkin-Elmer.  Fixed foreground check.
X *
X * Revision 2.7   84/11/16  19:35:39  ames!jaw
X * Cache common hash codes based on input statistics; this improves
X * performance for low-density raster images.  Pass on #ifdef bundle
X * from Turkowski.
X *
X * Revision 2.6   84/11/05  19:18:21  ames!jaw
X * Vary size of hash tables to reduce time for small files.
X * Tune PDP-11 hash function.
X *
X * Revision 2.5   84/10/30  20:15:14  ames!jaw
X * Junk chaining; replace with the simpler (and, on the VAX, faster)
X * double hashing, discussed within.  Make block compression standard.
X *
X * Revision 2.4   84/10/16  11:11:11  ames!jaw
X * Introduce adaptive reset for block compression, to boost the rate
X * another several percent.  (See mailing list notes.)
X *
X * Revision 2.3   84/09/22  22:00:00  petsd!joe
X * Implemented "-B" block compress.  Implemented REVERSE sorting of tab_next.
X * Bug fix for last bits.  Changed fwrite to putchar loop everywhere.
X *
X * Revision 2.2   84/09/18  14:12:21  ames!jaw
X * Fold in news changes, small machine typedef from thomas,
X * #ifdef interdata from joe.
X *
X * Revision 2.1   84/09/10  12:34:56  ames!jaw
X * Configured fast table lookup for 32-bit machines.
X * This cuts user time in half for b <= FBITS, and is useful for news batching
X * from VAX to PDP sites.  Also sped up decompress() [fwrite->putc] and
X * added signal catcher [plus beef in writeerr()] to delete effluvia.
X *
X * Revision 2.0   84/08/28  22:00:00  petsd!joe
X * Add check for foreground before prompting user.  Insert maxbits into
X * compressed file.  Force file being uncompressed to end with ".Z".
X * Added "-c" flag and "zcat".  Prepared for release.
X *
X * Revision 1.10  84/08/24  18:28:00  turtlevax!ken
X * Will only compress regular files (no directories), added a magic number
X * header (plus an undocumented -n flag to handle old files without headers),
X * added -f flag to force overwriting of possibly existing destination file,
X * otherwise the user is prompted for a response.  Will tack on a .Z to a
X * filename if it doesn't have one when decompressing.  Will only replace
X * file if it was compressed.
X *
X * Revision 1.9  84/08/16  17:28:00  turtlevax!ken
X * Removed scanargs(), getopt(), added .Z extension and unlimited number of
X * filenames to compress.  Flags may be clustered (-Ddvb12) or separated
X * (-D -d -v -b 12), or combination thereof.  Modes and other status is
X * copied with copystat().  -O bug for 4.2 seems to have disappeared with
X * 1.8.
X *
X * Revision 1.8  84/08/09  23:15:00  joe
X * Made it compatible with vax version, installed jim's fixes/enhancements
X *
X * Revision 1.6  84/08/01  22:08:00  joe
X * Sped up algorithm significantly by sorting the compress chain.
X *
X * Revision 1.5  84/07/13  13:11:00  srd
X * Added C version of vax asm routines.  Changed structure to arrays to
X * save much memory.  Do unsigned compares where possible (faster on
X * Perkin-Elmer)
X *
X * Revision 1.4  84/07/05  03:11:11  thomas
X * Clean up the code a little and lint it.  (Lint complains about all
X * the regs used in the asm, but I'm not going to "fix" this.)
X *
X * Revision 1.3  84/07/05  02:06:54  thomas
X * Minor fixes.
X *
X * Revision 1.2  84/07/05  00:27:27  thomas
X * Add variable bit length output.
X *
X */
X#include <ctype.h>
X#include <signal.h>
X/* #include <sys/stat.h> */
X
X#define ARGVAL() (*++(*argv) || (--argc && *++argv))
X
int n_bits;				/* number of bits/code */
int maxbits = BITS;			/* user settable max # bits/code */
code_int maxcode;			/* maximum code, given n_bits */
code_int maxmaxcode = 1L << BITS;	/* should NEVER generate this code */
X#ifdef COMPATIBLE		/* But wrong! */
X# define MAXCODE(n_bits)	(1L << (n_bits) - 1)
X#else
X# define MAXCODE(n_bits)	((1L << (n_bits)) - 1)
X#endif /* COMPATIBLE */
X
X#ifdef XENIX_16
count_int htab0[8192];
count_int htab1[8192];
count_int htab2[8192];
count_int htab3[8192];
count_int htab4[8192];
count_int htab5[8192];
count_int htab6[8192];
count_int htab7[8192];
count_int htab8[HSIZE-65536];
count_int * htab[9] = {
X	htab0, htab1, htab2, htab3, htab4, htab5, htab6, htab7, htab8 };
X
X#define htabof(i)	(htab[(i) >> 13][(i) & 0x1fff])
unsigned short code0tab[16384];
unsigned short code1tab[16384];
unsigned short code2tab[16384];
unsigned short code3tab[16384];
unsigned short code4tab[16384];
unsigned short * codetab[5] = {
X	code0tab, code1tab, code2tab, code3tab, code4tab };
X
X#define codetabof(i)	(codetab[(i) >> 14][(i) & 0x3fff])
X
X#else	/* Normal machine */
X/* save some memory if only doing uncompress */
X#ifdef UNCOMPRESS_ONLY
X/* should be (2 ** BITS + 8000) bytes */
count_int htab[((1L << BITS) + 8000) / sizeof(count_int)];
X#else
count_int htab [HSIZE];
X#endif
unsigned short codetab [HSIZE]; 
X#define htabof(i)	htab[i]
X#define codetabof(i)	codetab[i]
X#endif	/* XENIX_16 */
code_int hsize = HSIZE;			/* for dynamic table sizing */
count_int fsize;
X
X/*
X * To save much memory, we overlay the table used by compress() with those
X * used by decompress().  The tab_prefix table is the same size and type
X * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
X * get this from the beginning of htab.  The output stack uses the rest
X * of htab, and contains characters.  There is plenty of room for any
X * possible stack (stack used to be 8000 characters).
X */
X
X#define tab_prefixof(i)	codetabof(i)
X#ifdef XENIX_16
X# define tab_suffixof(i)	((char_type *)htab[(i)>>15])[(i) & 0x7fff]
X# define de_stack		((char_type *)(htab2))
X#else	/* Normal machine */
X# define tab_suffixof(i)	((char_type *)(htab))[i]
X# define de_stack		((char_type *)&tab_suffixof(1L<<BITS))
X#endif	/* XENIX_16 */
X
code_int free_ent = 0;			/* first unused entry */
int exit_stat = 0;
X
code_int getcode();
X
Usage() {
X#ifdef DEBUG
X(void)fprintf(stderr,"Usage: compress [-dDVfc] [-b maxbits] [file ...]\n");
X}
int debug = 0;
X#else
X(void)fprintf(stderr,"Usage: compress [-dfvcV] [-b maxbits] [file ...]\n");
X}
X#endif /* DEBUG */
int nomagic = 0;	/* Use a 3-byte magic number header, unless old file */
int zcat_flg = 0;	/* Write output on stdout, suppress messages */
int quiet = 1;		/* don't tell me about compression */
X
X/*
X * block compression parameters -- after all codes are used up,
X * and compression rate changes, start over.
X */
int block_compress = BLOCK_MASK;
int clear_flg = 0;
long int ratio = 0;
X#define CHECK_GAP 10000	/* ratio check interval */
count_int checkpoint = CHECK_GAP;
X/*
X * the next two codes should not be changed lightly, as they must not
X * lie within the contiguous general code space.
X */ 
X#define FIRST	257	/* first free entry */
X#define	CLEAR	256	/* table clear output code */
X
int force = 0;
char ofname [100];
int oktozap = 0;	/* true if unlink(ofname) won't lose data */
X
X#ifdef DEBUG
int verbose = 0;
X#endif /* DEBUG */
void (*bgnd_flag)();
X
int do_decomp = 0;
X
X/*****************************************************************
X * TAG( main )
X *
X * Algorithm from "A Technique for High Performance Data Compression",
X * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
X *
X * Usage: compress [-dfvc] [-b bits] [file ...]
X * Inputs:
X *	-d:	    If given, decompression is done instead.
X *
X *      -c:         Write output on stdout, don't remove original.
X *
X *      -b:         Parameter limits the max number of bits/code.
X *
X *	-f:	    Forces output file to be generated, even if one already
X *		    exists, and even if no space is saved by compressing.
X *		    If -f is not used, the user will be prompted if stdin is
X *		    a tty, otherwise, the output file will not be overwritten.
X *
X *      -v:	    Write compression statistics
X *
X * 	file ...:   Files to be compressed.  If none specified, stdin
X *		    is used.
X * Outputs:
X *	file.Z:	    Compressed form of file with same mode, owner, and utimes
X * 	or stdout   (if stdin used as input)
X *
X * Assumptions:
X *	When filenames are given, replaces with the compressed version
X *	(.Z suffix) only if the file decreases in size.
X * Algorithm:
X * 	Modified Lempel-Ziv method (LZW).  Basically finds common
X * substrings and replaces them with a variable size code.  This is
X * deterministic, and can be done on the fly.  Thus, the decompression
X * procedure needs no input table, but tracks the way the table was built.
X */
X
uncompress()
X{
X    char tempname[100];
X    char *cp, *rindex(), *malloc();
X    extern onintr(), oops();
X
X    if(maxbits < INIT_BITS) maxbits = INIT_BITS;
X    if (maxbits > BITS) maxbits = BITS;
X    maxmaxcode = 1L << maxbits;
X
X	    /* Check the magic number */
X		if ((getchar()!=(magic_header[0] & 0xFF))
X		 || (getchar()!=(magic_header[1] & 0xFF))) {
X		    (void)fprintf(stderr, "stdin: not in compressed format\n");
X		    exit(20);
X		}
X		maxbits = getchar();	/* set -b from file */
X		block_compress = maxbits & BLOCK_MASK;
X		maxbits &= BIT_MASK;
X		maxmaxcode = 1L << maxbits;
X		fsize = 100000;		/* assume stdin large for USERMEM */
X		if(maxbits > BITS) {
X			(void)fprintf(stderr,
X			"stdin: compressed with %d bits, can only handle %d bits\n",
X			maxbits, BITS);
X			exit(21);
X		}
X	    decompress();
X}
X
static int offset;
long int in_count = 1;			/* length of input */
long int bytes_out;			/* length of compressed output */
long int out_count = 0;			/* # of codes output (for debugging) */
X
X/*****************************************************************
X * TAG( output )
X *
X * Output the given code.
X * Inputs:
X * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
X *		that n_bits =< (long)wordsize - 1.
X * Outputs:
X * 	Outputs code to the file.
X * Assumptions:
X *	Chars are 8 bits long.
X * Algorithm:
X * 	Maintain a BITS character long buffer (so that 8 codes will
X * fit in it exactly).  Use the VAX insv instruction to insert each
X * code in turn.  When the buffer fills up empty it and start over.
X */
X
static char buf[BITS];
X
X#ifndef vax
char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
X#endif /* vax */
X
output( code )
code_int  code;
X{
X#ifdef DEBUG
X    static int col = 0;
X#endif /* DEBUG */
X
X    /*
X     * On the VAX, it is important to have the register declarations
X     * in exactly the order given, or the asm will break.
X     */
X    register int r_off = offset, bits= n_bits;
X    register char * bp = buf;
X
X#ifdef DEBUG
X	if ( verbose )
X	    (void)fprintf( stderr, "%5d%c", code,
X		    (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
X#endif /* DEBUG */
X    if ( code >= 0 ) {
X#ifdef vax
X	/* VAX DEPENDENT!! Implementation on other machines is below.
X	 *
X	 * Translation: Insert BITS bits from the argument starting at
X	 * offset bits from the beginning of buf.
X	 */
X	0;	/* Work around for pcc -O bug with asm and if stmt */
X	asm( "insv	4(ap),r11,r10,(r9)" );
X#else /* not a vax */
X/* 
X * byte/bit numbering on the VAX is simulated by the following code
X */
X	/*
X	 * Get to the first byte.
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X	/*
X	 * Since code is always >= 8 bits, only need to mask the first
X	 * hunk on the left.
X	 */
X	*bp = (*bp & rmask[r_off]) | ((long)code << r_off) & lmask[r_off];
X	bp++;
X	bits -= (8 - r_off);
X	code >>= 8 - r_off;
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if ( bits >= 8 ) {
X	    *bp++ = code;
X	    code >>= 8;
X	    bits -= 8;
X	}
X	/* Last bits. */
X	if(bits)
X	    *bp = code;
X#endif /* vax */
X	offset += n_bits;
X	if ( offset == ((long)n_bits << 3) ) {
X	    bp = buf;
X	    bits = n_bits;
X	    bytes_out += bits;
X	    do
X		putchar(*bp++);
X	    while(--bits);
X	    offset = 0;
X	}
X
X	/*
X	 * If the next entry is going to be too big for the code size,
X	 * then increase it, if possible.
X	 */
X	if ( free_ent > maxcode || (clear_flg > 0))
X	{
X	    /*
X	     * Write the whole buffer, because the input side won't
X	     * discover the size increase until after it has read it.
X	     */
X	    if ( offset > 0 ) {
X		if( fwrite( buf, 1, n_bits, stdout ) != n_bits)
X			writeerr();
X		bytes_out += n_bits;
X	    }
X	    offset = 0;
X
X	    if ( clear_flg ) {
X    	        maxcode = MAXCODE (n_bits = INIT_BITS);
X	        clear_flg = 0;
X	    }
X	    else {
X	    	n_bits++;
X	    	if ( n_bits == maxbits )
X		    maxcode = maxmaxcode;
X	    	else
X		    maxcode = MAXCODE(n_bits);
X	    }
X#ifdef DEBUG
X	    if ( debug ) {
X		(void)fprintf( stderr, "\nChange to %d bits\n", n_bits );
X		col = 0;
X	    }
X#endif /* DEBUG */
X	}
X    } else {
X	/*
X	 * At EOF, write the rest of the buffer.
X	 */
X	if ( offset > 0 )
X	    (void)fwrite( buf, 1, (offset + 7) / 8, stdout );
X	bytes_out += (offset + 7) / 8;
X	offset = 0;
X	(void)fflush( stdout );
X#ifdef DEBUG
X	if ( verbose )
X	    (void)fprintf( stderr, "\n" );
X#endif /* DEBUG */
X	if( ferror( stdout ) )
X		writeerr();
X    }
X}
X
X/*
X * Decompress stdin to stdout.  This routine adapts to the codes in the
X * file building the "string" table on-the-fly; requiring no table to
X * be stored in the compressed file.  The tables used herein are shared
X * with those of the compress() routine.  See the definitions above.
X */
X
decompress() {
X    register char_type *stackp;
X    register int finchar;
X    register code_int code, oldcode, incode;
X
X    /*
X     * As above, initialize the first 256 entries in the table.
X     */
X    maxcode = MAXCODE(n_bits = INIT_BITS);
X    for ( code = 255; code >= 0; code-- ) {
X	tab_prefixof(code) = 0;
X	tab_suffixof(code) = (char_type)code;
X    }
X    free_ent = ((block_compress) ? FIRST : 256 );
X
X    finchar = oldcode = getcode();
X    if(oldcode == -1)	/* EOF already? */
X	return;			/* Get out of here */
X    putchar( (char)finchar );		/* first code must be 8 bits = char */
X    if(ferror(stdout))		/* Crash if can't write */
X	writeerr();
X    stackp = de_stack;
X
X    while ( (code = getcode()) > -1 ) {
X
X	if ( (code == CLEAR) && block_compress ) {
X	    for ( code = 255; code >= 0; code-- )
X		tab_prefixof(code) = 0;
X	    clear_flg = 1;
X	    free_ent = FIRST - 1;
X	    if ( (code = getcode ()) == -1 )	/* O, untimely death! */
X		break;
X	}
X	incode = code;
X	/*
X	 * Special case for KwKwK string.
X	 */
X	if ( code >= free_ent ) {
X            *stackp++ = finchar;
X	    code = oldcode;
X	}
X
X	/*
X	 * Generate output characters in reverse order
X	 */
X#ifdef SIGNED_COMPARE_SLOW
X	while ( ((unsigned long)code) >= ((unsigned long)256) ) {
X#else
X	while ( code >= 256 ) {
X#endif
X	    *stackp++ = tab_suffixof(code);
X	    code = tab_prefixof(code);
X	}
X	*stackp++ = finchar = tab_suffixof(code);
X
X	/*
X	 * And put them out in forward order
X	 */
X	do
X	    putchar ((char) *--stackp );
X	while ( stackp > de_stack );
X
X	/*
X	 * Generate the new entry.
X	 */
X	if ( (code=free_ent) < maxmaxcode ) {
X	    tab_prefixof(code) = (unsigned short)oldcode;
X	    tab_suffixof(code) = finchar;
X	    free_ent = code+1;
X	} 
X	/*
X	 * Remember previous code.
X	 */
X	oldcode = incode;
X    }
X    (void)fflush( stdout );
X    if(ferror(stdout))
X	writeerr();
X}
X
X/*****************************************************************
X * TAG( getcode )
X *
X * Read one code from the standard input.  If EOF, return -1.
X * Inputs:
X * 	stdin
X * Outputs:
X * 	code or -1 is returned.
X */
X
code_int
getcode() {
X    /*
X     * On the VAX, it is important to have the register declarations
X     * in exactly the order given, or the asm will break.
X     */
X    register code_int code;
X    static int poffset = 0, size = 0;
X    static char_type pbuf[BITS];
X    register int r_off, bits;
X    register char_type *bp = pbuf;
X
X    if ( clear_flg > 0 || poffset >= size || free_ent > maxcode ) {
X	/*
X	 * If the next entry will be too big for the current code
X	 * size, then we must increase the size.  This implies reading
X	 * a new pbuffer full, too.
X	 */
X	if ( free_ent > maxcode ) {
X	    n_bits++;
X	    if ( n_bits == maxbits )
X		maxcode = maxmaxcode;	/* won't get any bigger now */
X	    else
X		maxcode = MAXCODE(n_bits);
X	}
X	if ( clear_flg > 0) {
X    	    maxcode = MAXCODE (n_bits = INIT_BITS);
X	    clear_flg = 0;
X	}
X	size = fread((char *) pbuf, 1, n_bits, stdin );
X	if ( size <= 0 )
X	    return -1;			/* end of file */
X	poffset = 0;
X	/* Round size down to integral number of codes */
X	size = ((long)size << 3) - (n_bits - 1);
X    }
X    r_off = poffset;
X    bits = n_bits;
X#ifdef vax
X    asm( "extzv   r10,r9,(r8),r11" );
X#else /* not a vax */
X	/*
X	 * Get to the first byte.
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X	/* Get first part (low order bits) */
X#ifdef NO_UCHAR
X	code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff;
X#else
X	code = (*bp++ >> r_off);
X#endif /* NO_UCHAR */
X	bits -= (8 - r_off);
X	r_off = 8 - r_off;		/* now, poffset into code word */
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if ( bits >= 8 ) {
X#ifdef NO_UCHAR
X	    code |= (*bp++ & 0xff) << r_off;
X#else
X	    code |= *bp++ << r_off;
X#endif /* NO_UCHAR */
X	    r_off += 8;
X	    bits -= 8;
X	}
X	/* high order bits. */
X	code |= (*bp & rmask[bits]) << r_off;
X#endif /* vax */
X    poffset += n_bits;
X
X    return code;
X}
X
char *
rindex(s, c)		/* For those who don't have it in libc.a */
register char *s, c;
X{
X	char *p;
X	for (p = NULL; *s; s++)
X	    if (*s == c)
X		p = s;
X	return(p);
X}
X
X#ifdef DEBUG
printcodes()
X{
X    /*
X     * Just print out codes from input file.  For debugging.
X     */
X    code_int code;
X    int col = 0, bits;
X
X    bits = n_bits = INIT_BITS;
X    maxcode = MAXCODE(n_bits);
X    free_ent = ((block_compress) ? FIRST : 256 );
X    while ( ( code = getcode() ) >= 0 ) {
X	if ( (code == CLEAR) && block_compress ) {
X   	    free_ent = FIRST - 1;
X   	    clear_flg = 1;
X	}
X	else if ( free_ent < maxmaxcode )
X	    free_ent++;
X	if ( bits != n_bits ) {
X	    (void)fprintf(stderr, "\nChange to %d bits\n", n_bits );
X	    bits = n_bits;
X	    col = 0;
X	}
X	(void)fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' );
X    }
X    putc( '\n', stderr );
X    exit( 0 );
X}
X
code_int sorttab[1L<<BITS];	/* sorted pointers into htab */
X
dump_tab()	/* dump string table */
X{
X    register int i, first;
X    register ent;
X#define STACK_SIZE	15000
X    int stack_top = STACK_SIZE;
X    register c;
X
X    if(do_decomp == 0) {	/* compressing */
X	register int flag = 1;
X
X	for(i=0; i<hsize; i++) {	/* build sort pointers */
X		if((long)htabof(i) >= 0) {
X			sorttab[codetabof(i)] = i;
X		}
X	}
X	first = block_compress ? FIRST : 256;
X	for(i = first; i < free_ent; i++) {
X		(void)fprintf(stderr, "%5d: \"", i);
X		de_stack[--stack_top] = '\n';
X		de_stack[--stack_top] = '"';
X		stack_top = in_stack((htabof(sorttab[i])>>maxbits)&0xff, 
X                                     stack_top);
X		for(ent=htabof(sorttab[i]) & ((1<<maxbits)-1);
X		    ent > 256;
X		    ent=htabof(sorttab[ent]) & ((1<<maxbits)-1)) {
X			stack_top = in_stack(htabof(sorttab[ent]) >> maxbits,
X						stack_top);
X		}
X		stack_top = in_stack(ent, stack_top);
X		(void)fwrite( &de_stack[stack_top], 1, STACK_SIZE-stack_top, stderr);
X	   	stack_top = STACK_SIZE;
X	}
X   } else if(!debug) {	/* decompressing */
X
X       for ( i = 0; i < free_ent; i++ ) {
X	   ent = i;
X	   c = tab_suffixof(ent);
X	   if ( isascii(c) && isprint(c) )
X	       (void)fprintf( stderr, "%5d: %5d/'%c'  \"",
X			   ent, tab_prefixof(ent), c );
X	   else
X	       (void)fprintf( stderr, "%5d: %5d/\\%03o \"",
X			   ent, tab_prefixof(ent), c );
X	   de_stack[--stack_top] = '\n';
X	   de_stack[--stack_top] = '"';
X	   for ( ; ent != NULL;
X		   ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) {
X	       stack_top = in_stack(tab_suffixof(ent), stack_top);
X	   }
X	   (void)fwrite( &de_stack[stack_top], 1, STACK_SIZE - stack_top, stderr );
X	   stack_top = STACK_SIZE;
X       }
X    }
X}
X
int
in_stack(c, stack_top)
X	register c, stack_top;
X{
X	if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) {
X	    de_stack[--stack_top] = c;
X	} else {
X	    switch( c ) {
X	    case '\n': de_stack[--stack_top] = 'n'; break;
X	    case '\t': de_stack[--stack_top] = 't'; break;
X	    case '\b': de_stack[--stack_top] = 'b'; break;
X	    case '\f': de_stack[--stack_top] = 'f'; break;
X	    case '\r': de_stack[--stack_top] = 'r'; break;
X	    case '\\': de_stack[--stack_top] = '\\'; break;
X	    default:
X	 	de_stack[--stack_top] = '0' + c % 8;
X	 	de_stack[--stack_top] = '0' + (c / 8) % 8;
X	 	de_stack[--stack_top] = '0' + c / 64;
X	 	break;
X	    }
X	    de_stack[--stack_top] = '\\';
X	}
X	return stack_top;
X}
X#endif /* DEBUG */
X
writeerr()
X{
X    perror ( ofname );
X    (void)unlink ( ofname );
X    exit ( 22 );
X}
X
X/*
X * This routine returns 1 if we are running in the foreground and stderr
X * is a tty.
X */
foreground()
X{
X	if(bgnd_flag) {	/* background? */
X		return(0);
X	} else {			/* foreground */
X		if(isatty(2)) {		/* and stderr is a tty */
X			return(1);
X		} else {
X			return(0);
X		}
X	}
X}
X
onintr ( )
X{
X    if (oktozap) (void)unlink ( ofname );
X    exit ( 23 );
X}
X
oops ( )	/* wild pointer -- assume bad input */
X{
X    if ( do_decomp == 1 ) 
X    	(void)fprintf ( stderr, "uncompress: corrupt input\n" );
X    if (oktozap) (void)unlink ( ofname );
X    exit ( 24 );
X}
X
cl_block ()		/* table clear for block compress */
X{
X    register long int rat;
X
X    checkpoint = in_count + CHECK_GAP;
X#ifdef DEBUG
X	if ( debug ) {
X    		(void)fprintf ( stderr, "count: %ld, ratio: ", in_count );
X     		prratio ( stderr, in_count, bytes_out );
X		(void)fprintf ( stderr, "\n");
X	}
X#endif /* DEBUG */
X
X    if(in_count > 0x007fffff) {	/* shift will overflow */
X	rat = bytes_out >> 8;
X	if(rat == 0) {		/* Don't divide by zero */
X	    rat = 0x7fffffff;
X	} else {
X	    rat = in_count / rat;
X	}
X    } else {
X	rat = (in_count << 8) / bytes_out;	/* 8 fractional bits */
X    }
X    if ( rat > ratio ) {
X	ratio = rat;
X    } else {
X	ratio = 0;
X#ifdef DEBUG
X	if(verbose)
X		dump_tab();	/* dump string table */
X#endif
X 	cl_hash ( (count_int) hsize );
X	free_ent = FIRST;
X	clear_flg = 1;
X	output ( (code_int) CLEAR );
X#ifdef DEBUG
X	if(debug)
X    		(void)fprintf ( stderr, "clear\n" );
X#endif /* DEBUG */
X    }
X}
X
cl_hash(phsize)		/* reset code table */
X	register count_int phsize;
X{
X#ifndef XENIX_16	/* Normal machine */
X	register count_int *htab_p = htab+phsize;
X#else
X	register j;
X	register long k = phsize;
X	register count_int *htab_p;
X#endif
X	register long i;
X	register long m1 = -1;
X
X#ifdef XENIX_16
X    for(j=0; j<=8 && k>=0; j++,k-=8192) {
X	i = 8192;
X	if(k < 8192) {
X		i = k;
X	}
X	htab_p = &(htab[j][i]);
X	i -= 16;
X	if(i > 0) {
X#else
X	i = phsize - 16;
X#endif
X 	do {				/* might use Sys V memset(3) here */
X		*(htab_p-16) = m1;
X		*(htab_p-15) = m1;
X		*(htab_p-14) = m1;
X		*(htab_p-13) = m1;
X		*(htab_p-12) = m1;
X		*(htab_p-11) = m1;
X		*(htab_p-10) = m1;
X		*(htab_p-9) = m1;
X		*(htab_p-8) = m1;
X		*(htab_p-7) = m1;
X		*(htab_p-6) = m1;
X		*(htab_p-5) = m1;
X		*(htab_p-4) = m1;
X		*(htab_p-3) = m1;
X		*(htab_p-2) = m1;
X		*(htab_p-1) = m1;
X		htab_p -= 16;
X	} while ((i -= 16) >= 0);
X#ifdef XENIX_16
X	}
X    }
X#endif
X    	for ( i += 16; i > 0; i-- )
X		*--htab_p = m1;
X}
X
prratio(stream, num, den)
XFILE *stream;
long int num, den;
X{
X	register int q;			/* Doesn't need to be long */
X
X	if(num > 214748L) {		/* 2147483647/10000 */
X		q = num / (den / 10000L);
X	} else {
X		q = 10000L * num / den;		/* Long calculations, though */
X	}
X	if (q < 0) {
X		putc('-', stream);
X		q = -q;
X	}
X	(void)fprintf(stream, "%d.%02d%%", q / 100, q % 100);
X}
X
X#ifdef C7
X
X/* C7 code by Uwe Doering (gemini@netmbx.uucp) */
X
do_c7()
X{
X 		int pid,wpid,pfildes[2],exit_stat = 0;
X 		FILE *rnews_pipe;
X
X
X		   if (pipe(pfildes) != 0) {
X		      fprintf(stderr, "could not open interprocess pipe\n");
X		      exit(7);
X		   }
X		   if ((pid = fork()) == -1) {
X		      fprintf(stderr, "could not fork\n");
X		      exit(8);
X		   }
X		   if (pid) {
X		      (void) fclose(stdin);
X		      if ((rnews_pipe = fdopen(pfildes[0],"r")) == NULL) {
X			 fprintf(stderr, "could not open rnews_pipe for input\n");
X			 exit(9);
X		      }
X		      (void) close(pfildes[1]);
X		      uncompress();
X		      (void) fclose(rnews_pipe);
X		      (void) close(pfildes[0]);
X		      while ((wpid = wait(&exit_stat)) != pid && wpid != -1)
X			    ;
X		      if (exit_stat)
X			 exit(10);
X		   }
X		   else {
X		      int result;
X
X		      (void) fclose(relaynews);
X		      if ((rnews_pipe = fdopen(pfildes[1],"w")) == NULL) {
X			 fprintf(stderr, "could not open rnews_pipe for output\n");
X			 exit(11);
X		      }
X		      (void) close(pfildes[0]);
X		      result = c7decode();
X		      (void) fclose(rnews_pipe);
X		      (void) close(pfildes[1]);
X		      exit(result);
X		   }
X
X}  /* do_c7 */
X
X/* c7decode */
X
X/*
X * This program is the inverse of encode
X *
X * It collects runs of 12 characters, combines pairs of those
X * to form 6 13 bit numbers, extracts the top bit of each of
X * those to make a 13th 6 bit character, and splits each of
X * the remaining 6 12 bit numbers to form 12 6 bit ones.
X *
X * The strings of 6 bit numbers are collected into groups of
X * 4 and converted into 3 8 bit characters.
X *
X * Now all that would be trivial, if we didn't need to worry
X * about ending all this correctly.  About 1/2 of the following
X * program wouldn't be here if the ending didn't matter....
X */
X
X/*
X * the following pair of characters can never occur as a pair
X * in legal input (since (90 * 91 + 90) > 2^13) - they are
X * noticed at the beginning of a 12 char block, and serve to
X * indicate that this block is the terminator.  The character
X * immediately following is the (expanded) terminator length.
X */
X#define	ENDMARK1	((90*91 + 90) / 91)
X#define	ENDMARK2	((90*91 + 90) % 91)
X
int errcnt = 0;
X
c7decode()
X{
X	register c;
X	register char *p;
X	register i;
X	register first = 1;
X	register cnt = 0;
X	char b12[12];
X	char c12[12];
X
X	p = b12;
X	i = 12;
X
X	while ((c = getchar()) != EOF) {
X		if (c < ' ' || c >= (' ' + 91)) {
X			if (errcnt++ == 0)
X				fprintf(stderr, "c7decode: Bad data\n");
X			continue;
X		}
X		if (i == 10 && p[-1] == ENDMARK1 && p[-2] == ENDMARK2) {
X			cnt = c - ' ';
X			i = 12;
X			p -= 2;
X			continue;
X		}
X		*p++ = c - ' ';
X		if (--i == 0) {
X			if (p == &b12[12]) {
X				if (!first)
X					pack12(c12, 12, 0);
X				else
X					first = 0;
X				p = c12;
X			} else {
X				pack12(b12, 12, 0);
X				p = b12;
X			}
X			i = 12;
X		}
X	}
X
X	if (p >= &b12[0] && p < &b12[12]) {
X		if (!first)
X			pack12(c12, 12, i == 12 ? cnt : 0);
X	} else
X		pack12(b12, 12, i == 12 ? cnt : 0);
X
X	if (i != 12) {
X		if (p >= &b12[0] && p < &b12[12])
X			pack12(b12, 12-i, cnt);
X		else
X			pack12(c12, 12-i, cnt);
X	}
X
X	return((errcnt > 0) ? 1 : 0);
X}
X
static char b4[4];
static int cnt = 0;
X
pack12(p, n, last)
X	register char *p;
X	register n;
X	int last;
X{
X	register i;
X	register char *q;
X	char b13[13];
X
X	{
X		register c;
X		register c13;
X
X		q = b13;
X		c13 = 0;
X
X		for (i = 0; i < n; i += 2) {
X			c = *p++ * 91;
X			c += *p++;
X			c13 <<= 1;
X			if (c & (1 << 12))
X				c13 |= 1;
X			*q++ = (c >> 6) & 0x3f;
X			*q++ = c & 0x3f;
X		}
X		*q++ = c13;
X		if (last)
X			q = &b13[last];
X	}
X
X	p = b13;
X	n = q - p;
X	i = cnt;
X	q = &b4[cnt];
X
X	while (--n > 0) {
X		*q++ = *p++;
X		if (++i == 4) {
X			char b3[3];
X			register char *b = b4;
X
X			/* inline expansion of pack6bit, to save calls ... */
X
X			q = b3;
X			*q++ = (b[0] << 2) | ((b[1] >> 4) & 0x3);
X			*q++ = (b[1] << 4) | ((b[2] >> 2) & 0xf);
X			*q = (b[2] << 6) | (b[3] & 0x3f);
X
X			q = b3;
X			while (--i > 0)
X				putchar(*q++);
X
X			q = b4;
X		}
X	}
X
X	*q++ = *p++;	/* the last octet */
X	++i;
X
X	if (last || i == 4) {
X		pack6bit(b4, i, last);
X		i = 0;
X	}
X
X	cnt = i;
X}
X
pack6bit(p, n, last)
X	register char *p;
X	register int n;
X	int last;
X{
X	register char *q;
X	register i = 3;
X	char b3[3];
X
X	if (last) {
X		i = p[n-1];
X		if (i >= 3) {
X			fprintf(stderr, "c7decode: Badly encoded file\n");
X			errcnt++;
X			i = 3;		/* do the best we can */
X		}
X	}
X
X	q = b3;
X	*q++ = (p[0] << 2) | ((p[1] >> 4) & 0x3);
X	*q++ = (p[1] << 4) | ((p[2] >> 2) & 0xf);
X	*q = (p[2] << 6) | (p[3] & 0x3f);
X
X	q = b3;
X
X	while (--i >= 0)
X		putchar(*q++);
X}
X
X#endif
END_OF_FILE
if test 38473 -ne `wc -c <'rnews.c'`; then
    echo shar: \"'rnews.c'\" unpacked with wrong size!
fi
# end of 'rnews.c'
fi
if test -f 'rnews.readme' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'rnews.readme'\"
else
echo shar: Extracting \"'rnews.readme'\" \(3242 characters\)
sed "s/^X//" >'rnews.readme' <<'END_OF_FILE'
X/*
X
JZ rnews.c V1.4 for C news 
X
C News has a fair amount of overhead in all the processes that get run 
between rnews and relaynews.  This program eliminates most of this 
overhead for a substantial performance increase.  I measure ~40% on my 
tests with 200k before compression batches.  Performance improvement 
will be higher on smaller batches or with uncompressed feeds.  
Basically, things go right from rnews into relaynews. 
X
Much of this overhead is concerned with disk space.  IMHO, there are 
better ways of doing disk space managment than C New's limited method 
of not unpacking news if the disk is full.  If news is continuing to 
come in, one is just delaying the inevitable (throwing away news) 
anyway.  There are other, more effective ways to take care of disk 
space; a progressive expire (see below) and a uucp that is smart 
enough to quit accepting incoming news when space is low and possibly 
understands a temporary failure return code.  
X
This rnews has an option to do progressive expires if there is not 
enough space on the spool partition.  The advantage of this is that 
you can run right on the edge of a full disk without ever worrying 
about losing news or trying to estimate how much will come in before 
you run expire again.  These are "expire -r"s; they run reasonably 
fast.  If expire tends to slow your system down excessively, you may 
want to consider adding a sleep() to expire to make the system more
usable while expire is running.  A cnews example (expire.c):
X
X        while ((line = readline(old)) != NULL) {
X>                unsigned int slowdown=0;  
X>                if (++slowdown % 50 == 0) sleep(1);  
X                line = doline(line);
X
Note that you should still run expire from cron now and then to 
rebuild the history file (use dbz).  Be sure to create the
X/usr/lib/news/explist.[A-E] files.
X
C News makes some attempts to address local news security but leaves 
some holes (newsspool and relaynews) that make some of this a wasted 
effort.  You don't ever want a user directly or indirectly running a 
news owned program while their uid is exposed.  There is little sense 
in using another id (such as news) if breaking that id (or purposely 
giving the passwd so someone can administer news) allows one to 
break many others by a trojan horse attack.  This program runs suid 
root and immediately gives it up.  It cures the problem of a uid being 
exposed through the rnews/newsspool entry point.  If you install the 
supplied relaynews.c the other entry point (inews/relaynews) will be 
secure from this attack.  You can get rid of setnewsids and change 
newsspool and relaynews.real permissions and ownership to be like the 
other files.  
X
This program should be in a public bin directory and:
X
X -rws--x--x  root  bin    rnews
X
Tested on SUNOS 4.0, Sys III (a 16 bit machine), and a Sys V.3.1 system. 
X
Copyright 1989 Jon Zeeff <zeeff@b-tech.ann-arbor.mi.us>
X
You can do whatever you want with this code provided you:
X
X1) leave my name on it
X2) don't hold me responsible for any problems  
X3) report all bugs and modifications to me via email 
X
Support for c7decode added by Uwe Doering <gemini@netmbx.UUCP>
I used the original Cnews c7decode.c with slight modifications.
X*/
END_OF_FILE
if test 3242 -ne `wc -c <'rnews.readme'`; then
    echo shar: \"'rnews.readme'\" unpacked with wrong size!
fi
# end of 'rnews.readme'
fi
if test -f 'spacefor.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'spacefor.h'\"
else
echo shar: Extracting \"'spacefor.h'\" \(744 characters\)
sed "s/^X//" >'spacefor.h' <<'END_OF_FILE'
X/*
X * spacefor.h - for spacefor.c
X */
X
X#define	UUCP_DIR	"/usr/spool/uucp"   /* Your uucp spool directory */
X/*
X * #undef HAVESTATFS for Ultrix; it's got one, but not what we want to use
X */
X#define HAVESTATFS			    /* have the statfs() call? */
X
X/*
X * If you're a Berkeley-based system & have 1024-byte blocks, or non-Berkeley
X * with 512-byte blocks, you don't have to do anything. If you've got a
X * special case, go below and do as directed.
X */
X#ifdef BSD
X#define BLK_SIZE        1024
X#else /* !BSD */
X#define BLK_SIZE        512
X#endif /* !BSD */
X/*
X * Uncomment these and replace custom with whatever value you need, if it
X * isn't 1024 on a Berkeley system or 512 on other types.
X */
X/*#undef BLKSIZE
X/*#define BLKSIZE         custom */
END_OF_FILE
if test 744 -ne `wc -c <'spacefor.h'`; then
    echo shar: \"'spacefor.h'\" unpacked with wrong size!
fi
# end of 'spacefor.h'
fi
if test -f 'spacefor.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'spacefor.c'\"
else
echo shar: Extracting \"'spacefor.c'\" \(3340 characters\)
sed "s/^X//" >'spacefor.c' <<'END_OF_FILE'
X/*
X * spacefor - spacefor.c - Brendan Kehoe
X *
X * a replacement for the spacefor shell script
X *
X * call with (e.g.):   spacefor.stub 1 articles [size]
X * you can specify the minimum amount of space you want left with the
X *  optional size argument (without a size argument, it uses the defaults
X *  that were in the old anne.jones
X *
X * NEWSCTL and NEWSARTS have to be exported into the environment before
X *  this is called for it to work
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#ifdef HAVE_STATFS
X#include <stdlib.h>
X#include <sys/vfs.h>
X#else /* !HAVE_STATFS */
X#include <sys/stat.h>
X#include <ustat.h>
X#endif /* HAVE_STATFS */
X#include "spacefor.h"
X
int
main(argc, argv)
X     int argc;
X     char **argv;
X{
X#ifdef HAVE_STATFS
X  struct statfs b;
X#else /* !HAVESTATFS */
X  struct stat b;
X  struct ustat device;
X#endif /* HAVESTATFS */
X  int nb, desire = 0, num_items;
X  char *arg, path_buf[100], *news_arts, *news_ctl;
X  extern char *getenv();
X  
X  if (argc < 3)
X    exit(1);
X  if ((argc == 4) && (isdigit(**(argv + 3)))) {
X    desire = atoi(*(argv + 3));
X  }
X  
X  if ((news_ctl = getenv("NEWSCTL")) == (char *)NULL) {
X    perror("newsctl");
X    puts("0");
X    exit(1);
X  }
X
X  if ((news_arts = getenv("NEWSARTS")) == (char *)NULL) {
X    perror("newsarts");
X    puts("0");
X    exit(1);
X  }
X  
X  if ((num_items = atoi(*(argv+1))) == 0) {
X    puts("10000");
X    exit(0);
X  }
X  
X  switch (*(*(argv+2))) {
X  case 'i':
X    arg = path_buf;
X    (void) strcpy(arg, news_arts);
X    (void) strcat(arg,"/in.coming");
X    desire = (desire) ? desire : 5000;
X    break;
X  case 'c': arg = news_ctl;
X    desire = (desire) ? desire : 3000;
X    break;
X  case 'o': arg = UUCP_DIR;
X    desire = (desire) ? desire : 10000;
X    break;
X  case 'a':
X    switch (*(*(argv+2)+2)) {
X    case 't': arg = news_arts;
X      desire = (desire) ? desire : 5000;
X      break;
X    case 'c': arg = news_arts;
X      desire = (desire) ? desire : 1;
X      break;
X    default: (void) fprintf(stderr, "%s: bad type argument `%s'!!\n",
X			    *argv, *(argv+2));
X      puts("0");
X      exit(2);
X    }
X    break;
X  default: (void) fprintf(stderr, "%s: bad type argument `%s'!!\n",
X			  *argv, *(argv+2));
X    puts("0");
X    exit(2);
X  }
X  
X#ifdef HAVE_STATFS
X  if (statfs(arg, &b) < 0) {
X    nb = 0;
X    fprintf(stderr, "%s: Can't statfs() `%s'\n", *argv, arg);
X  } else
X    nb = ((int)b.f_bavail - desire) * BLK_SIZE / num_items;
X#ifdef DEBUG
X  printf("nb = (b.f_bavail - desire) * BLK_SIZE / num_items;\n");
X  printf("%d = (%ld - %d) * %d / %d;\n", nb, b.f_bavail, desire, BLK_SIZE, 
X	 num_items);
X#endif /* DEBUG */
X#else /* !HAVESTATFS */
X  if (stat(arg,&b)) {
X    nb = 0;         /* can't stat arg */
X    fprintf(stderr, "%s: Can't stat() `%s'\n", *argv, arg);
X  } else
X    if (ustat(b.st_dev, &device)) {
X      nb = 0;         /* can't stat the device */
X      fprintf(stderr, "%s: Can't ustat() `%s'\n", *argv, b.st_dev);
X    } else
X      nb = (device.f_tfree - desire) * BLK_SIZE / num_items;
X#ifdef DEBUG
X  printf("nb = (device.f_tfree - desire) * BLK_SIZE / num_items;\n");
X  printf("%d = (%d - %d) * %d / %d;\n", nb, device.f_tfree, desire, BLK_SIZE,
X	 num_items);
X#endif /* DEBUG */
X#endif /* HAVESTATFS */
X  
X  if (nb > 10000)
X    nb = 10000;
X  if (nb <= 0)
X    puts("0");
X  else
X    (void) printf("%d\n", nb);
X  return(0);	/* shut UP lint! */
X}
END_OF_FILE
if test 3340 -ne `wc -c <'spacefor.c'`; then
    echo shar: \"'spacefor.c'\" unpacked with wrong size!
fi
# end of 'spacefor.c'
fi
if test -f 'spacefor.sh' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'spacefor.sh'\"
else
echo shar: Extracting \"'spacefor.sh'\" \(1224 characters\)
sed "s/^X//" >'spacefor.sh' <<'END_OF_FILE'
X#! /bin/sh
X# spacefor - determine available disk space
X# About how many things of $1 bytes will fit in the available space for
X# stuff of type $2 ("incoming", "articles", "control", "outbound $3",
X# or "archive") without cramping things too badly?
X#
X# Note that right now the new spacefor does NOT work on systems that aren't able
X# to do a df on a directory to see through to its mount point (e.g. can
X# do it on /usr/lib to see /usr's filesystem).
X# Edit spacefor.h to adjust your block size & uucp directory if necessary.
X#
X# Thanks to ns@iddth.id.dk for the $* and $NEWSBIN/inject stuff.
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
export NEWSARTS NEWSCTL NEWSBIN
PATH=$NEWSCTL/bin:$NEWSBIN:$NEWSBIN/inject:$NEWSPATH ; export PATH
umask $NEWSUMASK
X
X# punt to server if necessary
if test -r $NEWSCTL/server
then
X	server="`cat $NEWSCTL/server`"
X	me="`hostname`"
X	if test " $server" != " $me"
X	then
X		exec rsh $server -n /bin/sh -c "'PATH=$PATH `basename $0` $*'"
X		# does not return
X	fi
fi
X#
X# now speed up inews a bit by doing this in a C program instead of using awk
X# $1 = # of items, $2 = type of item, and $3, how many blocks should be left
X#  free, is optional
spacefor.stub $*
END_OF_FILE
if test 1224 -ne `wc -c <'spacefor.sh'`; then
    echo shar: \"'spacefor.sh'\" unpacked with wrong size!
fi
chmod +x 'spacefor.sh'
# end of 'spacefor.sh'
fi
if test -f 'tear.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tear.c'\"
else
echo shar: Extracting \"'tear.c'\" \(1124 characters\)
sed "s/^X//" >'tear.c' <<'END_OF_FILE'
X/*
X * tear - tear.c - 10/04/90 - Brendan Kehoe
X *
X * a replacement for the tear shell script & awk procedure
X */
X
X#include <stdio.h>
X#include <string.h>
X
extern char	*safemalloc(), *get_a_line();
X
main(argc, argv)
X	int argc;
X	char **argv;
X{
X	register char *lp;
X	register int curline;
X	extern char *safemalloc(), *get_a_line();
X	char *line, *hdr, *body;
X	short inbody, doit;
X	FILE *fin, *fout;
X
X	line = safemalloc(1001);
X	hdr = safemalloc(100);
X	body = safemalloc(100);
X
X	if (argc < 2)
X		exit(1);
X
X	if (argc == 2)
X		fin = stdin;
X	else if ((fin = fopen(*(argv+2),"r"))==(FILE *)NULL) {
X		exit(1);
X	}
X
X	strcpy(hdr,*(argv+1));
X	strcpy(body,*(argv+1));
X	strcat(hdr,"hdr");
X	strcat(body,"body");
X	fout = fopen(hdr,"w");
X
X	lp=line;
X	inbody = doit = curline = 0;
X	while (get_a_line(line,1000,fin)) {
X		curline++;
X		if (inbody == 0) {
X			if ((*lp != ' ') && (*lp != '\t')) {
X				if ((!strchr(lp,':'))||
X				    ((curline>1)&&(*lp == '\n')))
X					doit = 1;
X			}
X			if (doit) {
X				inbody = 1;
X				fclose(fout);
X				fout = fopen(body,"w");
X			}
X		}
X		fputs(line,fout);
X	}
X	fclose(fout);
X	fclose(fin);
X	return(0);	/* shut UP lint! */
X}
END_OF_FILE
if test 1124 -ne `wc -c <'tear.c'`; then
    echo shar: \"'tear.c'\" unpacked with wrong size!
fi
# end of 'tear.c'
fi
echo shar: End of shell archive.
exit 0
-- cut here --
Brendan Kehoe | Soon: brendan@cs.widener.edu [ Halloween IX: The Router Ghost ]
For now: kehoe@scotty.dccs.upenn.edu | Also: brendan.kehoe@cyber.widener.edu
  "The latest polls indicate you're in danger of losing touch with the
	     common man."   "Oh DEAR ... heaven forfend!"