sources-request@mirror.UUCP (02/18/87)
Submitted by: rtech!bobm (Bob Mcqueer) Mod.sources: Volume 8, Issue 64 Archive-name: vn/Part01 This is the source for the alternate news reader vn. I'm sending you the four archives I put up in net.sources as four separate mail messages. Archive 1 contains documentation and a README file concerning installation. I tried to make these archives about the size that seemed to be acceptable in existing mod.sources postings - about 40K each. If you are a devoted user of the other readers, copy your .newsrc before you give this one a try - you might not like what it does. My justification is that a pretty fair number of people DO like what it does - consider it an alternate paradigm which basically got its start a few years back because I was getting sick of beating on the 'n' key. Bob McQueer {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm [ We use notes internally -- I haven't tried this. --r$ ] #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If all goes well, you will see the message "End of archive 1 (of 3)." # Contents: MANIFEST README config.h digest.c envir_set.c groupdir.c # hash.c head.h mf.nore mf.re mf.sysv newsrc.c pagefile.c reader.h # reg.c sig_set.c storage.c strings.c tmpnam.c # Wrapped by rs@mirror on Tue Feb 17 12:10:01 1987 PATH=/bin:/usr/bin:/usr/ucb; export PATH echo shar: extracting "'MANIFEST'" '(974 characters)' if test -f 'MANIFEST' ; then echo shar: will not over-write existing file "'MANIFEST'" else sed 's/^X//' >MANIFEST <<'@//E*O*F MANIFEST//' X File Name Kit # Description X----------------------------------------------------------- X MANIFEST 1 X README 1 X config.h 1 X digest.c 1 X envir_set.c 1 X groupdir.c 1 X hash.c 1 X head.h 1 X mf.nore 1 X mf.re 1 X mf.sysv 1 X newsrc.c 1 X pagefile.c 1 X reader.c 2 X reader.h 1 X reg.c 1 X sig_set.c 1 X storage.c 1 X strings.c 1 X strtok.c 2 X term_set.c 2 X tmpnam.c 1 X tty.h 2 X tty_set.c 2 X tune.h 2 X userlist.c 2 X vn.c 3 X vn.h 2 X vn.man 3 X vnglob.c 2 @//E*O*F MANIFEST// if test 974 -ne "`wc -c <'MANIFEST'`"; then echo shar: error transmitting "'MANIFEST'" '(should have been 974 characters)' fi fi # end of overwriting check echo shar: extracting "'README'" '(7590 characters)' if test -f 'README' ; then echo shar: will not over-write existing file "'README'" else sed 's/^X//' >README <<'@//E*O*F README//' XInstallation procedure steps: X X1) Edit file config.h, which defines system dependent parameters such as Xspool directory, and so on. I have NOT ifdef'ed this file for likely Xchanges for SYSV or anything like that. You probably can use this file Xuntouched only if you are UCB with news installed in all the standard places. XEven then, you may not like some of the defaults. X XNote very carefully the definitions involving mailer treatment. X X2) decide which makefile to use, edit it to fill in the correct libraries Xfor your system. See notes 2) below for explanation. X X3) make vn X X4) put the executable where you want it. put the man page, vn.man Xwhere you want it. roff it with -man to print it out. If you made Xsome configuration changes, or you are a SYSV installation, you may Xwant to modify the manual a bit. It is written to reflect the actions in Xthe config.h as distributed, and as used on UCB (it refers to job control). X XNotes: X X1) X If you modify the default mailer, poster, printer or editor, this is X their invocation, ufile being a tempfile name. X X "mailer [path] <ufile" - path obtained from newsarticle, possibly X modified by user. May be invoked with no argument dependent X on how you set up config.h. No path argument if INLETTER. X "poster <ufile" - thus the "inews -h" to get header lines from X file, giving user a chance to modify headers. X "printer files 2>/dev/null" X "editor ufile" X X You may also be interested in the header file "tune.h" which contains X some sizing / performance affecting parameters. X X2) X There are three makefile templates, all very simple, because I X want to make you think about whether you have the system V regular X expression library, and strtok() or not. If you have regex and X regcmp (as opposed to re_exec and re_comp, the ucb regular X expression library), I advise you to use them. regex and X regcmp are used because they have the ability to keep several X regular expressions compiled at once. The file "reg.c" is an X implementation of regcmp and regex on top of the ucb calls, X with a special procedure called "regfree" used in place of the X generic "free" call used with regcmp / regex. Use the system V X calls if you have them available because: X X i) They should be faster than the reg.c code, which X recompiles the "current" ucb string every time you X switch regular expressions. X X ii) I briefly checked out reg.c once, and it seemed to X work. Our system has the "real" calls available, so I X run with those. reg.c hasn't been used much by me, although X I've had nobody tell me it doesn't work. X X strtok.c defines the strtok() and strpbrk() functions. As with X regex, if you have system libraries, they are likely more efficient - X these string routines will often be done in assembler on a given X machine. X X tmpnam.c defines the tmpnam() routine. Even if a BSD system has it, X you might use it anyhow. We are currently running under Ultrix on a X microvax, and in fact, the Ultrix version doesn't work right (as of X the last time I checked). Also, the tmpnam.c version will produce X temp files named vnXXXXXX, instead of a generic tmpXXXXXX. X X There are 2 IFDEF's handled in the makefile templates: X X SYSV - system V specific portions. X X JOBCONTROL - brackets the parts dealing with the SIGTSTP X signal handling for job control. This could really X be done with "#ifndef SYSV", I suppose, but this X clearly marks this specific difference. X X The ifdef OLDRC may be set to make the .newsrc format consistent X with an apparently earlier readnews format which would not take X ranges for "articles read". This also makes -i an alias for a -x X option, and assumes that any arguments with no options are really X -n options. These changes came from Australia, and are apparently X compatible with whatever other newsreader was being used there. X X SYSV, JOBCONTROL, OLDRC and the definitions referred to in config.h X are the whole set. X X If you are BSD but have regex/regcomp: X X mf.re is the makefile template. Fill in your local X libraries (termcap), the library to find regex/regcmp in. X This makefile defines "regfree" simply to be "free", and X doesn't link the "reg" object. JOBCONTROL defined. This X template also assumes you have strtok() / strpbrk(). If you X don't, include strtok.o in the objects. This is actually X the makefile I use for Ultrix as given. X X If you are BSD but don't have regex/regcomp: X X mf.nore is the makefile template. Fill in your local X libraries (termcap). This makefile pulls in the "reg" X object. JOBCONTROL defined. If you DO have strtok() / X strpbrk() available, you might remove strtok.o from the X object list. X X If you are SYS V: X X mf.sysv is the makefile template. Fill in your local X libraries (termcap). This makefile does not pull in X the extra objects. It defines "regfree" to "free", and X turns the BSD index / rindex into strchr / strrchr. X SYSV defined. X X I haven't personally tried any SYSV stuff out - I don't have X access to a SYS V machine running news. You might want to X include tmpnam.o in the object list instead of using the X standard one, to get specifically named temp. files. X X Something pretty close to the current vn version used to run here X on a VAX 780 under BSD 4.2. X XNEWS VERSIONS: X XI know there's a lot of news versions running out there. We seem to Xbe running something called B2.10.3, for those of you whom that signifies Xanything to. Something to look at is your "active" file. I expect to Xfind a newsgroup followed by the high, then low, spooling numbers (followed Xby a y/n for posting priveledge, which I don't care about). Earlier news Xversions used to only have a single spooling number. vn is written to Xhandle this, assuming 0 for a low spool. My basic assumptions are that Xthe "active" file gives the newsgroups and spooling information and that Xarticles will be numeric filenames living in the directory obtained by Xreplacing periods with slashes in the newsgroup, and prefixing the spool Xdirectory. I hope these really are "basic" assumptions that work across all Xversions. vn is intended to be highly fault-tolerant regarding what it Xfinds for header lines - it may not be able to make sense out of an "article", Xbut it's willing to let you look at it anyway, with the single exception Xthat it throws away articles which turn out to be empty or 1 line files ( XCOUNTING the header lines - articles with 0 lines in them are OK. I'm Xjust preventing the "unknown subject, unknown author, and no text" case). X XRESOURCE USE: X Xvn should look like people sitting in an editor once it is done with its Xreading phase. During the reading phase, it is beating mercilessly on Xthe spooling directory, reading file after file. I have thought from time Xto time about having a daemon do this work periodically, building a master Xfile of title information for vn to access. Its reading phase would then Xbe a "pause" rather than a "phase", with the penalty that you couldn't Xread anything until the daemon had gotten around to it. X Xvn maintains a large temporary file containing the users page screens. XAgain, it should look a lot like the user is using an editor which has Xa temp file out there for its edit buffer. MAX_C in "tune.h" can be Xused to help control the size. X Xmalloc() usage - Around 50 bytes per newsgroup is dynamically allocated, Xplus storage for character strings and the current screen structure. XMy guess would be on the order of 20-40K. It's all "permanent" information Xmaintained for the entire session, hence unfreed. Regular expression Xstorage is freed, but is minimal anyway. @//E*O*F README// if test 7590 -ne "`wc -c <'README'`"; then echo shar: error transmitting "'README'" '(should have been 7590 characters)' fi 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 sed 's/^X//' >config.h <<'@//E*O*F config.h//' X/* X** vn news reader. X** X** config.h - system configuration parameters X** X** see copyright disclaimer / history in vn.c source file X*/ X X#define DEF_ED "/usr/ucb/vi" /* editor to use if no EDITOR variable */ X#define DEF_PS1 "$ " /* ! command prompt if no PS1 */ X#define DEF_SAVE "vn.save" /* save file */ X X/* X** mailer interface. If INLETTER is defined, a "To:" line will be X** placed in the file being editted by the user. Otherwise, the X** address will be an argument on the mailer's command line, with the X** user prompted for possible correction. In either case, "Subject: " X** is included in the file. X** X** If MAILSMART is set, alternate header lines will be used instead of X** the "Path: " line to determine the address because we assume the mailer X** is intelligent enough to do routing. X** X** If ADDRMUNGE is set, it is the name of a local routine which will be X** called to make further address modifications before the address is used. X** It will be passed the address string, which it may modify. RECLEN bytes X** of storage are available at the address passed. It will only be called X** once for a given address. X** X** DEF_MAIL is the mailer used in absence of MAILER variable. X*/ X#define INLETTER X#define MAILSMART X#define DEF_MAIL "/usr/lib/sendmail -t" X X#define DEF_PRINT "/usr/ucb/lpr" /* print command */ X#define DEF_POST "/usr/lib/news/inews -h" /* followup posting command */ X X#define DEF_NEWSRC ".newsrc" X#define DEF_CCFILE "author_copy" X#define DEF_KEYXLN ".vnkey" X X#define SPOOLDIR "/usr/spool/news" X#define ACTFILE "/usr/lib/news/active" X X/* X** foreground flag for messages. applies only if JOBCONTROL undefined X** (SYS V). set to 1 to see newsgroup messages, etc. during reading phase, X** 0 for "silent" operation - be warned that this may suppress some X** non-fatal diagnostic messages - find all references to fgprintf to X** see what is suppressed. X*/ X#define NOJOB_FG 1 X X/* X** arrow key treatment. If PAGEARROW is defined, right and left arrow X** keys will be synonyms for <return> (next-page) and <backspace> (previous). X** Otherwise, the right arrow will function as down, and the left as up. X** Made configurable because while there is no lateral motion on the screen X** to associate with the right and left arrows, you might not like them X** changing pages on you. X*/ X#define PAGEARROW X X/* X** if USEVS is defined, terminal initialization / exit for vn will include the X** "vs"/"ve" pair as well as "ti"/"te". This doesn't matter on a lot of X** terminals, but may make vn display behaviour closer to "vi" since vs/ve X** is vi's "visual mode" sequence. For instance, I believe the commonly X** used definitions for these strings on multi-page concepts allows the X** program to run in the first page of the terminal, preserving the more X** recent part of your session on exit X** X** #define USEVS X*/ X X/* X** temp file name template for mktemp(). Used in tmpnam.c, does not apply X** if you use a system library tmpnam(). BE CAREFUL - VNTEMPNAME MUST X** contain a string of 6 X's for mktemp() (actually, a place where 6 X's X** are intended to go). TMP_XOFFSET absolutely MUST point to the first of X** the X's. Yes, writing into a literal string is sloppy. To the best of X** my knowledge, tmpnam.c is the only place you'll find vn code doing it. X** We make this configurable in case you want temp files somewhere else. X*/ X#define VNTEMPNAME "/usr/tmp/vnXXXXXX" X#define TMP_XOFFSET 11 @//E*O*F config.h// if test 3422 -ne "`wc -c <'config.h'`"; then echo shar: error transmitting "'config.h'" '(should have been 3422 characters)' fi fi # end of overwriting check echo shar: extracting "'digest.c'" '(5027 characters)' if test -f 'digest.c' ; then echo shar: will not over-write existing file "'digest.c'" else sed 's/^X//' >digest.c <<'@//E*O*F digest.c//' X/* X** vn news reader. X** X** digest.c - digest unpacking routines X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include "config.h" X#include "vn.h" X#include "head.h" X Xextern int Digest; Xextern int L_allow; Xextern int C_allow; Xextern PAGE Page; X Xextern char *F_head, *T_head, *L_head, *D_head; X Xdigest_page (idx,skip) Xint idx; X{ X char *ptr,name[24],*title,*index(); X FILE *fp; X int i,len; X char subj[RECLEN],date[RECLEN],from[RECLEN],junk[RECLEN],*str_store(); X long pos; X X Digest = Page.b[idx].art_id; X sprintf (name,"%d", Digest); X X if ((fp = fopen(name,"r")) == NULL) X return (-1); X X subj[0] = date[0] = from[0] = junk[0] = '\0'; X X skip_header (fp); X X /* skip over some articles if requested to */ X for (i=skip; i > 0; --i) X { X if (dig_advance(fp,from,subj,date,junk,&pos) < 0) X return (-1); X } X X /* every new call to a digest Page "loses" a small amount of storage */ X title = str_store(Page.b[idx].art_t); X if ((ptr = index(title,'~')) != 0) X *ptr = '\0'; X title [C_allow - 20] = '\0'; X X for (i=0; i < L_allow && X (len = dig_advance(fp,from,subj,date,junk,&pos)) >= 0; ++i) X { X Page.b[i].art_id = i+1+skip; X Page.b[i].art_mark = ' '; X subj [C_allow] = '\0'; X from [C_allow] = '\0'; X sprintf (name,"%d",len); X form_title (date,subj,name,from,100); X strcpy (Page.b[i].art_t,date); X } X X fclose (fp); X X if (i == 0) X return (-1); X X Page.h.name = title; X Page.h.artnum = i; X return (i); X} X X/* X returns name of file containing "article", NULL for failure X*/ Xchar * digest_extract (s,art) Xchar *s; Xint art; X{ X char name[24]; X FILE *fout,*fin; X char subj[RECLEN],date[RECLEN],from[RECLEN],bufr[RECLEN]; X char extra[RECLEN]; X char *index(); X long pos; X int lines; X X sprintf (name,"%d", Digest); X if ((fin = fopen(name,"r")) == NULL) X return (NULL); X X for (skip_header (fin); art > 0; --art) X if ((lines = dig_advance(fin,from,subj,date,extra,&pos)) < 0) X { X fclose (fin); X return (NULL); X } X X tmpnam(s); X X if ((fout = fopen(s,"w")) == NULL) X { X fclose (fin); X unlink (s); X return (NULL); X } X X fseek(fin,0L,0); X X while (fgets(bufr,RECLEN-1,fin) != NULL && index(bufr,':') != NULL) X { X if (strncmp(bufr,F_head,FHDLEN) == 0) X { X fprintf (fout,"%s%s\n",F_head,from); X continue; X } X if (strncmp(bufr,T_head,THDLEN) == 0) X { X fprintf (fout,"%s%s\n",T_head,subj); X continue; X } X if (strncmp(bufr,D_head,DHDLEN) == 0) X { X fprintf (fout,"%s%s\n",D_head,date); X continue; X } X /* defer line count header - it comes last */ X if (strncmp(bufr,L_head,LHDLEN) == 0) X continue; X fprintf (fout,"%s",bufr); X } X X /* toss in extra header lines, line count header, extra newline */ X fprintf (fout,"%s%s%d\n\n",extra,L_head,lines); X X fseek (fin,pos,0); X X while (fgets(bufr,RECLEN-1,fin) != NULL && strncmp(bufr,"--------",8) != 0) X fprintf(fout,"%s",bufr); X X fclose (fin); X fclose (fout); X return (s); X} X Xdig_list (s) Xchar *s; X{ X char *ptr,*out,*new,ns[L_tmpnam],tmp[RECLEN],*strtok(); X int i; X X prinfo ("Extracting articles ....."); X strcpy (tmp,s); X out = s; X X for (ptr = strtok(tmp," "); ptr != NULL; ptr = strtok(NULL," ")) X { X i = atoi(ptr); X if ((new = digest_extract(ns,i)) != NULL) X { X sprintf (out,"%s ",new); X out += strlen(new) + 1; X } X } X X *out = '\0'; X X if (*s == '\0') X strcpy (s,"NULLDIGEST"); X} X Xdig_ulist (s) Xchar *s; X{ X char *strtok(); X for (s = strtok(s," "); s != NULL; s = strtok(NULL," ")) X unlink (s); X} X X/* X returns # lines in article, -1 for failure X scans past article, returns position of start. X also returns "extra" header lines encountered, WITH newlines. X*/ Xstatic dig_advance (fp,from,subj,date,extra,pos) XFILE *fp; Xchar *from,*subj,*date,*extra; Xlong *pos; X{ X char buf[RECLEN]; X char *ptr, *index(); X int len,state,lcount; X X lcount = state = 0; X *extra = '\0'; X X while (fgets(buf,RECLEN-1,fp) != NULL) X { X buf[(len = strlen(buf) - 1)] = '\0'; X for (--len ; len >= 0 && buf[len] == ' ' || buf[len] == '\t'; --len) X buf[len] = '\0'; X ++len; X X switch(state) X { X case 0: X /* skip blank lines before header */ X if (len == 0) X break; X state = 1; /* fall through */ X case 1: X if (strncmp(buf,F_head,FHDLEN) == 0) X { X strcpy (from,buf+FHDLEN); X break; X } X if (strncmp(buf,T_head,THDLEN) == 0) X { X strcpy (subj,buf+THDLEN); X break; X } X if (strncmp(buf,D_head,DHDLEN) == 0) X { X strcpy (date,buf+DHDLEN); X break; X } X /* put wierd header lines in extra */ X if ((ptr = index(buf,':')) != NULL) X { X *ptr = '\0'; X if (index(buf, ' ') == NULL) X { X *ptr = ':'; X sprintf(extra,"%s\n",buf); X extra += strlen(extra); X break; X } X *ptr = ':'; X } X state = 2; X X /* remember the newline we lopped off */ X *pos = ftell(fp)-strlen(buf)-1; /* fall through */ X case 2: X ++lcount; X if (strncmp("--------",buf,8) == 0) X { X --lcount; X return (lcount); X } X break; X } X } X X return (-1); X} X Xstatic skip_header (fp) XFILE *fp; X{ X char buf[RECLEN]; X X while (fgets(buf,RECLEN-1,fp) != NULL) X if (strncmp("--------",buf,8) == 0) X break; X} @//E*O*F digest.c// if test 5027 -ne "`wc -c <'digest.c'`"; then echo shar: error transmitting "'digest.c'" '(should have been 5027 characters)' fi 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 sed 's/^X//' >envir_set.c <<'@//E*O*F envir_set.c//' X/* X** vn news reader. X** X** envir_set.c - routine to obtain pertinent environment variable settings X** and set up file / directory names X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include <pwd.h> X#include <sys/param.h> X#include "config.h" X Xextern char *Editor,*Ps1,*Mailer,*Printer,*Poster; Xextern char *Onews, *Newsrc, *Orgdir, *Savedir, *Ccfile; /* path names */ Xextern char Cxitop[], Cxitor[], Cxrtoi[], Cxptoi[]; X X#ifdef SYSV Xextern char *getcwd(); X#define getwd(a) getcwd(a,sizeof(a)) X#define MAXPATHLEN 240 X#else Xextern char *getwd(); X#endif X X/* X environment variable, original directory string setup. X*/ X Xenvir_set () X{ X char dbuf [MAXPATHLEN], *rcname, *ccname, *keyxln; X char *getenv(), *getcwd(), *str_store(); X struct passwd *ptr, *getpwuid(); X X if ((Ps1 = getenv("PS1")) == NULL) X Ps1 = DEF_PS1; X if ((Editor = getenv("EDITOR")) == NULL) X Editor=DEF_ED; X if ((Mailer = getenv("MAILER")) == NULL) X Mailer=DEF_MAIL; X if ((Poster = getenv("POSTER")) == NULL) X Poster=DEF_POST; X if ((Printer = getenv("PRINTER")) == NULL) X Printer=DEF_PRINT; X if ((rcname = getenv("NEWSRC")) == NULL) X rcname=DEF_NEWSRC; X if ((ccname = getenv("CCFILE")) == NULL) X ccname=DEF_CCFILE; X if ((keyxln = getenv("VNKEY")) == NULL) X keyxln=DEF_KEYXLN; X Savedir = getenv("VNSAVE"); X X /* X set original directory strings. create empty Newsrc if it doesn't exist X */ X X ptr = getpwuid (getuid()); X if ((Orgdir = getwd(dbuf)) == NULL) X printex ("cannot stat pwd"); X Orgdir = str_store (Orgdir); X if (Savedir == NULL) X Savedir = Orgdir; X if (*rcname != '/') X { X sprintf (dbuf, "%s/%s",ptr->pw_dir,rcname); X Newsrc = str_store (dbuf); X } X else X Newsrc = str_store (rcname); X if (*ccname != '/') X { X sprintf (dbuf, "%s/%s",ptr->pw_dir,ccname); X Ccfile = str_store (dbuf); X } X else X Ccfile = str_store (ccname); X sprintf (dbuf, "%s/%s%s",ptr->pw_dir,".vn","XXXXXX"); X Onews = str_store (mktemp(dbuf)); X if (access (Newsrc,0) != 0) X creat (Newsrc,0666); X X if (*keyxln != '/') X { X sprintf(dbuf, "%s/%s",ptr->pw_dir,keyxln); X set_kxln(dbuf); X } X else X set_kxln(keyxln); X} X Xstatic Xset_kxln(fname) Xchar *fname; X{ X FILE *fp; X int i; X char bufr[80]; X char in,out,*ptr; X char *index(), xln_str(); X X for (i=0; i < 128; ++i) X Cxitop[i] = Cxitor[i] = Cxptoi[i] = Cxrtoi[i] = i; X X if ((fp = fopen(fname,"r")) != NULL) X { X while(fgets(bufr,79,fp) != NULL) X { X if (strncmp(bufr+1,"==",2) == 0) X ptr = bufr+2; X else X ptr = index(bufr+1,'='); X if (ptr == NULL) X continue; X *ptr = '\0'; X ++ptr; X in = xln_str(bufr+1); X out = xln_str(ptr); X switch(bufr[0]) X { X case 'r': X case 'R': X Cxrtoi[out] = in; X Cxitor[in] = out; X break; X case 'p': X case 'P': X Cxptoi[out] = in; X Cxitop[in] = out; X default: X break; X } X } X fclose(fp); X } X} X Xstatic char Xxln_str(s) Xchar *s; X{ X if (*s < '0' || *s > '9') X return(*s & 0x7f); X return((char)(atoi(s) & 0x7f)); X} @//E*O*F envir_set.c// if test 2931 -ne "`wc -c <'envir_set.c'`"; then echo shar: error transmitting "'envir_set.c'" '(should have been 2931 characters)' fi 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 sed 's/^X//' >groupdir.c <<'@//E*O*F groupdir.c//' X/* X** vn news reader. X** X** groupdir.c - translation between newsgroup name and directory X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include "config.h" X#include "vn.h" X Xextern PAGE Page; X X/* X g_dir converts newsgroup name to directory string X*/ Xg_dir(s,t) Xchar *s,*t; X{ X char *ptr, *index(); X sprintf (t,"%s/%s",SPOOLDIR,s); X for (ptr=t+strlen(SPOOLDIR)+1; (ptr = index(ptr,'.')) != NULL; *ptr = '/') X ; X} X X X/* X change directory to group X*/ Xcd_group () X{ X char dbuf [RECLEN]; X g_dir ((Page.h.group)->nd_name,dbuf); X if (chdir(dbuf) < 0) X { X Page.h.artnum = 1; X Page.b[0].art_id = 0; X strcpy (Page.b[0].art_t, "CANNOT FIND NEWSGROUP"); X } X} @//E*O*F groupdir.c// if test 686 -ne "`wc -c <'groupdir.c'`"; then echo shar: error transmitting "'groupdir.c'" '(should have been 686 characters)' fi 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 sed 's/^X//' >hash.c <<'@//E*O*F hash.c//' X/* X** vn news reader. X** X** hash.c - hash table routines X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include "config.h" X#include "tune.h" X#include "vn.h" X X/* X** hash table manipulation routines: X** also sets Ncount, allocates Newsorsder array, and sets Newsorder X** initially to order newsgroups were entered in (active file order) X*/ X Xextern int Ncount; Xextern NODE **Newsorder; X Xstatic NODE *Tab [HASHSIZE]; /* hash Table */ X Xhashinit () X{ X int i; X for (i=0; i < HASHSIZE; ++i) X Tab[i] = NULL; X Ncount = 0; X} X X/* X enter new node (name s, articles n, low l) in hash Table, X initial flags = 0. As nodes are entered, pnum item is temporarily X used to indiacte entry order for initial construction of Newsorder X array via entry_order(); X*/ XNODE *hashenter(s,n,l) Xchar *s; Xint n; Xint l; X{ X char *str_store(); X NODE *ptr,*node_store(); X int i; X X i=hash(s); X ptr = node_store(); X ptr->next = Tab[i]; X Tab[i] = ptr; X if (l > n) X l = n; X ptr->pnum = Ncount; X ++Ncount; X ptr->rdnum = l; X ptr->state = 0; X ptr->art = n; X ptr->nd_name = str_store(s); X return (ptr); X} X XNODE *hashfind(s) Xchar *s; X{ X NODE *ptr; X X for (ptr = Tab[hash(s)]; ptr != NULL && strcmp(ptr->nd_name,s) != 0; X ptr = ptr->next) X ; X return (ptr); X} X X/* X** entry order is called after all hash_enter's have been done, PRIOR X** to the use of pnum item for anything else. It constructs the initial X** Newsorder array. X*/ Xentry_order() X{ X int i; X NODE *ptr; X X if ((Newsorder = (NODE **) malloc(Ncount*sizeof(NODE *))) == NULL) X printex("Cannot allocate memory for Newsorder array"); X for (i=0; i < HASHSIZE; ++i) X { X for (ptr = Tab[i]; ptr != NULL; ptr = ptr->next) X Newsorder[ptr->pnum] = ptr; X } X} X Xstatic hash (s) Xchar *s; X{ X int rem; X for (rem=0; *s != '\0'; ++s) X rem = (rem*128 + (*s&0x7f)) % HASHSIZE; X return (rem); X} @//E*O*F hash.c// if test 1842 -ne "`wc -c <'hash.c'`"; then echo shar: error transmitting "'hash.c'" '(should have been 1842 characters)' fi 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 sed 's/^X//' >head.h <<'@//E*O*F head.h//' X/* X** vn news reader. X** X** head.h - header line strings and lengths X** X** see copyright disclaimer / history in vn.c source file X*/ X X/* X header lines and associated lengths. Strings should X actually be used once in strings.c. X*/ X#define RHEAD "References: " X#define RHDLEN 12 X#define MHEAD "Message-ID: " X#define MHDLEN 12 X#define PHEAD "Path: " X#define PHDLEN 6 X#define DHEAD "Date: " X#define DHDLEN 6 X#define RTHEAD "Reply-To: " X#define RTHDLEN 10 X#define TOHEAD "To: " X#define TOHDLEN 4 X#define FHEAD "From: " X#define FHDLEN 6 X#define FTHEAD "Followup-To: " X#define FTHDLEN 13 X#define DISHEAD "Distribution: " X#define DISHDLEN 14 X#define THEAD "Subject: " X#define THDLEN 9 X#define LHEAD "Lines: " X#define LHDLEN 7 X#define NHEAD "Newsgroups: " X#define NHDLEN 12 X X#define CHFIRST "FSL" /* first char's of those used in page display */ @//E*O*F head.h// if test 839 -ne "`wc -c <'head.h'`"; then echo shar: error transmitting "'head.h'" '(should have been 839 characters)' fi fi # end of overwriting check echo shar: extracting "'mf.nore'" '(254 characters)' if test -f 'mf.nore' ; then echo shar: will not over-write existing file "'mf.nore'" else sed 's/^X//' >mf.nore <<'@//E*O*F mf.nore//' XCFLAGS= -O -DJOBCONTROL XLIBS= -ltermcap X XOBJS= hash.o groupdir.o envir_set.o newsrc.o pagefile.o reader.o storage.o sig_set.o term_set.o tty_set.o userlist.o vn.o vnglob.o digest.o strings.o tmpnam.o reg.o strtok.o X Xvn: $(OBJS) X cc -o vn $(OBJS) $(LIBS) @//E*O*F mf.nore// if test 254 -ne "`wc -c <'mf.nore'`"; then echo shar: error transmitting "'mf.nore'" '(should have been 254 characters)' fi fi # end of overwriting check echo shar: extracting "'mf.re'" '(272 characters)' if test -f 'mf.re' ; then echo shar: will not over-write existing file "'mf.re'" else sed 's/^X//' >mf.re <<'@//E*O*F mf.re//' XCFLAGS= -O -Dregfree=free -DJOBCONTROL XREGLIB= XLIBS= -ltermcap X XOBJS= hash.o groupdir.o envir_set.o newsrc.o pagefile.o reader.o storage.o sig_set.o term_set.o tty_set.o userlist.o vn.o vnglob.o digest.o strings.o tmpnam.o X Xvn: $(OBJS) X cc -o vn $(OBJS) $(LIBS) $(REGLIB) @//E*O*F mf.re// if test 272 -ne "`wc -c <'mf.re'`"; then echo shar: error transmitting "'mf.re'" '(should have been 272 characters)' fi fi # end of overwriting check echo shar: extracting "'mf.sysv'" '(271 characters)' if test -f 'mf.sysv' ; then echo shar: will not over-write existing file "'mf.sysv'" else sed 's/^X//' >mf.sysv <<'@//E*O*F mf.sysv//' XCFLAGS= -O -Dregfree=free -Dindex=strchr -Drindex=strrchr -DSYSV XLIBS= -ltermcap X XOBJS= hash.o groupdir.o envir_set.o newsrc.o pagefile.o reader.o storage.o sig_set.o term_set.o tty_set.o userlist.o vn.o vnglob.o digest.o strings.o X Xvn: $(OBJS) X cc -o vn $(OBJS) $(LIBS) @//E*O*F mf.sysv// if test 271 -ne "`wc -c <'mf.sysv'`"; then echo shar: error transmitting "'mf.sysv'" '(should have been 271 characters)' fi 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 sed 's/^X//' >newsrc.c <<'@//E*O*F newsrc.c//' X/* X** vn news reader. X** X** newsrc.c - routines to deal with the newsrc file X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include <ctype.h> X#include "config.h" X#include "tty.h" X#include "vn.h" X Xextern NODE **Newsorder; Xextern char *Onews, *Newsrc; Xextern int Ncount, Lrec, C_allow; Xextern int Ntopt, Nntopt, Nwopt, Nnwopt; Xextern char *Topt[], *Negtopt[], *Wopt[], *Negwopt[]; Xextern int Nounsub, Listfirst; X X/* X global flags signifying options set X*/ X#define GF_ALL 1 /* -x option - scan everything */ X#define GF_SPEC 2 /* -n option(s) - user specified groups */ X#define GF_OVER 4 /* command line specification - overide marks */ X Xstatic char *Options[OPTLINES]; Xstatic int New_idx, Max_name, Optlines; Xstatic unsigned Gflags = 0; X X/* X routines for dealing with the .newsrc file and options X*/ X X/* X command name argument is already omitted from argv argc in this X routine. Only the option arguments are present. We process X options before we scan the rest of .newsrc, which redoes Newsorder, X ie. we don't clobber Ncount until options are processed. X*/ Xscan_newsrc (argc,argv) Xint argc; Xchar **argv; X{ X FILE *fp, *fopen(); X static char marks[] = X { X NEWS_ON, NEWS_OFF, '\0' X }; X char *str_store (); X int line, len, num; X char buf [RECLEN], trail, optpflag, submark, *fret, *ptr, *strpbrk(), *strtok(); X X X /* initialize hash table, open temp file, fill table with active articles */ X hashinit (); X fill_active (); X temp_open(); X X if (argc > 0) X { X Gflags |= GF_OVER; X arg_opt(argc,argv); X optpflag = 'y'; X } X else X optpflag = 'n'; X X if ((fp = fopen (Newsrc,"r")) == NULL) X printex ("can't open %s for reading",Newsrc); X X Optlines = 0; X X for (line = 1; (fret = fgets(buf,RECLEN-1,fp)) != NULL && emptyline(buf) == 1; ++line) X ; X if (fret != NULL && strncmp (buf,"options",7) == 0) X { X Options[0] = str_store(buf); X Optlines = 1; X trail = buf [strlen(buf)-2]; X for ( ; (fret = fgets(buf,RECLEN-1,fp)) != NULL; ++line) X { X if (trail != '\\' && buf[0] != ' ' && buf[0] != '\t') X break; X if (Optlines >= OPTLINES) X printex ("%s - too many option lines (%d allowed)",Newsrc,OPTLINES); X Options[Optlines] = str_store(buf); X ++Optlines; X if ((len = strlen(buf)) >= 2 && buf[len-2] != '\\') X trail = buf[len-2]; X else X trail = '\0'; X } X } X X /* do the options from the newsrc file if there weren't command line args */ X if (Optlines > 0 && optpflag == 'n') X newsrc_opt (); X X Ncount = 0; X X for ( ; fret != NULL; ++line, fret = fgets(buf,RECLEN-1,fp)) X { X if (emptyline(buf) == 1) X continue; X if ((ptr = strpbrk(buf,marks)) == NULL) X { X fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n", X line,Newsrc,buf); X continue; X } X submark = *ptr; X *ptr = '\0'; X ++ptr; X num = 0; X for (ptr = strtok(ptr," ,-\n"); ptr != NULL; ptr = strtok(NULL," ,-\n")) X { X len = atoi (ptr); X for ( ; *ptr >= '0' && *ptr <= '9'; ++ptr) X ; X if (*ptr != '\0' || len < num) X { X num = -1; X fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n", X line,Newsrc,buf); X break; X } X num = len; X } X if (num < 0) X continue; X chkgroup (buf,submark,num); X } X fclose (fp); X X /* now take care of groups not specified in .newsrc */ X art_active(); X X /* free up the option string storage */ X for (num=0; num < Ntopt; ++num) X regfree (Topt[num]); X for (num=0; num < Nwopt; ++num) X regfree (Wopt[num]); X for (num=0; num < Nntopt; ++num) X regfree (Negtopt[num]); X for (num=0; num < Nnwopt; ++num) X regfree (Negwopt[num]); X Ntopt = Nwopt = Nntopt = Nnwopt = 0; X} X Xstatic emptyline(s) Xchar *s; X{ X while (isspace(*s)) X ++s; X if (*s == '\0') X return (1); X return (0); X} X X/* X fill hash table from active news group list X temporarily makes "Newsorder" active list order. X This is needed to be able to process options X before scanning user order. X*/ Xstatic fill_active () X{ X FILE *f,*fopen (); X char *nread, act_rec[RECLEN], *strtok(); X int num,lownum; X X Max_name = 0; X if ((f = fopen (ACTFILE,"r")) == NULL) X printex ("couldn't open %s\n",ACTFILE); X while (fgets(act_rec, RECLEN-1, f) != NULL) X { X if (strtok (act_rec," \n") == NULL) X continue; X nread = strtok (NULL, " \n"); X if (nread != NULL) X num = atoi(nread); X else X num = 0; X nread = strtok (NULL, " \n"); X if (nread != NULL) X lownum = atoi(nread); X else X lownum = 0; X if (lownum > 0) X --lownum; X if (strlen(act_rec) > Max_name) X Max_name = strlen(act_rec); X hashenter (act_rec, num, lownum); X } X X /* construct initial Newsorder */ X entry_order(); X X fclose (f); X} X X/* X check active newsgroups not mentioned in NEWSRC file X (FLG_SCAN not set) X*/ Xstatic art_active () X{ X char act_rec[RECLEN], *strtok(); X NODE *ptr,*hashfind(); X FILE *f,*fopen(); X if ((f = fopen (ACTFILE,"r")) == NULL) X printex ("couldn't open %s\n",ACTFILE); X New_idx = Ncount; X while (fgets(act_rec, RECLEN-1, f) != NULL) X { X if (strtok (act_rec," \n") == NULL) X continue; X if ((ptr = hashfind (act_rec)) == NULL) X printex("%s - unexpected hash table failure",act_rec); X if ((ptr->state & FLG_SCAN) == 0) X chkgroup (ptr->nd_name, NEWS_ON, 0); X } X} X X/* X check group for new articles: X s - group X c - subscription indicator from NEWSRC X n - number read X*/ Xstatic chkgroup (s,c,n) Xchar *s,c; Xint n; X{ X NODE *ptr, *hashfind(); X int lold,lowart; X lold = Lrec; X if ((ptr = hashfind(s)) != NULL && (ptr->state & FLG_SCAN) == 0) X { X Newsorder [Ncount] = ptr; X ++Ncount; X ptr->pages = 0; X ptr->state |= FLG_SCAN; X if (c == NEWS_ON) X ptr->state |= FLG_SUB; X /* if "read" more than exist reset to zero */ X if (n > ptr->art) X n = 0; X lowart = ptr->rdnum; X if (n < ptr->rdnum) X n = ptr->rdnum; X ptr->orgrd = ptr->pgrd = ptr->rdnum = n; X ptr->pgshwn = 0L; X X /* X ** scan decision is rather complex, since GF_ALL setting X ** overides "n" value, GF_SPEC indicates FLG_SPEC flag used. X ** if GF_OVER set, FLG_SPEC overides subscription mark, else X ** FLG_SPEC AND subscribed is neccesary. X */ X if ((Gflags & GF_SPEC) != 0) X { X if ((ptr->state & FLG_SPEC) == 0) X c = NEWS_OFF; X else X { X if ((Gflags & GF_OVER) != 0) X c = NEWS_ON; X } X } X if ((Gflags & GF_ALL) != 0) X n = lowart; X if (c == NEWS_ON && ptr->art > n) X { X outgroup (s,n,ptr->art); X if (lold != Lrec) X { X ptr->pnum = lold+1; X ptr->pages = Lrec - lold; X ptr->state |= FLG_PAGE; X } X } X } X} X X/* X wr_newsrc writes the .newsrc file X*/ Xwr_newsrc () X{ X FILE *fp,*fopen(); X NODE *p; X char c; X int i,rc; X X if (link(Newsrc,Onews) < 0) X printex ("can't backup %s to %s before writing",Newsrc,Onews); X X if (unlink(Newsrc) < 0 || (fp = fopen(Newsrc,"w")) == NULL) X printex ("can't open %s for writing (backed up in %s)",Newsrc,Onews); X else X { X clearerr(fp); X for (i=0; (rc = ferror(fp)) == 0 && i < Optlines; ++i) X fprintf (fp,"%s",Options[i]); X for (i=0; rc == 0 && i < Ncount; ++i) X { X p = Newsorder[i]; X if ((p->state & FLG_SUB) == 0) X c = NEWS_OFF; X else X c = NEWS_ON; X#ifdef OLDRC X fprintf (fp,"%s%c %d\n",p->nd_name,c,p->rdnum); X#else X if (p->rdnum > 0) X fprintf (fp,"%s%c 1-%d\n",p->nd_name,c,p->rdnum); X else X fprintf (fp,"%s%c 0\n",p->nd_name,c); X#endif X rc = ferror(fp); X } X fclose (fp); X if (rc != 0) X printex ("write of %s failed, old copy stored in %s",Newsrc,Onews); X else X unlink (Onews); X } X} X Xnew_groups () X{ X int i,wrem,w; X char fs[24],c_end; X if (New_idx >= Ncount || C_allow < (w = Max_name+1)) X return (0); X term_set (ERASE); X printf (NEWGFORM,Newsrc); X sprintf (fs,"%%-%ds%%c",Max_name); X wrem = C_allow; X for (i=New_idx; i < Ncount; ++i) X { X if ((wrem -= w) < w) X { X wrem = C_allow; X c_end = '\n'; X } X else X c_end = ' '; X printf (fs,(Newsorder[i])->nd_name,c_end); X } X if ((++wrem) < C_allow) X putchar ('\n'); X return (i-New_idx); X} X X/* X arg_opt must be called prior to option scanning, since X it uses the options array. This is a bit of a kludge, X but it saves a bunch of work. NOTE - no command name argument X*/ Xstatic arg_opt (argc,argv) Xint argc; Xchar **argv; X{ X if (argc > OPTLINES) X printex ("too many command line options (%d allowed)\n",OPTLINES); X for (Optlines=0; Optlines < argc; ++Optlines) X { X Options[Optlines] = *argv; X ++argv; X } X newsrc_opt(); X} X X/* X option setting routine: X sets global flags: GF_ALL for -x option GF_SPEC for -n. X sets up filter array for article scanning X*/ Xstatic newsrc_opt() X{ X int i; X char curopt,tmp[RECLEN],*tok,*strtok(),*index(); X X Nounsub = Listfirst = 0; X Ntopt = Nwopt = Nnwopt = Nntopt = 0; X curopt = '\0'; X for (i=0; i < Optlines; ++i) X { X strcpy(tmp,Options[i]); X for (tok = strtok(tmp,",\\ \t\n"); tok != NULL; tok = strtok(NULL,",\\ \t\n")) X { X if (*tok != '-') X do_opt (curopt,tok); X else X { X for (++tok; index("nwt",*tok) == NULL; ++tok) X { X /* options with no strings */ X switch(*tok) X { X case 'S': X Gflags &= ~GF_OVER; X break; X case '%': X Listfirst = 1; X break; X case 'U': X Nounsub = 1; X break; X#ifdef OLDRC X case 'i': X /* Treat "-i" as synonym for "-x" */ X#endif X case 'x': X Gflags |= GF_ALL; X default: X break; X } X } X curopt = *tok; X if (*(++tok) != '\0') X do_opt (curopt,tok); X } X } X } X} X X/* do_opt is for options with strings attached */ Xstatic do_opt (opt,str) Xchar opt, *str; X{ X switch (opt) X { X case 'n': X Gflags |= GF_SPEC; X specmark(str); X break; X case 'w': X specfilter (FIL_AUTHOR,str); X break; X case 't': X specfilter (FIL_TITLE,str); X break; X default: X#ifdef OLDRC X Gflags |= GF_SPEC; /* Assume anything else is newsgroup */ X specmark(str); X#endif X break; X } X} X Xstatic specfilter (comp,str) Xchar comp,*str; X{ X char *regcmp(); X int *count; X char **rex; X X /* X ** we may set rex one past end of array. we will error before X ** referencing it if that's the case, however. X */ X if (*str == '!') X { X if (comp == FIL_TITLE) X { X count = &Nntopt; X rex = Negtopt + *count; X } X else X { X count = &Nnwopt; X rex = Negwopt + *count; X } X ++str; X } X else X { X if (comp == FIL_TITLE) X { X count = &Ntopt; X rex = Topt + *count; X } X else X { X count = &Nwopt; X rex = Wopt + *count; X } X } X if (*count >= NUMFILTER) X printex ("too many %c options, %d allowed",comp,NUMFILTER); X if ((*rex = regcmp(str,(char *) 0)) == NULL) X printex ("%c option regular expression syntax: %s",comp,str); X ++(*count); X} X X/* X handle the newsgroup specification string. X ("all" convention - braack!!!) X*/ Xstatic specmark (s) Xchar *s; X{ X unsigned ormask,andmask; X int i,len; X char *ptr,*re,pattern[RECLEN],*regex(),*regcmp(); X X if (*s == '!') X { X ++s; X ormask = 0; X andmask = ~FLG_SPEC; X if (*s == '\0') X return; X } X else X { X ormask = FLG_SPEC; X andmask = 0xffff; X } X X /* convert "all" not bounded by alphanumerics to ".*". ".all" becomes ".*" */ X for (ptr = s; (len = findall(ptr)) >= 0; ptr += len+1) X { X if (len > 0 && isalnum (s[len-1])) X continue; X if (isalnum (s[len+3])) X continue; X if (len > 0 && s[len-1] == '.') X { X --len; X strcpy (s+len,s+len+1); X } X s[len] = '.'; X s[len+1] = '*'; X strcpy (s+len+2,s+len+3); X } X X /* now use regular expressions */ X sprintf (pattern,"^%s$",s); X if ((re = regcmp(pattern,(char *) 0)) == NULL) X printex ("n option regular expression syntax: %s",s); X for (i=0; i < Ncount; ++i) X { X if (regex(re,(Newsorder[i])->nd_name) != NULL) X { X (Newsorder[i])->state |= ormask; X (Newsorder[i])->state &= andmask; X } X } X regfree (re); X} X Xstatic findall (s) Xchar *s; X{ X int len; X for (len=0; *s != '\0'; ++s,++len) X { X if (*s == 'a' && strncmp(s,"all",3) == 0) X return (len); X } X return (-1); X} @//E*O*F newsrc.c// if test 11576 -ne "`wc -c <'newsrc.c'`"; then echo shar: error transmitting "'newsrc.c'" '(should have been 11576 characters)' fi 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 sed 's/^X//' >pagefile.c <<'@//E*O*F pagefile.c//' X/* X** vn news reader. X** X** pagefile.c - routines to deal with page display tempfile X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X X#ifdef SYSV X#include <sys/types.h> X#include <fcntl.h> X#endif X X#include <sys/file.h> X#include "vn.h" X#include "head.h" X Xextern int Ncount,Lrec,L_allow,Cur_page,C_allow; Xextern int Nwopt, Nnwopt, Ntopt, Nntopt; Xextern char *Wopt[], *Topt[], *Negtopt[], *Negwopt[]; Xextern NODE **Newsorder; Xextern PAGE Page; Xextern int Digest; X Xextern char *Aformat; X Xextern char *T_head, *F_head, *L_head; X Xstatic int Tdes; /* temp file descriptor */ Xstatic int Pgsize; /* block size for seeking file */ X X/* X routines which deal with the temp file containing X display pages. Note the "invisible" file feature - X tempfile is unlinked from /usr/tmp immediately. when X Tdes is closed by UNIX the disk space will be given back. X*/ X Xtemp_open () X{ X char tmpart [L_tmpnam]; X Lrec = -1; X tmpnam (tmpart); X Pgsize = sizeof (HEAD) + L_allow * sizeof(BODY); X if ((Tdes = open(tmpart,O_RDWR|O_CREAT)) < 0) X printex ("can't open %s",tmpart); X unlink (tmpart); X} X X/* X create page records for newsgroup s X all articles between low and hi are to be included. X*/ Xoutgroup (s,low,hi) Xchar *s; Xint low,hi; X{ X int i,aid; X char title[RECLEN],gd[RECLEN]; X g_dir(s,gd); X if (chdir(gd) < 0) X { X grp_indic(s,0); X return; X } X grp_indic(s,1); X aid = 0; X for (i=low+1; i <= hi; ++i) X { X if (digname (i,title) >= 0) X { X Page.b[aid].art_id = i; X Page.b[aid].art_mark = ' '; X strcpy (Page.b[aid].art_t, title); X if ((++aid) >= L_allow) X { X X /* start next page */ X Page.h.artnum = L_allow; X do_write (); X ++Lrec; X aid = 0; X } X } X } X X /* last page (partial) */ X if (aid != 0) X { X Page.h.artnum = aid; X do_write (); X ++Lrec; X } X} X X/* X set current page to n. use Pgsize and lseek to find it in X temp file (descriptor Tdes). X*/ Xfind_page (n) Xint n; X{ X long off,lseek(); X int i,last; X Cur_page = n; X off = Pgsize; X off *= (long) n; X lseek (Tdes, off, 0); X if (read(Tdes, (char *) &(Page.h), sizeof(HEAD)) < sizeof(HEAD)) X printex("bad temp file read"); X i = Pgsize - sizeof(HEAD); X if (read(Tdes, (char *) Page.b, i) < i) X printex("bad temp file read"); X last = -1; X for (i=0; i < Ncount; ++i) X { X if ((Newsorder[i])->pages > 0) X { X if ((Newsorder[i])->pnum > n) X break; X last = i; X } X } X if (last < 0) X printex ("can't find page %d",n); X Page.h.group = Newsorder[last]; X Page.h.name = (Page.h.group)->nd_name; X cd_group (); X} X Xwrite_page () X{ X long off,lseek(); X if (!Digest) X { X off = Pgsize; X off *= (long) Cur_page; X lseek (Tdes, off, 0); X do_write(); X } X} X Xstatic do_write() X{ X int num; X X if (write(Tdes, (char *) &(Page.h), sizeof(HEAD)) < sizeof(HEAD)) X printex ("Bad temp file write"); X num = L_allow * sizeof(BODY); X if (write(Tdes, (char *) Page.b, num) < num) X printex ("Bad temp file write"); X} X X/* X find article title: X n - articles id X t - returned title - must have storage for RECLEN, assumed to be X > 3 * max title length also. X*/ Xstatic digname (n, t) Xint n; Xchar *t; X{ X int i,j; X FILE *fp,*fopen(); X char ff [MAX_C+1],fn [MAX_C+1],fl [MAX_C+1],*index(); X X /* open article */ X sprintf (t,"%d", n); X if ((fp = fopen(t,"r")) == NULL) X return (-1); X X /* get subject, from and lines by reading article */ X ff[0] = fn[0] = fl[0] = '?'; X ff[1] = fn[1] = fl[1] = '\0'; X ff[C_allow] = fn[C_allow] = fl[C_allow] = '\0'; X for (i = 0; i < HDR_LINES && fgets(t,RECLEN-1,fp) != NULL; ++i) X { X if (index(CHFIRST,t[0]) == NULL) X continue; X t[strlen(t) - 1] = '\0'; X if (strncmp(T_head,t,THDLEN) == 0) X { X for (j=0; j < Nntopt; ++j) X { X if (regex(Negtopt[j],t+THDLEN) != NULL) X { X fclose(fp); X return(-1); X } X } X if (Ntopt > 0) X { X for (j=0; j < Ntopt; ++j) X { X if (regex(Topt[j],t+THDLEN) != NULL) X break; X } X if (j >= Ntopt) X { X fclose(fp); X return(-1); X } X } X strncpy(fn,t+THDLEN,C_allow); X continue; X } X if (strncmp(F_head,t,FHDLEN) == 0) X { X for (j=0; j < Nnwopt; ++j) X { X if (regex(Negwopt[j],t+FHDLEN) != NULL) X { X fclose(fp); X return(-1); X } X } X if (Nwopt > 0) X { X for (j=0; j < Nwopt; ++j) X { X if (regex(Wopt[j],t+FHDLEN) != NULL) X break; X } X if (j >= Nwopt) X { X fclose(fp); X return(-1); X } X } X strncpy(ff,t+FHDLEN,C_allow); X continue; X } X if (strncmp(L_head,t,LHDLEN) == 0) X { X strncpy(fl,t+LHDLEN,C_allow); X break; X } X } X X fclose (fp); X X /* reject empty or 1 line files */ X if (i < 2) X return (-1); X X form_title (t,fn,fl,ff,n); X return (0); X} X Xform_title (t,fn,fl,ff,n) Xchar *t,*fn,*fl,*ff; Xint n; X{ X char *ptr,*index(); X int i; X X if ((ptr = index(ff,'(')) != NULL && strlen(ptr) > 3) X ff = ptr; X sprintf (t,TFORMAT,fn,fl,ff); X sprintf(ff,Aformat,' ',' ',n); X i = C_allow - strlen(ff) + 1; /* remember newline in Aformat */ X t[i] = '\0'; X ctl_xlt(t); X return (0); X} X X/* replace control characters in titles */ Xstatic ctl_xlt(s) Xchar *s; X{ X while (*s != '\0') X { X if (*s < ' ') X *s += 'A' - 1; X ++s; X } X} @//E*O*F pagefile.c// if test 5053 -ne "`wc -c <'pagefile.c'`"; then echo shar: error transmitting "'pagefile.c'" '(should have been 5053 characters)' fi 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 sed 's/^X//' >reader.h <<'@//E*O*F reader.h//' X/* X** vn news reader. X** X** reader.h - article reading interface definitions X** X** see copyright disclaimer / history in vn.c source file X*/ X X#define PAGE_MID ":more (%2d%%):" X#define PAGE_NEXT ":next article:" X#define PAGE_END ":end:" X#define PAGE_NO ":?:" X#define PPR_MAX 18 /* maximum length of PAGE prompts */ X X/* X reading commands: no control chars, add help message to helppg X SAVE, PRINT, HEADTOG and SETROT are also recognized X*/ X#define HPG_HEAD "toggle header print flag" X#define HPG_ROT "toggle rotation" X#define HPG_SAVE "save article in a file" X#define HPG_PRINT "print article" X#define PG_NEXT 'n' X#define HPG_NEXT "next article, if any" X#define PG_QUIT 'q' X#define HPG_QUIT "quit reading articles, if any more to read" X#define PG_FLIP 'Q' X#define HPG_FLIP "quit reading, and turn to next page of articles" X#define PG_FOLLOW 'f' X#define HPG_FOLLOW "post followup to article" X#define PG_REPLY 'm' X#define HPG_REPLY "send mail to author of article" X#define PG_HELP '?' X#define HPG_HELP "see this help menu" X#define PG_REWIND 'r' X#define HPG_REWIND "rewind article to beginning" X#define PG_WIND 'e' X#define HPG_WIND "seek to end of article (to next/end prompt)" X#define PG_STEP '\n' X#define HPG_STEP "next line" X#define PG_SEARCH '/' X#define HPG_SEARCH "search for regular expression in remainder of article" X#define SEARCHFORM "search pattern (%s) ? " X#define HPG_DEF "\n anything else to continue normal reading" X#define HPG_EDEF "\n anything else to try reading next article, if any" @//E*O*F reader.h// if test 1498 -ne "`wc -c <'reader.h'`"; then echo shar: error transmitting "'reader.h'" '(should have been 1498 characters)' fi 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 sed 's/^X//' >reg.c <<'@//E*O*F reg.c//' X/* X** vn news reader. X** X** reg.c - implementation of regex / regcmp on top of UCB library X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X X#define RGBLKSIZE 20 X Xstruct _regtab X{ X struct _regtab *link; X char *regstr; X}; X Xtypedef struct _regtab REGTAB; X Xstatic REGTAB *Chain = NULL; Xstatic REGTAB *Free = NULL; Xstatic REGTAB *Compiled = NULL; X Xregfree(s) Xchar *s; X{ X REGTAB *ptr,*cmp,*old; X X cmp = (REGTAB *) s; X old = NULL; X X for (ptr = Chain; ptr != NULL; ptr = (old = ptr)->link) X { X if (ptr == cmp) X { X if (old == NULL) X Chain = Chain->link; X else X old->link = ptr->link; X ptr->link = Free; X Free = ptr; X break; X } X } X} X Xchar *regcmp(str) Xchar *str; X{ X int i; X char *str_store(); X char *re_comp(); X X if (re_comp(str) != NULL) X { X Compiled = NULL; /* make sure we're OK */ X return(NULL); X } X X if (Free == NULL) X { X Free = (REGTAB *) malloc(RGBLKSIZE * sizeof(REGTAB)); X if (Free == NULL) X printex ("regcmp: memory allocation failure"); X for (i = 0; i < RGBLKSIZE - 1; ++i) X Free[i].link = Free + i + 1; X Free[i].link = NULL; X } X X Compiled = Free; X Free = Free->link; X X Compiled->link = Chain; X Chain = Compiled; X Compiled->regstr = str_store(str); X X return ((char *) Compiled); X} X Xchar *regex(reg,str) Xchar *reg,*str; X{ X REGTAB *cmp; X X cmp = (REGTAB *) reg; X X if (cmp == Compiled) X { X if (re_exec(str)) X return(str); X return (NULL); X } X X for (Compiled = Chain; Compiled != NULL; Compiled = Compiled->link) X { X if (Compiled == cmp) X break; X } X X if (Compiled == NULL) X printex ("regex: bad pointer"); X X re_comp(Compiled->regstr); X X if (re_exec(str)) X return(str); X X return(NULL); X} @//E*O*F reg.c// if test 1656 -ne "`wc -c <'reg.c'`"; then echo shar: error transmitting "'reg.c'" '(should have been 1656 characters)' fi 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 sed 's/^X//' >sig_set.c <<'@//E*O*F sig_set.c//' X/* X** vn news reader. X** X** sig_set.c - signal handler X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include <sys/signal.h> X#include <sgtty.h> X#include <setjmp.h> X#include "tty.h" X#include "vn.h" X#include "config.h" X Xextern int L_allow; Xextern char *Version; X Xstatic int Sigflag=BRK_INIT; /* phase of interaction */ Xstatic FILE **Fpseek; /* article reading file pointer pointer */ Xstatic int Foreground; Xstatic jmp_buf Jumploc; /* for BRK_SESS phase */ Xstatic char *Cur_scn; /* current group name being scanned */ X X/* X interrupt handler - unusual termination (longjmp and printex aborts) X if not abort, remember to reset signal trap X CAUTION - the passing of a jump buffer is a little dicey - assumes X type jump_buf is an array. X X sigcatch and sig_set control a lot of i/o on stderr also, since X it is so intimately related to signal interaction. Note that the X SIGTSTP action causes a "stopped on tty output" if raw terminal X mode is restored by tty_set(RESTORE). We don't get it if we were X already cooked since tty_set avoids calling ioctl if it doesn't X have to. X*/ Xstatic sigcatch (sig) Xint sig; X{ X char buf [MAX_C+1]; X int pgrp; X X /* disable signal while processing it */ X signal (sig,SIG_IGN); X X switch (sig) X { X case SIGINT: X case SIGQUIT: X break; X X#ifdef JOBCONTROL X case SIGTSTP: X /* ignore SIGTTOU so we don't get stopped if [kc]sh grabs the tty */ X signal(SIGTTOU, SIG_IGN); X tty_set (SAVEMODE); X term_set (MOVE,0,L_allow+RECBIAS-1); X printf ("\n"); X Foreground = 0; X fflush (stdout); X fflush (stderr); X signal(SIGTTOU, SIG_DFL); X X /* Send the TSTP signal to suspend our process group */ X signal(SIGTSTP, SIG_DFL); X sigsetmask(0); X kill (0, SIGTSTP); X X /* WE ARE NOW STOPPED */ X X /* X WELCOME BACK! X if terminals process group is ours, we are foregrounded again X and can turn newsgroup name printing back on X */ X tty_set (RESTORE); X switch (Sigflag) X { X case BRK_SESS: X signal (SIGTSTP,sigcatch); X longjmp (Jumploc,1); X case BRK_IN: X ioctl (1,TIOCGPGRP,&pgrp); X if (pgrp == getpgrp(0)) X { X Foreground = 1; X if (Cur_scn != NULL) X fgprintf (" %s\n",Cur_scn); X } X break; X default: X break; X } X signal (SIGTSTP,sigcatch); X return; X#endif X default: X printex (BRK_MSG,sig); X } X X /* QUIT and INTERRUPT signals */ X switch (Sigflag) X { X case BRK_SESS: X /* if in session, ask if really a quit, do longjump if not */ X term_set (ERASE); X tty_set (RAWMODE); X user_str (buf, BRK_PR, 1); X if (buf[0] == 'y') X printex (BRK_MSG,sig); X signal (sig,sigcatch); X longjmp (Jumploc,1); X case BRK_READ: X /* if reading seek file to end to abort page printing */ X printf ("\n"); X if (*Fpseek == NULL || fseek(*Fpseek,0L,2) < 0) X putchar ('\07'); X break; X default: X printex (BRK_MSG,sig); X } X signal (sig,sigcatch); X} X X/* X sig_set controls what will be done with a signal when picked up by X sigcatch. grp_indic / fgprintf is included here to keep knowledge X of TSTP state localized. X*/ X/* VARARGS */ Xsig_set (flag,dat) Xint flag, *dat; X{ X int i, *xfer, pgrp; X if (Sigflag == BRK_INIT) X { X Cur_scn = NULL; X signal (SIGINT,sigcatch); X signal (SIGQUIT,sigcatch); X signal (SIGHUP,sigcatch); X signal (SIGTERM,sigcatch); X#ifdef JOBCONTROL X signal (SIGTSTP,sigcatch); X ioctl (1,TIOCGPGRP,&pgrp); X if (pgrp == getpgrp(0)) X { X Foreground = 1; X fgprintf ("Visual News, Release %s, reading:\n",Version); X } X else X Foreground = 0; X#else X Foreground = NOJOB_FG; X#endif X } X switch (flag) X { X case BRK_IN: X case BRK_OUT: X Sigflag = flag; X break; X case BRK_READ: X if (Sigflag != BRK_SESS) X printex ("unexpected read state, sig_set\n"); X Fpseek = (FILE **) dat; X Sigflag = BRK_READ; X break; X case BRK_SESS: X xfer = (int *) Jumploc; X for (i=0; i < sizeof(Jumploc) / sizeof(int); ++i) X xfer[i] = dat[i]; X Sigflag = BRK_SESS; X break; X case BRK_RFIN: X if (Sigflag != BRK_READ) X printex ("unexpected finish state, sig_set\n"); X Sigflag = BRK_SESS; X break; X default: X printex ("bad state %d, sig_set\n",flag); X } X} X Xgrp_indic (s,ok) Xchar *s; Xint ok; X{ X NODE *ptr,*hashfind(); X X /* we go to hash table because s might be a temporary buffer */ X if ((ptr = hashfind(s)) != NULL) X { X Cur_scn = ptr->nd_name; X if (Foreground) X { X if (ok) X fgprintf(" %s\n",Cur_scn); X else X fgprintf(" %s - Can't access spool directory\n",Cur_scn); X } X } X} X Xfgprintf (fs,a,b,c,d,e) Xchar *fs; Xint a,b,c,d,e; X{ X if (Foreground) X fprintf (stderr,fs,a,b,c,d,e); X fflush (stderr); X} @//E*O*F sig_set.c// if test 4501 -ne "`wc -c <'sig_set.c'`"; then echo shar: error transmitting "'sig_set.c'" '(should have been 4501 characters)' fi 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 sed 's/^X//' >storage.c <<'@//E*O*F storage.c//' X/* X** vn news reader. X** X** storage.c - storage allocation routines X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include "vn.h" X Xextern char *malloc(); X Xextern int L_allow; X Xextern PAGE Page; X/* X Storage allocaters. One more call to malloc in entry_order routine. X*/ X Xchar *str_store (s) Xchar *s; X{ X static unsigned av_len = 0; /* current storage available */ X static char *avail; X int len; X X if (s == NULL) X s = ""; X X if ((len = strlen(s)+1) > av_len) X { X if (len > STRBLKSIZE) X av_len = len; X else X av_len = STRBLKSIZE; X if ((avail = malloc(av_len)) == NULL) X printex ("can't allocate memory for string storage"); X } X strcpy (avail,s); X s = avail; X avail += len; X av_len -= len; X return (s); X} X X/* X** called after number of terminal lines (L_allow) is known, to set X** up storage for Page. X*/ Xpage_alloc () X{ X char *body; X X if ((body = malloc(L_allow*sizeof(BODY))) == NULL) X printex ("can't allocate memory for display storage"); X X Page.b = (BODY *) body; X} X XNODE X*node_store() X{ X static int nd_avail = 0; X static NODE *nd; X NODE *ret; X X if (nd_avail <= 0) X { X if ((nd = (NODE *) malloc(sizeof(NODE)*NDBLKSIZE)) == NULL) X printex ("can't allocate memory for newsgroup table"); X nd_avail = NDBLKSIZE; X } X --nd_avail; X ret = nd; X ++nd; X return(ret); X} @//E*O*F storage.c// if test 1309 -ne "`wc -c <'storage.c'`"; then echo shar: error transmitting "'storage.c'" '(should have been 1309 characters)' fi 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 sed 's/^X//' >strings.c <<'@//E*O*F strings.c//' X/* X** vn news reader. X** X** strings.c - character strings X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include "vn.h" X#include "head.h" X Xchar *Version = "12/86"; X Xchar *No_msg = "No articles"; Xchar *Hdon_msg = "Headers being printed"; Xchar *Hdoff_msg = "Headers being suppressed"; Xchar *Roton_msg = "ROT 13"; Xchar *Rotoff_msg = "NO ROT"; X Xchar *Aformat = AFORMAT; X Xchar *Contstr = " ******** any key to continue ********"; X Xchar *R_head = RHEAD; Xchar *M_head = MHEAD; Xchar *P_head = PHEAD; Xchar *D_head = DHEAD; Xchar *F_head = FHEAD; Xchar *FT_head = FTHEAD; Xchar *T_head = THEAD; Xchar *L_head = LHEAD; Xchar *N_head = NHEAD; Xchar *RT_head = RTHEAD; Xchar *TO_head = TOHEAD; Xchar *DIS_head = DISHEAD; @//E*O*F strings.c// if test 720 -ne "`wc -c <'strings.c'`"; then echo shar: error transmitting "'strings.c'" '(should have been 720 characters)' fi 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 sed 's/^X//' >tmpnam.c <<'@//E*O*F tmpnam.c//' X/* X** vn news reader. X** X** tmpnam.c - tmpnam() replacement for UCB, also uses non-generic name. X** X** see copyright disclaimer / history in vn.c source file X*/ X X#include <stdio.h> X#include "config.h" X Xchar *tmpnam (buf) Xchar *buf; X{ X static char *ptr = VNTEMPNAME; X X /* depends on string initialized above */ X sprintf (ptr+TMP_XOFFSET,"XXXXXX"); X X mktemp (ptr); X X if (buf != NULL) X strcpy (buf,ptr); X X return (ptr); X} @//E*O*F tmpnam.c// if test 420 -ne "`wc -c <'tmpnam.c'`"; then echo shar: error transmitting "'tmpnam.c'" '(should have been 420 characters)' fi fi # end of overwriting check echo shar: "End of archive 1 (of 3)." cp /dev/null ark1isdone DONE=true for I in 1 2 3; do if test -! f ark${I}isdone; then echo "You still need to run archive ${I}." DONE=false fi done case $DONE in true) echo "You have run all 3 archives." echo 'Now read the README' ;; esac ## End of shell archive. exit 0