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

echo "This is rn kit 4 (of 10).  If kit 4 is complete, the line"
echo '"'"End of kit 4 (of 10)"'" 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!'
/* $Id: intrp.c,v 4.4 1991/09/09 20:18:23 sob Exp sob $
 *
 * $Log: intrp.c,v $
 * Revision 4.4  1991/09/09  20:18:23  sob
 * release 4.4
 *
 *
 * 
 */
/* This software is Copyright 1991 by Stan Barber. 
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 */
#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 "respond.h"
#include "rcstuff.h"
#include "bits.h"
#include "artio.h"
#include "term.h"
#include "final.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 utsn;
#	undef SITENAME
#	define SITENAME utsn.nodename
#   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 */

#ifdef CONDSUB
char *skipinterp ANSI((char *,char *));
#endif

static void abort_interp ANSI((void));

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

    spool = savestr(filexp(SPOOL));	/* usually /usr/spool/news */
    
    /* get environmental stuff */

#ifdef NEWSADMIN
    {
#ifdef GETPWENT
	struct passwd *getpwnam();
	struct passwd *pwd = getpwnam(NEWSADMIN);

	if (pwd != NULL)
	    newsuid = pwd->pw_uid;
#else
#ifdef TILDENAME
	char tildenews[2+sizeof NEWSADMIN];
	strcpy(tildenews, "~");
	strcat(tildenews, NEWSADMIN);
	(void) filexp(tildenews);
#else
	#error "At least one of GETPWENT or TILDENAME needed to get NEWSADMIN"
#endif  /* TILDENAME */
#endif	/* GETPWENT */
    }
#endif	/* NEWSADMIN */
    /* get home directory */

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

    dotdir = getval("DOTDIR",homedir);

    /* get login name */

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

#ifdef NEWSADMIN
    /* if this is the news admin than load his UID into newsuid */

    if ( strEQ(logname,NEWSADMIN) )
	newsuid = getuid();
#endif

    if (checkflag)			/* that getwd below takes ~1/3 sec. */
	return;				/* and we do not need it for -c */
    getwd(tcbuf);			/* find working directory name */
    origdir = savestr(tcbuf);		/* and remember it */

    /* get the real name of the person (%N) */
    /* Must be done after logname is read in because BERKNAMES uses that */

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

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

    headname = savestr(filexp(HEADNAME));

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

#ifdef HOSTFILE
    if ((tmpfp = fopen(HOSTFILE,"r")) == NULL) {
	hostname = "unknown";
	printf("Warning: Couldn't open %s to determine hostname!\n", HOSTFILE); 
    } else {
	fgets(buf, sizeof(buf), tmpfp);
	buf[strlen(buf)-1] = 0;
	hostname = savestr(buf);
	fclose(tmpfp);
    }
#else
#ifdef GETHOSTNAME
    gethostname(buf,sizeof buf);
    hostname = savestr(buf);
#else
#ifdef DOUNAME
    /* get sysname */
    uname(&utsn);
#else
#ifdef PHOSTNAME
    {
	FILE *popen();
	FILE *pipefp = popen(PHOSTNAME,"r");
	
	if (pipefp == Nullfp) {
	    printf("Can't find hostname\n");
	    sig_catcher(0);
	}
	fgets(buf,sizeof buf,pipefp);
	buf[strlen(buf)-1] = '\0';	/* wipe out newline */
	hostname = savestr(buf);
	pclose(pipefp);
    }
#endif	/* PHOSTNAME */
#endif	/* DOUNAME */
#endif	/* GETHOSTNAME */
#endif	/* HOSTFILE */
    if (index(SITENAME,'.') == NULL) {
	sprintf(buf, "%s.%s", SITENAME, OURDOMAIN);
	sitename = savestr(buf);
    } else
	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[CBUFLEN];
    char scrbuf[CBUFLEN];
    register char *d;

#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("< %s\n",s) FLUSH;
#endif
    interp(filename, (sizeof filename), s);	
					/* interpret any % escapes */
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	printf("%% %s\n",filename) FLUSH;
#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) FLUSH;
#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);
		strcat(scrbuf, s);
		strcpy(filename, scrbuf);
#ifdef DEBUGGING
		if (debug & DEB_FILEXP)
		    printf("r %s %s\n",tildename,tildedir) FLUSH;
#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);
		    if ( pwd == NULL){
			printf("%s is an unknown user",tildename) FLUSH;
			sig_catcher(0);
		    }
		    sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
		    tildedir = savestr(pwd->pw_dir);
		    strcpy(filename,scrbuf);
		    endpwent();
		}
#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;
		    
		    if (pfp == Nullfp) {
			printf(cantopen,"passwd") FLUSH;
			sig_catcher(0);
		    }
		    while (fgets(tmpbuf,512,pfp) != Nullch) {
			d = cpytill(scrbuf,tmpbuf,':');
#ifdef DEBUGGING
			if (debug & DEB_FILEXP)
			    printf("p %s\n",tmpbuf) FLUSH;
#endif
			if (strEQ(scrbuf,tildename)) {
			    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) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("~login not impl.\n",stdout) FLUSH;
#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) FLUSH;
#endif
	interp(filename, (sizeof 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) FLUSH;
#endif
    return filename;
}

#ifdef CONDSUB
/* skip interpolations */

char *
skipinterp(pattern,stoppers)
register char *pattern;
char *stoppers;
{

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
#ifdef DEBUGGING
	if (debug & 8)
	    printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
#endif
	if (*pattern == '%' && pattern[1]) {
	    switch (*++pattern) {
	    case '{':
		for (pattern++; *pattern && *pattern != '}'; pattern++)
		    if (*pattern == '\\')
			pattern++;
		break;
	    case '[':
		for (pattern++; *pattern && *pattern != ']'; pattern++)
		    if (*pattern == '\\')
			pattern++;
		break;
#ifdef CONDSUB
	    case '(': {
		pattern = skipinterp(pattern+1,"!=");
		if (!*pattern)
		    goto getout;
		for (pattern++; *pattern && *pattern != '?'; pattern++)
		    if (*pattern == '\\')
			pattern++;
		if (!*pattern)
		    goto getout;
		pattern = skipinterp(pattern+1,":)");
		if (*pattern == ':')
		    pattern = skipinterp(pattern+1,")");
		break;
	    }
#endif
#ifdef BACKTICK
	    case '`': {
		pattern = skipinterp(pattern+1,"`");
		break;
	    }
#endif
#ifdef PROMPTTTY
	    case '"':
		pattern = skipinterp(pattern+1,"\"");
		break;
#endif
	    default:
		break;
	    }
	    pattern++;
	}
	else {
	    if (*pattern == '^' && pattern[1])
		pattern += 2;
	    else if (*pattern == '\\' && pattern[1])
		pattern += 2;
	    else
		pattern++;
	}
    }
getout:
    return pattern;			/* where we left off */
}
#endif

/* interpret interpolations */

char *
dointerp(dest,destsize,pattern,stoppers)
register char *dest;
register int destsize;
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;
    int metabit = 0;

    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
#ifdef DEBUGGING
	if (debug & 8)
	    printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
#endif
	if (*pattern == '%' && pattern[1]) {
	    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,destsize,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) FLUSH;
			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,destsize,pattern+1,":)");
			if (*pattern == ':')
			    pattern = skipinterp(pattern+1,")");
		    }
		    else {
			pattern = skipinterp(pattern+1,":)");
			if (*pattern == ':')
			    pattern++;
			pattern = dointerp(dest,destsize,pattern,")");
		    }
		    s = dest;
		    bra_compex = oldbra_compex;
		    free_compex(&cond_compex);
		    break;
		}
#endif
#ifdef BACKTICK
		case '`': {
		    FILE *pipefp, *popen();

		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
		    pipefp = popen(scrbuf,"r");
		    if (pipefp != Nullfp) {
			int len;

			len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
			    pipefp);
			scrbuf[len] = '\0';
			pclose(pipefp);
		    }
		    else {
			printf("\nCan't run %s\n",scrbuf);
			*scrbuf = '\0';
		    }
		    for (s=scrbuf; *s; s++) {
			if (*s == '\n') {
			    if (s[1])
				*s = ' ';
			    else
				*s = '\0';
			}
		    }
		    s = scrbuf;
		    break;
		}
#endif
#ifdef PROMPTTTY
		case '"':
		    pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
		    fputs(scrbuf,stdout) FLUSH;
		    resetty();
		    gets(scrbuf);
		    noecho();
		    crmode();
		    s = scrbuf;
		    break;
#endif
		case '~':
		    s = homedir;
		    break;
		case '.':
		    s = dotdir;
		    break;
		case '$':
		    s = scrbuf;
		    sprintf(s,"%d",getpid());
		    break;
		case '#':
		    s = scrbuf;
		    sprintf(s,"%d",perform_cnt);
		    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;
#ifdef SERVER
		    sprintf(s,"%s/rrn%ld.%d",spool,(long)art,getpid());
#else
		    sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
#endif
#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 'e':
		    s = (extractprog ? extractprog : "-");
		    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 
			s = ngs_buf = fetchlines(art,NGS_LINE);
		    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,MESSID_LINE);
		    if (*s && *s != '<') {
			sprintf(scrbuf,"<%s>",artid_buf);
			s = scrbuf;
		    }
		    break;
		case 'I':			/* ref article indicator */
		    s = scrbuf;
		    sprintf(scrbuf,"'%s'",indstr);
		    break;
		case 'l':			/* rn library */
#ifdef NEWSADMIN
		    s = newsadmin;
#else
		    s = "???";
#endif
		    break;
		case 'L':			/* login id */
		    s = logname;
		    break;
		case 'm':		/* current mode */
		    s = scrbuf;
		    *s = mode;
		    s[1] = '\0';
		    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 = getenv("NEWSORG");
		    if ( s == Nullch) 
			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,(sizeof 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,(sizeof scrbuf),refs_buf);
			/* no more than 3 prior references allowed,
			** including the one concatenated below */
			if ((s = rindex(scrbuf,'<')) > scrbuf) {
			    *s = '\0';
			    h = rindex(scrbuf,'<');
			    *s = '<';
			    if (h > scrbuf)
				strcpy(scrbuf,h);
			}
		    }
		    else
			*scrbuf = '\0';
		    if (!artid_buf)
			artid_buf = fetchlines(art,MESSID_LINE);
		    if (artid_buf[0] == '<')
			safecat(scrbuf,artid_buf,sizeof(scrbuf));
		    else if (artid_buf[0]) {
			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", TRUE))
			*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': {
		    int unseen;

		    unseen = (art <= lastart) && !was_read(art);
		    sprintf(scrbuf,"%ld",(long)toread[ng]-unseen);
		    s = scrbuf;
		    break;
		}
		case 'x':			/* news library */
		    s = lib;
		    break;
		case 'X':			/* rn library */
		    s = rnlib;
		    break;
		case 'z':
#ifdef LINKART
		    s = linkartname;	/* so Eunice people get right file */
#else
		    s = scrbuf;
		    sprintf(s,"%ld",(long)art);
#endif
		    if (stat(s,&filestat) < 0)
			filestat.st_size = 0L;
		    sprintf(scrbuf,"%5ld",(long)filestat.st_size);
		    s = scrbuf;
		    break;
		default:
		    if (--destsize <= 0)
			abort_interp();
		    *dest++ = *pattern | metabit;
		    s = nullstr;
		    break;
		}
	    }
	    if (!s)
		s = nullstr;
	    pattern++;
	    if (upper || lastcomp) {
		char *t;

		if (s != scrbuf) {
		    safecpy(scrbuf,s,(sizeof scrbuf));
		    s = scrbuf;
		}
		if (upper || !(t=rindex(s,'/')))
		    t = s;
		while (*t && !isalpha(*t))
		    t++;
		if (islower(*t))
		    *t = toupper(*t);
	    }
	    i = metabit;		/* maybe get into register */
	    if (s == dest) {
		while (*dest) {
		    if (--destsize <= 0)
			abort_interp();
		    *dest++ |= i;
		}
	    }
	    else {
		while (*s) {
		    if (--destsize <= 0)
			abort_interp();
		    *dest++ = *s++ | i;
		}
	    }
	}
	else {
	    if (--destsize <= 0)
		abort_interp();
	    if (*pattern == '^' && pattern[1]) {
		++pattern;			/* skip uparrow */
		i = *pattern;		/* get char into a register */
		if (i == '?')
		    *dest++ = '\177' | metabit;
		else if (i == '(') {
		    metabit = 0200;
		    destsize++;
		}
		else if (i == ')') {
		    metabit = 0;
		    destsize++;
		}
		else
		    *dest++ = i & 037 | metabit;
		pattern++;
	    }
	    else if (*pattern == '\\' && pattern[1]) {
		++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 | metabit;
		    --pattern;
		}
		else if (i == 'b')
		    *dest++ = '\b' | metabit;
		else if (i == 'f')
		    *dest++ = '\f' | metabit;
		else if (i == 'n')
		    *dest++ = '\n' | metabit;
		else if (i == 'r')
		    *dest++ = '\r' | metabit;
		else if (i == 't')
		    *dest++ = '\t' | metabit;
		else
		    *dest++ = i | metabit;
		pattern++;
	    }
	    else
		*dest++ = *pattern++ | metabit;
	}
    }
    *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,destsize,pattern)
char *dest;
int destsize;
char *pattern;
{
    dointerp(dest,destsize,pattern,Nullch);
#ifdef DEBUGGING
    if (debug & DEB_FILEXP)
	fputs(dest,stdout);
#endif
}

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

void
refscpy(dest,destsize,src)
register char *dest, *src;
register int destsize;
{
    register char *dot, *at, *beg;
    char tmpbuf[64];
    
    while (*src) {
	if (*src != '<') {
	    if (--destsize <= 0)
		break;
	    *dest++ = '<';
	    at = dot = Nullch;
	    beg = src;
	    while (*src && *src != ' ' && *src != ',') {
		if (*src == '.')
		    dot = src;
		else if (*src == '@')
		    at = src;
		if (--destsize <= 0)
		    break;
		*dest++ = *src++;
	    }
	    if (destsize <= 0)
		break;
	    if (dot && !at) {
		int len;

		*dest = *dot++ = '\0';
		sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
		len = strlen(tmpbuf);
		if (destsize > len) {
		    strcpy(dest,tmpbuf);
		    dest = dest + len;
		    destsize -= len;
		}
	    }
	    if (--destsize <= 0)
		break;
	    *dest++ = '>';
	}
	else {
	    while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
	    if (destsize <= 0)
		break;
	}
	while (*src == ' ' || *src == ',') src++;
	if (*src && --destsize > 0)
	    *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;
    char tmpbuf[512];

#ifdef PASSNAMES
#ifdef GETPWENT
#ifndef convex
    struct passwd *getpwuid();
#endif
    struct passwd *pwd = getpwuid(uid);
    
    s = pwd->pw_gecos;
#else
    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 */
	c = buf + strlen(buf);		/* in the middle of the night */
	strcat(c,logname);		/* before the morning after */
	strcat(c,s+1);
	if (islower(*c))
	    *c = toupper(*c);		/* 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
}

static void
abort_interp()
{
    fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
    sig_catcher(0);
}


!STUFFY!FUNK!
echo Extracting ng.c
cat >ng.c <<'!STUFFY!FUNK!'
/* $Id: ng.c,v 4.4 1991/09/09 20:23:31 sob Exp sob $
 *
 * $Log: ng.c,v $
 * Revision 4.4  1991/09/09  20:23:31  sob
 * release 4.4
 *
 *
 * 
 */
/* This software is Copyright 1991 by Stan Barber. 
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 */

#include "EXTERN.h"
#include "common.h"
#include "rn.h"
#include "term.h"
#include "final.h"
#include "util.h"
#include "artsrch.h"
#include "cheat.h"
#include "help.h"
#include "kfile.h"
#include "rcstuff.h"
#include "head.h"
#include "bits.h"
#include "art.h"
#include "artio.h"
#include "ngstuff.h"
#include "intrp.h"
#include "respond.h"
#include "ngdata.h"
#include "backpage.h"
#include "rcln.h"
#include "last.h"
#include "search.h"
#ifdef SERVER
#include "server.h"
#endif
#include "uudecode.h"
#include "INTERN.h"
#include "ng.h"
#include "artstate.h"			/* somebody has to do it */

/* art_switch() return values */

#define AS_NORM 0
#define AS_INP 1
#define AS_ASK 2
#define AS_CLEAN 3

ART_NUM recent_art = -1;	/* previous article # for '-' command */
ART_NUM curr_art = -1;		/* current article # */
int exit_code = NG_NORM;

void
ng_init()
{

#ifdef KILLFILES
    open_kfile(KF_GLOBAL);
#endif
#ifdef CUSTOMLINES
    init_compex(&hide_compex);
    init_compex(&page_compex);
#endif
}

/* do newsgroup on line ng with name ngname */

/* assumes that we are chdir'ed to SPOOL, and assures that that is
 * still true upon return, but chdirs to SPOOL/ngname in between
 *
 * If you can understand this routine, you understand most of the program.
 * The basic structure is:
 *	for each desired article
 *		for each desired page
 *			for each line on page
 *				if we need another line from file
 *					get it
 *					if it's a header line
 *						do special things
 *				for each column on page
 *					put out a character
 *				end loop
 *			end loop
 *		end loop
 *	end loop
 *
 *	(Actually, the pager is in another routine.)
 *
 * The chief problem is deciding what is meant by "desired".  Most of
 * the messiness of this routine is due to the fact that people want
 * to do unstructured things all the time.  I have used a few judicious
 * goto's where I thought it improved readability.  The rest of the messiness
 * arises from trying to be both space and time efficient.  Have fun.
 */

int
do_newsgroup(start_command)
char *start_command;			/* command to fake up first */
{
#ifdef SERVER
    char artname[MAXFILENAME];
    char intrpwork[MAXFILENAME];
    static long our_pid=0;
#endif /* SERVER */
    char oldmode = mode;
    register long i;			/* scratch */
    int skipstate;			/* how many unavailable articles */
					/*   have we skipped already? */
    
    char *whatnext = "%sWhat next? [%s]";

#ifdef SERVER
    if (our_pid == 0)           /* Agreed, this is gross */
        our_pid = getpid();
#endif /* SERVER */

#ifdef ARTSEARCH
    srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0);
					/* did they say -S? */
#endif
    
    mode = 'a';
    recent_art = curr_art = -1;
    exit_code = NG_NORM;

#ifdef SERVER
    sprintf(ser_line, "GROUP %s", ngname);
    put_server(ser_line);
    if (nntp_get(ser_line, sizeof(ser_line)) < 0) {
	fprintf(stderr, "\nrrn: Unexpected close of server socket.\n");
	finalize(1);
    }
    if (*ser_line != CHAR_OK) {
	if (atoi(ser_line) != ERR_NOGROUP){
		fprintf(stderr, "\nrrn: server response to GROUP %s:\n%s\n",
			ngname, ser_line);
		finalize(1);
	}
	return (-1);
    }
#else /* not SERVER */
    if (eaccess(ngdir,5)) {		/* directory read protected? */
	if (eaccess(ngdir,0)) {
#ifdef VERBOSE
	    IF(verbose)
		printf("\nNewsgroup %s does not have a spool directory!\n",
		    ngname) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		printf("\nNo spool for %s!\n",ngname) FLUSH;
#endif
#ifdef CATCHUP
	    catch_up(ng);
#endif
	    toread[ng] = TR_NONE;
	}
	else {
#ifdef VERBOSE
	    IF(verbose)
		printf("\nNewsgroup %s is not currently accessible.\n",
		    ngname) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		printf("\n%s not readable.\n",ngname) FLUSH;
#endif
	    toread[ng] = TR_NONE;	/* make this newsgroup invisible */
					/* (temporarily) */
	}
	mode = oldmode;
	return -1;
    }

    /* chdir to newsgroup subdirectory */

    if (chdir(ngdir)) {
	printf(nocd,ngdir) FLUSH;
	mode = oldmode;
	return -1;
    }
#endif /* SERVER */

#ifdef CACHESUBJ
    subj_list = Null(char **);		/* no subject list till needed */
#endif
    
    /* initialize control bitmap */

    if (initctl()) {
	mode = oldmode;
	return -1;
    }

    /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */

    in_ng = TRUE;			/* tell the world we are here */
    forcelast = TRUE;			/* if 0 unread, do not bomb out */

    /* remember what newsgroup we were in for sake of posterity */

    writelast();

    /* see if there are any special searches to do */

#ifdef KILLFILES
    open_kfile(KF_LOCAL);
#ifdef VERBOSE
    IF(verbose)
	kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE);
    ELSE
#endif
#ifdef TERSE
	kill_unwanted(firstart,"Killing...\n\n",TRUE);
#endif
#endif
    art=firstart;
    
    /* do they want a special top line? */

    firstline = getval("FIRSTLINE",Nullch);

    /* custom line suppression, custom page ending */

#ifdef CUSTOMLINES
    if (hideline = getval("HIDELINE",Nullch))
	compile(&hide_compex,hideline,TRUE,TRUE);
    if (pagestop = getval("PAGESTOP",Nullch))
	compile(&page_compex,pagestop,TRUE,TRUE);
#endif

    /* now read each unread article */

    rc_changed = doing_ng = TRUE;	/* enter the twilight zone */
    skipstate = 0;			/* we have not skipped anything (yet) */
    checkcount = 0;			/* do not checkpoint for a while */
    do_fseek = FALSE;			/* start 1st article at top */
    if (art > lastart)
	art=firstart;			/* init the for loop below */
    for (; art<=lastart+1; ) {		/* for each article */

	/* do we need to "grow" the newsgroup? */

	if (art > lastart || forcegrow)
	    grow_ctl(getngsize(ng));
	check_first(art);		/* make sure firstart is still 1st */
	if (start_command) {		/* fake up an initial command? */
	    prompt = whatnext;
	    strcpy(buf,start_command);
	    free(start_command);
	    start_command = Nullch;
	    art = lastart+1;
	    goto article_level;
	}
	if (art>lastart) {		/* are we off the end still? */
	    ART_NUM ucount = 0;		/* count of unread articles left */

	    for (i=firstart; i<=lastart; i++)
		if (!(ctl_read(i)))
		    ucount++;		/* count the unread articles */
#ifdef DEBUGGING
	    /*NOSTRICT*/
	    if (debug && ((ART_NUM)toread[ng]) != ucount)
		printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount)
		  FLUSH;
#endif
	    /*NOSTRICT*/
	    toread[ng] = (ART_UNREAD)ucount;	/* this is perhaps pointless */
	    art = lastart + 1;		/* keep bitmap references sane */
	    if (art != curr_art) {
		recent_art = curr_art;
					/* remember last article # (for '-') */
		curr_art = art;      /* remember this article # */
	    }
	    if (erase_screen)
		clear();			/* clear the screen */
	    else
		fputs("\n\n",stdout) FLUSH;
#ifdef VERBOSE
	    IF(verbose)
		printf("End of newsgroup %s.",ngname);
					/* print pseudo-article */
	    ELSE
#endif
#ifdef TERSE
		printf("End of %s",ngname);
#endif
	    if (ucount) {
		    printf("  (%ld article%s still unread)",
			(long)ucount,ucount==1?nullstr:"s");
	    }
	    else {
		if (!forcelast)
		    goto cleanup;	/* actually exit newsgroup */
	    }
	    prompt = whatnext;
#ifdef ARTSEARCH
	    srchahead = 0;		/* no more subject search mode */
#endif
	    fputs("\n\n",stdout) FLUSH;
	    skipstate = 0;		/* back to none skipped */
	}
	else if (!reread && was_read(art)) {
					/* has this article been read? */
	    art++;			/* then skip it */
	    continue;
	}
	else if
	  (!reread && !was_read(art)
#ifdef SERVER
	    && nntpopen(art,GET_HEADER) == Nullfp) { 
#else
	    && artopen(art) == Nullfp) { /* never read it, & cannot find it? */
	    if (errno != ENOENT) {	/* has it not been deleted? */
#ifdef VERBOSE
		IF(verbose)
		    printf("\n(Article %ld exists but is unreadable.)\n",
			(long)art) FLUSH;
		ELSE
#endif /* VERBOSE */
#ifdef TERSE
		    printf("\n(%ld unreadable.)\n",(long)art) FLUSH;
#endif /* TERSE */
		skipstate = 0;
		sleep(2);
	    }
#endif /* SERVER */
	    switch(skipstate++) {
	    case 0:
		clear();
#ifdef VERBOSE
		IF(verbose)
		    fputs("Skipping unavailable article",stdout);
		ELSE
#endif /* VERBOSE */
#ifdef TERSE
		    fputs("Skipping",stdout);
#endif /* TERSE */
		pad(just_a_sec/3);
		sleep(1);
		break;
	    case 1:
		fputs("..",stdout);
		fflush(stdout);
		break;
	    default:
		putchar('.');
		fflush(stdout);
#ifndef SERVER
#define READDIR
#ifdef READDIR
		{			/* fast skip patch */
		    ART_NUM newart;
		    
		    if (! (newart=getngmin(".",art)))
			newart = lastart+1;
		    for (i=art; i<newart; i++)
			oneless(i);
		    art = newart - 1;
		}
#endif /* READDIR */
#else
		{
			char	ser_line[NNTP_STRLEN];
			ART_NUM	newart;

			if (isfirstart) {
				sprintf(ser_line, "STAT %d",absfirst);
				put_server(ser_line);
				if (nntp_get(ser_line, sizeof(ser_line)) < 0) {
					fprintf(stderr, 
					"\nrrn: Unexpected close of server socket.\n");
					finalize(1);
				}
				newart=absfirst;
				isfirstart=FALSE;
			} 
			else {
				put_server("NEXT");
				if  (nntp_get(ser_line, sizeof(ser_line)) < 0) {
					fprintf(stderr,
					"\nrrn: unexpected close of server socket.\n");
					finalize(1);
				}
				if (ser_line[0] != CHAR_OK) {
					newart = lastart + 1;
				}
				else
					newart = atoi(ser_line+4);
			}				
			for (i=art; i<newart; i++)
				oneless(i);
			art = newart - 1;
		}
#endif /* SERVER */
		break;
	    }
	    oneless(art);		/* mark deleted as read */
	    art++;			/* try next article */
	    continue;
	}
	else {				/* we have a real live article */
	    skipstate = 0;		/* back to none skipped */
	    if (art != curr_art) {
		recent_art = curr_art;
					/* remember last article # (for '-') */
		curr_art = art;      /* remember this article # */
	    }
	    if (!do_fseek) {		/* starting at top of article? */
		artline = 0;		/* start at the beginning */
		topline = -1;		/* and remember top line of screen */
					/*  (line # within article file) */
	    }
	    clear();			/* clear screen */
	    if (!artopen(art)) {	/* make sure article is found & open */
		printf("Article %ld of %s is not available.\n\n",
		    (long)art,ngname) FLUSH;
		prompt = whatnext;
#ifdef ARTSEARCH
		srchahead = 0;
#endif
	    }
	    else {			/* found it, so print it */
		switch (do_article()) {
		case DA_CLEAN:		/* quit newsgroup */
		    goto cleanup;
		case DA_TOEND:		/* do not mark as read */
		    goto reask_article; 
		case DA_RAISE:		/* reparse command at end of art */
		    goto article_level;
		case DA_NORM:		/* normal end of article */
		    break;
		}
	    }
	    if (art >= absfirst)	/* don't mark non-existant articles */
		mark_as_read();		/* mark current article as read */
	    do_hiding = TRUE;
#ifdef ROTATION
	    rotate = FALSE;
#endif
	}

/* if these gotos bother you, think of this as a little state machine */

reask_article:
#ifdef MAILCALL
	setmail();
#endif
	setdfltcmd();
#ifdef CLEAREOL
	if (erase_screen && can_home_clear)
	    clear_rest();
#endif /* CLEAREOL */
	unflush_output();		/* disable any ^O in effect */
	standout();			/* enter standout mode */
	printf(prompt,mailcall,dfltcmd);/* print prompt, whatever it is */
	un_standout();			/* leave standout mode */
	putchar(' ');
	fflush(stdout);
reinp_article:
	reread = FALSE;
	eat_typeahead();
#ifdef PENDING
	look_ahead();			/* see what we can do in advance */
	if (!input_pending())
	    collect_subjects();		/* loads subject cache until */
					/* input is pending */
#endif
	getcmd(buf);
	if (errno || *buf == '\f') {
	    if (LINES < 100 && !int_count)
		*buf = '\f';		/* on CONT fake up refresh */
	    else {
		putchar('\n') FLUSH;		/* but only on a crt */
		goto reask_article;
	    }
	}
article_level:

	/* parse and process article level command */

	switch (art_switch()) {
	case AS_INP:			/* multichar command rubbed out */
	    goto reinp_article;
	case AS_ASK:			/* reprompt "End of article..." */
	    goto reask_article;
	case AS_CLEAN:			/* exit newsgroup */
	    goto cleanup;
	case AS_NORM:			/* display article art */
	    break;
	}
    }					/* end of article selection loop */
    
/* shut down newsgroup */

cleanup:
    uud_end();
#ifdef KILLFILES
    kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE);
					/* do cleanup from KILL file, if any */
#endif
    in_ng = FALSE;			/* leave newsgroup state */
    if (artfp != Nullfp) {		/* article still open? */
	fclose(artfp);			/* close it */
	artfp = Nullfp;			/* and tell the world */
#ifdef SERVER
        interp(intrpwork,MAXFILENAME-1, "%P");
        sprintf(artname, "%s/rrn%ld.%ld", intrpwork,(long) openart, our_pid);
        UNLINK(artname);
#endif /* SERVER */
	openart = 0;
    }
    putchar('\n') FLUSH;
#ifdef DELAYMARK
	yankback();				/* do a Y command */
#endif
	restore_ng();			/* reconstitute .newsrc line */
    doing_ng = FALSE;			/* tell sig_catcher to cool it */
    free(ctlarea);			/* return the control area */
#ifdef CACHESUBJ
    if (subj_list) {
	for (i=OFFSET(lastart); i>=0; --i)
	    if (subj_list[i])
		free(subj_list[i]);
#ifndef lint
	free((char*)subj_list);
#endif /* lint */
    }
#endif
    write_rc();				/* and update .newsrc */
    rc_changed = FALSE;			/* tell sig_catcher it is ok */
    if (chdir(spool)) {
	printf(nocd,spool) FLUSH;
	sig_catcher(0);
    }
#ifdef KILLFILES
    if (localkfp) {
	fclose(localkfp);
	localkfp = Nullfp;
    }
#endif
    mode = oldmode;
    return exit_code;
}					/* Whew! */

/* decide what to do at the end of an article */

int
art_switch()
{
    register ART_NUM i;
      
    setdef(buf,dfltcmd);
#ifdef VERIFY
    printcmd();
#endif

    switch (*buf) {
    case 'p':			/* find previous unread article */
	do {
	    if (art <= firstart)
		break;
	    art--;
#ifdef SERVER
	} while (was_read(art) || nntpopen(art,GET_HEADER) == Nullfp);
#else
	} while (was_read(art) || artopen(art) == Nullfp);
#endif
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case 'P':		/* goto previous article */
	if (art > absfirst)
	    art--;
	else {
#ifdef VERBOSE
	    IF(verbose)
		fprintf(stdout,"\n\
There are no%s articles prior to this one.\n\
",*buf=='P'?nullstr:" unread") FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fprintf(stdout,"\n\
No previous%s articles\n\
",*buf=='P'?nullstr:" unread") FLUSH;
#endif
	    art = curr_art;
	    return AS_ASK;
	}
	reread = TRUE;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case '-':
	if (recent_art >= 0) {
	    art = recent_art;
	    reread = TRUE;
#ifdef ARTSEARCH
	    srchahead = -(srchahead != 0);
#endif
	    return AS_NORM;
	}
	else {
	    exit_code = NG_MINUS;
	    return AS_CLEAN;
	}
    case 'n':		/* find next unread article? */
	if (art > lastart) {
	    if (!toread[ng])
		return AS_CLEAN;
	    art = firstart;
	}
#ifdef ARTSEARCH
	else if (scanon && srchahead) {
	    *buf = Ctl('n');
	    goto normal_search;
	}
#endif
	else
	    art++;

#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case 'N':			/* goto next article */
	if (art > lastart)
	    art = absfirst;
	else
	    art++;
	if (art <= lastart)
	    reread = TRUE;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case '$':
	art = lastart+1;
	forcelast = TRUE;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
    case '1': case '2': case '3':	/* goto specified article */
    case '4': case '5': case '6':	/* or do something with a range */
    case '7': case '8': case '9': case '.':
	forcelast = TRUE;
	switch (numnum()) {
	case NN_INP:
	    return AS_INP;
	case NN_ASK:
	    return AS_ASK;
	case NN_REREAD:
	    reread = TRUE;
#ifdef ARTSEARCH
	    if (srchahead)
		srchahead = -1;
#endif
	    break;
	case NN_NORM:
	    if (was_read(art)) {
		art = firstart;
		pad(just_a_sec/3);
	    }
	    else {
		putchar('\n');
		return AS_ASK;
	    }
	    break;
	}
	return AS_NORM;
    case Ctl('k'):
	edit_kfile();
	return AS_ASK;
    case 'K':
    case 'k':
    case Ctl('n'):	/* search for next article with same subject */
    case Ctl('p'):	/* search for previous article with same subject */
    case '/': case '?':
normal_search:
#ifdef ARTSEARCH
    {		/* search for article by pattern */
	char cmd = *buf;
	
	reread = TRUE;		/* assume this */
	page_line = 1;
	switch (art_search(buf, (sizeof buf), TRUE)) {
	case SRCH_ERROR:
	    art = curr_art;
	    return AS_ASK;
	case SRCH_ABORT:
	    art = curr_art;
	    return AS_INP;
	case SRCH_INTR:
#ifdef VERBOSE
	    IF(verbose)
		printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		printf("\n(Intr at %ld)\n",(long)art) FLUSH;
#endif
	    art = curr_art;
			    /* restore to current article */
	    return AS_ASK;
	case SRCH_DONE:
	    fputs("done\n",stdout) FLUSH;
	    pad(just_a_sec/3);	/* 1/3 second */
	    if (!srchahead) {
		art = curr_art;
		return AS_ASK;
	    }
	    art = firstart;
	    reread = FALSE;
	    return AS_NORM;
	case SRCH_SUBJDONE:
#ifdef UNDEF
	    fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH;
	    pad(just_a_sec/3);	/* 1/3 second */
#endif
	    art = firstart;
	    reread = FALSE;
	    return AS_NORM;
	case SRCH_NOTFOUND:
	    fputs("\n\n\n\nNot found.\n",stdout) FLUSH;
	    art = curr_art;  /* restore to current article */
	    return AS_ASK;
	case SRCH_FOUND:
	    if (cmd == Ctl('n') || cmd == Ctl('p'))
		oldsubject = TRUE;
	    break;
	}
	return AS_NORM;
    }
#else
    buf[1] = '\0';
    notincl(buf);
    return AS_ASK;
#endif
    case 'u':			/* unsubscribe from this newsgroup? */
	rcchar[ng] = NEGCHAR;
	return AS_CLEAN;
    case 'M':
#ifdef DELAYMARK
	if (art <= lastart) {
	    delay_unmark(art);
	    printf("\nArticle %ld will return.\n",(long)art) FLUSH;
	}
#else
	notincl("M");
#endif
	return AS_ASK;
    case 'm':
	if (art <= lastart) {
	    unmark_as_read();
	    printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH;
	}
	return AS_ASK;
    case 'c':			/* catch up */
      reask_catchup:
#ifdef VERBOSE
	IF(verbose)
	    in_char("\nDo you really want to mark everything as read? [yn] ",
		'C');
	ELSE
#endif
#ifdef TERSE
	    in_char("\nReally? [ynh] ", 'C');
#endif
	setdef(buf,"y");
#ifdef VERIFY
	printcmd();
#endif
	putchar('\n') FLUSH;
	if (*buf == 'h') {
#ifdef VERBOSE
	    IF(verbose)
		fputs("\
Type y or SP to mark all articles as read.\n\
Type n to leave articles marked as they are.\n\
Type u to mark everything read and unsubscribe.\n\
",stdout) FLUSH;
	    ELSE
#endif
#ifdef TERSE
		fputs("\
y or SP to mark all read.\n\
n to forget it.\n\
u to mark all and unsubscribe.\n\
",stdout) FLUSH;
#endif
	    goto reask_catchup;
	}
	else if (*buf == 'n' || *buf == 'q') {
	    return AS_ASK;
	}
	else if (*buf != 'y' && *buf != 'u') {
	    fputs(hforhelp,stdout) FLUSH;
	    settle_down();
	    goto reask_catchup;
	}
	for (i = firstart; i <= lastart; i++) {
	    oneless(i);		/* mark as read */
	}
#ifdef DELAYMARK
	if (dmfp)
	    yankback();
#endif
	if (*buf == 'u') {
	    rcchar[ng] = NEGCHAR;
	    return AS_CLEAN;
	}
	art = lastart+1;
	forcelast = FALSE;
	return AS_NORM;
    case 'Q':
	exit_code = NG_ASK;
	/* FALL THROUGH */
    case 'q':			/* go back up to newsgroup level? */
	return AS_CLEAN;
    case 'j':
	putchar('\n') FLUSH;
	if (art <= lastart)
	    mark_as_read();
	return AS_ASK;
    case 'h': {			/* help? */
	int cmd;

	if ((cmd = help_art()) > 0)
	    pushchar(cmd);
	return AS_ASK;
    }
    case '&':
	if (switcheroo()) /* get rest of command */
	    return AS_INP;	/* if rubbed out, try something else */
	return AS_ASK;
    case '#':
#ifdef VERBOSE
	IF(verbose)
	    printf("\nThe last article is %ld.\n",(long)lastart) FLUSH;
	ELSE
#endif
#ifdef TERSE
	    printf("\n%ld\n",(long)lastart) FLUSH;
#endif
	return AS_ASK;
    case '=': {			/* list subjects */
	char tmpbuf[256];
	ART_NUM oldart = art;
	int cmd;
	char *subjline = getval("SUBJLINE",Nullch);
#ifndef CACHESUBJ
	char *s;
#endif

	page_init();
#ifdef CACHESUBJ
	if (!subj_list)
	    fetchsubj(art,TRUE,FALSE);
#endif
	for (i=firstart; i<=lastart && !int_count; i++) {
#ifdef CACHESUBJ
	    if (!was_read(i) &&
	      (subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)) &&
	      *subj_list[OFFSET(i)] ) {
		sprintf(tmpbuf,"%5ld ", i);
		if (subjline) {
		    art = i;
		    interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
		}
		else
		    safecpy(tmpbuf + 6, subj_list[OFFSET(i)],
			(sizeof tmpbuf) - 6);
		if (cmd = print_lines(tmpbuf,NOMARKING)) {
		    if (cmd > 0)
			pushchar(cmd);
		    break;
		}
	    }
#else
	    if (!was_read(i) && (s = fetchsubj(i,FALSE,FALSE)) && *s) {
		sprintf(tmpbuf,"%5ld ", i);
		if (subjline) {	/* probably fetches it again! */
		    art = i;
		    interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
		}
		else
		    safecpy(tmpbuf + 6, s, (sizeof tmpbuf) - 6);
		if (cmd = print_lines(tmpbuf,NOMARKING)) {
		    if (cmd > 0)
			pushchar(cmd);
		    break;
		}
	    }
#endif
	}
	int_count = 0;
	art = oldart;
	return AS_ASK;
    }
    case '^':
	art = firstart;
#ifdef ARTSEARCH
	srchahead = 0;
#endif
	return AS_NORM;
#if defined(CACHESUBJ) && defined(DEBUGGING)
    case 'D':
	printf("\nFirst article: %ld\n",(long)firstart) FLUSH;
	if (!subj_list)
	    fetchsubj(art,TRUE,FALSE);
	if (subj_list != Null(char **)) {
	    for (i=1; i<=lastart && !int_count; i++) {
		if (subj_list[OFFSET(i)])
		    printf("%5ld %c %s\n",
			i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]) FLUSH;
	    }
	}
	int_count = 0;
	return AS_ASK;
#endif
    case 'v':
	if (art <= lastart) {
	    reread = TRUE;
	    do_hiding = FALSE;
	}
	return AS_NORM;
#ifdef ROTATION
    case Ctl('x'):
#endif
    case Ctl('r'):
#ifdef ROTATION
	rotate = (*buf==Ctl('x'));
#endif
	if (art <= lastart)
	    reread = TRUE;
	return AS_NORM;
#ifdef ROTATION
    case 'X':
	rotate = !rotate;
	/* FALL THROUGH */
#else
    case Ctl('x'):
    case 'x':
    case 'X':
	notincl("x");
	return AS_ASK;
#endif
    case 'l': case Ctl('l'):		/* refresh screen */
	if (art <= lastart) {
	    reread = TRUE;
	    clear();
	    do_fseek = TRUE;
	    artline = topline;
	    if (artline < 0)
		artline = 0;
	}
	return AS_NORM;
    case 'b': case Ctl('b'):		/* back up a page */
	if (art <= lastart) {
	    ART_LINE target;

	    reread = TRUE;
	    clear();
	    do_fseek = TRUE;
	    target = topline - (LINES - 2);
	    artline = topline;
	    if (artline >= 0) do {
		artline--;
	    } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0);
	    topline = artline;
	    if (artline < 0)
		artline = 0;
	}
	return AS_NORM;
    case '!':			/* shell escape */
	if (escapade())
	    return AS_INP;
	return AS_ASK;
    case 'C': {
	cancel_article();
	return AS_ASK;
    }
    case 'z': {
	supersede_article();	/* supersedes */
	return AS_ASK;
    }
    case 'R':
    case 'r': {			/* reply? */
	reply();
	return AS_ASK;
    }
    case 'F':
    case 'f': {			/* followup command */
	followup();
	forcegrow = TRUE;		/* recalculate lastart */
	return AS_ASK;
    }
    case '|':
    case 'w': case 'W':
    case 's': case 'S':		/* save command */
    case 'e':			/* extract command */
	if (save_article() == SAVE_ABORT)
	    return AS_INP;
	int_count = 0;
	return AS_ASK;
    case 'E':
	if (uu_out != Nullfp)
	    uud_end();
	else
	    putchar('\n') FLUSH;
	return AS_ASK;
#ifdef DELAYMARK
    case 'Y':				/* yank back M articles */
	yankback();
	art = firstart;			/* from the beginning */
	return AS_NORM;			/* pretend nothing happened */
#endif
#ifdef STRICTCR
    case '\n':
	fputs(badcr,stdout) FLUSH;
	return AS_ASK;
#endif
    default:
	printf("\n%s",hforhelp) FLUSH;
	settle_down();
	return AS_ASK;
    }
}

#ifdef MAILCALL
/* see if there is any mail */

void
setmail()
{
    if (! (mailcount++)) {
	char *mailfile = filexp(getval("MAILFILE",MAILFILE));
	
	if (stat(mailfile,&filestat) < 0 || !filestat.st_size
	    || filestat.st_atime > filestat.st_mtime)
	    mailcall = nullstr;
	else
	    mailcall = getval("MAILCALL","(Mail) ");
    }
    mailcount %= 10;			/* check every 10 articles */
}
#endif

void
setdfltcmd()
{
    if (!toread[ng]) {
	if (art > lastart)
	    dfltcmd = "qnp";
	else
	    dfltcmd = "npq";
    }
    else {
#ifdef ARTSEARCH
	if (srchahead)
	    dfltcmd = "^Nnpq";
	else
#endif
	    dfltcmd = "npq";
	}
}
!STUFFY!FUNK!
echo Extracting search.c
cat >search.c <<'!STUFFY!FUNK!'
/* $Id: search.c,v 4.4 1991/09/09 20:27:37 sob Exp sob $
 *
 * $Log: search.c,v $
 * Revision 4.4  1991/09/09  20:27:37  sob
 * release 4.4
 *
 *
 */

/* 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 || compex->expbuf == 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;
    }
   /*NOTREACHED*/
}
 
/* 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) FLUSH;
		    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) FLUSH;
		    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) FLUSH;
		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 INIT
cat >INIT <<'!STUFFY!FUNK!'
+Hfrom 
!STUFFY!FUNK!
echo ""
echo "End of kit 4 (of 10)"
cat /dev/null >kit4isdone
config=true
for iskit in 1 2 3 4 5 6 7 8 9 10 ; 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
