bobm@rtech.UUCP (Bob Mcqueer) (01/01/87)
Bob McQueer {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm cut here ----------------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # vn.c # config.h # head.h # reader.h # tty.h # tune.h # vn.h # This archive created: Thu Jan 1 11:15:25 1987 export PATH; PATH=/bin:$PATH echo shar: extracting "'vn.c'" '(25993 characters)' if test -f 'vn.c' then echo shar: will not over-write existing file "'vn.c'" else cat << \SHAR_EOF > 'vn.c' /* vn news reader for visual page oriented display of news aimed at scanning large numbers of articles. Original program by Bob McQueer in several versions 1983-1986. Released into the public domain in 1986. While no copyright notice appears, the original author asks that a history of changes crediting the proper people be maintained. Bob McQueer {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm History: (bobm@rtech) 5/86 - first "public" version (bobm@rtech) 12/86 - version incorporates: bug fixes: str_store NULL string bug not picking up first article in newsgroup RESTART terminal reset for exit to editor. skip whitespace in "empty" digest lines while unpacking DISTRIBUTION line in followups. :100%: prompt on last line in reader. interpretation of multiple negations -w -t options. Many thanks to several people who noted the first two bugs as well as fixes. Thank you to Karl Williamson for helpful information tracking down the "concept terminal" bug (RESTART). SYSV ifdef's adapted from those done by Larry Tepper at ATT Denver - sent in by Karl Williamson, drutx!khw. Many people submitted SYSV ifdef's - thank you all. print capability from reader from Karl Williamson, drutx!khw Changes to use alternate header lines for mail, from changes by Andy Marrinson, andy@icom.UUCP (ihnp4!icom!andy). Ifdef'ed to allow local configuration (bobm@rtech). "author_copy" file for followups, prevention of multiple "re: "'s, insert blank line and original author line before excerpted text from Andy Marrinson, andy@icom.UUCP Search string capability in reader, from Lawrie Brown, seismo!munnari!cdsadfa.oz!lpb (Australia). Somewhat modified by interaction with the :100%: bug. Arrow key support, adapted from changes by Lawrie Brown. Modified to simply not allow control keys for arrows (allowing SOME controls is too prone to problems, esp. with .vnkey), and to allow the PAGEARROW ifdef (bobm@rtech) prevention of followups to "mod" and "announce", from Lawrie Brown. OLDRC ifdef adapted from changes by Lawrie Brown. ADDRMUNGE added to allow OZ domain addressing changes from Australia to be grafted back in, and provide a hook for anybody else wanting to do something similar. Bob McQueer, bobm@rtech: a menu selection from the % command to jump to a new newsgroup linked list on hash table - no longer a compiled in limit for number of newsgroups .vnkey keystroke mapping file. options to get the % command list on entry, and to change how unsubscribed groups are handled for updates. allow configurable use of vs / ve pair for terminal handling. Known bugs: non-erasure of stuff on prompt line when the new string includes an escape sequence (like PS1 maybe) because it doesn't realize that the escape sequence won't overprint the existing stuff control-w and update on exit may not update pages which have been scanned in funny orders by jumping into the middle of groups inaccurate numbers on '%' command results - reflect ranges, not actual numbers of articles. no arrow keys recognized which don't begin with <escape> doesn't know about the version 2.11 'm' in active list, or use the 'y' / 'n' either. crash due to embedding $\(\) type substring specifiers in regular expressions. Obscure and hard to fix in a proper and portable way. */ #include <stdio.h> #include <setjmp.h> #include "config.h" #include "tty.h" #include "vn.h" /* UNIX error number */ extern int errno; extern NODE **Newsorder; extern char Erasekey, Killkey; extern int Rot; extern char *Ps1,*Printer; extern char *Orgdir,*Savefile,*Savedir; extern int Ncount, Cur_page, Lrec, L_allow, C_allow; extern int Headflag; extern PAGE Page; extern int Digest; extern char *No_msg; extern char *Hdon_msg; extern char *Hdoff_msg; extern char *Roton_msg; extern char *Rotoff_msg; extern char Cxitop[], Cxptoi[]; extern char *Aformat; extern char *Contstr; extern char *Kl,*Kr,*Ku,*Kd; extern int Nounsub, Listfirst; static int C_info; static int Dskip, Drec; static char *Unsub_msg = "Unsubscribed"; static char *Egroup_msg = "Entire newsgroup"; /* Help message table. Character for command, plus its help message. Table order is order of presentation to user. */ static struct HELPTAB { char cmd, *msg; int dig; char *amsg; } Helptab [] = { { QUIT, "quit", 1, NULL}, { UP, "(or up arrow) move up [number of lines]", 1, NULL}, { DOWN, "(or down arrow) move down [number of lines]", 1, NULL}, #ifdef PAGEARROW { BACK, "(or left arrow) previous page [number of pages]", 1, NULL}, { FORWARD, "(or right arrow) next page [number of pages]", 1, NULL}, #else { BACK, "previous page [number of pages]", 1, NULL}, { FORWARD, "next page [number of pages]", 1, NULL}, #endif { DIGEST, "unpack digest", 1, "exit digest"}, { READ, "read article [number of articles]", 1, NULL}, { ALTREAD, "read article (alternate 'r')", 1, NULL}, { READALL, "read all articles on page", 1, NULL}, { READSTRING, "specify articles to read", 1, NULL}, { SAVE, "save or pipe article [number of articles]", 1, NULL}, { SAVEALL, "save or pipe all articles on page", 1, NULL}, { SAVESTRING, "specify articles to save", 1, NULL}, { ALTSAVE, "specify articles to save (alternate ctl-s)", 1, NULL}, { PRINT, "print article [number of articles]", 1, NULL}, { PRINTALL, "print all article on page", 1, NULL}, { PRINTSTRING, "specify articles to print", 1, NULL}, { UPDATE, "update .newsrc status to cursor", 0, NULL}, { UPALL, "update .newsrc status for whole newsgroup", 0, NULL}, { UPSEEN, "update .newsrc status for all pages displayed", 0, NULL}, { ORGGRP, "recover original .newsrc status for newsgroup", 0, NULL}, { ORGSTAT, "recover all original .newsrc status", 0, NULL}, { SSTAT, "display count of groups and pages - shown and total", 0, NULL}, { GRPLIST, "list newsgroups with new article, updated counts", 0, NULL}, { NEWGROUP, "specify newsgroup to display and/or resubscribe to", 1, NULL}, { UNSUBSCRIBE, "unsubscribe from group", 0, NULL}, { MARK, "mark/unmark article [number of articles]", 1, NULL}, { ART_MARK, "mark/unmark article [number of articles]", 1, NULL}, { UNMARK, "erase marks on articles", 1, NULL}, { HEADTOG, "toggle flag for display of headers when reading", 1, NULL}, { SETROT, "toggle rotation for reading", 1, NULL}, { REDRAW, "redraw screen", 1, NULL}, { UNESC, "escape to UNIX to execute a command", 1, NULL}, { HELP, "show this help menu", 1, NULL} }; main(argc,argv) int argc; char **argv; { /* initialize environment variables, scan .newsrc file, using any command line options present. */ term_set (START); envir_set (); sig_set (BRK_IN); scan_newsrc (argc-1,argv+1); tty_set (BACKSTOP); if (Lrec >= 0) session (); else { new_groups (); fprintf (stderr,"\nNo News\n"); } tty_set (COOKED); wr_newsrc (); term_set (STOP); } /* main session handler processing input commands locals: count - count attached to command highrec - highest line on current page crec - current line NOTE: this is where a setjmp call is made to set the break reentry location. Keep the possible user states in mind. */ session () { char alist [RECLEN], c; int newg, i, j, count, highrec, crec; jmp_buf brkbuf; tty_set (RAWMODE); newg = new_groups(); find_page (0); Digest = 0; /* reentry point for break from within session interaction */ setjmp (brkbuf); sig_set (BRK_SESS,brkbuf); Headflag = FALSE; Rot = 0; /* done this way so that user gets "really quit?" break treatment */ if (newg > 0) { printf ("\n%s",Contstr); getnoctl(); newg = 0; } /* list preview option - clear after first time for long jumps */ if (Listfirst) { /* tot_list settings will be overwritten in this case */ tot_list(&crec,&highrec); Listfirst = 0; } /* if breaking from a digest, recover original page */ if (Digest) { find_page(Cur_page); Digest = 0; } show (); crec = RECBIAS; highrec = Page.h.artnum + RECBIAS; term_set (MOVE,0,crec); /* handle commands until QUIT, update global/local status and display for each. */ for (count = getkey(&c); c != QUIT; count = getkey(&c)) { for (i=0; i < (j = sizeof(Helptab)/sizeof(struct HELPTAB)); ++i) if (Helptab[i].cmd == c) break; if (i >= j || (Digest && !Helptab[i].dig)) { preinfo (UDKFORM,Cxptoi[HELP]); term_set (MOVE, 0, crec); continue; } switch (c) { case HEADTOG: if (Headflag) { Headflag = FALSE; prinfo (Hdoff_msg); } else { Headflag = TRUE; prinfo (Hdon_msg); } term_set (MOVE,0,crec); break; case SETROT: if (Rot == 0) { Rot = 13; prinfo (Roton_msg); } else { Rot = 0; prinfo (Rotoff_msg); } term_set (MOVE,0,crec); break; case SSTAT: count_msg (); term_set (MOVE,0,crec); break; case GRPLIST: tot_list (&crec,&highrec); show(); term_set (MOVE,0,crec); break; case REDRAW: show(); term_set (MOVE,0,crec); break; case UNSUBSCRIBE: (Page.h.group)->state &= ~FLG_SUB; wr_newsrc (); prinfo (Unsub_msg); term_set (MOVE,0,crec); break; case UPDATE: (Page.h.group)->rdnum = Page.b[crec-RECBIAS].art_id; wr_show (); wr_newsrc(); term_set (MOVE,0,crec); break; case UPALL: (Page.h.group)->rdnum = (Page.h.group)->art; wr_newsrc(); wr_show(); prinfo (Egroup_msg); term_set (MOVE,0,crec); break; case ORGGRP: (Page.h.group)->rdnum = (Page.h.group)->orgrd; wr_newsrc(); wr_show(); prinfo (Egroup_msg); term_set (MOVE,0,crec); break; case UPSEEN: up_seen(); prinfo ("All pages displayed to this point updated"); wr_show(); wr_newsrc(); term_set (MOVE,0,crec); break; case ORGSTAT: for (i = 0; i < Ncount; ++i) (Newsorder[i])->rdnum = (Newsorder[i])->orgrd; prinfo ("Original data recovered"); wr_show(); wr_newsrc(); term_set (MOVE,0,crec); break; case UP: if (crec != RECBIAS) { crec -= count; if (crec < RECBIAS) crec = RECBIAS; term_set (MOVE, 0, crec); } else putchar ('\07'); break; case DOWN: if (crec < (highrec - 1)) { crec += count; if (crec >= highrec) crec = highrec - 1; term_set (MOVE, 0, crec); } else putchar ('\07'); break; case MARK: case ART_MARK: count += crec - 1; if (count >= highrec) count = highrec - 1; for (i=crec; i <= count; ++i) { if (Page.b[i-RECBIAS].art_mark != ART_MARK) Page.b[i-RECBIAS].art_mark = ART_MARK; else Page.b[i-RECBIAS].art_mark = ' '; if (i != crec) term_set (MOVE, 0, i); printf ("%c\010",Page.b[i-RECBIAS].art_mark); } if (count != crec) term_set (MOVE, 0, crec); write_page (); break; case UNMARK: for (i=0; i < Page.h.artnum; ++i) { if (Page.b[i].art_mark == ART_MARK) { Page.b[i].art_mark = ' '; term_set (MOVE, 0, i+RECBIAS); putchar (' '); } } term_set (MOVE, 0, crec); write_page (); break; case BACK: count *= -1; /* fall through */ case FORWARD: if (forward (count, &crec, &highrec) >= 0) show(); else preinfo ("No more pages"); term_set (MOVE,0,crec); break; case DIGEST: if (Digest) { Digest = 0; find_page (Cur_page); show(); crec = Drec + RECBIAS + 1; highrec = Page.h.artnum + RECBIAS; if (crec >= highrec) crec = highrec - 1; term_set (MOVE,0,crec); break; } Dskip = count - 1; Drec = crec - RECBIAS; if (digest_page(Drec,Dskip) >= 0) { show(); crec = RECBIAS; highrec = Page.h.artnum + RECBIAS; term_set (MOVE,0,crec); break; } Digest = 0; preinfo ("Can't unpack the article"); term_set (MOVE,0,crec); break; case NEWGROUP: if ((i = spec_group()) < 0) { term_set (MOVE,0,crec); break; } Digest = 0; show(); crec = RECBIAS; highrec = Page.h.artnum + RECBIAS; term_set (MOVE,0,crec); break; case SAVE: genlist (alist,crec-RECBIAS,count); savestr (alist); term_set (MOVE,0,crec); break; case SAVEALL: genlist (alist,0,L_allow); savestr (alist); term_set (MOVE,0,crec); break; case SAVESTRING: case ALTSAVE: userlist (alist); savestr (alist); term_set (MOVE,0,crec); break; case READ: case ALTREAD: genlist (alist,crec-RECBIAS,count); readstr (alist,&crec,&highrec,count); break; case READALL: genlist (alist,0,L_allow); readstr (alist,&crec,&highrec,0); break; case READSTRING: userlist (alist); readstr (alist,&crec,&highrec,0); break; case PRINT: genlist (alist,crec-RECBIAS,count); printstr (alist); term_set (MOVE,0,crec); break; case PRINTALL: genlist (alist,0,L_allow); printstr (alist); term_set (MOVE, 0, crec); break; case PRINTSTRING: userlist (alist); printstr (alist); term_set (MOVE, 0, crec); break; case HELP: help (); show (); term_set (MOVE, 0, crec); break; case UNESC: user_str (alist,Ps1,1); term_set (ERASE); fflush (stdout); tty_set (SAVEMODE); if (chdir(Orgdir) < 0) printf ("change to original directory, %s, failed",Orgdir); else { system (alist); tty_set (RESTORE); term_set (RESTART); } printf (Contstr); getnoctl (); cd_group (); show (); term_set (MOVE, 0, crec); break; default: printex ("Unhandled key: %c", c); break; } } Digest = 0; for (i=0; i < Ncount; ++i) { if ((Newsorder[i])->rdnum < (Newsorder[i])->pgrd) break; } if (i < Ncount) { user_str (alist,"Some displayed pages not updated - update ? ",1); if (alist[0] == 'y') up_seen(); } sig_set (BRK_OUT); } /* ** update status of Newsgroups to all seen pages */ up_seen() { int i; for (i = 0; i < Ncount; ++i) { if (Nounsub && ((Newsorder[i])->state & FLG_SUB) == 0) { (Newsorder[i])->rdnum = (Newsorder[i])->art; continue; } if ((Newsorder[i])->rdnum < (Newsorder[i])->pgrd) (Newsorder[i])->rdnum = (Newsorder[i])->pgrd; } } /* count_msg displays count information */ count_msg () { int i, gpnum, gscan, gpage; unsigned long mask; gpnum = 1; for (gscan = gpage = i = 0; i<Ncount; ++i) { if (((Newsorder[i])->state & FLG_PAGE) != 0) { if (((Newsorder[i])->pnum + (Newsorder[i])->pages - 1) < Cur_page) ++gpnum; ++gpage; for (mask=1; mask != 0L; mask <<= 1) if (((Newsorder[i])->pgshwn & mask) != 0L) ++gscan; } } prinfo (CFORMAT,Cur_page+1,Lrec+1,gscan,gpnum,gpage); } /* forward utility handles paging to allow it to happen globally. (from readstr, for instance) */ forward (count, crec, highrec) int count, *crec, *highrec; { if (!Digest) { if ((count < 0 && Cur_page <= 0) || (count > 0 && Cur_page >= Lrec)) return (-1); Cur_page += count; if (Cur_page < 0) Cur_page = 0; if (Cur_page > Lrec) Cur_page = Lrec; find_page (Cur_page); *crec = RECBIAS; *highrec = Page.h.artnum + RECBIAS; return (0); } /* ** in digests, paging past the end of the digest returns to ** page extracted from. */ if (Dskip > 0 && (Dskip + count*L_allow) < 0) Dskip = 0; else Dskip += count * L_allow; find_page (Cur_page); if (Dskip >= 0) { if (digest_page(Drec,Dskip) >= 0) { *crec = RECBIAS; *highrec = Page.h.artnum + RECBIAS; return (0); } } Digest = 0; *crec = Drec + RECBIAS + 1; *highrec = Page.h.artnum + RECBIAS; if (*crec >= *highrec) *crec = *highrec - 1; return (0); } /* error/abnormal condition cleanup and abort routine pass stack to printf */ printex (s,a,b,c,d,e,f) char *s; long a,b,c,d,e,f; { static int topflag=0; if (topflag == 0) { ++topflag; term_set (STOP); tty_set (COOKED); fflush (stdout); fprintf (stderr,s,a,b,c,d,e,f); fprintf (stderr," (error code %d)\n",errno); exit (1); } else fprintf (stderr,s,a,b,c,d,e,f); } /* getkey obtains user keystroke with count from leading numerics, if any. Picks up arrow key sequences and maps them to other keys. Also translates character through Cxitop array since this routine is only used in session loop. Saves untranslating arrow keys. */ getkey (c) char *c; { int i, j; static char ckseq[32]; /* Check for leading count */ for (i = 0; (*c = getchar() & 0x7f) >= '0' && *c <= '9'; i = i * 10 + *c - '0') ; /* @#$!!! flakey front ends that won't map newlines in raw mode */ if (*c == '\012' || *c == '\015') *c = '\n'; /* @#$!!! flakey terminals which send control sequences for cursors! */ if( *c == '\033' ) { /* ** Check if part of cursor key input sequence ** (pitch unknown escape sequences) */ j = 0; ckseq[j] = *c; ckseq[j+1] = '\0'; while(*c == Ku[j] || *c == Kd[j] || *c == Kl[j] || *c == Kr[j]) { if( strcmp(ckseq, Ku) == 0 ) { *c = UP; break; } if( strcmp(ckseq, Kd) == 0 ) { *c = DOWN; break; } #ifdef PAGEARROW if( strcmp(ckseq, Kl) == 0 ) { *c = BACK; break; } if( strcmp(ckseq, Kr) == 0 ) { *c = FORWARD; break; } #else if( strcmp(ckseq, Kl) == 0 ) { *c = UP; break; } if( strcmp(ckseq, Kr) == 0 ) { *c = DOWN; break; } #endif *c = (getchar() & 0x7f); ckseq[++j] = *c; ckseq[j+1] = '\0'; } } else *c = Cxitop[*c]; if (i <= 0) i = 1; return (i); } /* get user key ignoring most controls */ getnoctl () { char c; while ((c = getchar() & 0x7f) < ' ' || c == '\177') { if (c == '\015' || c == '\012') c = '\n'; if (c == '\n' || c == '\b' || c == '\t') return (c); } return ((int) c); } /* generate list of articles on current page, count articles, starting with first. */ genlist (list,first,count) char *list; int first,count; { int i; for (i=first; i < Page.h.artnum && count > 0; ++i) { sprintf (list,"%d ",Page.b[i].art_id); list += strlen(list); --count; } } /* send list of articles to printer */ printstr (s) char *s; { char *ptr, cmd [RECLEN], *strpbrk(); prinfo ("preparing print command ...."); for (ptr = s; (ptr = strpbrk(ptr, LIST_SEP)) != NULL; ++ptr) *ptr = ' '; while (*s == ' ') ++s; if (Digest) dig_list (s); if (*s != '\0') { sprintf (cmd,"%s %s 2>/dev/null",Printer,s); if (system (cmd) == 0) prinfo ("Sent to printer"); else preinfo ("Print failed"); } else preinfo (No_msg); if (Digest) dig_ulist (s); } /* concatenate articles to save file with appropriate infoline messages. prompt for save file, giving default. If save file begins with "|" handle as a filter to pipe to. NOTE - every user specification of a new Savefile "loses" some storage, but it shouldn't be a very great amount. */ savestr (s) char *s; { char *ptr, cmd [RECLEN], newfile [MAX_C+1], prompt[MAX_C]; char *strtok(), *strpbrk(), *str_store(); for (ptr = s; (ptr = strpbrk(ptr, LIST_SEP)) != NULL; ++ptr) *ptr = ' '; while (*s == ' ') ++s; if (Digest) dig_list (s); if (*s != '\0') { sprintf (prompt,SAVFORM,Savefile); user_str (newfile,prompt,1); ptr = newfile; if (*ptr == '|') { sprintf(cmd,"cat %s %s",s,ptr); term_set (ERASE); fflush (stdout); tty_set (SAVEMODE); system (cmd); tty_set (RESTORE); printf (Contstr); getnoctl (); show (); } else { prinfo ("saving .... "); if (*ptr == '\0') ptr = Savefile; else Savefile = str_store(ptr); if (*ptr != '/' && *ptr != '$') sprintf(cmd,"cat %s >>%s/%s 2>/dev/null",s,Savedir,ptr); else sprintf(cmd,"cat %s >>%s 2>/dev/null",s,ptr); if (system (cmd) == 0) prinfo ("Saved"); else preinfo ("Could not append save file"); } } else preinfo (No_msg); if (Digest) dig_ulist (s); } /* basic page display routine. erase screen and format current page */ show () { int i; unsigned long mask; char helpstr[40]; term_set (ERASE); C_info = 0; i = Cur_page - (Page.h.group)->pnum + 1; if (Digest) printf (DHFORMAT,Page.h.name); else printf (HFORMAT,Page.h.name,i,(Page.h.group)->pages); mask = 1L << (i-1); (Page.h.group)->pgshwn |= mask; mask = 1; for (--i; i > 0 && (mask & (Page.h.group)->pgshwn) != 0 ; --i) mask <<= 1; if (i <= 0) (Page.h.group)->pgrd = Page.b[(Page.h.artnum)-1].art_id; for (i=0; i < Page.h.artnum; ++i) { if (Digest) { printf(Aformat,Page.b[i].art_mark,ART_UNWRITTEN,Page.b[i].art_id); printf("%s",Page.b[i].art_t); continue; } if ((Page.h.group)->rdnum >= Page.b[i].art_id) printf(Aformat,Page.b[i].art_mark,ART_WRITTEN,Page.b[i].art_id); else printf(Aformat,Page.b[i].art_mark,ART_UNWRITTEN,Page.b[i].art_id); printf("%s",Page.b[i].art_t); } sprintf(helpstr,HELPFORM,Cxptoi[HELP]); if (!Digest && ((Page.h.group)->state & FLG_SUB) == 0) prinfo ("%s, %s",Unsub_msg,helpstr); else prinfo (helpstr); } /* update written status marks on screen */ wr_show () { int i,row; char c; row = RECBIAS; for (i=0; i < Page.h.artnum; ++i) { term_set (MOVE,WRCOL,row); if ((Page.h.group)->rdnum >= Page.b[i].art_id) c = ART_WRITTEN; else c = ART_UNWRITTEN; printf("%c",c); ++row; } } /* obtain user input of group name, becomes current page if valid. returns -1 or page number. calling routine does the show, if needed */ spec_group () { char nbuf [MAX_C + 1]; NODE *p, *hashfind(); user_str(nbuf,"Newsgroup ? ",1); if (*nbuf == '\0' || (p = hashfind(nbuf)) == NULL) { preinfo ("Not a newsgroup"); return (-1); } if ((p->state & FLG_PAGE) == 0) { if ((p->state & FLG_SUB) == 0) { p->state |= FLG_SUB; wr_newsrc (); prinfo ("Not subscribed: resubscribed for next reading session"); } else prinfo ("No news for that group"); return (-1); } if ((p->state & FLG_SUB) == 0) { p->state |= FLG_SUB; wr_newsrc (); } find_page (p->pnum); return (p->pnum); } /* obtain user input with prompt p. Optionally on info line. handle erase and kill characters, suppresses leading white space. */ user_str (s,p,iline) char *s; char *p; int iline; { int i,idx; if (iline) { prinfo ("%s",p); idx = C_info; } else { printf ("%s",p); idx = strlen(p); } for (i=0; idx < C_allow && (s[i] = getchar() & 0x7f) != '\012' && s[i] != '\015'; ++i) { if (s[i] == Erasekey) { if (i > 0) { term_set (RUBSEQ); i -= 2; --idx; } continue; } if (s[i] == Killkey) { prinfo ("%s",p); i = -1; continue; } if ((s[i] == ' ' || s[i] == '\t') && i == 0) { i = -1; continue; } ++idx; putchar (s[i]); } if (iline) C_info = idx; s[i] = '\0'; } /* print something on the information line, clearing any characters not overprinted. preinfo includes reverse video and a bell for error messages. */ preinfo (s,a,b,c,d,e,f) { int l; char buf[RECLEN]; term_set (MOVE,0,INFOLINE); putchar ('\07'); term_set (ONREVERSE); sprintf (buf,s,a,b,c,d,e,f); printf (" %s ",buf); term_set (OFFREVERSE); l = strlen(buf) + 2; if (l < C_info) term_set (ZAP,l,C_info); C_info = l; fflush(stdout); } prinfo (s,a,b,c,d,e,f) char *s; long a,b,c,d,e,f; { int l; char buf[RECLEN]; term_set (MOVE,0,INFOLINE); sprintf (buf,s,a,b,c,d,e,f); printf ("%s",buf); l = strlen(buf); if (l < C_info) term_set (ZAP,l,C_info); C_info = l; fflush(stdout); } /* help menu */ help () { int i,lcount,lim; term_set (ERASE); lim = L_allow + RECBIAS - 2; printf("%s\n",HELP_HEAD); lcount = HHLINES; for (i=0; i < (sizeof(Helptab))/(sizeof(struct HELPTAB)); ++i) { if (Digest && !(Helptab[i].dig)) continue; ++lcount; if (Digest && Helptab[i].amsg != NULL) h_print (Cxptoi[Helptab[i].cmd],Helptab[i].amsg); else h_print (Cxptoi[Helptab[i].cmd],Helptab[i].msg); if (lcount >= lim) { printf ("\n%s",Contstr); getnoctl (); term_set (MOVE,0,lim+1); term_set (ZAP,0,strlen(Contstr)); term_set (MOVE,0,lim-1); putchar ('\n'); lcount = 0; } } if (lcount > 0) { printf ("\n%s",Contstr); getnoctl (); } } /* h_print prints a character and a legend for a help menu. */ h_print(c,s) char c,*s; { if (strlen(s) > (C_allow - 14)) s [C_allow - 14] = '\0'; if (c > ' ' && c != '\177') printf (" %c - %s\n",c,s); else { switch (c) { case '\177': printf (" <delete> - %s\n",s); break; case '\040': printf (" <space> - %s\n",s); break; case '\033': printf (" <escape> - %s\n",s); break; case '\n': printf (" <return> - %s\n",s); break; case '\t': printf (" <tab> - %s\n",s); break; case '\b': printf (" <back sp> - %s\n",s); break; case '\f': printf ("<formfeed> - %s\n",s); break; case '\07': printf (" <bell> - %s\n",s); break; case '\0': printf (" <null> - %s\n",s); break; default: if (c < '\033') { c += 'a' - 1; printf(" control-%c - %s\n",c,s); } else printf(" %c0%o - %s\n",'\\',(int) c,s); break; } } } tot_list (rec,hirec) int *rec; int *hirec; { int i,max,len; char c; char ff[MAX_C+1]; term_set (ERASE); for (max=i=0; i < Ncount; ++i) { if ((Newsorder[i])->pages == 0) continue; if ((len = strlen((Newsorder[i])->nd_name)) > max) max = len; } sprintf (ff,"%%4d %%%ds: %%3d new %%3d updated\n",max); for (len=i=0; i < Ncount; ++i) { if ((Newsorder[i])->pages == 0) continue; printf (ff, i, (Newsorder[i])->nd_name, (Newsorder[i])->art - (Newsorder[i])->orgrd, (Newsorder[i])->rdnum - (Newsorder[i])->orgrd); ++len; if (len == L_allow && i < (Ncount-1)) { printf("\nr - return, n - new group, other to continue ... "); if ((c = getnoctl()) == 'r' || c == 'n') break; printf ("\n\n"); len = 0; } } if (i >= Ncount) { printf("n - new group, other to return ... "); c = getnoctl(); } if (c == 'n') { printf("\n"); user_str(ff,"Newsgroup number ? ",0); i = atoi(ff); if (i < 0) i = 0; if (i >= Ncount) i = Ncount-1; find_page((Newsorder[i])->pnum); *rec = RECBIAS; *hirec = Page.h.artnum + RECBIAS; } } SHAR_EOF fi # end of overwriting check echo shar: extracting "'config.h'" '(3422 characters)' if test -f 'config.h' then echo shar: will not over-write existing file "'config.h'" else cat << \SHAR_EOF > 'config.h' /* ** vn news reader. ** ** config.h - system configuration parameters ** ** see copyright disclaimer / history in vn.c source file */ #define DEF_ED "/usr/ucb/vi" /* editor to use if no EDITOR variable */ #define DEF_PS1 "$ " /* ! command prompt if no PS1 */ #define DEF_SAVE "vn.save" /* save file */ /* ** mailer interface. If INLETTER is defined, a "To:" line will be ** placed in the file being editted by the user. Otherwise, the ** address will be an argument on the mailer's command line, with the ** user prompted for possible correction. In either case, "Subject: " ** is included in the file. ** ** If MAILSMART is set, alternate header lines will be used instead of ** the "Path: " line to determine the address because we assume the mailer ** is intelligent enough to do routing. ** ** If ADDRMUNGE is set, it is the name of a local routine which will be ** called to make further address modifications before the address is used. ** It will be passed the address string, which it may modify. RECLEN bytes ** of storage are available at the address passed. It will only be called ** once for a given address. ** ** DEF_MAIL is the mailer used in absence of MAILER variable. */ #define INLETTER #define MAILSMART #define DEF_MAIL "/usr/lib/sendmail -t" #define DEF_PRINT "/usr/ucb/lpr" /* print command */ #define DEF_POST "/usr/lib/news/inews -h" /* followup posting command */ #define DEF_NEWSRC ".newsrc" #define DEF_CCFILE "author_copy" #define DEF_KEYXLN ".vnkey" #define SPOOLDIR "/usr/spool/news" #define ACTFILE "/usr/lib/news/active" /* ** foreground flag for messages. applies only if JOBCONTROL undefined ** (SYS V). set to 1 to see newsgroup messages, etc. during reading phase, ** 0 for "silent" operation - be warned that this may suppress some ** non-fatal diagnostic messages - find all references to fgprintf to ** see what is suppressed. */ #define NOJOB_FG 1 /* ** arrow key treatment. If PAGEARROW is defined, right and left arrow ** keys will be synonyms for <return> (next-page) and <backspace> (previous). ** Otherwise, the right arrow will function as down, and the left as up. ** Made configurable because while there is no lateral motion on the screen ** to associate with the right and left arrows, you might not like them ** changing pages on you. */ #define PAGEARROW /* ** if USEVS is defined, terminal initialization / exit for vn will include the ** "vs"/"ve" pair as well as "ti"/"te". This doesn't matter on a lot of ** terminals, but may make vn display behaviour closer to "vi" since vs/ve ** is vi's "visual mode" sequence. For instance, I believe the commonly ** used definitions for these strings on multi-page concepts allows the ** program to run in the first page of the terminal, preserving the more ** recent part of your session on exit ** ** #define USEVS */ /* ** temp file name template for mktemp(). Used in tmpnam.c, does not apply ** if you use a system library tmpnam(). BE CAREFUL - VNTEMPNAME MUST ** contain a string of 6 X's for mktemp() (actually, a place where 6 X's ** are intended to go). TMP_XOFFSET absolutely MUST point to the first of ** the X's. Yes, writing into a literal string is sloppy. To the best of ** my knowledge, tmpnam.c is the only place you'll find vn code doing it. ** We make this configurable in case you want temp files somewhere else. */ #define VNTEMPNAME "/usr/tmp/vnXXXXXX" #define TMP_XOFFSET 11 SHAR_EOF fi # end of overwriting check echo shar: extracting "'head.h'" '(839 characters)' if test -f 'head.h' then echo shar: will not over-write existing file "'head.h'" else cat << \SHAR_EOF > 'head.h' /* ** vn news reader. ** ** head.h - header line strings and lengths ** ** see copyright disclaimer / history in vn.c source file */ /* header lines and associated lengths. Strings should actually be used once in strings.c. */ #define RHEAD "References: " #define RHDLEN 12 #define MHEAD "Message-ID: " #define MHDLEN 12 #define PHEAD "Path: " #define PHDLEN 6 #define DHEAD "Date: " #define DHDLEN 6 #define RTHEAD "Reply-To: " #define RTHDLEN 10 #define TOHEAD "To: " #define TOHDLEN 4 #define FHEAD "From: " #define FHDLEN 6 #define FTHEAD "Followup-To: " #define FTHDLEN 13 #define DISHEAD "Distribution: " #define DISHDLEN 14 #define THEAD "Subject: " #define THDLEN 9 #define LHEAD "Lines: " #define LHDLEN 7 #define NHEAD "Newsgroups: " #define NHDLEN 12 #define CHFIRST "FSL" /* first char's of those used in page display */ SHAR_EOF fi # end of overwriting check echo shar: extracting "'reader.h'" '(1498 characters)' if test -f 'reader.h' then echo shar: will not over-write existing file "'reader.h'" else cat << \SHAR_EOF > 'reader.h' /* ** vn news reader. ** ** reader.h - article reading interface definitions ** ** see copyright disclaimer / history in vn.c source file */ #define PAGE_MID ":more (%2d%%):" #define PAGE_NEXT ":next article:" #define PAGE_END ":end:" #define PAGE_NO ":?:" #define PPR_MAX 18 /* maximum length of PAGE prompts */ /* reading commands: no control chars, add help message to helppg SAVE, PRINT, HEADTOG and SETROT are also recognized */ #define HPG_HEAD "toggle header print flag" #define HPG_ROT "toggle rotation" #define HPG_SAVE "save article in a file" #define HPG_PRINT "print article" #define PG_NEXT 'n' #define HPG_NEXT "next article, if any" #define PG_QUIT 'q' #define HPG_QUIT "quit reading articles, if any more to read" #define PG_FLIP 'Q' #define HPG_FLIP "quit reading, and turn to next page of articles" #define PG_FOLLOW 'f' #define HPG_FOLLOW "post followup to article" #define PG_REPLY 'm' #define HPG_REPLY "send mail to author of article" #define PG_HELP '?' #define HPG_HELP "see this help menu" #define PG_REWIND 'r' #define HPG_REWIND "rewind article to beginning" #define PG_WIND 'e' #define HPG_WIND "seek to end of article (to next/end prompt)" #define PG_STEP '\n' #define HPG_STEP "next line" #define PG_SEARCH '/' #define HPG_SEARCH "search for regular expression in remainder of article" #define SEARCHFORM "search pattern (%s) ? " #define HPG_DEF "\n anything else to continue normal reading" #define HPG_EDEF "\n anything else to try reading next article, if any" SHAR_EOF fi # end of overwriting check echo shar: extracting "'tty.h'" '(404 characters)' if test -f 'tty.h' then echo shar: will not over-write existing file "'tty.h'" else cat << \SHAR_EOF > 'tty.h' /* ** vn news reader. ** ** tty.h - codes for tty_set and term_set ** ** see copyright disclaimer / history in vn.c source file */ #define MOVE 100 #define ERASE 101 #define START 102 #define STOP 103 #define RUBSEQ 104 #define ZAP 105 #define ONREVERSE 106 #define OFFREVERSE 107 #define RESTART 108 #define RAWMODE 200 #define COOKED 201 #define SAVEMODE 202 #define RESTORE 203 #define BACKSTOP 204 SHAR_EOF fi # end of overwriting check echo shar: extracting "'tune.h'" '(2613 characters)' if test -f 'tune.h' then echo shar: will not over-write existing file "'tune.h'" else cat << \SHAR_EOF > 'tune.h' /* ** vn news reader. ** ** tune.h - system tuning parameters ** ** see copyright disclaimer / history in vn.c source file */ /* ** buffer size needed for tmpnam() */ #ifndef L_tmpnam #define L_tmpnam 48 #endif /* ** hash table size. linked list type of table which can expand to ** arbitrary density, including densities > 100%. Number of entries ** will be number of newsgroups in active list. This should be a prime ** number ("long division" of string modulo table size hash function). */ #define HASHSIZE 809 /* ** maximum number of columns on terminal. If made smaller, there ** will be a savings in the size of the temporary file used ** for holding displays, at the penalty of not being able to use ** the entire screen width on terminals actually possessing more ** columns than this. A block roughly on the order of this value ** times the number of lines the terminal has is maintained per page in ** the temp file, and read / written as displays are interacted ** with. MIN_C put here because MAX_C > MIN_C. MIN_C is the minumum ** number of columns for which a "reasonable" display can be produced. ** before making it smaller, look at all uses of C_allow and variable ** to see that a setting that small won't screw up array bounds. */ #define MAX_C 132 #define MIN_C 36 /* ** large size for general purpose local buffers. only used in automatic ** variable declarations. Used with fgets for buffer size when reading ** file records, to hold pathnames, commands, etc. Reduce if you blow ** out stack storage. If reduced too far, will eventually show up ** as syntax errors on reading .newsrc's and the active list, and ** scrozzled article information arising from truncated header lines. ** The reply path line will probably be the first thing to cause trouble. ** Look through the reader to find the worst case chain of declarations ** (on the order of 12 or so is probably the max). */ #define RECLEN 1200 /* ** to protect against reading entire articles to find non-existent header ** lines if an article should be hosed, only a limited number of records ** are searched. Should be big enough to get down to the last header ** entry on legitimate articles. */ #define HDR_LINES 24 /* records of article to search for header line */ /* these determine some static array sizes */ #define OPTLINES 60 /* maximum number of option lines in .newsrc */ #define NUMFILTER 24 /* max number of filters on articles */ /* block sizes for allocation routines */ #define STRBLKSIZE 1800 /* string storage allocation block */ #define NDBLKSIZE 50 /* NODE structures to allocate at a time */ SHAR_EOF fi # end of overwriting check echo shar: extracting "'vn.h'" '(4278 characters)' if test -f 'vn.h' then echo shar: will not over-write existing file "'vn.h'" else cat << \SHAR_EOF > 'vn.h' /* ** vn news reader. ** ** vn.h - general parameters ** ** see copyright disclaimer / history in vn.c source file */ #include "tune.h" #define TRUE 1 #define FALSE 0 #ifdef OLDRC #define NARGOPT "lprxfuMs" #else #define NARGOPT "lprxfuMsi" #endif #define FIL_AUTHOR 'w' #define FIL_TITLE 't' /* newsrc states */ #define NEWS_ON ':' #define NEWS_OFF '!' /* bit flags for state of newsgroup */ #define FLG_SCAN 1 #define FLG_SUB 2 #define FLG_PAGE 4 #define FLG_WRIT 8 #define FLG_SPEC 16 #define LIST_SEP " ," #define ED_MARK '>' #define ART_MARK '*' #define ART_WRITTEN '_' #define ART_UNWRITTEN ' ' #define FPFIX "Re: " #define FPFLEN 4 #define ANFORM ":%s - %c for help:\n" #define ANFLINES 1 #define NOFORM "can't open article %s\n" #define NEWGFORM "groups not mentioned in %s:\n" #define SAVFORM "save file (%s) ? " #define UDKFORM "undefined key - %c for help" #define HELPFORM "%c for help" /* page display format and dependent parameters */ #define HFORMAT "\n%s (page %d of %d):" #define DHFORMAT "\n%s (DIGEST EXTRACTION):" #define TFORMAT "%s ~ %s %s" #define AFORMAT "\n%c%c%d) " /* begin with newline - see show routine */ #define CFORMAT "page %d of %d (%d shown), newsgroup %d of %d" #define RECBIAS 2 /* lines before articles - depends on HFORMAT */ #define AFLEN 5 /* min. char. in article id - depends on AFORMAT */ #define WRCOL 1 /* column of written mark. depends on AFORMAT */ #define INFOLINE 0 /* HFORMAT TFORMAT leaves for use */ /* command characters - don't use numerics or <ESC> ALTSAVE is a hack to avoid having to use ctl-s - XON/XOFF. Wanted to preserve "s" pneumonic and lower / control /cap convention. */ #define DIGEST 'd' #define UP 'k' #define DOWN 'j' #define FORWARD '\012' #define BACK '\010' #define READ 'r' #define ALTREAD ' ' #define READALL 'R' #define READSTRING '\022' #define SAVE 's' #define SAVEALL 'S' #define SAVESTRING '\023' #define ALTSAVE '\024' #define PRINT 'p' #define PRINTALL 'P' #define PRINTSTRING '\020' #define MARK 'x' #define UNMARK 'X' #define REDRAW '\014' #define QUIT 'q' #define SSTAT '#' #define GRPLIST '%' #define ORGGRP 'o' #define ORGSTAT 'O' #define UPDATE 'w' #define UNSUBSCRIBE 'u' #define UPALL 'W' #define UPSEEN '\027' #define UNESC '!' #define NEWGROUP 'n' #define HEADTOG 'h' #define SETROT 'z' #define HELP '?' #define HELP_HEAD "[...] = effect of optional number preceding command\n\ pipes are specified by filenames beginning with |\n\ articles specified as a list of numbers, title search string, or\n\ * to specify marked articles. ! may be used to negate any\n" #define HHLINES 5 /* lines (CRs + 1) contained in HELP_HEAD */ /* state flags for handling breaks / values for sig_set calls. BRK_IN, BRK_SESS, BRK_READ and BRK_OUT are the states. All but BRK_INIT are used as calls to sig_set. BRK_RFIN indicates a return from BRK_READ to BRK_SESS (no jump location passed), */ #define BRK_INIT 0 /* initial value, indicating uncaught signals */ #define BRK_IN 1 /* in NEWSRC / article scanning phase */ #define BRK_SESS 2 /* in page interactive session */ #define BRK_READ 3 /* reading articles */ #define BRK_RFIN 4 /* finished reading, return to old mode */ #define BRK_OUT 5 /* NEWSRC updating phase */ #define BRK_PR "really quit ? " #define BRK_MSG "\nQUIT (signal %d)" /* newsgroup structure (node of hash table) next - hashtable link nd_name - name of newsgroup (key to reach node by) pnum - page number, initially used to establish Newsorder pages - number of pages for news display rdnum - articles read orgrd - original articles read number pgshwn - pages shown mask pgrd - article number on highest conecutively shown page art - articles in group state - status */ typedef struct _node { struct _node *next; char *nd_name; int pnum,pages,art,rdnum,orgrd,pgrd; unsigned long pgshwn; unsigned state; } NODE; /* newsgroup information for page display name - of group group - pointer to table entry artnum - number of articles */ typedef struct { char *name; NODE *group; int artnum; } HEAD; /* article information - id (spool) number, title string, mark, written. */ typedef struct { int art_id; char art_mark; char art_written; char art_t[MAX_C-AFLEN]; } BODY; typedef struct { HEAD h; BODY *b; } PAGE; SHAR_EOF fi # end of overwriting check # End of shell archive exit 0
bobm@rtech.UUCP (Bob Mcqueer) (01/01/87)
Bob McQueer {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm cut here ----------------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # reader.c # newsrc.c # This archive created: Thu Jan 1 11:15:26 1987 export PATH; PATH=/bin:$PATH echo shar: extracting "'reader.c'" '(18610 characters)' if test -f 'reader.c' then echo shar: will not over-write existing file "'reader.c'" else cat << \SHAR_EOF > 'reader.c' /* ** vn news reader. ** ** reader.c - article reading interface - "more" like. ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <sys/types.h> #include "tty.h" #include "config.h" #include "vn.h" #include "head.h" #include "reader.h" #define PERTAB 8 /* tab expansion factor */ #define BACKTRACK 24 extern char *Printer,*Editor,*Mailer,*Poster,*Orgdir,*Savefile,*Savedir,*Ccfile; extern int L_allow; extern int C_allow; extern int Rot; extern int Headflag; extern int Digest; extern char *No_msg; extern char *Roton_msg; extern char *Rotoff_msg; extern char *Hdon_msg; extern char *Hdoff_msg; extern char *T_head, *FT_head, *N_head, *L_head, *RT_head, *DIS_head; extern char *TO_head, *F_head, *P_head, *M_head, *R_head; extern char Cxrtoi[], Cxitor[]; static FILE *Fpread; static char *Fname; static char *Lookahead; static int Rlines; static int Hlines; #ifdef ADDRMUNGE static int Newaddr; #endif /* readstr routine is the "funnel" to the reading state, and controls signal setting. Some "session" context is passed along to allow jumping back to a different display WARNING: NOTHING below readstr should call strtok() */ readstr (s,crec,highrec,count) char *s; int *crec, *highrec; int count; { char *fnext, *strtok(); int pc; Fname = strtok(s,LIST_SEP); if (Fname != NULL) { term_set (ERASE); sig_set (BRK_READ,&Fpread); fnext = strtok(NULL,LIST_SEP); while (Fname != NULL && readfile(fnext,&pc) >= 0) { if (Digest) unlink (Fname); Fname = fnext; fnext = strtok (NULL,LIST_SEP); } if (Digest && Fname != NULL) unlink (Fname); if (pc != 0) forward (pc, crec, highrec); else { *crec += count; if (*crec >= *highrec) *crec = *highrec - 1; } sig_set (BRK_RFIN); show (); term_set (MOVE, 0, *crec); } else { preinfo ("%s",No_msg); term_set (MOVE, 0, *crec); } } /* readfile presents article: sn - name of next article, NULL if last. pages - pages to advance on return, if applicable returns 0 for "continue", <0 for "quit" */ static readfile (sn,pages) char *sn; int *pages; { FILE *fopen(); int lines,percent,artlin; long rew_pos, ftell(); char c, buf[RECLEN], mid[RECLEN], ngrp[RECLEN], dist[RECLEN]; char from[RECLEN], title[RECLEN], flto[RECLEN], reply[RECLEN]; char pstr[24], dgname[48], getpgch(), *index(), *digest_extract(); char *tgetstr(); *pages = 0; term_set(ERASE); if (Digest) { lines = atoi(Fname); if ((Fname = digest_extract(dgname,lines)) == NULL) { printf ("couldn't extract article %d",lines); return (0); } } if ((Fpread = fopen(Fname,"r")) == NULL) { printf ("couldn't open article %s",Fname); return (0); } Hlines = gethead (mid, from, title, ngrp, flto, reply, dist, &artlin); printf (ANFORM,Fname,Cxrtoi[PG_HELP]); lines = 1; rew_pos = ftell(Fpread); if (Headflag) { rewind(Fpread); Rlines = 0; } else { /* use do_out to guard against control sequences */ Rlines = Hlines; sprintf (buf,"%s%s\n",T_head,title); lines += do_out(buf,1); sprintf (buf,"%s%s\n",F_head,from); lines += do_out(buf,1); if (index(ngrp,',') != NULL) { sprintf (buf,"%s%s\n",N_head,ngrp); lines += do_out(buf,1); } if (*flto != '\0') { sprintf (buf,"%s%s\n",FT_head,flto); lines += do_out(buf,1); } printf ("%s%d\n",L_head,artlin); /* no controls */ ++lines; } /* will return out of outer while loop */ Lookahead = NULL; while (1) { /* ** lines counts folded lines from do_out. ** globals Hlines and Rlines refer to records. ** If Lookahead is null after this loop, we've ** hit EOF. */ lines += do_out(Lookahead,L_allow-lines); while (1) { if (Lookahead == NULL) { if (fgets(buf,RECLEN-1,Fpread) == NULL) break; Lookahead = buf; if (Rot != 0 && Rlines >= Hlines) rot_line(buf); ++Rlines; } if (lines >= L_allow) break; lines += do_out(buf,L_allow-lines); } if (Lookahead != NULL) { /* ** calculation is truncated rather than rounded, ** so we shouldn't get "100%". Subtract 2 for ** 1 line lookahead and empty line at beginning ** of article. */ if (Headflag) percent = ((Rlines-2)*100)/(artlin+Hlines); else percent = ((Rlines-Hlines-2)*100)/artlin; sprintf (pstr,PAGE_MID,percent); } else { if (sn == NULL) strcpy (pstr,PAGE_END); else strcpy (pstr,PAGE_NEXT); } c = getpgch(pstr,mid,from,reply,title,ngrp,flto,dist); /* handle user input: CAUTION!! return cases must close Fpread. */ switch (c) { case PG_NEXT: fclose (Fpread); return (0); case PG_FLIP: *pages = 1; /* fall through */ case PG_QUIT: fclose (Fpread); return (-1); case PG_REWIND: if (Headflag) { Rlines = 0; rewind (Fpread); } else { fseek (Fpread,rew_pos,0); Rlines = Hlines; } Lookahead = NULL; lines = 2 - RECBIAS; break; case PG_SEARCH: searcher(buf); lines = 2 - RECBIAS; lines += do_out(buf,L_allow-lines); break; case PG_WIND: fseek (Fpread,0L,2); lines = 2 - RECBIAS; Lookahead = NULL; break; case PG_STEP: if (Lookahead == NULL) { fclose (Fpread); return (0); } lines = L_allow - 1; break; default: if (Lookahead == NULL) { fclose (Fpread); return (0); } lines = 2 - RECBIAS; break; } } } /* gethead obtains subject, reply, message id, from, lines, newsgroup and followup-to lines of article for later use in mailing replies and posting followups, does not rewind, but leaves file at end of header lines. Returns number of header lines. */ static gethead (mid, from, title, ngrp, flto, reply, dist, lin) char *mid, *from, *title, *ngrp, *flto, *reply, *dist; int *lin; { int count; char buf [RECLEN], *index(); long pos,ftell(); #ifdef ADDRMUNGE Newaddr = 1; #endif *lin = 0; *dist = *mid = *from = *title = *ngrp = *flto = *reply = '\0'; /* for conditional is abnormal - expected exit is break */ for (count = 0; count < HDR_LINES && fgets(buf,RECLEN-1,Fpread) != NULL; ++count) { /* reset position and bail out at first non-header line */ if (index(buf,':') == NULL) { pos = ftell(Fpread); pos -= strlen(buf); fseek (Fpread,pos,0); break; } #ifdef MAILSMART if (strncmp(buf,RT_head,RTHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (reply,buf+RTHDLEN); continue; } #else if (strncmp(buf,P_head,PHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (reply,buf+PHDLEN); continue; } #endif if (strncmp(buf,DIS_head,DISHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (dist,buf+DISHDLEN); continue; } if (strncmp(buf,M_head,MHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (mid,buf+MHDLEN); continue; } if (strncmp(buf,F_head,FHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (from,buf+FHDLEN); continue; } if (strncmp(buf,T_head,THDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (title,buf+THDLEN); continue; } if (strncmp(buf,N_head,NHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (ngrp,buf+NHDLEN); continue; } if (strncmp(buf,FT_head,FTHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; strcpy (flto,buf+FTHDLEN); continue; } if (strncmp(buf,L_head,LHDLEN) == 0) { buf [strlen(buf)-1] = '\0'; *lin = atoi(buf+LHDLEN); continue; } } #ifdef MAILSMART if (*reply == '\0') strcpy(reply,from); #endif return (count); } /* getpgch prints prompt and gets command from user handles "mail", "save" and "followup" internally as well as flag resets. */ static char getpgch(prompt,mid,from,reply,title,ngrp,flto,dist) char *prompt, *mid, *from, *reply, *title, *ngrp, *flto, *dist; { char c; int ic; term_set (ONREVERSE); printf("%s\015",prompt); term_set (OFFREVERSE); while ((ic=getnoctl()) != EOF) { switch (c = Cxitor[ic]) { case SETROT: term_set (ZAP,0,PPR_MAX); if (Rot == 0) { Rot = 13; printf ("%s\n",Roton_msg); } else { Rot = 0; printf ("%s\n",Rotoff_msg); } if (Lookahead != NULL && Rlines > Hlines) rot_line(Lookahead); break; case HEADTOG: term_set (ZAP,0,PPR_MAX); if (Headflag) { Headflag = FALSE; printf ("%s\n",Hdoff_msg); } else { Headflag = TRUE; printf ("%s\n",Hdon_msg); } break; case PG_HELP: term_set (ZAP,0,PPR_MAX); help_pg (); break; case PG_REPLY: mail (reply,title,from); break; case PG_FOLLOW: followup (mid,title,ngrp,flto,from,dist); break; case SAVE: saver (); break; case PRINT: printr (); break; default: term_set (ZAP,0,PPR_MAX); return (c); } term_set (ONREVERSE); printf("%s\015",prompt); term_set (OFFREVERSE); } term_set (ZAP,0,PPR_MAX); return (c); } /* save article Like the savestr routine, it "loses" some storage every time the user specifies a new file, but this should not be significant */ static saver () { char *fn,cmd[RECLEN],*str_store(),*rprompt(); tty_set (SAVEMODE); sprintf (cmd,SAVFORM,Savefile); fn = rprompt(cmd,cmd); if (fn != NULL) Savefile = str_store(fn); if (*Savefile != '/' && *Savefile != '$') sprintf (cmd,"cat %s >>%s/%s",Fname,Savedir,Savefile); else sprintf (cmd,"cat %s >>%s",Fname,Savefile); system (cmd); tty_set (RESTORE); } /* invoke editor on new temp file, mail using reply line, possibly first allowing user to overide the reply (not INLETTER) */ static mail (p, t, f) char *p, *t, *f; { char *new, fn[L_tmpnam], cmd [RECLEN+60], *rprompt (); FILE *fp, *fopen(); tmpnam (fn); if ((fp = fopen(fn,"w")) == NULL) printex ("can't open %s\n",fn); if ((new = index(p, '(')) != NULL) *new = '\0'; /* a poor way of deleting comments */ #ifdef ADDRMUNGE if (Newaddr) { Newaddr = 0; ADDRMUNGE(p); } #endif if (strncmp(t, FPFIX, FPFLEN) == 0) t += FPFLEN; /* don't add multiple Re:s */ #ifdef INLETTER fprintf (fp,"%s%s\n%s%s%s\n\n%s:\n", TO_head, p, T_head, FPFIX, t, f); #else fprintf (fp,"%s%s%s\n\n%s:\n", T_head, FPFIX, t, f); #endif edcopy (fp); fclose (fp); tty_set (SAVEMODE); #ifndef INLETTER sprintf (cmd,"ADDRESS: %s\nreturn to accept, or input new address: ",p); if ((new = rprompt(cmd,cmd)) != NULL) strcpy (p,new); #endif sprintf (cmd,"%s %s", Editor, fn); chdir (Orgdir); system (cmd); cd_group (); new = rprompt ("still want to mail it ? ",cmd); if (new != NULL && (*new == 'y' || *new == 'Y')) { #ifndef INLETTER sprintf (cmd,"%s '%s' <%s", Mailer, p, fn); #else sprintf (cmd,"%s <%s", Mailer, fn); #endif system (cmd); printf ("given to mailer\n"); } else printf ("not mailed\n"); unlink (fn); tty_set (RESTORE); term_set (RESTART); } /* post a followup article, invoking editor for user after creating new temp file. remove after posting. Hack in ".followup" if posting newsgroup ends in ".general" - similar hack for preventing mod & announce groups - should really be more thorough and parse the whole string. User can change, anyway. */ static followup (m,t,n,ft,oa,dist) char *m, *t, *n, *ft, *oa, *dist; { char fn[L_tmpnam], *new, cmd [RECLEN], *rprompt(); FILE *f, *fopen(); char *rindex(); if (*ft != '\0') strcpy (cmd,ft); else strcpy (cmd,n); new = rindex(cmd,'.'); if (new != NULL && strcmp(new,".general") == 0) strcpy (new,".followup"); if ( strncmp(cmd, "mod.", 4) == 0 || strcmp(new, ".announce") == 0) { term_set (ONREVERSE); printf("Cannot post a follow-up to \"%s\", reply with mail to moderator\007\n", cmd); term_set (OFFREVERSE); return; } tmpnam (fn); if ((f = fopen(fn,"w")) == NULL) printex ("can't open %s\n",fn); if (strncmp(t, FPFIX, FPFLEN) == 0) t += FPFLEN; /* don't add multiple Re:s */ fprintf (f,"%s%s%s\n%s%s\n%s%s\n",T_head,FPFIX,t,N_head,cmd,R_head,m); if (*dist != '\0') fprintf(f,"%s%s\n",DIS_head,dist); fprintf (f,"\nin article %s, %s says:\n",m,oa); edcopy (f); fclose (f); tty_set (SAVEMODE); sprintf (cmd,"%s %s", Editor, fn); chdir (Orgdir); system (cmd); cd_group (); new = rprompt("still want to post it ? ",cmd); if (new != NULL && (*new == 'y' || *new == 'Y')) { sprintf (cmd,"%s <%s", Poster, fn); system (cmd); printf ("given to posting program\n"); save_article (fn); } else printf ("not posted\n"); unlink (fn); tty_set (RESTORE); term_set (RESTART); } /* get user buffer, return whitespace delimited token without using strtok(). buffer is allowed to overwrite prompt string. */ static char * rprompt(s,buf) char *s,*buf; { printf("%s",s); fgets (buf,RECLEN-1,stdin); while (*buf == ' ' || *buf == '\t') ++buf; if (*buf == '\n' || *buf == '\0') return (NULL); for (s = buf; *s != ' ' && *s != '\t' && *s != '\n' && *s != '\0'; ++s) ; *s = '\0'; return (buf); } /* edcopy copies article to file which user is editting for a reply or followup, so it may be referenced. It places ED_MARK in the left hand margin. */ edcopy(fp) FILE *fp; { long current; char buf[RECLEN]; int i; /* save position, rewind and skip over header lines */ current = ftell(Fpread); rewind (Fpread); for (i=0; i < HDR_LINES; ++i) { if (fgets(buf,RECLEN-1,Fpread) == NULL) break; if (strncmp(buf,L_head,LHDLEN) == 0) break; } /* if line already begins with ED_MARK, forget about the space */ while (fgets(buf,RECLEN-1,Fpread) != NULL) { if (buf[0] == ED_MARK) fprintf(fp,"%c%s",ED_MARK,buf); else fprintf(fp,"%c %s",ED_MARK,buf); } /* restore position */ fseek(Fpread,current,0); } /* help menus */ static help_pg() { h_print (Cxrtoi[PG_NEXT],HPG_NEXT); h_print (Cxrtoi[PG_QUIT],HPG_QUIT); h_print (Cxrtoi[PG_FLIP],HPG_FLIP); h_print (Cxrtoi[PG_REWIND],HPG_REWIND); h_print (Cxrtoi[PG_WIND],HPG_WIND); h_print (Cxrtoi[PG_SEARCH],HPG_SEARCH); h_print (Cxrtoi[PG_STEP],HPG_STEP); h_print (Cxrtoi[PG_REPLY],HPG_REPLY); h_print (Cxrtoi[PG_FOLLOW],HPG_FOLLOW); h_print (Cxrtoi[SAVE],HPG_SAVE); h_print (Cxrtoi[PRINT],HPG_PRINT); h_print (Cxrtoi[SETROT],HPG_ROT); h_print (Cxrtoi[HEADTOG],HPG_HEAD); h_print (Cxrtoi[PG_HELP],HPG_HELP); printf ("%s\n",HPG_DEF); } rot_line (s) unsigned char *s; { for ( ; *s != '\0'; ++s) { if (*s >= 'A' && *s <= 'Z') { *s += Rot; if (*s > 'Z') *s -= 26; continue; } if (*s >= 'a' && *s <= 'z') { *s += Rot; if (*s > 'z') *s -= 26; } } } /* ** output record. folds record to terminal width on word boundaries, ** returning number of lines output. If limit is reached, remainder ** of buffer waiting to be output is returned. Processes several ** special characters: ** form-feed - return "lim" lines so we stop on that line ** tabs - counts "expanded" width ** backspace - assumes they work, -1 width unless in first col. ** bell - pass through with zero width ** newline - end of record. ** del - turns into '_' ** other control - 'A' - 1 added ('01' = ctl-A). Makes escape = "[". ** (prevents "letter bombs" containing inappropriate control ** sequences for the terminal). ** ** Sets Lookahead pointer to remainder of line or NULL. */ static do_out(s,lim) char *s; int lim; { int len,i; char cs,*word,*start; Lookahead = NULL; if (s == NULL) return(0); len = 0; start = word = s; /* ** NOTE: "normal" return is buried inside switch, at newline ** ending record */ for (i=0; i < lim; ++i) { for ( ; len < C_allow; ++s) { switch (*s) { case '\n': *s = '\0'; /* fall through */ case '\0': printf("%s\n",start); return(i+1); case '\t': len = ((len/PERTAB)+1)*PERTAB; word = s; break; case '\b': if (len > 0) --len; break; case '\014': *s = ' '; i = lim-1; /* fall through */ case ' ': word = s+1; ++len; break; case '\177': *s = '_'; ++len; break; default: if (*s < ' ') *s += 'A' - 1; ++len; /* fall through */ case '\07': break; } } cs = *s; *s = '\0'; if ((len = strlen(word)) < BACKTRACK) { *s = cs; s = word; cs = *s; *s = '\0'; } else len = 0; printf("%s\n",start); start = s; *s = cs; } Lookahead = start; return(lim); } save_article(tempfname) char *tempfname; { FILE *in, *out; int c; time_t timenow, time(); char *today, *ctime(); if ((in = fopen(tempfname, "r")) == NULL) return; if ((out = fopen(Ccfile, "a")) == NULL) { fclose(in); return; } timenow = time((time_t)0); today = ctime(&timenow); fprintf(out,"From vn %s",today); while ((c=getc(in)) != EOF) putc(c, out); putc('\n', out); fclose(in); fclose(out); printf ("a copy has been saved in %s\n", Ccfile); } /* send article to printer */ static printr () { char cmd[RECLEN]; tty_set (SAVEMODE); printf("Sent to printer\n"); sprintf (cmd,"%s %s 2>/dev/null",Printer,Fname); system (cmd); tty_set (RESTORE); } /* search article for specified search pattern, returning the line on which it is found in buf, a null buffer otherwise. The input file will be positioned either after the line on which the pattern is found, or unaaltered if match fails. */ searcher (buf) char *buf; { static char searchstr[RECLEN] = ""; char lasave[RECLEN]; char *s, *reg, *rprompt(), *regcmp(), *regex(); long current; int orlines; /* save position, then request search pattern */ current = ftell(Fpread); orlines = Rlines; tty_set (SAVEMODE); sprintf (lasave,SEARCHFORM,searchstr); s = rprompt(lasave,lasave); tty_set (RESTORE); if (s != NULL) strcpy(searchstr, lasave); /* Now compile the search string */ if(( reg = regcmp(searchstr, (char *)0)) == NULL) { printf("Invalid search string \"%s\"\n", searchstr); *buf = '\0'; return; } /* try lookahead buffer first */ if (Lookahead != NULL && regex(reg,Lookahead) != NULL) { strcpy(buf,Lookahead); regfree(reg); return; } /* Lookahead can point into buf */ if (Lookahead != NULL) strcpy(lasave,Lookahead); /* now start reading lines, rotating if necessary and do search */ while (fgets(buf,RECLEN-1,Fpread) != NULL) { if (Rot != 0 && Rlines >= Hlines) rot_line(buf); ++Rlines; if( regex(reg, buf) != NULL ){ /* Got it */ term_set (ONREVERSE); printf("\n\tSkipping ....\n\n"); term_set (OFFREVERSE); regfree(reg); return; } } /* no dice, so restore position */ regfree(reg); term_set (ONREVERSE); printf("Cannot find string \"%s\" in remainder of article\007\n", searchstr); term_set (OFFREVERSE); fseek(Fpread,current,0); Rlines = orlines; if (Lookahead != NULL) strcpy(buf,lasave); else *buf = '\0'; return(0.0); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'newsrc.c'" '(11576 characters)' if test -f 'newsrc.c' then echo shar: will not over-write existing file "'newsrc.c'" else cat << \SHAR_EOF > 'newsrc.c' /* ** vn news reader. ** ** newsrc.c - routines to deal with the newsrc file ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <ctype.h> #include "config.h" #include "tty.h" #include "vn.h" extern NODE **Newsorder; extern char *Onews, *Newsrc; extern int Ncount, Lrec, C_allow; extern int Ntopt, Nntopt, Nwopt, Nnwopt; extern char *Topt[], *Negtopt[], *Wopt[], *Negwopt[]; extern int Nounsub, Listfirst; /* global flags signifying options set */ #define GF_ALL 1 /* -x option - scan everything */ #define GF_SPEC 2 /* -n option(s) - user specified groups */ #define GF_OVER 4 /* command line specification - overide marks */ static char *Options[OPTLINES]; static int New_idx, Max_name, Optlines; static unsigned Gflags = 0; /* routines for dealing with the .newsrc file and options */ /* command name argument is already omitted from argv argc in this routine. Only the option arguments are present. We process options before we scan the rest of .newsrc, which redoes Newsorder, ie. we don't clobber Ncount until options are processed. */ scan_newsrc (argc,argv) int argc; char **argv; { FILE *fp, *fopen(); static char marks[] = { NEWS_ON, NEWS_OFF, '\0' }; char *str_store (); int line, len, num; char buf [RECLEN], trail, optpflag, submark, *fret, *ptr, *strpbrk(), *strtok(); /* initialize hash table, open temp file, fill table with active articles */ hashinit (); fill_active (); temp_open(); if (argc > 0) { Gflags |= GF_OVER; arg_opt(argc,argv); optpflag = 'y'; } else optpflag = 'n'; if ((fp = fopen (Newsrc,"r")) == NULL) printex ("can't open %s for reading",Newsrc); Optlines = 0; for (line = 1; (fret = fgets(buf,RECLEN-1,fp)) != NULL && emptyline(buf) == 1; ++line) ; if (fret != NULL && strncmp (buf,"options",7) == 0) { Options[0] = str_store(buf); Optlines = 1; trail = buf [strlen(buf)-2]; for ( ; (fret = fgets(buf,RECLEN-1,fp)) != NULL; ++line) { if (trail != '\\' && buf[0] != ' ' && buf[0] != '\t') break; if (Optlines >= OPTLINES) printex ("%s - too many option lines (%d allowed)",Newsrc,OPTLINES); Options[Optlines] = str_store(buf); ++Optlines; if ((len = strlen(buf)) >= 2 && buf[len-2] != '\\') trail = buf[len-2]; else trail = '\0'; } } /* do the options from the newsrc file if there weren't command line args */ if (Optlines > 0 && optpflag == 'n') newsrc_opt (); Ncount = 0; for ( ; fret != NULL; ++line, fret = fgets(buf,RECLEN-1,fp)) { if (emptyline(buf) == 1) continue; if ((ptr = strpbrk(buf,marks)) == NULL) { fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n", line,Newsrc,buf); continue; } submark = *ptr; *ptr = '\0'; ++ptr; num = 0; for (ptr = strtok(ptr," ,-\n"); ptr != NULL; ptr = strtok(NULL," ,-\n")) { len = atoi (ptr); for ( ; *ptr >= '0' && *ptr <= '9'; ++ptr) ; if (*ptr != '\0' || len < num) { num = -1; fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n", line,Newsrc,buf); break; } num = len; } if (num < 0) continue; chkgroup (buf,submark,num); } fclose (fp); /* now take care of groups not specified in .newsrc */ art_active(); /* free up the option string storage */ for (num=0; num < Ntopt; ++num) regfree (Topt[num]); for (num=0; num < Nwopt; ++num) regfree (Wopt[num]); for (num=0; num < Nntopt; ++num) regfree (Negtopt[num]); for (num=0; num < Nnwopt; ++num) regfree (Negwopt[num]); Ntopt = Nwopt = Nntopt = Nnwopt = 0; } static emptyline(s) char *s; { while (isspace(*s)) ++s; if (*s == '\0') return (1); return (0); } /* fill hash table from active news group list temporarily makes "Newsorder" active list order. This is needed to be able to process options before scanning user order. */ static fill_active () { FILE *f,*fopen (); char *nread, act_rec[RECLEN], *strtok(); int num,lownum; Max_name = 0; if ((f = fopen (ACTFILE,"r")) == NULL) printex ("couldn't open %s\n",ACTFILE); while (fgets(act_rec, RECLEN-1, f) != NULL) { if (strtok (act_rec," \n") == NULL) continue; nread = strtok (NULL, " \n"); if (nread != NULL) num = atoi(nread); else num = 0; nread = strtok (NULL, " \n"); if (nread != NULL) lownum = atoi(nread); else lownum = 0; if (lownum > 0) --lownum; if (strlen(act_rec) > Max_name) Max_name = strlen(act_rec); hashenter (act_rec, num, lownum); } /* construct initial Newsorder */ entry_order(); fclose (f); } /* check active newsgroups not mentioned in NEWSRC file (FLG_SCAN not set) */ static art_active () { char act_rec[RECLEN], *strtok(); NODE *ptr,*hashfind(); FILE *f,*fopen(); if ((f = fopen (ACTFILE,"r")) == NULL) printex ("couldn't open %s\n",ACTFILE); New_idx = Ncount; while (fgets(act_rec, RECLEN-1, f) != NULL) { if (strtok (act_rec," \n") == NULL) continue; if ((ptr = hashfind (act_rec)) == NULL) printex("%s - unexpected hash table failure",act_rec); if ((ptr->state & FLG_SCAN) == 0) chkgroup (ptr->nd_name, NEWS_ON, 0); } } /* check group for new articles: s - group c - subscription indicator from NEWSRC n - number read */ static chkgroup (s,c,n) char *s,c; int n; { NODE *ptr, *hashfind(); int lold,lowart; lold = Lrec; if ((ptr = hashfind(s)) != NULL && (ptr->state & FLG_SCAN) == 0) { Newsorder [Ncount] = ptr; ++Ncount; ptr->pages = 0; ptr->state |= FLG_SCAN; if (c == NEWS_ON) ptr->state |= FLG_SUB; /* if "read" more than exist reset to zero */ if (n > ptr->art) n = 0; lowart = ptr->rdnum; if (n < ptr->rdnum) n = ptr->rdnum; ptr->orgrd = ptr->pgrd = ptr->rdnum = n; ptr->pgshwn = 0L; /* ** scan decision is rather complex, since GF_ALL setting ** overides "n" value, GF_SPEC indicates FLG_SPEC flag used. ** if GF_OVER set, FLG_SPEC overides subscription mark, else ** FLG_SPEC AND subscribed is neccesary. */ if ((Gflags & GF_SPEC) != 0) { if ((ptr->state & FLG_SPEC) == 0) c = NEWS_OFF; else { if ((Gflags & GF_OVER) != 0) c = NEWS_ON; } } if ((Gflags & GF_ALL) != 0) n = lowart; if (c == NEWS_ON && ptr->art > n) { outgroup (s,n,ptr->art); if (lold != Lrec) { ptr->pnum = lold+1; ptr->pages = Lrec - lold; ptr->state |= FLG_PAGE; } } } } /* wr_newsrc writes the .newsrc file */ wr_newsrc () { FILE *fp,*fopen(); NODE *p; char c; int i,rc; if (link(Newsrc,Onews) < 0) printex ("can't backup %s to %s before writing",Newsrc,Onews); if (unlink(Newsrc) < 0 || (fp = fopen(Newsrc,"w")) == NULL) printex ("can't open %s for writing (backed up in %s)",Newsrc,Onews); else { clearerr(fp); for (i=0; (rc = ferror(fp)) == 0 && i < Optlines; ++i) fprintf (fp,"%s",Options[i]); for (i=0; rc == 0 && i < Ncount; ++i) { p = Newsorder[i]; if ((p->state & FLG_SUB) == 0) c = NEWS_OFF; else c = NEWS_ON; #ifdef OLDRC fprintf (fp,"%s%c %d\n",p->nd_name,c,p->rdnum); #else if (p->rdnum > 0) fprintf (fp,"%s%c 1-%d\n",p->nd_name,c,p->rdnum); else fprintf (fp,"%s%c 0\n",p->nd_name,c); #endif rc = ferror(fp); } fclose (fp); if (rc != 0) printex ("write of %s failed, old copy stored in %s",Newsrc,Onews); else unlink (Onews); } } new_groups () { int i,wrem,w; char fs[24],c_end; if (New_idx >= Ncount || C_allow < (w = Max_name+1)) return (0); term_set (ERASE); printf (NEWGFORM,Newsrc); sprintf (fs,"%%-%ds%%c",Max_name); wrem = C_allow; for (i=New_idx; i < Ncount; ++i) { if ((wrem -= w) < w) { wrem = C_allow; c_end = '\n'; } else c_end = ' '; printf (fs,(Newsorder[i])->nd_name,c_end); } if ((++wrem) < C_allow) putchar ('\n'); return (i-New_idx); } /* arg_opt must be called prior to option scanning, since it uses the options array. This is a bit of a kludge, but it saves a bunch of work. NOTE - no command name argument */ static arg_opt (argc,argv) int argc; char **argv; { if (argc > OPTLINES) printex ("too many command line options (%d allowed)\n",OPTLINES); for (Optlines=0; Optlines < argc; ++Optlines) { Options[Optlines] = *argv; ++argv; } newsrc_opt(); } /* option setting routine: sets global flags: GF_ALL for -x option GF_SPEC for -n. sets up filter array for article scanning */ static newsrc_opt() { int i; char curopt,tmp[RECLEN],*tok,*strtok(),*index(); Nounsub = Listfirst = 0; Ntopt = Nwopt = Nnwopt = Nntopt = 0; curopt = '\0'; for (i=0; i < Optlines; ++i) { strcpy(tmp,Options[i]); for (tok = strtok(tmp,",\\ \t\n"); tok != NULL; tok = strtok(NULL,",\\ \t\n")) { if (*tok != '-') do_opt (curopt,tok); else { for (++tok; index("nwt",*tok) == NULL; ++tok) { /* options with no strings */ switch(*tok) { case 'S': Gflags &= ~GF_OVER; break; case '%': Listfirst = 1; break; case 'U': Nounsub = 1; break; #ifdef OLDRC case 'i': /* Treat "-i" as synonym for "-x" */ #endif case 'x': Gflags |= GF_ALL; default: break; } } curopt = *tok; if (*(++tok) != '\0') do_opt (curopt,tok); } } } } /* do_opt is for options with strings attached */ static do_opt (opt,str) char opt, *str; { switch (opt) { case 'n': Gflags |= GF_SPEC; specmark(str); break; case 'w': specfilter (FIL_AUTHOR,str); break; case 't': specfilter (FIL_TITLE,str); break; default: #ifdef OLDRC Gflags |= GF_SPEC; /* Assume anything else is newsgroup */ specmark(str); #endif break; } } static specfilter (comp,str) char comp,*str; { char *regcmp(); int *count; char **rex; /* ** we may set rex one past end of array. we will error before ** referencing it if that's the case, however. */ if (*str == '!') { if (comp == FIL_TITLE) { count = &Nntopt; rex = Negtopt + *count; } else { count = &Nnwopt; rex = Negwopt + *count; } ++str; } else { if (comp == FIL_TITLE) { count = &Ntopt; rex = Topt + *count; } else { count = &Nwopt; rex = Wopt + *count; } } if (*count >= NUMFILTER) printex ("too many %c options, %d allowed",comp,NUMFILTER); if ((*rex = regcmp(str,(char *) 0)) == NULL) printex ("%c option regular expression syntax: %s",comp,str); ++(*count); } /* handle the newsgroup specification string. ("all" convention - braack!!!) */ static specmark (s) char *s; { unsigned ormask,andmask; int i,len; char *ptr,*re,pattern[RECLEN],*regex(),*regcmp(); if (*s == '!') { ++s; ormask = 0; andmask = ~FLG_SPEC; if (*s == '\0') return; } else { ormask = FLG_SPEC; andmask = 0xffff; } /* convert "all" not bounded by alphanumerics to ".*". ".all" becomes ".*" */ for (ptr = s; (len = findall(ptr)) >= 0; ptr += len+1) { if (len > 0 && isalnum (s[len-1])) continue; if (isalnum (s[len+3])) continue; if (len > 0 && s[len-1] == '.') { --len; strcpy (s+len,s+len+1); } s[len] = '.'; s[len+1] = '*'; strcpy (s+len+2,s+len+3); } /* now use regular expressions */ sprintf (pattern,"^%s$",s); if ((re = regcmp(pattern,(char *) 0)) == NULL) printex ("n option regular expression syntax: %s",s); for (i=0; i < Ncount; ++i) { if (regex(re,(Newsorder[i])->nd_name) != NULL) { (Newsorder[i])->state |= ormask; (Newsorder[i])->state &= andmask; } } regfree (re); } static findall (s) char *s; { int len; for (len=0; *s != '\0'; ++s,++len) { if (*s == 'a' && strncmp(s,"all",3) == 0) return (len); } return (-1); } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0
bobm@rtech.UUCP (Bob Mcqueer) (01/01/87)
Bob McQueer {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm cut here ----------------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # digest.c # envir_set.c # groupdir.c # hash.c # pagefile.c # reg.c # sig_set.c # storage.c # strings.c # strtok.c # term_set.c # tmpnam.c # tty_set.c # userlist.c # vnglob.c # This archive created: Thu Jan 1 11:15:28 1987 export PATH; PATH=/bin:$PATH echo shar: extracting "'digest.c'" '(5027 characters)' if test -f 'digest.c' then echo shar: will not over-write existing file "'digest.c'" else cat << \SHAR_EOF > 'digest.c' /* ** vn news reader. ** ** digest.c - digest unpacking routines ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" #include "vn.h" #include "head.h" extern int Digest; extern int L_allow; extern int C_allow; extern PAGE Page; extern char *F_head, *T_head, *L_head, *D_head; digest_page (idx,skip) int idx; { char *ptr,name[24],*title,*index(); FILE *fp; int i,len; char subj[RECLEN],date[RECLEN],from[RECLEN],junk[RECLEN],*str_store(); long pos; Digest = Page.b[idx].art_id; sprintf (name,"%d", Digest); if ((fp = fopen(name,"r")) == NULL) return (-1); subj[0] = date[0] = from[0] = junk[0] = '\0'; skip_header (fp); /* skip over some articles if requested to */ for (i=skip; i > 0; --i) { if (dig_advance(fp,from,subj,date,junk,&pos) < 0) return (-1); } /* every new call to a digest Page "loses" a small amount of storage */ title = str_store(Page.b[idx].art_t); if ((ptr = index(title,'~')) != 0) *ptr = '\0'; title [C_allow - 20] = '\0'; for (i=0; i < L_allow && (len = dig_advance(fp,from,subj,date,junk,&pos)) >= 0; ++i) { Page.b[i].art_id = i+1+skip; Page.b[i].art_mark = ' '; subj [C_allow] = '\0'; from [C_allow] = '\0'; sprintf (name,"%d",len); form_title (date,subj,name,from,100); strcpy (Page.b[i].art_t,date); } fclose (fp); if (i == 0) return (-1); Page.h.name = title; Page.h.artnum = i; return (i); } /* returns name of file containing "article", NULL for failure */ char * digest_extract (s,art) char *s; int art; { char name[24]; FILE *fout,*fin; char subj[RECLEN],date[RECLEN],from[RECLEN],bufr[RECLEN]; char extra[RECLEN]; char *index(); long pos; int lines; sprintf (name,"%d", Digest); if ((fin = fopen(name,"r")) == NULL) return (NULL); for (skip_header (fin); art > 0; --art) if ((lines = dig_advance(fin,from,subj,date,extra,&pos)) < 0) { fclose (fin); return (NULL); } tmpnam(s); if ((fout = fopen(s,"w")) == NULL) { fclose (fin); unlink (s); return (NULL); } fseek(fin,0L,0); while (fgets(bufr,RECLEN-1,fin) != NULL && index(bufr,':') != NULL) { if (strncmp(bufr,F_head,FHDLEN) == 0) { fprintf (fout,"%s%s\n",F_head,from); continue; } if (strncmp(bufr,T_head,THDLEN) == 0) { fprintf (fout,"%s%s\n",T_head,subj); continue; } if (strncmp(bufr,D_head,DHDLEN) == 0) { fprintf (fout,"%s%s\n",D_head,date); continue; } /* defer line count header - it comes last */ if (strncmp(bufr,L_head,LHDLEN) == 0) continue; fprintf (fout,"%s",bufr); } /* toss in extra header lines, line count header, extra newline */ fprintf (fout,"%s%s%d\n\n",extra,L_head,lines); fseek (fin,pos,0); while (fgets(bufr,RECLEN-1,fin) != NULL && strncmp(bufr,"--------",8) != 0) fprintf(fout,"%s",bufr); fclose (fin); fclose (fout); return (s); } dig_list (s) char *s; { char *ptr,*out,*new,ns[L_tmpnam],tmp[RECLEN],*strtok(); int i; prinfo ("Extracting articles ....."); strcpy (tmp,s); out = s; for (ptr = strtok(tmp," "); ptr != NULL; ptr = strtok(NULL," ")) { i = atoi(ptr); if ((new = digest_extract(ns,i)) != NULL) { sprintf (out,"%s ",new); out += strlen(new) + 1; } } *out = '\0'; if (*s == '\0') strcpy (s,"NULLDIGEST"); } dig_ulist (s) char *s; { char *strtok(); for (s = strtok(s," "); s != NULL; s = strtok(NULL," ")) unlink (s); } /* returns # lines in article, -1 for failure scans past article, returns position of start. also returns "extra" header lines encountered, WITH newlines. */ static dig_advance (fp,from,subj,date,extra,pos) FILE *fp; char *from,*subj,*date,*extra; long *pos; { char buf[RECLEN]; char *ptr, *index(); int len,state,lcount; lcount = state = 0; *extra = '\0'; while (fgets(buf,RECLEN-1,fp) != NULL) { buf[(len = strlen(buf) - 1)] = '\0'; for (--len ; len >= 0 && buf[len] == ' ' || buf[len] == '\t'; --len) buf[len] = '\0'; ++len; switch(state) { case 0: /* skip blank lines before header */ if (len == 0) break; state = 1; /* fall through */ case 1: if (strncmp(buf,F_head,FHDLEN) == 0) { strcpy (from,buf+FHDLEN); break; } if (strncmp(buf,T_head,THDLEN) == 0) { strcpy (subj,buf+THDLEN); break; } if (strncmp(buf,D_head,DHDLEN) == 0) { strcpy (date,buf+DHDLEN); break; } /* put wierd header lines in extra */ if ((ptr = index(buf,':')) != NULL) { *ptr = '\0'; if (index(buf, ' ') == NULL) { *ptr = ':'; sprintf(extra,"%s\n",buf); extra += strlen(extra); break; } *ptr = ':'; } state = 2; /* remember the newline we lopped off */ *pos = ftell(fp)-strlen(buf)-1; /* fall through */ case 2: ++lcount; if (strncmp("--------",buf,8) == 0) { --lcount; return (lcount); } break; } } return (-1); } static skip_header (fp) FILE *fp; { char buf[RECLEN]; while (fgets(buf,RECLEN-1,fp) != NULL) if (strncmp("--------",buf,8) == 0) break; } SHAR_EOF fi # end of overwriting check echo shar: extracting "'envir_set.c'" '(2931 characters)' if test -f 'envir_set.c' then echo shar: will not over-write existing file "'envir_set.c'" else cat << \SHAR_EOF > 'envir_set.c' /* ** vn news reader. ** ** envir_set.c - routine to obtain pertinent environment variable settings ** and set up file / directory names ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <pwd.h> #include <sys/param.h> #include "config.h" extern char *Editor,*Ps1,*Mailer,*Printer,*Poster; extern char *Onews, *Newsrc, *Orgdir, *Savedir, *Ccfile; /* path names */ extern char Cxitop[], Cxitor[], Cxrtoi[], Cxptoi[]; #ifdef SYSV extern char *getcwd(); #define getwd(a) getcwd(a,sizeof(a)) #define MAXPATHLEN 240 #else extern char *getwd(); #endif /* environment variable, original directory string setup. */ envir_set () { char dbuf [MAXPATHLEN], *rcname, *ccname, *keyxln; char *getenv(), *getcwd(), *str_store(); struct passwd *ptr, *getpwuid(); if ((Ps1 = getenv("PS1")) == NULL) Ps1 = DEF_PS1; if ((Editor = getenv("EDITOR")) == NULL) Editor=DEF_ED; if ((Mailer = getenv("MAILER")) == NULL) Mailer=DEF_MAIL; if ((Poster = getenv("POSTER")) == NULL) Poster=DEF_POST; if ((Printer = getenv("PRINTER")) == NULL) Printer=DEF_PRINT; if ((rcname = getenv("NEWSRC")) == NULL) rcname=DEF_NEWSRC; if ((ccname = getenv("CCFILE")) == NULL) ccname=DEF_CCFILE; if ((keyxln = getenv("VNKEY")) == NULL) keyxln=DEF_KEYXLN; Savedir = getenv("VNSAVE"); /* set original directory strings. create empty Newsrc if it doesn't exist */ ptr = getpwuid (getuid()); if ((Orgdir = getwd(dbuf)) == NULL) printex ("cannot stat pwd"); Orgdir = str_store (Orgdir); if (Savedir == NULL) Savedir = Orgdir; if (*rcname != '/') { sprintf (dbuf, "%s/%s",ptr->pw_dir,rcname); Newsrc = str_store (dbuf); } else Newsrc = str_store (rcname); if (*ccname != '/') { sprintf (dbuf, "%s/%s",ptr->pw_dir,ccname); Ccfile = str_store (dbuf); } else Ccfile = str_store (ccname); sprintf (dbuf, "%s/%s%s",ptr->pw_dir,".vn","XXXXXX"); Onews = str_store (mktemp(dbuf)); if (access (Newsrc,0) != 0) creat (Newsrc,0666); if (*keyxln != '/') { sprintf(dbuf, "%s/%s",ptr->pw_dir,keyxln); set_kxln(dbuf); } else set_kxln(keyxln); } static set_kxln(fname) char *fname; { FILE *fp; int i; char bufr[80]; char in,out,*ptr; char *index(), xln_str(); for (i=0; i < 128; ++i) Cxitop[i] = Cxitor[i] = Cxptoi[i] = Cxrtoi[i] = i; if ((fp = fopen(fname,"r")) != NULL) { while(fgets(bufr,79,fp) != NULL) { if (strncmp(bufr+1,"==",2) == 0) ptr = bufr+2; else ptr = index(bufr+1,'='); if (ptr == NULL) continue; *ptr = '\0'; ++ptr; in = xln_str(bufr+1); out = xln_str(ptr); switch(bufr[0]) { case 'r': case 'R': Cxrtoi[out] = in; Cxitor[in] = out; break; case 'p': case 'P': Cxptoi[out] = in; Cxitop[in] = out; default: break; } } fclose(fp); } } static char xln_str(s) char *s; { if (*s < '0' || *s > '9') return(*s & 0x7f); return((char)(atoi(s) & 0x7f)); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'groupdir.c'" '(686 characters)' if test -f 'groupdir.c' then echo shar: will not over-write existing file "'groupdir.c'" else cat << \SHAR_EOF > 'groupdir.c' /* ** vn news reader. ** ** groupdir.c - translation between newsgroup name and directory ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" #include "vn.h" extern PAGE Page; /* g_dir converts newsgroup name to directory string */ g_dir(s,t) char *s,*t; { char *ptr, *index(); sprintf (t,"%s/%s",SPOOLDIR,s); for (ptr=t+strlen(SPOOLDIR)+1; (ptr = index(ptr,'.')) != NULL; *ptr = '/') ; } /* change directory to group */ cd_group () { char dbuf [RECLEN]; g_dir ((Page.h.group)->nd_name,dbuf); if (chdir(dbuf) < 0) { Page.h.artnum = 1; Page.b[0].art_id = 0; strcpy (Page.b[0].art_t, "CANNOT FIND NEWSGROUP"); } } SHAR_EOF fi # end of overwriting check echo shar: extracting "'hash.c'" '(1842 characters)' if test -f 'hash.c' then echo shar: will not over-write existing file "'hash.c'" else cat << \SHAR_EOF > 'hash.c' /* ** vn news reader. ** ** hash.c - hash table routines ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" #include "tune.h" #include "vn.h" /* ** hash table manipulation routines: ** also sets Ncount, allocates Newsorsder array, and sets Newsorder ** initially to order newsgroups were entered in (active file order) */ extern int Ncount; extern NODE **Newsorder; static NODE *Tab [HASHSIZE]; /* hash Table */ hashinit () { int i; for (i=0; i < HASHSIZE; ++i) Tab[i] = NULL; Ncount = 0; } /* enter new node (name s, articles n, low l) in hash Table, initial flags = 0. As nodes are entered, pnum item is temporarily used to indiacte entry order for initial construction of Newsorder array via entry_order(); */ NODE *hashenter(s,n,l) char *s; int n; int l; { char *str_store(); NODE *ptr,*node_store(); int i; i=hash(s); ptr = node_store(); ptr->next = Tab[i]; Tab[i] = ptr; if (l > n) l = n; ptr->pnum = Ncount; ++Ncount; ptr->rdnum = l; ptr->state = 0; ptr->art = n; ptr->nd_name = str_store(s); return (ptr); } NODE *hashfind(s) char *s; { NODE *ptr; for (ptr = Tab[hash(s)]; ptr != NULL && strcmp(ptr->nd_name,s) != 0; ptr = ptr->next) ; return (ptr); } /* ** entry order is called after all hash_enter's have been done, PRIOR ** to the use of pnum item for anything else. It constructs the initial ** Newsorder array. */ entry_order() { int i; NODE *ptr; if ((Newsorder = (NODE **) malloc(Ncount*sizeof(NODE *))) == NULL) printex("Cannot allocate memory for Newsorder array"); for (i=0; i < HASHSIZE; ++i) { for (ptr = Tab[i]; ptr != NULL; ptr = ptr->next) Newsorder[ptr->pnum] = ptr; } } static hash (s) char *s; { int rem; for (rem=0; *s != '\0'; ++s) rem = (rem*128 + (*s&0x7f)) % HASHSIZE; return (rem); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'pagefile.c'" '(5053 characters)' if test -f 'pagefile.c' then echo shar: will not over-write existing file "'pagefile.c'" else cat << \SHAR_EOF > 'pagefile.c' /* ** vn news reader. ** ** pagefile.c - routines to deal with page display tempfile ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #ifdef SYSV #include <sys/types.h> #include <fcntl.h> #endif #include <sys/file.h> #include "vn.h" #include "head.h" extern int Ncount,Lrec,L_allow,Cur_page,C_allow; extern int Nwopt, Nnwopt, Ntopt, Nntopt; extern char *Wopt[], *Topt[], *Negtopt[], *Negwopt[]; extern NODE **Newsorder; extern PAGE Page; extern int Digest; extern char *Aformat; extern char *T_head, *F_head, *L_head; static int Tdes; /* temp file descriptor */ static int Pgsize; /* block size for seeking file */ /* routines which deal with the temp file containing display pages. Note the "invisible" file feature - tempfile is unlinked from /usr/tmp immediately. when Tdes is closed by UNIX the disk space will be given back. */ temp_open () { char tmpart [L_tmpnam]; Lrec = -1; tmpnam (tmpart); Pgsize = sizeof (HEAD) + L_allow * sizeof(BODY); if ((Tdes = open(tmpart,O_RDWR|O_CREAT)) < 0) printex ("can't open %s",tmpart); unlink (tmpart); } /* create page records for newsgroup s all articles between low and hi are to be included. */ outgroup (s,low,hi) char *s; int low,hi; { int i,aid; char title[RECLEN],gd[RECLEN]; g_dir(s,gd); if (chdir(gd) < 0) { grp_indic(s,0); return; } grp_indic(s,1); aid = 0; for (i=low+1; i <= hi; ++i) { if (digname (i,title) >= 0) { Page.b[aid].art_id = i; Page.b[aid].art_mark = ' '; strcpy (Page.b[aid].art_t, title); if ((++aid) >= L_allow) { /* start next page */ Page.h.artnum = L_allow; do_write (); ++Lrec; aid = 0; } } } /* last page (partial) */ if (aid != 0) { Page.h.artnum = aid; do_write (); ++Lrec; } } /* set current page to n. use Pgsize and lseek to find it in temp file (descriptor Tdes). */ find_page (n) int n; { long off,lseek(); int i,last; Cur_page = n; off = Pgsize; off *= (long) n; lseek (Tdes, off, 0); if (read(Tdes, (char *) &(Page.h), sizeof(HEAD)) < sizeof(HEAD)) printex("bad temp file read"); i = Pgsize - sizeof(HEAD); if (read(Tdes, (char *) Page.b, i) < i) printex("bad temp file read"); last = -1; for (i=0; i < Ncount; ++i) { if ((Newsorder[i])->pages > 0) { if ((Newsorder[i])->pnum > n) break; last = i; } } if (last < 0) printex ("can't find page %d",n); Page.h.group = Newsorder[last]; Page.h.name = (Page.h.group)->nd_name; cd_group (); } write_page () { long off,lseek(); if (!Digest) { off = Pgsize; off *= (long) Cur_page; lseek (Tdes, off, 0); do_write(); } } static do_write() { int num; if (write(Tdes, (char *) &(Page.h), sizeof(HEAD)) < sizeof(HEAD)) printex ("Bad temp file write"); num = L_allow * sizeof(BODY); if (write(Tdes, (char *) Page.b, num) < num) printex ("Bad temp file write"); } /* find article title: n - articles id t - returned title - must have storage for RECLEN, assumed to be > 3 * max title length also. */ static digname (n, t) int n; char *t; { int i,j; FILE *fp,*fopen(); char ff [MAX_C+1],fn [MAX_C+1],fl [MAX_C+1],*index(); /* open article */ sprintf (t,"%d", n); if ((fp = fopen(t,"r")) == NULL) return (-1); /* get subject, from and lines by reading article */ ff[0] = fn[0] = fl[0] = '?'; ff[1] = fn[1] = fl[1] = '\0'; ff[C_allow] = fn[C_allow] = fl[C_allow] = '\0'; for (i = 0; i < HDR_LINES && fgets(t,RECLEN-1,fp) != NULL; ++i) { if (index(CHFIRST,t[0]) == NULL) continue; t[strlen(t) - 1] = '\0'; if (strncmp(T_head,t,THDLEN) == 0) { for (j=0; j < Nntopt; ++j) { if (regex(Negtopt[j],t+THDLEN) != NULL) { fclose(fp); return(-1); } } if (Ntopt > 0) { for (j=0; j < Ntopt; ++j) { if (regex(Topt[j],t+THDLEN) != NULL) break; } if (j >= Ntopt) { fclose(fp); return(-1); } } strncpy(fn,t+THDLEN,C_allow); continue; } if (strncmp(F_head,t,FHDLEN) == 0) { for (j=0; j < Nnwopt; ++j) { if (regex(Negwopt[j],t+FHDLEN) != NULL) { fclose(fp); return(-1); } } if (Nwopt > 0) { for (j=0; j < Nwopt; ++j) { if (regex(Wopt[j],t+FHDLEN) != NULL) break; } if (j >= Nwopt) { fclose(fp); return(-1); } } strncpy(ff,t+FHDLEN,C_allow); continue; } if (strncmp(L_head,t,LHDLEN) == 0) { strncpy(fl,t+LHDLEN,C_allow); break; } } fclose (fp); /* reject empty or 1 line files */ if (i < 2) return (-1); form_title (t,fn,fl,ff,n); return (0); } form_title (t,fn,fl,ff,n) char *t,*fn,*fl,*ff; int n; { char *ptr,*index(); int i; if ((ptr = index(ff,'(')) != NULL && strlen(ptr) > 3) ff = ptr; sprintf (t,TFORMAT,fn,fl,ff); sprintf(ff,Aformat,' ',' ',n); i = C_allow - strlen(ff) + 1; /* remember newline in Aformat */ t[i] = '\0'; ctl_xlt(t); return (0); } /* replace control characters in titles */ static ctl_xlt(s) char *s; { while (*s != '\0') { if (*s < ' ') *s += 'A' - 1; ++s; } } SHAR_EOF fi # end of overwriting check echo shar: extracting "'reg.c'" '(1656 characters)' if test -f 'reg.c' then echo shar: will not over-write existing file "'reg.c'" else cat << \SHAR_EOF > 'reg.c' /* ** vn news reader. ** ** reg.c - implementation of regex / regcmp on top of UCB library ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #define RGBLKSIZE 20 struct _regtab { struct _regtab *link; char *regstr; }; typedef struct _regtab REGTAB; static REGTAB *Chain = NULL; static REGTAB *Free = NULL; static REGTAB *Compiled = NULL; regfree(s) char *s; { REGTAB *ptr,*cmp,*old; cmp = (REGTAB *) s; old = NULL; for (ptr = Chain; ptr != NULL; ptr = (old = ptr)->link) { if (ptr == cmp) { if (old == NULL) Chain = Chain->link; else old->link = ptr->link; ptr->link = Free; Free = ptr; break; } } } char *regcmp(str) char *str; { int i; char *str_store(); char *re_comp(); if (re_comp(str) != NULL) { Compiled = NULL; /* make sure we're OK */ return(NULL); } if (Free == NULL) { Free = (REGTAB *) malloc(RGBLKSIZE * sizeof(REGTAB)); if (Free == NULL) printex ("regcmp: memory allocation failure"); for (i = 0; i < RGBLKSIZE - 1; ++i) Free[i].link = Free + i + 1; Free[i].link = NULL; } Compiled = Free; Free = Free->link; Compiled->link = Chain; Chain = Compiled; Compiled->regstr = str_store(str); return ((char *) Compiled); } char *regex(reg,str) char *reg,*str; { REGTAB *cmp; cmp = (REGTAB *) reg; if (cmp == Compiled) { if (re_exec(str)) return(str); return (NULL); } for (Compiled = Chain; Compiled != NULL; Compiled = Compiled->link) { if (Compiled == cmp) break; } if (Compiled == NULL) printex ("regex: bad pointer"); re_comp(Compiled->regstr); if (re_exec(str)) return(str); return(NULL); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'sig_set.c'" '(4501 characters)' if test -f 'sig_set.c' then echo shar: will not over-write existing file "'sig_set.c'" else cat << \SHAR_EOF > 'sig_set.c' /* ** vn news reader. ** ** sig_set.c - signal handler ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <sys/signal.h> #include <sgtty.h> #include <setjmp.h> #include "tty.h" #include "vn.h" #include "config.h" extern int L_allow; extern char *Version; static int Sigflag=BRK_INIT; /* phase of interaction */ static FILE **Fpseek; /* article reading file pointer pointer */ static int Foreground; static jmp_buf Jumploc; /* for BRK_SESS phase */ static char *Cur_scn; /* current group name being scanned */ /* interrupt handler - unusual termination (longjmp and printex aborts) if not abort, remember to reset signal trap CAUTION - the passing of a jump buffer is a little dicey - assumes type jump_buf is an array. sigcatch and sig_set control a lot of i/o on stderr also, since it is so intimately related to signal interaction. Note that the SIGTSTP action causes a "stopped on tty output" if raw terminal mode is restored by tty_set(RESTORE). We don't get it if we were already cooked since tty_set avoids calling ioctl if it doesn't have to. */ static sigcatch (sig) int sig; { char buf [MAX_C+1]; int pgrp; /* disable signal while processing it */ signal (sig,SIG_IGN); switch (sig) { case SIGINT: case SIGQUIT: break; #ifdef JOBCONTROL case SIGTSTP: /* ignore SIGTTOU so we don't get stopped if [kc]sh grabs the tty */ signal(SIGTTOU, SIG_IGN); tty_set (SAVEMODE); term_set (MOVE,0,L_allow+RECBIAS-1); printf ("\n"); Foreground = 0; fflush (stdout); fflush (stderr); signal(SIGTTOU, SIG_DFL); /* Send the TSTP signal to suspend our process group */ signal(SIGTSTP, SIG_DFL); sigsetmask(0); kill (0, SIGTSTP); /* WE ARE NOW STOPPED */ /* WELCOME BACK! if terminals process group is ours, we are foregrounded again and can turn newsgroup name printing back on */ tty_set (RESTORE); switch (Sigflag) { case BRK_SESS: signal (SIGTSTP,sigcatch); longjmp (Jumploc,1); case BRK_IN: ioctl (1,TIOCGPGRP,&pgrp); if (pgrp == getpgrp(0)) { Foreground = 1; if (Cur_scn != NULL) fgprintf (" %s\n",Cur_scn); } break; default: break; } signal (SIGTSTP,sigcatch); return; #endif default: printex (BRK_MSG,sig); } /* QUIT and INTERRUPT signals */ switch (Sigflag) { case BRK_SESS: /* if in session, ask if really a quit, do longjump if not */ term_set (ERASE); tty_set (RAWMODE); user_str (buf, BRK_PR, 1); if (buf[0] == 'y') printex (BRK_MSG,sig); signal (sig,sigcatch); longjmp (Jumploc,1); case BRK_READ: /* if reading seek file to end to abort page printing */ printf ("\n"); if (*Fpseek == NULL || fseek(*Fpseek,0L,2) < 0) putchar ('\07'); break; default: printex (BRK_MSG,sig); } signal (sig,sigcatch); } /* sig_set controls what will be done with a signal when picked up by sigcatch. grp_indic / fgprintf is included here to keep knowledge of TSTP state localized. */ /* VARARGS */ sig_set (flag,dat) int flag, *dat; { int i, *xfer, pgrp; if (Sigflag == BRK_INIT) { Cur_scn = NULL; signal (SIGINT,sigcatch); signal (SIGQUIT,sigcatch); signal (SIGHUP,sigcatch); signal (SIGTERM,sigcatch); #ifdef JOBCONTROL signal (SIGTSTP,sigcatch); ioctl (1,TIOCGPGRP,&pgrp); if (pgrp == getpgrp(0)) { Foreground = 1; fgprintf ("Visual News, Release %s, reading:\n",Version); } else Foreground = 0; #else Foreground = NOJOB_FG; #endif } switch (flag) { case BRK_IN: case BRK_OUT: Sigflag = flag; break; case BRK_READ: if (Sigflag != BRK_SESS) printex ("unexpected read state, sig_set\n"); Fpseek = (FILE **) dat; Sigflag = BRK_READ; break; case BRK_SESS: xfer = (int *) Jumploc; for (i=0; i < sizeof(Jumploc) / sizeof(int); ++i) xfer[i] = dat[i]; Sigflag = BRK_SESS; break; case BRK_RFIN: if (Sigflag != BRK_READ) printex ("unexpected finish state, sig_set\n"); Sigflag = BRK_SESS; break; default: printex ("bad state %d, sig_set\n",flag); } } grp_indic (s,ok) char *s; int ok; { NODE *ptr,*hashfind(); /* we go to hash table because s might be a temporary buffer */ if ((ptr = hashfind(s)) != NULL) { Cur_scn = ptr->nd_name; if (Foreground) { if (ok) fgprintf(" %s\n",Cur_scn); else fgprintf(" %s - Can't access spool directory\n",Cur_scn); } } } fgprintf (fs,a,b,c,d,e) char *fs; int a,b,c,d,e; { if (Foreground) fprintf (stderr,fs,a,b,c,d,e); fflush (stderr); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'storage.c'" '(1309 characters)' if test -f 'storage.c' then echo shar: will not over-write existing file "'storage.c'" else cat << \SHAR_EOF > 'storage.c' /* ** vn news reader. ** ** storage.c - storage allocation routines ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "vn.h" extern char *malloc(); extern int L_allow; extern PAGE Page; /* Storage allocaters. One more call to malloc in entry_order routine. */ char *str_store (s) char *s; { static unsigned av_len = 0; /* current storage available */ static char *avail; int len; if (s == NULL) s = ""; if ((len = strlen(s)+1) > av_len) { if (len > STRBLKSIZE) av_len = len; else av_len = STRBLKSIZE; if ((avail = malloc(av_len)) == NULL) printex ("can't allocate memory for string storage"); } strcpy (avail,s); s = avail; avail += len; av_len -= len; return (s); } /* ** called after number of terminal lines (L_allow) is known, to set ** up storage for Page. */ page_alloc () { char *body; if ((body = malloc(L_allow*sizeof(BODY))) == NULL) printex ("can't allocate memory for display storage"); Page.b = (BODY *) body; } NODE *node_store() { static int nd_avail = 0; static NODE *nd; NODE *ret; if (nd_avail <= 0) { if ((nd = (NODE *) malloc(sizeof(NODE)*NDBLKSIZE)) == NULL) printex ("can't allocate memory for newsgroup table"); nd_avail = NDBLKSIZE; } --nd_avail; ret = nd; ++nd; return(ret); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'strings.c'" '(720 characters)' if test -f 'strings.c' then echo shar: will not over-write existing file "'strings.c'" else cat << \SHAR_EOF > 'strings.c' /* ** vn news reader. ** ** strings.c - character strings ** ** see copyright disclaimer / history in vn.c source file */ #include "vn.h" #include "head.h" char *Version = "12/86"; char *No_msg = "No articles"; char *Hdon_msg = "Headers being printed"; char *Hdoff_msg = "Headers being suppressed"; char *Roton_msg = "ROT 13"; char *Rotoff_msg = "NO ROT"; char *Aformat = AFORMAT; char *Contstr = " ******** any key to continue ********"; char *R_head = RHEAD; char *M_head = MHEAD; char *P_head = PHEAD; char *D_head = DHEAD; char *F_head = FHEAD; char *FT_head = FTHEAD; char *T_head = THEAD; char *L_head = LHEAD; char *N_head = NHEAD; char *RT_head = RTHEAD; char *TO_head = TOHEAD; char *DIS_head = DISHEAD; SHAR_EOF fi # end of overwriting check echo shar: extracting "'strtok.c'" '(1082 characters)' if test -f 'strtok.c' then echo shar: will not over-write existing file "'strtok.c'" else cat << \SHAR_EOF > 'strtok.c' /* ** vn news reader. ** ** strtok.c - strtok() and strpbrk() string routines using UCB index(). ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> char *strpbrk (s,del) char *s, *del; { char *ptr,*index(); if (s == NULL) return (NULL); for (; *del != '\0'; ++del) if ((ptr = index(s,*del)) != NULL) return (ptr); return (NULL); } char *strtok(str,delim) char *str, *delim; { char *tokstart, *tokend, *first_ch (), *last_ch(); static char *save=NULL; if (str != NULL) save = str; if (save == NULL) return (NULL); tokstart = first_ch (save, delim); tokend = last_ch (tokstart, delim); save = first_ch (tokend, delim); *tokend = '\0'; if (*tokstart == '\0') return (NULL); return (tokstart); } static char *first_ch (str,delim) char *str,*delim; { char *index (); char *f; for (f = str; *f != '\0' && index(delim,*f) != NULL; ++f) ; return (f); } static char *last_ch (str,delim) char *str,*delim; { char *index (); char *f; for (f = str; *f != '\0' && index(delim,*f) == NULL; ++f) ; return (f); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'term_set.c'" '(4914 characters)' if test -f 'term_set.c' then echo shar: will not over-write existing file "'term_set.c'" else cat << \SHAR_EOF > 'term_set.c' /* ** vn news reader. ** ** term_set.c - terminal control, hides termcap interface ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "tty.h" #include "vn.h" extern int L_allow, C_allow; extern char *Ku, *Kd, *Kl, *Kr; static outc (c) char c; { putchar (c); } /* term_set controls terminal through termcap START sets global parameters related to terminal also, as well as allocating display buffer which depends on terminal lines, and allocating escape strings. RESTART simply re-issues the initialization - used following system calls that could have goofed up the terminal state. */ /* ** Escape strings. */ static char *Cm,*Cl,*So,*Se,*Te,*Bc,*Ce,*Ti,*Ks,*Ke; #ifdef USEVS static char *Vs,*Ve; #endif static int Backspace; /* backspace works */ static int Overstrike; /* terminal overstrikes */ static t_setup() { int i; char *tgetstr(), *getenv(), *str_store(); char *c, tc_buf[2048],optstr[2048]; c = optstr; if (tgetent(tc_buf,getenv("TERM")) != 1) printex ("%s - unknown terminal",getenv("TERM")); /* get needed capabilities */ Cm = str_store(tgetstr("cm",&c)); Cl = str_store(tgetstr("cl",&c)); So = str_store(tgetstr("so",&c)); Se = str_store(tgetstr("se",&c)); Te = str_store(tgetstr("te",&c)); Ti = str_store(tgetstr("ti",&c)); Bc = str_store(tgetstr("bc",&c)); Ce = str_store(tgetstr("ce",&c)); Kd = str_store(tgetstr("kd",&c)); Ke = str_store(tgetstr("ke",&c)); Kl = str_store(tgetstr("kl",&c)); Kr = str_store(tgetstr("kr",&c)); Ks = str_store(tgetstr("ks",&c)); Ku = str_store(tgetstr("ku",&c)); #ifdef USEVS Vs = str_store(tgetstr("vs",&c)); Ve = str_store(tgetstr("ve",&c)); #endif Backspace = tgetflag("bs"); Overstrike = tgetflag("os"); if ( *Cm == '\0' || *Cl == '\0') { printex ("cursor control and erase capability needed"); } /* ** Checks for arrow keys which don't issue something beginning ** with <ESC>. This is more paranoid than we need to be, strictly ** speaking - we could get away with any string which didn't ** conflict with controls used for commands. However, that would ** be a maintenance headache - we will simply reserve <ESC> as the ** only char not to be used for commands, and punt on terminals ** which don't send reasonable arrow keys. It would be confusing ** to have keys work partially, also. I know of no terminal with ** one arrow key beginning with an escape, and another beginning ** with something else, but let's be safe. This also insists on ** definitions for all 4 arrows, which seems reasonable. */ if ((*Ku != '\0' && *Ku != '\033') || *Kl != *Ku || *Kr != *Ku || *Kd != *Ku) { fgprintf("WARNING: arrow keys will not work for this terminal"); Ku = Kd = Kl = Kr = Kd = Ke = ""; } if (Overstrike) fgprintf ("WARNING: terminal overstrikes - can't update display without erase\n"); i = RECBIAS+1 < HHLINES+2 ? HHLINES+2 : RECBIAS+1; if ((L_allow = tgetnum("li")) < i) { if (L_allow < 0) printex ("can't determine number of lines on terminal"); printex ("too few lines for display - %d needed", i); } /* ** C_allow set so as to not use extreme right column. ** Avoids "bad wraparound" problems - we're deciding it's best ** to ALWAYS assume no automargin, and take care of it ourselves */ if((C_allow = tgetnum("co")) > MAX_C) C_allow = MAX_C; else --C_allow; if (C_allow < MIN_C) { if (C_allow < 0) printex("can't determine number of columns on terminal."); printex ("too few columns for display - %d needed",MIN_C); } L_allow -= RECBIAS; page_alloc(); tputs(Ti,1,outc); tputs(Ks,1,outc); #ifdef USEVS tputs(Vs,1,outc); #endif } /* VARARGS */ term_set(cmd,x,y) int cmd,x,y; { char *tgoto(); int i; switch (cmd) { case MOVE: tputs (tgoto(Cm,x,y),1,outc); break; case ERASE: tputs(Cl,1,outc); break; case ONREVERSE: tputs(So,1,outc); break; case OFFREVERSE: tputs(Se,1,outc); break; case START: t_setup(); break; case RESTART: tputs(Ti,1,outc); tputs(Ks,1,outc); #ifdef USEVS tputs(Vs,1,outc); #endif break; case STOP: term_set (MOVE,0,L_allow+RECBIAS-1); printf ("\n"); tputs(Ke,1,outc); tputs(Te,1,outc); #ifdef USEVS tputs(Ve,1,outc); #endif break; case RUBSEQ: if (Overstrike) { /* space overprint is futile */ if (Backspace) putchar('\010'); else tputs(Bc,1,outc); break; } if (Backspace) printf("%c %c",'\010','\010'); else { tputs(Bc,1,outc); putchar(' '); tputs(Bc,1,outc); } break; case ZAP: if (Ce != NULL && *Ce != '\0') tputs(Ce,1,outc); else { if (Overstrike) break; /* punt */ for (i=x; i < y; ++i) putchar(' '); if (Backspace) { for (i=x; i < y; ++i) putchar('\010'); } else { for (i=x; i < y; ++i) tputs(Bc,1,outc); } } break; default: printex ("term_set unknown code (%d)",cmd); break; } return (0); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'tmpnam.c'" '(420 characters)' if test -f 'tmpnam.c' then echo shar: will not over-write existing file "'tmpnam.c'" else cat << \SHAR_EOF > 'tmpnam.c' /* ** vn news reader. ** ** tmpnam.c - tmpnam() replacement for UCB, also uses non-generic name. ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" char *tmpnam (buf) char *buf; { static char *ptr = VNTEMPNAME; /* depends on string initialized above */ sprintf (ptr+TMP_XOFFSET,"XXXXXX"); mktemp (ptr); if (buf != NULL) strcpy (buf,ptr); return (ptr); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'tty_set.c'" '(2552 characters)' if test -f 'tty_set.c' then echo shar: will not over-write existing file "'tty_set.c'" else cat << \SHAR_EOF > 'tty_set.c' /* ** vn news reader. ** ** tty_set.c - interface to ioctl (system tty interface) ** ** see copyright disclaimer / history in vn.c source file */ #ifdef SYSV #include <termio.h> #else #include <sgtty.h> #endif #include "tty.h" extern char Erasekey,Killkey; #ifdef SYSV static struct termio C_tp, O_tp; #else static struct sgttyb C_tp; static unsigned short O_lflag; #endif static unsigned S_flag=0; static int R_ignore=0; /* up/down counter of reset calls to ignore */ #define IO_GOT 1 /* have polled for original terminal mode */ #define IO_RAW 2 /* in RAW (CBREAK actually) mode */ /* tty_set handles ioctl calls. SAVEMODE, RESTORE are used around system calls and interrupts to assure cooked mode, and restore raw if raw on SAVEMODE. The pair results in no calls to ioctl if we are cooked already when SAVEMODE is called, and may be nested, provided we desire no "restore" of cooked mode after restoring raw. When we get the original terminal mode, we also save erase and kill. sig_set makes an ioctl call to get process group leader. Otherwise ioctl calls should come through here. */ tty_set(cmd) int cmd; { int rc; unsigned mask; switch (cmd) { case BACKSTOP: #ifdef JOBCONTROL if ((rc = ioctl(1,TIOCLGET,&mask)) != 0) break; mask |= LTOSTOP; rc = ioctl(1,TIOCLSET,&mask); #else rc = 0; #endif break; case RAWMODE: if ((S_flag & IO_RAW) != 0) { rc = 0; break; } if ((S_flag & IO_GOT) == 0) { /* Save original modes, get erase / kill */ #ifdef SYSV rc = ioctl(0,TCGETA,&C_tp); O_tp = C_tp; Erasekey = C_tp.c_cc[VERASE]; Killkey = C_tp.c_cc[VKILL]; #else rc = ioctl(0,TIOCGETP,&C_tp); O_lflag = C_tp.sg_flags; Erasekey = C_tp.sg_erase; Killkey = C_tp.sg_kill; #endif } #ifdef SYSV C_tp.c_lflag &= ~(ECHO | ICANON); C_tp.c_cc[VMIN] = 1; rc = ioctl(0,TCSETAW,&C_tp); #else C_tp.sg_flags |= CBREAK; C_tp.sg_flags &= ~ECHO; rc = ioctl(0,TIOCSETP,&C_tp); #endif S_flag = IO_GOT|IO_RAW; break; case COOKED: if ((S_flag & IO_RAW) != 0) { #ifdef SYSV C_tp = O_tp; rc = ioctl(0,TCSETAW,&C_tp); #else C_tp.sg_flags = O_lflag; rc = ioctl(0,TIOCSETP,&C_tp); #endif S_flag &= ~IO_RAW; } else rc = 0; break; case SAVEMODE: if ((S_flag & IO_RAW) != 0) { tty_set(COOKED); R_ignore = 0; } else ++R_ignore; rc = 0; break; case RESTORE: if (R_ignore <= 0) { tty_set(RAWMODE); } else --R_ignore; rc = 0; break; default: rc = -1; } if (rc < 0) printex ("ioctl failure, tty_set: %d",cmd); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'userlist.c'" '(1923 characters)' if test -f 'userlist.c' then echo shar: will not over-write existing file "'userlist.c'" else cat << \SHAR_EOF > 'userlist.c' /* ** vn news reader. ** ** userlist.c - generate user's list of articles ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "vn.h" extern PAGE Page; /* generate user list of articles - either article numbers are input directly (numeric list), or input is a search string - invoke regular expression library and examine titles search string "*" reserved for marked articles. Strings may be prefixed with '!' for negation. */ userlist (list) char *list; { int i,j,anum[RECLEN/2],acount; char neg, *s, sbuf[MAX_C+1], *reg, *regex(), *regcmp(), *index(), *strtok(); user_str (sbuf,"Articles or title search string : ",1); if (sbuf[0] == '!') { neg = '!'; s = sbuf+1; } else { neg = '\0'; s = sbuf; } for (i=0; s[i] != '\0'; ++i) { if (index(LIST_SEP,s[i]) == NULL) { if (s[i] < '0' || s[i] > '9') break; } } acount = 0; if (s[i] == '\0') { for (s = strtok(s,LIST_SEP); s != NULL; s = strtok(NULL,LIST_SEP)) { anum[acount] = atoi(s); ++acount; } } else { if (s[0] == ART_MARK) { for (i=0; i < Page.h.artnum; ++i) { if (Page.b[i].art_mark == ART_MARK) { anum[acount] = Page.b[i].art_id; ++acount; } } } else { reg = regcmp(s,(char *) 0); if (reg != NULL) { for (i=0; i < Page.h.artnum; ++i) { if (regex(reg,Page.b[i].art_t) != NULL) { anum[acount] = Page.b[i].art_id; ++acount; } } regfree (reg); } else preinfo ("bad regular expression syntax"); } } /* algorithm is inefficient, but we're only handling a few numbers */ *list = '\0'; for (i=0; i < Page.h.artnum; ++i) { for (j=0; j < acount && anum[j] != Page.b[i].art_id; ++j) ; if (neg == '!') { if (j < acount) continue; } else { if (j >= acount) continue; } sprintf (list,"%d ",Page.b[i].art_id); list += strlen(list); } } SHAR_EOF fi # end of overwriting check echo shar: extracting "'vnglob.c'" '(1613 characters)' if test -f 'vnglob.c' then echo shar: will not over-write existing file "'vnglob.c'" else cat << \SHAR_EOF > 'vnglob.c' /* ** vn news reader. ** ** vnglob.c - global variables - see string.c also ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" #include "vn.h" #include "head.h" /* global data structure */ NODE **Newsorder; /* .newsrc file order */ char *Editor,*Ps1,*Mailer,*Printer,*Poster; char Erasekey, Killkey; /* user keys from stty */ char *Newsrc, *Orgdir; /* .newsrc file, and original pwd */ char *Onews; /* temp. file for backing up .newsrc */ char *Savefile = DEF_SAVE; /* file in which to save articles */ char *Savedir; /* default directory for saved articles */ char *Ccfile; /* author_copy file, stored /bin/mail fmt */ int Rot; /* rotation */ int Headflag; /* header printing flag */ int Digest; /* if non-zero, digest article */ char *Ku, *Kd, *Kl, *Kr; /* Cursor movement capabilities */ /* character translation arrays for commands */ char Cxitop[128], Cxitor[128], Cxrtoi[128], Cxptoi[128]; /* cur_page - current page displayed; lrec - last record l_allow - lines allowed for article display c_allow - columns allowed ncount = newsorder index nfltr - number of filters */ int Cur_page, Lrec, L_allow, C_allow, Ncount, Nfltr; /* article filtration options. */ char *Wopt[NUMFILTER]; /* regular expressions for -w options */ char *Topt[NUMFILTER]; /* regular expressions for -t options */ char *Negwopt[NUMFILTER]; /* regular expressions for negated -w options */ char *Negtopt[NUMFILTER]; /* regular expressions for negated -t options */ int Nwopt, Ntopt, Nnwopt, Nntopt; int Nounsub, Listfirst; /* current page */ PAGE Page; SHAR_EOF fi # end of overwriting check # End of shell archive exit 0