[comp.sources.misc] v14i094: rn patch to identify posters of articles

em@dce.ie (Eamonn McManus) (09/16/90)

Posting-number: Volume 14, Issue 94
Submitted-by: em@dce.ie (Eamonn McManus)
Archive-name: rn-comments/part01

This patch to rn is intended to solve the problem where you are looking
at a news article and think, "Isn't that the person who said...?"  It allows
you to have a file containing a set of entries, each of which consists of a
person's address and an associated comment.  The comment is printed out
under the From line of any article by that person.  So if someone posts
something really stupid (or really clever) you can arrange for all their
subsequent articles to be branded (or emblazoned) with your own personal
prejudice against (or for) that person.

#! /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:  RNPREJUDICE RNPREJ.PATCH checkfrom.c stb.c stb.h
# Wrapped by em@dce.ie on Tue Sep 11 23:38:38 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f RNPREJUDICE -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"RNPREJUDICE\"
else
echo shar: Extracting \"RNPREJUDICE\" \(3995 characters\)
sed "s/^X//" >RNPREJUDICE <<'END_OF_RNPREJUDICE'
XABOUT RNPREJUDICE
X
XThe rnprejudice patch is intended to solve the problem where you are looking
Xat a news article and think, "Isn't that the person who said...?"  It allows
Xyou to have a file containing a set of entries, each of which consists of a
Xperson's address and an associated comment.  The comment is printed out
Xunder the From line of any article by that person.  So if someone posts
Xsomething really stupid (or really clever) you can arrange for all their
Xsubsequent articles to be branded (or emblazoned) with your own personal
Xprejudice against (or for) that person.
X
XThe modified rn looks in the RNPREJUDICE environment variable for the name
Xof the prejudice file.  You can define this variable in your login script,
Xor via the -E option to rn.
X
XLines in the prejudice file beginning with # are comments.  Unindented lines
Xcontain mail addresses which are checked against the From line of each
Xarticle.  If a match is found, the indented lines following the match will
Xbe printed.  The From line must match exactly, i.e., the case of letters
Xmust be the same.  The parenthesised personal name is ignored in the match.
X
X# Here is an example rnprejudice file.
Xem@dce.ie
X	Author of rnprejudice patch
Xemcmanus@cs.tcd.ie
X	Author of rnprejudice patch
X# If someone has multiple addresses, they must have multiple entries.
Xquestionable@severed-head.com
X	Believes that the Earth is flat, that skyscapers are the remnants of
X	a lost civilisation, and that Reagan wasn't so bad after all.
XBIFF@BIT.NET
X	Classic BIFF
X
XIn your .rnmac file you might define a macro like this:
X# Edit the rnprejudice file
X*	!echo '%t' >>$RNPREJUDICE; vi + $RNPREJUDICE\n
XThis appends the e-mail address from the current article to the prejudice
Xfile and starts you up in the editor, where you can add the comment.  When
Xyou return from the edit, rn will notice that the file has been edited and
Xread in the new copy.
X
X
XAPPLYING THE PATCH
X
XIn the rn source directory, feed the file RNPREJ.PATCH to the patch program.
XThis makes a small change to art.c so that it calls a function to look up
Xthe From line of an article after displaying it, and modifies the Makefile
Xto know about the new files checkfrom.c, stb.c, and stb.h.  Because the
Xchanges are so small, they should work with most versions of rn.  The patch
Xwas derived from version 4.3.2.2, patchlevel 44.
X
X"make rn" should recompile rn without problems.  If the new files fail to
Xcompile, mail me <em@dce.ie> with a description of the problem.
X
X
XIMPLEMENTATION NOTES
X
XSince the prejudice entries are kept entirely in memory, the patched rn
Xprobably won't run on machines with small address spaces.
X
XThe in-memory data are handled by the code in stb.c.  This uses a binary
Xtree for each possible initial letter.  If you keep your rnprejudice file
Xin alphabetical order, performance will be bad.  A hash table might be
Xbetter, but I wanted to avoid a big overhead when the prejudice file is
Xsmall.  (In my experience it grows pretty quickly, though.)  It would be
Xeasy to replace this file with a different implementation.
X
XThere are some things that could be improved:
X* Lookups should not be case-sensitive, at least for the domain portion of
X  the address.  The addresses in people's postings tend to show up with
X  consistent case however.
X* It would be convenient if you could have different aliases for the same
X  person, rather than having to enter the same prejudice multiple times as
X  at present.  Each block of unindented lines could be taken as a set of
X  addresses for the same person.
X* It might be better to look up the name in the parenthesised comment instead
X  of or as well as the mail address.
X* If you change the RNPREJUDICE environment variable within rn (using &-E)
X  the code does not notice.  This could be fixed, but I don't see any need
X  at present.
X
X
XSTATUS
X
XThis patch is by Eamonn McManus, Datacode Communications Ltd <em@dce.ie>.
XIt is not copyrighted and may be used freely.
X
X$Id: RNPREJUDICE,v 1.1 90/09/11 23:01:34 em Exp $
END_OF_RNPREJUDICE
if test 3995 -ne `wc -c <RNPREJUDICE`; then
    echo shar: \"RNPREJUDICE\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f RNPREJ.PATCH -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"RNPREJ.PATCH\"
else
echo shar: Extracting \"RNPREJ.PATCH\" \(4738 characters\)
sed "s/^X//" >RNPREJ.PATCH <<'END_OF_RNPREJ.PATCH'
X*** art.c.old	Thu Jan 25 20:51:50 1990
X--- art.c	Tue Sep 11 21:09:11 1990
X***************
X*** 193,198 ****
X--- 193,201 ----
X  		restart = Nullch;	/* and reset the flag */
X  	    }
X  	    else {			/* not a restart */
X+ 		/* em@dce.ie: look up From in prejudice database. */
X+ 		if (in_header == FROM_LINE)
X+ 			linenum += checkfrom(art_buf);
X  		if (fgets(art_buf,LBUFLEN,artfp)==Nullch) {
X  					/* if all done */
X  		    mode = oldmode;
X*** Makefile.old	Sat Jan 20 18:15:38 1990
X--- Makefile	Mon Aug 13 22:32:49 1990
X***************
X*** 55,61 ****
X  h1 = addng.h art.h artio.h artsrch.h backpage.h bits.h cheat.h common.h
X  h2 = final.h head.h help.h init.h intrp.h kfile.h last.h ndir.h ng.h
X  h3 = ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h
X! h4 = respond.h rn.h search.h sw.h term.h util.h
X  
X  h = $(h1) $(h2) $(h3) $(h4) $(h5)
X  
X--- 55,61 ----
X  h1 = addng.h art.h artio.h artsrch.h backpage.h bits.h cheat.h common.h
X  h2 = final.h head.h help.h init.h intrp.h kfile.h last.h ndir.h ng.h
X  h3 = ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h
X! h4 = respond.h rn.h search.h sw.h term.h util.h stb.h
X  
X  h = $(h1) $(h2) $(h3) $(h4) $(h5)
X  
X***************
X*** 62,68 ****
X  c1 = addng.c art.c artio.c artsrch.c backpage.c bits.c cheat.c
X  c2 = final.c head.c help.c init.c intrp.c kfile.c last.c $(NDIRC) ng.c
X  c3 = ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c
X! c4 = respond.c rn.c search.c sw.c term.c util.c
X  
X  c = $(c1) $(c2) $(c3) $(c4) $(c5)
X  
X--- 62,68 ----
X  c1 = addng.c art.c artio.c artsrch.c backpage.c bits.c cheat.c
X  c2 = final.c head.c help.c init.c intrp.c kfile.c last.c $(NDIRC) ng.c
X  c3 = ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c
X! c4 = respond.c rn.c search.c sw.c term.c util.c checkfrom.c stb.c
X  
X  c = $(c1) $(c2) $(c3) $(c4) $(c5)
X  
X***************
X*** 69,75 ****
X  obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o
X  obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO) ng.o
X  obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o
X! obj4 = respond.o rn.o search.o sw.o term.o util.o
X  
X  obj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5)
X  
X--- 69,75 ----
X  obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o
X  obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO) ng.o
X  obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o
X! obj4 = respond.o rn.o search.o sw.o term.o util.o checkfrom.o stb.o
X  
X  obj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5)
X  
X*** Makefile.SH.old	Sat Jan 20 17:57:51 1990
X--- Makefile.SH	Mon Aug 13 16:26:13 1990
X***************
X*** 64,70 ****
X  h1 = addng.h art.h artio.h artsrch.h backpage.h bits.h cheat.h common.h
X  h2 = final.h head.h help.h init.h intrp.h kfile.h last.h ndir.h ng.h
X  h3 = ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h
X! h4 = respond.h rn.h search.h sw.h term.h util.h
X  #NNTPh5 = server.h
X  
X  h = $(h1) $(h2) $(h3) $(h4) $(h5)
X--- 64,70 ----
X  h1 = addng.h art.h artio.h artsrch.h backpage.h bits.h cheat.h common.h
X  h2 = final.h head.h help.h init.h intrp.h kfile.h last.h ndir.h ng.h
X  h3 = ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h
X! h4 = respond.h rn.h search.h sw.h term.h util.h stb.h
X  #NNTPh5 = server.h
X  
X  h = $(h1) $(h2) $(h3) $(h4) $(h5)
X***************
X*** 72,78 ****
X  c1 = addng.c art.c artio.c artsrch.c backpage.c bits.c cheat.c
X  c2 = final.c head.c help.c init.c intrp.c kfile.c last.c $(NDIRC) ng.c
X  c3 = ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c
X! c4 = respond.c rn.c search.c sw.c term.c util.c
X  #NNTPc5 = $(NNTPDIR)/common/clientlib.c
X  
X  c = $(c1) $(c2) $(c3) $(c4) $(c5)
X--- 72,78 ----
X  c1 = addng.c art.c artio.c artsrch.c backpage.c bits.c cheat.c
X  c2 = final.c head.c help.c init.c intrp.c kfile.c last.c $(NDIRC) ng.c
X  c3 = ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c
X! c4 = respond.c rn.c search.c sw.c term.c util.c checkfrom.c stb.c
X  #NNTPc5 = $(NNTPDIR)/common/clientlib.c
X  
X  c = $(c1) $(c2) $(c3) $(c4) $(c5)
X***************
X*** 80,86 ****
X  obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o
X  obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO) ng.o
X  obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o
X! obj4 = respond.o rn.o search.o sw.o term.o util.o
X  #NNTPobj5 =  $(NNTPDIR)/common/clientlib.o
X  
X  obj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5)
X--- 80,86 ----
X  obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o
X  obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o $(NDIRO) ng.o
X  obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o
X! obj4 = respond.o rn.o search.o sw.o term.o util.o checkfrom.o stb.o
X  #NNTPobj5 =  $(NNTPDIR)/common/clientlib.o
X  
X  obj = $(obj1) $(obj2) $(obj3) $(obj4) $(obj5)
END_OF_RNPREJ.PATCH
if test 4738 -ne `wc -c <RNPREJ.PATCH`; then
    echo shar: \"RNPREJ.PATCH\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f checkfrom.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"checkfrom.c\"
else
echo shar: Extracting \"checkfrom.c\" \(4004 characters\)
sed "s/^X//" >checkfrom.c <<'END_OF_checkfrom.c'
X/* checkfrom.c - print information about news article originator. */
X
X/* By Eamonn McManus, <em@dce.ie>.  This file is not copyrighted.
X * $Id: checkfrom.c,v 1.2 90/09/11 23:37:39 em Exp $
X *
X * This file contains the checkfrom() function, invoked from (modified) rn
X * after a From: header line to see if the person named is mentioned in the
X * RNPREJUDICE file.  If so, the text for that person is printed, and the
X * number of lines that were printed is returned.  If not, 0 is returned.
X *
X * Each key in the file is on a line of its own,
X * followed by zero or more data lines starting with whitespace.  Lines
X * beginning with # are comments, as are leading indented lines.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "config.h"
X#ifdef __STDC__
X#include <stdlib.h>
X#include <string.h>
X#undef index
X#define index strchr
X#else
Xextern char *malloc();
Xextern void free();
Xextern char *getenv();
Xextern char *index();
X#endif
X
Xtypedef char *datum;
X#include "stb.h"
X
X
Xextern char *savestr ARGS((char *str));
X
Xstatic stb *db;
X
X
X/* Read the file into memory.  Return the number of keys read, or -1 on error.
X */
Xstatic int read_file(f)
Xregister FILE *f;
X{
X	register int c, i;
X	int nkeys;
X	char key[1024];		/* Hardcoded limits; tough. */
X	char data[4096], *datacopy;
X	if (db == NULL && (db = stb_new_table()) == NULL)
X		return -1;
X	nkeys = 0;
X	/* Each iteration reads a key and following data. */
X	while ((c = getc(f)) != EOF) {
X		/* Get the key. */
X		for (i = 0; i < sizeof key - 1 && c != '\n'; c = getc(f)) {
X			if (c == EOF)
X				return nkeys;
X			key[i++] = c;
X		}
X		if (i == 0 || key[0] == '#' || isspace(key[0]))
X			continue;
X		key[i] = '\0';
X		/* Get the data. */
X		i = 0;
X		/* Read the indented lines. */
X		while (isspace(c = getc(f))) {
X			/* Read a line. */
X			while (1) {
X				if (c == EOF)
X					return nkeys;
X				if (i < sizeof data - 1)
X					data[i++] = c;
X				if (c == '\n')
X					break;
X				c = getc(f);
X			}
X		}
X		(void) ungetc(c, f);
X		data[i] = '\0';
X		datacopy = savestr(data);
X		if (stb_insert(db, key, &datacopy) == NULL) {
X			free(datacopy);
X		} else nkeys++;
X	}
X	return nkeys;
X}
X
X
X/* Action function for stb_free_tree. */
Xstatic int sfree(p)
Xchar *p;
X{
X	free(p);
X	return 0;
X}
X
X
X/* See if the electronic address in the fromline appears in the prejudice
X * database.  If so, print the indented lines that follow.  Return the
X * number of lines printed.  The string pointed to by fromline is modified.
X */
Xint checkfrom(fromline)
Xregister char *fromline;
X{
X	register char *p, *dbname;
X	register stb_node *s;
X	int lines, t;
X	static char beenhere;
X	static FILE *f;
X	static time_t mtime;
X	struct stat st;
X	/* Slurp in the database if we haven't already, or if it has been
X	 * modified since we read it.
X	 */
X	if (beenhere) {
X		if (f == NULL)
X			return 0;
X		if ((t = fstat(fileno(f), &st)) < 0 || st.st_mtime > mtime) {
X			(void) stb_free_table(db, sfree); db = NULL;
X			if (t < 0) {
X				fclose(f); f = NULL;
X				return 0;
X			} else {
X				rewind(f);
X				mtime = st.st_mtime;
X				beenhere = 0;
X			}
X		}
X	} else {
X		if ((dbname = getenv("RNPREJUDICE")) == NULL)
X			return 0;
X		if ((f = fopen(dbname, "r")) == NULL ||
X		    fstat(fileno(f), &st) < 0) {
X			printf("[Could not open RNPREJUDICE=\"%s\"]\n", dbname);
X			f = NULL;
X			return 1;
X		}
X		mtime = st.st_mtime;
X	}
X	if (!beenhere) {
X		beenhere++;
X		if (read_file(f) < 0) {
X			printf("[Format error in $RNPREJUDICE]\n");
X			fclose(f); f = NULL;
X			return 1;
X		}
X	}
X	/* Isolate the address part of the from line.  Skip the initial
X	 * "From:" as well as any trailing stuff (usually the personal
X	 * name comment).
X	 */
X	for ( ; *fromline && !isspace(*fromline); fromline++) ;
X	for ( ; isspace(*fromline); fromline++) ;
X	for (p = fromline; *p && !isspace(*p); p++) ;
X	if (p == fromline)
X		return 0;
X	*p = '\0';
X	if ((s = stb_lookup(db, fromline)) == NULL)
X		return 0;
X	fputs(s->datum, stdout);
X	for (lines = 0, p = s->datum; (p = index(p, '\n')) != NULL;
X	     lines++, p++) ;
X	return lines;
X}
END_OF_checkfrom.c
if test 4004 -ne `wc -c <checkfrom.c`; then
    echo shar: \"checkfrom.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f stb.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"stb.c\"
else
echo shar: Extracting \"stb.c\" \(6095 characters\)
sed "s/^X//" >stb.c <<'END_OF_stb.c'
X/* stb.c - symbol table functions */
X
X/* By Eamonn McManus <em@dce.ie>.  This file is not copyrighted.
X * $Id: stb.c,v 1.2 90/09/11 23:37:49 em Exp $
X *
X * Functions using a simple binary forest to implement symbol table
X * operations.  Should be recompiled with DATUM defined on the command
X * line as a declaration for variable datum of suitable type.  Default
X * is equivalent to -DDATUM="char *datum".
X */
X
X#ifndef DATUM
X#define DATUM char *datum
X#endif
X
Xtypedef DATUM;
X
X#include "stb.h"
X#ifdef __STDC__
X#include <string.h>	/* For strcpy(). */
X#include <stdlib.h>
X#else
Xextern char *malloc();
Xextern char *strcpy();
Xextern void free();
X#endif
X
X#ifndef NULL
X# define NULL 0
X#endif
X
X
X/* Return a new symbol table.  NULL if can't make one. */
Xstb *stb_new_table()
X{
X	/* Note that calloc() to get an array of NULLs is not portable. */
X	register int i;
X	register stb *s = (stb *) malloc(sizeof(stb));
X	for (i = 0; i < MAXCHAR; i++)
X		(*s)[i] = NULL;
X	return s;
X}
X
X
X/* Free tree rooted at root, using function datumfree. */
Xstatic int free_tree(root, datumfree)
Xregister stb_node *root;
Xregister int (*datumfree)();
X{
X	register stb_node *n;
X	for ( ; root != NULL; root = n) {
X		if (free_tree(root->link[0], datumfree) < 0)
X			return -1;
X		n = root->link[1];
X		if ((*datumfree)(root->datum) < 0)
X			return -1;
X		free((char *) root->name);
X		free((char *) root);
X	}
X	return 0;
X}
X
X
X/* Free a symbol table.  Return 0 if OK, -1 if error.  Datumfree is a
X * routine to free the datum in each node, returning the same error status.
X */
Xint stb_free_table(table, datumfree)
Xregister stb *table;
Xregister int (*datumfree)();
X{
X	register int i;
X	for (i = 0; i < MAXCHAR; i++)
X		if (free_tree((*table)[i], datumfree) < 0)
X			return -1;
X	free((char *) table);
X	return 0;
X}
X
X
X/* Look up a symbol.  Return pointer to its node or NULL if not found. */
Xstb_node *stb_lookup(table, name)
Xstb *table;
Xregister char *name;
X{
X	register stb_node *n;
X	register int order;
X	if (name[0] == '\0')
X		return (*table)['\0'];	/* Only one null symbol. */
X	if ((unsigned char) name[0] >= MAXCHAR)
X		return NULL;		/* Invalid. */
X	for (n = (*table)[(unsigned char) *name++]; n; n = n->link[order > 0])
X		if ((order = strcmp(name, n->name + 1)) == 0)
X			return n;
X	return NULL;
X}
X
X
X/* Insert a symbol.  Returns the inserted node, or NULL if there was an
X * error or the symbol was already present.
X */
Xstb_node *stb_insert(table, name, newdatum)
Xstb *table;
Xregister char *name;
Xdatum *newdatum;
X{
X	register stb_node *parent, *n, **link;
X	register int order;
X	register char *namecpy;
X	if ((unsigned char) *name >= MAXCHAR)
X		return NULL;		/* Invalid. */
X	parent = NULL;
X	link = *table + (unsigned char) name[0];
X	n = *link;
X	if (*name++) {			/* Only one null symbol, no search. */
X		while (n != NULL) {
X			if ((order = strcmp(name, n->name + 1)) == 0)
X				break;
X			link = &n->link[order > 0];
X			parent = n; n = *link;
X		}
X	}
X	if (n != NULL)
X		return NULL;		/* Already present. */
X	if ((namecpy = malloc((unsigned) strlen(--name) + 1)) == NULL)
X		return NULL;		/* Out of memory. */
X	(void) strcpy(namecpy, name);
X	if ((n = (stb_node *) malloc(sizeof(stb_node))) == NULL)
X		return NULL;
X	n->link[0] = n->link[1] = NULL;
X	n->parent = parent; n->name = namecpy; n->datum = *newdatum;
X	*link = n;
X	return n;
X}
X
X
X/* Subsequent functions are not used in the rnprejudice code. */
X#ifdef UNPREJUDICED
X
X/* Return lowest valued descendant of node. */
Xstatic stb_node *first(node)
Xregister stb_node *node;
X{
X	while (node->link[0] != NULL)
X		node = node->link[0];
X	return node;
X}
X
X
X/* Return first symbol in table, NULL if table is empty. */
Xstb_node *stb_first(table)
Xregister stb *table;
X{
X	register int i;
X	for (i = 0; i < MAXCHAR; i++)
X		if ((*table)[i] != NULL)
X			return first((*table)[i]);
X	return NULL;
X}
X
X
X/* Return the next symbol after the given node, NULL if none. */
Xstb_node *stb_next(table, node)
Xstb *table;
Xregister stb_node *node;
X{
X	register int i;
X	/* If there is a right pointer, return its smallest descendant.
X	 * If this is the left descendant of a node, return that node.
X	 * Otherwise, we want that node's next.
X	 */
X	if (node->link[1] != NULL)
X		return first(node->link[1]);
X	for ( ; node->parent != NULL; node = node->parent)
X		if (node->parent->link[0] == node)
X			return node->parent;
X	/* At root for this initial char.  Find next tree. */
X	for (i = node->name[0] + 1; i < MAXCHAR; i++)
X		if ((*table)[i])
X			return first((*table)[i]);
X	return NULL;
X}
X
X
X/* Delete a node.  Return 0 if OK, else -1.  Datumfree is a function to
X * free the datum part of the node, returning the same kind of status.
X */
Xint stb_delete(table, node, datumfree)
Xstb *table;
Xregister stb_node *node;
Xint (*datumfree)();
X{
X	register stb_node **link, *succ;
X	/* Find the link from the parent that we want to change. */
X	if (node->parent == NULL)
X		link = *table + node->name[0];	/* In the array of roots. */
X	else	link = &node->parent->link[node->parent->link[1] == node];
X	/* If either descendant is NULL, replace with the other.  Otherwise
X	 * find the successor and replace with that.
X	 */
X	if (node->link[0] == NULL)
X		*link = node->link[1];
X	else if (node->link[1] == NULL)
X		*link = node->link[0];
X	else {
X		succ = first(node->link[1]);
X		/* Replace the deleted node with its successor, succ.  Succ
X		 * has no left child (since that would be the successor), so
X		 * it inherits the original node's left child.
X		 * If succ is the right child of the original node, that is
X		 * enough.  Otherwise, succ's parent
X		 * inherits succ's right child as its left child (which was
X		 * succ), and succ inherits the original node's right child.
X		 */
X		succ->link[0] = node->link[0]; node->link[0]->parent = succ;
X		if (succ->parent != node) {
X			succ->parent->link[0] = succ->link[1];
X			if (succ->link[1] != NULL)
X				succ->link[1]->parent = succ->parent;
X			succ->link[1] = node->link[1];
X				node->link[1]->parent = succ;
X		}
X		*link = succ;
X	}
X	if (*link != NULL)
X		(*link)->parent = node->parent;
X	if ((*datumfree)(node->datum) < 0)
X		return -1;
X	free((char *)node->name); free((char *)node);
X	return 0;
X}
X
X#endif	/* UNPREJUDICED */
END_OF_stb.c
if test 6095 -ne `wc -c <stb.c`; then
    echo shar: \"stb.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f stb.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"stb.h\"
else
echo shar: Extracting \"stb.h\" \(871 characters\)
sed "s/^X//" >stb.h <<'END_OF_stb.h'
X/* stb.h - declarations for symbol-table package stb.c. */
X
X/* By Eamonn McManus <em@dce.ie>.  This file is not copyrighted.
X * $Id: stb.h,v 1.2 90/09/11 23:38:07 em Exp $
X */
X
X#ifndef MAXCHAR
X# define MAXCHAR 256
X#endif
X
Xtypedef struct stb_node {
X	struct stb_node *link[2], *parent;
X	char *name;
X	datum datum;		/* datum must be typedef'd first. */
X} stb_node;
X
Xtypedef stb_node *stb[MAXCHAR];
X
X#ifndef ARGS
X# ifndef __STDC__
X#  define ARGS(x) ()
X# else
X#  define ARGS(x) x
X# endif
X#endif
X
Xstb *stb_new_table ARGS((void));
Xint stb_free_table ARGS((stb *table, int (*datumfree)()));
Xstb_node *stb_lookup ARGS((stb *table, char *name));
Xstb_node *stb_insert ARGS((stb *table, char *name, datum *newdatum));
Xstb_node *stb_first ARGS((stb *table));
Xstb_node *stb_next ARGS((stb *table, stb_node *node));
Xint stb_delete ARGS((stb *table, stb_node *node, int (*datumfree)()));
END_OF_stb.h
if test 871 -ne `wc -c <stb.h`; then
    echo shar: \"stb.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0