nick@ultima.cs.uts.oz (Nick Andrew) (11/28/89)
Geez I'm stupid ... neatly segregate the files into appropriately
sized shars, then forget to create and post one! Here is the remaining
sharfile.
Sorry, Nick.
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: stat.c std.c std.h storage.c strings.c strtok.c svart.c
# term_set.c tmpnam.c tty.h tty_set.c tune.h
# Wrapped by nick@nswitgould on Tue Nov 28 07:59:57 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'stat.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'stat.c'\"
else
echo shar: Extracting \"'stat.c'\" \(4301 characters\)
sed "s/^X//" >'stat.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** stat.c - stat and log file collection
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X#include <stdio.h>
X#include <sys/types.h>
X#ifdef SYSV
X#include <fcntl.h>
X#endif
X
X#ifndef MINIX
X/* Minix doesn't have sys/file.h for some reason */
X#include <sys/file.h>
X#endif
X
X#include <sys/stat.h>
X#include <pwd.h>
X#include "config.h"
X#include "node.h"
X
Xextern NODE *hashfind();
Xextern char *strtok();
Xextern int Ncount;
Xextern NODE **Newsorder;
X
X#ifdef VNLOGFILE
Xstatic char Start[80];
X#endif
X
Xstat_start()
X{
X#ifdef VNLOGFILE
X char *ctime();
X long now;
X
X time(&now);
X strcpy(Start,ctime(&now));
X#endif
X}
X
X/*
X** flag = 0, "NO NEWS" type session.
X** = 1, regular session.
X** = -1, aborted session
X**
X** CAUTION: routine CALLED from within printex() - do NOT
X** call printex(). Simply do message to stderr on fail.
X*/
Xstat_end(flag)
Xint flag;
X{
X NODE *nd;
X char *nl,*index();
X char *how;
X struct passwd *ptr, *getpwuid();
X struct stat buf;
X long now;
X char bufr[80];
X char name[60];
X FILE *fp;
X int fd;
X long chk, rd, pg;
X int i;
X
X#ifdef VNLOGFILE
X if (stat(VNLOGFILE,&buf) == 0 && (fp = fopen(VNLOGFILE,"a")) != NULL)
X {
X time(&now);
X strcpy(bufr,ctime(&now));
X if ((nl = index(bufr,'\n')) != NULL)
X *nl = '\0';
X if ((nl = index(Start,'\n')) != NULL)
X *nl = '\0';
X if (flag == 0)
X how = "NO NEWS";
X else
X {
X if (flag > 0)
X how = "OK";
X else
X how = "ABORTED";
X }
X ptr = getpwuid (getuid());
X fprintf(fp, "%s\t%s - %s %s\n", ptr->pw_name, Start, bufr, how);
X fclose (fp);
X }
X#endif
X
X#ifdef VNSTATFILE
X /*
X ** Stat file is done with a fixed record size, and maintaining the
X ** existing record order exactly so that concurrent users will do
X ** the least damage. If two users actually read & update a single
X ** record simultaneously, we should just lose one user's counts.
X ** Short of implementing a locking scheme, we probably won't do
X ** much better. Disadvantages are that deleted newsgroups never
X ** get cleaned out, order is set by the first user whose
X ** statistics are collected, it will break if anyone modifies it,
X ** and the file is a bit larger than it needs to be.
X **
X ** record format:
X **
X ** CCCCCC PPPPPP RRRRRR newsgroup name .... \n
X ** ^ ^ ^ ^ ^
X ** 0 7 14 21 char 79
X **
X ** CCCCCC - count of sessions searching group
X ** PPPPPP - count of sessions actually finding pages for group
X ** RRRRRR - count of sessions actually accessing articles in group
X */
X if ((fd = open(VNSTATFILE,O_RDWR)) > 0)
X {
X bufr[80] = '\0';
X
X /*
X ** read a record, find the newsgroup, update counts.
X ** If changed, seek back & overwrite. By using fixed
X ** length records, we should only lose something on
X ** concurrent writes of the same record, and by writing
X ** the ENTIRE record, we keep it consistent
X */
X while ((i = read(fd,bufr,80)) == 80 && bufr[79] == '\n')
X {
X chk = atoi(bufr);
X pg = atoi(bufr+7);
X rd = atoi(bufr+14);
X strcpy(name,bufr+21);
X nl = strtok(name," \n");
X if (nl == NULL || (nd = hashfind(nl)) == NULL)
X continue;
X nd->flags |= FLG_STAT;
X if ((nd->flags & (FLG_SEARCH|FLG_ACC|FLG_PAGE)) == 0)
X continue;
X if ((nd->flags & FLG_SEARCH) != 0)
X ++chk;
X if ((nd->flags & FLG_PAGE) != 0)
X ++pg;
X if ((nd->flags & FLG_ACC) != 0)
X ++rd;
X if (chk > 999999L)
X chk = 999999L;
X if (pg > 999999L)
X pg = 999999L;
X if (rd > 999999L)
X rd = 999999L;
X sprintf(bufr,"%6ld",chk);
X bufr[6] = ' ';
X sprintf(bufr+7,"%6ld",pg);
X bufr[13] = ' ';
X sprintf(bufr+14,"%6ld",rd);
X bufr[20] = ' ';
X lseek(fd,-80L,1);
X write(fd,bufr,80);
X }
X
X /* format screwed up ? */
X if (i != 0)
X {
X lseek(fd,(long) -i,1);
X fprintf(stderr,"bad data in %s\n",VNSTATFILE);
X }
X
X /* may have aborted during vns_news() */
X if (Newsorder == NULL)
X Ncount = 0;
X
X /* now append any groups not in file yet */
X for (i = 0; i < Ncount; ++i)
X {
X nd = Newsorder[i];
X if ((nd->flags & FLG_STAT) != 0)
X continue;
X chk = rd = pg = 0;
X if ((nd->flags & FLG_SEARCH) != 0)
X chk = 1;
X if ((nd->flags & FLG_PAGE) != 0)
X pg = 1;
X if ((nd->flags & FLG_ACC) != 0)
X rd = 1;
X sprintf(bufr,"%6ld %6ld %6ld %-58s\n",
X chk, pg, rd, nd->nd_name);
X write(fd,bufr,80);
X }
X close(fd);
X }
X#endif
X}
END_OF_FILE
if test 4301 -ne `wc -c <'stat.c'`; then
echo shar: \"'stat.c'\" unpacked with wrong size!
fi
# end of 'stat.c'
fi
if test -f 'std.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'std.c'\"
else
echo shar: Extracting \"'std.c'\" \(24307 characters\)
sed "s/^X//" >'std.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <pwd.h>
X#include <ctype.h>
X
X#ifndef MINIX
X/* Minix is missing it */
X#include <sys/param.h>
X#endif
X
X#include "server.h"
X#include "config_std.h"
X#include "std.h"
X
X#ifdef MINIX
X#define clearerr(p) (((p)->_flags) &= ~_ERR)
X#endif
X
X#ifndef MAXPATHLEN
X#define MAXPATHLEN 240
X#endif
X
Xextern NODE *hashfind();
Xextern FILE *fopen();
Xextern char *index(), *rindex();
Xextern char *malloc();
Xextern char *str_tstore(), *str_tpool(), *str_store();
Xextern char *strtok(), *strpbrk();
Xextern char *regex(), *regcmp();
X
X#ifdef MAILCHOOSE
Xextern int (*Massage)();
X#endif
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
Xchar *Vns_version = "res1.1";
X
Xstatic char *Onews, *Newsrc;
Xstatic int Ntopt, Nntopt, Nwopt, Nnwopt;
X
Xstatic char *Wopt[NUMFILTER]; /* regular expressions for -w options */
Xstatic char *Topt[NUMFILTER]; /* for -t options */
Xstatic char *Negwopt[NUMFILTER]; /* for negated -w options */
Xstatic char *Negtopt[NUMFILTER]; /* for negated -t options */
X
Xstatic char *Options[OPTLINES];
Xstatic int Max_name, Optlines;
Xstatic unsigned Gflags = 0;
Xstatic char **Active;
Xstatic int Actnum;
Xstatic char *Mailer, *Poster;
X
Xstatic char *RT_head = RTHEAD;
Xstatic char *P_head = PHEAD;
Xstatic char *M_head = MHEAD;
Xstatic char *R_head = RHEAD;
Xstatic char *TO_head = TOHEAD;
Xstatic char *F_head = FHEAD;
Xstatic char *FT_head = FTHEAD;
Xstatic char *T_head = THEAD;
Xstatic char *DIS_head = DISHEAD;
Xstatic char *L_head = LHEAD;
Xstatic char *N_head = NHEAD;
X
Xstatic char *Fpfix = FPFIX;
X
X/*
X** environment setup.
X*/
Xvns_envir()
X{
X char dbuf[MAXPATHLEN], *rcname;
X char *vn_env();
X struct passwd *ptr, *getpwuid();
X#ifdef MAILCHOOSE
X int mail_prompt();
X
X Massage = mail_prompt;
X#endif
X
X ptr = getpwuid (getuid());
X
X rcname = vn_env("MAILER",DEF_MAIL);
X#ifdef INLETTER
X sprintf(dbuf,"cat %%s | %s",rcname);
X#else
X /* used as a format string TWICE (%%%% -> %% -> %) */
X sprintf(dbuf,"cat %%%%s | %s %%s",rcname);
X#endif
X Mailer = str_store(dbuf);
X rcname = vn_env("VNPOSTER",DEF_POST);
X sprintf(dbuf,"%s %%s",rcname);
X Poster = str_store(dbuf);
X rcname = vn_env("NEWSRC",DEF_NEWSRC);
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
X /* above logic guarantees that Newsrc contains a '/' */
X strcpy(dbuf,Newsrc);
X strcpy(rindex(dbuf,'/')+1,".vnXXXXXX");
X mktemp(dbuf);
X Onews = str_store (dbuf);
X
X if (access (Newsrc,0) != 0)
X creat (Newsrc,0666);
X}
X
X/*
X change directory to group
X*/
Xvns_gset(grp)
Xchar *grp;
X{
X char dbuf [RECLEN];
X g_dir (grp,dbuf);
X if (chdir(dbuf) < 0)
X printex("can't change to newsgroup directory");
X}
X
X/*
X g_dir converts newsgroup name to directory string
X*/
Xstatic
Xg_dir(s,t)
Xchar *s,*t;
X{
X char *ptr;
X sprintf (t,"%s/%s",SPOOLDIR,s);
X for (ptr=t+strlen(SPOOLDIR)+1; (ptr = index(ptr,'.')) != NULL; *ptr = '/')
X ;
X}
X
X/*
X** myfind is used for hashfind() calls which aren't supposed to fail.
X*/
Xstatic NODE *
Xmyfind(name)
Xchar *name;
X{
X NODE *n;
X
X n = hashfind(name);
X if (n == NULL)
X printex("Unexpected table lookup failure");
X return (n);
X}
X
Xvns_news(argc,argv,lfirst,nun)
Xint argc;
Xchar **argv;
Xint *lfirst, *nun;
X{
X FILE *fp;
X static char marks[] =
X {
X NEWS_ON, NEWS_OFF, '\0'
X };
X int line, len, num;
X char buf [RECLEN], trail, optpflag, submark, *fret, *ptr;
X
X ++argv;
X --argc;
X
X /* fill table with active newsgroups */
X fill_active ();
X
X if (argc > 0)
X {
X Gflags |= GF_OVER;
X arg_opt(argc,argv,lfirst,nun);
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 (lfirst,nun);
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,0);
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 /* free the active list */
X free ((char *) Active);
X}
X
Xstatic
Xemptyline(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 This is needed to be able to process options
X before scanning user order. Constructs an array
X of active newsgroup names for the rest of vns_nws().
X*/
Xstatic
Xfill_active ()
X{
X FILE *f;
X char *nread, act_rec[RECLEN];
X int num,lownum,rcount;
X
X Max_name = 0;
X if ((f = fopen (ACTFILE,"r")) == NULL)
X printex ("couldn't open %s\n",ACTFILE);
X
X /*
X ** we do things this way so that we only examine active records
X ** once, minimizing the window where changes could screw us up
X ** at the cost of possibly alloc'ing a few extra bytes. We start
X ** with a count of one to have a positive rcount for alloc.
X */
X for(rcount=1; fgets(act_rec, RECLEN-1, f) != NULL; ++rcount)
X ;
X if ((Active = (char **) malloc(rcount*sizeof(char *))) == NULL)
X printex("Memory allocation failure");
X
X rewind(f);
X
X Actnum = 0;
X while (Actnum < rcount && 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
X /* enter newsgroup, point to permanent copy of name */
X hashenter (act_rec, num, lownum);
X Active[Actnum] = (myfind(act_rec))->nd_name;
X ++Actnum;
X }
X
X fclose (f);
X}
X
X/*
X check active newsgroups not mentioned in NEWSRC file
X (SFLG_SCAN not set)
X*/
Xstatic
Xart_active ()
X{
X int i;
X NODE *ptr;
X
X for( i=0; i < Actnum; ++i)
X {
X ptr = myfind(Active[i]);
X if ((ptr->state & SFLG_SCAN) == 0)
X chkgroup (ptr->nd_name, NEWS_ON, 0, 1);
X }
X}
X
X/*
X check group for new articles:
X s - group
X c - subscription indicator from NEWSRC
X n - number read
X new - new newsgroup flag
X*/
Xstatic
Xchkgroup (s,c,n,new)
Xchar *s,c;
Xint n;
Xint new;
X{
X NODE *ptr;
X char sub;
X int nrd;
X int lowart;
X int st;
X
X if ((ptr = hashfind(s)) != NULL && (ptr->state & SFLG_SCAN) == 0)
X {
X ptr->state |= SFLG_SCAN;
X
X#ifdef SYN_CHECK
X /* if "read" more than exist, reset */
X if (n > ptr->highnum)
X {
X n = ptr->highnum - SYN_SETBACK;
X fgprintf("%s: .newsrc out of synch, resetting\n",s);
X }
X#endif
X lowart = ptr->lownum;
X if (n < ptr->lownum)
X n = ptr->lownum;
X
X nrd = n;
X sub = c;
X
X /*
X ** scan decision is rather complex, since GF_ALL setting
X ** overides "n" value, GF_SPEC indicates SFLG_SPEC flag used.
X ** if GF_OVER set, SFLG_SPEC overides subscription mark, else
X ** SFLG_SPEC AND subscribed is neccesary.
X */
X if ((Gflags & GF_SPEC) != 0)
X {
X if ((ptr->state & SFLG_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 fw_group(s, new, sub == NEWS_ON, nrd, c == NEWS_ON);
X if (c == NEWS_ON && ptr->highnum > n)
X {
X st = outgroup (s,n,ptr->highnum);
X if (st > nrd)
X fw_chg(new, sub == NEWS_ON, st, c == NEWS_ON);
X }
X }
X}
X
X/*
X vns_write writes the .newsrc file
X*/
Xvns_write(news,ncount)
XNODE **news;
Xint ncount;
X{
X FILE *fp;
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 = news[i];
X if ((p->flags & 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
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
Xarg_opt (argc,argv,lfirst,nun)
Xint argc;
Xchar **argv;
Xint *lfirst, *nun;
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(lfirst,nun);
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
Xnewsrc_opt(lfirst,nun)
Xint *lfirst, *nun;
X{
X int i;
X char curopt,tmp[RECLEN],*tok;
X
X *nun = *lfirst = 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 *lfirst = 1;
X break;
X case 'U':
X *nun = 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
Xdo_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
Xspecfilter (comp,str)
Xchar comp,*str;
X{
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
Xspecmark (s)
Xchar *s;
X{
X unsigned ormask,andmask;
X int i,len;
X char *ptr,*re,pattern[RECLEN];
X NODE *nptr;
X
X if (*s == '!')
X {
X ++s;
X ormask = 0;
X andmask = ~SFLG_SPEC;
X if (*s == '\0')
X return;
X }
X else
X {
X ormask = SFLG_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 < Actnum; ++i)
X {
X nptr = myfind(Active[i]);
X if (regex(re,nptr->nd_name) != NULL)
X {
X nptr->state |= ormask;
X nptr->state &= andmask;
X }
X }
X regfree (re);
X}
X
Xstatic
Xfindall (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}
X
Xstatic
Xgrp_indic (s,ok)
Xchar *s;
Xint ok;
X{
X if (ok)
X fgprintf(" %s\n",s);
X else
X fgprintf(" %s - Can't access spool directory\n",s);
X}
X
X/*
X enter newsgroup articles.
X all articles between low and hi are to be included.
X
X Returns the highest number less than an OPENED (not neccesarily
X accepted) article to allow caller to revise "articles read"
X number beyond non-existent articles.
X*/
Xoutgroup (s,low,hi)
Xchar *s;
Xint low,hi;
X{
X int i;
X char subj[RECLEN], lines[RECLEN], auth[RECLEN], gd[RECLEN];
X int ret,op;
X
X if ((hi-low) > MAXARTRANGE)
X low = hi - MAXARTRANGE;
X
X ret = low;
X op = 1;
X
X g_dir(s,gd);
X if (chdir(gd) < 0)
X {
X grp_indic(s,0);
X return (ret);
X }
X grp_indic(s,1);
X for (i=low+1; i <= hi; ++i)
X {
X if (digname(i,subj,lines,auth,&op) >= 0)
X {
X fw_art(i,subj,lines,auth);
X }
X else
X {
X if (op)
X ret = i;
X }
X }
X
X return(ret);
X}
X
X/*
X** open article and interpret options, if any. The op parameter is set
X** to ZERO if and only if an article is opened. Used above as a flag to
X** indicate no articles opened yet.
X*/
Xstatic digname (n, subj, lines, auth, op)
Xint n;
Xchar *subj, *lines, *auth;
Xint *op;
X{
X int i,j;
X FILE *fp;
X char t[RECLEN];
X char *nfgets();
X
X /* open article */
X sprintf (t,"%d", n);
X if ((fp = fopen(t,"r")) == NULL)
X return (-1);
X *op = 0;
X
X /* get subject, from and lines by reading article */
X subj[0] = lines[0] = auth[0] = '?';
X subj[1] = lines[1] = auth[1] = '\0';
X for (i = 0; i < HDR_LINES && nfgets(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 strcpy(subj,t+THDLEN);
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 strcpy(auth,t+FHDLEN);
X continue;
X }
X if (strncmp(L_head,t,LHDLEN) == 0)
X {
X strcpy(lines,t+LHDLEN);
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 return (0);
X}
X
X/*
X** special fgets for reading header lines, which unfolds continued lines
X** and throws away trailing stuff on buffer overflow.
X*/
Xstatic char *
Xnfgets(buf, size, fp)
Xchar *buf;
Xint size;
XFILE *fp;
X{
X register int c;
X
X while (!feof(fp))
X {
X if ((c = getc(fp)) == '\n')
X {
X if ((c = getc(fp)) == '\t' || c == ' ')
X continue;
X ungetc(c, fp);
X *buf = '\n';
X ++buf;
X *buf = '\0';
X ++buf;
X return (buf);
X }
X
X /* prevent "terminal bombs" */
X if (c < ' ' || c == '\177')
X {
X switch(c)
X {
X case '\r':
X case '\010':
X case '\07':
X break;
X case '\177':
X c = '~';
X break;
X case '\t':
X c = ' ';
X break;
X default:
X if (size > 1)
X {
X *buf = '^';
X ++buf;
X --size;
X }
X c += 'A' - 1;
X break;
X }
X }
X
X if (size > 0)
X {
X *buf = c;
X ++buf;
X --size;
X }
X if (c == '\r')
X {
X if ((c = getc(fp)) != '\n')
X {
X ungetc(c, fp);
X continue;
X }
X if ((c = getc(fp)) != ' ' && c != '\t')
X {
X *buf = '\0';
X ++buf;
X ungetc(c, fp);
X return (buf);
X }
X --buf;
X ++size;
X continue;
X }
X }
X
X *buf = '\0';
X ++buf;
X return (NULL);
X}
X
Xstatic char *Mail[2], *Show[6], *Post[4];
Xstatic char *Priv[8];
Xstatic char *Pool = NULL;
X
XFILE *
Xvns_aopen(art,hdr)
Xint art;
XARTHEADER *hdr;
X{
X char buf[RECLEN];
X char *dist, *reply, *from, *ngrp, *flto, *path, *resubj;
X FILE *fp;
X int n;
X char *mail_trim();
X
X dist = resubj = path = reply = from = ngrp = flto = NULL;
X
X sprintf(buf,"%d",art);
X if ((fp = fopen(buf,"r")) == NULL)
X return(NULL);
X
X /*
X ** we only really need a lot extra if MAILCHOOSE, but allocating
X ** a temporary array of pointers isn't that much. Similarly, a
X ** few assignments, and the "Priv" declaration are only needed
X ** with some define settings. Not worth ifdef'ing.
X */
X Pool = str_tpool(100);
X
X hdr->artid = "<some article>";
X hdr->from = "<somebody>";
X hdr->priv = Priv;
X hdr->postcmd = Poster;
X hdr->mail = Mail;
X hdr->show = Show;
X hdr->post = Post;
X hdr->priv_num = hdr->show_num = hdr->post_num = hdr->mail_num = 0;
X
X /* for conditional is abnormal - expected exit is break */
X for (n=0; n < HDR_LINES && fgets(buf,RECLEN-1,fp) != NULL; ++n)
X {
X /* bail out at first non-header line */
X if (buf[0] == '\n')
X break;
X if (strncmp(buf,RT_head,RTHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X reply = str_tstore(Pool,buf+RTHDLEN);
X continue;
X }
X if (strncmp(buf,P_head,PHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X path = str_tstore(Pool,buf+PHDLEN);
X continue;
X }
X if (strncmp(buf,DIS_head,DISHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X dist = str_tstore(Pool,buf);
X continue;
X }
X if (strncmp(buf,M_head,MHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X hdr->artid = str_tstore(Pool,buf+MHDLEN);
X continue;
X }
X if (strncmp(buf,F_head,FHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
X from = hdr->from = (hdr->show)[hdr->show_num]+FHDLEN;
X ++(hdr->show_num);
X continue;
X }
X if (strncmp(buf,T_head,THDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
X if (strncmp(buf+THDLEN,Fpfix,FPFLEN) != 0)
X {
X sprintf(buf,"%s%s%s",T_head,Fpfix,
X ((hdr->show)[hdr->show_num])+THDLEN);
X resubj = str_tstore(Pool,buf);
X }
X else
X resubj = (hdr->show)[hdr->show_num];
X ++(hdr->show_num);
X continue;
X }
X if (strncmp(buf,N_head,NHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X
X /* if multiple newsgroups, include in "show" */
X if (index(buf,',') != NULL)
X {
X (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
X ngrp = (hdr->show)[hdr->show_num] + NHDLEN;
X ++(hdr->show_num);
X }
X else
X ngrp = str_tstore(Pool,buf+NHDLEN);
X continue;
X }
X if (strncmp(buf,FT_head,FTHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
X flto = (hdr->show)[hdr->show_num] + FTHDLEN;
X ++(hdr->show_num);
X continue;
X }
X if (strncmp(buf,L_head,LHDLEN) == 0)
X {
X buf [strlen(buf)-1] = '\0';
X hdr->lines = atoi(buf+LHDLEN);
X (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
X ++(hdr->show_num);
X continue;
X }
X }
X
X hdr->hlines = n;
X
X#ifdef MAILCHOOSE
X (hdr->priv)[hdr->priv_num] = resubj;
X ++(hdr->priv_num);
X if (reply != NULL)
X {
X (hdr->priv)[hdr->priv_num] = mail_trim(reply);
X ++(hdr->priv_num);
X }
X if (from != NULL)
X {
X (hdr->priv)[hdr->priv_num] = mail_trim(from);
X ++(hdr->priv_num);
X }
X if (path != NULL)
X {
X (hdr->priv)[hdr->priv_num] = mail_trim(path);
X ++(hdr->priv_num);
X }
X#else
X#ifdef MAILSMART
X if (reply == NULL)
X if (from != NULL)
X reply = from;
X else
X {
X if (path != NULL)
X reply = path;
X }
X#else
X if (path != NULL)
X reply = path;
X#endif
X if (reply != NULL)
X reply = mail_trim(reply);
X mail_cmd(hdr,reply,resubj);
X#endif /* MAILCHOOSE */
X
X if (flto == NULL)
X {
X if ((flto = ngrp) == NULL)
X flto = "group.unknown";
X }
X ngrp = rindex(flto,'.');
X
X if (strncmp("mod.",flto,4) == 0 ||
X (ngrp != NULL && strcmp(".announce",ngrp) == 0))
X {
X sprintf(buf,"Cannot post a follow-up to \"%s\", reply with mail to moderator",flto);
X hdr->post_err = str_tstore(Pool,buf);
X return (fp);
X }
X
X if (ngrp != NULL && strcmp(ngrp,".general") == 0)
X {
X *ngrp = '\0';
X sprintf(buf,"%s%s.followup",N_head,flto);
X }
X else
X sprintf(buf,"%s%s",N_head,flto);
X flto = str_tstore(Pool,buf);
X
X hdr->post_err = NULL;
X
X if (resubj != NULL)
X {
X (hdr->post)[hdr->post_num] = resubj;
X ++(hdr->post_num);
X }
X
X (hdr->post)[hdr->post_num] = flto;
X ++(hdr->post_num);
X
X sprintf(buf,"%s%s",R_head,hdr->artid);
X (hdr->post)[hdr->post_num] = str_tstore(Pool,buf);
X ++(hdr->post_num);
X
X if (dist != NULL)
X {
X (hdr->post)[hdr->post_num] = dist;
X ++(hdr->post_num);
X }
X
X return (fp);
X}
X
X#ifdef MAILCHOOSE
X/*
X** routine to prompt user for mail path approval
X*/
Xstatic
Xmail_prompt(hdr)
XARTHEADER *hdr;
X{
X int i;
X char buf[RECLEN],*ptr;
X
X tty_set(SAVEMODE);
X for (i=1; i < hdr->priv_num; ++i)
X {
X printf("%d - %s\n",i,(hdr->priv)[i]);
X }
X printf("\nType number to choose one of the above, or input address: ");
X fgets(buf,RECLEN-1,stdin);
X tty_set(RESTORE);
X
X ptr = strtok(buf," \t\n");
X if (ptr == NULL)
X ptr = "";
X
X i = strlen(ptr);
X if (i == 1)
X {
X i = atoi(ptr);
X if (i > 0 && i <= hdr->priv_num)
X ptr = (hdr->priv)[i];
X i = 1;
X }
X
X /*
X ** If the user keeps cycling through here on the same article,
X ** we will eventually run out of strings. We made Pool large
X ** enough to make it unlikely (user will have to retry about 80
X ** times without switching articles). Hardly elegant, but should
X ** be sufficient.
X */
X if (i > 1 && hdr->priv_num < 8)
X {
X (hdr->priv)[hdr->priv_num] = str_tstore(Pool,ptr);
X ++(hdr->priv_num);
X }
X mail_cmd(hdr,ptr,(hdr->priv)[0]);
X}
X#endif
X
X/*
X** trim () off potential mail address, and make copy if needed.
X** addr must be allocated string.
X*/
Xstatic char *
Xmail_trim(addr)
Xchar *addr;
X{
X char buf[RECLEN];
X char *ptr;
X
X if (index(addr,'(') == NULL)
X return(addr);
X
X strcpy(buf,addr);
X ptr = index(buf,'(');
X for (--ptr; *ptr == ' ' || *ptr == '\t'; --ptr)
X ;
X ++ptr;
X *ptr = '\0';
X return (str_tstore(Pool,buf));
X}
X
X/*
X** format mail command. Subj must point to allocated string.
X*/
Xstatic
Xmail_cmd(hdr,addr,subj)
XARTHEADER *hdr;
Xchar *addr, *subj;
X{
X char buf[RECLEN];
X
X if (addr == NULL || *addr == '\0')
X {
X hdr->mail_err = "No address";
X return;
X }
X
X hdr->mail_err = NULL;
X ;
X
X#ifdef INLETTER
X hdr->mailcmd = Mailer;
X sprintf(buf,"%s%s",TO_head,addr);
X (hdr->mail)[0] = str_tstore(Pool,buf);
X hdr->mail_num = 1;
X#else
X sprintf(buf,Mailer,addr);
X hdr->mailcmd = str_tstore(Pool,buf);
X hdr->mail_num = 0;
X#endif
X if (subj != NULL)
X {
X (hdr->mail)[hdr->mail_num] = subj;
X ++(hdr->mail_num);
X }
X}
X
Xvns_aclose(fp)
XFILE *fp;
X{
X if (Pool != NULL)
X str_tfree(Pool);
X Pool = NULL;
X fclose(fp);
X}
X
X/*
X** we don't use the count / name / mode arguments because this doesn't
X** implement any fancy article massaging
X*/
Xvns_asave(art,fp)
Xint art;
XFILE *fp;
X{
X char buf[RECLEN];
X FILE *fin;
X
X sprintf(buf,"%d",art);
X if ((fin = fopen(buf,"r")) == NULL)
X return;
X
X while (fgets(buf,RECLEN-1,fin) != NULL)
X fputs(buf,fp);
X fclose(fin);
X}
X
Xvns_exit()
X{
X}
END_OF_FILE
if test 24307 -ne `wc -c <'std.c'`; then
echo shar: \"'std.c'\" unpacked with wrong size!
fi
# end of 'std.c'
fi
if test -f 'std.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'std.h'\"
else
echo shar: Extracting \"'std.h'\" \(886 characters\)
sed "s/^X//" >'std.h' <<'END_OF_FILE'
X/*
X newsrc states
X*/
X#define NEWS_ON ':'
X#define NEWS_OFF '!'
X
X#define SFLG_SCAN 1
X#define SFLG_SPEC 2
X
X#define FPFIX "Re: "
X#define FPFLEN 4
X
X#define FIL_AUTHOR 'w'
X#define FIL_TITLE 't'
X
X/*
X header lines and associated lengths. Strings should
X actually be used only once.
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 */
END_OF_FILE
if test 886 -ne `wc -c <'std.h'`; then
echo shar: \"'std.h'\" unpacked with wrong size!
fi
# end of 'std.h'
fi
if test -f 'storage.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'storage.c'\"
else
echo shar: Extracting \"'storage.c'\" \(2261 characters\)
sed "s/^X//" >'storage.c' <<'END_OF_FILE'
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 "tune.h"
X#include "node.h"
X#include "page.h"
X
Xextern char *malloc();
X
Xextern int L_allow;
X
Xextern PAGE Page;
X/*
X Storage allocaters.
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}
X
X/*
X** temp string storage
X*/
X
Xtypedef struct
X{
X int len;
X int idx;
X char **ptr;
X} STRINGPOOL;
X
Xchar *
Xstr_tpool(n)
Xint n;
X{
X int size;
X STRINGPOOL *p;
X
X size = sizeof(STRINGPOOL) + n * sizeof(char **);
X
X if ((p = (STRINGPOOL *) malloc(size)) == NULL)
X printex("Cannot allocate temporary string storage");
X
X p->ptr = (char **)(p+1);
X p->len = n;
X p->idx = 0;
X
X return((char *) p);
X}
X
Xchar *
Xstr_tstore(cp,s)
Xchar *cp;
Xchar *s;
X{
X STRINGPOOL *p;
X int len;
X
X p = (STRINGPOOL *) cp;
X if (p->idx >= p->len)
X printex("Temporary string storage overflow");
X len = strlen(s)+1;
X if ((cp = malloc(len)) == NULL)
X printex("Cannot allocate copy of string");
X strcpy(cp,s);
X (p->ptr)[p->idx] = cp;
X ++(p->idx);
X
X return(cp);
X}
X
Xchar **
Xstr_taptr(cp)
Xchar *cp;
X{
X STRINGPOOL *p;
X
X p = (STRINGPOOL *) cp;
X
X return (p->ptr + p->idx);
X}
X
Xstr_tfree(cp)
Xchar *cp;
X{
X STRINGPOOL *p;
X int i;
X
X p = (STRINGPOOL *) cp;
X for (i=0; i < p->idx; ++i)
X free((p->ptr)[i]);
X free (cp);
X}
END_OF_FILE
if test 2261 -ne `wc -c <'storage.c'`; then
echo shar: \"'storage.c'\" unpacked with wrong size!
fi
# end of 'storage.c'
fi
if test -f 'strings.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'strings.c'\"
else
echo shar: Extracting \"'strings.c'\" \(537 characters\)
sed "s/^X//" >'strings.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** strings.c - read only character strings
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X
X#include "tune.h"
X#include "node.h"
X#include "page.h"
X
Xchar *Version = "4/88";
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 *Brk_fmt = "QUIT (signal %d)";
X
Xchar *List_sep = " \t,";
END_OF_FILE
if test 537 -ne `wc -c <'strings.c'`; then
echo shar: \"'strings.c'\" unpacked with wrong size!
fi
# end of 'strings.c'
fi
if test -f 'strtok.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'strtok.c'\"
else
echo shar: Extracting \"'strtok.c'\" \(1082 characters\)
sed "s/^X//" >'strtok.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** strtok.c - strtok() and strpbrk() string routines using UCB index().
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X
X#include <stdio.h>
X
Xchar *strpbrk (s,del)
Xchar *s, *del;
X{
X char *ptr,*index();
X if (s == NULL)
X return (NULL);
X for (; *del != '\0'; ++del)
X if ((ptr = index(s,*del)) != NULL)
X return (ptr);
X return (NULL);
X}
X
Xchar *strtok(str,delim)
Xchar *str, *delim;
X{
X char *tokstart, *tokend, *first_ch (), *last_ch();
X static char *save=NULL;
X
X if (str != NULL)
X save = str;
X
X if (save == NULL)
X return (NULL);
X
X tokstart = first_ch (save, delim);
X tokend = last_ch (tokstart, delim);
X save = first_ch (tokend, delim);
X *tokend = '\0';
X
X if (*tokstart == '\0')
X return (NULL);
X
X return (tokstart);
X}
X
Xstatic char *first_ch (str,delim)
Xchar *str,*delim;
X{
X char *index ();
X char *f;
X
X for (f = str; *f != '\0' && index(delim,*f) != NULL; ++f)
X ;
X
X return (f);
X}
X
Xstatic char *last_ch (str,delim)
Xchar *str,*delim;
X{
X char *index ();
X char *f;
X
X for (f = str; *f != '\0' && index(delim,*f) == NULL; ++f)
X ;
X
X return (f);
X}
END_OF_FILE
if test 1082 -ne `wc -c <'strtok.c'`; then
echo shar: \"'strtok.c'\" unpacked with wrong size!
fi
# end of 'strtok.c'
fi
if test -f 'svart.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'svart.c'\"
else
echo shar: Extracting \"'svart.c'\" \(4834 characters\)
sed "s/^X//" >'svart.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** svart.c - article save routine
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X#include <stdio.h>
X#include <pwd.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "config.h"
X#include "tty.h"
X#include "tune.h"
X#include "node.h"
X#include "page.h"
X
Xextern PAGE Page;
Xextern int Digest;
Xextern char *List_sep;
Xextern char *Home;
Xextern char *Savedir;
Xextern FILE *(*Saveopen)();
Xextern int (*Digsaver)();
X
X/*
X** save article in file. Called from reader and session both.
X** handles "|" pipe convention. Caller passes in return message buffer.
X*/
Xsave_art(art,idest,msg)
Xchar *art;
Xchar *idest;
Xchar *msg;
X{
X char fn[L_tmpnam+1];
X char cmd[RECLEN];
X char *mode;
X char *dest, dstore[RECLEN];
X struct stat sbuf;
X int rstat;
X char *colon;
X char *pcnt;
X char *index();
X
X /* temporary copy so we don't overwrite saved string */
X strcpy((dest = dstore),idest);
X
X if (*dest == '|')
X {
X tmpnam(fn);
X if (art_xfer(fn,art,"w",NULL) != 0)
X {
X strcpy(msg,"Can't open temporary file");
X return (-1);
X }
X sprintf(cmd,"cat %s %s",fn,dest);
X tty_set (SAVEMODE);
X rstat = system (cmd);
X tty_set (RESTORE);
X sprintf(msg,"Command returns %d",rstat);
X return (rstat);
X }
X
X if (Saveopen != NULL)
X return (art_xfer(dest,art,"a",msg));
X
X if ((colon = index(dest,':')) != NULL)
X {
X mode = dest;
X *colon = '\0';
X dest = colon+1;
X }
X else
X mode = "a";
X
X if (*dest == '~')
X {
X if (twiddle(dest,msg) < 0)
X return (-1);
X }
X
X if (*dest == '\0')
X strcpy(dest,"%d");
X
X if (*dest != '/')
X {
X if (noslash(dest,msg) < 0)
X return (-1);
X }
X
X if ((pcnt = index(dest,'%')) != NULL && pcnt[1] == 'd')
X {
X if (Digest)
X sprintf(cmd,dest,Digest);
X else
X sprintf(cmd,dest,atoi(art));
X dest = cmd;
X }
X
X rstat = stat(dest,&sbuf);
X
X if (art_xfer(dest,art,mode,msg) != 0)
X {
X sprintf(msg,"Can't open %s with mode %s",dest,mode);
X return(-1);
X }
X
X if (rstat != 0)
X {
X sprintf(msg,"Created %s",dest);
X return(0);
X }
X
X if (strcmp(mode,"a") == 0)
X {
X sprintf(msg,"Appended %s",dest);
X return(0);
X }
X
X sprintf(msg,"Wrote (mode %s) %s",mode,dest);
X return(0);
X}
X
Xstatic
Xnoslash(dest,msg)
Xchar *dest;
Xchar *msg;
X{
X char *pcnt;
X char buf[RECLEN];
X char dir[RECLEN];
X struct stat sbuf;
X
X strcpy(buf,Page.h.name);
X#ifdef SYSV
X buf[14] = '\0';
X#endif
X if ((pcnt = index(Savedir,'%')) != NULL && pcnt[1] == 's')
X sprintf(dir,Savedir,buf);
X else
X strcpy(dir,Savedir);
X if (dir[0] == '~')
X {
X if (twiddle(dir,msg) < 0)
X return (-1);
X }
X if (stat(dir,&sbuf) != 0)
X {
X#ifdef SYSV
X /*
X ** late enough releases of SYSV may have a mkdir() call, but
X ** this is an obscure feature anyway. We'll accept the fork.
X */
X sprintf(buf,"mkdir %s",dir);
X if (system(buf) != 0)
X#else
X if (mkdir(dir,0755) != 0)
X#endif
X {
X sprintf(msg,"Cannot make directory %s",dir);
X return (-1);
X }
X }
X sprintf(buf,"%s/%s",dir,dest);
X strcpy(dest,buf);
X return (0);
X}
X
Xstatic
Xtwiddle(dest,msg)
Xchar *dest, *msg;
X{
X char *tail;
X char *name;
X char tmp;
X char buf[RECLEN];
X struct passwd *ptr, *getpwnam();
X
X for (tail=name=dest+1; *tail != '/' && *tail != '\0'; ++tail)
X ;
X
X if (*name == '\0' || *name == '/')
X sprintf(buf,"%s%s",Home,tail);
X else
X {
X tmp = *tail;
X *tail = '\0';
X ptr = getpwnam(name);
X *tail = tmp;
X if (ptr == NULL)
X {
X sprintf(msg,"Can't interpret ~%s",name);
X return(-1);
X }
X sprintf(buf,"%s%s",ptr->pw_dir,tail);
X }
X
X strcpy(dest,buf);
X return (0);
X}
X
X/*
X** transfer contents of a list of articles to a file. If Digest, this
X** is simply a list of files. If not, it is a list of articles to be
X** saved with vns_asave. Parses list destructively with
X** strtok(). Return 0 for success, -1 for failure to open file.
X**
X** Called directly to copy a list of articles to a temp. file to
X** direct to printer.
X**
X** NOTE:
X** The msg argument only matters if Saveopen is not NULL. If so, it
X** is non-NULL if *Saveopen is to be called, and points to the message
X** buffer. Will be called with msg = NULL to fill temp. files rather
X** than user named files. If *Saveopen is called, mode argument is
X** actually returned by it, and only matters for vns_asave call.
X*/
Xart_xfer(fn,list,mode,msg)
Xchar *fn, *list, *mode, *msg;
X{
X char *p;
X FILE *fout, *fin;
X int count;
X char buf[RECLEN];
X char *strtok();
X
X if (Saveopen != NULL && msg != NULL)
X fout = (*Saveopen)(fn,msg,&mode);
X else
X fout = fopen(fn,mode);
X if (fout == NULL)
X return (-1);
X
X count = 0;
X for (p = strtok(list,List_sep); p != NULL; p = strtok(NULL,List_sep))
X {
X if (Digest)
X {
X if (Digsaver != NULL)
X {
X (*Digsaver)(p,fout,count,fn,mode);
X ++count;
X continue;
X }
X fin = fopen(p,"r");
X if (fin == NULL)
X continue;
X while (fgets(buf,RECLEN-1,fin) != NULL)
X fputs(buf,fout);
X fclose(fin);
X continue;
X }
X vns_asave(atoi(p),fout,count,fn,mode);
X ++count;
X }
X fclose(fout);
X return(0);
X}
END_OF_FILE
if test 4834 -ne `wc -c <'svart.c'`; then
echo shar: \"'svart.c'\" unpacked with wrong size!
fi
# end of 'svart.c'
fi
if test -f 'term_set.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'term_set.c'\"
else
echo shar: Extracting \"'term_set.c'\" \(4953 characters\)
sed "s/^X//" >'term_set.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** term_set.c - terminal control, hides termcap interface
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X
X#include <stdio.h>
X#include "tty.h"
X#include "config.h"
X#include "tune.h"
X#include "node.h"
X#include "page.h"
X
Xextern int L_allow, C_allow;
Xextern char *Ku, *Kd, *Kl, *Kr;
X
Xstatic outc (c)
Xchar c;
X{
X putchar (c);
X}
X
X/*
X term_set controls terminal through termcap
X START sets global parameters related to terminal also,
X as well as allocating display buffer which depends on
X terminal lines, and allocating escape strings. RESTART
X simply re-issues the initialization - used following system
X calls that could have goofed up the terminal state.
X*/
X
X/*
X** Escape strings.
X*/
X
Xstatic char *Cm,*Cl,*So,*Se,*Te,*Bc,*Ce,*Ti,*Ks,*Ke;
X#ifdef USEVS
Xstatic char *Vs,*Ve;
X#endif
X
Xstatic int Backspace; /* backspace works */
Xstatic int Overstrike; /* terminal overstrikes */
X
Xstatic t_setup()
X{
X char *tgetstr(), *vn_env(), *str_store();
X char *c, tc_buf[2048],optstr[2048];
X char *tvar;
X
X tvar = vn_env("TERM",DEF_TERM);
X
X c = optstr;
X if (tgetent(tc_buf,tvar) != 1)
X printex ("%s - unknown terminal",tvar);
X
X /* get needed capabilities */
X Cm = str_store(tgetstr("cm",&c));
X Cl = str_store(tgetstr("cl",&c));
X So = str_store(tgetstr("so",&c));
X Se = str_store(tgetstr("se",&c));
X Te = str_store(tgetstr("te",&c));
X Ti = str_store(tgetstr("ti",&c));
X Bc = str_store(tgetstr("bc",&c));
X Ce = str_store(tgetstr("ce",&c));
X Kd = str_store(tgetstr("kd",&c));
X Ke = str_store(tgetstr("ke",&c));
X Kl = str_store(tgetstr("kl",&c));
X Kr = str_store(tgetstr("kr",&c));
X Ks = str_store(tgetstr("ks",&c));
X Ku = str_store(tgetstr("ku",&c));
X#ifdef USEVS
X Vs = str_store(tgetstr("vs",&c));
X Ve = str_store(tgetstr("ve",&c));
X#endif
X Backspace = tgetflag("bs");
X Overstrike = tgetflag("os");
X
X if ( *Cm == '\0' || *Cl == '\0')
X {
X printex ("cursor control and erase capability needed");
X }
X
X /*
X ** Checks for arrow keys which don't issue something beginning
X ** with <ESC>. This is more paranoid than we need to be, strictly
X ** speaking - we could get away with any string which didn't
X ** conflict with controls used for commands. However, that would
X ** be a maintenance headache - we will simply reserve <ESC> as the
X ** only char not to be used for commands, and punt on terminals
X ** which don't send reasonable arrow keys. It would be confusing
X ** to have keys work partially, also. I know of no terminal with
X ** one arrow key beginning with an escape, and another beginning
X ** with something else, but let's be safe. This also insists on
X ** definitions for all 4 arrows, which seems reasonable.
X */
X
X if ((*Ku != '\0' && *Ku != '\033') || *Kl != *Ku || *Kr != *Ku || *Kd != *Ku)
X {
X fgprintf("WARNING: arrow keys will not work for this terminal");
X Ku = Kd = Kl = Kr = Kd = Ke = "";
X }
X
X if (Overstrike)
X fgprintf ("WARNING: terminal overstrikes - can't update display without erase\n");
X
X if ((L_allow = tgetnum("li")) < REQLINES)
X {
X if (L_allow < 0)
X printex ("can't determine number of lines on terminal");
X printex ("too few lines for display - %d needed", REQLINES);
X }
X
X /*
X ** C_allow set so as to not use extreme right column.
X ** Avoids "bad wraparound" problems - we're deciding it's best
X ** to ALWAYS assume no automargin, and take care of it ourselves
X */
X if((C_allow = tgetnum("co")) > MAX_C)
X C_allow = MAX_C;
X else
X --C_allow;
X if (C_allow < MIN_C)
X {
X if (C_allow < 0)
X printex("can't determine number of columns on terminal.");
X printex ("too few columns for display - %d needed",MIN_C);
X }
X
X L_allow -= RECBIAS;
X page_alloc();
X tputs(Ti,1,outc);
X tputs(Ks,1,outc);
X#ifdef USEVS
X tputs(Vs,1,outc);
X#endif
X}
X
X/* VARARGS */
Xterm_set(cmd,x,y)
Xint cmd,x,y;
X{
X char *tgoto();
X int i;
X switch (cmd)
X {
X case MOVE:
X tputs (tgoto(Cm,x,y),1,outc);
X break;
X case ERASE:
X tputs(Cl,1,outc);
X break;
X case ONREVERSE:
X tputs(So,1,outc);
X break;
X case OFFREVERSE:
X tputs(Se,1,outc);
X break;
X case START:
X t_setup();
X break;
X case RESTART:
X tputs(Ti,1,outc);
X tputs(Ks,1,outc);
X#ifdef USEVS
X tputs(Vs,1,outc);
X#endif
X break;
X case STOP:
X term_set (MOVE,0,L_allow+RECBIAS-1);
X printf ("\n");
X tputs(Ke,1,outc);
X tputs(Te,1,outc);
X#ifdef USEVS
X tputs(Ve,1,outc);
X#endif
X break;
X case RUBSEQ:
X if (Overstrike)
X {
X /* space overprint is futile */
X if (Backspace)
X putchar('\010');
X else
X tputs(Bc,1,outc);
X break;
X }
X if (Backspace)
X printf("%c %c",'\010','\010');
X else
X {
X tputs(Bc,1,outc);
X putchar(' ');
X tputs(Bc,1,outc);
X }
X break;
X case ZAP:
X if (Ce != NULL && *Ce != '\0')
X tputs(Ce,1,outc);
X else
X {
X if (Overstrike)
X break; /* punt */
X for (i=x; i < y; ++i)
X putchar(' ');
X if (Backspace)
X {
X for (i=x; i < y; ++i)
X putchar('\010');
X }
X else
X {
X for (i=x; i < y; ++i)
X tputs(Bc,1,outc);
X }
X }
X break;
X default:
X printex ("term_set unknown code (%d)",cmd);
X break;
X }
X return (0);
X}
END_OF_FILE
if test 4953 -ne `wc -c <'term_set.c'`; then
echo shar: \"'term_set.c'\" unpacked with wrong size!
fi
# end of 'term_set.c'
fi
if test -f 'tmpnam.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'tmpnam.c'\"
else
echo shar: Extracting \"'tmpnam.c'\" \(420 characters\)
sed "s/^X//" >'tmpnam.c' <<'END_OF_FILE'
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}
END_OF_FILE
if test 420 -ne `wc -c <'tmpnam.c'`; then
echo shar: \"'tmpnam.c'\" unpacked with wrong size!
fi
# end of 'tmpnam.c'
fi
if test -f 'tty.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'tty.h'\"
else
echo shar: Extracting \"'tty.h'\" \(451 characters\)
sed "s/^X//" >'tty.h' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** tty.h - codes for tty_set and term_set
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X
X#define MOVE 100
X#define ERASE 101
X#define START 102
X#define STOP 103
X#define RUBSEQ 104
X#define ZAP 105
X#define ONREVERSE 106
X#define OFFREVERSE 107
X#define RESTART 108
X
X#define RAWMODE 200
X#ifndef MINIX
X#define COOKED 201
X#else
X#define XCOOKED 201
X#endif
X#define SAVEMODE 202
X#define RESTORE 203
X#define BACKSTOP 204
END_OF_FILE
if test 451 -ne `wc -c <'tty.h'`; then
echo shar: \"'tty.h'\" unpacked with wrong size!
fi
# end of 'tty.h'
fi
if test -f 'tty_set.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'tty_set.c'\"
else
echo shar: Extracting \"'tty_set.c'\" \(2713 characters\)
sed "s/^X//" >'tty_set.c' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** tty_set.c - interface to ioctl (system tty interface)
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X
X#ifdef MINIX
X/* Minix uses non-SysV type ioctls */
X#undef SYSV
X#endif
X
X#ifdef SYSV
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X
X#include "tty.h"
X
Xextern char Erasekey,Killkey;
X
X#ifdef SYSV
Xstatic struct termio C_tp, O_tp;
X#else
Xstatic struct sgttyb C_tp;
Xstatic unsigned short O_lflag;
X#endif
X
Xstatic unsigned S_flag=0;
Xstatic int R_ignore=0; /* up/down counter of reset calls to ignore */
X
X#define IO_GOT 1 /* have polled for original terminal mode */
X#define IO_RAW 2 /* in RAW (CBREAK actually) mode */
X
X/*
X tty_set handles ioctl calls. SAVEMODE, RESTORE are used around
X system calls and interrupts to assure cooked mode, and restore
X raw if raw on SAVEMODE. The pair results in no calls to ioctl
X if we are cooked already when SAVEMODE is called, and may be nested,
X provided we desire no "restore" of cooked mode after restoring raw.
X
X When we get the original terminal mode, we also save erase and kill.
X
X sig_set makes an ioctl call to get process group leader. Otherwise
X ioctl calls should come through here.
X*/
Xtty_set(cmd)
Xint cmd;
X{
X int rc;
X unsigned mask;
X
X switch (cmd)
X {
X case BACKSTOP:
X#ifdef JOBCONTROL
X if ((rc = ioctl(1,TIOCLGET,&mask)) != 0)
X break;
X mask |= LTOSTOP;
X rc = ioctl(1,TIOCLSET,&mask);
X#else
X rc = 0;
X#endif
X break;
X case RAWMODE:
X if ((S_flag & IO_RAW) != 0)
X {
X rc = 0;
X break;
X }
X if ((S_flag & IO_GOT) == 0)
X {
X /* Save original modes, get erase / kill */
X#ifdef SYSV
X rc = ioctl(0,TCGETA,&C_tp);
X O_tp = C_tp;
X Erasekey = C_tp.c_cc[VERASE];
X Killkey = C_tp.c_cc[VKILL];
X#else
X rc = ioctl(0,TIOCGETP,&C_tp);
X O_lflag = C_tp.sg_flags;
X Erasekey = C_tp.sg_erase;
X Killkey = C_tp.sg_kill;
X#endif
X }
X#ifdef SYSV
X C_tp.c_lflag &= ~(ECHO | ICANON);
X C_tp.c_cc[VMIN] = 1;
X rc = ioctl(0,TCSETAW,&C_tp);
X#else
X C_tp.sg_flags |= CBREAK;
X C_tp.sg_flags &= ~ECHO;
X rc = ioctl(0,TIOCSETP,&C_tp);
X#endif
X S_flag = IO_GOT|IO_RAW;
X break;
X#ifndef MINIX
X case COOKED:
X#else
X case XCOOKED:
X#endif
X if ((S_flag & IO_RAW) != 0)
X {
X#ifdef SYSV
X C_tp = O_tp;
X rc = ioctl(0,TCSETAW,&C_tp);
X#else
X C_tp.sg_flags = O_lflag;
X rc = ioctl(0,TIOCSETP,&C_tp);
X#endif
X S_flag &= ~IO_RAW;
X }
X else
X rc = 0;
X break;
X case SAVEMODE:
X if ((S_flag & IO_RAW) != 0)
X {
X#ifndef MINIX
X tty_set(COOKED);
X#else
X tty_set(XCOOKED);
X#endif
X R_ignore = 0;
X }
X else
X ++R_ignore;
X rc = 0;
X break;
X case RESTORE:
X if (R_ignore <= 0)
X {
X tty_set(RAWMODE);
X }
X else
X --R_ignore;
X rc = 0;
X break;
X default:
X rc = -1;
X }
X if (rc < 0)
X printex ("ioctl failure, tty_set: %d",cmd);
X}
END_OF_FILE
if test 2713 -ne `wc -c <'tty_set.c'`; then
echo shar: \"'tty_set.c'\" unpacked with wrong size!
fi
# end of 'tty_set.c'
fi
if test -f 'tune.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'tune.h'\"
else
echo shar: Extracting \"'tune.h'\" \(2198 characters\)
sed "s/^X//" >'tune.h' <<'END_OF_FILE'
X/*
X** vn news reader.
X**
X** tune.h - system tuning parameters
X**
X** see copyright disclaimer / history in vn.c source file
X*/
X
X/*
X** buffer size needed for tmpnam()
X*/
X#ifndef L_tmpnam
X#define L_tmpnam 48
X#endif
X
X/*
X** hash table size. linked list type of table which can expand to
X** arbitrary density, including densities > 100%. Number of entries
X** will be number of newsgroups in active list. This should be a prime
X** number ("long division" of string modulo table size hash function).
X*/
X#define HASHSIZE 809
X
X/*
X** maximum number of columns on terminal. If made smaller, there
X** will be a savings in the size of the temporary file used
X** for holding displays, at the penalty of not being able to use
X** the entire screen width on terminals actually possessing more
X** columns than this. A block roughly on the order of this value
X** times the number of lines the terminal has is maintained per page in
X** the temp file, and read / written as displays are interacted
X** with. MIN_C put here because MAX_C > MIN_C. MIN_C is the minumum
X** number of columns for which a "reasonable" display can be produced.
X** before making it smaller, look at all uses of C_allow and variable
X** to see that a setting that small won't screw up array bounds.
X*/
X#define MAX_C 132
X#define MIN_C 36
X
X/*
X** large size for general purpose local buffers. only used in automatic
X** variable declarations. Used with fgets for buffer size when reading
X** file records, to hold pathnames, commands, etc. Reduce if you blow
X** out stack storage. If reduced too far, may eventually show up
X** as syntax errors on interacting with vns_ routines, or command line
X** botches.
X*/
X#define RECLEN 1200
X
X/* block sizes for allocation routines */
X#define STRBLKSIZE 1800 /* string storage allocation block */
X#define NDBLKSIZE 50 /* NODE structures to allocate at a time */
X
X/*
X** maximum number of articles to allow for processing in a single user
X** list. Used only to declare an array of pointers on the stack, so it
X** can be fair sized without much problem. In practicality, there is
X** no use for it being larger than the greatest line length available
X** on the CRT's being used.
X*/
X#define MAXARTLIST 200
END_OF_FILE
if test 2198 -ne `wc -c <'tune.h'`; then
echo shar: \"'tune.h'\" unpacked with wrong size!
fi
# end of 'tune.h'
fi
echo shar: End of shell archive.
exit 0
--
"Zeta Microcomputer Software"
ACSnet: nick@ultima.cs.uts.oz
UUCP: ...!uunet!munnari!ultima.cs.uts.oz!nick
Fidonet: Nick Andrew on 3:713/602 (Zeta)