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