[net.sources] rn version 4.1, part 4 of 8

lwall@sdcrdcf.UUCP (Larry Wall) (10/13/84)

#! /bin/sh

# Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 
# through sh.  When all 8 kits have been run, read README.

echo "This is rn kit 4 (of 8).  If kit 4 is complete, the line"
echo '"'"End of kit 4 (of 8)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting intrp.c
cat >intrp.c <<'!STUFFY!FUNK!'
/* $Header: intrp.c,v 4.1 84/09/24 11:57:13 lwall Exp $
 *
 * $Log:	intrp.c,v $
 * Revision 4.1  84/09/24  11:57:13  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.4  84/09/19  17:08:13  lwall
 * Ifdef'ed some stuff that should have been.
 * 
 * Revision 4.0.1.3  84/09/12  17:36:37  lwall
 * pwd.h now included by common.h.
 * 
 * Revision 4.0.1.2  84/09/10  15:13:49  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/04  15:14:39  lwall
 * LINKART option for Eunice sites.
 * 
 * Revision 4.0  84/09/04  09:50:48  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "search.h"
#include "head.h"
#include "rn.h"
#include "artsrch.h"
#include "ng.h"
#include "util.h"
#include "respond.h"
#include "rcstuff.h"
#include "bits.h"
#include "artio.h"
#include "INTERN.h"
#include "intrp.h"

char orgname[] = ORGNAME;

/* name of this site */
#ifdef GETHOSTNAME
    char *hostname;
#   undef SITENAME
#   define SITENAME hostname
#else !GETHOSTNAME
#   ifdef DOUNAME
#	include <sys/utsname.h>
	struct utsname uts;
#	undef SITENAME
#	define SITENAME uts.sysname
#   else !DOUNAME
#	ifdef PHOSTNAME
	    char *hostname;
#	    undef SITENAME
#	    define SITENAME hostname
#	else !PHOSTNAME
#	    ifdef WHOAMI
#		undef SITENAME
#		define SITENAME sysname
#	    endif WHOAMI
#	endif PHOSTNAME
#   endif DOUNAME
#endif GETHOSTNAME

#ifdef TILDENAME
static char *tildename = Nullch;
static char *tildedir = Nullch;
#endif

char *realname INIT(Nullch);	/* real name of sender from /etc/passwd */

char *dointerp();
char *getrealname();

void
intrp_init(tcbuf)
char *tcbuf;
{
    char *getlogin();

    lib = savestr(filexp(LIB));
    rnlib = savestr(filexp(RNLIB));
    spool = savestr(filexp(SPOOL));	/* usually /usr/spool/news */

    getwd(tcbuf);			/* find working directory name */
    origdir = savestr(tcbuf);		/* and remember it */
    
    /* get environmental stuff */

    /* get home directory */

    homedir = getenv("HOME");
    if (homedir == Nullch)
	homedir = getenv("LOGDIR");

    dotdir = getval("DOTDIR",homedir);

    /* get the real name of the person (%N) */

    strcpy(tcbuf,getrealname(getuid()));
    realname = savestr(tcbuf);

    /* get login name */

    logname = getenv("USER");
    if (logname == Nullch)
	logname = getenv("LOGNAME");
#ifdef GETLOGIN
    if (logname == Nullch)
	logname = savestr(getlogin());
#endif

    /* name of header file (%h) */

    headname = savestr(filexp(HEADNAME));

    /* name of this site (%H) */

#ifdef GETHOSTNAME
    gethostname(buf,sizeof buf);
    hostname = savestr(buf);
#else
#ifdef DOUNAME
    /* get sysname */
    uname(&uts);
#else
#ifdef PHOSTNAME
    {
	FILE *popen();
	FILE *pipefp = popen(PHOSTNAME,"r");

	fgets(buf,sizeof buf,tmpfp);
	buf[strlen(buf)-1] = '\0';	/* wipe out newline */
	hostname = savestr(buf);
	pclose(pipefp);
    }
#endif
#endif
#endif
    sitename = savestr(SITENAME);
}

/* expand filename via %, ~, and $ interpretation */
/* returns pointer to static area */
/* Note that there is a 1-deep cache of ~name interpretation */

char *
filexp(s)
register char *s;
{
    static char filename[MAXFILENAME];
    char scrbuf[MAXFILENAME];
    register char *d;

#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("< %s\n",s);
#endif
    interp(filename,s);			/* interpret any % escapes */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("%% %s\n",filename);
#endif
    s = filename;
    if (*s == '~') {	/* does destination start with ~? */
	if (!*(++s) || *s == '/') {
	    sprintf(scrbuf,"%s%s",homedir,s);
				/* swap $HOME for it */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("~ %s\n",scrbuf);
#endif
	    strcpy(filename,scrbuf);
	}
	else {
#ifdef TILDENAME
	    for (d=scrbuf; isalnum(*s); s++,d++)
		*d = *s;
	    *d = '\0';
	    if (tildedir && strEQ(tildename,scrbuf)) {
		strcpy(scrbuf,tildedir);
#ifdef DEBUGGING
		if (debug & DEB_FILEXP)
		    printf("r %s %s\n",tildename,tildedir);
#endif
	    }
	    else {
		if (tildename) {
		    free(tildename);
		    free(tildedir);
		    tildedir = Nullch;
		    tildename = savestr(scrbuf);
		}
#ifdef GETPWENT		/* getpwnam() is not the paragon of efficiency */
		{
		    struct passwd *getpwnam();
		    struct passwd *pwd = getpwnam(tildename);

		    sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
		    tildedir = savestr(pwd->pw_dir);
#ifdef NEWSADMIN
		    if (strEQ(newsadmin,tildename))
			newsuid = atoi(pwd->pw_uid);
#endif
		    strcpy(filename,scrbuf);
#ifdef GETPWENT
		    endpwent();
#endif
		}
#else			/* this will run faster, and is less D space */
		{	/* just be sure LOGDIRFIELD is correct */
		    FILE *pfp = fopen("/etc/passwd","r");
		    char tmpbuf[512];
		    int i;
		    
		    while (fgets(tmpbuf,512,pfp) != Nullch) {
			d = cpytill(scrbuf,tmpbuf,':');
#ifdef DEBUGGING
			if (debug & DEB_FILEXP)
			    printf("p %s\n",tmpbuf);
#endif
			if (strEQ(scrbuf,tildename)) {
#ifdef NEWSADMIN
			    if (strEQ(newsadmin,tildename))
				newsuid = atoi(index(d,':')+1);
#endif
			    for (i=LOGDIRFIELD-2; i; i--) {
				if (d)
				    d = index(d+1,':');
			    }
			    if (d) {
				cpytill(scrbuf,d+1,':');
				tildedir = savestr(scrbuf);
				strcat(scrbuf,s);
				strcpy(filename,scrbuf);
			    }
			    break;
			}
		    }
		    fclose(pfp);
		}
#endif
	    }
#else !TILDENAME
#ifdef VERBOSE
	    IF(verbose)
		fputs("~loginname not implemented.\n",stdout);
	    ELSE
#endif
#ifdef TERSE
		fputs("~login not impl.\n",stdout);
#endif
#endif
	}
    }
    else if (*s == '$') {	/* starts with some env variable? */
	d = scrbuf;
	*d++ = '%';
	if (s[1] == '{')
	    strcpy(d,s+2);
	else {
	    *d++ = '{';
	    for (s++; isalnum(*s); s++) *d++ = *s;
				/* skip over token */
	    *d++ = '}';
	    strcpy(d,s);
	}
#ifdef DEBUGGING
	if (debug & DEB_FILEXP)
	    printf("$ %s\n",scrbuf);
#endif
	interp(filename,scrbuf);	/* this might do some extra '%'s but */
					/* that is how the Mercedes Benz */
    }
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("> %s\n",filename);
#endif
    return filename;
}

/* interpret interpolations */

char *
dointerp(dest,pattern,stoppers)
register char *dest;
register char *pattern;
char *stoppers;
{
    char *subj_buf = Nullch;
    char *ngs_buf = Nullch;
    char *refs_buf = Nullch;
    char *artid_buf = Nullch;
    char *reply_buf = Nullch;
    char *from_buf = Nullch;
    char *path_buf = Nullch;
    char *follow_buf = Nullch;
    char *dist_buf = Nullch;
    char *line_buf = Nullch;
    register char *s, *h;
    register int i;
    char scrbuf[512];
    bool upper = FALSE;
    bool lastcomp = FALSE;

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
	if (*pattern == '%') {
	    upper = FALSE;
	    lastcomp = FALSE;
	    for (s=Nullch; !s; ) {
		switch (*++pattern) {
		case '^':
		    upper = TRUE;
		    break;
		case '`':
		    lastcomp = TRUE;
		    break;
		case '/':
#ifdef ARTSRCH
		    s = scrbuf;
		    if (!index("/?g",pattern[-2]))
			*s++ = '/';
		    strcpy(s,lastpat);
		    s += strlen(s);
		    if (pattern[-2] != 'g') {
			if (index("/?",pattern[-2]))
			    *s++ = pattern[-2];
			else
			    *s++ = '/';
			if (art_howmuch == 1)
			    *s++ = 'h';
			else if (art_howmuch == 2)
			    *s++ = 'a';
			if (art_doread)
			    *s++ = 'r';
		    }
		    *s = '\0';
		    s = scrbuf;
#else
		    s = nullstr;
#endif
		    break;
		case '{':
		    pattern = cpytill(scrbuf,pattern+1,'}');
		    if (s = index(scrbuf,'-'))
			*s++ = '\0';
		    else
			s = nullstr;
		    s = getval(scrbuf,s);
		    break;
		case '[':
		    pattern = cpytill(scrbuf,pattern+1,']');
		    i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
		    if (line_buf)
			free(line_buf);
		    s = line_buf = fetchlines(art,i);
		    break;
#ifdef CONDSUB
		case '(': {
		    COMPEX *oldbra_compex = bra_compex;
		    COMPEX cond_compex;
		    char rch;
		    bool matched;
		    
		    init_compex(&cond_compex);
		    pattern = dointerp(dest,pattern+1,"!=");
		    rch = *pattern;
		    if (rch == '!')
			pattern++;
		    if (*pattern != '=')
			goto getout;
		    pattern = cpytill(scrbuf,pattern+1,'?');
		    if (!*pattern)
			goto getout;
		    if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
			printf("%s: %s\n",scrbuf,s);
			pattern += strlen(pattern);
			goto getout;
		    }
		    matched = (execute(&cond_compex,dest) != Nullch);
		    if (cond_compex.nbra)	/* were there brackets? */
			bra_compex = &cond_compex;
		    if (matched==(rch == '=')) {
			pattern = dointerp(dest,pattern+1,":)");
			if (*pattern == ':')
			    pattern = dointerp(scrbuf,pattern+1,")");
		    }
		    else {
			pattern = dointerp(scrbuf,pattern+1,":)");
			if (*pattern == ':')
			    pattern++;
			pattern = dointerp(dest,pattern,")");
		    }
		    s = dest;
		    bra_compex = oldbra_compex;
		    free_compex(&cond_compex);
		    break;
		}
#endif
		case '~':
		    s = homedir;
		    break;
		case '.':
		    s = dotdir;
		    break;
		case '$':
		    s = scrbuf;
		    sprintf(s,"%d",getpid());
		    break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
#ifdef CONDSUB
		    s = getbracket(bra_compex,*pattern - '0');
#else
		    s = nullstr;
#endif
		    break;
		case 'a':
		    s = scrbuf;
		    sprintf(s,"%ld",(long)art);
		    break;
		case 'A':
#ifdef LINKART
		    s = linkartname;	/* so Eunice people get right file */
#else
		    s = scrbuf;
		    sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
#endif
		    break;
		case 'b':
		    s = savedest;
		    break;
		case 'B':
		    s = scrbuf;
		    sprintf(s,"%ld",(long)savefrom);
		    break;
		case 'c':
		    s = ngdir;
		    break;
		case 'C':
		    s = ngname;
		    break;
		case 'd':
		    s = scrbuf;
		    sprintf(s,"%s/%s",spool,ngdir);
		    break;
		case 'D':
		    s = dist_buf = fetchlines(art,DIST_LINE);
		    break;
		case 'f':			/* from line */
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REPLY_LINE].ht_minpos >= 0) {
						/* was there a reply line? */
			if (!(s=reply_buf))
			    s = reply_buf = fetchlines(art,REPLY_LINE);
		    }
		    else if (!(s = from_buf))
			s = from_buf = fetchlines(art,FROM_LINE);
		    break;
		case 'F':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[FOLLOW_LINE].ht_minpos >= 0)
					/* is there a Followup-To line? */
			s = follow_buf = fetchlines(art,FOLLOW_LINE);
		    else {
			int off;
		
			s = ngs_buf = fetchlines(art,NGS_LINE);
			if (h = instr(s,"net.general")) {
			    off = h-s;
			    strncpy(scrbuf,s,off+4);
			    strcpy(scrbuf+off+4,"followup");
			    safecpy(scrbuf+off+12,h+11,sizeof(scrbuf));
			    s = scrbuf;
			}
		    }
		    break;
		case 'h':			/* header file name */
		    s = headname;
		    break;
		case 'H':			/* host name */
		    s = sitename;
		    break;
		case 'i':
		    if (!(s=artid_buf))
			s = artid_buf = fetchlines(art,ARTID_LINE);
		    if (*s != '<') {
			sprintf(scrbuf,"<%s>",artid_buf);
			s = scrbuf;
		    }
		    break;
		case 'l':			/* rn library */
#ifdef NEWSADMIN
		    s = newsadmin;
#else
		    s = "???";
#endif
		    break;
		case 'L':			/* login id */
		    s = logname;
		    break;
		case 'M':
#ifdef DELAYMARK
		    sprintf(scrbuf,"%ld",(long)dmcount);
		    s = scrbuf;
#else
		    s = nullstr;
#endif
		    break;
		case 'n':			/* newsgroups */
		    s = ngs_buf = fetchlines(art,NGS_LINE);
		    break;
		case 'N':			/* full name */
		    s = getval("NAME",realname);
		    break;
		case 'o':			/* organization */
		    s = getval("ORGANIZATION",orgname);
#ifdef ORGFILE
		    if (*s == '/') {
			FILE *ofp = fopen(s,"r");

			if (ofp) {
			    fgets(scrbuf,sizeof scrbuf,ofp);
			    fclose(ofp);
			    s = scrbuf;
			    s[strlen(s)-1] = '\0';
			}
		    }
#endif
		    break;
		case 'O':
		    s = origdir;
		    break;
		case 'p':
		    s = cwd;
		    break;
		case 'P':
		    s = spool;
		    break;
		case 'r':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REFS_LINE].ht_minpos >= 0) {
			refs_buf = fetchlines(art,REFS_LINE);
			refscpy(scrbuf,refs_buf);
		    }
		    else
			*scrbuf = '\0';
		    s = rindex(scrbuf,'<');
		    break;
		case 'R':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REFS_LINE].ht_minpos >= 0) {
			refs_buf = fetchlines(art,REFS_LINE);
			refscpy(scrbuf,refs_buf);
		    }
		    else
			*scrbuf = '\0';
		    if (!artid_buf)
			artid_buf = fetchlines(art,ARTID_LINE);
		    if (artid_buf[0] == '<')
			safecat(scrbuf,artid_buf,sizeof(scrbuf));
		    else {
			char tmpbuf[64];
    
			sprintf(tmpbuf,"<%s>",artid_buf);
			safecat(scrbuf,tmpbuf,sizeof(scrbuf));
		    }
		    s = scrbuf;
		    break;
		case 's':
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE,TRUE);
						/* get subject handy */
		    while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    if (h = instr(s,"- (nf"))
			*h = '\0';
		    break;
		case 'S':
		    if (!(s=subj_buf))
			s = subj_buf = fetchsubj(art,TRUE,TRUE);
						/* get subject handy */
		    if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
						/* skip extra Re: */
			s += 3;
			if (*s == ' ')
			    s++;
		    }
		    break;
		case 't':
		case 'T':
#ifdef ASYNC_PARSE
		    parse_maybe(art);
#endif
		    if (htype[REPLY_LINE].ht_minpos >= 0) {
					/* was there a reply line? */
			if (!(s=reply_buf))
			    s = reply_buf = fetchlines(art,REPLY_LINE);
		    }
		    else if (!(s = from_buf))
			s = from_buf = fetchlines(art,FROM_LINE);
		    if (*pattern == 'T') {
			if (htype[PATH_LINE].ht_minpos >= 0) {
					/* should we substitute path? */
			    s = path_buf = fetchlines(art,PATH_LINE);
			}
			i = strlen(sitename);
			if (strnEQ(sitename,s,i) && s[i] == '!')
			    s += i + 1;
		    }
		    if ((h=index(s,'(')) != Nullch)
						/* strip garbage from end */
			*(h-1) = '\0';
		    else if ((h=index(s,'<')) != Nullch) {
						/* or perhaps from beginning */
			s = h+1;
			if ((h=index(s,'>')) != Nullch)
			    *h = '\0';
		    }
		    break;
		case 'u':
		    sprintf(scrbuf,"%ld",(long)toread[ng]);
		    s = scrbuf;
		    break;
		case 'U':
		    sprintf(scrbuf,"%ld",
			(long)(((ART_NUM)toread[ng]) - 1 + was_read(art)));
		    s = scrbuf;
		    break;
		case 'x':			/* rn library */
		    s = lib;
		    break;
		case 'X':			/* rn library */
		    s = rnlib;
		    break;
		default:
		    *dest++ = *pattern;
		    s = nullstr;
		    break;
		}
	    }
	    if (!s)
		s = nullstr;
	    pattern++;
	    if (upper || lastcomp) {
		char *t;

		if (s != scrbuf) {
		    strcpy(scrbuf,s);
		    s = scrbuf;
		}
		if (upper || !(t=rindex(s,'/')))
		    t = s;
		while (*t && !isalpha(*t))
		    t++;
		if (islower(*t))
		    *t = toupper(*t);
	    }
	    if (s == dest) {
		while (*dest)
		    dest++;
	    }
	    else {
		while (*s)
		    *dest++ = *s++;
	    }
	}
	else if (*pattern == '\\') {
	    ++pattern;			/* skip backslash */
	    i = *pattern;		/* get char into a register */

	    /* this used to be a switch but the if may save space */
	    
	    if (i >= '0' && i <= '7') {
		i = 1;
		while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
		    i <<= 3;
		    i += *pattern++ - '0';
		}
		*dest++ = i & 0377;
		--pattern;
	    }
	    else if (i == 'b')
		*dest++ = '\b';
	    else if (i == 'f')
		*dest++ = '\f';
	    else if (i == 'n')
		*dest++ = '\n';
	    else if (i == 'r')
		*dest++ = '\r';
	    else if (i == 't')
		*dest++ = '\t';
	    else
		*dest++ = *pattern;
	    pattern++;
	}
	else
	    *dest++ = *pattern++;
    }
    *dest = '\0';
getout:
    if (subj_buf != Nullch)	/* return any checked out storage */
	free(subj_buf);
    if (ngs_buf != Nullch)
	free(ngs_buf);
    if (refs_buf != Nullch)
	free(refs_buf);
    if (artid_buf != Nullch)
	free(artid_buf);
    if (reply_buf != Nullch)
	free(reply_buf);
    if (from_buf != Nullch)
	free(from_buf);
    if (path_buf != Nullch)
	free(path_buf);
    if (follow_buf != Nullch)
	free(follow_buf);
    if (dist_buf != Nullch)
	free(dist_buf);
    if (line_buf != Nullch)
	free(line_buf);
    return pattern;			/* where we left off */
}

void
interp(dest,pattern)
char *dest;
char *pattern;
{
    dointerp(dest,pattern,Nullch);
}

/* copy a references line, normalizing as we go */

void
refscpy(dest,src)
register char *dest, *src;
{
    register char *dot, *at, *beg;
    char tmpbuf[64];
    
    while (*src) {
	if (*src != '<') {
	    *dest++ = '<';
	    at = dot = Nullch;
	    beg = src;
	    while (*src && *src != ' ' && *src != ',') {
		if (*src == '.')
		    dot = src;
		else if (*src == '@')
		    at = src;
		*dest++ = *src++;
	    }
	    if (dot && !at) {
		*dest = *dot++ = '\0';
		sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
		strcpy(dest,tmpbuf);
		dest = dest + strlen(dest);
	    }
	    *dest++ = '>';
	}
	else {
	    while (*src && (*dest++ = *src++) != '>') ;
	}
	while (*src == ' ' || *src == ',') src++;
	if (*src)
	    *dest++ = ' ';
    }
    *dest = '\0';
} 

/* get the person's real name from /etc/passwd */
/* (string is overwritten, so it must be copied) */

char *
getrealname(uid)
int uid;
{
    char *s, *c;

#ifdef PASSNAMES
#ifdef GETPWENT
    struct passwd *pwd = getpwuid(uid);
    
    s = pwd->pw_gcos;
#else
    char tmpbuf[512];
    int i;

    getpw(uid, tmpbuf);
    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
	if (s)
	    s = index(s,':')+1;
    }
    if (!s)
	return nullstr;
    cpytill(tmpbuf,s,':');
    s = tmpbuf;
#endif
#ifdef BERKNAMES
#ifdef BERKJUNK
    while (*s && !isalnum(*s) && *s != '&') s++;
#endif
    if ((c = index(s, ',')) != Nullch)
	*c = '\0';
    if ((c = index(s, ';')) != Nullch)
	*c = '\0';
    s = cpytill(buf,s,'&');
    if (*s == '&') {			/* whoever thought this one up was */
	strcat(buf,logname);		/* in the middle of the night */
	strcat(buf,s+1);		/* before the morning after */
	if (islower(*buf))
	    *buf = toupper(*buf);	/* gack and double gack */
    }
#else
    if ((c = index(s, '(')) != Nullch)
	*c = '\0';
    if ((c = index(s, '-')) != Nullch)
	s = c;
    strcpy(buf,tmpbuf);
#endif
#ifdef GETPWENT
    endpwent();
#endif
    return buf;				/* return something static */
#else
    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
	fgets(buf,sizeof buf,tmpfp);
	fclose(tmpfp);
	buf[strlen(buf)-1] = '\0';
	return buf;
    }
    return "PUT YOUR NAME HERE";
#endif
}

!STUFFY!FUNK!
echo Extracting search.c
cat >search.c <<'!STUFFY!FUNK!'
/* $Header: search.c,v 4.1 84/09/24 12:09:45 lwall Exp $
 *
 * $Log:	search.c,v $
 * Revision 4.1  84/09/24  12:09:45  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/12  17:34:48  lwall
 * Moved some includes to common.h.
 * 
 * Revision 4.0.1.2  84/09/10  15:29:52  lwall
 * Delinted.
 * 
 * Revision 4.0.1.1  84/09/05  10:38:46  lwall
 * Changed CEOF to CEND to avoid conflict with ttychars.h.
 * 
 * Revision 4.0  84/09/04  09:52:33  lwall
 * Baseline for netwide release
 * 
 */

/* string search routines */
 
/*		Copyright (c) 1981,1980 James Gosling		*/
 
/* Modified Aug. 12, 1981 by Tom London to include regular expressions
   as in ed.  RE stuff hacked over by jag to correct a few major problems,
   mainly dealing with searching within the buffer rather than copying
   each line to a separate array.  Newlines can now appear in RE's */

/* Ripped to shreds and glued back together to make a search package,
 * July 6, 1984, by Larry Wall. (If it doesn't work, it's probably my fault.)
 * Changes include:
 *	Buffer, window, and mlisp stuff gone.
 *	Translation tables reduced to 1 table.
 *	Expression buffer is now dynamically allocated.
 *	Character classes now implemented with a bitmap.
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "INTERN.h"
#include "search.h"

#ifndef BITSPERBYTE
#define BITSPERBYTE 8
#endif

#define BMAPSIZ (127 / BITSPERBYTE + 1)

/* meta characters in the "compiled" form of a regular expression */
#define	CBRA	2		/* \( -- begin bracket */
#define	CCHR	4		/* a vanilla character */
#define	CDOT	6		/* . -- match anything except a newline */
#define	CCL	8		/* [...] -- character class */
#define	NCCL	10		/* [^...] -- negated character class */
#define	CDOL	12		/* $ -- matches the end of a line */
#define	CEND	14		/* The end of the pattern */
#define	CKET	16		/* \) -- close bracket */
#define	CBACK	18		/* \N -- backreference to the Nth bracketed
				   string */
#define CIRC	20		/* ^ matches the beginning of a line */

#define WORD	32		/* matches word character \w */
#define NWORD	34		/* matches non-word characer \W */
#define WBOUND	36		/* matches word boundary \b */
#define NWBOUND	38		/* matches non-(word boundary) \B */
 
#define	STAR	01		/* * -- Kleene star, repeats the previous
				   REas many times as possible; the value
				   ORs with the other operator types */
 
#define ASCSIZ 0200
typedef char	TRANSTABLE[ASCSIZ];

static	TRANSTABLE trans = {
0000,0001,0002,0003,0004,0005,0006,0007,
0010,0011,0012,0013,0014,0015,0016,0017,
0020,0021,0022,0023,0024,0025,0026,0027,
0030,0031,0032,0033,0034,0035,0036,0037,
0040,0041,0042,0043,0044,0045,0046,0047,
0050,0051,0052,0053,0054,0055,0056,0057,
0060,0061,0062,0063,0064,0065,0066,0067,
0070,0071,0072,0073,0074,0075,0076,0077,
0100,0101,0102,0103,0104,0105,0106,0107,
0110,0111,0112,0113,0114,0115,0116,0117,
0120,0121,0122,0123,0124,0125,0126,0127,
0130,0131,0132,0133,0134,0135,0136,0137,
0140,0141,0142,0143,0144,0145,0146,0147,
0150,0151,0152,0153,0154,0155,0156,0157,
0160,0161,0162,0163,0164,0165,0166,0167,
0170,0171,0172,0173,0174,0175,0176,0177,
};
static bool folding = FALSE;

static int err;
static char *FirstCharacter;

void
search_init()
{
#ifdef UNDEF
    register int    i;
    
    for (i = 0; i < ASCSIZ; i++)
	trans[i] = i;
#else
    ;
#endif
}

void
init_compex(compex)
register COMPEX *compex;
{
    /* the following must start off zeroed */

    compex->eblen = 0;
    compex->brastr = Nullch;
}

void
free_compex(compex)
register COMPEX *compex;
{
    if (compex->eblen) {
	free(compex->expbuf);
	compex->eblen = 0;
    }
    if (compex->brastr) {
	free(compex->brastr);
	compex->brastr = Nullch;
    }
}

static char *gbr_str = Nullch;
static int gbr_siz = 0;

char *
getbracket(compex,n)
register COMPEX *compex;
int n;
{
    int length = compex->braelist[n] - compex->braslist[n];

    if (!compex->nbra || n > compex->nbra || !compex->braelist[n] || length<0)
	return nullstr;
    growstr(&gbr_str, &gbr_siz, length+1);
    safecpy(gbr_str, compex->braslist[n], length+1);
    return gbr_str;
}

void
case_fold(which)
int which;
{
    register int i;

    if (which != folding) {
	if (which) {
	    for (i = 'A'; i <= 'Z'; i++)
		trans[i] = tolower(i);
	}
	else {
	    for (i = 'A'; i <= 'Z'; i++)
		trans[i] = i;
	}
	folding = which;
    }
}

/* Compile the given regular expression into a [secret] internal format */

char *
compile (compex, strp, RE, fold)
register COMPEX *compex;
register char   *strp;
int RE;
int fold;
{
    register int c;
    register char  *ep;
    char   *lastep;
    char    bracket[NBRA],
	   *bracketp;
    char **alt = compex->alternatives;
    char *retmes = "Badly formed search string";
 
    case_fold(compex->do_folding = fold);
    if (!compex->eblen) {
	compex->expbuf = safemalloc(84);
	compex->eblen = 80;
    }
    ep = compex->expbuf;		/* point at expression buffer */
    *alt++ = ep;			/* first alternative starts here */
    bracketp = bracket;			/* first bracket goes here */
    if (*strp == 0) {			/* nothing to compile? */
	if (*ep == 0)			/* nothing there yet? */
	    return "Null search string";
	return Nullch;			/* just keep old expression */
    }
    compex->nbra = 0;			/* no brackets yet */
    lastep = 0;
    for (;;) {
	if (ep - compex->expbuf >= compex->eblen)
	    grow_eb(compex);
	c = *strp++;			/* fetch next char of pattern */
	if (c == 0) {			/* end of pattern? */
	    if (bracketp != bracket) {	/* balanced brackets? */
#ifdef VERBOSE
		retmes = "Unbalanced parens";
#endif
		goto cerror;
	    }
	    *ep++ = CEND;		/* terminate expression */
	    *alt++ = 0;			/* terminal alternative list */
	    /*
	    compex->eblen = ep - compex->expbuf + 1;
	    compex->expbuf = saferealloc(compex->expbuf,compex->eblen+4); */
	    return Nullch;		/* return success */
	}
	if (c != '*')
	    lastep = ep;
	if (!RE) {			/* just a normal search string? */
	    *ep++ = CCHR;		/* everything is a normal char */
	    *ep++ = c;
	}
	else				/* it is a regular expression */
	    switch (c) {
 
		case '\\':		/* meta something */
		    switch (c = *strp++) {
		    case '(':
			if (compex->nbra >= NBRA) {
#ifdef VERBOSE
			    retmes = "Too many parens";
#endif
			    goto cerror;
			}
			*bracketp++ = ++compex->nbra;
			*ep++ = CBRA;
			*ep++ = compex->nbra;
			break;
		    case '|':
			if (bracketp>bracket) {
#ifdef VERBOSE
			    retmes = "No \\| in parens";	/* Alas! */
#endif
			    goto cerror;
			}
			*ep++ = CEND;
			*alt++ = ep;
			break;
		    case ')':
			if (bracketp <= bracket) {
#ifdef VERBOSE
			    retmes = "Unmatched right paren";
#endif
			    goto cerror;
			}
			*ep++ = CKET;
			*ep++ = *--bracketp;
			break;
		    case 'w':
			*ep++ = WORD;
			break;
		    case 'W':
			*ep++ = NWORD;
			break;
		    case 'b':
			*ep++ = WBOUND;
			break;
		    case 'B':
			*ep++ = NWBOUND;
			break;
		    case '0': case '1': case '2': case '3': case '4':
		    case '5': case '6': case '7': case '8': case '9':
			*ep++ = CBACK;
			*ep++ = c - '0';
			break;
		    default:
			*ep++ = CCHR;
			if (c == '\0')
			    goto cerror;
			*ep++ = c;
			break;
		    }
		    break;
		case '.':
		    *ep++ = CDOT;
		    continue;
 
		case '*':
		    if (lastep == 0 || *lastep == CBRA || *lastep == CKET
			|| *lastep == CIRC
			|| (*lastep&STAR)|| *lastep>NWORD)
			goto defchar;
		    *lastep |= STAR;
		    continue;
 
		case '^':
		    if (ep != compex->expbuf && ep[-1] != CEND)
			goto defchar;
		    *ep++ = CIRC;
		    continue;
 
		case '$':
		    if (*strp != 0 && (*strp != '\\' || strp[1] != '|'))
			goto defchar;
		    *ep++ = CDOL;
		    continue;
 
		case '[': {		/* character class */
		    register int i;
		    
		    if (ep - compex->expbuf >= compex->eblen - BMAPSIZ)
			grow_eb(compex);	/* reserve bitmap */
		    for (i = BMAPSIZ; i; --i)
			ep[i] = 0;
		    
		    if ((c = *strp++) == '^') {
			c = *strp++;
			*ep++ = NCCL;	/* negated */
		    }
		    else
			*ep++ = CCL;	/* normal */
		    
		    i = 0;		/* remember oldchar */
		    do {
			if (c == '\0') {
#ifdef VERBOSE
			    retmes = "Missing ]";
#endif
			    goto cerror;
			}
			if (*strp == '-' && *(++strp))
			    i = *strp++;
			else
			    i = c;
			while (c <= i) {
			    ep[c / BITSPERBYTE] |= 1 << (c % BITSPERBYTE);
			    if (fold && isalpha(c))
				ep[(c ^ 32) / BITSPERBYTE] |=
				    1 << ((c ^ 32) % BITSPERBYTE);
					/* set the other bit too */
			    c++;
			}
		    } while ((c = *strp++) != ']');
		    ep += BMAPSIZ;
		    continue;
		}
 
	    defchar:
		default:
		    *ep++ = CCHR;
		    *ep++ = c;
	    }
    }
cerror:
    compex->expbuf[0] = 0;
    compex->nbra = 0;
    return retmes;
}

void
grow_eb(compex)
register COMPEX *compex;
{
    compex->eblen += 80;
    compex->expbuf = saferealloc(compex->expbuf, (MEM_SIZE)compex->eblen + 4);
}

char *
execute (compex, addr)
register COMPEX *compex;
char *addr;
{
    register char *p1 = addr;
    register char *trt = trans;
    register int c;
 
    if (addr == Nullch)
	return Nullch;
    if (compex->nbra) {			/* any brackets? */
	for (c = 0; c <= compex->nbra; c++)
	    compex->braslist[c] = compex->braelist[c] = Nullch;
	if (compex->brastr)
	    free(compex->brastr);
	compex->brastr = savestr(p1);	/* in case p1 is not static */
	p1 = compex->brastr;		/* ! */
    }
    case_fold(compex->do_folding);	/* make sure table is correct */
    FirstCharacter = p1;		/* for ^ tests */
    if (compex->expbuf[0] == CCHR && !compex->alternatives[1]) {
	c = trt[compex->expbuf[1]];	/* fast check for first character */
	do {
	    if (trt[*p1] == c && advance (compex, p1, compex->expbuf))
		return p1;
	    p1++;
	} while (*p1 && !err);
	return Nullch;
    }
    else {			/* regular algorithm */
	do {
	    register char **alt = compex->alternatives;
	    while (*alt) {
		if (advance (compex, p1, *alt++))
		    return p1;
	    }
	    p1++;
	} while (*p1 && !err);
	return Nullch;
    }
}
 
/* advance the match of the regular expression starting at ep along the
   string lp, simulates an NDFSA */
bool
advance (compex, lp, ep)
register COMPEX *compex;
register char *ep;
register char *lp;
{
    register char *curlp;
    register char *trt = trans;
    register int i;
 
    while ((*ep & STAR) || *lp || *ep == CIRC || *ep == CKET)
	switch (*ep++) {
 
	    case CCHR:
		if (trt[*ep++] != trt[*lp]) return FALSE;
		lp++;
		continue;
 
	    case CDOT:
		if (*lp == '\n') return FALSE;
		lp++;
		continue;
 
	    case CDOL:
		if (!*lp || *lp == '\n')
		    continue;
		return FALSE;
 
	    case CIRC:
		if (lp == FirstCharacter || lp[-1]=='\n')
		    continue;
		return FALSE;
 
	    case WORD:
		if (isalnum(*lp)) {
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case NWORD:
		if (!isalnum(*lp)) {
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case WBOUND:
		if ((lp == FirstCharacter || !isalnum(lp[-1])) !=
			(!*lp || !isalnum(*lp)) )
		    continue;
		return FALSE;
 
	    case NWBOUND:
		if ((lp == FirstCharacter || !isalnum(lp[-1])) ==
			(!*lp || !isalnum(*lp)))
		    continue;
		return FALSE;
 
	    case CEND:
		return TRUE;
 
	    case CCL:
		if (cclass (ep, *lp, 1)) {
		    ep += BMAPSIZ;
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case NCCL:
		if (cclass (ep, *lp, 0)) {
		    ep += BMAPSIZ;
		    lp++;
		    continue;
		}
		return FALSE;
 
	    case CBRA:
		compex->braslist[*ep++] = lp;
		continue;
 
	    case CKET:
		i = *ep++;
		compex->braelist[i] = lp;
		compex->braelist[0] = lp;
		compex->braslist[0] = compex->braslist[i];
		continue;
 
	    case CBACK:
		if (compex->braelist[i = *ep++] == 0) {
		    fputs("bad braces\n",stdout);
		    err = TRUE;
		    return FALSE;
		}
		if (backref (compex, i, lp)) {
		    lp += compex->braelist[i] - compex->braslist[i];
		    continue;
		}
		return FALSE;
 
	    case CBACK | STAR:
		if (compex->braelist[i = *ep++] == 0) {
		    fputs("bad braces\n",stdout);
		    err = TRUE;
		    return FALSE;
		}
		curlp = lp;
		while (backref (compex, i, lp)) {
		    lp += compex->braelist[i] - compex->braslist[i];
		}
		while (lp >= curlp) {
		    if (advance (compex, lp, ep))
			return TRUE;
		    lp -= compex->braelist[i] - compex->braslist[i];
		}
		continue;
 
	    case CDOT | STAR:
		curlp = lp;
		while (*lp++ && lp[-1] != '\n');
		goto star;
 
	    case WORD | STAR:
		curlp = lp;
		while (*lp++ && isalnum(lp[-1]));
		goto star;
 
	    case NWORD | STAR:
		curlp = lp;
		while (*lp++ && !isalnum(lp[-1]));
		goto star;
 
	    case CCHR | STAR:
		curlp = lp;
		while (*lp++ && trt[lp[-1]] == trt[*ep]);
		ep++;
		goto star;
 
	    case CCL | STAR:
	    case NCCL | STAR:
		curlp = lp;
		while (*lp++ && cclass (ep, lp[-1], ep[-1] == (CCL | STAR)));
		ep += BMAPSIZ;
		goto star;
 
	star:
		do {
		    lp--;
		    if (advance (compex, lp, ep))
			return TRUE;
		} while (lp > curlp);
		return FALSE;
 
	    default:
		fputs("Badly compiled pattern\n",stdout);
		err = TRUE;
		return -1;
	}
	if (*ep == CEND || *ep == CDOL) {
	    return TRUE;
    }
    return FALSE;
}
 
bool
backref (compex, i, lp)
register COMPEX *compex;
register int i;
register char *lp;
{
    register char *bp;
 
    bp = compex->braslist[i];
    while (*lp && *bp == *lp) {
	bp++;
	lp++;
	if (bp >= compex->braelist[i])
	    return TRUE;
    }
    return FALSE;
}

bool
cclass (set, c, af)
register char  *set;
register int c;
{
    c &= 0177;
#if BITSPERBYTE == 8
    if (set[c >> 3] & 1 << (c & 7))
#else
    if (set[c / BITSPERBYTE] & 1 << (c % BITSPERBYTE))
#endif
	return af;
    return !af;
}
!STUFFY!FUNK!
echo Extracting term.c
cat >term.c <<'!STUFFY!FUNK!'
/* $Header: term.c,v 4.1 84/09/24 12:10:42 lwall Exp $
 *
 * $Log:	term.c,v $
 * Revision 4.1  84/09/24  12:10:42  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.4  84/09/24  10:27:07  lwall
 * print_lines() did not do fireworks suppression right.
 * 
 * Revision 4.0.1.3  84/09/19  17:12:36  lwall
 * Ifdef'ed some stuff that should have been.
 * 
 * Revision 4.0.1.2  84/09/12  17:22:14  lwall
 * ifdef usg -> ifdef TERMIO.
 * 
 * Revision 4.0.1.1  84/09/10  15:33:08  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:51  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "final.h"
#include "help.h"
#include "cheat.h"
#include "intrp.h"
#include "INTERN.h"
#include "term.h"

char ERASECH;		/* rubout character */
char KILLCH;		/* line delete character */
char tcarea[TCSIZE];	/* area for "compiled" termcap strings */

/* terminal initialization */

void
term_init(tcbuf)
char *tcbuf;		/* temp area for "uncompiled" termcap entry */
{
    char *tmpaddr;
    int status;

#ifndef read_tty
    /* do no delay reads on something that always gets closed on exit */

    devtty = open("/dev/tty",0);
    fcntl(devtty,F_SETFL,O_NDELAY);
#endif
    
    savetty();				/* remember current tty state */

#ifdef TERMIO
    ospeed = _tty.c_cflag & CBAUD;	/* for tputs() */
    ERASECH = _tty.c_cc[VERASE];	/* for finish_command() */
    KILLCH = _tty.c_cc[VKILL];		/* for finish_command() */
#else
    ospeed = _tty.sg_ospeed;		/* for tputs() */
    ERASECH = _tty.sg_erase;		/* for finish_command() */
    KILLCH = _tty.sg_kill;		/* for finish_command() */
#endif

    noecho();				/* turn off echo */
    crmode();				/* enter cbreak mode */

    /* get all that good termcap stuff */

#ifdef HAVETERMLIB
    status = tgetent(tcbuf,getenv("TERM"));	/* get termcap entry */
    if (status < 1) {
#ifdef VERBOSE
	printf("No termcap %s found.\n", status ? "file" : "entry");
#else
	fputs("Termcap botch\n",stdout)
#endif
	finalize(1);
    }
    tmpaddr = tcarea;			/* set up strange tgetstr pointer */
    tgetstr("pc",&tmpaddr);		/* get pad character */
    PC = *tcarea;			/* get it where tputs wants it */
    if (!tgetflag("bs")) {		/* is backspace not used? */
	BC = tmpaddr;			/* find out what is */
	tgetstr("bc",&tmpaddr);
    }
    else
	BC = "\010";			/* make a backspace handy */
    UP = tmpaddr;			/* move up a line */
    tgetstr("up",&tmpaddr);
    if (!*UP)				/* no UP string? */
	marking = 0;			/* disable any marking */
    if (muck_up_clear)			/* this is for weird HPs */
	CL = "\n\n\n\n";
    else {
	CL = tmpaddr;			/* get clear string */
	tgetstr("cl",&tmpaddr);
    }
    CE = tmpaddr;			/* clear to end of line string */
    tgetstr("ce",&tmpaddr);
    SO = tmpaddr;			/* begin standout */
    tgetstr("so",&tmpaddr);
    SE = tmpaddr;			/* end standout */
    tgetstr("se",&tmpaddr);
    if ((SG = tgetnum("sg"))<0)
	SG = 0;				/* blanks left by SG, SE */
    US = tmpaddr;			/* start underline */
    tgetstr("us",&tmpaddr);
    UE = tmpaddr;			/* end underline */
    tgetstr("ue",&tmpaddr);
    if ((UG = tgetnum("ug"))<0)
	UG = 0;				/* blanks left by US, UE */
    if (*US)
	UC = nullstr;			/* UC must not be NULL */
    else {
	UC = tmpaddr;			/* underline a character */
	tgetstr("uc",&tmpaddr);
    }
    if (!*US && !*UC) {			/* no underline mode? */
	US = SO;			/* substitute standout mode */
	UE = SE;
	UG = SG;
    }
    LINES = tgetnum("li");		/* lines per page */
    COLS = tgetnum("co");		/* columns on page */
    AM = tgetflag("am");		/* terminal wraps automatically? */
    XN = tgetflag("xn");		/* then eats next newline? */
#else
    ??????				/* Roll your own... */
#endif
    if (LINES > 0) {			/* is this a crt? */
	if (!initlines)			/* no -i? */
	    if (ospeed >= B9600)	/* whole page at >= 9600 baud */
		initlines = LINES;
	    else if (ospeed >= B4800)	/* 16 lines at 4800 */
		initlines = 16;
	    else			/* otherwise just header */
		initlines = 8;
    }
    else {				/* not a crt */
	LINES = 30000;			/* so don't page */
	CL = "\n\n";			/* put a couple of lines between */
	if (!initlines)			/* make initlines reasonable */
	    initlines = 8;
    }
    if (COLS <= 0)
	COLS = 80;
    just_a_sec =			/* 1 second of padding */
      ospeed >= B300 ? (
        ospeed >= B9600 ? 960 :
        ospeed == B4800 ? 480 :
        ospeed == B2400 ? 240 :
        ospeed == B1800 ? 180 :
        ospeed == B1200 ? 120 :
        ospeed == B600  ?  60 :
     /* ospeed == B300*/   30
     ) : (				/* avoid 'expression overflow' grrr */
        ospeed == B200  ?  20 :
        ospeed == B150  ?  15 :		/* do I really have to type the */
        ospeed == B134  ?  13 :		/* rest of this? */
        ospeed == B110  ?  11 :
        ospeed == B75   ?   8 :
        ospeed == B50   ?   5 :
     /* ospeed == B0 */   960		/* if we are running detached I */
     );					/*  don't want to know about it! */

    
}

/* routine to pass to tputs */

char
putchr(ch)
register char ch;
{
    putchar(ch);
}

/* input the 2nd and succeeding characters of a multi-character command */
/* returns TRUE if command finished, FALSE if they rubbed out first character */

bool
finish_command(donewline)
int donewline;
{
    register char *s;

    s = buf;
    if (s[1] != FINISHCMD)		/* someone faking up a command? */
	return TRUE;
    do {
      top:
	if (*s < ' ') {
	    putchar('^');
	    putchar(*s | 64);
	}
	else
	    putchar(*s);		/* echo previous character */
	s++;
re_read:
	fflush(stdout);
	getcmd(s);
	if (errno || *s == Ctl('l')) {
	    *s = Ctl('r');		/* force rewrite on CONT */
	}
	if (*s == '\033') {		/* substitution desired? */
#ifdef ESCSUBS
	    char tmpbuf[4], *cpybuf;

	    tmpbuf[0] = '%';
	    read_tty(&tmpbuf[1],1);
#ifdef RAWONLY
	    tmpbuf[1] &= 0177;
#endif
	    tmpbuf[2] = '\0';
	    if (tmpbuf[1] == 'h') {
		help_subs();
		*s = '\0';
		reprint();
		goto re_read;
	    }
	    else if (tmpbuf[1] == '\033') {
		*s = '\0';
		cpybuf = savestr(buf);
		interp(buf,cpybuf);
		free(cpybuf);
		s = buf + strlen(buf);
		reprint();
		goto re_read;
	    }
	    else {
		interp(s,tmpbuf);
		fputs(s,stdout);
		s += strlen(s);
	    }
	    goto re_read;
#else
	    notincl("^[");
	    *s = '\0';
	    reprint();
	    goto re_read;
#endif
	}
	else if (*s == ERASECH) {	/* they want to rubout a char? */
	    rubout();
	    s--;			/* discount the char rubbed out */
	    if (*s < ' ')
		rubout();
	    if (s == buf) {		/* entire string gone? */
		fflush(stdout);		/* return to single char command mode */
		return FALSE;
	    }
	    else
		goto re_read;
	}
	else if (*s == KILLCH) {	/* wipe out the whole line? */
	    while (s-- != buf) {	/* emulate that many ERASEs */
		rubout();
		if (*s < ' ')
		    rubout();
	    }
	    fflush(stdout);
	    return FALSE;		/* return to single char mode */
	}
#ifdef WORDERASE
	else if (*s == Ctl('w')) {	/* wipe out one word? */
	    *s-- = ' ';
	    while (!isspace(*s) || isspace(s[1])) {
		rubout();
		if (s-- == buf) {
		    fflush(stdout);
		    return FALSE;	/* return to single char mode */
		}
		if (*s < ' ')
		    rubout();
	    }
	    s++;
	    goto re_read;
	}
#endif
	else if (*s == Ctl('r')) {
	    *s = '\0';
	    reprint();
	    goto re_read;
	}
	else if (*s == Ctl('v')) {
	    putchar('^');
	    backspace();
	    getcmd(s);
	    goto top;
	}
    } while (*s != '\n');		/* till a newline (not echoed) */
    *s = '\0';				/* terminate the string nicely */
    if (donewline)
	putchar('\n');
    return TRUE;			/* say we succeeded */
}

/* discard any characters typed ahead */

void
eat_typeahead()
{
    /*NOSTRICT*/
    if (!typeahead) {
#ifdef PENDING
	while (input_pending())
	    read_tty(buf,sizeof(buf));
#else /* this is probably v7 */
	ioctl(_tty_ch,TIOCSETP,&_tty);
#endif
    }
}

#ifndef read_tty
/* read a character from the terminal, with hacks for O_NDELAY reads */

int
read_tty(addr,size)
char *addr;
int size;
{
    if (is_input) {
	*addr = pending_ch;
	is_input = FALSE;
	return 1;
    }
    else
	return read(0,addr,size);
}
#endif

/* print an underlined string, one way or another */

void
underprint(s)
register char *s;
{
    assert(UC);
    if (*UC) {		/* char by char underline? */
	while (*s) {
	    if (*s < ' ') {
		putchar('^');
		backspace();/* back up over it */
		underchar();/* and do the underline */
		putchar(*s+64);
		backspace();/* back up over it */
		underchar();/* and do the underline */
	    }
	    else {
		putchar(*s);
		backspace();/* back up over it */
		underchar();/* and do the underline */
	    }
	    s++;
	}
    }
    else {		/* start and stop underline */
	underline();	/* start underlining */
	while (*s) {
	    if (*s < ' ') {
		putchar('^');
		putchar(*s+64);
	    }
	    else
		putchar(*s);
	    s++;
	}
	un_underline();	/* stop underlining */
    }
}

/* keep screen from flashing strangely on magic cookie terminals */

#ifdef NOFIREWORKS
void
no_sofire()
{
    if (*UP && *SE) {		/* should we disable fireworks? */
	putchar('\n');
	un_standout();
	up_line();
	putchar('\r');
    }
}

void
no_ulfire()
{
    if (*UP && *US) {		/* should we disable fireworks? */
	putchar('\n');
	un_underline();
	up_line();
	putchar('\r');
    }
}
#endif

/* get a character into a buffer */

void
getcmd(whatbuf)
register char *whatbuf;
{
    int_count = 0;
    errno = 0;
    if (read_tty(whatbuf,1) < 0 && !errno)
	errno = EINTR;
    if (errno && errno != EINTR) {
	perror(readerr);
	sig_catcher(0);
    }
#ifdef RAWONLY
    *whatbuf &= 0177;
#endif
    if (whatbuf == buf)
	whatbuf[1] = FINISHCMD;		/* tell finish_command to work */
}

int
get_anything()
{
    char tmpbuf[2];

reask_anything:
    standout();
#ifdef VERBOSE
    IF(verbose)
	fputs("[Type space to continue] ",stdout);
    ELSE
#endif
#ifdef TERSE
	fputs("[MORE] ",stdout);
#endif
    un_standout();
    fflush(stdout);
    eat_typeahead();
    if (int_count) {
	return -1;
    }
    collect_subjects();			/* loads subject cache until */
					/* input is pending */
    getcmd(tmpbuf);
    if (errno || *tmpbuf == '\f') {
	putchar('\n');			/* if return from stop signal */
	goto reask_anything;		/* give them a prompt again */
    }
    if (*tmpbuf == 'h') {
#ifdef VERBOSE
	IF(verbose)
	    fputs("\nType q to quit or space to continue.\n",stdout);
	ELSE
#endif
#ifdef TERSE
	    fputs("\nq to quit, space to continue.\n",stdout);
#endif
	goto reask_anything;
    }
    else if (*tmpbuf != ' ') {
	putchar('\r');	/* put cursor at beginning of line */
	erase_eol();	/* and erase the prompt */
	return *tmpbuf;
    }
    if (erase_screen) 			/* -e? */
	clear();			/* clear screen */
    else {
	putchar('\r');	/* put cursor at beginning of line */
	erase_eol();	/* and erase the prompt */
    }
    return 0;
}

void
in_char(prompt)
char *prompt;
{
reask_in_char:
    fputs(prompt,stdout);
    fflush(stdout);
    eat_typeahead();
    getcmd(buf);
    if (errno || *buf == '\f') {
	putchar('\n');			/* if return from stop signal */
	goto reask_in_char;		/* give them a prompt again */
    }
}

int
print_lines(what_to_print,hilite)
char *what_to_print;
int hilite;
{
    register char *s;
    register int i;

    if (page_line < 0)			/* they do not want to see this? */
	return -1;
    for (s=what_to_print; *s; ) {
	if (page_line >= LINES || int_count) {
	    if (int_count || get_anything()) {
		page_line = -1;		/* disable further print_lines */
		return -1;
	    }
	    page_line = 2;
	}
	else
	    page_line++;
	if (hilite == STANDOUT) {
#ifdef NOFIREWORKS
	    if (erase_screen)
		no_sofire();
#endif
	    standout();
	}
	else if (hilite == UNDERLINE) {
#ifdef NOFIREWORKS
	    if (erase_screen)
		no_ulfire();
#endif
	    underline();
	}
	for (i=0; i<COLS; i++) {
	    if (!*s)
		break;
	    if (*s >= ' ')
		putchar(*s);
	    else if (*s == '\t') {
		putchar(*s);
		i = ((i+8) & ~7) - 1; 
	    }
	    else if (*s == '\n') {
		i = 32000;
	    }
	    else {
		i++;
		putchar('^');
		putchar(*s + 64);
	    }
	    s++;
	}
	if (i) {
	    if (hilite == STANDOUT)
		un_standout();
	    else if (hilite == UNDERLINE)
		un_underline();
	    if (AM && i == COLS)
		fflush(stdout);
	    else
		putchar('\n');
	}
    }
    return 0;
}

void
pad(num)
int num;
{
    register int i;

    for (i = num; i; --i)
	putchar(PC);
    fflush(stdout);
}

/* echo the command just typed */

#ifdef VERIFY
void
printcmd()
{
    if (verify && buf[1] == FINISHCMD) {
	if (*buf < ' ') {
	    putchar('^');
	    putchar(*buf | 64);
	    backspace();
	    backspace();
	}
	else {
	    putchar(*buf);
	    backspace();
	}
	fflush(stdout);
    }
}
#endif

void
rubout()
{
    backspace();			/* do the old backspace, */
    putchar(' ');			/*   space, */
    backspace();			/*     backspace trick */
}

void
reprint()
{
    register char *s;

    fputs("^R\n",stdout);
    for (s = buf; *s; s++) {
	if (*s < ' ') {
	    putchar('^');
	    putchar(*s | 64);
	}
	else
	    putchar(*s);
    }
}

!STUFFY!FUNK!
echo Extracting rcln.c
cat >rcln.c <<'!STUFFY!FUNK!'
/* $Header: rcln.c,v 4.1 84/09/24 12:04:36 lwall Exp $
 *
 * $Log:	rcln.c,v $
 * Revision 4.1  84/09/24  12:04:36  lwall
 * Real baseline.
 * 
 * Revision 4.0.1.3  84/09/18  16:09:18  lwall
 * Fixed previous fix.
 * 
 * Revision 4.0.1.2  84/09/13  11:16:43  lwall
 * Check for out-of-bounds Xreffing.
 * 
 * Revision 4.0.1.1  84/09/10  15:23:02  lwall
 * Delinted.
 * 
 * Revision 4.0  84/09/04  09:52:01  lwall
 * Baseline for netwide release
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "rcstuff.h"
#include "ngdata.h"
#include "INTERN.h"
#include "rcln.h"

void
rcln_init()
{
    ;
}

#ifdef CATCHUP
void
catch_up(ngx)
NG_NUM ngx;
{
    char tmpbuf[128];
    
#ifdef VERBOSE
    IF(verbose)
	printf("\nMarking %s as all read.\n",rcline[ngx]);
    ELSE
#endif
#ifdef TERSE
	fputs("\nMarked read\n",stdout);
#endif
    sprintf(tmpbuf,"%s: 1-%ld", rcline[ngx],(long)getngsize(ngx));
    free(rcline[ngx]);
    rcline[ngx] = savestr(tmpbuf);
    *(rcline[ngx] + rcnums[ngx] - 1) = '\0';
    write_rc();
}
#endif

/* add an article number to a newsgroup, if it isn't already read */

int
addartnum(artnum,ngnam)
ART_NUM artnum;
char *ngnam;
{
    register NG_NUM ngnum = find_ng(ngnam);
    register char *s, *t, *maxt = Nullch;
    ART_NUM min = 0, max = -1, lastnum = 0;
    char *mbuf;
    bool morenum;

    if (!artnum)
	return 0;
    if (ngnum == nextrcline || !rcnums[ngnum])
					/* not found in newsrc? */
	return 0;
#ifdef CACHEFIRST
    if (!abs1st[ngnum])
#else
    if (!toread[ngnum])
#endif
					/* now is a good time to trim down */
	set_toread(ngnum);		/* the list due to expires if we */
					/* have not yet. */
#ifdef DEBUGGING
    if (artnum > ngmax[ngnum] + 10	/* allow for incoming articles */
       ) {
	printf("\nCorrupt Xref line!!!  %ld --> %s(1..%ld)\n",
	    artnum,ngnam,
	    ngmax[ngnum]);
	paranoid = TRUE;		/* paranoia reigns supreme */
	return -1;			/* hope this was the first newsgroup */
    }
#endif

    if (toread[ngnum] == TR_BOGUS)
	return 0;
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%ld->\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
    s = rcline[ngnum] + rcnums[ngnum];
    while (*s == ' ') s++;		/* skip spaces */
    t = s;
    while (isdigit(*s) && artnum >= (min = atol(s))) {
					/* while it might have been read */
	for (t = s; isdigit(*t); t++) ;	/* skip number */
	if (*t == '-') {		/* is it a range? */
	    t++;			/* skip to next number */
	    if (artnum <= (max = atol(t)))
		return 0;		/* it is in range => already read */
	    lastnum = max;		/* remember it */
	    maxt = t;			/* remember position in case we */
					/* want to overwrite the max */
	    while (isdigit(*t)) t++;	/* skip second number */
	}
	else {
	    if (artnum == min)		/* explicitly a read article? */
		return 0;
	    lastnum = min;		/* remember what the number was */
	    maxt = Nullch;		/* last one was not a range */
	}
	while (*t && !isdigit(*t)) t++;	/* skip comma and any spaces */
	s = t;
    }
    
    /* we have not read it, so insert the article number before s */
    
    morenum = isdigit(*s);		/* will it need a comma after? */
    *(rcline[ngnum] + rcnums[ngnum] - 1) = rcchar[ngnum];
    mbuf = safemalloc((MEM_SIZE)(strlen(s) + (s-rcline[ngnum]) + 8));
    strcpy(mbuf,rcline[ngnum]);		/* make new rc line */
    if (maxt && lastnum && artnum == lastnum+1)
    					/* can we just extend last range? */
	t = mbuf + (maxt-rcline[ngnum]);/* then overwrite previous max */
    else {
	t = mbuf + (t-rcline[ngnum]);	/* point t into new line instead */
	if (lastnum) {			/* have we parsed any line? */
	    if (!morenum)		/* are we adding to the tail? */
		*t++ = ',';		/* supply comma before */
	    if (!maxt && artnum == lastnum+1 && *(t-1) == ',')
					/* adjacent singletons? */
		*(t-1) = '-';		/* turn them into a range */
	}
    }
    if (morenum) {			/* is there more to life? */
	if (min == artnum+1) {		/* can we consolidate further? */
	    bool range_before = (*(t-1) == '-');
	    bool range_after;
	    char *nextmax;

	    for (nextmax = s; isdigit(*nextmax); nextmax++) ;
	    range_after = *nextmax++ == '-';
	    
	    if (range_before)
		*t = '\0';		/* artnum is redundant */
	    else
		sprintf(t,"%ld-",(long)artnum);/* artnum will be new min */
	    
	    if (range_after)
		s = nextmax;		/* *s is redundant */
	/*  else
		s = s */		/* *s is new max */
	}
	else
	    sprintf(t,"%ld,",(long)artnum);	/* put the number and comma */
    }
    else
	sprintf(t,"%ld",(long)artnum);	/* put the number there (wherever) */
    strcat(t,s);			/* copy remainder of line */
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%s\n",mbuf);
    }
#endif
    free(rcline[ngnum]);
    rcline[ngnum] = mbuf;		/* pull the switcheroo */
    *(rcline[ngnum] + rcnums[ngnum] - 1) = '\0';
					/* wipe out : or ! */
    if (toread[ngnum] > TR_NONE)	/* lest we turn unsub into bogus */
	--toread[ngnum];
    return 0;
}

#ifdef MCHASE
/* delete an article number from a newsgroup, if it is there */

void
subartnum(artnum,ngnam)
register ART_NUM artnum;
char *ngnam;
{
    register NG_NUM ngnum = find_ng(ngnam);
    register char *s, *t;
    register ART_NUM min, max;
    char *mbuf;
    int curlen;

    if (!artnum)
	return;
    if (ngnum == nextrcline || !rcnums[ngnum])
	return;				/* not found in newsrc? */
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%ld<-\n%s%c%s\n",(long)artnum,rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
    s = rcline[ngnum] + rcnums[ngnum];
    while (*s == ' ') s++;		/* skip spaces */
    
    /* a little optimization, since it is almost always the last number */
    
    for (t=s; *t; t++) ;		/* find end of string */
    curlen = t-rcline[ngnum];
    for (t--; isdigit(*t); t--) ;	/* find previous delim */
    if (*t == ',' && atol(t+1) == artnum) {
	*t = '\0';
	if (toread[ngnum] >= TR_NONE)
	    ++toread[ngnum];
#ifdef DEBUGGING
	if (debug & DEB_XREF_MARKER)
	    printf("%s%c %s\n",rcline[ngnum],rcchar[ngnum],s);
#endif
	return;
    }

    /* not the last number, oh well, we may need the length anyway */

    while (isdigit(*s) && artnum >= (min = atol(s))) {
					/* while it might have been read */
	for (t = s; isdigit(*t); t++) ;	/* skip number */
	if (*t == '-') {		/* is it a range? */
	    t++;			/* skip to next number */
	    max = atol(t);
	    while (isdigit(*t)) t++;	/* skip second number */
	    if (artnum <= max) {
					/* it is in range => already read */
		if (artnum == min) {
		    min++;
		    artnum = 0;
		}
		else if (artnum == max) {
		    max--;
		    artnum = 0;
		}
		*(rcline[ngnum] + rcnums[ngnum] - 1) = rcchar[ngnum];
		mbuf = safemalloc((MEM_SIZE)(curlen + (artnum?15:2)));
		*s = '\0';
		strcpy(mbuf,rcline[ngnum]);	/* make new rc line */
		s = mbuf + (s-rcline[ngnum]);
					/* point s into mbuf now */
		if (artnum) {		/* split into two ranges? */
		    prange(s,min,artnum-1);
		    s += strlen(s);
		    *s++ = ',';
		    prange(s,artnum+1,max);
		}
		else			/* only one range */
		    prange(s,min,max);
		s += strlen(s);
		strcpy(s,t);		/* copy remainder over */
#ifdef DEBUGGING
		if (debug & DEB_XREF_MARKER) {
		    printf("%s\n",mbuf);
		}
#endif
		free(rcline[ngnum]);
		rcline[ngnum] = mbuf;	/* pull the switcheroo */
		*(rcline[ngnum] + rcnums[ngnum] - 1) = '\0';
					/* wipe out : or ! */
		if (toread[ngnum] >= TR_NONE)
		    ++toread[ngnum];
		return;
	    }
	}
	else {
	    if (artnum == min) {	/* explicitly a read article? */
		if (*t == ',')		/* pick a comma, any comma */
		    t++;
		else if (s[-1] == ',')
		    s--;
		else if (s[-2] == ',')	/* (in case of space) */
		    s -= 2;
		strcpy(s,t);		/* no need to realloc */
		if (toread[ngnum] >= TR_NONE)
		    ++toread[ngnum];
#ifdef DEBUGGING
		if (debug & DEB_XREF_MARKER) {
		    printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum],
		      rcline[ngnum] + rcnums[ngnum]);
		}
#endif
		return;
	    }
	}
	while (*t && !isdigit(*t)) t++;	/* skip comma and any spaces */
	s = t;
    }
}

void
prange(where,min,max)
char *where;
ART_NUM min,max;
{
    if (min == max)
	sprintf(where,"%ld",(long)min);
    else
	sprintf(where,"%ld-%ld",(long)min,(long)max);
}
#endif

/* calculate the number of unread articles for a newsgroup */

void
set_toread(ngnum)
register NG_NUM ngnum;
{
    register char *s, *c, *h;
    char tmpbuf[64], *mybuf = tmpbuf;
    char *nums;
    int length;
    ART_NUM ngsize = getngsize(ngnum);
    ART_NUM unread = ngsize;
    ART_NUM newmax;

#ifdef DEBUGGING
    ngmax[ngnum] = ngsize;		/* for checking out-of-range Xrefs */
#endif
    if (ngsize == TR_BOGUS) {
	printf("Warning!  Bogus newsgroup: %s\n",rcline[ngnum]);
	paranoid = TRUE;
	toread[ngnum] = TR_BOGUS;
	return;
    }
#ifdef CACHEFIRST
    if (!abs1st[ngnum])
#else
    if (!toread[ngnum])
#endif
    {
	sprintf(tmpbuf," 1-%ld",(long)ngsize);
	if (strNE(tmpbuf,rcline[ngnum]+rcnums[ngnum]))
	    checkexpired(ngnum,ngsize);	/* this might realloc rcline */
    }
    nums = rcline[ngnum]+rcnums[ngnum];
    length = strlen(nums);
    if (length >= 60)
	mybuf = safemalloc((MEM_SIZE)(length+5));
    strcpy(mybuf,nums);
    mybuf[length++] = ',';
    mybuf[length] = '\0';
    for (s = mybuf; isspace(*s); s++)
	    ;
    for ( ; (c = index(s,',')) != Nullch ; s = ++c) {
				    /* for each range */
	*c = '\0';			/* keep index from running off */
	if ((h = index(s,'-')) != Nullch)	/* find - in range, if any */
	    unread -= (newmax = atol(h+1)) - atol(s) + 1;
	else if (newmax = atol(s))
	    unread--;		/* recalculate length */
	if (newmax > ngsize) {	/* paranoia check */
	    unread = -1;
	    break;
	}
    }
    if (unread >= 0)		/* reasonable number? */
	toread[ngnum] = (ART_UNREAD)unread;
					/* remember how many are left */
    else {				/* SOMEONE RESET THE NEWSGROUP!!! */
	toread[ngnum] = (ART_UNREAD)ngsize;
					/* assume nothing carried over */
	printf("Warning!  Somebody reset %s--assuming nothing read.\n",
	    rcline[ngnum]);
	*(rcline[ngnum] + rcnums[ngnum]) = '\0';
	paranoid = TRUE;		/* enough to make a guy paranoid */
    }
    if (mybuf != tmpbuf)
	free(mybuf);
    if (rcchar[ngnum] == NEGCHAR)
	toread[ngnum] = TR_UNSUB;
}

/* make sure expired articles are marked as read */

void
checkexpired(ngnum,ngsize)
register NG_NUM ngnum;
ART_NUM ngsize;
{
    register ART_NUM a1st = getabsfirst(ngnum,ngsize);
    register char *s, *t;
    register ART_NUM num, lastnum = 0;
    char *mbuf, *newnum;

    if (a1st<=1)
	return;
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("1-%ld->\n%s%c%s\n",(long)(a1st-1),rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
    for (s = rcline[ngnum] + rcnums[ngnum]; isspace(*s); s++);
    while (*s && (num = atol(s)) <= a1st) {
	while (isdigit(*s)) s++;
	while (*s && !isdigit(*s)) s++;
	lastnum = num;
    }
    if (*s) {
	if (s[-1] == '-') {			/* landed in a range? */
	    if (lastnum != 1)
		sprintf(rcline[ngnum]+rcnums[ngnum]," 1-%s",s);
	    goto ret;
	}
    }
    /* s now points to what should follow first range */
    if (s - rcline[ngnum] > rcnums[ngnum] + 10) 
	mbuf = rcline[ngnum];
    else {
	mbuf = safemalloc((MEM_SIZE)(rcnums[ngnum] + strlen(s) + 10));
	strcpy(mbuf,rcline[ngnum]);
    }
    newnum = t = mbuf+rcnums[ngnum];
    sprintf(t," 1-%ld",(long)(a1st - (lastnum != a1st)));
    if (*s) {
	t += strlen(t);
	*t++ = ',';
	strcpy(t,s);
    }
    if (mbuf == rcline[ngnum]) {
	rcline[ngnum] = saferealloc(rcline[ngnum],
	    (MEM_SIZE)(rcnums[ngnum] + strlen(newnum) + 1));
    }
    else {
	free(rcline[ngnum]);
	rcline[ngnum] = mbuf;
    }

ret:
#ifdef DEBUGGING
    if (debug & DEB_XREF_MARKER) {
	printf("%s%c%s\n",rcline[ngnum],rcchar[ngnum],
	  rcline[ngnum] + rcnums[ngnum]);
    }
#endif
}

!STUFFY!FUNK!
echo Extracting MANIFEST
cat >MANIFEST <<'!STUFFY!FUNK!'
After all the rn kits are run you should have the following files:

Filename	Kit Description
--------	--- -----------
Configure        2  A shell script that installs everything system dependent.
EXTERN.h         8  When included, makes other includes not belong to me.
HACKERSGUIDE     6  A brief guide to the contorted innards of rn.
INTERN.h         8  When included, makes other includes belong to me.
MANIFEST         4  This list of files.
Makefile.SH      6  The makefile.
Pnews.SH         5  A news posting shell script that knows about -h.
Pnews.man        7  Manual page for Pnews.
README           6  Installation instructions.
Rnmail.SH        6  A mailer that knows about -h.
Rnmail.man       7  Manual page for Rnmail.
Wishlist         8  What the next version wants in it.
addng.c          7  Routines for scanning the active file for new newsgroups.
addng.h          8  Public info regarding addng.c.
art.c            3  Routines to display an article.
art.h            8  Public info regarding art.c.
art.help.SH      3  Shell script for help at the article level.
artio.c          7  Reserved for the article abstract type, someday.
artio.h          8  Public info regarding artio.c.
artsrch.c        6  Routines for searching among articles.
artsrch.h        8  Public info regarding artsrch.c.
artstate.h       8  Info on the current state of the article.
backpage.c       5  Routines for paging backwards in articles.
backpage.h       8  Public info regarding backpage.c.
bits.c           1  Bitmap management functions.
bits.h           7  Public info regarding bits.c.
cheat.c          7  Routines to do lookahead of several types.
cheat.h          8  Public info regarding cheat.c.
common.h         3  Global info.
final.c          7  Finalization (exit) routines.
final.h          8  Public info regarding final.c.
head.c           6  Header parsing routines.
head.h           7  Public info regarding head.c.
header.c.pat     7  DOXREFS patch for header.c.
header.h.pat     8  DOXREFS patch for header.h.
help.c           6  Help routines.
help.h           8  Public info regarding help.c.
inews.c.1.pat    2  DOXREFS and LINKART patches for 2.10.1 news.
inews.c.2.pat    5  DOXREFS and LINKART patches for 2.10.2 news.
init.c           6  Initialization (startup) routines.
init.h           8  Public info regarding init.c.
intrp.c          4  Filename expansion and % interpretation routines.
intrp.h          8  Public info regarding intrp.c.
kfile.c          6  KILL file routines.
kfile.h          8  Public info regarding kfile.c.
kitleader        8  Shell script to produce front of kit.
kitlists.c       7  Knapsack packer.
kittrailer       8  Shell script to produce end of kit.
last.c           7  Routines for handling the .rnlast file.
last.h           8  Public info regarding last.c.
makedepend.SH    7  Shell script to generate make dependencies.
makedir.SH       7  Shell script to make nested subdirectories.
makedist         1  Shell script to make a distribution kit.
makekit          8  Shell script to make a kit file.
manifake         8  Shell script to make MANIFEST.new file.
manimake         8  Shell script to make MANIFEST file.
mbox.saver.SH    8  Shell script to save an article to a mailbox.
ndir.c           7  4.2 directory routine emulation.
ndir.h           7  Public info regarding ndir.c.
newsetup.SH      7  Shell script to create a .newsrc file.
newsetup.man     7  Manual page for newsetup.
newsgroups.SH    7  Shell script to list unsubscribed newsgroups.
newsgroups.man   7  Manual page for newsgroups.
newsnews.SH      7  A motd-like file that rn may print at startup.
ng.c             2  Routines to display a newsgroup.
ng.h             8  Public info regarding ng.c.
ng.help.SH       7  Shell script to do newsgroup selection help.
ngdata.c         7  General data fetching routines for a newsgroup.
ngdata.h         8  Public info regarding ngdata.c.
ngsrch.c         7  Routines to search among newsgroups.
ngsrch.h         8  Public info regarding ngsrch.c.
ngstuff.c        6  Support routines for ng.c.
ngstuff.h        8  Public info regarding ng.c.
norm.saver.SH    8  Shell script to save an article to a normal file.
only.c           7  Routines to perform newsgroup restriction.
only.h           2  Public info regarding only.c.
pager.help.SH    7  Shell script for help at the pager level.
rcln.c           4  Routines to mung a .newsrc line.
rcln.h           8  Public info regarding rcln.c.
rcstuff.c        3  Routines to mung the .newsrc file.
rcstuff.h        7  Public info regarding rcstuff.c.
respond.c        5  Various routines for doing things with articles.
respond.h        8  Public info regarding respond.c.
rn.c             5  Main program.
rn.h             8  Public info regarding rn.c.
rn.man           1  Manual pages for rn. PLEASE READ.
search.c         4  Regular expression processing ala emacs.
search.h         7  Public info regarding search.c.
subs.help.SH     7  Shell script for help for escape substitutions.
sw.c             5  Switch processing routines.
sw.h             8  Public info regarding switch.c.
term.c           4  Terminal interface routines.
term.h           6  Public info regarding term.c.
util.c           5  Utility routines.
util.h           8  Public info regarding util.c.
!STUFFY!FUNK!
echo ""
echo "End of kit 4 (of 8)"
cat /dev/null >kit4isdone
config=true
for iskit in 1 2 3 4 5 6 7 8; do
    if test -f kit${iskit}isdone; then
	echo "You have run kit ${iskit}."
    else
	echo "You still need to run kit ${iskit}."
	config=false
    fi
done
case $config in
    true)
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
esac
: I do not append .signature, but someone might mail this.
exit