lwall@sdcrdcf.UUCP (Larry Wall) (10/13/84)
#! /bin/sh # Make a new directory for the rn sources, cd to it, and run kits 1 thru 8 # through sh. When all 8 kits have been run, read README. echo "This is rn kit 6 (of 8). If kit 6 is complete, the line" echo '"'"End of kit 6 (of 8)"'" will echo at the end.' echo "" export PATH || (echo "You didn't use sh, you clunch." ; kill $$) echo Extracting help.c cat >help.c <<'!STUFFY!FUNK!' /* $Header: help.c,v 4.1 84/09/24 11:48:20 lwall Exp $ * * $Log: help.c,v $ * Revision 4.1 84/09/24 11:48:20 lwall * Real baseline. * * Revision 4.0.1.1 84/09/10 15:11:41 lwall * Delinted. * * Revision 4.0 84/09/04 09:50:27 lwall * Baseline for netwide release * */ #include "EXTERN.h" #include "common.h" #include "rn.h" #include "term.h" #include "INTERN.h" #include "help.h" void help_init() { ; } void help_page() { #ifdef PAGERHELP doshell(sh,filexp(PAGERHELP)); #else putchar('\n'); page_line = 1; print_lines("\ Paging commands:\n\ ",STANDOUT); print_lines("\n\ SP Display the next page.\n\ x Display the next page decrypted (rot13).\n\ d Display half a page more.\n\ CR Display one more line.\n\ ^R,v,^X Restart the current article (v=verbose header, ^X=rot13).\n\ ",NOMARKING); print_lines("\ ^B Back up one page.\n\ ^L,X Refresh the screen (X=rot13).\n\ g pat Go to (search forward within article for) pattern.\n\ G Search again for current pattern within article.\n\ ^G Search for next line beginning with \"Subject:\".\n\ q Quit the pager, go to end of article. Leave article read or unread.\n\ j Junk this article (mark it read). Goes to end of article.\n\ \n\ ",NOMARKING); print_lines("\ The following commands skip the rest of the current article, then behave\n\ just as if typed to the 'What next?' prompt at the end of the article:\n\ ",STANDOUT); print_lines("\n\ n Scan forward for next unread article.\n\ N Go to next article.\n\ ^N Scan forward for next unread article with same title.\n\ p,P,^P Same as n,N,^N, only going backwards.\n\ - Go to previously displayed article.\n\ \n\ ",NOMARKING); print_lines("\ The following commands also take you to the end of the article.\n\ Type h at end of article for a description of these commands:\n\ ",STANDOUT); print_lines("\ # $ & / = ? c C f F k K ^K m M number r R ^R s S u v w W Y ^ |\n\ \n\ (To return to the middle of the article after one of these commands, type ^L.)\n\ ",NOMARKING); #endif } void help_art() { #ifdef ARTHELP doshell(sh,filexp(ARTHELP)); #else putchar('\n'); page_line = 1; print_lines("\ Article Selection commands:\n\ ",STANDOUT); print_lines("\n\ n,SP Scan forward for next unread article.\n\ N Go to next article.\n\ ^N Scan forward for next unread article with same subject.\n\ p,P,^P Same as n,N,^N, only going backwards.\n\ - Go to previously displayed article.\n\ ",NOMARKING); print_lines("\ number Go to specified article.\n\ range{,range} command{:command}\n\ Apply one or more commands to one or more ranges of articles.\n\ Ranges are of the form: number | number-number. You may use . for\n\ the current article, and $ for the last article.\n\ Valid commands are: j, m, M, s, S, and !.\n\ ",NOMARKING); print_lines("\ /pattern/modifiers\n\ Scan forward for article containing pattern in the subject line.\n\ (Use ?pat? to scan backwards; append h to scan headers, a to scan\n\ entire articles, r to scan read articles, c to make case sensitive.\n\ /pattern/modifiers:command{:command}\n\ Apply one or more commands to the set of articles matching pattern.\n\ Use a K modifier to save entire command to the KILL file for this\n\ newsgroup. Commands m and M, if first, imply an r modifier.\n\ Valid commands are: j, m, M, s, S, and !.\n\ ",NOMARKING); print_lines("\ f,F Submit a followup article (F = include this article).\n\ r,R Reply through net mail (R = include this article).\n\ s ... Save to file or pipe via sh.\n\ S ... Save via preferred shell.\n\ w,W Like s and S but save without the header.\n\ | ... Same as s|...\n\ C Cancel this article, if yours.\n\ ",NOMARKING); print_lines("\ ^R,v Restart article (v=verbose).\n\ ^X Restart article, rot13 mode.\n\ c Catch up (mark all articles as read).\n\ ^B Back up one page.\n\ ^L Refresh the screen. You can get back to the pager with this.\n\ X Refresh screen in rot13 mode.\n\ ",NOMARKING); print_lines("\ ^ Go to first unread article. Disables subject search mode.\n\ $ Go to end of newsgroup. Disables subject search mode.\n\ ",NOMARKING); print_lines("\# Print last article number.\n\ & Print current values of command-line switches.\n\ &switch {switch}\n\ Set or unset more switches.\n\ j Junk this article (mark it read). Stays at end of article.\n\ m Mark article as still unread.\n\ M Mark article as still unread upon exiting newsgroup or Y command.\n\ ",NOMARKING); print_lines("\ Y Yank back articles marked temporarily read via M.\n\ k Mark current SUBJECT as read.\n\ K Mark current SUBJECT as read, and save command in KILL file.\n\ = List subjects of unread articles.\n\ u Unsubscribe to this newsgroup.\n\ ^K Edit local KILL file (the one for this newsgroup).\n\ q Quit this newsgroup for now.\n\ ",NOMARKING); #endif } void help_ng() { #ifdef NGHELP doshell(sh,filexp(NGHELP)); #else putchar('\n'); page_line = 1; print_lines("\ Newsgroup Selection commands:\n\ ",STANDOUT); if (ng != nextrcline) print_lines("\ \n\ y,SP Do this newsgroup now.\n\ .cmd Do this newsgroup, executing cmd as first command.\n\ = Equivalent to .=<carriage return>.\n\ u Unsubscribe from this newsgroup.\n\ c Catch up (mark this newsgroup all read).\n\ ",NOMARKING); print_lines("\ \n\ n Go to the next newsgroup with unread news.\n\ N Go to the next newsgroup.\n\ p Go to the previous newsgroup with unread news.\n\ P Go to the previous newsgroup.\n\ ",NOMARKING); print_lines("\ - Go to the previously displayed newsgroup.\n\ 1 Go to the first newsgroup.\n\ ^ Go to the first newsgroup with unread news.\n\ $ Go to the last newsgroup.\n\ ",NOMARKING); print_lines("\ g name Go to the named newsgroup. Subscribe to new newsgroups this way too.\n\ /pat Search forward for newsgroup matching pattern.\n\ ?pat Search backward for newsgroup matching pattern.\n\ (Use * and ? style patterns. Append r to include read newsgroups.)\n\ ",NOMARKING); print_lines("\ l pat List unsubscribed newsgroups containing pattern.\n\ m name Move named newsgroup elsewhere (no name moves current newsgroup).\n\ o pat Only display newsgroups matching pattern. Omit pat to unrestrict.\n\ a pat Like o, but also scans for unsubscribed newsgroups matching pattern.\n\ L List current .newsrc.\n\ ",NOMARKING); print_lines("\ & Print current command-line switch settings.\n\ &switch {switch}\n\ Set (or unset) more command-line switches.\n\ !cmd Shell escape.\n\ ",NOMARKING); print_lines("\ q Quit rn.\n\ ^K Edit the global KILL file. Use commands like /pattern/j to suppress\n\ pattern in every newsgroup.\n\ v Print version.\n\ ",NOMARKING); #endif } #ifdef ESCSUBS void help_subs() { #ifdef SUBSHELP doshell(sh,filexp(SUBSHELP)); #else putchar('\n'); page_line = 1; print_lines("\ Valid substitutions are:\n\ ",STANDOUT); print_lines("\ \n\ a Current article number\n\ A Full name of current article (%P/%c/%a)\n\ b Destination of last save command, often a mailbox\n\ B Bytes to ignore at beginning of last saved article\n\ ",NOMARKING); print_lines("\ c Current newsgroup, directory form\n\ C Current newsgroup, dot form\n\ d Full name of newsgroup directory (%P/%c)\n\ D Distribution line from current article\ ",NOMARKING); print_lines("\ f Who the current article is from\n\ F Newsgroups to followup to (from Newsgroups and Followup-To)\n\ h (This help message)\n\ H Host name (yours)\n\ i Message-I.D. line from current article, with <>\n\ ",NOMARKING); print_lines("\ l News administrator's login name, if any\n\ L Login name (yours)\n\ M Number of article marked with M\n\ n Newsgroups from current article\n\ N Full name (yours)\n\ ",NOMARKING); print_lines("\ o Organization (yours)\n\ O Original working directory (where you ran rn from)\n\ p Your private news directory (from -d)\n\ P Public news spool directory\n\ ",NOMARKING); print_lines("\ r Last reference (parent article id)\n\ R References list for followup article\n\ s Subject, with all Re's and (nf)'s stripped off\n\ S Subject, with one Re stripped off\ ",NOMARKING); print_lines("\ t New To line derived from From and Reply-To (Internet format)\n\ T New To line derived from Path\n\ u Number of unread articles\n\ U Number of unread articles not counting current article\n\ x News library directory\n\ X Rn library directory\n\ ",NOMARKING); print_lines("\ ~ Your home directory\n\ . Directory containing . files\n\ $ Current process number\n\ / Last search string\n\ ESC Run preceding command through % interpretation\n\ ",NOMARKING); #endif } #endif !STUFFY!FUNK! echo Extracting artsrch.c cat >artsrch.c <<'!STUFFY!FUNK!' /* $Header: artsrch.c,v 4.1 84/09/24 11:42:18 lwall Exp $ * * $Log: artsrch.c,v $ * Revision 4.1 84/09/24 11:42:18 lwall * Real baseline. * * Revision 4.0.1.1 84/09/10 15:05:28 lwall * Delinted. * * Revision 4.0 84/09/04 09:49:35 lwall * Baseline for netwide release * */ #include "EXTERN.h" #include "common.h" #include "search.h" #include "term.h" #include "util.h" #include "intrp.h" #include "bits.h" #include "kfile.h" #include "head.h" #include "final.h" #include "cheat.h" #include "ng.h" #include "artio.h" #include "INTERN.h" #include "artsrch.h" void artsrch_init() { #ifdef ARTSEARCH #ifdef ZEROGLOB init_compex(&sub_compex); init_compex(&art_compex); #endif #endif } /* search for an article containing some pattern */ #ifdef ARTSEARCH int art_search(patbuf,get_cmd) char *patbuf; /* if patbuf != buf, get_cmd must */ int get_cmd; /* be set to FALSE!!! */ { char *pattern; /* unparsed pattern */ register char cmdchr = *patbuf; /* what kind of search? */ register char *s; bool backward = cmdchr == '?' || cmdchr == Ctl('p'); /* direction of search */ COMPEX *compex; /* which compiled expression */ char *cmdlst = Nullch; /* list of commands to do */ int normal_return = SRCH_NOTFOUND; /* assume no commands */ bool saltaway = FALSE; /* store in KILL file? */ char howmuch; /* search just the subjects */ bool doread; /* search read articles? */ bool foldcase = TRUE; /* fold upper and lower case? */ int_count = 0; if (cmdchr == '/' || cmdchr == '?') { /* normal search? */ if (get_cmd && buf == patbuf) if (!finish_command(FALSE)) /* get rest of command */ return SRCH_ABORT; compex = &art_compex; if (patbuf[1]) { howmuch = 0; doread = FALSE; } else { howmuch = art_howmuch; doread = art_doread; } s = cpytill(buf,patbuf+1,cmdchr);/* ok to cpy buf+1 to buf */ pattern = buf; if (*pattern) { if (*lastpat) free(lastpat); lastpat = savestr(pattern); } if (*s) { /* modifiers or commands? */ for (s++; *s && index("Kharc",*s); s++) { if (*s == 'h') /* scan header */ howmuch = 1; else if (*s == 'a') /* scan article */ howmuch = 2; else if (*s == 'r') /* scan read articles */ doread = TRUE; else if (*s == 'K') /* put into KILL file */ saltaway = TRUE; else if (*s == 'c') /* make search case sensitive */ foldcase = FALSE; } } while (isspace(*s) || *s == ':') s++; if (*s) { if (*s == 'm' || *s == 'M') doread = TRUE; if (*s == 'k') /* grandfather clause */ *s = 'j'; cmdlst = savestr(s); normal_return = SRCH_DONE; } art_howmuch = howmuch; art_doread = doread; if (srchahead) srchahead = -1; } else { register char *h; howmuch = 0; /* just search subjects */ doread = (cmdchr == Ctl('p')); if (cmdchr == Ctl('n')) normal_return = SRCH_SUBJDONE; compex = &sub_compex; pattern = patbuf+1; strcpy(pattern,": *"); h = pattern + strlen(pattern); interp(h,"%s"); /* fetch current subject */ if (cmdchr == 'K') { saltaway = TRUE; cmdchr = 'k'; } if (cmdchr == 'k') { normal_return = SRCH_DONE; cmdlst = savestr("j"); mark_as_read(art); /* this article has this subject */ if (!*h) { #ifdef VERBOSE IF(verbose) fputs("\nCannot delete null subject.\n",stdout); ELSE #endif #ifdef TERSE fputs("\nNull subject.\n",stdout); #endif return SRCH_ABORT; } #ifdef VERBOSE else if (verbose) printf("\nMarking subject \"%s\" as read.\n",h); #endif } else if (!srchahead) srchahead = -1; h[24] = '\0'; /* compensate for notesfiles */ while (*h) { if (index("\\[.^*$'\"",*h) != Nullch) *h++ = '.'; else h++; } #ifdef DEBUGGING if (debug) { printf("\npattern = %s\n",pattern); } #endif } if ((s = compile(compex,pattern,TRUE,foldcase)) != Nullch) { /* compile regular expression */ printf("\n%s\n",s); return SRCH_ABORT; } #ifdef KILLFILES if (saltaway) { char saltbuf[LBUFLEN]; s = saltbuf; sprintf(s,"/%s/",pattern); s += strlen(s); if (doread) *s++ = 'r'; if (howmuch==1) *s++ = 'h'; else if (howmuch==2) *s++ = 'a'; *s++ = ':'; if (!cmdlst) cmdlst = savestr("j"); safecpy(s,cmdlst,LBUFLEN-(s-saltbuf)); kf_append(saltbuf); } #endif if (cmdlst && index(cmdlst,'=')) normal_return = SRCH_ERROR; /* listing subjects is an error? */ if (get_cmd) { fputs("\nSearching...\n",stdout); /* give them something to read */ } if (!backward && art > lastart) art = (doread ? absfirst : firstart) - 1; if (srchahead > 0) { if (!backward) art = srchahead - 1; srchahead = -1; } assert(!cmdlst || *cmdlst); for (;;) { if (int_count) { int_count = 0; if (cmdlst) free(cmdlst); return SRCH_INTR; } if (backward ? (--art < absfirst || (!doread && art < firstart)) : (++art > lastart) ) { /* out of articles? */ if (cmdlst) free(cmdlst); return normal_return; } /*NOSTRICT*/ if (doread || !was_read(art)) { if (wanted(compex,art,howmuch)) { /* does the shoe fit? */ if (cmdlst) { if (perform(cmdlst,TRUE)) { if (cmdlst) free(cmdlst); return SRCH_INTR; } } else { if (cmdlst) free(cmdlst); return SRCH_FOUND; } } else if (!cmdlst && ! (art%50)) { printf("...%ld",(long)art); fflush(stdout); } } } } /* determine if article fits pattern */ /* returns TRUE if it exists and fits pattern, FALSE otherwise */ bool wanted(compex, artnum, scope) COMPEX *compex; ART_NUM artnum; char scope; { if (!scope) { char subj_buf[266]; strcpy(subj_buf, "Subject: "); strncpy(subj_buf+9,fetchsubj(artnum,FALSE,FALSE),256); #ifdef DEBUGGING if (debug & DEB_SEARCH_AHEAD) printf("%s\n",subj_buf); #endif return execute(compex,subj_buf) != Nullch; } #ifdef CACHESUBJ else fetchsubj(artnum,FALSE,FALSE);/* might as well get subject handy */ #endif if (artopen(artnum) == Nullfp) /* ensure that article is open */ return FALSE; /* if not, return NO MATCH */ scope--; while (fgets(buf,LBUFLEN,artfp) != Nullch) { /* for each line of article */ if (!scope && index(buf,':') == Nullch && *buf != ' ' && *buf != '\t') /* if headers only and out of header */ return FALSE; /* say no go */ if (execute(compex,buf) != Nullch) { /* does pattern matcher match? */ return TRUE; /* say Eureka */ } } return FALSE; /* out of article, so no match */ } #endif !STUFFY!FUNK! echo Extracting init.c cat >init.c <<'!STUFFY!FUNK!' /* $Header: init.c,v 4.1 84/09/24 11:56:37 lwall Exp $ * * $Log: init.c,v $ * Revision 4.1 84/09/24 11:56:37 lwall * Real baseline. * * Revision 4.0.1.2 84/09/12 17:36:04 lwall * pwd.h now included by common.h. * * Revision 4.0.1.1 84/09/10 15:12:37 lwall * Delinted. * * Revision 4.0 84/09/04 09:50:42 lwall * Baseline for netwide release * */ #include "EXTERN.h" #include "common.h" #include "util.h" #include "final.h" #include "term.h" #include "last.h" #include "rn.h" #include "rcstuff.h" #include "ngdata.h" #include "util.h" #include "only.h" #include "intrp.h" #include "addng.h" #include "sw.h" #include "art.h" #include "artsrch.h" #include "artio.h" #include "backpage.h" #include "bits.h" #include "cheat.h" #include "head.h" #include "help.h" #include "kfile.h" #include "ngsrch.h" #include "ngstuff.h" #include "rcln.h" #include "respond.h" #include "ng.h" #include "INTERN.h" #include "init.h" bool initialize(argc,argv) int argc; char *argv[]; { char *tcbuf; register bool foundany = FALSE; long time(); tcbuf = safemalloc(1024); /* make temp buffer for termcap and */ /* other initialization stuff */ /* decode switches */ sw_init(argc,argv,tcbuf); /* must not do % interps! */ /* (but may mung environment) */ /* start up file expansion and the % interpreter */ intrp_init(tcbuf); /* now make sure we have a current working directory */ cwd_check(); /* now that we know where to save things, cd to news directory */ if (chdir(spool)) { printf(nocd,spool); finalize(1); } /* init signals, status flags and terminal stuff */ final_init(); term_init(tcbuf); /* get info on last rn run, if any */ last_init(tcbuf); free(tcbuf); /* recover 1024 bytes */ /* make sure we are the right version */ version_check(); /* make sure we are the sole possessors of .newsrc */ lock_check(); /* check for news news */ newsnews_check(); /* open active file, etc. */ ngdata_init(); /* now read in the .newsrc file */ foundany = rcstuff_init(); /* it looks like we will actually read something, so init everything */ addng_init(); art_init(); artio_init(); artsrch_init(); backpage_init(); bits_init(); cheat_init(); /* final_init(); already done */ head_init(); help_init(); /* intrp_init(); already done */ kfile_init(); /* last_init(); already done */ ng_init(); /* ngdata_init(); already done */ ngsrch_init(); ngstuff_init(); only_init(); rcln_init(); /* rcstuff_init(); already done */ respond_init(); rn_init(); search_init(); /* sw_init(); already done */ /* term_init(); already done */ util_init(); #ifdef FINDNEWNG fstat(actfp->_file,&filestat); /* did active file grow? */ if (filestat.st_size != lastactsiz) { long actsiz = filestat.st_size; /* remember new size */ NG_NUM oldnext = nextrcline; /* remember # lines in newsrc */ #ifdef FASTNEW bool munged = writesoft || !lastactsiz; /* bad soft ptrs -> edited active */ #else bool munged = TRUE; /* just assume .newsrc munged */ #endif #ifdef VERBOSE IF(verbose) fputs("\nChecking active list for new newsgroups...\n",stdout); ELSE #endif #ifdef TERSE fputs("\nNew newsgroups:\n",stdout); #endif #ifdef FASTNEW if (!munged) { /* maybe just do tail of file? */ fseek(actfp,lastactsiz-1,0); fgets(buf,LBUFLEN,actfp); munged = (*buf != '\n'); if (!munged) munged = newlist(munged,FALSE); } #endif if (munged) { /* must we scan entire file? */ fseek(actfp,0L,0); /* rewind active file */ newlist(munged,FALSE); /* sure hope they use hashing... */ } lastactsiz = actsiz; /* remember for .rnlast */ if (nextrcline != oldnext) { /* did we add any new groups? */ foundany = TRUE; /* let main() know */ starthere = 0; /* and start ng scan from the top */ } } #endif time(&lasttime); /* remember when we inited-- */ /* ends up back in .rnlast */ writelast(); /* in fact, put it there now */ #ifdef FINDNEWNG # ifdef ONLY if (maxngtodo) /* patterns on command line? */ foundany |= scanactive(); # endif #endif return foundany; } /* make sure there is no rn out there already */ void lock_check() { lockname = savestr(filexp(LOCKNAME)); if (!checkflag) { tmpfp = fopen(lockname,"r"); if (tmpfp != Nullfp) { int processnum; fgets(buf,LBUFLEN,tmpfp); fclose(tmpfp); processnum = atoi(buf); #ifdef VERBOSE IF(verbose) printf("You seem to have left an rn running, process %d.\n", processnum); ELSE #endif #ifdef TERSE printf("Rn left running, #%d.\n", processnum); #endif if (kill(processnum, SIGEMT)) { /* does process not exist? */ /* (rn ignores SIGEMT) */ sleep(2); #ifdef VERBOSE IF(verbose) fputs("\n\ That process does not seem to exist anymore. The count of read articles\n\ may be incorrect in the last newsgroup accessed by that other (defunct)\n\ process.\n\n",stdout); ELSE #endif #ifdef TERSE fputs("\nProcess crashed.\n",stdout); #endif if (*lastngname) { #ifdef VERBOSE IF(verbose) printf("(The last newsgroup accessed was %s.)\n\n", lastngname); ELSE #endif #ifdef TERSE printf("(In %s.)\n\n",lastngname); #endif } get_anything(); putchar('\n'); } else { #ifdef VERBOSE IF(verbose) fputs("\n\ You may not have two copies of rn running simultaneously. Goodbye.\n\ ",stdout); ELSE #endif #ifdef TERSE fputs("\nCan't start another.\n",stdout); #endif finalize(0); } } tmpfp = fopen(lockname,"w"); fprintf(tmpfp,"%d\n",getpid()); fclose(tmpfp); } } void newsnews_check() { char *newsnewsname = filexp(NEWSNEWSNAME); if (!checkflag && (tmpfp = fopen(newsnewsname,"r")) != Nullfp) { fstat(tmpfp->_file,&filestat); if (filestat.st_mtime > lasttime) { while (fgets(buf,sizeof(buf),tmpfp) != Nullch) fputs(buf,stdout); get_anything(); putchar('\n'); } fclose(tmpfp); } } void version_check() { set_ngname("net.announce"); if (access(ngdir,0)) { #ifdef VERBOSE IF(verbose) fputs("Can't find net.announce. Wrong news version?\n",stdout); ELSE #endif #ifdef TERSE fputs("Wrong version?\n",stdout); #endif finalize(1); } } !STUFFY!FUNK! echo Extracting head.c cat >head.c <<'!STUFFY!FUNK!' /* $Header: head.c,v 4.1 84/09/24 11:47:10 lwall Exp $ * * $Log: head.c,v $ * Revision 4.1 84/09/24 11:47:10 lwall * Real baseline. * * Revision 4.0.1.1 84/09/10 15:11:08 lwall * Delinted. * * Revision 4.0 84/09/04 09:50:17 lwall * Baseline for netwide release * */ #include "EXTERN.h" #include "common.h" #include "artio.h" #include "bits.h" #include "util.h" #include "INTERN.h" #include "head.h" bool first_one; /* is this the 1st occurance of this header line? */ static char htypeix[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; void head_init() { register int i; for (i=HEAD_FIRST+1; i<HEAD_LAST; i++) htypeix[*htype[i].ht_name - 'a'] = i; } int set_line_type(bufptr,colon) char *bufptr; register char *colon; { char lc[LONGKEY+3]; register char *t, *f; register int i, len; for (t=lc,f=bufptr; f<colon; f++, t++) { if (isspace(*f)) /* guard against space before : */ break; *t = isupper(*f) ? tolower(*f) : *f; } *t = '\0'; f = lc; /* get lc into register */ len = t - f; /* now scan the headtype table, backwards so we don't have to supply an * extra terminating value, using first letter as index, and length as * optimization to avoid calling subroutine strEQ unnecessarily. Hauls. */ if (islower(*f)) { for (i = htypeix[*f - 'a']; *htype[i].ht_name == *f; --i) { if (len == htype[i].ht_length && strEQ(f, htype[i].ht_name)) { return i; } } } return SOME_LINE; } void start_header(artnum) ART_NUM artnum; { register int i; for (i=0; i<HEAD_LAST; i++) { htype[i].ht_minpos = -1; htype[i].ht_maxpos = 0; } in_header = SOME_LINE; first_one = FALSE; #ifdef ASYNC_PARSE parsed_art = artnum; #endif } bool parseline(art_buf,newhide,oldhide) char *art_buf; int newhide, oldhide; { if (*art_buf == ' ' || *art_buf == '\t') /* header continuation line? */ return oldhide; else { /* maybe another header line */ char *s; if (first_one) { /* did we just pass 1st occurance? */ first_one = FALSE; htype[in_header].ht_maxpos = artpos; /* remember where line left off */ } s = index(art_buf,':'); if (s == Nullch || s-art_buf > LONGKEY+2) { /* is it the end of the header? */ htype[PAST_HEADER].ht_minpos = (*art_buf == '\n') ? ftell(artfp) : artpos; /* remember where body starts */ in_header = PAST_HEADER; } else { /* it is a new header line */ in_header = set_line_type(art_buf,s); first_one = (htype[in_header].ht_minpos < 0); if (htype[in_header].ht_minpos < 0) htype[in_header].ht_minpos = artpos; if (htype[in_header].ht_flags & HT_HIDE) return newhide; } } return FALSE; /* don't hide this line */ } #ifdef ASYNC_PARSE int parse_maybe(artnum) ART_NUM artnum; { char tmpbuf[LBUFLEN]; if (parsed_art == artnum) return 0; /* no maybe about it now */ if (artopen(artnum) == Nullfp) { return -1; } start_header(artnum); while (in_header) { artpos = ftell(artfp); if (fgets(tmpbuf,LBUFLEN,artfp) == Nullch) break; parseline(tmpbuf,FALSE,FALSE); } in_header = PAST_HEADER; return 0; } #endif /* get the subject line for an article */ char * fetchsubj(artnum,current_subject,copy) ART_NUM artnum; /* article to get subject from */ bool current_subject; /* is it in a parsed header? */ bool copy; /* do you want it savestr()ed? */ { char *s = Nullch, *t; #ifdef CACHESUBJ if (!subj_list) { register ART_NUM i; /*NOSTRICT*/ subj_list = (char**)safemalloc((MEM_SIZE)((OFFSET(lastart)+2)*sizeof(char *))); for (i=0; i<=OFFSET(lastart); i++) subj_list[i] = Nullch; } if (!artnum || artnum > lastart) s = nullstr; else s = subj_list[OFFSET(artnum)]; #endif if (s == Nullch) { if (current_subject) { s = fetchlines(artnum,SUBJ_LINE); #ifdef CACHESUBJ subj_list[OFFSET(artnum)] = s; #endif } else { s = safemalloc((MEM_SIZE)256); *s = '\0'; if (artopen(artnum) != Nullfp) { do { if (fgets(s,256,artfp) == Nullch) strcpy(s, "Title: \n"); } while (strnNE(s,"Title:",6) && strnNE(s,"Subject:",8)); s[strlen(s)-1] = '\0'; t = index(s,':')+1; while (*t == ' ') t++; strcpy(s, t); } s = saferealloc(s, (MEM_SIZE)strlen(s)+1); #ifdef CACHESUBJ subj_list[OFFSET(artnum)] = s; #endif } } #ifdef CACHESUBJ if (copy) { t = savestr(s); return t; } else return s; #else if (copy) return s; else { safecpy(cmd_buf,s,CBUFLEN); /* hope this is okay--we're */ return cmd_buf; /* really scraping for space here */ } #endif } /* get header lines from an article */ char * fetchlines(artnum,which_line) ART_NUM artnum; /* article to get line from */ int which_line; /* type of line desired */ { char *newbuf, *t, tmp_buf[LBUFLEN]; register ART_POS curpos; int size; register ART_POS firstpos; register ART_POS lastpos; #ifdef ASYNC_PARSE if (parse_maybe(artnum)) artnum = 0; #endif firstpos = htype[which_line].ht_minpos; lastpos = htype[which_line].ht_maxpos; if (!artnum || firstpos < 0 || artopen(artnum) == Nullfp) { newbuf = safemalloc((unsigned int)1); *newbuf = '\0'; return newbuf; } /*NOSTRICT*/ size = lastpos - firstpos + 1; #ifdef DEBUGGING if (debug && (size < 1 || size > 1000)) { printf("Firstpos = %ld, lastpos = %ld\n",(long)firstpos,(long)lastpos); gets(tmp_buf); } #endif newbuf = safemalloc((unsigned int)size); *newbuf = '\0'; fseek(artfp,firstpos,0); for (curpos = firstpos; curpos < lastpos; curpos = ftell(artfp)) { if (fgets(tmp_buf,LBUFLEN,artfp) == Nullch) break; if (*tmp_buf == ' ' || *tmp_buf == '\t') t = tmp_buf; else t = index(tmp_buf,':')+1; if (t == Nullch) break; else { while (*t == ' ' || *t == '\t') t++; safecat(newbuf,t,size); } } return newbuf; } !STUFFY!FUNK! echo Extracting ngstuff.c cat >ngstuff.c <<'!STUFFY!FUNK!' /* $Header: ngstuff.c,v 4.1 84/09/24 12:03:20 lwall Exp $ * * $Log: ngstuff.c,v $ * Revision 4.1 84/09/24 12:03:20 lwall * Real baseline. * * Revision 4.0.1.3 84/09/22 17:03:54 lwall * escapade() was not chdiring back. * * Revision 4.0.1.2 84/09/19 17:12:10 lwall * Ifdef'ed some stuff that should have been. * * Revision 4.0.1.1 84/09/10 15:21:21 lwall * Delinted. * * Revision 4.0 84/09/04 09:51:51 lwall * Baseline for netwide release * */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "util.h" #include "ng.h" #include "bits.h" #include "intrp.h" #include "cheat.h" #include "head.h" #include "final.h" #include "sw.h" #include "INTERN.h" #include "ngstuff.h" void ngstuff_init() { ; } /* do a shell escape */ int escapade() { register char *s; bool interactive = (buf[1] == FINISHCMD); bool docd; char whereiam[256]; if (!finish_command(interactive)) /* get remainder of command */ return -1; s = buf+1; docd = *s != '!'; if (!docd) { s++; } else { getwd(whereiam); if (chdir(cwd)) { printf(nocd,cwd); sig_catcher(0); } } while (*s == ' ') s++; /* skip leading spaces */ interp(cmd_buf,s); /* interpret any % escapes */ resetty(); /* make sure tty is friendly */ doshell(Nullch,cmd_buf); /* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ if (docd) { if (chdir(whereiam)) { printf(nocd,whereiam); sig_catcher(0); } } #ifdef MAILCALL; mailcount = 0; /* force recheck */ #endif return 0; } /* process & command */ int switcheroo() { if (!finish_command(TRUE)) /* get rest of command */ return -1; /* if rubbed out, try something else */ if (!buf[1]) pr_switches(); else { bool docd = (instr(buf,"-d") != Nullch); char whereami[256]; if (docd) getwd(whereami); sw_list(buf+1); if (docd) { cwd_check(); if (chdir(whereami)) { /* -d does chdirs */ printf(nocd,whereami); sig_catcher(0); } } } return 0; } /* process range commands */ int numnum() { ART_NUM min, max; char *cmdlst = Nullch; register char *s, *c; ART_NUM oldart = art; char tmpbuf[LBUFLEN]; bool justone = TRUE; /* assume only one article */ if (!finish_command(TRUE)) /* get rest of command */ return NN_INP; #ifdef ARTSRCH if (srchahead) srchahead = -1; #endif for (s=buf; *s && (isdigit(*s) || index(" ,-.$",*s)); s++) if (!isdigit(*s)) justone = FALSE; if (*s) { cmdlst = savestr(s); justone = FALSE; } else if (!justone) cmdlst = savestr("m"); *s++ = ','; *s = '\0'; safecpy(tmpbuf,buf,LBUFLEN); for (s = tmpbuf; c = index(s,','); s = ++c) { *c = '\0'; if (*s == '.') min = oldart; else min = atol(s); if (min<absfirst) { /* make sure it is reasonable */ min = absfirst; printf("(First article is %ld)\n",(long)absfirst); pad(just_a_sec/3); } if ((s=index(s,'-')) != Nullch) { s++; if (*s == '$') max = lastart; else if (*s == '.') max = oldart; else max = atol(s); } else max = min; if (max>lastart) { max = lastart; if (min > max) min = max; printf("(Last article is %ld)\n",(long)lastart); pad(just_a_sec/3); } if (max < min) { fputs("\nBad range\n",stdout); if (cmdlst) free(cmdlst); return NN_ASK; } if (justone) { art = min; return NN_REREAD; } check_first(min); for (art=min; art<=max; art++) { if (perform(cmdlst,TRUE)) { #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art); ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art); #endif if (cmdlst) free(cmdlst); return NN_ASK; } } } art = oldart; if (cmdlst) free(cmdlst); return NN_NORM; } int perform(cmdlst,toplevel) register char *cmdlst; int toplevel; { register int ch; if (toplevel) { printf("%-6ld",art); fflush(stdout); } for (; ch = *cmdlst; cmdlst++) { if (isspace(ch) || ch == ':') continue; if (ch == 'j') { mark_as_read(art); #ifdef VERBOSE IF(verbose) fputs("\tJunked",stdout); #endif } else if (ch == 'm') { unmark_as_read(art); #ifdef VERBOSE IF(verbose) fputs("\tMarked unread",stdout); #endif } else if (ch == 'M') { #ifdef DELAYMARK delay_unmark(art); #ifdef VERBOSE IF(verbose) fputs("\tWill return",stdout); #endif #else notincl("M"); return -1; #endif } else if (ch == '=') { printf("\t%s",fetchsubj(art,FALSE,FALSE)); #ifdef VERBOSE IF(verbose) ; ELSE #endif putchar('\n'); /* ghad! */ } else if (ch == 'C') { #ifdef ASYNC_PARSE printf("\t%sancelled",(cancel_article() ? "Not c" : "C")); #else notincl("C"); return -1; #endif } else if (ch == '%') { #ifdef ASYNC_PARSE char tmpbuf[512]; cmdlst = dointerp(tmpbuf,cmdlst,":"); if (*cmdlst != ':') --cmdlst; if (perform(tmpbuf,FALSE)) return -1; #else notincl("%"); return -1; #endif } else if (index("!&sSwW|",ch)) { cmdlst = cpytill(buf,cmdlst,':') - 1; /* we now have the command in buf */ if (ch == '!') { escapade(); #ifdef VERBOSE IF(verbose) fputs("\tShell escaped",stdout); #endif } else if (ch == '&') { switcheroo(); #ifdef VERBOSE IF(verbose) fputs("\tSwitched",stdout); #endif } else { putchar('\t'); save_article(); } } else { printf("\t???%s\n",cmdlst); return -1; } #ifdef VERBOSE fflush(stdout); #endif } if (toplevel) { #ifdef VERBOSE IF(verbose) putchar('\n'); ELSE #endif #ifdef TERSE putchar(' '); #endif } return 0; } !STUFFY!FUNK! echo Extracting kfile.c cat >kfile.c <<'!STUFFY!FUNK!' /* $Header: kfile.c,v 4.1 84/09/24 11:58:18 lwall Exp $ * * $Log: kfile.c,v $ * Revision 4.1 84/09/24 11:58:18 lwall * Real baseline. * * Revision 4.0.1.6 84/09/24 10:42:22 lwall * mention() did not terminate highlighting correctly. * * Revision 4.0.1.5 84/09/14 08:54:53 lwall * kf_append did not append a newline. * * Revision 4.0.1.4 84/09/13 12:04:57 lwall * UNLINK for Eunice. * * Revision 4.0.1.3 84/09/10 15:15:58 lwall * Delinted. * * Revision 4.0.1.2 84/09/06 17:02:30 lwall * Implemented newsgroup exit commands. * * Revision 4.0.1.1 84/09/04 12:15:59 lwall * Allow switch setting (via &) in KILL files. * * Revision 4.0 84/09/04 09:50:56 lwall * Baseline for netwide release * */ #include "EXTERN.h" #include "common.h" #include "term.h" #include "util.h" #include "artsrch.h" #include "ng.h" #include "bits.h" #include "intrp.h" #include "ngstuff.h" #include "INTERN.h" #include "kfile.h" static bool exitcmds = FALSE; void kfile_init() { ; } #ifndef KILLFILES int edit_kfile() { notincl("^K"); return -1; } #else KILLFILES char killglobal[] = KILLGLOBAL; char killlocal[] = KILLLOCAL; void mention(str) char *str; { #ifdef NOFIREWORKS no_sofire(); #endif standout(); fputs(str,stdout); un_standout(); putchar('\n'); fflush(stdout); } int do_kfile(kfp,entering) FILE *kfp; int entering; { art = lastart+1; fseek(kfp,0L,0); /* rewind file */ while (fgets(buf,LBUFLEN,kfp) != Nullch) { buf[strlen(buf)-1] = '\0'; if (strnEQ(buf,"THRU",4)) { firstart = atol(buf+4)+1; continue; } if (*buf == 'X') { /* exit command? */ if (entering) { exitcmds = TRUE; continue; } strcpy(buf,buf+1); } else { if (!entering) continue; } if (*buf == '&') { mention(buf); switcheroo(); } else if (*buf == '/' && firstart <= lastart) { mention(buf); switch (art_search(buf,FALSE)) { case SRCH_ABORT: continue; case SRCH_INTR: #ifdef VERBOSE IF(verbose) printf("\n(Interrupted at article %ld)\n",(long)art); ELSE #endif #ifdef TERSE printf("\n(Intr at %ld)\n",(long)art); #endif return -1; case SRCH_DONE: break; case SRCH_SUBJDONE: fputs("\tsubject not found (???)\n",stdout); break; case SRCH_NOTFOUND: fputs("\tnot found\n",stdout); break; case SRCH_FOUND: fputs("\tfound\n",stdout); } } } return 0; } void kill_unwanted(starting,message,entering) ART_NUM starting; char *message; int entering; { bool intr = FALSE; /* did we get an interrupt? */ ART_NUM oldfirst; if (localkfp || globkfp) { if (!entering && !exitcmds) return; exitcmds = FALSE; oldfirst = firstart; firstart = starting; clear(); if (message) fputs(message,stdout); if (localkfp) intr = do_kfile(localkfp,entering); if (globkfp && !intr) intr = do_kfile(globkfp,entering); if (entering && localkfp && !intr) setthru(lastart); putchar('\n'); if (entering) get_anything(); forcelast = FALSE; firstart = oldfirst; } } void setthru(thru) ART_NUM thru; { FILE *newkfp; fseek(localkfp,0L,0); /* rewind current file */ strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); UNLINK(buf); /* to prevent file reuse */ if (newkfp = fopen(buf,"w")) { fprintf(newkfp,"THRU %ld\n",(long)thru); while (fgets(buf,LBUFLEN,localkfp) != Nullch) { if (strnEQ(buf,"THRU",4)) continue; fputs(buf,newkfp); } fclose(newkfp); open_kfile(KF_LOCAL); /* and reopen local file */ } } /* edit KILL file for newsgroup */ int edit_kfile() { int r = -1; if (in_ng) strcpy(buf,filexp(getval("KILLLOCAL",killlocal))); else strcpy(buf,filexp(getval("KILLGLOBAL",killglobal))); if ((r = makedir(buf,MD_FILE)) >= 0) { sprintf(cmd_buf,"%s %s", filexp(getval("VISUAL",getval("EDITOR",defeditor))),buf); printf("\nEditing %s KILL file:\n%s\n", (in_ng?"local":"global"),cmd_buf); resetty(); /* make sure tty is friendly */ r = doshell(sh,cmd_buf);/* invoke the shell */ noecho(); /* and make terminal */ crmode(); /* unfriendly again */ open_kfile(in_ng); } else printf("Can't make %s\n",buf); return r; } void open_kfile(local) int local; { char *kname = filexp(local ? getval("KILLLOCAL",killlocal) : getval("KILLGLOBAL",killglobal) ); stat(kname,&filestat); if (!filestat.st_size) /* nothing in the file? */ UNLINK(kname); /* delete the file */ if (local) { if (localkfp) fclose(localkfp); localkfp = fopen(kname,"r"); } else { if (globkfp) fclose(globkfp); globkfp = fopen(kname,"r"); } } void kf_append(cmd) char *cmd; { strcpy(cmd_buf,filexp(getval("KILLLOCAL",killlocal))); if (makedir(cmd_buf,MD_FILE) >= 0) { #ifdef VERBOSE IF(verbose) printf("\nDepositing command in %s...",cmd_buf); ELSE #endif #ifdef TERSE printf("\n--> %s...",cmd_buf); #endif fflush(stdout); sleep(2); if ((tmpfp = fopen(cmd_buf,"a")) != Nullfp) { fseek(tmpfp,0L,2); /* get to EOF for sure */ fprintf(tmpfp,"%s\n",cmd); fclose(tmpfp); fputs("done\n",stdout); } else fputs("ERROR\n",stdout); } } #endif KILLFILES !STUFFY!FUNK! echo Extracting Rnmail.SH cat >Rnmail.SH <<'!STUFFY!FUNK!' case $CONFIG in '') . config.sh ;; esac echo "Extracting Rnmail (with variable substitutions)" $spitshell >Rnmail <<!GROK!THIS! #! /bin/sh # $Header: Rnmail.SH,v 4.1 84/09/24 11:38:51 lwall Exp $ # # $Log: Rnmail.SH,v $ # Revision 4.1 84/09/24 11:38:51 lwall # Real baseline. # # Revision 4.0.1.2 84/09/12 15:18:54 lwall # Check for sh interpretation. # # Revision 4.0.1.1 84/09/04 10:56:18 lwall # Added possibility of using /usr/lib/news/recmail. # # Revision 4.0 84/09/04 09:49:08 lwall # Baseline for netwide release # # # syntax: Rnmail -h headerfile [oldart] or # Rnmail destination-list or just # Rnmail 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 # your site name sitename="$sitename" # your organization name orgname="$orgname" # what pager you use--if you have kernal paging use cat pager="$pager" # how you derive full names, bsd, usg, or other nametype="$nametype" # default editor defeditor="$defeditor" # how not to do a newline with echo n="$n" c="$c" test=${test-test} sed=${sed-sed} echo=${echo-echo} cat=${cat-cat} grep=${grep-grep} rm=${rm-rm} !GROK!THIS! $spitshell >>Rnmail <<'!NO!SUBS!' tmpart=/tmp/rnmail$$ dotdir=${DOTDIR-${HOME-$LOGDIR}} headerfile="" case $# in 0) ;; *) case $1 in -h) headerfile="$2" case $# in 3) oldart=$3 ;; esac ;; esac ;; esac case $headerfile in '') case $# in 0) to=h while $test "$to" = h ; do $echo "" $echo -n "To: " read to case $to in h) $cat <<'EOH' Type the net address of those people that you wish the message sent to. Note that you will be asked later for additional addresses of people to whom the message is not addressed but you wish to get copies. Separate multiple addresses with spaces. EOH ;; esac done ;; *) to="$*" ;; esac to=`$echo "$to" | $sed 's/ */ /g'` title=h while $test "$title" = h ; do $echo "" $echo -n "Title/Subject: " read title case $title in h) $cat <<'EOH' Type the title for your message. EOH ;; esac done # now build a file with a header for them to edit orgname=${ORGANIZATION-$orgname} case $orgname in /*) orgname=`$cat $orgname` ;; esac $cat > $tmpart <<EOHeader To: $to Subject: $title Organization: $orgname Cc: Bcc: EOHeader ;; *) $cat < $headerfile > $tmpart ;; esac file=h while $test "$file" = h ; do $echo "" $echo -n "Input file name? [RETURN for new file]: " read file case $file in h) $cat <<'EOH' If you have already produced the body of your message, 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 message. EOH ;; '') $echo "" >> $tmpart state=edit ;; *) $cat $file >>$tmpart state=ask ;; esac done $echo "" while true ; do case $state in edit) rescue="sleep 1; $cat $tmpart >>${HOME-$LOGDIR}/dead.letter ; $echo saved in ${HOME-$LOGDIR}/dead.letter ; $rm -f $tmpart; exit" trap "$rescue" 1 trap "" 2 tmp=h while $test "$tmp" = h ; do $echo -n "Editor [${VISUAL-${EDITOR-$defeditor}}]: " 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 ${VISUAL-${EDITOR-$defeditor}} $tmpart $oldart trap "$rescue" 2 state=ask ;; ask) $echo "" $echo -n "Send, abort, edit, or list? " 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 message, a to abort and append the message to dead.letter, e to edit the message again, or l to list the message. EOH esac ;; send) if $test -f $dotdir/.signature; then echo $n "Append .signature file? [y] $c" read ans case $ans in ''|y*) cat $dotdir/.signature >> $tmpart esac fi case $mailer in *sendmail) $mailer -t <$tmpart ;; # but recmail does not know about Bcc, alas *recmail) $mailer <$tmpart ;; *) set `$sed <$tmpart -n -e '/^To:/{' -e 's/To: *//p' -e q -e '}'` set "$@" `$sed <$tmpart -n -e '/^Cc:/{' -e 's/Cc: *//p' -e q -e '}'` set "$@" `$sed <$tmpart -n -e '/^Bcc:/{' -e 's/Bcc: *//p' -e q -e '}'` $grep -v "^Bcc:" <$tmpart | $mailer "$@" ;; esac case $? in 0) state=cleanup ;; *) state=rescue ;; esac ;; rescue) $cat $tmpart >> ${HOME-$LOGDIR}/dead.letter $echo "Message saved to ${HOME-$LOGDIR}/dead.letter" state=cleanup ;; cleanup) $rm -f $tmpart exit ;; esac done !NO!SUBS! $eunicefix Rnmail chmod 755 Rnmail !STUFFY!FUNK! echo Extracting README cat >README <<'!STUFFY!FUNK!' Rn Kit, Version 4.1 Copyright (c) 1984, Larry Wall You may copy the rn kit as long as you aren't trying to make money off it. -------------------------------------------------------------------------- (Note: Berkeleyphiles and -phobes: The 4.1 above is unrelated to 4.1bsd.) Please read all the directions below before you proceed any further, and then follow them carefully. Failure to do so may void your warranty. :-) After you have unpacked your kit, you should have all the files listed in MANIFEST. Installation 1) Run Configure. This will figure out various things about your system. Some things Configure will figure out for itself, other things it will ask you about. It will then procede to make config.h, config.sh, Makefile, and a bunch of shell scripts. It will also do a make depend for you. You might possibly have to trim # comments from the front of Configure if your sh doesn't handle them, but all other # comments will be taken care of. 2) Glance through config.h and common.h to make sure system dependencies are correct. Most of them should have been taken care of by running the Configure script. If you have any additional changes to make to the C definitions, they can be done in the Makefile, in config.h, or in common.h. If you have strange mailboxes on your system you should modify mbox.saver to correctly append an article to a mailbox or folder. If you are on a machine with limited address space, you may have to remove some of the special functions of rn to make it fit. This is easily done by undefining symbols in the System Dependencies section of common.h. You should run "make depend" again to be safe. 3) make This will attempt to make rn in the current directory. 4) make install This will put rn, newsetup, newsgroups, Pnews, and Rnmail into a public directory (normally /usr/local/bin), and put a number of files into the private rn library (normally /usr/lib/news/rn). It will also try to put the man pages in a reasonable place. 5) Read the manual entry before running rn. It's quite different from readnews. 6) Install the Xref patch to header.h, header.c, and inews.c. These patches are found in header.h.pat, header.c.pat, and inews.c.?.pat, where ? is either 1 for news 2.10.1 (or earlier) or 2 for 2.10.2. The purpose of the Xref patch is to put an Xref: line in the header of articles posted to more than one newsgroup. Rn uses this line to keep from showing such postings more than once. Other than that rn will work without this patch, so if you just want to try out rn you can delay putting in the patch. When you put in the patch and recompile inews, don't forget to define DOXREFS in the makefile for inews. Do NOT define LINKART unless you are a Eunice site and really want to do that. In fact, if you are not a Eunice site you needn't install the LINKART part of the patch. NOTE: the Makefile that comes with some of the older news systems does not have all the dependencies quite right. In particular, ifuncs.c may not recompile when you change header.h. If this happens when you install the Xref patch, inews will start dumping core. Make sure both inews.o and ifuncs.o depend on header.h in the Makefile. Eunice users: the inews.c patch contains both the Xref patch and a LINKART patch to put a form of "symbolic link" between articles posted to multiple newsgroups. What it does is to put the article into the first newsgroup on the Newsgroups line, and in subsequent newsgroups it just puts a little file containing the name of the article in the first newsgroup. Rn (when compiled with the LINKART option) is clever about these pseudo-articles, and ends up opening the right one. YOU WILL NOT be able to use readnews or vnews on your system without modification, though. If you do this, be sure to define both DOXREFS and LINKART in the makefile for inews.c. If you are using the option in inews that copies instead of linking, you will want to rip that out. NOTE: if you transmit articles to other systems using xfernews with the U flag, Xref's can leak out of your system, as can Date-Received's. This may make neighboring sites unhappy unless they also have the Xref patch installed. For now, either don't use the U flag, or fix the inews/xfernews interaction. 7) Try rn, and play with some of the switches. You may want to make -/ default on your system. This is done in common.h. You may want to modify which header lines are displayed by default--this is done in head.h. 8) IMPORTANT! Help save the world! Communicate any problems and suggested patches to me, lwall@sdcrdcf.UUCP (Larry Wall), so we can keep the world in sync. If you have a problem, there's someone else out there who either has had or will have the same problem. 9) If you are going to hack on rn, please read the HACKERSGUIDE first. !STUFFY!FUNK! echo Extracting term.h cat >term.h <<'!STUFFY!FUNK!' /* $Header: term.h,v 4.1 84/09/24 12:11:06 lwall Exp $ * * $Log: term.h,v $ * Revision 4.1 84/09/24 12:11:06 lwall * Real baseline. * * Revision 4.0.1.2 84/09/12 17:21:34 lwall * Little TERMIO bug. * * Revision 4.0.1.1 84/09/10 15:34:07 lwall * Delinted. * * Revision 4.0 84/09/04 09:52:57 lwall * Baseline for netwide release * */ #ifdef PENDING #ifdef FIONREAD /* must have FIONREAD or O_NDELAY for input_pending() */ #define read_tty(addr,size) read(0,addr,size) #define input_pending() (ioctl(0, FIONREAD, &iocount),(int)iocount) EXT long iocount INIT(0); #else #include <fcntl.h> EXT int devtty INIT(0); EXT bool is_input INIT(FALSE); EXT char pending_ch INIT(0); #define input_pending() (is_input?TRUE:(is_input=read(devtty,&pending_ch,1))) #endif #else #define read_tty(addr,size) read(0,addr,size) #define input_pending() (FALSE) #endif /* stuff wanted by terminal mode diddling routines */ #ifdef TERMIO EXT struct termio _tty, _oldtty; #else EXT struct sgttyb _tty; EXT int _res_flg INIT(0); #endif EXT int _tty_ch INIT(2); EXT bool bizarre INIT(FALSE); /* do we need to restore terminal? */ /* terminal mode diddling routines */ #ifdef TERMIO #define crmode() ((bizarre=1),_tty.c_lflag &=~ICANON,_tty.c_cc[VMIN] = 1,ioctl(_tty_ch,TCSETAF,&_tty)) #define nocrmode() ((bizarre=1),_tty.c_lflag |= ICANON,_tty.c_cc[VEOF] = CEOF,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.c_lflag |= ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define noecho() ((bizarre=1),_tty.c_lflag &=~ECHO, ioctl(_tty_ch, TCSETA, &_tty)) #define nl() ((bizarre=1),_tty.c_iflag |= ICRNL,_tty.c_oflag |= ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define nonl() ((bizarre=1),_tty.c_iflag &=~ICRNL,_tty.c_oflag &=~ONLCR,ioctl(_tty_ch, TCSETAW, &_tty)) #define savetty() ((bizarre=1),ioctl(_tty_ch, TCGETA, &_oldtty),ioctl(_tty_ch, TCGETA, &_tty)) #define resetty() ((bizarre=0),ioctl(_tty_ch, TCSETAF, &_oldtty)) #else #define raw() ((bizarre=1),_tty.sg_flags|=RAW, stty(_tty_ch,&_tty)) #define noraw() ((bizarre=1),_tty.sg_flags&=~RAW,stty(_tty_ch,&_tty)) #define crmode() ((bizarre=1),_tty.sg_flags |= CBREAK, stty(_tty_ch,&_tty)) #define nocrmode() ((bizarre=1),_tty.sg_flags &= ~CBREAK,stty(_tty_ch,&_tty)) #define echo() ((bizarre=1),_tty.sg_flags |= ECHO, stty(_tty_ch, &_tty)) #define noecho() ((bizarre=1),_tty.sg_flags &= ~ECHO, stty(_tty_ch, &_tty)) #define nl() ((bizarre=1),_tty.sg_flags |= CRMOD,stty(_tty_ch, &_tty)) #define nonl() ((bizarre=1),_tty.sg_flags &= ~CRMOD, stty(_tty_ch, &_tty)) #define savetty() ((bizarre=1),gtty(_tty_ch, &_tty), _res_flg = _tty.sg_flags) #define resetty() ((bizarre=0),_tty.sg_flags = _res_flg, stty(_tty_ch, &_tty)) #endif #ifdef TIOCSTI #define forceme(c) ioctl(_tty_ch,TIOCSTI,c) /* pass character in " " */ #else #define forceme(c) #endif /* termcap stuff */ /* * NOTE: if you don't have termlib you'll either have to define these strings * and the tputs routine, or you'll have to redefine the macros below */ #ifdef HAVETERMLIB EXT char *BC INIT(Nullch); /* backspace character */ EXT char *UP INIT(Nullch); /* move cursor up one line */ EXT char *CL INIT(Nullch); /* home and clear screen */ EXT char *CE INIT(Nullch); /* clear to end of line */ EXT char *SO INIT(Nullch); /* begin standout mode */ EXT char *SE INIT(Nullch); /* end standout mode */ EXT int SG INIT(0); /* blanks left by SO and SE */ EXT char *US INIT(Nullch); /* start underline mode */ EXT char *UE INIT(Nullch); /* end underline mode */ EXT char *UC INIT(Nullch); /* underline a character, if that's how it's done */ EXT int UG INIT(0); /* blanks left by US and UE */ EXT bool AM INIT(FALSE); /* does terminal have automatic margins? */ EXT bool XN INIT(FALSE); /* does it eat 1st newline after automatic wrap? */ EXT char PC INIT(0); /* pad character for use by tputs() */ EXT short ospeed INIT(0); /* terminal output speed, for use by tputs() */ EXT int LINES INIT(0), COLS INIT(0); /* size of screen */ EXT int just_a_sec INIT(960); /* 1 sec at current baud rate */ /* (number of nulls) */ /* define a few handy macros */ #define backspace() tputs(BC,0,putchr) #define clear() tputs(CL,LINES,putchr) #define erase_eol() tputs(CE,1,putchr) #define underline() tputs(US,1,putchr) #define un_underline() tputs(UE,1,putchr) #define underchar() tputs(UC,0,putchr) #define standout() tputs(SO,1,putchr) #define un_standout() tputs(SE,1,putchr) #define up_line() tputs(UP,1,putchr) #else ???????? /* up to you */ #endif EXT int page_line INIT(1); /* line number for paging in print_line (origin 1) */ void term_init(); char putchr(); /* routine for tputs to call */ bool finish_command(); void eat_typeahead(); #ifndef read_tty int read_tty(); #endif void underprint(); #ifdef NOFIREWORKS void no_sofire(); void no_ulfire(); #endif void getcmd(); int get_anything(); void in_char(); int print_lines(); void pad(); void printcmd(); void rubout(); void reprint(); !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. * 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.1.[2-9]. (I will use 4.1.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 Extracting Makefile.SH cat >Makefile.SH <<'!STUFFY!FUNK!' case $CONFIG in '') . config.sh ;; esac echo "Extracting Makefile (with variable substitutions)" cat >Makefile <<!GROK!THIS! # $Header: Makefile.SH,v 4.1 84/09/24 11:37:35 lwall Exp $ # # $Log: Makefile.SH,v $ # Revision 4.1 84/09/24 11:37:35 lwall # Real baseline. # # Revision 4.0.1.4 84/09/12 16:57:01 lwall # Added ndir. # # Revision 4.0.1.3 84/09/12 15:16:38 lwall # Check for sh on install. # # Revision 4.0.1.2 84/09/06 14:01:35 lwall # Made not install newsnews over existent one. # # Revision 4.0.1.1 84/09/06 13:15:19 lwall # General cleanup. # # Revision 4.0 84/09/04 09:48:44 lwall # Baseline for netwide release # rnbin = $rnbin rnlib = $rnlib mansrc = $mansrc CFLAGS = $iandd -O LDFLAGS = $iandd libs = $ndirlib $termlib $jobslib !GROK!THIS! cat >>Makefile <<'!NO!SUBS!' public = rn newsetup newsgroups Pnews Rnmail private = norm.saver mbox.saver ng.help art.help pager.help subs.help makedir filexp h1 = addng.h art.h artio.h artsrch.h backpage.h bits.h cheat.h common.h h2 = final.h head.h help.h init.h intrp.h kfile.h last.h ndir.h ng.h h3 = ngdata.h ngsrch.h ngstuff.h only.h rcln.h rcstuff.h h4 = respond.h rn.h search.h sw.h term.h util.h h = $(h1) $(h2) $(h3) $(h4) c1 = addng.c art.c artio.c artsrch.c backpage.c bits.c cheat.c c2 = final.c head.c help.c init.c intrp.c kfile.c last.c ndir.c ng.c c3 = ngdata.c ngsrch.c ngstuff.c only.c rcln.c rcstuff.c c4 = respond.c rn.c search.c sw.c term.c util.c c = $(c1) $(c2) $(c3) $(c4) obj1 = addng.o art.o artio.o artsrch.o backpage.o bits.o cheat.o obj2 = final.o head.o help.o init.o intrp.o kfile.o last.o ndir.o ng.o obj3 = ngdata.o ngsrch.o ngstuff.o only.o rcln.o rcstuff.o obj4 = respond.o rn.o search.o sw.o term.o util.o obj = $(obj1) $(obj2) $(obj3) $(obj4) lintflags = -phbvxac .c.o: $(CC) -c $(CFLAGS) $*.c rn: $(obj) $(CC) $(LDFLAGS) $(obj) $(libs) -o rn # if a .h file depends on another .h file... $(h): touch $@ install: rn # won't work with csh export PATH || exit 1 - mv $(rnbin)/rn $(rnbin)/rn.old - if test `pwd` != $(rnbin); then cp $(public) $(rnbin); fi cd $(rnbin); chmod 755 $(public) chmod 755 makedir - makedir `filexp $(rnlib)` - if test `pwd` != `filexp $(rnlib)`; then cp $(private) `filexp $(rnlib)`; fi cd `filexp $(rnlib)`; chmod 755 $(private) - if test ! -f `filexp $(rnlib)/newsnews`; then cp newsnews `filexp $(rnlib)`; fi - if test `pwd` != $(mansrc); then cp $(manpages) $(mansrc); fi # The following lint has practically everything turned on. Unfortunately, # you have to wade through a lot of mumbo jumbo that can't be suppressed. # If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message # for that spot. lint: lint $(lintflags) $(defs) $(c) > rn.fuzz depend: makedepend # AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE $(obj): @ echo "You haven't done a "'"make depend" yet!'; exit 1 !NO!SUBS! $eunicefix Makefile !STUFFY!FUNK! echo "" echo "End of kit 6 (of 8)" cat /dev/null >kit6isdone config=true for iskit in 1 2 3 4 5 6 7 8; 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