Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/3/84; site genrad.UUCP Path: utzoo!decvax!genrad!sources-request From: sources-request@genrad.UUCP Newsgroups: mod.sources Subject: rn version 4.3 (kit 5 of 9) Message-ID: <826@genrad.UUCP> Date: Sat, 11-May-85 07:56:03 EDT Article-I.D.: genrad.826 Posted: Sat May 11 07:56:03 1985 Date-Received: Sun, 12-May-85 02:45:52 EDT Sender: john@genrad.UUCP Organization: System Development Corporation R&D, Santa Monica Lines: 2695 Approved: john@genrad.UUCP From: lwall@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= ' ') 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 <>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 ${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$' <$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 $tmpart <rn.c <<'!STUFFY!FUNK!' /* rn -- new readnews program * * From: lwall@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 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= 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