discolo@ucsbcsl.UUCP (Anthony V. Discolo) (05/04/85)
Here is the source for my rn, and tips on installing it. (1) rn.h contains local pathnames, and misc2.c contains the pathname for inews; look at these files before compiling. (2) pager.diffs contains diffs to 4.2BSD more source to turn it into a news paginator which has a nice header with article id, author, and subject at the top of every page. If you don't have the source or are running another flavor of UNIX, then you can just define PAGER in rn.h to be /usr/ucb/more. If you do have the 4.2BSD more source, then define PAGER to be the modified more. If you don't end up using the news paginator, you will have to comment out the following lines: digest.c: 135-139 misc2.c: 178-182, 211-215 which are of the form if (strcmp(pager, PAGER) == 0) sprintf(..) else because the code was designed around PAGER having a header option. NOTE: rn has been tested on a 4.2BSD system with all the terminals that we have here (Wyse, Freedom, BitGraph, Z19, Tektronix), and uses only the termlib library. I don't know how it will work on other terminals, or other flavors of UNIX. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CUT HERE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # This is a shar archive, extract by # % sh archive echo x - Disclaimer cat << ! > Disclaimer Author: Anthony V. Discolo Computer Systems Lab University of California Santa Barbara, CA 93106 (805) 961-4178 Disclaimer: I take no responsibility whatsoever for the condition of this software. Unrestricted use is hereby granted as long as this header remains intact. ! echo x - Makefile cat << ! > Makefile CFLAGS = -O SRCS = active.c articles.c digest.c inotab.c misc1.c misc2.c tty.c OBJS = active.o articles.o digest.o inotab.o misc1.o misc2.o tty.o all: rn rn: rn.o \${OBJS} cc \${CFLAGS} -o rn rn.o \${OBJS} -ltermlib checknews: checknews.o active.o misc1.o misc2.o tty.o cc \${CFLAGS} -o checknews checknews.o active.o misc1.o misc2.o tty.o -ltermlib convrc: convrc.o cc -O -o convrc convrc.o lint: lint rn.c \${SRCS} ctags: ctags rn.c \${SRCS} clean: rm -f *.o ! echo x - Readme cat << ! > Readme /staff/csl/discolo/src/cmd/rn/Readme: March 25, 1985 April 9, 1985 April 21, 1985 Disclaimer: I take no responsibility whatsoever for the condition of this software. Unrestricted use is hereby granted as long as this header remains intact. This is a new version of readnews. I think it has most of the useful features of the old one. What the new version of readnews gives you is a way to quickly scan the articles to determine which ones you want to read. This is done by grouping the articles of a single newsgroup together with author and subject, and also being able to preview the article[1]. I think the previewing feature is a very nice one, and will help you weed out uninteresting articles. If there are more articles than can fit on a screen, then the articles are split into multiple screens, and you are able to go back and forth between them, selecting the articles you want to read. Articles are gathered one screen at a time, so even reading a newsgroup from the beginning does not take a long initialization period[2]. This is the first phase. After you have selected the articles you want to read, you signal the program by a command, and it goes into the second phase of reading news, which presents the articles that you've selected through a news paginator. After you read each article, you can save it, reply or followup, etc., just like the old readnews. You can go back and forth between the two phases for any newsgroup; you can go back and forth between newsgroups. If you have already read an article, and are displaying the articles a second time, you will see which articles have been previously read. Another personal design issue I have engineered into this version of readnews is that the .newsrc file contains only the last article id read, and cannot contain \`\`holes'' in the list for unread articles. Also, the .newsrc can contain unsubscribed newsgroups (via the '!' operator), but there is not an options line. Furthermore, this readnews will not pick up new newsgroups automatically, but usually you will find out about new newsgroups through existing newsgroups anyway[3]. The bottom line here is that my version is not compatible with the older versions, and that you will have to edit your .newsrc to use my version. The format of the new version of .newsrc is <newsgroup>[:!] <last articleid> Enough introduction, here is how you actually use my readnews. The usage is \`\`rn [ -Ppx ] [ -v lines ] [ -n ng1 ng2 ... ]'', where the meaning for the options are P: Turn off automatic previewing p: Turn on automatic previewing x: Don't listen to .newsrc v: Define the number of lines in the preview area. n: Specify newsgroups to be read By default, the previewing area is 5 lines, and automatic previewing is silently turned off if the baud rate is less than 2400 baud (this can be overridden with -p). If there is news, you will get a listing of the articles in the first newsgroup and be put at the first article. This is the first phase. Here you can use the following commands in the first phase: <return>, +, j Next article -, k Previous article c Select article for reading and caesar decode it d Select digest for reading n Next screenful of articles p Previous screenful of articles s Select article for straight reading u Unselect article/digest for reading v Preview article <esc> Read selected articles E Save all articles for next time N Next newsgroup P Previous newsgroup Q Quit, update .newsrc S Save unread articles for next time U Unsubscribe to a newsgroup V Toggle automatic previewing X Quit, don't update .newsrc ^Z Suspend After you select articles for reading by pressing <esc>, you will be presented with the articles in order through the news paginator. This is the second phase. After you read an article, you can execute the following commands: ? Gives a little help c [ rot ] Decode last article e Quit, return to same newsgroup f Submit a followup article p Reprint this article q Quit, go to next newsgroup r Reply via mail s <filename> Save article in <filename> - Back to previous article # Display the newsgroup and number of articles There is also a version of checknews which works with the new version of .newsrc. This should be enough to get you started. If you have any questions or comments, please address them to Anthony V. Discolo Department of Computer Science U.C. Santa Barbara Santa Barbara, CA 93106 (805) 961-4178 {ucbvax,cepu,arizona}!ucsbcsl!discolo --------------- Footnotes: [1] The idea for previewing an article came from Dave Elrod at the Computer Systems Lab. I wish I had thought of it. [2] Gerry Pollack reminded me that gathering all the articles at one time was a stupid way to do it. [3] This feature will be added in when I figure out a good way to do it. ! echo x - active.c cat << ! > active.c /* * active - Routines dealing with ACTIVE file. */ #include <stdio.h> #include <sys/param.h> #include "rn.h" /* * Read the ACTIVE file, inserting values into hash table. */ readactive() { FILE *a; int ngid; char ngnm[40]; if ((a = fopen(ACTIVE, "r")) == NULL) { perror(ACTIVE); exit(1); } while (fscanf(a, "%s%d", ngnm, &ngid) != EOF) anginsert(ngnm, ngid); fclose(a); } /* * Insert newsgroup into hashed table. */ anginsert(ngname, maxid) char *ngname; int maxid; { int hval; register struct angent *ang; hval = anghash(ngname); ang = angtab[hval]; if (ang == NULL) ang = angtab[hval] = angalloc(); else { while (ang->ang_next != NULL) ang = ang->ang_next; ang->ang_next = angalloc(); ang = ang->ang_next; } ang->ang_next = NULL; strcpy(ang->ang_name, ngname); ang->ang_maxid = maxid; } /* * Return maximum article id given newsgroup. */ angval(ngname) char *ngname; { int hval, len; register struct angent *ang; len = strlen(ngname); hval = anghash(ngname); ang = angtab[hval]; if (ang == NULL) return(0); while (ang != NULL) { if (strncmp(ngname, ang->ang_name, len) == 0) return(ang->ang_maxid); ang = ang->ang_next; } return(0); } anghash(name) char *name; { int h; h = 0; while (*name) { h += (int) (*name) * (int) (*name); name++; } return(h % ACTABSIZ); } struct angent *angalloc() { char *malloc(); struct angent *a; a = (struct angent *) malloc(sizeof(struct angent)); if (a == NULL) { printf("angalloc: No more memory\n"); exit(1); } return(a); } ! echo x - articles.c cat << ! > articles.c /* * articles - Major article routines. Miscellaneous article * routines are in misc2.c. */ #include <stdio.h> #include <sys/param.h> #include <sys/stat.h> #include "rn.h" /* * Gather one screenful of unread articles for a newsgroup. */ gatherarticles(ng) struct newsgroup *ng; { short screen; static short firstime = 1; int angv; register int anum, narticles; register struct article *a, *lasta; struct stat sbuf; screen = maxscreens(ng) + 1; if (!firstime && screen == 1) message("Searching %s", ng->ng_name, 0); /* * If we've reached the end, no need in looking anymore. */ if ((angv = angval(ng->ng_name)) == ng->ng_lastid) return(0); if (screen != 1) message("Searching for more articles in %s", ng->ng_name); narticles = 0; lasta = ng->ng_end; for (anum = ng->ng_lastid + 1; anum <= angv; anum++) { ng->ng_lastid = anum; if ((af = openarticle(anum)) == NULL) continue; /* * Check to see whether we have already * seen this article in another newsgroup. */ if (fstat(fileno(af), &sbuf) < 0 || itval(sbuf.st_ino)) { fclose(af); continue; } /* * Weed out garbaged articles. */ if (sbuf.st_size < 30) { fclose(af); continue; } itinsert(sbuf.st_ino); a = aralloc(); if (ng->ng_start == NULL) { ng->ng_start = a; a->a_forw = a->a_back = NULL; } else { lasta->a_forw = a; a->a_back = lasta; } a->a_forw = NULL; a->a_id = anum; a->a_screen = screen; strncpy(a->a_date, getheader("Date"), sizeof(a->a_date)); strncpy(a->a_from, getheader("From"), sizeof(a->a_from)); strncpy(a->a_subj, getheader("Subject"), sizeof(a->a_subj)); strncpy(a->a_keywds, getheader("Keywords"), sizeof(a->a_keywds)); strncpy(a->a_ngs, getheader("Newsgroups"), sizeof(a->a_ngs)); a->a_lines = atoi(getheader("Lines")); a->a_text = textstart(); lasta = a; fclose(af); if (++narticles % screenful == 0) break; } if (narticles) { firstime = 0; ng->ng_narticles += narticles; ng->ng_end = lasta; } return(narticles); } /* * Select articles from a newsgroup. This code is * terribly convoluted, mainly because you can go * forward and backward between newsgroups within * this routine, and certain operations imply others. */ selectarticles(ng) struct newsgroup *ng; { short scr, quit, noadvance; int art, rot; register char c; char *p, buf[BUFSIZ]; struct newsgroup *lastn; register struct article *a; struct article *lasta; scr = 1; startover: noecho(); cooked(); cd(ng->ng_dir); a = ng->ng_start; while (a != NULL && a->a_screen != scr) a = a->a_forw; if (a == NULL) { message("panic: Can't find screen %d", scr); exit(1); } signal(SIGTSTP, SIG_IGN); screensetup(ng, scr); listarticles(a); if (autoview) preview(a); toarticle(0); fflush(stdout); art = 0; signal(SIGTSTP, SIG_DFL); raw(); for (;;) { startvisual(); c = getchar(); endvisual(); switch (c) { /* * Read selected articles. */ case '\033': if (selections(ng)) goto out; else { message("No selections.", 0); toarticle(art); } break; /* * Suspend. */ case CTRL(z): dosuspend(ng, scr); if (autoview) preview(a); toarticle(art); break; /* * Save all the articles in this * newsgroup for next time. */ case 'E': message("Saving all of %s for next time.", ng->ng_name, 0); sleep(2); ng->ng_touched = 0; ng->ng_narticles = 0; ng->ng_start = NULL; goto nextorprev; /* * Next newsgroup. */ case 'N': /* * Before the last read newsgroup, * or before the absolute last * newsgroup. */ ng->ng_touched = 1; if ((lastng != NULL && ng != lastng) || ng != curng) { if ((ng = nextng(ng)) != NULL) { scr = 1; goto startover; } else { unsetty(); return; } } /* * At the absolute last newsgroup. */ else if (ng == lastng) { message("No more newsgroups.", 0); toarticle(art); } /* * At the last read newsgroup. */ else if (ng == curng) { unsetty(); return; } break; /* * Previous newsgroup. */ case 'P': if ((lastn = prevng(ng)) != NULL) { ng = lastn; scr = 1; goto startover; } else { message("No previous newsgroup.", 0); toarticle(art); } break; /* * Save some of the articles in this * newsgroup for next time. */ case 'S': message("Saving %s/%d-%d for next time.", ng->ng_name, a->a_id, ng->ng_lastid, 0); sleep(2); ng->ng_lastid = a->a_id - 1; goto nextorprev; /* * Quit the whole thing. */ case 'Q': done(0); break; /* * Unsubscribe to this newsgroup, and try to * go to the next newsgroup. If there is no * next newsgroup, try to go to the previous * newsgroup. Very ugly. */ case 'U': ng->ng_touched = ng->ng_unsubscribe = 1; message("Unsubscribed to %s", ng->ng_name); sleep(2); nextorprev: /* * If we are at the current * newsgroup, there could be more, * so just return. */ if (lastng == NULL && ng == curng) { unsetty(); return; } /* * Can we go forward a newsgroup? */ else if (ng != lastng && (lastn = nextng(ng)) != NULL) { ng = lastn; scr = 1; goto startover; } /* * If we can't find the next newsgroup, * try to find the previous newsgroup. * Note that we reset lastng and curng * to make it look like we are at the * last newsgroup. */ else if (ng == lastng && (lastn = prevng(ng)) != NULL) { ng = lastng = curng = lastn; scr = 1; goto startover; } /* * If we get to here there is no * where to go, so just quit. */ else done(0); break; /* * Toggle automatic previewing. */ case 'V': autoview = autoview ? 0 : 1; if (autoview) preview(a); message("Automatic previewing is now %s.", autoview ? "on" : "off"); toarticle(art); break; /* * Quit without writing .newsrc. */ case 'X': done(1); break; /* * Caesar decode this article. */ case 'c': markarticle(art, 'c'); fflush(stdout); a->a_select = DECODE; goto nextart; /* * Select digest article. */ case 'd': markarticle(art, 'd'); fflush(stdout); a->a_select = DIGEST; /* fall through */ /* * Next article. */ case '+': case 'j': case '\r': nextart: ng->ng_touched = 1; if (a->a_forw && a->a_forw->a_screen == scr) { a = a->a_forw; art++; if (autoview) preview(a); toarticle(art); fflush(stdout); } break; /* * Previous article. */ case 'k': case '-': if (a->a_back) { a = a->a_back; art--; if (autoview) preview(a); toarticle(art); fflush(stdout); } break; /* * Next screenful of articles. */ case 'n': ng->ng_touched = 1; if (scr < maxscreens(ng) || gatherarticles(ng) != 0) { scr++; goto startover; } else { message("No more screenfuls.", 0); toarticle(art); } break; /* * Previous screenful of articles. */ case 'p': if (scr == 1) { message("No previous screenful.", 0); toarticle(art); } else { scr--; goto startover; } break; /* * Select article for reading. */ case 's': markarticle(art, '+'); fflush(stdout); ng->ng_touched = 1; a->a_select = LETTER; goto nextart; /* * Unselect article. */ case 'u': if (a->a_read) markarticle(art, '*'); else markarticle(art, ' '); fflush(stdout); a->a_select = 0; break; /* * Preview an article. */ case 'v': if (!autoview) { preview(a); toarticle(art); } break; } } out: unsetty(); clearscreen(); fflush(stdout); a = ng->ng_start; realtop: quit = noadvance = 0; for (; a != NULL && !quit; a = a->a_forw) { if (!a->a_select) continue; a->a_read = a->a_select; top: if (a->a_select == LETTER) prarticle(ng, a); else if (a->a_select == DECODE) caesar(ng, a, 13); else prdigest(a); again: printf(": "); fflush(stdout); gets(buf); switch (buf[0]) { case '\0': break; case '?': dohelp(); goto again; case '#': printf("%d article%c in %s\n", ng->ng_narticles, ng->ng_narticles > 1 ? 's' : 0, ng->ng_name); goto again; /* * If we can't go back two articles, just repeat * the last article. */ case '-': if ((lasta = prevarticle(ng, a)) != NULL) a = lasta; goto top; /* * Reprint article with decoding. */ case 'c': p = index(buf, ' '); if (p == NULL) rot = 13; else { rot = atoi(p); if (rot < 0) rot = 13; } caesar(ng, a, rot); goto again; /* * Exit to same newsgroup. */ case 'e': noadvance++; goto nomore; /* * Submit a followup article. */ case 'f': followup(a); goto again; /* * Reprint this article. */ case 'p': goto top; case 'q': quit++; break; /* * Compose a mail reply. */ case 'r': reply(a); goto again; case 's': savearticle(a, buf); goto again; default: printf("%s: No such command. Type ? for help.\n", buf); goto again; } } nomore: /* * Mark articles as read, but not selected. */ for (a = ng->ng_start; a != NULL; a = a->a_forw) { if (a->a_read && a->a_select) a->a_select = 0; } /* * Figure out what newsgroup to display next. */ if (!noadvance && ng != curng && (lastn = nextng(ng)) != NULL) { ng = lastn; scr = 1; goto startover; } else if (noadvance || ng == lastng) goto startover; } ! echo x - checknews.c cat << ! > checknews.c /* * checknews - Check to see if there is news. * * Author: Anthony V. Discolo * Computer Systems Lab * University of California * Santa Barbara, CA 93106 * (805) 961-4178 * * Disclaimer: I take no responsibility whatsoever for the condition * of this software. Unrestricted use is hereby granted * as long as this header remains intact. */ #include <stdio.h> #include <pwd.h> #include <sys/param.h> #include "rn.h" int yes; int no; int silent; int isnews; main(argc, argv) int argc; char *argv[]; { char *p; if (argc == 1) yes++; while (--argc && (*++argv)[0] == '-') { switch ((*argv)[1]) { case 'n': no++; break; case 'q': silent++; break; case 'y': yes++; break; default: usage(); } } pw = getpwuid(getuid()); if (pw == NULL) { printf("Who are you?\n"); exit(1); } if ((p = getenv("NEWSRC")) != NULL) strcpy(newsrc, p); else sprintf(newsrc, "%s/.newsrc", pw->pw_dir); if ((rcf = fopen(newsrc, "r")) == NULL) { perror(newsrc); exit(1); } readrc(); fclose(rcf); readactive(); docheck(); } docheck() { register struct newsgroup *ng; while ((ng = getng()) != NULL) { if (!ng->ng_unsubscribe && ng->ng_begid < angval(ng->ng_name)) { isnews++; break; } } if (isnews) { if (yes) { printf("There is news.\n"); exit(0); } else if (silent) exit(1); } else { if (no) { printf("No news is good news.\n"); exit(0); } else if (silent) exit(0); } } usage() { printf("Usage: checknews [ -nyq ]\n"); exit(1); } ! echo x - convrc.c cat << ! > convrc.c /* * convrc - Convert to new style .newsrc file. */ #include <stdio.h> #include <ctype.h> main(argc, argv) int argc; char *argv[]; { FILE *f; int maxid; char buf[BUFSIZ], ng[40], *p; if (argc != 2) { printf("Usage: convrc file\n"); exit(1); } if ((f = fopen(argv[1], "r")) == NULL) { perror(argv[1]); exit(1); } while (fgets(buf, sizeof(buf), f) != NULL) { sscanf(buf, "%s", ng); if (strcmp(ng, "options") == 0 || buf[0] == '\t') continue; p = &buf[strlen(buf) - 2]; while (isdigit(*p)) --p; p++; maxid = atoi(p); printf("%s %d\n", ng, maxid); } fclose(f); } ! echo x - digest.c cat << ! > digest.c /* * digest - Digest routines. */ #include <stdio.h> #include <ctype.h> #include <sys/param.h> #include "rn.h" /* * Print a digest. */ prdigest(a) struct article *a; { FILE *f; int fpos, dtotal, quit; char *p, buf[BUFSIZ]; struct article *digest, *curart, *lastart; if ((f = openarticle(a->a_id)) == NULL) { perror("digest"); return; } dtotal = 0; digest = NULL; fseek(f, a->a_text, 0); /* * Scan digest, gathering articles. */ for (;;) { while ((p = fgets(buf, sizeof(buf), f)) != NULL && !isheader(buf)) ; if (p == NULL) break; curart = aralloc(); if (digest == NULL) { digest = curart; digest->a_back = NULL; } else { lastart->a_forw = curart; curart->a_back = lastart; } curart->a_id = a->a_id; curart->a_screen = ++dtotal; /* screen = digest article # */ curart->a_forw = NULL; do { buf[strlen(buf) - 1] = 0; if (strncmp(buf, "Date: ", 6) == 0) strncpy(curart->a_date, buf + 6, sizeof(curart->a_date)); else if (strncmp(buf, "From: ", 6) == 0) strncpy(curart->a_from, buf + 6, sizeof(curart->a_from)); else if (strncmp(buf, "Subject: ", 9) == 0) strncpy(curart->a_subj, buf + 9, sizeof(curart->a_subj)); else if (strncmp(buf, "To: ", 4) == 0) ; /* XXX */ else if (strncmp(buf, "Cc: ", 4) == 0) ; /* XXX */ fpos = ftell(f); if (fgets(buf, sizeof(buf), f) == NULL) break; } while (isheader(buf)); curart->a_text = fpos; lastart = curart; } fseek(f, a->a_text, 0); /* * Put out introductory ditty. */ while (fgets(buf, sizeof(buf), f) != NULL && !isheader(buf)) fputs(buf, stdout); quit = 0; for (curart = digest; curart != NULL && !quit; curart = curart->a_forw) { top: printf("\nDigest article %d of %d\n", curart->a_screen, dtotal); printf("Date: %s\n", curart->a_date); printf("From: %s\n", curart->a_from); printf("Subject: %s\n", curart->a_subj); printf("More? [ynq] "); gets(buf); switch (buf[0]) { case '-': if (curart->a_back != NULL) curart = curart->a_back; else printf("Can't go back.\n"); goto top; break; case 'n': break; case 'q': quit++; break; case 's': savedarticle(curart, a, buf); break; case '\0': case 'y': prdarticle(curart, dtotal); if (curart->a_screen != dtotal) { printf(": "); fflush(stdout); gets(buf); if (buf[0] == 's') savedarticle(curart, a, buf); } else { printf("\nLast digest article? [-sq] "); fflush(stdout); gets(buf); if (buf[0] == '-') goto top; else if (buf[0] == 's') savedarticle(curart, a, buf); } break; } } } /* * Print digest article. */ prdarticle(a, tot) struct article *a; int tot; { FILE *p, *f; char cmd[200], buf[BUFSIZ]; if (strcmp(pager, PAGER) == 0) sprintf(cmd, "%s -p -h \"[Digest %d/%d %-23.23s %s]\"", pager, a->a_screen, tot, justaddr(a->a_from), fixsubj(a->a_subj, 38)); else strcpy(cmd, pager); if ((p = popen(cmd, "w")) == NULL) { perror("pager"); return; } if ((f = openarticle(a->a_id)) == NULL) { perror("article"); return; } fseek(f, a->a_text, 0); while (fgets(buf, sizeof(buf), f) != NULL && !isheader(buf)) fputs(buf, p); fclose(f); pclose(p); } savedarticle(a, ap, xname) struct article *a; struct article *ap; char *xname; { FILE *r, *w; short append; register int lines, chars; char *name, buf[BUFSIZ], cwd[MAXPATHLEN]; if ((name = index(xname, ' ')) == NULL) { oops: printf("Save requires a filename.\n"); return; } name++; if (strlen(name) == 0) goto oops; if ((r = openarticle(a->a_id)) == NULL) { perror("article"); return; } if (getwd(cwd) == 0) { fclose(r); puts(cwd); return; } cd(homedir); append = !access(name, 0); if ((w = fopen(name, "a")) == NULL) { perror(name); fclose(r); cd(cwd); return; } fprintf(w, "Digest article %d in %s/%d\n", a->a_screen, curng->ng_name, ap->a_id); fprintf(w, "Date: %s\n", a->a_date); fprintf(w, "From: %s\n", a->a_from); fprintf(w, "Subject: %s\n", a->a_subj); fseek(r, a->a_text, 0); lines = chars = 0; while (fgets(buf, sizeof(buf), r) != NULL && !isheader(buf)) { lines++; chars += strlen(buf); fputs(buf, w); } cd(cwd); fclose(w); fclose(r); printf("\"%s\" [%s] %d/%d\n", name, append ? "Appended" : "New file", lines, chars); } isheader(s) char *s; { register char *p; p = s; while (*p && isalpha(*p)) p++; if (*p == ':') { return(strncmp(s, "Date: ", 6) == 0 || strncmp(s, "From: ", 6) == 0 || strncmp(s, "To: ", 4) == 0 || strncmp(s, "Cc: ", 4) == 0 || strncmp(s, "Subject: ", 9) == 0); } return(0); } ! echo x - inotab.c cat << ! > inotab.c /* * inotab - Inode hash table routines. */ #include <stdio.h> #include <sys/param.h> #include "rn.h" /* * Insert inode into hashed table. */ itinsert(ino) long ino; { int hval; register struct inotab *it; hval = ino % ITABSIZ; it = itab[hval]; if (it == NULL) it = itab[hval] = italloc(); else { while (it->i_next != NULL) it = it->i_next; it->i_next = italloc(); it = it->i_next; } it->i_ino = ino; it->i_next = NULL; } /* * Return whether an inode has been seen yet. */ itval(ino) long ino; { int hval; register struct inotab *it; hval = ino % ITABSIZ; it = itab[hval]; if (it == NULL) { return(0); } while (it != NULL) { if (it->i_ino == ino) { return(1); } it = it->i_next; } return(0); } struct inotab *italloc() { char *malloc(); struct inotab *it; it = (struct inotab *) malloc(sizeof(struct inotab)); if (it == NULL) { printf("italloc: No more memory\n"); exit(1); } return(it); } ! echo x - misc1.c cat << ! > misc1.c /* * misc1 - Miscellaneous routines. */ #include <stdio.h> #include <ctype.h> #include <sys/param.h> #include "rn.h" /* * Read and store .newsrc. */ readrc() { register int i; char buf[BUFSIZ]; i = 0; while (fgets(buf, sizeof(buf), rcf) != NULL) { buf[strlen(buf) - 1] = 0; rcngs[i++] = strsave(buf); } rcngs[i] = NULL; ngindex = 0; } /* * Create a new .newsrc for the user. Reopen path * for reading on rcf file descriptor. */ newrc(path) char *path; { FILE *a; char ng[40]; if ((a = fopen(ACTIVE, "r")) == NULL) { perror(ACTIVE); exit(1); } if ((rcf = fopen(path, "w")) == NULL) { perror(path); exit(1); } while (fscanf(a, "%s%*d", ng) != EOF) fprintf(rcf, "%s 0\n", ng); fclose(rcf); fclose(a); rcf = fopen(path, "r"); } message(s, a, b, c, d, e, f, g) char *s; { tgo(ttylines - 1, 0); fflush(stdout); clearline(); inverseon(); printf(s, a, b, c, d, e, f, g); inverseoff(); fflush(stdout); } /* * Transform an integer into a string. */ char *idtoname(id) int id; { static char buf[20]; sprintf(buf, "%d", id); return(buf); } /* * Add a newsgroup name to the newsgroup list * gathered from argv. */ addng(ngname) char *ngname; { static char **ngptr; if (ngptr == 0) ngptr = &nglist[0]; *ngptr++ = ngname; } /* * Fake a .newsrc for argv supplied newsgroups. */ fakenrc() { register char **p; sprintf(fakerc, "/tmp/news%06d", getpid()); if ((rcf = fopen(fakerc, "w")) == NULL) { perror(fakerc); exit(1); } for (p = nglist; *p != NULL; p++) fprintf(rcf, "%s: %d\n", *p, getlastart(*p)); fclose(rcf); rcf = fopen(fakerc, "r"); } /* * Return the last article number for a newsgroup in the * .newsrc, or 0 if .newsrc or newsgroup doesn't exist. * This is slow, so hopefully we won't have to do it a lot. */ getlastart(ngname) char *ngname; { FILE *f; int ngid; char ngnm[40]; if ((f = fopen(newsrc, "r")) == NULL) return(0); while (fscanf(f, "%s%d", ngnm, &ngid) != EOF) { if (strncmp(ngnm, ngname, strlen(ngname)) == 0) return(ngid); } return(0); } /* * Replace '.' with '/' to make filename. */ char *ngtopath(s) register char *s; { register char *p; static char buf[BUFSIZ]; p = buf; while (*s) { if (*s == '.') *p = '/'; else *p = *s; p++; s++; } *p = 0; return(buf); } /* * Figure out the number of screens in a newsgroup. */ maxscreens(ng) struct newsgroup *ng; { short s, r; s = ng->ng_narticles / screenful; r = ng->ng_narticles % screenful; return(r ? s + 1 : s); } char *prchar(c) char c; { static char prbuf[10]; if (c == ' ') strcpy(prbuf, "<blank>"); else if (iscntrl(c)) sprintf(prbuf, "^%c", c + '@'); else sprintf(prbuf, "%c", c); /* stupid, I know */ return(prbuf); } char *strsave(s) char *s; { char *p, *malloc(); p = malloc(strlen(s) + 1); if (p == NULL) { printf("alloc: No more memory\n"); exit(1); } strcpy(p, s); return(p); } trim(s, len) register char *s; register int len; { register int l = 0; while (*s) { if (l == len) { *s++ = '\n'; *s = 0; return; } else if (l > len) { *(s - 1) = '\n'; *++s = 0; return; } if (*s == '\t') l += 8 - (l % 8); s++; l++; } } cd(dir) char *dir; { if (chdir(dir) < 0) { message("panic: Can't chdir to %s", dir, 0); exit(1); } } done(nowrite) short nowrite; { FILE *f; int ngid; register char **p; char xnewsrc[MAXPATHLEN], ng[80]; register struct newsgroup *n; unsetty(); tgo(ttylines - 1, 0); clearline(); fflush(stdout); fclose(rcf); if (nglist[0] != NULL) unlink(fakerc); if (nowrite || ignorerc) goto xit; cd(homedir); if (nglist[0] != NULL) { sprintf(xnewsrc, "%s.new", newsrc); if ((rcf = fopen(newsrc, "r")) == NULL) { perror(newsrc); exit(1); } if ((f = fopen(xnewsrc, "w")) == NULL) { perror(xnewsrc); exit(1); } while (fscanf(rcf, "%s%d", ng, &ngid) != EOF) { for (n = newsgroups; n != NULL; n = n->ng_forw) { if (strncmp(ng, n->ng_name, strlen(ng) - 1) == 0) break; } if (n != NULL) { fprintf(f, "%s: %d\n", n->ng_name, n->ng_lastid); } else { fprintf(f, "%s %d\n", ng, ngid); } } fclose(f); fclose(rcf); if (rename(xnewsrc, newsrc) < 0) perror("rename"); } else { if ((rcf = fopen(newsrc, "w")) == NULL) { perror(newsrc); exit(1); } /* * Write out all newsgroups that have been read. */ for (n = newsgroups, p = rcngs; n != NULL; n = n->ng_forw, p++) { if (n->ng_touched) { fprintf(rcf, "%s%c %d\n", n->ng_name, n->ng_unsubscribe ? '!' : ':', n->ng_lastid); } else fprintf(rcf, "%s\n", *p); } /* * Write out any unread newsgroups. */ for (; *p != 0; p++) fprintf(rcf, "%s\n", *p); fclose(rcf); } xit: exit(0); } ! echo x - misc2.c cat << ! > misc2.c /* * misc2 - Miscellaneous article routines. */ #include <stdio.h> #include <sys/param.h> #include <sys/stat.h> #include "rn.h" /* * Submit a followup article. */ followup(a) struct article *a; { FILE *f; char buf[BUFSIZ], refs[80], id[40], temp[100]; struct stat sbuf; printf("Posting followup article to network. Please use\n"); printf("reply ('r') instead unless your article is of general\n"); printf("interest.\n"); printf("\nDo you want to continue? [yn] "); gets(buf); if (buf[0] == 'n') return; if ((af = openarticle(a->a_id)) == NULL) { perror("article"); return; } strncpy(refs, getheader("References"), sizeof(refs)); strncpy(id, getheader("Message-ID"), sizeof(id)); fclose(af); sprintf(temp, "/tmp/followup%06d", getpid()); if ((f = fopen(temp, "w")) == NULL) { perror(temp); return; } fprintf(f, "Newsgroups: %s\n", a->a_ngs); fprintf(f, "Subject: "); if (strncmp(a->a_subj, "Re:", 3) != 0) fprintf(f, "Re: "); fprintf(f, "%s\n", a->a_subj); fprintf(f, "References: "); if (strlen(refs)) fprintf(f, "%s, ", refs); fprintf(f, "%s\n", id); if (strlen(a->a_keywds)) fprintf(f, "Keywords: %s\n", a->a_keywds); fclose(f); sprintf(buf, "%s %s", editor, temp); system(buf); if (stat(temp, &sbuf) < 0 || sbuf.st_size < 15) printf("Followup article mangled. Article not posted.\n"); else { sprintf(buf, "/usr/bin/inews -h -D < %s", temp); system(buf); printf("Article posted.\n"); } unlink(temp); } /* * Reply via mail to an article. */ reply(a) struct article *a; { FILE *f; register char *p; char *rindex(), buf[BUFSIZ], path[BUFSIZ], refs[80], id[40], temp[100]; struct stat sbuf; if ((af = openarticle(a->a_id)) == NULL) { perror("article"); return; } strncpy(path, getheader("Path"), sizeof(path)); if (strlen(path) == 0) { printf("Can't find return address. Sorry.\n"); fclose(af); return; } strncpy(refs, getheader("References"), sizeof(refs)); strncpy(id, getheader("Message-ID"), sizeof(id)); sprintf(temp, "/tmp/followup%06d", getpid()); if ((f = fopen(temp, "w")) == NULL) { perror(temp); return; } p = index(path, '!'); if (p == NULL) { printf("Return address too short. Sorry.\n"); fclose(af); return; } fprintf(f, "To: %s\n", p + 1); fprintf(f, "Subject: "); if (strncmp(a->a_subj, "Re:", 3) != 0) fprintf(f, "Re: "); fprintf(f, "%s\n", a->a_subj); fprintf(f, "Newsgroups: %s\n", a->a_ngs); fprintf(f, "References: "); if (strlen(refs)) fprintf(f, "%s, ", refs); fprintf(f, "%s\n", id); if (strlen(a->a_keywds)) fprintf(f, "Keywords: %s\n", a->a_keywds); fclose(f); p = rindex(mailer, '/'); /* * \`\`Regular'' mailer. */ if (p == NULL || strcmp(p + 1, "comp") != 0) { sprintf(buf, "%s %s", editor, temp); system(buf); if (stat(temp, &sbuf) < 0 || sbuf.st_size < 15) { printf("Reply article mangled. Article not delivered.\n"); return; } else sprintf(buf, "%s < %s", mailer, temp); } /* * MH mailer. */ else sprintf(buf, "%s -form %s", mailer, temp); if (system(buf) == 0) printf("Reply sent.\n"); unlink(temp); } /* * Set up the screen for selection. */ screensetup(ng, scr) struct newsgroup *ng; short scr; { register int i; clearscreen(); printf("Newsgroup: %s: Page %d%c\n", ng->ng_name, scr, ng->ng_lastid == angval(ng->ng_name) ? 0 : '+'); tgo(ttylines - (navlines + 2), 0); for (i = 0; i < ttycols; i++) putchar('-'); tgo(1, 0); } /* * List all articles in this screenful. */ listarticles(a) register struct article *a; { short s; s = a->a_screen; while (a != NULL && a->a_screen == s) { printf(" %25.25s %c %-.48s\n", justaddr(a->a_from), artstatus(a), a->a_subj); a = a->a_forw; } } /* * Print an article. */ prarticle(ng, a) struct newsgroup *ng; struct article *a; { FILE *p, *f; register int i; char cmd[200], buf[BUFSIZ]; if (strcmp(pager, PAGER) == 0) sprintf(cmd, "%s -p -h \"[%s/%d %-20.20s %s]\"", pager, ng->ng_name, a->a_id, justaddr(a->a_from), fixsubj(a->a_subj, 30)); else strcpy(cmd, pager); if ((p = popen(cmd, "w")) == NULL) { perror("pager"); return; } if ((f = openarticle(a->a_id)) == NULL) { perror("article"); return; } fseek(f, a->a_text, 0); while (fgets(buf, sizeof(buf), f) != NULL) fputs(buf, p); fclose(f); pclose(p); } /* * Caesar decoding. */ caesar(ng, a, rot) struct newsgroup *ng; struct article *a; int rot; { FILE *p, *f; int n; char cmd[200], buf[BUFSIZ]; if (strcmp(pager, PAGER) == 0) sprintf(cmd, "%s %d | %s -p -h \"[%s/%d %-20.20s %s]\"", CAESAR, rot, pager, ng->ng_name, a->a_id, justaddr(a->a_from), fixsubj(a->a_subj, 30)); else sprintf(cmd, "%s %d | %s", CAESAR, rot, pager); if ((p = popen(cmd, "w")) == NULL) { perror("caesar"); return; } if ((f = openarticle(a->a_id)) == NULL) { perror("article"); return; } fseek(f, a->a_text, 0); while (fgets(buf, sizeof(buf), f) != NULL) fputs(buf, p); fclose(f); pclose(p); } /* * Put the first few lines of the article at * the bottom of the screen. */ preview(a) struct article *a; { FILE *ar; register int i; char buf[BUFSIZ]; cooked(); tgo(ttylines - (navlines + 1), 0); if ((ar = openarticle(a->a_id)) != NULL) { fseek(ar, a->a_text, 0); while (fgets(buf, sizeof(buf), ar) != NULL && strlen(buf) == 1) ; clearline(); trim(buf, ttycols - 1); fputs(buf, stdout); for (i = 0; i < navlines - 1; i++) { clearline(); if (fgets(buf, sizeof(buf), ar) != NULL) { trim(buf, ttycols - 1); fputs(buf, stdout); } else putchar('\n'); } fclose(ar); } fflush(stdout); raw(); } savearticle(a, xname) struct article *a; char *xname; { FILE *r, *w; short append; register int lines, chars; char *name, buf[BUFSIZ], cwd[MAXPATHLEN]; if ((name = index(xname, ' ')) == NULL) { oops: printf("Save requires a filename.\n"); return; } name++; if (strlen(name) == 0) goto oops; if ((r = openarticle(a->a_id)) == NULL) { perror("article"); return; } if (getwd(cwd) == 0) { fclose(r); puts(cwd); return; } cd(homedir); append = !access(name, 0); if ((w = fopen(name, "a")) == NULL) { perror(name); fclose(r); cd(cwd); return; } lines = chars = 0; while (fgets(buf, sizeof(buf), r) != NULL) { fputs(buf, w); lines++; chars += strlen(buf); } cd(cwd); fclose(w); fclose(r); printf("\"%s\" [%s] %d/%d\n", name, append ? "Appended" : "New file", lines, chars); } markarticle(art, c) short art; char c; { toarticle(art); putchar(c); putchar('\b'); } /* * Give user minimal help. */ dohelp() { printf("Commands are:\n"); printf(" \\\n Print next article\n"); printf(" c [ rot ] Decode last article\n"); printf(" e Quit, return to same newsgroup\n"); printf(" f Submit followup article\n"); printf(" p Reprint this article\n"); printf(" q Quit, go to next newsgroup\n"); printf(" r Reply to article via mail\n"); printf(" s <filename> Save article in a file\n"); printf(" - Go to previous article\n"); } /* * Get the next newsgroup from .newsrc. */ struct newsgroup *getng() { int len; char *p, *malloc(); struct newsgroup *n; n = (struct newsgroup *) malloc(sizeof(struct newsgroup)); if (n == NULL) { printf("getng: No more memory\n"); exit(1); } next: if ((p = rcngs[ngindex++]) == NULL) return(NULL); if (rcngs[ngindex] == NULL) lastng = n; if (sscanf(p, "%s%d", n->ng_name, &n->ng_begid) != 2) return(NULL); len = strlen(n->ng_name); if (n->ng_name[len - 1] == '!') { n->ng_unsubscribe++; n->ng_lastid = n->ng_begid; } else if (n->ng_name[len - 1] != ':') { fprintf(stderr, "Illegal .newsrc line: \"%s\"\n", p); sleep(2); goto next; } n->ng_name[len - 1] = 0; /* remove [:!] */ if (ignorerc) n->ng_begid = 0; n->ng_narticles = n->ng_touched = 0; n->ng_lastid = n->ng_begid; strcpy(n->ng_dir, "/usr/spool/news/"); strcat(n->ng_dir, ngtopath(n->ng_name)); return(n); } /* * Get the named header from an article. */ char *getheader(xhname) char *xhname; { int len; static char buf[BUFSIZ]; char hname[30]; sprintf(hname, "%s: ", xhname); len = strlen(hname); rewind(af); while (fgets(buf, sizeof(buf), af) != NULL && strlen(buf) > 1) { if (strncmp(buf, hname, strlen(hname)) == 0) { buf[strlen(buf) - 1] = 0; return(&buf[len]); } } return(""); } /* * See if we can go back one article * in the article list. If the argument is * NULL, return the last article. */ struct article *prevarticle(ng, a) struct newsgroup *ng; register struct article *a; { if (a == NULL) { a = ng->ng_start; while (a->a_forw != NULL) a = a->a_forw; } else a = a->a_back; while (a != NULL && !a->a_read && !a->a_select) a = a->a_back; return(a); } /* * Find the next readable article. */ struct article *nextarticle(a) register struct article *a; { if (a == NULL) return(NULL); a = a->a_forw; while (a != NULL && !a->a_select) a = a->a_forw; return(a); } /* * See if we can go back one newsgroup. If * the argument is NULL, return the last newsgroup. */ struct newsgroup *prevng(ng) register struct newsgroup *ng; { if (ng == NULL) { ng = newsgroups; while (ng->ng_forw != NULL) ng = ng->ng_forw; } else ng = ng->ng_back; while (ng != NULL && (!ng->ng_narticles || ng->ng_unsubscribe)) ng = ng->ng_back; return(ng); } /* * See if we can go forward one newsgroup. */ struct newsgroup *nextng(ng) register struct newsgroup *ng; { ng = ng->ng_forw; while (ng != NULL && (!ng->ng_narticles || ng->ng_unsubscribe)) ng = ng->ng_forw; return(ng); } /* * Print article header. */ prartheader(ng, a) struct newsgroup *ng; struct article *a; { printf("\nArticle %d of %d\n", a->a_id, ng->ng_lastid); printf("Date: %s\n", a->a_date); printf("From: %s\n", a->a_from); printf("Subject: %s\n", a->a_subj); if (strlen(a->a_keywds)) printf("Keywords: %s\n", a->a_keywds); printf("Newsgroups: %s\n", a->a_ngs); } /* * Strip off real name, etc. */ char *justaddr(fromline) char *fromline; { register char *p, *q; static char buf[100]; if ((q = index(fromline, '!')) != NULL || (q = index(fromline, '@')) != NULL) { while (*q != ' ' && *q != '<' && q != fromline) --q; if (*q == ' ' || *q == '<') q++; strcpy(buf, q); p = buf; while (*p && *p != ' ' && *p != '\t' && *p != '>') p++; *p = 0; return(buf); } else return(fromline); } /* * Figure out where the beginning of the article is. */ textstart() { register int len, cnt = 0; char buf[BUFSIZ]; rewind(af); while (fgets(buf, sizeof(buf), af) != NULL) { if ((len = strlen(buf)) == 1) break; cnt += len; } return(cnt + 1); } /* * Return true if an article is selected * for reading. */ selections(ng) struct newsgroup *ng; { register struct article *a; for (a = ng->ng_start; a != NULL; a = a->a_forw) { if (a->a_select) return(1); } return(0); } artstatus(a) struct article *a; { if (a->a_select == LETTER) return('+'); else if (a->a_select == DIGEST) return('d'); else if (a->a_select == DECODE) return('c'); else if (a->a_read) return('*'); return(' '); } /* * An article memory allocator. */ struct article *aralloc() { char *malloc(); struct article *a; a = (struct article *) malloc(sizeof(struct article)); if (a == NULL) { printf("aralloc: No more memory\n"); exit(1); } return(a); } /* * Have to escape all funny characters in the subject, * and fill out (or truncate) to len characters. */ char *fixsubj(s, len) char *s; unsigned len; { register int i, fudge; register char *p, *q; static char subj[200]; q = subj; fudge = 0; for (p = s; *p != NULL; p++) { if (*p == '"' || *p == '$' || *p == '\\\') { *q++ = '\\\'; fudge++; } *q++ = *p; } subj[len + fudge] = 0; p = &subj[len + fudge - 1]; while (p >= q) *p-- = ' '; return(subj); } dosuspend(ng, scr) struct newsgroup *ng; short scr; { register struct article *a; tgo(ttylines - 1, 0); clearline(); unsetty(); fflush(stdout); kill(0, SIGTSTP); screensetup(ng, scr); a = ng->ng_start; while (a->a_screen != scr) a = a->a_forw; listarticles(a); setty(); } ! echo x - newsrc.example cat << ! > newsrc.example general: 33 ca.news.group: 5 ca.wanted: 6 fa.arpa-bboard: 134 fa.human-nets: 48 fa.info-mac: 1306 fa.info-terms: 1 fa.tcp-ip: 1 fa.telecom: 97 la.eats: 23 mod.map.uucp: 12 mod.movies: 14 mod.sources: 47 mod.std: 1 mod.unix: 30 net.bugs.4bsd: 460 net.bugs.v7: 20 net.bugs: 232 net.dcom: 336 net.games.rogue: 856 net.general: 481 net.info-terms: 195 net.jobs: 442 net.jokes: 3302 net.lan: 331 net.lang.st80: 108 net.mail: 236 net.micro.68k: 338 net.micro.mac: 888 net.micro: 1917 net.music.folk: 139 net.news.adm: 127 net.news.b: 281 net.news: 1021 net.research: 52 net.rumor: 246 net.sources.bugs: 244 net.sources.games: 19 net.sources: 1158 net.unix-wizards: 3331 net.unix: 1831 net.usenix: 146 net.usoft: 32 net.wanted.sources: 636 ! echo x - pager.diffs cat << ! > pager.diffs *** newspager.c Thu May 2 11:48:31 1985 --- /usr/src/ucb/more/more.c Fri Mar 30 09:14:56 1984 *************** *** 9,16 ** ** modified by Geoff Peck, UCB to add underlining, single spacing ** modified by John Foderaro, UCB to add -c and MORE environment variable - ** - ** modified by Anthony Discolo, UCSB to add -h option for news. */ #include <stdio.h> --- 9,14 ----- ** ** modified by Geoff Peck, UCB to add underlining, single spacing ** modified by John Foderaro, UCB to add -c and MORE environment variable */ #include <stdio.h> *************** *** 64,70 int catch_susp; /* We should catch the SIGTSTP signal */ char **fnames; /* The list of file names */ int nfiles; /* Number of files left to process */ - char *header; /* Print out at top of page */ char *shell; /* The name of the shell to use */ int shellp; /* A previous shell command exists */ char ch; --- 62,67 ----- int catch_susp; /* We should catch the SIGTSTP signal */ char **fnames; /* The list of file names */ int nfiles; /* Number of files left to process */ char *shell; /* The name of the shell to use */ int shellp; /* A previous shell command exists */ char ch; *************** *** 116,129 initterm (); if(s = getenv("MORE")) argscan(s); while (--nfiles > 0) { ! if ((ch = (*++fnames)[0]) == '-' && (*fnames)[1] == 'h') { ! --nfiles; ! fnames++; ! if (nfiles == 0) ! break; ! header = *fnames; ! } ! else if (ch == '-') { argscan(*fnames+1); } else if (ch == '+') { --- 113,119 ----- initterm (); if(s = getenv("MORE")) argscan(s); while (--nfiles > 0) { ! if ((ch = (*++fnames)[0]) == '-') { argscan(*fnames+1); } else if (ch == '+') { *************** *** 144,151 } else break; } - if (header) - Lpp -= 2; /* allow clreol only if Home and eraseln and EodClr strings are * defined, and in that case, make sure we are in noscroll mode */ --- 134,139 ----- } else break; } /* allow clreol only if Home and eraseln and EodClr strings are * defined, and in that case, make sure we are in noscroll mode */ *************** *** 391,397 { register int c; register int nchars; - int hlen; int length; /* length of current line */ static int prev_len = 1; /* length of previous line */ --- 379,384 ----- { register int c; register int nchars; int length; /* length of current line */ static int prev_len = 1; /* length of previous line */ *************** *** 395,401 int length; /* length of current line */ static int prev_len = 1; /* length of previous line */ - hlen = 0; for (;;) { while (num_lines > 0 && !Pause) { if (header && !hlen && num_lines > Lpp - 4) { --- 382,387 ----- int length; /* length of current line */ static int prev_len = 1; /* length of previous line */ for (;;) { while (num_lines > 0 && !Pause) { if ((nchars = getline (f, &length)) == EOF) *************** *** 398,408 hlen = 0; for (;;) { while (num_lines > 0 && !Pause) { - if (header && !hlen && num_lines > Lpp - 4) { - hlen = strlen(header); - prbuf(header, hlen > 79 ? 79 : hlen); - printf("\n\n"); - } if ((nchars = getline (f, &length)) == EOF) { if (clreol) --- 384,389 ----- for (;;) { while (num_lines > 0 && !Pause) { if ((nchars = getline (f, &length)) == EOF) { if (clreol) *************** *** 433,439 break; num_lines--; } - hlen = 0; fflush(stdout); if ((c = Getc(f)) == EOF) { --- 414,419 ----- break; num_lines--; } fflush(stdout); if ((c = Getc(f)) == EOF) { ! echo x - rn.c cat << ! > rn.c /* * rn - Read news. * * Author: Anthony V. Discolo * Computer Systems Lab * University of California * Santa Barbara, CA 93106 * (805) 961-4178 * * Disclaimer: I take no responsibility whatsoever for the condition * of this software. Unrestricted use is hereby granted * as long as this header remains intact. */ #include <stdio.h> #include <pwd.h> #include <signal.h> #include <sys/param.h> #include "rn.h" main(argc, argv) int argc; char *argv[]; { short sawone; char *key; struct newsgroup *lastn; initerm(); /* * This may get turned off later. */ if (!autoview && ttybaud() > 1200) autoview++; navlines = 5; while (--argc && (*++argv)[0] == '-') { key = *argv; if (*(key + 1) == 0) usage(); while (*++key) { switch (*key) { case 'n': if (argc <= 1) usage(); while (argc > 1 && (*(argv + 1))[0] != '-') { --argc; addng(*++argv); } break; case 'P': autoview = 0; break; case 'p': autoview++; break; case 'x': ignorerc++; break; case 'v': if (argc > 1) { navlines = atoi(*++argv); if (navlines < 5) navlines = 5; else if (navlines > ttylines / 2) navlines = ttylines / 2; } else usage(); break; default: usage(); } } } /* * Set screen size. */ screenful = ttylines - (navlines + 3); pw = getpwuid(getuid()); if (pw == NULL) { printf("Who are you?\n"); exit(1); } if ((key = getenv("NEWSRC")) != NULL) strcpy(newsrc, key); else sprintf(newsrc, "%s/.newsrc", pw->pw_dir); /* * Fake an .newsrc if the user supplied us with * a newsgroup list, fakerc() will reopen the * fake .newsrc on rcf. */ if (nglist[0] != NULL) fakenrc(); /* * If the .newsrc file doesn't exist, newrc() will * create one and reopen it on rcf. */ else if ((rcf = fopen(newsrc, "r")) == NULL) newrc(newsrc); readrc(); fclose(rcf); readactive(); if ((pager = getenv("PAGER")) == NULL) pager = PAGER; if ((editor = getenv("EDITOR")) == NULL) editor = EDITOR; if ((mailer = getenv("MAILER")) == NULL) mailer = MAILER; if (getwd(homedir) == 0) { puts(homedir); exit(1); } signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGPIPE, SIG_IGN); lastn = lastng = NULL; sawone = 0; while ((curng = getng()) != NULL) { if (newsgroups == NULL) { newsgroups = curng; curng->ng_forw = NULL; curng->ng_back = NULL; } else { lastn->ng_forw = curng; curng->ng_forw = NULL; curng->ng_back = lastn; } lastn = curng; if (chdir(curng->ng_dir) < 0) continue; if (!curng->ng_unsubscribe) { if (gatherarticles(curng)) { sawone++; selectarticles(curng); } } } if (sawone && (curng = prevng(NULL)) != NULL) { lastng = curng; message("No more newsgroups.", 0); sleep(2); cd(curng->ng_dir); selectarticles(curng); } else if (!sawone) printf("No news.\n"); done(1); } usage() { printf("Usage: rn [ -Ppx ] [ -v lines ] [ -n ng1 ng2 ... ]\n"); exit(1); } ! echo x - rn.h cat << ! > rn.h /* * rn.h */ #include "tty.h" #define ACTIVE "/usr/lib/news/active" /* active newsgroups */ #define CAESAR "/usr/lib/news/caesar" /* decoding program */ #define PAGER "/staff/csl/discolo/bin/newspager" #define EDITOR "/usr/ucb/vi" #define MAILER "/usr/lib/sendmail -t" #define MAXNGS 400 /* number of argv newsgroups */ #define ACTABSIZ 137 /* must be prime */ #define ITABSIZ 1000 /* inode hash tabsiz */ /* * Article types. */ #define LETTER 1 #define DIGEST 2 #define DECODE 3 FILE *rcf; FILE *af; FILE *popen(); int ignorerc; /* start at article 1 */ int autoview; /* automatic previewing */ int ngindex; int screenful; /* # of articles/page */ int navlines; /* # of autoview lines/page */ char *tgoto(); char *rcngs[MAXNGS]; /* from .newsrc */ char homedir[MAXPATHLEN]; char newsrc[MAXPATHLEN]; char fakerc[MAXPATHLEN]; char *nglist[MAXNGS]; /* from command line */ char *pager; char *editor; char *mailer; char *getenv(); char *ngtopath(); char *getheader(); char *justaddr(); char *idtoname(); char *prchar(); char *fixsubj(); char *index(); char *strsave(); struct passwd *pw; /* * Hashed version of the ACTIVE file. */ struct angent { char ang_name[80]; /* name */ int ang_maxid; /* maximum article id */ struct angent *ang_next; /* hash chain */ } *angtab[ACTABSIZ]; /* * Hashed inode table. Used in finding duplicate articles. */ struct inotab { long i_ino; struct inotab *i_next; } *itab[ITABSIZ]; /* * A newsgroup. */ struct newsgroup { char ng_name[80]; /* name */ int ng_begid; /* first article id */ int ng_lastid; /* last article id */ int ng_narticles; /* number of articles in newsgroup */ char ng_dir[MAXPATHLEN]; /* directory */ short ng_unsubscribe; /* if user unsubscribes to this ng */ short ng_touched; /* this newsgroup has been perused */ struct article *ng_start; /* beginning of article list */ struct article *ng_end; /* end of article list */ struct newsgroup *ng_forw; /* pointers to other newsgroups */ struct newsgroup *ng_back; }; /* * An article. */ struct article { short a_read; /* true if article has been read */ short a_select; /* selected for reading */ short a_screen; /* screen number */ int a_id; /* article id */ char a_date[40]; /* date of article */ char a_from[80]; /* address of author */ char a_subj[80]; /* subject of article */ char a_keywds[80]; /* keywords */ char a_ngs[80]; /* newsgroups article applies */ int a_lines; /* lines in article */ int a_text; /* offset where text begins */ struct article *a_forw; struct article *a_back; }; struct angent *angalloc(); struct inotab *italloc(); struct newsgroup *newsgroups; /* newsgroup list */ struct newsgroup *getng(); struct newsgroup *prevng(); struct newsgroup *nextng(); struct newsgroup *curng; /* the \`\`current'' newsgroup */ struct newsgroup *lastng; /* the last newsgroup */ struct article *aralloc(); struct article *prevarticle(); /* * Article macros. */ #define toarticle(n) tputs(tgoto(CM, 27, n + 1), 1, putch) #define openarticle(n) fopen(idtoname(n), "r") /* * Miscellaneous macros. */ #define CTRL(c) ('c' & 037) ! echo x - tty.c cat << ! > tty.c /* * tty -- Termcap interface. */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sgtty.h> #include "tty.h" int mode; int speeds[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200 }; char tbuf[BUFSIZ]; char clearbuf[100]; char *clearptr; char *term; char *junk; char *BC; char *BELL = "\007"; char *CE; char *CL; char *DO; char *SE; char *SO; char *SP; char *UP; char *VE; char *VS; char *tgetstr(); char *getenv(); struct sgttyb tty; /* * Get and initialize terminal characteristics. */ initerm() { if (ioctl(0, TIOCGETP, &tty) < 0) { printf("rn: Cannot ioctl ttychars\n"); exit(1); } if ((term = getenv("TERM")) == NULL) { printf("rn: Terminal type unknown\n"); exit(1); } if (tgetent(tbuf, term) != 1) { printf("rn: Cannot acquire termcap entry for %s\n", term); exit(1); } clearptr = clearbuf; if ((ttylines = tgetnum("li", &clearptr)) < 0) ttylines = 24; if ((ttycols = tgetnum("co", &clearptr)) < 0) ttycols = 80; CL = tgetstr("cl", &clearptr); CE = tgetstr("ce", &clearptr); DO = tgetstr("do", &clearptr); CM = tgetstr("cm", &clearptr); SP = tgetstr("nd", &clearptr); SO = tgetstr("so", &clearptr); SE = tgetstr("se", &clearptr); UP = tgetstr("up", &clearptr); VE = tgetstr("ve", &clearptr); VS = tgetstr("vs", &clearptr); if (!DO) DO = "\012"; if (!CL || !CE || !CM) { printf("rn: %s needs addressible cursor\n", term); exit(1); } if (tgetflag("bs") != 1) BC = tgetstr("bc", &clearptr); else BC = "\010"; } setty() { tty.sg_flags &= ~ECHO; tty.sg_flags |= RAW; ioctl(0, TIOCSETP, &tty); if (VS) tput(VS); fflush(stdout); } unsetty() { tty.sg_flags |= ECHO; tty.sg_flags &= ~RAW; ioctl(0, TIOCSETP, &tty); if (VE) tput(VE); fflush(stdout); } raw() { tty.sg_flags |= RAW; ioctl(0, TIOCSETP, &tty); } cooked() { tty.sg_flags &= ~RAW; ioctl(0, TIOCSETP, &tty); } echo() { tty.sg_flags |= ECHO; ioctl(0, TIOCSETP, &tty); } noecho() { tty.sg_flags &= ~ECHO; ioctl(0, TIOCSETP, &tty); } startvisual() { if (VS) tput(VS); fflush(stdout); } endvisual() { if (VE) tput(VE); fflush(stdout); } ttybaud() { return(tty.sg_ospeed ? speeds[tty.sg_ospeed] : -1); } putch(c) { putchar(c); } inverseon() { tput(SO); } inverseoff() { tput(SE); } clearscreen() { tput(CL); } beep() { tput(BELL); } upcur() { tput(UP); } downcur() { tput(DO); } rightcur() { tput(SP); } leftcur() { tput(BC); } clearline() { tput(CE); } ! echo x - tty.h cat << ! > tty.h /* * tty.h */ int putch(); int ttylines; int ttycols; char *CM; /* * Termlib macros. */ #define tgo(l, c) tputs(tgoto(CM, c, l), 1, putch) #define tput(s) tputs(s, 1, putch) ! -- uucp: {ucbvax,cepu}!ucsbcsl!discolo arpa: ucsbcsl!discolo@BERKELEY csnet: discolo@ucsb USMail: U.C. Santa Barbara Department of Computer Science Santa Barbara, CA 93106 GTE: (805) 961-4178