rn version 4.3 (kit 5 of 9)

sources-request at genrad.UUCP sources-request at genrad.UUCP
Sat May 11 21:56:03 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 5 (of 9).  If kit 5 is complete, the line"
echo '"'"End of kit 5 (of 9)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting term.c
cat >term.c <<'!STUFFY!FUNK!'
/* $Header: term.c,v 4.3 85/05/01 11:51:10 lwall Exp $
 *
 * $Log:	term.c,v $
 * Revision 4.3  85/05/01  11:51:10  lwall
 * Baseline for release with 4.3bsd.
 * 
 */

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

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

/* guarantee capability pointer != Nullch */
/* (I believe terminfo will ignore the &tmpaddr argument.) */

#define Tgetstr(key) ((tmpstr = tgetstr(key,&tmpaddr)) ? tmpstr : nullstr)

#ifdef PUSHBACK
struct keymap {
    char km_type[128];
    union km_union {
	struct keymap *km_km;
	char *km_str;
    } km_ptr[128];
};

#define KM_NOTHIN 0
#define KM_STRING 1
#define KM_KEYMAP 2
#define KM_BOGUS 3

#define KM_TMASK 3
#define KM_GSHIFT 4
#define KM_GMASK 7

typedef struct keymap KEYMAP;

KEYMAP *topmap INIT(Null(KEYMAP*));

void mac_init();
KEYMAP *newkeymap();
void show_keymap();
void pushstring();
#endif

/* terminal initialization */

void
term_init()
{
    savetty();				/* remember current tty state */

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

    /* The following could be a table but I can't be sure that there isn't */
    /* some degree of sparsity out there in the world. */

    switch (ospeed) {			/* 1 second of padding */
#ifdef BEXTA
        case BEXTA:  just_a_sec = 1920; break;
#else
#ifdef B19200
        case B19200: just_a_sec = 1920; break;
#endif
#endif
        case B9600:  just_a_sec =  960; break;
        case B4800:  just_a_sec =  480; break;
        case B2400:  just_a_sec =  240; break;
        case B1800:  just_a_sec =  180; break;
        case B1200:  just_a_sec =  120; break;
        case B600:   just_a_sec =   60; break;
	case B300:   just_a_sec =   30; break;
	/* do I really have to type the rest of this??? */
        case B200:   just_a_sec =   20; break;
        case B150:   just_a_sec =   15; break;
        case B134:   just_a_sec =   13; break;
        case B110:   just_a_sec =   11; break;
        case B75:    just_a_sec =    8; break;
        case B50:    just_a_sec =    5; break;
        default:     just_a_sec =  960; break;
					/* if we are running detached I */
    }					/*  don't want to know about it! */
}

/* set terminal characteristics */

void
term_set(tcbuf)
char *tcbuf;		/* temp area for "uncompiled" termcap entry */
{
    char *tmpaddr;			/* must not be register */
    register char *tmpstr;
    char *tgetstr();
    char *s;
    int status;

#ifdef PENDING
#ifndef FIONREAD
    /* do no delay reads on something that always gets closed on exit */

    devtty = open("/dev/tty",0);
    if (devtty < 0) {
	printf(cantopen,"/dev/tty") FLUSH;
	finalize(1);
    }
    fcntl(devtty,F_SETFL,O_NDELAY);
#endif
#endif
    
    /* get all that good termcap stuff */

#ifdef HAVETERMLIB
    status = tgetent(tcbuf,getenv("TERM"));	/* get termcap entry */
    if (status < 1) {
#ifdef VERBOSE
	printf("No termcap %s found.\n", status ? "file" : "entry") FLUSH;
#else
	fputs("Termcap botch\n",stdout) FLUSH
#endif
	finalize(1);
    }
    tmpaddr = tcarea;			/* set up strange tgetstr pointer */
    s = Tgetstr("pc");			/* get pad character */
    PC = *s;				/* get it where tputs wants it */
    if (!tgetflag("bs"))		/* is backspace not used? */
	BC = Tgetstr("bc");		/* find out what is */
    else
	BC = "\b";			/* make a backspace handy */
    UP = Tgetstr("up");			/* move up a line */
    if (!*UP)				/* no UP string? */
	marking = 0;			/* disable any marking */
    if (muck_up_clear)			/* this is for weird HPs */
	CL = "\n\n\n\n";
    else
	CL = Tgetstr("cl");		/* get clear string */
    CE = Tgetstr("ce");			/* clear to end of line string */
#ifdef CLEAREOL
    CM = Tgetstr("cm");			/* cursor motion - PWP */
    HO = Tgetstr("ho");			/* home cursor if no CM - PWP */
    CD = Tgetstr("cd");			/* clear to end of display - PWP */
    if (!*CE || !*CD || (!*CM && !*HO))	/* can we CE, CD, and home? */
	can_home_clear = FALSE;		/*  no, so disable use of clear eol */
#endif CLEAREOL
    SO = Tgetstr("so");			/* begin standout */
    SE = Tgetstr("se");			/* end standout */
    if ((SG = tgetnum("sg"))<0)
	SG = 0;				/* blanks left by SG, SE */
    US = Tgetstr("us");			/* start underline */
    UE = Tgetstr("ue");			/* end underline */
    if ((UG = tgetnum("ug"))<0)
	UG = 0;				/* blanks left by US, UE */
    if (*US)
	UC = nullstr;			/* UC must not be NULL */
    else
	UC = Tgetstr("uc");		/* underline a character */
    if (!*US && !*UC) {			/* no underline mode? */
	US = SO;			/* substitute standout mode */
	UE = SE;
	UG = SG;
    }
    LINES = tgetnum("li");		/* lines per page */
    COLS = tgetnum("co");		/* columns on page */
    AM = tgetflag("am");		/* terminal wraps automatically? */
    XN = tgetflag("xn");		/* then eats next newline? */
    VB = Tgetstr("vb");
    if (!*VB)
	VB = "\007";
    CR = Tgetstr("cr");
    if (!*CR) {
	if (tgetflag("nc") && *UP) {
	    CR = safemalloc((MEM_SIZE)strlen(UP)+2);
	    sprintf(CR,"%s\r",UP);
	}
	else
	    CR = "\r";
    }
#else
    ??????				/* Roll your own... */
#endif
    if (LINES > 0) {			/* is this a crt? */
	if (!initlines)			/* no -i? */
	    if (ospeed >= B9600)	/* whole page at >= 9600 baud */
		initlines = LINES;
	    else if (ospeed >= B4800)	/* 16 lines at 4800 */
		initlines = 16;
	    else			/* otherwise just header */
		initlines = 8;
    }
    else {				/* not a crt */
	LINES = 30000;			/* so don't page */
	CL = "\n\n";			/* put a couple of lines between */
	if (!initlines)			/* make initlines reasonable */
	    initlines = 8;
    }
    if (COLS <= 0)
	COLS = 80;
    noecho();				/* turn off echo */
    crmode();				/* enter cbreak mode */

#ifdef PUSHBACK
    mac_init(tcbuf);
#endif
}

#ifdef PUSHBACK
void
mac_init(tcbuf)
char *tcbuf;
{
    char tmpbuf[1024];

    tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r");
    if (tmpfp != Nullfp) {
	while (fgets(tcbuf,1024,tmpfp) != Nullch) {
	    mac_line(tcbuf,tmpbuf,(sizeof tmpbuf));
	}
	fclose(tmpfp);
    }
}

void
mac_line(line,tmpbuf,tbsize)
char *line;
char *tmpbuf;
int tbsize;
{
    register char *s, *m;
    register KEYMAP *curmap;
    register int ch;
    register int garbage = 0;
    static char override[] = "\nkeymap overrides string\n";

    if (topmap == Null(KEYMAP*))
	topmap = newkeymap();
    if (*line == '#' || *line == '\n')
	return;
    if (line[ch = strlen(line)-1] == '\n')
	line[ch] = '\0';
    m = dointerp(tmpbuf,tbsize,line," \t");
    if (!*m)
	return;
    while (*m == ' ' || *m == '\t') m++;
    for (s=tmpbuf,curmap=topmap; *s; s++) {
	ch = *s & 0177;
	if (s[1] == '+' && isdigit(s[2])) {
	    s += 2;
	    garbage = (*s & KM_GMASK) << KM_GSHIFT;
	}
	else
	    garbage = 0;
	if (s[1]) {
	    if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) {
		puts(override,stdout) FLUSH;
		free(curmap->km_ptr[ch].km_str);
		curmap->km_ptr[ch].km_str = Nullch;
	    }
	    curmap->km_type[ch] = KM_KEYMAP + garbage;
	    if (curmap->km_ptr[ch].km_km == Null(KEYMAP*))
		curmap->km_ptr[ch].km_km = newkeymap();
	    curmap = curmap->km_ptr[ch].km_km;
	}
	else {
	    if ((curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP)
		puts(override,stdout) FLUSH;
	    else {
		curmap->km_type[ch] = KM_STRING + garbage;
		curmap->km_ptr[ch].km_str = savestr(m);
	    }
	}
    }
}

KEYMAP*
newkeymap()
{
    register int i;
    register KEYMAP *map;

#ifndef lint
    map = (KEYMAP*)safemalloc(sizeof(KEYMAP));
#else
    map = Null(KEYMAP*);
#endif lint
    for (i=127; i>=0; --i) {
	map->km_ptr[i].km_km = Null(KEYMAP*);
	map->km_type[i] = KM_NOTHIN;
    }
    return map;
}

void
show_macros()
{
    char prebuf[64];

    if (topmap != Null(KEYMAP*)) {
	print_lines("Macros:\n",STANDOUT);
	*prebuf = '\0';
	show_keymap(topmap,prebuf);
    }
}

void
show_keymap(curmap,prefix)
register KEYMAP *curmap;
char *prefix;
{
    register int i;
    register char *next = prefix + strlen(prefix);
    register int kt;

    for (i=0; i<128; i++) {
	if (kt = curmap->km_type[i]) {
	    if (i < ' ')
		sprintf(next,"^%c",i+64);
	    else if (i == ' ')
		strcpy(next,"\\040");
	    else if (i == 127)
		strcpy(next,"^?");
	    else
		sprintf(next,"%c",i);
	    if ((kt >> KM_GSHIFT) & KM_GMASK) {
		sprintf(cmd_buf,"+%d", (kt >> KM_GSHIFT) & KM_GMASK);
		strcat(next,cmd_buf);
	    }
	    switch (kt & KM_TMASK) {
	    case KM_NOTHIN:
		sprintf(cmd_buf,"%s	%c\n",prefix,i);
		print_lines(cmd_buf,NOMARKING);
		break;
	    case KM_KEYMAP:
		show_keymap(curmap->km_ptr[(char)i].km_km, prefix);
		break;
	    case KM_STRING:
		sprintf(cmd_buf,"%s	%s\n",prefix,curmap->km_ptr[i].km_str);
		print_lines(cmd_buf,NOMARKING);
		break;
	    case KM_BOGUS:
		sprintf(cmd_buf,"%s	BOGUS\n",prefix);
		print_lines(cmd_buf,STANDOUT);
		break;
	    }
	}
    }
}

#endif

/* routine to pass to tputs */

char
putchr(ch)
register char ch;
{
    putchar(ch);
#ifdef lint
    ch = Null(char);
    ch = ch;
#endif
}

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

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

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

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

/* discard any characters typed ahead */

void
eat_typeahead()
{
#ifdef PUSHBACK
    if (!typeahead && nextin==nextout)	/* cancel only keyboard stuff */
#else
    if (!typeahead)
#endif
    {
#ifdef PENDING
	while (input_pending())
	    read_tty(buf,sizeof(buf));
#else /* this is probably v7 */
	ioctl(_tty_ch,TIOCSETP,&_tty);
#endif
    }
}

void
settle_down()
{
    dingaling();
    fflush(stdout);
    sleep(1);
#ifdef PUSHBACK
    nextout = nextin;			/* empty circlebuf */
#endif
    eat_typeahead();
}

#ifdef PUSHBACK
/* read a character from the terminal, with multi-character pushback */

int
read_tty(addr,size)
char *addr;
int size;
{
    if (nextout != nextin) {
	*addr = circlebuf[nextout++];
	nextout %= PUSHSIZE;
	return 1;
    }
    else {
	size = read(0,addr,size);
#ifdef RAWONLY
	*addr &= 0177;
#endif
	return size;
    }
}

#ifdef PENDING
#ifndef FIONREAD
int
circfill()
{
    register int howmany = read(devtty,circlebuf+nextin,1);

    if (howmany) {
	nextin += howmany;
	nextin %= PUSHSIZE;
    }
    return howmany;
}
#endif PENDING
#endif FIONREAD

void
pushchar(c)
char c;
{
    nextout--;
    if (nextout < 0)
	nextout = PUSHSIZE - 1;
    if (nextout == nextin) {
	fputs("\npushback buffer overflow\n",stdout) FLUSH;
	sig_catcher(0);
    }
    circlebuf[nextout] = c;
}

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

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

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

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

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

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

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

/* get a character into a buffer */

void
getcmd(whatbuf)
register char *whatbuf;
{
#ifdef PUSHBACK
    register KEYMAP *curmap;
    register int i;
    bool no_macros; 
    int times = 0;			/* loop detector */
    char scrchar;

tryagain:
    curmap = topmap;
    no_macros = (whatbuf != buf && nextin == nextout); 
#endif
    for (;;) {
	int_count = 0;
	errno = 0;
	if (read_tty(whatbuf,1) < 0 && !errno)
	    errno = EINTR;
	if (errno && errno != EINTR) {
	    perror(readerr);
	    sig_catcher(0);
	}
#ifdef PUSHBACK
	if (*whatbuf & 0200 || no_macros) {
	    *whatbuf &= 0177;
	    goto got_canonical;
	}
	if (curmap == Null(KEYMAP*))
	    goto got_canonical;
	for (i = (curmap->km_type[*whatbuf] >> KM_GSHIFT) & KM_GMASK; i; --i){
	    read_tty(&scrchar,1);
	}
	switch (curmap->km_type[*whatbuf] & KM_TMASK) {
	case KM_NOTHIN:			/* no entry? */
	    if (curmap == topmap)	/* unmapped canonical */
		goto got_canonical;
	    settle_down();
	    goto tryagain;
	case KM_KEYMAP:			/* another keymap? */
	    curmap = curmap->km_ptr[*whatbuf].km_km;
	    assert(curmap != Null(KEYMAP*));
	    break;
	case KM_STRING:			/* a string? */
	    pushstring(curmap->km_ptr[*whatbuf].km_str);
	    if (++times > 20) {		/* loop? */
		fputs("\nmacro loop?\n",stdout);
		settle_down();
	    }
	    no_macros = FALSE;
	    goto tryagain;
	}
#else
#ifdef RAWONLY
	*whatbuf &= 0177;
#endif
	break;
#endif
    }

got_canonical:
    if (whatbuf == buf)
	whatbuf[1] = FINISHCMD;		/* tell finish_command to work */
}

#ifdef PUSHBACK
void
pushstring(str)
char *str;
{
    register int i;
    char tmpbuf[PUSHSIZE];
    register char *s = tmpbuf;

    assert(str != Nullch);
    interp(s,PUSHSIZE,str);
    for (i = strlen(s)-1; i >= 0; --i) {
	s[i] ^= 0200; 
	pushchar(s[i]);
    }
}
#endif

int
get_anything()
{
    char tmpbuf[2];

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

void
in_char(prompt)
char *prompt;
{
    char oldmode = mode;

reask_in_char:
    unflush_output();			/* disable any ^O in effect */
    fputs(prompt,stdout);
    fflush(stdout);
    eat_typeahead();
    mode = 'm';
    getcmd(buf);
    if (errno || *buf == '\f') {
	putchar('\n') FLUSH;		/* if return from stop signal */
	goto reask_in_char;		/* give them a prompt again */
    }
    mode = oldmode;
}

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

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

void
page_init()
{
    page_line = 1;
    if (erase_screen)
	clear();
    else
	putchar('\n') FLUSH;
}

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

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

/* echo the command just typed */

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

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

void
reprint()
{
    register char *s;

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

#ifdef CLEAREOL
/* start of additions by Paul Placeway (PWP) */

void
home_cursor()
{
    char *tgoto();

    if (!*HO) {			/* no home sequence? */
	if (!*CM) {		/* no cursor motion either? */
	    fputs ("\n\n\n", stdout);
	    return;		/* forget it. */
	}
	tputs (tgoto (CM, 0, 0), 1, putchr);	/* go to home via CM */
	return;
    }
    else {			/* we have home sequence */
	tputs (HO, 1, putchr);	/* home via HO */
    }
}
#endif CLEAREOL
!STUFFY!FUNK!
echo Extracting Pnews.SH
cat >Pnews.SH <<'!STUFFY!FUNK!'
case $CONFIG in
    '') . config.sh ;;
esac
echo "Extracting Pnews (with variable substitutions)"
$spitshell >Pnews <<!GROK!THIS!
$startsh
# $Header: Pnews.SH,v 4.3 85/05/01 12:20:33 lwall Exp $
#
# $Log:	Pnews.SH,v $
# Revision 4.3  85/05/01  12:20:33  lwall
# Baseline for release with 4.3bsd.
# 
#
# syntax: Pnews -h headerfile			or
#	  Pnews -h headerfile oldarticle	or
#         Pnews newsgroup title			or just
#         Pnews

export PATH || (echo "OOPS, this isn't sh.  Desperation time.  I will feed myself to sh."; sh \$0; kill \$\$)

# System dependencies

mailer="${mailer-/bin/mail}"
# if you change this to something that does signatures, take out signature code

case $portable in
define)
# your site name
sitename=\`$hostcmd\`
# where recordings, distributions and moderators are kept
lib=\`$filexp $lib\`
# where important rn things are kept
rnlib=\`$filexp $rnlib\`
;;
undef)
# your site name
sitename="$sitename"
# where recordings, distributions and moderators are kept
lib="$lib"
# where important rn things are kept
rnlib="$rnlib"
;;
esac

# your organization name
orgname="$orgname"
# what pager you use--if you have kernal paging use cat
pager="\${PAGER-$pager}"
# how you derive full names, bsd, usg, or other
nametype="$nametype"
# default editor
defeditor="$defeditor"
# how not to echo with newline
n="$n"
c="$c"

# You should also look at the distribution warnings below marked !DIST!
# to make sure any distribution regions you are a member of are included.
# The following are some prototypical distribution groups.  If you do not
# use them all set the unused ones to a non-null string such as 'none'.
loc="$locpref"
org="$orgpref"
city="$citypref"
state="$statepref"
cntry="$cntrypref"
cont="$contpref"

test=${test-test}
sed=${sed-sed}
echo=${echo-echo}
cat=${cat-cat}
egrep=${egrep-egrep}
grep=${grep-grep}
rm=${rm-rm}
tr=${tr-tr}
inews=${inews-inews}

!GROK!THIS!
$spitshell >>Pnews <<'!NO!SUBS!'
if $test -f ${DOTDIR-${HOME-$LOGDIR}}/.pnewsexpert; then
    expertise=expert
else
    $cat <<'EOM'
I see you've never used this version of Pnews before.  I will give you extra
help this first time through, but then you must remember what you learned.
If you don't understand any question, type h and a CR (carriage return) for
help.

If you've never posted an article to the net before, it is HIGHLY recommended
that you read the netiquette document found in net.announce.newusers so
that you'll know to avoid the commonest blunders.  To do that, interrupt
Pnews, and get to the top-level prompt of rn.  Say "g net.announce.newusers"
and you are on your way.

EOM
    expertise=beginner
fi

case $cntry in
  can) stpr=Province ;;
  *)   stpr=State ;;
esac

tmpart=/tmp/article$$

headerfile=""
case $# in
0) ;;
*)  case $1 in
    -h)
	headerfile="$2"
	shift
	shift
	case $# in
	0)
	    oldart=""
	    ;;
	*)
	    oldart="$1"
	    shift
	    ;;
	esac
	;;
    esac
    ;;
esac

case $headerfile in
'')
    . $rnlib/Pnews.header
    ;;
*)
    $cat < $headerfile  > $tmpart
    ;;
esac
    rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.article ; $echo saved in ${HOME-$LOGDIR}/dead.article ; $rm -f $tmpart; exit"
    trap "$rescue" 1
    trap "$rescue" 2

$echo ""
set X `$sed < $tmpart -n -e '/^Distribution: /{' -e p -e q -e '}' -e '/^$/q'`
shift
case $# in
0|1)
    set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'`
    shift
    case $# in
    0|1)
	set "x net.whatever"
	;;
    esac
    ;;
*)
    set $1 $2.whatever
    ;;
esac
shift

#: play recorded message
#if $test -s ${lib}/recording ; then
#     ng=`$echo $1 | $sed "s/,.*//"`
#    _rec1=${lib}/`$sed -n "/^$ng/s/^.*	//p" ${lib}/recording`
#    _tmp=`$echo $ng |$sed "s/\..*//"`
#    _rec2=${lib}/`$cat -s ${lib}/recording|$grep ${_tmp}.all|$sed "s/^.*	//"`
#    if $test -f ${_rec1} ; then
#	$cat -s ${_rec1}
#    fi
#    if $test -f ${_rec2} ; then
#	$cat -s ${_rec2}
#    fi
#fi

# tell them what we think they are doing... !DIST!
case $1 in
net.*)
    $echo 'This program posts news to many hundreds of machines throughout the world.'
    ;;
$cont.*)
    $echo 'This program posts news to many machines throughout the continent.'
    ;;
$cntry.*)
    $echo 'This program posts news to many machines throughout the country.'
    ;;
$state.*)
    $echo 'This program posts news to many machines throughout the state.'
    ;;
$city.*)
    $echo 'This program posts news to many machines throughout the city.'
    ;;
$org.*)
    $echo 'This program posts news to machines throughout the organization.'
    ;;
$loc.*)
    $echo 'This program posts news to machines throughout the local organization.'
    ;;
*.*)
    $echo 'This program may post news to many machines.'
    ;;
*)
    $echo 'This program posts news to everyone on the machine.'
    ;;
esac
ans=""
while $test "$ans" = "" ; do
    $echo $n "Are you absolutely sure that you want to do this? [ny] $c"
    read ans
    case $ans in
    y*) ;;
    f*) suppressmess=y ;;
    h*) $cat <<'EOH'

Type n or CR to exit, y to post.

EOH
	ans="" ;;
    *) exit ;;
    esac
done

file=h
while $test "$file" = h ; do
    $echo ""
    $echo $n "Prepared file to include [none]: $c"
    read file
    case $file in
    h)
	$cat <<'EOH'

If you have already produced the body of your article, type the filename
for it here.  If you just want to proceed directly to the editor, type a
RETURN.  In any event, you will be allowed to edit as many times as you
want before you send off the article.
EOH
	;;
    '')
	$echo "" >> $tmpart
	state=edit
	;;
    *)
	$cat $file >>$tmpart
	state=ask
	;;
    esac
done

$echo ""

while true ; do
    case $state in
    edit)
	case $expertise in
	beginner)
	    $cat </dev/null >${DOTDIR-${HOME-$LOGDIR}}/.pnewsexpert
	    $cat <<'EOMessage'
A temporary file has been created for you to edit.  Be sure to leave at
least one blank line between the header and the body of your message.
(And until a certain bug is fixed all over the net, don't start the body of
your message with any indentation, or it may get eaten.)

Within the header may be fields that you don't understand.  If you don't
understand a field (or even if you do), you can simply leave it blank, and
it will go away when the article is posted.

Type return to get the default editor, or type the name of your favorite
editor.

EOMessage
	    ;;
	esac
	case "${VISUAL-${EDITOR-}}" in
	'')
	    tmp=h
	    ;;
	*)
	    tmp=''
	    ;;
	esac
	while $test "$tmp" = h ; do
	    $echo $n "Editor [${VISUAL-${EDITOR-$defeditor}}]: $c"
	    read tmp
	    case $tmp in
	    h)
		$cat <<'EOH'

Type a return to get the default editor, or type the name of the editor you
prefer.  The default editor depends on the VISUAL and EDITOR environment
variables.

EOH
		;;
	    '')
		;;
	    *)
		VISUAL=$tmp
		export VISUAL
		;;
	    esac
	done
	trap : 2
	${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart
	trap "$rescue" 2
	state=ask
	;;
	
    ask)
	$echo ""
	$echo $n "Send, abort, edit, or list? $c"
	read ans
	
	case $ans in
	a*)
	    state=rescue
	    ;;
	e*)
	    state=edit
	    ;;
	l*)
	    $pager $tmpart
	    state=ask
	    ;;
	s*)
	    state=send
	    ;;
	h*)
	    $cat <<'EOH'

Type s to send the article, a to abort and append the article to dead.article,
e to edit the article again, or l to list the article.
EOH
	esac
	;;
    
    send)
	set X `$sed < $tmpart -n -e '/^Newsgroups: /{' -e p -e q -e '}'`
	shift
	case $# in
	2)
	    state=cleanup
	    if $test -f $lib/moderators; then
		tryinews=no
		shift
		case "$1" in
		*,*) set `$echo $1 | tr ',' ' '`;;
		esac
		for newsgroup in $*; do
# the following screwy sed should prevent Eunice from hanging on no match
		    moderator=`$sed <$lib/moderators \\
		    -e "/^$newsgroup[ 	]/!s/.*//" \\
		    -e "s/^$newsgroup[ 	]//"`
		    case ${moderator}X in
		    X)  tryinews=yes
			;;
		    *)
			$echo Mailing to moderator $moderator
			case "$mailer" in
			*recmail)
			    $echo To: $moderator | $cat - $tmpart | $mailer
			    ;;
			*)
			    $mailer $moderator < $tmpart
			    ;;
			esac
			case $? in
			0) ;;
			*)
			    $echo Unable to mail to moderator $moderator
			    state=rescue
			    ;;
			esac
			;;
		    esac
		done
	    else
		tryinews=yes
	    fi
	    case "$tryinews" in
	    yes)
		if $inews -h < $tmpart ; then
		    : null
		else
		    state=rescue
		fi
		;;
	    esac
	    ;;
	*)
	    $echo ""
	    $echo "Malformed Newsgroups line."
	    $echo ""
	    sleep 1
	    state=edit
	    ;;
	esac
	;;
    rescue)
	$cat $tmpart >> ${HOME-$LOGDIR}/dead.article
	$echo "Article saved to ${HOME-$LOGDIR}/dead.article"
	state=cleanup
	;;
    cleanup)
	$rm -f $tmpart
	exit
	;;
    esac
done
!NO!SUBS!
$eunicefix Pnews
chmod 755 Pnews
$spitshell >Pnews.header <<'!NO!SUBS!'
case $# in
0)
    ng=h
    while $test "$ng" = h ; do
	$echo ""
	$echo $n "Newsgroup(s): $c"
	read ng
	case $ng in
	h)
	    $cat <<'EOH'

Type the name of one or more newsgroups to which you wish to post an article.
If you want to post to multiple newsgroups, it is better to do them all at
once than to post to each newsgroup individually, which defeats the news
reading programs' strategies of eliminating duplicates.

Separate multiple newsgroup names with commas.
EOH
	    ;;
	esac
    done
    ;;
*)
    ng=$1
    shift
    ;;
esac
case $ng in
*\ *)
    ng=`$echo "$ng" | $sed 's/[, ] */,/g'`
    ;;
esac
case $ng in
net.*|fa.*|mod.*)
    defdist=net
    dist=h
    ;;
*.*)
    defdist=`expr "X$ng" : 'X\([a-z0-9]*\)'`
    dist=h
    ;;
*)
    defdist=''
    dist=''
    ;;
esac

while $test "$dist" = h ; do
    if $test -f $lib/distributions; then
	$echo " "
	$echo "Your local distribution prefixes are:"
	$cat $lib/distributions
    else
	$egrep -v '[	 ]none$' <<EOM

Your local distribution prefixes are:
    Local organization:	$loc
    Organization:		$org
    City:			$city
    $stpr:  		$state
    Country:		$cntry
    Continent:		$cont
    Everywhere:		net,mod,fa

EOM
    fi
    $echo $n "Distribution ($defdist): $c"
    read dist
    case $dist in
    '') dist=$defdist ;;
    esac
    case $dist in
    h)
	$cat <<'EOH'

The Distribution line may be used to limit the distribution of an article
to some subset of the systems that would receive the article based only on
the Newsgroups line.  For example, if you want to sell your car in net.auto,
and you live in New Jersey, you might want to put "nj" on the Distribution
line to avoid advertising in California, which has enough problems of its own.
The actual area designators to use depend on where you are, of course.
EOH
	;;
    ''|$loc*|$org*|$city*|$state*|$cntry*|$cont*|fa*|mod*)
	;;
    net*|world*)
	dist=''
	;;
    *)  
	if $test -f $lib/distributions && \
	  $egrep "^$dist[ 	]" $lib/distributions >$tmpart && \
	  $test -s $tmpart; then
	    : null
	else
	    $echo "Unrecognized distribution prefix--type h for help."
	    dist=h
	fi
	;;
    esac
done

case $ng in
*net.general*)
    follow=`echo "$ng" | sed 's/net\.general/net.followup/g'`
    ;;
*)
    follow=""
    ;;
esac

case $# in
0)
    title=h
    while $test "$title" = h ; do
	$echo ""
	$echo $n "Title/Subject: $c"
	read title
	case $title in
	h)
	    $cat <<'EOH'

Type the title for your article.  Please make it as informative as possible
(within reason) so that people who aren't interested won't have to read the
article to find out they aren't interested.  This includes marking movie
spoilers as (spoiler), and rotated jokes as (rot 13).
EOH
	;;
	esac
    done
    ;;
*)
    title="$*"
    ;;
esac

# now build a file with a header for them to edit

set X ${USER-${LOGNAME-`who am i`}}
shift
logname=$1
case $logname in
*!*) logname=`expr "$logname" : '!\(.*\)$'` ;;
esac
case ${NAME-$nametype} in
bsd)
    fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:;]*\).*"'$'"/\1/" -e "q" -e "}" -e "d"`
    case $fullname in
    *'&'*) : GACK
	lname=`$echo $logname | $tr 'a-z' 'A-Z'`
	lname=`$echo $lname $logname | $sed 's/^\(.\)[^ ]* ./\1/'`
	fullname=`$echo "$fullname" | $sed "s/&/${lname}/"`
	;;
    esac
    ;;
usg)
    fullname=`$sed </etc/passwd -e "/^$logname:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:\([^(:]*\).*"'$'"/\1/" -e "s/^.*-//" -e "q" -e "}" -e "d"`
    ;;
*)
    fullname=${NAME-`$cat ${HOME-$LOGDIR}/.fullname`}
    ;;
esac

orgname=${ORGANIZATION-$orgname}
case $orgname in
/*) orgname=`$cat $orgname` ;;
esac

$cat > $tmpart <<EOHeader
Newsgroups: $ng
Subject: $title
Expires: 
References: 
Sender: 
Reply-To: $logname@$sitename.UUCP ($fullname)
Followup-To: $follow
Distribution: $dist
Organization: $orgname
Keywords: 

EOHeader

!NO!SUBS!
$eunicefix Pnews.header
!STUFFY!FUNK!
echo Extracting rn.c
cat >rn.c <<'!STUFFY!FUNK!'
/*  rn -- new readnews program
 *
 *  From: lwall at sdcrdcf.UUCP (Larry Wall)
 *  Organization: System Development Corporation, Santa Monica
 *
 *  begun:   01/14/83
 *	1.0: 04/08/83
 *      2.0: 09/01/83
 */

static char rnid[] = "@(#)$Header: rn.c,v 4.3 85/05/01 11:47:56 lwall Exp $";

/* $Log:	rn.c,v $
 * Revision 4.3  85/05/01  11:47:56  lwall
 * Baseline for release with 4.3bsd.
 * 
 */

#include "INTERN.h"
#include "common.h"
#include "rn.h"
#include "EXTERN.h"
#include "rcstuff.h"
#include "term.h"
#include "final.h"
#include "ngdata.h"
#include "util.h"
#include "only.h"
#include "ngsrch.h"
#include "help.h"
#include "last.h"
#include "init.h"
#include "intrp.h"
#include "rcln.h"
#include "sw.h"
#include "addng.h"
#include "ng.h"
#include "INTERN.h"

void
rn_init()
{
    ;
}

void
main(argc,argv)
int argc;
char *argv[];
{
    bool foundany = initialize(argc,argv);
    register char *s;
    bool oh_for_the_good_old_days = FALSE;
    
    if (maxngtodo)
	starthere = 0;
    else if (!foundany) {		/* nothing to do? */
#ifdef VERBOSE
	if (verbose)
	    fputs("\
No unread news in subscribed-to newsgroups.  To subscribe to a new\n\
newsgroup use the g<newsgroup> command.\n\
",stdout) FLUSH;
#endif
	starthere = nextrcline;
    }

    /* loop through all unread news */

    {
	char promptbuf[80];
	bool special = FALSE;		/* temporarily allow newsgroup */
					/*   with no unread news? */
	bool retry;			/* cycle back to top of list? */
	NG_NUM recent_ng = 0;
	
	current_ng = 0;
	do {
	    retry = FALSE;
	    if (findlast) {
		findlast = FALSE;
		starthere = 0;
		if (*lastngname) {
		    if ((ng = find_ng(lastngname)) == nextrcline)
			ng = 0;
		    else {
			set_ngname(lastngname);
		    	set_toread(ng);
			if (toread[ng] <= TR_NONE)
			    ng = 0;
		    }
		}
	    }
	    else {
		ng = starthere;
		starthere = 0;
	    }
	    while (ng <= nextrcline) {	/* for each newsgroup */
		mode = 'n';
		if (ng >= nextrcline) {	/* after the last newsgroup? */
		    ng = nextrcline;	/* force it to 1 after */
#ifdef ONLY
		    if (maxngtodo) {
			if (retry)
#ifdef VERBOSE
			    IF(verbose)
				printf("\nRestriction %s%s still in effect.\n",
				    ngtodo[0],
				    maxngtodo > 1 ? ", etc." : nullstr) FLUSH;
			    ELSE
#endif
#ifdef TERSE
				fputs("\n(\"Only\" mode.)\n",stdout) FLUSH;
#endif
			else {
#ifdef VERBOSE
			    IF(verbose)
				fputs("\nNo articles under restriction.",
				  stdout) FLUSH;
			    ELSE
#endif
#ifdef TERSE
				fputs("\nNo \"only\" articles.",stdout) FLUSH;
#endif
			    end_only();	/* release the restriction */
			    retry = TRUE;
			}
		    }
#endif
		    dfltcmd = (retry ? "npq" : "qnp");
#ifdef VERBOSE
		    IF(verbose)
			sprintf(promptbuf,
			    "\n******** End of newsgroups--what next? [%s] ",
			    dfltcmd);
		    ELSE
#endif
#ifdef TERSE
			sprintf(promptbuf,
			    "\n**** End--next? [%s] ", dfltcmd);
#endif
		}
		else {
		    bool shoe_fits;	/* newsgroup matches restriction? */

		    if (toread[ng] >= TR_NONE) {	/* recalc toread? */
			set_ngname(rcline[ng]);
			if (shoe_fits = (special || inlist(ngname)))
			    set_toread(ng);
			if (paranoid) {
			    recent_ng = current_ng;
			    current_ng = ng;
			    cleanup_rc();
					/* this may move newsgroups around */
			    ng = current_ng;
			    set_ngname(rcline[ng]);
			}
		    }
		    if (toread[ng] < (maxngtodo||special ? TR_NONE : TR_ONE) || !shoe_fits) {
					/* unwanted newsgroup? */
			ng++;		/* then skip it */
			continue;
		    }
		    dfltcmd = "ynq";
#ifdef VERBOSE
		    IF(verbose)
			sprintf(promptbuf,
			    "\n******** %3ld unread article%c in %s--read now? [%s] ",
			    (long)toread[ng], (toread[ng]==TR_ONE ? ' ' : 's'),
			    ngname, dfltcmd);	/* format prompt string */
		    ELSE
#endif
#ifdef TERSE
			sprintf(promptbuf,
			    "\n**** %3ld in %s--read? [%s] ",
			    (long)toread[ng],
			    ngname,dfltcmd);	/* format prompt string */
#endif
		}
		special = FALSE;	/* go back to normal mode */
		if (ng != current_ng) {
		    recent_ng = current_ng;
					/* remember previous newsgroup */
		    current_ng = ng;	/* remember current newsgroup */
		}
    reask_newsgroup:
		unflush_output();	/* disable any ^O in effect */
		fputs(promptbuf,stdout) FLUSH;/* print prompt */
		fflush(stdout);
    reinp_newsgroup:
		eat_typeahead();
		getcmd(buf);
		if (errno || *buf == '\f') {
		    putchar('\n') FLUSH; /* if return from stop signal */
		    goto reask_newsgroup;	/* give them a prompt again */
		}
		setdef(buf,dfltcmd);
#ifdef VERIFY
		printcmd();
#endif
		switch (*buf) {
		case 'p':		/* find previous unread newsgroup */
		    do {
			if (ng <= 0)
			    break;
			ng--;
			if (toread[ng] == TR_NONE)
			    set_toread(ng);
		    } while (toread[ng] <= TR_NONE);
		    break;
		case 'P':		/* goto previous newsgroup */
		    do {
			if (ng <= 0)
			    break;
			ng--;
		    } while (toread[ng] < TR_NONE);
		    special = TRUE;	/* don't skip it if toread==0 */
		    break;
		case '-':
		    ng = recent_ng;	/* recall previous newsgroup */
		    special = TRUE;	/* don't skip it if toread==0 */
		    break;
		case 'q': case 'Q': case 'x':	/* quit? */
		    oh_for_the_good_old_days = (*buf == 'x');
		    putchar('\n') FLUSH;
		    ng = nextrcline+1;	/* satisfy */
		    retry = FALSE;	/*   loop conditions */
		    break;
		case '^':
		    putchar('\n') FLUSH;
		    ng = 0;
		    break;
		case 'n': case '+':	/* find next unread newsgroup */
		    if (ng == nextrcline) {
			putchar('\n') FLUSH;
			retry = TRUE;
		    }
		    else if (toread[ng] > TR_NONE)
			retry = TRUE;
		    ng++;
		    break;
		case 'N':		/* goto next newsgroup */
		    ng++;
		    special = TRUE;	/* and don't skip it if toread==0 */
		    break;
		case '1':		/* goto 1st newsgroup */
		    ng = 0;
		    special = TRUE;	/* and don't skip it if toread==0 */
		    break;
		case '$':
		    ng = nextrcline;	/* goto last newsgroup */
		    retry = TRUE;
		    break;
		case 'L':
		    list_newsgroups();
		    goto reask_newsgroup;
		case '/': case '?':	/* scan for newsgroup pattern */
#ifdef NGSEARCH
		    switch (ng_search(buf,TRUE)) {
		    case NGS_ABORT:
			goto reinp_newsgroup;
		    case NGS_INTR:
#ifdef VERBOSE
			IF(verbose)
			    fputs("\n(Interrupted)\n",stdout) FLUSH;
			ELSE
#endif
#ifdef TERSE
			    fputs("\n(Intr)\n",stdout) FLUSH;
#endif
			ng = current_ng;
			goto reask_newsgroup;
		    case NGS_FOUND:
			special = TRUE;	/* don't skip it if toread==0 */
			break;
		    case NGS_NOTFOUND:
#ifdef VERBOSE
			IF(verbose)
			    fputs("\n\nNot found--use g to add newsgroups\n",
				stdout) FLUSH;
			ELSE
#endif
#ifdef TERSE
			    fputs("\n\nNot found\n",stdout) FLUSH;
#endif
			goto reask_newsgroup;
		    }
#else
		    notincl("/");
#endif
		    break;
		case 'm':
#ifndef RELOCATE
		    notincl("m");
		    break;
#endif		    
		case 'g':	/* goto named newsgroup */
		    if (!finish_command(FALSE))
					/* if they didn't finish command */
			goto reinp_newsgroup;	/* go try something else */
		    for (s = buf+1; *s == ' '; s++);
					/* skip leading spaces */
		    if (!*s)
			strcpy(s,ngname);
#ifdef RELOCATE
		    if (!get_ng(s,*buf=='m'))	/* try to find newsgroup */
#else
		    if (!get_ng(s,FALSE))	/* try to find newsgroup */
#endif
			ng = current_ng;/* if not found, go nowhere */
		    special = TRUE;	/* don't skip it if toread==0 */
		    break;
#ifdef DEBUGGING
		case 'D':
		    printf("\nTries: %d Hits: %d\n",
			softtries,softtries-softmisses) FLUSH;
		    goto reask_newsgroup;
#endif
		case '!':		/* shell escape */
		    if (escapade())	 /* do command */
			goto reinp_newsgroup;
					/* if rubbed out, re input */
		    goto reask_newsgroup;
		case Ctl('k'):		/* edit global KILL file */
		    edit_kfile();
		    goto reask_newsgroup;
		case 'c':		/* catch up */
#ifdef CATCHUP
reask_catchup:
#ifdef VERBOSE
		    in_char("\nDo you really want to mark everything as read? [yn] ");
#else
		    in_char("\nReally? [ynh] ");
#endif
		    putchar('\n') FLUSH;
		    setdef(buf,"y");
		    if (*buf == 'h') {
#ifdef VERBOSE
		    printf("Type y or SP to mark all articles as read.\n");
		    printf("Type n to leave articles marked as they are.\n");
#else
		    printf("y or SP to mark all read.\n");
		    printf("n to forget it.\n");
#endif
			goto reask_catchup;
		    }
		    else if (*buf!=' ' && *buf!='y' && *buf!='n' && *buf!='q') {
			printf(hforhelp);
			settle_down();
			goto reask_catchup;
		    } else if ( (*buf == ' ' || *buf == 'y') && ng<nextrcline )
			catch_up(ng);
		    else
			retry = TRUE;
		    ng++;
#else
		    notincl("c");
#endif
		    break;
		case 'u':		/* unsubscribe */
		    if (ng < nextrcline && toread[ng] >= TR_NONE) {
					/* unsubscribable? */
			printf(unsubto,rcline[ng]) FLUSH;
			rcchar[ng] = NEGCHAR;
					/* unsubscribe to (from?) it */
			toread[ng] = TR_UNSUB;
					/* and make line invisible */
			ng++;		/* do an automatic 'n' */
		    }
		    break;
		case 'h': {		/* help */
		    int cmd;

		    if ((cmd = help_ng()) > 0)
			pushchar(cmd);
		    goto reask_newsgroup;
		}
		case 'a':
#ifndef FINDNEWNG
		    notincl("a");
		    goto reask_newsgroup;
#else
		    /* FALL THROUGH */
#endif
		case 'o':
#ifdef ONLY
		{
#ifdef FINDNEWNG
		    bool doscan = (*buf == 'a');
#endif

		    if (!finish_command(TRUE)) /* get rest of command */
			goto reinp_newsgroup;	/* if rubbed out, try something else */
		    end_only();
		    if (buf[1]) {
			bool minusd = instr(buf+1,"-d") != Nullch;

			sw_list(buf+1);
			if (minusd)
			    cwd_check();
			putchar('\n') FLUSH;
#ifdef FINDNEWNG
			if (doscan && maxngtodo)
			    scanactive();
#endif
		    }
		    ng = 0;		/* simulate ^ */
		    retry = FALSE;
		    break;
		}
#else
		    notincl("o");
		    goto reask_newsgroup;
#endif
		case '&':
		    if (switcheroo()) /* get rest of command */
			goto reinp_newsgroup;	/* if rubbed out, try something else */
		    goto reask_newsgroup;
		case 'l': {		/* list other newsgroups */
		    if (!finish_command(TRUE)) /* get rest of command */
			goto reinp_newsgroup;	/* if rubbed out, try something else */
		    for (s = buf+1; *s == ' '; s++);
		    			/* skip leading spaces */
		    sprintf(cmd_buf,"%s '%s'",filexp(NEWSGROUPS),s);
		    resetty();
		    if (doshell(sh,cmd_buf))
#ifdef VERBOSE
			IF(verbose)
			    fputs("    (Error from newsgroups program)\n",
				stdout) FLUSH;
			ELSE
#endif
#ifdef TERSE
			    fputs("(Error)\n",stdout) FLUSH;
#endif
		    noecho();
		    crmode();
		    goto reask_newsgroup;
		}
		case '.': case '=':
		case 'y': case 'Y': /* do normal thing */
		    if (ng >= nextrcline) {
			fputs("\nNot on a newsgroup.",stdout) FLUSH;
			goto reask_newsgroup;
		    }
		    if (*buf == '=')
			s = savestr("=");
		    else if (*buf == '.') {	/* start command? */
			if (!finish_command(FALSE)) /* get rest of command */
			    goto reinp_newsgroup;
			s = savestr(buf+1);
					/* do_newsgroup will free it */
		    }
		    else
			s = Nullch;
		    if (toread[ng])
			retry = TRUE;
		    switch (do_newsgroup(s)) {
		    case NG_ERROR:
		    case NG_NORM:
			ng++;
			break;
		    case NG_ASK:
			goto reask_newsgroup;
		    case NG_MINUS:
			ng = recent_ng;	/* recall previous newsgroup */
			special = TRUE;	/* don't skip it if toread==0 */
			break;
		    }
		    break;
#ifdef STRICTCR
		case '\n':
		    fputs(badcr,stdout) FLUSH;
		    goto reask_newsgroup;
#endif
		case 'v':
		    printf("\n%s\n",rnid) FLUSH;
		    goto reask_newsgroup;
		default:
		    printf("\n%s",hforhelp) FLUSH;
		    settle_down();
		    goto reask_newsgroup;
		}
	    }
	} while (retry);
    }

    /* now write .newsrc back out */

    write_rc();

    if (oh_for_the_good_old_days)
	get_old_rc();

    finalize(0);			/* and exit */
}

/* set current newsgroup */

void
set_ngname(what)
char *what;
{
    int len = strlen(what)+1;

    growstr(&ngname,&ngnlen,len);
    strcpy(ngname,what);
    growstr(&ngdir,&ngdlen,len);
    strcpy(ngdir,getngdir(ngname));
}

static char *myngdir;
static int ngdirlen = 0;

char *
getngdir(ngnam)
char *ngnam;
{
    register char *s;

    growstr(&myngdir,&ngdirlen,strlen(ngnam)+1);
    strcpy(myngdir,ngnam);
    for (s = myngdir; *s; s++)
	if (*s == '.')
	    *s = '/';
    return myngdir;
}

!STUFFY!FUNK!
echo Extracting rcln.c
cat >rcln.c <<'!STUFFY!FUNK!'
/* $Header: rcln.c,v 4.3 85/05/01 11:45:36 lwall Exp $
 *
 * $Log:	rcln.c,v $
 * Revision 4.3  85/05/01  11:45:36  lwall
 * Baseline for release with 4.3bsd.
 * 
 */

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

void
rcln_init()
{
    ;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

void
set_toread(ngnum)
register NG_NUM ngnum;
{
    register char *s, *c, *h;
    char tmpbuf[64], *mybuf = tmpbuf;
    char *nums;
    int length;
#ifdef CACHEFIRST
    bool virgin_ng = (!abs1st[ngnum]);
#endif
    ART_NUM ngsize = getngsize(ngnum);
    ART_NUM unread = ngsize;
    ART_NUM newmax;

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

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

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

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

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

!STUFFY!FUNK!
echo Extracting HACKERSGUIDE
cat >HACKERSGUIDE <<'!STUFFY!FUNK!'
Hacking Notes

If you aren't interested in mucking with the innards of rn, don't read this.

In the interests of both space and time optimization, things are done inside
rn that don't always conform to the highest ideals of programming.  To the
extent I felt it was practical, I've tried to conform to good programming
practice, but you must realize that my goal was to make a better mousetrap,
so certain conscious tradeoffs were made in the design of rn right from the
start.  In particular, if you want to hack on rn (and I wouldn't blame you,
it's fun), beware of the following:
  
  * buf and cmd_buf are reused all over the place.  11-squishing is a good
    term for it.  No, I'm on a Vax now, but I've been there.

  * The article header is parsed on the fly, while it is being displayed.
    In fact, practically everything is done on the fly within the article
    display loop, and there are plenty of state variables.  The header
    is never explicitly stored in memory; rather, pointers are kept into
    the file.  The information required to backup pages is not stored in
    memory, except for 1 buffer's worth.  The information required to do
    the delayed mark as unread (M) is not stored in memory either.

  * Lots of contortions are gone through to avoid using static memory, or
    allocating unnecessary memory, or losing track of allocated memory,
    while at the same time allowing .newsrc lines and header lines to be
    ANY length up to the amount of memory you have.  Rn spends a great deal
    of effort being lazy.  Do not use a static buffer when you can use
    growstr().

  * Lots of contortions are gone through to try to do things when people
    aren't waiting, or have only been waiting a very short time.  Guessing
    the next article to be opened and opening it, searching ahead for the
    next article with the same subject, delaying the look up of the number
    of articles in a newsgroup, writing the rest of the page while the
    reader is examining the header, cacheing up subjects while the user
    is reading, checkpointing the .newsrc only while the reader is in the
    middle of an interesting article, are some of the strategies employed.
  
  * There are plenty of goto's.  Most of them involve going back to reprompt,
    to reask for input, or to just plain do the unstructured things people
    want to do when they are glaring at a terminal.  If they bother you
    too much, just think of rn as a big state machine.  If they don't bother
    you at all, I don't want you hacking on rn.

  * Put all includes at the front of the file, before the first function,
    or makedepend will not work right.  I could relax this, but makedepend
    would take about 5 times longer to run.

In general then, feel free to hack on rn.  Just don't broadcast untested
patches to the net.  Remember that there are people with limited address
spaces and limited cpu cycles.  If you add a wonderful new feature and
want to publish a patch, put #ifdef's around it so that people who don't
want it or can't afford it can work around it.  THIS MEANS YOU.  We don't
need 57 varieties of mutually incompatible and incomprehensible rn floating
about the net.  Consider telling me about your patch so that I can consider
including it in the standard version.  A COMPLETE PATCH TAKES INTO ACCOUNT
SYSTEM DEPENDENCIES AS DETERMINED BY THE CONFIGURE SCRIPT.

* Don't use ints where rn uses typedefs, in particular, for article numbers.
* Don't use %d anywhere that someone might need a %ld.  (Just because YOU
    typedefed it as an int doesn't mean someone else won't need a long.)
* Don't use %D, that's archaic.
* Put FLUSHes after printf()s, fputs()es and putchar('\n')s for our poor
    brethern and sistern without line buffering.
* Declare the type of every function.  Use void, even if your C compiler
    doesn't.
* Follow the style that rn already uses!  This is my pet peeve.  Well, one of
    them, anyway.  I follow other people's strange styles when modifying
    their programs, so I'd be much obliged if you did likewise.
* Use lint.
* Use RCS.  Start a new branch, like 4.3.[2-9].  (I will use 4.3.1 myself.)
* Be structured wherever it doesn't interfere with practicality.
* Long live paranoid programming.  The rest of the program is out to get you.
    The world is out to destroy the program, not to mention the .newsrc.
    And then there's always bitrot...
* Stop reading this lugubrious trash and start thinking for yourself.
* Thank you and good night.
!STUFFY!FUNK!
echo ""
echo "End of kit 5 (of 9)"
cat /dev/null >kit5isdone
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