rn version 4.3 (kit 3 of 9)

sources-request at genrad.UUCP sources-request at genrad.UUCP
Fri May 10 21:47:19 AEST 1985


From: lwall at sdcrdcf.UUCP (Larry Wall)

---------------- cut here ---------------
#! /bin/sh

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

echo "This is rn kit 3 (of 9).  If kit 3 is complete, the line"
echo '"'"End of kit 3 (of 9)"'" 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.3 85/05/01 11:40:54 lwall Exp $
 *
 * $Log:	intrp.c,v $
 * Revision 4.3  85/05/01  11:40:54  lwall
 * Baseline for release with 4.3bsd.
 * 
 */

#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 "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 uts;
#	undef SITENAME
#	define SITENAME uts.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 */

char *dointerp();
char *getrealname();
#ifdef CONDSUB
char *skipinterp();
#endif

static void abort_interp();

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

    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 login name */

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

    /* 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 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");
	
	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
#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[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);

		    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;
		    
		    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)) {
#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) 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 '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,MESSID_LINE);
		    if (*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 = 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);
		    }
		    else
			*scrbuf = '\0';
		    if (!artid_buf)
			artid_buf = fetchlines(art,MESSID_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':			/* 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;

#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
}

static void
abort_interp()
{
    fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
    sig_catcher(0);
}
!STUFFY!FUNK!
echo Extracting common.h
cat >common.h <<'!STUFFY!FUNK!'
/* $Header: common.h,v 4.3 85/05/01 11:37:11 lwall Exp $
 * 
 * $Log:	common.h,v $
 * Revision 4.3  85/05/01  11:37:11  lwall
 * Baseline for release with 4.3bsd.
 * 
 */

#include "config.h"	/* generated by installation script */
#ifdef WHOAMI
#    include <whoami.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>

#ifndef isalnum
#   define isalnum(c) (isalpha(c) || isdigit(c))
#endif

#include <errno.h>
#include <signal.h>
#ifdef IOCTL
#include <sys/ioctl.h>
#endif IOCTL

#ifdef FCNTL
#   include <fcntl.h>
#endif

#ifdef TERMIO
#   include <termio.h>
#else
#   include <sgtty.h>
#endif

#ifdef GETPWENT
#   include <pwd.h>
#endif

#define BITSPERBYTE 8
#define LBUFLEN 512	/* line buffer length */
			/* (don't worry, .newsrc lines can exceed this) */
#ifdef pdp11
#   define CBUFLEN 256	/* command buffer length */
#   define PUSHSIZE 128
#else
#   define CBUFLEN 512	/* command buffer length */
#   define PUSHSIZE 256
#endif
#ifdef pdp11
#   define MAXFILENAME 128
#else
#   define MAXFILENAME 512
#endif
#define LONGKEY 15	/* longest keyword: currently "posting-version" */
#define FINISHCMD 0177

/* some handy defs */

#define bool char
#define TRUE (1)
#define FALSE (0)
#define Null(t) ((t)0)
#define Nullch Null(char *)
#define Nullfp Null(FILE *)

#define Ctl(ch) (ch & 037)

#define strNE(s1,s2) (strcmp(s1,s2))
#define strEQ(s1,s2) (!strcmp(s1,s2))
#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))

/* Things we can figure out ourselves */

#ifdef SIGTSTP
#   define BERKELEY 	/* include job control signals? */
#endif

#ifdef SIGPROF
#   define BSD42		/* do we have Berkeley 4.2? */
#endif

#ifdef FIONREAD
#   define PENDING
#else
#   ifdef O_NDELAY
#	define PENDING
#   endif
#endif

#ifdef EUNICE
#   define LINKART		/* add 1 level of possible indirection */
#   define UNLINK(victim) while (!unlink(victim))
#else
#   define UNLINK(victim) unlink(victim)
#endif

/* Valid substitutions for strings marked with % comment are:
 *	%a	Current article number
 *	%A	Full name of current article (%P/%c/%a)
 *		(if LINKART defined, is the name of the real article)
 *	%b	Destination of a save command, a mailbox or command
 *	%B	The byte offset to the beginning of the article for saves
 *		with or without the header
 *	%c	Current newsgroup, directory form
 *	%C	Current newsgroup, dot form
 *	%d	%P/%c
 *	%D	Old Distribution: line
 *	%f	Old From: line or Reply-To: line
 *	%F	Newsgroups to followup to from Newsgroups: and Followup-To:
 *	%h	Name of header file to pass to mail or news poster
 *	%H	Host name (yours)
 *	%i	Old Message-I.D.: line, with <>
 *	%I	Inclusion indicator
 *	%l	News administrator login name
 *	%L	Login name (yours)
 *	%M	Number of articles markd with M
 *	%n	Newsgroups from source article
 *	%N	Full name (yours)
 *	%o	Organization (yours)
 *	%O	Original working directory (where you ran rn from)
 *	%p	Your private news directory (-d switch)
 *	%P	Public news spool directory (SPOOLDIR)
 *	%r	Last reference (parent article id)
 *	%R	New references list
 *	%s	Subject, with all Re's and (nf)'s stripped off
 *	%S	Subject, with one Re stripped off
 *	%t	New To: line derived from From: and Reply-To (Internet always)
 *	%T	New To: line derived from Path:
 *	%u	Number of unread articles
 *	%U	Number of unread articles disregarding current article
 *	%x	News library directory, usually /usr/lib/news
 *	%X	Rn library directory, usually %x/rn
 *	%z	Size of current article in bytes.
 *	%~	Home directory
 *	%.	Directory containing . files
 *	%$	current process number
 *	%{name} Environment variable "name".  %{name-default} form allowed.
 *	%[name]	Header line beginning with "Name: ", without "Name: " 
 *	%"prompt"
 *		Print prompt and insert what is typed.
 *	%`command`
 *		Insert output of command.
 *	%(test_text=pattern?if_text:else_text)
 *		Substitute if_text if test_text matches pattern, otherwise
 *		substitute else_text.  Use != for negated match.
 *		% substitutions are done on test_text, if_text, and else_text.
 *		(Note: %() only works if CONDSUB defined.)
 *	%digit	Substitute the text matched by the nth bracket in the last
 *		pattern that had brackets.  %0 matches the last bracket
 *		matched, in case you had alternatives.
 *
 *	Put ^ in the middle to capitalize the first letter: %^C = Net.jokes
 *	Put _ in the middle to capitalize last component: %_c = net/Jokes
 *
 *	~ interpretation in filename expansion happens after % expansion, so
 *	you could put ~%{NEWSLOGNAME-news} and it will expand correctly.
 */

/* *** System Dependent Stuff *** */

/* NOTE: many of these are defined in the config.h file */

/* name of organization */
#ifndef ORGNAME
#   define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota"
#endif

#ifndef MBOXCHAR
#   define MBOXCHAR 'F'	/* how to recognize a mailbox by 1st char */
#endif

#ifndef ROOTID
#   define ROOTID 0        /* uid of superuser */
#endif

#ifdef NORMSIG
#   define sigset signal
#   define sigignore(sig) signal(sig,SIG_IGN)
#endif

#ifndef LOGDIRFIELD
#   define LOGDIRFIELD 6		/* Which field (origin 1) is the */
					/* login directory in /etc/passwd? */
					/* (If it is not kept in passwd, */
					/* but getpwnam() returns it, */
					/* define the symbol GETPWENT) */
#endif
#ifndef GCOSFIELD
#   define GCOSFIELD 5
#endif

#ifndef NEGCHAR
#   define NEGCHAR '!'
#endif

/* Space conservation section */

/* To save D space, cut down size of MAXRCLINE, NGMAX, VARYSIZE. */
#define MAXRCLINE 500	/* number of lines allowed in .newsrc */
			/* several parallel arrays affected. */
			/* (You can have more lines in the active file, */
			/* just not in the .newsrc) */
#define HASHSIZ 547	/* should be prime, and at least MAXRCLINE + 10% */
#define NGMAX 100	/* number of newsgroups allowed on command line */
			/* undefine ONLY symbol to disable "only" feature */
#define VARYSIZE 256	/* this makes a block 1024 bytes long in DECville */
			/* (used by virtual array routines) */

/* Undefine any of the following features to save both I and D space */
/* In general, earlier ones are easier to get along without */
/* Pdp11's without split I and D may have to undefine them all */
#define DEBUGGING	/* include debugging code */
#define PUSHBACK	/* macros and keymaps using pushback buffer */
#define SPEEDOVERMEM	/* use more memory to run faster */
#define WORDERASE	/* enable ^W to erase a word */
#define MAILCALL	/* check periodically for mail */
#define CLEAREOL	/* use clear to end-of-line instead of clear screen */
#define NOFIREWORKS	/* keep whole screen from flashing on certain */
			/* terminals such as older Televideos */
#define VERIFY		/* echo the command they just typed */
#define HASHNG		/* hash newsgroup lines for fast lookup-- */
			/* linear search used if not defined */
#define CONDSUB		/* allow %(cond?text:text) */
#define BACKTICK	/* allow %`command` */
#define PROMPTTTY	/* allow %"prompt" */
#define ULSMARTS	/* catch _^H in text and do underlining */
#define TERMMOD		/* allow terminal type modifier on switches */
#define BAUDMOD		/* allow baudrate modifier on switches */
#define GETLOGIN	/* use getlogin() routine as backup to environment */
			/* variables USER or LOGNAME */
#define ORGFILE		/* if organization begins with /, look up in file */
#define TILDENAME	/* allow ~logname expansion */
#define SETENV		/* allow command line environment variable setting */
#define GETWD		/* use our getwd() instead of piped in pwd */
#ifndef BSD42		/* 4.2 sites should just use groups for this */
#define SETUIDGID	/* substitute eaccess() for access() so that rn */
			/* can run setuid or setgid */
			/* if not setuid or setgid, you don't need it */
#endif
#define MAKEDIR		/* use our makedir() instead of shell script */
#define MEMHELP		/* keep help messages in memory */
#define VERBOSE		/* compile in more informative messages */
#define TERSE		/* compile in shorter messages */
			/* (Note: both VERBOSE and TERSE can be defined; -t
			 * sets terse mode.  One or the other MUST be defined.
			 */
#ifndef pdp11
#   define CACHESUBJ	/* cache subject lines in memory */
			/* without this ^N still works but runs really slow */
			/* but you save lots and lots of D space */
#   define CACHEFIRST	/* keep absolute first article numbers in memory */
			/* cost: about 2k */
#endif
#define ROTATION	/* enable x, X and ^X commands to work */
#define DELBOGUS	/* ask if bogus newsgroups should be deleted */
#define RELOCATE	/* allow newsgroup rearranging */
#define ESCSUBS		/* escape substitutions in multi-character commands */
#define DELAYMARK	/* allow articles to be temporarily marked as read */
			/* until exit from current newsgroup or Y command */
#define MCHASE		/* unmark xrefed articles on m or M */
#define MUNGHEADER	/* allow alternate header formatting via */
			/* environment variable ALTHEADER (not impl) */
#define ASYNC_PARSE	/* allow parsing headers asyncronously to reading */
			/* used by MCHASE and MUNGHEADER */
#define FINDNEWNG	/* check for new newsgroups on startup */
#define FASTNEW		/* do optimizations on FINDNEWNG for faster startup */
			/* (this optimization can make occasional mistakes */
			/* if a group is removed and another group of the */
			/* same length is added, and if no softpointers are */
			/* affected by said change.) */
#define INNERSEARCH	/* search command 'g' with article */
#define CATCHUP		/* catchup command at newsgroup level */
#define NGSEARCH	/* newsgroup pattern matching */
#define ONLY		/* newsgroup restrictions by pattern */
#define KILLFILES	/* automatic article killer files */
#define ARTSEARCH	/* pattern searches among articles */
			/* /, ?, ^N, ^P, k, K */

/* some dependencies among options */

#ifndef ARTSEARCH
#   undef KILLFILES
#   undef INNERSEARCH
#   undef CACHESUBJ
#endif

#ifndef DELAYMARK
#   ifndef MCHASE
#	ifndef MUNGHEADER
#	    undef ASYNC_PARSE
#	endif
#   endif
#endif

#ifndef SETUIDGID
#   define eaccess access
#endif

#ifdef ONLY				/* idiot lint doesn't grok #if */
#   define NGSORONLY
#else
#   ifdef NGSEARCH
#	define NGSORONLY
#   endif
#endif

#ifdef VERBOSE
#   ifdef TERSE
#	define IF(c) if (c)
#	define ELSE else
#   else !TERSE
#	define IF(c)
#	define ELSE
#   endif
#else !VERBOSE
#   ifndef TERSE
#	define TERSE
#   endif
#   define IF(c) "IF" outside of VERBOSE???
#   define ELSE "ELSE" outside of VERBOSE???
#endif

#ifdef DEBUGGING
#   define assert(ex) {if (!(ex)){fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);sig_catcher(0);}}
#else
#   define assert(ex) ;
#endif

#ifdef SPEEDOVERMEM
#   define OFFSET(x) (x)
#else
#   define OFFSET(x) ((x)-absfirst)
#endif

/* If you're strapped for space use the help messages in shell scripts */
/* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */
#ifdef MEMHELP  /* undef MEMHELP above to get them all as sh scripts */
#   undef NGHELP
#   undef ARTHELP
#   undef PAGERHELP
#   undef SUBSHELP
#else
#   ifndef NGHELP			/* % and ~ */
#	define NGHELP "%X/ng.help"
#   endif
#   ifndef ARTHELP			/* % and ~ */
#	define ARTHELP "%X/art.help"
#   endif
#   ifndef PAGERHELP		/* % and ~ */
#	define PAGERHELP "%X/pager.help"
#   endif
#   ifndef SUBSHELP		/* % and ~ */
#	define SUBSHELP "%X/subs.help"
#   endif
#endif

#ifdef CLEAREOL
#   define TCSIZE 512	/* capacity for termcap strings */
#else
#   ifdef pdp11
#	define TCSIZE 256	/* capacity for termcap strings */
#   else
#	define TCSIZE 512	/* capacity for termcap srings */
#   endif
#endif

/* Additional ideas:
 *	Make the do_newsgroup() routine a separate process.
 *	Keep .newsrc on disk instead of in memory.
 *	Overlays, if you have them.
 *	Get a bigger machine.
 */

/* End of Space Conservation Section */

/* More System Dependencies */

/* news library */
#ifndef LIB		/* ~ and %l only ("~%l" is permissable) */
#   define LIB "/usr/lib/news"
#endif

/* path to private executables */
#ifndef RNLIB		/* ~, %x and %l only */
#   define RNLIB "%x/rn"
#endif

/* system-wide RNINIT switches */
#ifndef GLOBINIT
#   define GLOBINIT "%X/INIT"
#endif

/* where to find news files */
#ifndef SPOOL			/* % and ~ */
#   define SPOOL "/usr/spool/news"
#endif

/* file containing list of active newsgroups and max article numbers */
#ifndef ACTIVE			/* % and ~ */
#   define ACTIVE "%x/active"
#endif

/* location of history file */
#ifndef ARTFILE			/* % and ~ */
#    define ARTFILE "%x/history"
#endif

/* command to setup a new .newsrc */
#ifndef NEWSETUP		/* % and ~ */
#   define NEWSETUP "newsetup"
#endif

/* command to display a list of un-subscribed-to newsgroups */
#ifndef NEWSGROUPS		/* % and ~ */
#   define NEWSGROUPS "newsgroups"
#endif

/* preferred shell for use in doshell routine */
/*  ksh or sh would be okay here */
#ifndef PREFSHELL
#   define PREFSHELL "/bin/csh"
#endif

/* path to fastest starting shell */
#ifndef SH
#   define SH "/bin/sh"
#endif

/* path to default editor */
#ifndef DEFEDITOR
#   define DEFEDITOR "/usr/ucb/vi"
#endif

/* location of macro file */
#ifndef RNMACRO
#   ifdef PUSHBACK
#	define RNMACRO "%./.rnmac"
#   endif
#endif

/* location of full name */
#ifndef FULLNAMEFILE
#   ifndef PASSNAMES
#	define FULLNAMEFILE "%./.fullname"
#   endif
#endif

/* virtual array file name template */
#ifndef VARYNAME		/* % and ~ */
#   define VARYNAME "/tmp/rnvary.%$"
#endif

/* file to pass header to followup article poster */
#ifndef HEADNAME		/* % and ~ */
#   define HEADNAME "%./.rnhead"
/* or alternately #define HEADNAME "/tmp/rnhead.%$" */
#endif

#ifndef MAKEDIR
/* shell script to make n-deep subdirectories */
#   ifndef DIRMAKER		/* % and ~ */
#	define DIRMAKER "%X/makedir"
#   endif
#endif

/* location of newsrc file */
#ifndef RCNAME		/* % and ~ */
#   define RCNAME "%./.newsrc"
#endif

/* temporary newsrc file in case we crash while writing out */
#ifndef RCTNAME		/* % and ~ */
#   define RCTNAME "%./.newnewsrc"
#endif

/* newsrc file at the beginning of this session */
#ifndef RCBNAME		/* % and ~ */
#   define RCBNAME "%./.oldnewsrc"
#endif

/* if existent, contains process number of current or crashed rn */
#ifndef LOCKNAME		/* % and ~ */
#   define LOCKNAME "%./.rnlock"
#endif

/* information from last invocation of rn */
#ifndef LASTNAME		/* % and ~ */
#   define LASTNAME "%./.rnlast"
#endif

/* file with soft pointers into the active file */
#ifndef SOFTNAME		/* % and ~ */
#   define SOFTNAME "%./.rnsoft"
#endif

/* list of article numbers to mark as unread later (see M and Y cmmands) */
#ifndef RNDELNAME		/* % and ~ */
#   define RNDELNAME "%./.rndelay"
#endif

/* a motd-like file for rn */
#ifndef NEWSNEWSNAME		/* % and ~ */
#   define NEWSNEWSNAME "%X/newsnews"
#endif

/* command to send a reply */
#ifndef MAILPOSTER		/* % and ~ */
#   define MAILPOSTER "Rnmail -h %h"
#endif

#ifdef INTERNET
#   ifndef MAILHEADER		/* % */
#	ifdef CONDSUB
#	    define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
#	else
#	    define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
#	endif
#   endif
#else
#   ifndef MAILHEADER		/* % */
#	ifdef CONDSUB
#	    define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
#	else
#	    define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
#	endif
#   endif
#endif

#ifndef YOUSAID			/* % */
#   define YOUSAID "In article %i you write:"
#endif

/* command to submit a followup article */
#ifndef NEWSPOSTER		/* % and ~ */
#   define NEWSPOSTER "Pnews -h %h"
#endif

#ifndef NEWSHEADER		/* % */
#   define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nReply-To: %L@%H.UUCP (%N)\nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
#endif

#ifndef ATTRIBUTION		/* % */
#   define ATTRIBUTION "In article %i %f writes:"
#endif

#ifndef PIPESAVER		/* % */
#   ifdef CONDSUB
#	define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b"
#   else
#	define PIPESAVER "tail +%Bc %A | %b"
#   endif
#endif

#ifndef NORMSAVER		/* % and ~ */
#   define NORMSAVER "%X/norm.saver %A %P %c %a %B %C \"%b\""
#endif

#ifndef MBOXSAVER		/* % and ~ */
#   ifdef MININACT		/* 2.10.2 site? */
#	define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %`date`\""
#   else
#	ifdef CONDSUB
#	    define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\""
					/* header munging with a vengeance */
#	else
#	    define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %[posted]\""
#	endif
#   endif
#endif

#ifdef MKDIRS

#   ifndef SAVEDIR			/* % and ~ */
#	define SAVEDIR "%p/%c"
#   endif
#   ifndef SAVENAME		/* % */
#	define SAVENAME "%a"
#   endif

#else

#   ifndef SAVEDIR			/* % and ~ */
#	define SAVEDIR "%p"
#   endif
#   ifndef SAVENAME		/* % */
#	define SAVENAME "%^C"
#   endif

#endif

#ifndef KILLGLOBAL		/* % and ~ */
#   define KILLGLOBAL "%p/KILL"
#endif

#ifndef KILLLOCAL		/* % and ~ */
#   define KILLLOCAL "%p/%c/KILL"
#endif

/* how to cancel an article */
#ifndef CANCEL
#   ifdef MININACT			/* 2.10.2 ? */
#	define CANCEL "%x/inews -h < %h"
#   else
#	define CANCEL "inews -h < %h"
#   endif
#endif

/* how to cancel an article, continued */
#ifndef CANCELHEADER
#   define CANCELHEADER "Newsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nReply-To: %L@%H.UUCP (%N)\nDistribution: %D\nOrganization: %o\n"
#endif

/* where to find the mail file */
#ifndef MAILFILE
#   define MAILFILE "/usr/spool/mail/%L"
#endif

/* some important types */

typedef int		NG_NUM;		/* newsgroup number */
typedef long		ART_NUM;	/* article number */
#ifdef pdp11
    typedef short	ART_UNREAD;	/* ordinarily this should be long */
					/* like ART_NUM, but assuming that */
					/* we stay less than 32767 articles */
					/* behind saves a lot of space. */
					/* NOTE: do not make unsigned. */
#else
    typedef long	ART_UNREAD;
#endif
typedef long		ART_POS;	/* char position in article file */
typedef int		ART_LINE;	/* line position in article file */
typedef short		ACT_POS;	/* char position in active file */
typedef unsigned int	MEM_SIZE;	/* for passing to malloc */

/* *** end of the machine dependent stuff *** */

/* GLOBAL THINGS */

/* file statistics area */

EXT struct stat filestat;

/* various things of type char */

char	*index();
char	*rindex();
char	*getenv();
char	*strcat();
char	*strcpy();
char	*sprintf();

EXT char buf[LBUFLEN+1];	/* general purpose line buffer */
EXT char cmd_buf[CBUFLEN];	/* buffer for formatting system commands */

EXT char *indstr INIT(">");	/* indent for old article embedded in followup */

EXT char *cwd INIT(Nullch);		/* current working directory */
EXT char *dfltcmd INIT(Nullch);	/* 1st char is default command */

/* switches */

#ifdef DEBUGGING
    EXT int debug INIT(0);				/* -D */
#   define DEB_INNERSRCH 32 
#   define DEB_FILEXP 64 
#   define DEB_HASH 128
#   define DEB_XREF_MARKER 256
#   define DEB_CTLAREA_BITMAP 512
#   define DEB_SOFT_POINTERS 1024
#   define DEB_NEWSRC_LINE 2048
#   define DEB_SEARCH_AHEAD 4096
#   define DEB_CHECKPOINTING 8192
#   define DEB_FEED_XREF 16384
#endif

#ifdef ARTSEARCH
    EXT int scanon INIT(0);				/* -S */
#endif

EXT bool mbox_always INIT(FALSE);			/* -M */
EXT bool norm_always INIT(FALSE);			/* -N */
EXT bool checkflag INIT(FALSE);			/* -c */
EXT bool suppress_cn INIT(FALSE);			/* -s */
EXT int countdown INIT(5);	/* how many lines to list before invoking -s */
EXT bool muck_up_clear INIT(FALSE);			/* -loco */
EXT bool erase_screen INIT(FALSE);			/* -e */
#ifdef CLEAREOL
EXT bool can_home_clear INIT(FALSE);		/* fancy -e -- PWP */
#endif CLEAREOL
EXT bool findlast INIT(FALSE);			/* -r */
EXT bool typeahead INIT(FALSE);			/* -T */
#ifdef VERBOSE
#   ifdef TERSE
	EXT bool verbose INIT(TRUE);			/* +t */
#   endif
#endif
#ifdef VERIFY
    EXT bool verify INIT(FALSE);			/* -v */
#endif

#define NOMARKING 0
#define STANDOUT 1
#define UNDERLINE 2
EXT int marking INIT(NOMARKING);			/* -m */

EXT ART_LINE initlines INIT(0);		/* -i */

/* miscellania */

long atol(), fseek(), ftell();
EXT bool in_ng INIT(FALSE);		/* current state of rn */
EXT char mode INIT('i');		/* current state of rn */

EXT FILE *tmpfp INIT(Nullfp);	/* scratch fp used for .rnlock, .rnlast, etc. */

EXT NG_NUM nextrcline INIT(0);	/* 1st unused slot in rcline array */
			/* startup to avoid checking twice in a row */

extern errno;

/* Factored strings */

EXT char nullstr[] INIT("");
EXT char sh[] INIT(SH);
EXT char defeditor[] INIT(DEFEDITOR);
EXT char hforhelp[] INIT("Type h for help.\n");
#ifdef STRICTCR
EXT char badcr[] INIT("\nUnnecessary CR ignored.\n");
#endif
EXT char readerr[] INIT("rn read error");
EXT char unsubto[] INIT("\n\nUnsubscribed to newsgroup %s\n");
EXT char cantopen[] INIT("Can't open %s\n");
EXT char cantcreate[] INIT("Can't create %s\n");

#ifdef VERBOSE
    EXT char nocd[] INIT("Can't chdir to directory %s\n");
#else
    EXT char nocd[] INIT("Can't find %s\n");
#endif

#ifdef NOLINEBUF
#define FLUSH ,fflush(stdout)
#else
#define FLUSH
#endif

#ifdef lint
#undef FLUSH
#define FLUSH
#undef putchar
#define putchar(c)
#endif
!STUFFY!FUNK!
echo Extracting bits.c
cat >bits.c <<'!STUFFY!FUNK!'
/* $Header: bits.c,v 4.3 85/05/01 11:36:15 lwall Exp $
 *
 * $Log:	bits.c,v $
 * Revision 4.3  85/05/01  11:36:15  lwall
 * Baseline for release with 4.3bsd.
 * 
 */

#include "EXTERN.h"
#include "common.h"
#include "rcstuff.h"
#include "head.h"
#include "util.h"
#include "final.h"
#include "rn.h"
#include "cheat.h"
#include "ng.h"
#include "artio.h"
#include "intrp.h"
#include "ngdata.h"
#include "rcln.h"
#include "kfile.h"
#include "INTERN.h"
#include "bits.h"

#ifdef DBM
#    ifdef NULL
#	undef NULL
#    endif NULL
#    include <dbm.h>
#endif DBM
MEM_SIZE ctlsize;			/* size of bitmap in bytes */

void
bits_init()
{
#ifdef DELAYMARK
    dmname = savestr(filexp(RNDELNAME));
#else
    ;
#endif
}

/* checkpoint the .newsrc */

void
checkpoint_rc()
{
#ifdef DEBUGGING
    if (debug & DEB_CHECKPOINTING) {
	fputs("(ckpt)",stdout);
	fflush(stdout);
    }
#endif
    if (doing_ng)
	restore_ng();			/* do not restore M articles */
    if (rc_changed)
	write_rc();
#ifdef DEBUGGING
    if (debug & DEB_CHECKPOINTING) {
	fputs("(done)",stdout);
	fflush(stdout);
    }
#endif
}

/* reconstruct the .newsrc line in a human readable form */

void
restore_ng()
{
    register char *s, *mybuf = buf;
    register ART_NUM i;
    ART_NUM count=0;
    int safelen = LBUFLEN - 16;

    strcpy(buf,rcline[ng]);		/* start with the newsgroup name */
    s = buf + rcnums[ng] - 1;		/* use s for buffer pointer */
    *s++ = rcchar[ng];			/* put the requisite : or !*/
    *s++ = ' ';				/* put the not-so-requisite space */
    for (i=1; i<=lastart; i++) {	/* for each article in newsgroup */
	if (s-mybuf > safelen) {	/* running out of room? */
	    safelen *= 2;
	    if (mybuf == buf) {		/* currently static? */
		*s = '\0';
		mybuf = safemalloc((MEM_SIZE)safelen + 16);
		strcpy(mybuf,buf);	/* so we must copy it */
		s = mybuf + (s-buf);
					/* fix the pointer, too */
	    }
	    else {			/* just grow in place, if possible */
		char *newbuf;

		newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16);
		s = newbuf + (s-mybuf);
		mybuf = newbuf;
	    }
	}
	if (!was_read(i))		/* still unread? */
	    count++;			/* then count it */
	else {				/* article was read */
	    ART_NUM oldi;

	    sprintf(s,"%ld",(long)i);	/* put out the min of the range */
	    s += strlen(s);		/* keeping house */
	    oldi = i;			/* remember this spot */
	    do i++; while (i <= lastart && was_read(i));
					/* find 1st unread article or end */
	    i--;			/* backup to last read article */
	    if (i > oldi) {		/* range of more than 1? */
		sprintf(s,"-%ld,",(long)i);
					/* then it out as a range */
		s += strlen(s);		/* and housekeep */
	    }
	    else
		*s++ = ',';		/* otherwise, just a comma will do */
	}
    }
    if (*(s-1) == ',')			/* is there a final ','? */
	s--;				/* take it back */
    *s++ = '\0';			/* and terminate string */
#ifdef DEBUGGING
    if (debug & DEB_NEWSRC_LINE && !panic) {
	printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) FLUSH;
	printf("%s\n",mybuf) FLUSH;
    }
#endif
    free(rcline[ng]);			/* return old rc line */
    if (mybuf == buf) {
	rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1);
					/* grab a new rc line */
	strcpy(rcline[ng], buf);	/* and load it */
    }
    else {
	mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1);
					/* be nice to the heap */
	rcline[ng] = mybuf;
    }
    *(rcline[ng] + rcnums[ng] - 1) = '\0';
    if (rcchar[ng] == NEGCHAR) {	/* did they unsubscribe? */
	printf(unsubto,ngname) FLUSH;
	toread[ng] = TR_UNSUB;		/* make line invisible */
    }
    else
	/*NOSTRICT*/
	toread[ng] = (ART_UNREAD)count;		/* remember how many unread there are */
}

/* mark an article unread, keeping track of toread[] */

void
onemore(artnum)
ART_NUM artnum;
{
#ifdef DEBUGGING
    if (debug && artnum < firstart) {
	printf("onemore: %d < %d\n",artnum,firstart) FLUSH;
	return;
    }
#endif
    if (ctl_read(artnum)) {
	ctl_clear(artnum);
	++toread[ng];
    }
}

/* mark an article read, keeping track of toread[] */

void
oneless(artnum)
ART_NUM artnum;
{
#ifdef DEBUGGING
    if (debug && artnum < firstart) {
	printf("oneless: %d < %d\n",artnum,firstart) FLUSH;
	return;
    }
#endif
    if (!ctl_read(artnum)) {
	ctl_set(artnum);
	if (toread[ng] > TR_NONE)
	    --toread[ng];
    }
}

/* mark an article as unread, making sure that firstart is properly handled */
/* cross-references are left as read in the other newsgroups */

void
unmark_as_read(artnum)
ART_NUM artnum;
{
    check_first(artnum);
    onemore(artnum);
#ifdef MCHASE
    if (!parse_maybe(artnum))
	chase_xrefs(artnum,FALSE);
#endif
}

#ifdef DELAYMARK
/* temporarily mark article as read.  When newsgroup is exited, articles */
/* will be marked as unread.  Called via M command */

void
delay_unmark(artnum)
ART_NUM artnum;
{
    if (dmfp == Nullfp) {
	dmfp = fopen(dmname,"w");
	if (dmfp == Nullfp) {
	    printf(cantcreate,dmname) FLUSH;
	    sig_catcher(0);
	}
    }
    oneless(artnum);			/* set the correct bit */
    dmcount++;
    fprintf(dmfp,"%ld\n",(long)artnum);
}
#endif

/* mark article as read.  If article is cross referenced to other */
/* newsgroups, mark them read there also. */

void
mark_as_read(artnum)
ART_NUM artnum;
{
    oneless(artnum);			/* set the correct bit */
    checkcount++;			/* get more worried about crashes */
    chase_xrefs(artnum,TRUE);
}

/* make sure we have bits set correctly down to firstart */

void
check_first(min)
ART_NUM min;
{
    register ART_NUM i = firstart;

    if (min < absfirst)
	min = absfirst;
    if (min < i) {
	for (i--; i>=min; i--)
	    ctl_set(i);		/* mark as read */
	firstart = min;
    }
}

/* bring back articles marked with M */

#ifdef DELAYMARK
void
yankback()
{
    register ART_NUM anum;

    if (dmfp) {			/* delayed unmarks pending? */
#ifdef VERBOSE
	printf("\nReturning %ld Marked article%s...\n",(long)dmcount,
	    dmcount == 1 ? nullstr : "s") FLUSH;
#endif
	fclose(dmfp);
	if (dmfp = fopen(dmname,"r")) {
	    while (fgets(buf,sizeof buf,dmfp) != Nullch) {
		anum = (ART_NUM)atol(buf);
		/*NOSTRICT*/
		onemore(anum);             /* then unmark them */
#ifdef MCHASE
		chase_xrefs(anum,FALSE);
#endif
	    }
	    fclose(dmfp);
	    dmfp = Nullfp;
	    UNLINK(dmname);		/* and be tidy */
	}
	else {
	    printf(cantopen,dmname) FLUSH;
	    sig_catcher(0);
	}
    }
    dmcount = 0;
}
#endif
    
/* run down xref list and mark as read or unread */

int
chase_xrefs(artnum,markread)
ART_NUM artnum;
int markread;
{
#ifdef ASYNC_PARSE
    if (parse_maybe(artnum))		/* make sure we have right header */
	return -1;
#endif
#ifdef DBM
    {
	datum lhs, rhs;
	datum fetch();
	register char *idp;
	char *ident_buf;
	static FILE * hist_file = Nullfp;
#else
    if (
#ifdef DEBUGGING
	debug & DEB_FEED_XREF ||
#endif
	htype[XREF_LINE].ht_minpos >= 0) {
					/* are there article# xrefs? */
#endif DBM
	char *xref_buf, *curxref;
	register char *xartnum;
	char *rver_buf = Nullch;
	static char *inews_site = Nullch;
	register ART_NUM x;
	char tmpbuf[128];

#ifdef DBM
	rver_buf = fetchlines(artnum,NGS_LINE);
					/* get Newsgroups */
	if (!index(rver_buf,','))	/* if no comma, no Xref! */
	    return 0;
	if (hist_file == Nullfp) {	/* Init. file accesses */
#ifdef DEBUGGING
	    if (debug)
		printf ("chase_xref: opening files\n");
#endif
	    dbminit(filexp(ARTFILE));
	    if ((hist_file = fopen (filexp(ARTFILE), "r")) == Nullfp)
		return 0;
	}
	xref_buf = safemalloc((MEM_SIZE)BUFSIZ);
	ident_buf = fetchlines(artnum,MESSID_LINE);
					/* get Message-ID */
#ifdef DEBUGGING
	if (debug)
	    printf ("chase_xref: Message-ID: %s\n", ident_buf);
#endif
	idp = ident_buf;
	while (*++idp)			/* make message-id case insensitive */
	    if (isupper(*idp))
	        *idp = tolower (*idp);
	lhs.dptr = ident_buf;		/* look up article by id */
	lhs.dsize = strlen(lhs.dptr) + 1;
	rhs = fetch(lhs);		/* fetch the record */
	if (rhs.dptr == NULL)		/* if null, nothing there */
	    goto wild_goose;
	fseek (hist_file, *((long *)rhs.dptr), 0);
					/* datum returned is position in hist file */
	fgets (xref_buf, BUFSIZ, hist_file);
#ifdef DEBUGGING
	if (debug)
	    printf ("Xref from history: %s\n", xref_buf);
#endif
	curxref = cpytill(tmpbuf, xref_buf, '\t') + 1;
	curxref = cpytill(tmpbuf, curxref, '\t') + 1;
#ifdef DEBUGGING
	if (debug)
	    printf ("chase_xref: curxref: %s\n", curxref);
#endif
#else !DBM
#ifdef DEBUGGING
	if (htype[XREF_LINE].ht_minpos >= 0)
#endif
	    xref_buf = fetchlines(artnum,XREF_LINE);
					/* get xrefs list */
#ifdef DEBUGGING
	else {
	    xref_buf = safemalloc((MEM_SIZE)100);
	    printf("Give Xref: ") FLUSH;
	    gets(xref_buf);
	}
#endif
#ifdef DEBUGGING
	if (debug & DEB_XREF_MARKER)
	    printf("Xref: %s\n",xref_buf) FLUSH;
#endif
	curxref = cpytill(tmpbuf,xref_buf,' ') + 1;

	/* Make sure site name on Xref matches what inews thinks site is.
	 * Check first against last inews_site.  If it matches, fine.
	 * If not, fetch inews_site from current Relay-Version line and
	 * check again.  This is so that if the new administrator decides
	 * to change the system name as known to inews, rn will still do
	 * Xrefs correctly--each article need only match itself to be valid.
	 */ 
	if (inews_site == Nullch || strNE(tmpbuf,inews_site)) {
	    char *t;

	    if (inews_site != Nullch)
		free(inews_site);
	    rver_buf = fetchlines(artnum,RVER_LINE);
	    if ((t = instr(rver_buf,"; site ")) == Nullch)
		inews_site = savestr(nullstr);
	    else {
		char new_site[128];

		cpytill(new_site,t + 7,'.');
		inews_site = savestr(new_site);
	    }
	    if (strNE(tmpbuf,inews_site)) {
#ifdef DEBUGGING
		if (debug)
		    printf("Xref not from %s--ignoring\n",inews_site) FLUSH;
#endif
		goto wild_goose;
	    }
	}
#endif DBM
	while (*curxref) {
					/* for each newsgroup */
	    curxref = cpytill(tmpbuf,curxref,' ');
#ifdef DBM
	    xartnum = index(tmpbuf,'/');
#else
	    xartnum = index(tmpbuf,':');
#endif DBM
	    if (!xartnum)		/* probably an old-style Xref */
		break;
	    *xartnum++ = '\0';
	    if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */
		x = atol(xartnum);
		if (x)
		    if (markread) {
			if (addartnum(x,tmpbuf))
			    goto wild_goose;
		    }
#ifdef MCHASE
		    else
			subartnum(x,tmpbuf);
#endif
	    }
	    while (*curxref && isspace(*curxref))
		curxref++;
	}
      wild_goose:
	free(xref_buf);
#ifdef DBM
	free(ident_buf);
#endif DBM
	if (rver_buf != Nullch)
	    free(rver_buf);
    }
    return 0;
}

int
initctl()
{
    char *mybuf = buf;			/* place to decode rc line */
    register char *s, *c, *h;
    register long i;
    register ART_NUM unread;
    
#ifdef DELAYMARK
    dmcount = 0;
#endif
    if ((lastart = getngsize(ng)) < 0)	/* this cannot happen (laugh here) */
	return -1;

    absfirst = getabsfirst(ng,lastart);	/* remember first existing article */
    if (!absfirst)			/* no articles at all? */
	absfirst = 1;			/* pretend there is one */
#ifndef lint
    ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20);
#endif lint
    ctlarea = safemalloc(ctlsize);	/* allocate control area */

    /* now modify ctlarea to reflect what has already been read */

    for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ;
					/* find numbers in rc line */
    i = strlen(s);
#ifndef lint
    if (i >= LBUFLEN-2)			/* bigger than buf? */
	mybuf = safemalloc((MEM_SIZE)(i+2));
#endif lint
    strcpy(mybuf,s);			/* make scratch copy of line */
    mybuf[i++] = ',';			/* put extra comma on the end */
    mybuf[i] = '\0';
    s = mybuf;				/* initialize the for loop below */
    if (strnEQ(s,"1-",2)) {		/* can we save some time here? */
	firstart = atol(s+2)+1;		/* ignore first range thusly */
	s=index(s,',') + 1;
    }
    else
	firstart = 1;			/* all the bits are valid for now */
    if (absfirst > firstart) {		/* do we know already? */
	firstart = absfirst;		/* no point calling getngmin again */
    }
    else if (artopen(firstart) == Nullfp) {
					/* first unread article missing? */
	i = getngmin(".",firstart);	/* see if expire has been busy */
	if (i) {			/* avoid a bunch of extra opens */
	    firstart = i;
	}
    }
#ifdef PENDING
#   ifdef CACHESUBJ
	subj_to_get = firstart;
#   endif
#endif
    unread = lastart - firstart + 1;	/* assume this range unread */
    for (i=OFFSET(firstart)/BITSPERBYTE; i<ctlsize; i++)
	ctlarea[i] = 0;			/* assume unread */
#ifdef DEBUGGING
    if (debug & DEB_CTLAREA_BITMAP) {
	printf("\n%s\n",mybuf) FLUSH;
	for (i=1; i <= lastart; i++)
	    if (! was_read(i))
		printf("%ld ",(long)i) FLUSH;
    }
#endif
    for ( ; (c = index(s,',')) != Nullch; s = ++c) {
					/* for each range */
	ART_NUM min, max;

	*c = '\0';			/* do not let index see past comma */
	if ((h = index(s,'-')) != Nullch) {	/* is there a -? */
	    min = atol(s);
	    max = atol(h+1);
	    if (min < firstart)		/* make sure range is in range */
		min = firstart;
	    if (max > lastart)
		max = lastart;
	    if (min <= max)		/* non-null range? */
		unread -= max - min + 1;/* adjust unread count */
	    for (i=min; i<=max; i++)	/* for all articles in range */
		ctl_set(i);		/* mark them read */
	}
	else if ((i = atol(s)) >= firstart && i <= lastart) {
					/* is single number reasonable? */
	    ctl_set(i);			/* mark it read */
	    unread--;			/* decrement articles to read */
	}
#ifdef DEBUGGING
	if (debug & DEB_CTLAREA_BITMAP) {
	    printf("\n%s\n",s) FLUSH;
	    for (i=1; i <= lastart; i++)
		if (! was_read(i))
		    printf("%ld ",(long)i) FLUSH;
	}
#endif
    }
#ifdef DEBUGGING
    if (debug & DEB_CTLAREA_BITMAP) {
	fputs("\n(hit CR)",stdout) FLUSH;
	gets(cmd_buf);
    }
#endif
    if (mybuf != buf)
	free(mybuf);
    toread[ng] = unread;
    return 0;
}

void
grow_ctl()
{
    ART_NUM newlast;
    ART_NUM tmpfirst;
    MEM_SIZE newsize;
    register ART_NUM i;

    forcegrow = FALSE;
    newlast = getngsize(ng);
    if (newlast > lastart) {
	ART_NUM tmpart = art;
#ifndef lint
	newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2);
#else
	newsize = Null(MEM_SIZE);
#endif lint
	if (newsize > ctlsize) {
	    newsize += 20;
	    ctlarea = saferealloc(ctlarea,newsize);
	    ctlsize = newsize;
	}
	toread[ng] += (ART_UNREAD)(newlast-lastart);
	for (i=lastart+1; i<=newlast; i++)
	    ctl_clear(i);	/* these articles are unread */
#ifdef CACHESUBJ
	if (subj_list != Null(char**)) {
#ifndef lint
	    subj_list = (char**)saferealloc((char*)subj_list,
		  (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) );
#endif lint
	    for (i=lastart+1; i<=newlast; i++)
		subj_list[OFFSET(i)] = Nullch;
	}
#endif
	tmpfirst = lastart+1;
	lastart = newlast;
#ifdef KILLFILES
#ifdef VERBOSE
	IF(verbose)
	    sprintf(buf,
		"%ld more article%s arrived--looking for more to kill...\n\n",
		(long)(lastart - firstart + 1),
		(lastart > firstart ? "s have" : " has" ) );
	ELSE			/* my, my, how clever we are */
#endif
#ifdef TERSE
	    strcpy(buf, "More news--killing...\n\n");
#endif
	kill_unwanted(tmpfirst,buf,TRUE);
#endif
	art = tmpart;
    }
}

!STUFFY!FUNK!
echo Extracting NEW
cat >NEW <<'!STUFFY!FUNK!'
		    NEW FEATURES WITH RN 4.3

New commands
	TAB (pager)	scan for end of quoted text.
	&& (anywhere)	set or display macros.
	Q (art level)	exit this newsgroup but stay on it.
	x (ng level)	exit rn without changing .newsrc.

New switch
	-=TERM-switch	apply switch if terminal is TERM.		

New environment variables
	RNMACRO		name of your macro and keymap file, if any.
	SUBJLINE	controls format of = article listing.

New % interpolations.
	%I		inclusion indicator (-F argument).
	%m		current mode of rn (newsgroup, pager, etc.)
	%z		length of current article.
	%"prompt"	prompt for input from keyboard.
	%`command`	same as shell backquotes.
	^char		now produces control-char.

Macros and Keymaps
	You may now define macros of any reasonable length and map those
	macros onto your keyboard in any way.  You can completely remap the
	keyboard if you wish.  Macros may contain % interpolations.

Global RNINIT file
	You can now set pseudo-environment variables on a system-wide basis
	in the file %X/INIT.  You don't have to recompile rn when you want 
	to try something different.

Pnews
	Can append your .signature if you like.
	Now knows how to mail to moderators.
	Runs somewhat faster now when invoked with -h.
	Will use %x/distributions if it exists.
	Doesn't ask for Distribution on local newsgroups.
	Doesn't ask for editor if EDITOR or VISUAL is set.

Terminal handling
	-L switch uses erase-to-end-of-line to leave info on screen as long
		as possible.
	rn -c will not flush typeahead in your login script now.
	In multi-character commands, \ now quotes the next character.
	Support for non-line-buffered machines.  Certain V7 machines will
		appreciate the speedup.

Configure
	Lets the poor people without job control do shell escapes.
	Now remembers your old answers and uses them for defaults.
	Searches much more widely for libraries.
	Looks for Mcc or cc -M if it needs to.
	Finds pg if it needs to.  (Note, pg users: you can use macros to
		reverse the sense of CR and SP in rn now.)
	Figures out where manual pages go.
	Figures out where mail is spooled.
	Looks for ioctl.h, if any.
	Determines if you have a builtin echo that works differently than
		/bin/echo.
	Asks if your mail takes Internet addresses.
	Works reasonably on more systems, such as "Pyramids".

Miscellaneous
	Commands may be typed directly to a help menu or subject list without
		having to type 'q' first.
	- command on first displayed article of a newsgroup takes you out to
		the previous newsgroup.  (Someday it will take you to the
		previous article in the previous newsgroup.)
	You can now easily get into a newsgroup with a KILL file and no
		unread articles.
	The catchup command at the top level now asks for confirmation.
	Interpretation routines now check for output buffer overflow.
	The pager no longer get hung up on non-initial ^L.
	The negative unread articles bug was fixed.
	Numerous small bug fixes.
!STUFFY!FUNK!
echo Extracting INIT
cat >INIT <<'!STUFFY!FUNK!'
-ESAMPLE="sample"
!STUFFY!FUNK!
echo ""
echo "End of kit 3 (of 9)"
cat /dev/null >kit3isdone
config=true
for iskit in 1 2 3 4 5 6 7 8 9; 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



More information about the Mod.sources mailing list