rsalz@bbn.com (Rich Salz) (02/06/88)
Submitted-by: Bob Mcqueer <amdahl!rtech!rtech!bobm@UUNET.UU.NET> Posting-number: Volume 13, Issue 21 Archive-name: vn.jan.88/part03 #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # envir_set.c # getch.c # hash.c # newdisp.c # printex.c # reg.c # sig_set.c # stat.c # storage.c # strings.c # strtok.c # svart.c # term_set.c # tmpnam.c # tty_set.c # userlist.c # vnglob.c # This archive created: Sat Jan 9 12:40:08 1988 export PATH; PATH=/bin:$PATH echo shar: extracting "'envir_set.c'" '(2837 characters)' if test -f 'envir_set.c' then echo shar: will not over-write existing file "'envir_set.c'" else cat << \SHAR_EOF > 'envir_set.c' /* ** vn news reader. ** ** envir_set.c - routine to obtain pertinent environment variable settings ** and set up file / directory names ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <pwd.h> #include <sys/param.h> #include "tune.h" #include "config.h" #include "vn.h" extern char *Editor, *Ps1, *Printer; extern char *Orgdir, *Savedir, *Ccfile; /* path names */ extern char Cxitop[], Cxitor[], Cxrtoi[], Cxptoi[]; extern char *Home; extern int More; #ifdef SYSV extern char *getcwd(); #define getwd(a) getcwd(a,sizeof(a)) #define MAXPATHLEN 240 #else extern char *getwd(); #endif /* environment variable, original directory string setup. */ envir_set () { char dbuf [MAXPATHLEN], *ccname, *keyxln; char *vn_env(), *getcwd(), *str_store(); struct passwd *ptr, *getpwuid(); vns_envir(); More = 0; Ps1 = vn_env("PS1",DEF_PS1); Editor = vn_env("EDITOR",DEF_ED); Printer = vn_env("PRINTER",DEF_PRINT); ccname = vn_env("CCFILE",DEF_CCFILE); keyxln = vn_env("VNKEY",DEF_KEYXLN); Savedir = vn_env("VNSAVE",NULL); More = (strcmp(vn_env("MORE",""), "-c") == 0 ? TRUE : FALSE); /* set original directory strings. */ if ((ptr = getpwuid(getuid())) == NULL) printex("Cannot obtain /etc/passwd entry"); Home = str_store(ptr->pw_dir); if ((Orgdir = getwd(dbuf)) == NULL) printex ("cannot stat pwd"); Orgdir = str_store (Orgdir); if (Savedir == NULL) Savedir = Orgdir; if (*ccname != '/') { sprintf (dbuf, "%s/%s",Home,ccname); Ccfile = str_store (dbuf); } else Ccfile = str_store (ccname); sprintf (dbuf, "%s/%s%s",Home,".vn","XXXXXX"); if (*keyxln != '/') { sprintf(dbuf, "%s/%s",Home,keyxln); set_kxln(dbuf); } else set_kxln(keyxln); } char * vn_env(var,def) char *var; char *def; { char pfx[RECLEN]; char *res; char *getenv(); if (var[0] != 'V' || var[1] != 'N') { sprintf(pfx,"VN%s",var); if ((res = getenv(pfx)) != NULL) return(res); } if ((res = getenv(var)) != NULL) return(res); return(def); } static set_kxln(fname) char *fname; { FILE *fp; int i; char bufr[80]; char in,out,*ptr; char *index(), xln_str(); for (i=0; i < 128; ++i) Cxitop[i] = Cxitor[i] = Cxptoi[i] = Cxrtoi[i] = i; if ((fp = fopen(fname,"r")) != NULL) { while(fgets(bufr,79,fp) != NULL) { if (strncmp(bufr+1,"==",2) == 0) ptr = bufr+2; else ptr = index(bufr+1,'='); if (ptr == NULL) continue; *ptr = '\0'; ++ptr; in = xln_str(bufr+1); out = xln_str(ptr); switch(bufr[0]) { case 'r': case 'R': Cxrtoi[out] = in; Cxitor[in] = out; break; case 'p': case 'P': Cxptoi[out] = in; Cxitop[in] = out; default: break; } } fclose(fp); } } static char xln_str(s) char *s; { if (*s < '0' || *s > '9') return(*s & 0x7f); return((char)(atoi(s) & 0x7f)); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'getch.c'" '(1924 characters)' if test -f 'getch.c' then echo shar: will not over-write existing file "'getch.c'" else cat << \SHAR_EOF > 'getch.c' /* ** vn news reader. ** ** getch.c - character i/o routines ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <setjmp.h> #include "config.h" #include "vn.h" extern char Cxitop[]; extern char *Ku, *Kd, *Kr, *Kl; /* getkey obtains user keystroke with count from leading numerics, if any. Picks up arrow key sequences and maps them to other keys. Also translates character through Cxitop array since this routine is only used in session loop. Saves untranslating arrow keys. */ getkey (c) char *c; { int i, j; static char ckseq[32]; /* Check for leading count */ for (i = 0; (*c = getchar() & 0x7f) >= '0' && *c <= '9'; i = i * 10 + *c - '0') ; /* @#$!!! flakey front ends that won't map newlines in raw mode */ if (*c == '\012' || *c == '\015') *c = '\n'; /* @#$!!! flakey terminals which send control sequences for cursors! */ if( *c == '\033' ) { /* ** Check if part of cursor key input sequence ** (pitch unknown escape sequences) */ j = 0; ckseq[j] = *c; ckseq[j+1] = '\0'; while(*c == Ku[j] || *c == Kd[j] || *c == Kl[j] || *c == Kr[j]) { if( strcmp(ckseq, Ku) == 0 ) { *c = UP; break; } if( strcmp(ckseq, Kd) == 0 ) { *c = DOWN; break; } #ifdef PAGEARROW if( strcmp(ckseq, Kl) == 0 ) { *c = BACK; break; } if( strcmp(ckseq, Kr) == 0 ) { *c = FORWARD; break; } #else if( strcmp(ckseq, Kl) == 0 ) { *c = UP; break; } if( strcmp(ckseq, Kr) == 0 ) { *c = DOWN; break; } #endif *c = (getchar() & 0x7f); ckseq[++j] = *c; ckseq[j+1] = '\0'; } } else *c = Cxitop[*c]; if (i <= 0) i = 1; return (i); } /* get user key ignoring most controls. Used from reader and other non-screen interactions. */ getnoctl () { char c; while ((c = getchar() & 0x7f) < ' ' || c == '\177') { if (c == '\015' || c == '\012') c = '\n'; if (c == '\n' || c == '\b' || c == '\t') return (c); } return ((int) c); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'hash.c'" '(1874 characters)' if test -f 'hash.c' then echo shar: will not over-write existing file "'hash.c'" else cat << \SHAR_EOF > 'hash.c' /* ** vn news reader. ** ** hash.c - hash table routines ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" #include "tune.h" #include "node.h" /* ** hash table manipulation routines: */ extern int Ncount; extern NODE **Newsorder; static NODE *Tab [HASHSIZE]; /* hash Table */ hashinit () { int i; for (i=0; i < HASHSIZE; ++i) Tab[i] = NULL; Ncount = 0; } /* enter new node (name s, articles n, low l) in hash Table, initial flags = 0. Set order to -1. */ NODE *hashenter(s,n,l) char *s; int n; int l; { char *str_store(); NODE *ptr,*node_store(); NODE *hashfind(); int i; if ((ptr = hashfind(s)) != NULL) { fgprintf ("Warning: group %s encountered twice",s); return (ptr); } i=hash(s); ptr = node_store(); ptr->next = Tab[i]; Tab[i] = ptr; if (l > n) l = n; ++Ncount; ptr->lownum = l; ptr->state = 0; ptr->data = NULL; ptr->flags = 0; ptr->highnum = n; ptr->nd_name = str_store(s); ptr->pgshwn = 0; ptr->order = -1; return (ptr); } NODE *hashfind(s) char *s; { NODE *ptr; for (ptr = Tab[hash(s)]; ptr != NULL && strcmp(ptr->nd_name,s) != 0; ptr = ptr->next) ; return (ptr); } make_newsorder() { char *malloc(); int i; NODE *ptr; if ((Newsorder = (NODE **) malloc(Ncount * sizeof(NODE *))) == NULL) printex("Memory allocation failure - newsorder array"); for (i=0; i < Ncount; ++i) Newsorder[i] = NULL; for (i=0; i < HASHSIZE; ++i) { for (ptr = Tab[i]; ptr != NULL; ptr = ptr->next) { if (ptr->order < 0 || ptr->order >= Ncount) printex("News order range error"); Newsorder[ptr->order] = ptr; } } for (i=0; i < Ncount; ++i) if (Newsorder[i] == NULL) printex("News order duplication error"); } static hash (s) char *s; { unsigned rem; for (rem=0; *s != '\0'; ++s) rem = (rem*128 + (*s&0x7f)) % HASHSIZE; return (rem); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'newdisp.c'" '(920 characters)' if test -f 'newdisp.c' then echo shar: will not over-write existing file "'newdisp.c'" else cat << \SHAR_EOF > 'newdisp.c' /* ** vn news reader. ** ** newgroups.c - display list of new groups since user's last ession. ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" #include "tty.h" #include "node.h" extern NODE **Newsorder; extern int Ncount, Lrec, C_allow; new_groups () { int i,wrem,w; int max; char fs[24],c_end; max = 0; for (i=0; i < Ncount; ++i) if (((Newsorder[i])->flags & FLG_NEW) != 0 && (w = strlen((Newsorder[i])->nd_name)) > max) max = w; sprintf (fs,"%%-%ds%%c",max); if (max <= 0) return (0); term_set (ERASE); printf ("New newsgroups:\n"); wrem = C_allow; for (i=0; i < Ncount; ++i) { if (((Newsorder[i])->flags & FLG_NEW) == 0) continue; if ((wrem -= max) < max) { wrem = C_allow; c_end = '\n'; } else c_end = ' '; printf (fs,(Newsorder[i])->nd_name,c_end); } if (c_end != '\n') putchar ('\n'); return (1); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'printex.c'" '(678 characters)' if test -f 'printex.c' then echo shar: will not over-write existing file "'printex.c'" else cat << \SHAR_EOF > 'printex.c' /* ** vn news reader. ** ** printex.c - print fatal error message and exit. ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <setjmp.h> #include "config.h" #include "tty.h" extern int errno; /* unix error number */ /* error/abnormal condition cleanup and abort routine pass stack to printf */ printex (s,a,b,c,d,e,f) char *s; long a,b,c,d,e,f; { static int topflag=0; if (topflag == 0) { ++topflag; term_set (STOP); tty_set (COOKED); fflush (stdout); fprintf (stderr,s,a,b,c,d,e,f); fprintf (stderr," (error code %d)\n",errno); vns_exit(1); stat_end(-1); exit (1); } else fprintf (stderr,s,a,b,c,d,e,f); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'reg.c'" '(1656 characters)' if test -f 'reg.c' then echo shar: will not over-write existing file "'reg.c'" else cat << \SHAR_EOF > 'reg.c' /* ** vn news reader. ** ** reg.c - implementation of regex / regcmp on top of UCB library ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #define RGBLKSIZE 20 struct _regtab { struct _regtab *link; char *regstr; }; typedef struct _regtab REGTAB; static REGTAB *Chain = NULL; static REGTAB *Free = NULL; static REGTAB *Compiled = NULL; regfree(s) char *s; { REGTAB *ptr,*cmp,*old; cmp = (REGTAB *) s; old = NULL; for (ptr = Chain; ptr != NULL; ptr = (old = ptr)->link) { if (ptr == cmp) { if (old == NULL) Chain = Chain->link; else old->link = ptr->link; ptr->link = Free; Free = ptr; break; } } } char *regcmp(str) char *str; { int i; char *str_store(); char *re_comp(); if (re_comp(str) != NULL) { Compiled = NULL; /* make sure we're OK */ return(NULL); } if (Free == NULL) { Free = (REGTAB *) malloc(RGBLKSIZE * sizeof(REGTAB)); if (Free == NULL) printex ("regcmp: memory allocation failure"); for (i = 0; i < RGBLKSIZE - 1; ++i) Free[i].link = Free + i + 1; Free[i].link = NULL; } Compiled = Free; Free = Free->link; Compiled->link = Chain; Chain = Compiled; Compiled->regstr = str_store(str); return ((char *) Compiled); } char *regex(reg,str) char *reg,*str; { REGTAB *cmp; cmp = (REGTAB *) reg; if (cmp == Compiled) { if (re_exec(str)) return(str); return (NULL); } for (Compiled = Chain; Compiled != NULL; Compiled = Compiled->link) { if (Compiled == cmp) break; } if (Compiled == NULL) printex ("regex: bad pointer"); re_comp(Compiled->regstr); if (re_exec(str)) return(str); return(NULL); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'sig_set.c'" '(4477 characters)' if test -f 'sig_set.c' then echo shar: will not over-write existing file "'sig_set.c'" else cat << \SHAR_EOF > 'sig_set.c' /* ** vn news reader. ** ** sig_set.c - signal handler ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <sys/signal.h> #include <sgtty.h> #include <setjmp.h> #include "tty.h" #include "config.h" #include "brk.h" #include "tune.h" #include "node.h" #include "page.h" extern int L_allow; extern char *Version; extern char *Brk_fmt; extern char *Vns_version; static int Sigflag=BRK_INIT; /* phase of interaction */ static FILE **Fpseek; /* article reading file pointer pointer */ static int Foreground; static jmp_buf Jumploc; /* for BRK_SESS phase */ static int Need_restart = 0; /* interrupt handler - unusual termination (longjmp and printex aborts) if not abort, remember to reset signal trap CAUTION - the passing of a jump buffer is a little dicey - assumes type jump_buf is an array. sigcatch and sig_set control a lot of i/o on stderr also, since it is so intimately related to signal interaction. Note that the SIGTSTP action causes a "stopped on tty output" if raw terminal mode is restored by tty_set(RESTORE). We don't get it if we were already cooked since tty_set avoids calling ioctl if it doesn't have to. */ static sigcatch (sig) int sig; { char buf [MAX_C+1]; int pgrp; /* disable signal while processing it */ signal (sig,SIG_IGN); switch (sig) { case SIGINT: case SIGQUIT: break; #ifdef JOBCONTROL case SIGTSTP: /* ignore SIGTTOU so we don't get stopped if [kc]sh grabs the tty */ signal(SIGTTOU, SIG_IGN); tty_set (SAVEMODE); term_set (MOVE,0,L_allow+RECBIAS-1); printf ("\n"); Foreground = 0; fflush (stdout); fflush (stderr); signal(SIGTTOU, SIG_DFL); /* Send the TSTP signal to suspend our process group */ signal(SIGTSTP, SIG_DFL); sigsetmask(0); kill (0, SIGTSTP); /* WE ARE NOW STOPPED */ /* WELCOME BACK! if terminals process group is ours, we are foregrounded again and can turn newsgroup name printing back on */ tty_set (RESTORE); /* ** Note concerning RESTART. If in state BRK_IN, we simply ** set a flag to do it upon switch to state BRK_SESS - we ** don't want to send i/o to the terminal when we ** background during BRK_IN phase ("stopped on tty output") */ switch (Sigflag) { case BRK_SESS: signal (SIGTSTP,sigcatch); term_set (RESTART); longjmp (Jumploc,1); case BRK_IN: Need_restart = 1; ioctl (1,TIOCGPGRP,&pgrp); if (pgrp == getpgrp(0)) Foreground = 1; break; default: term_set (RESTART); break; } signal (SIGTSTP,sigcatch); return; #endif default: printex (Brk_fmt,sig); } /* QUIT and INTERRUPT signals */ switch (Sigflag) { case BRK_SESS: /* if in session, ask if really a quit, do longjump if not */ term_set (ERASE); tty_set (RAWMODE); user_str (buf, BRK_PR, 1, ""); if (buf[0] == 'y') printex (Brk_fmt,sig); signal (sig,sigcatch); longjmp (Jumploc,1); case BRK_READ: /* if reading seek file to end to abort page printing */ printf ("\n"); if (*Fpseek == NULL || fseek(*Fpseek,0L,2) < 0) putchar ('\07'); break; default: printex (Brk_fmt,sig); } signal (sig,sigcatch); } /* sig_set controls what will be done with a signal when picked up by sigcatch. fgprintf is included here to keep knowledge of TSTP state localized. */ /* VARARGS */ sig_set (flag,dat) int flag, *dat; { int i, *xfer, pgrp; if (Sigflag == BRK_INIT) { signal (SIGINT,sigcatch); signal (SIGQUIT,sigcatch); signal (SIGTERM,sigcatch); #ifdef JOBCONTROL signal (SIGTSTP,sigcatch); ioctl (1,TIOCGPGRP,&pgrp); if (pgrp == getpgrp(0)) { Foreground = 1; fgprintf ("Visual News, %s(%s), reading:\n", Version, Vns_version); } else Foreground = 0; #else Foreground = NOJOB_FG; #endif } switch (flag) { case BRK_IN: case BRK_OUT: Sigflag = flag; break; case BRK_READ: if (Sigflag != BRK_SESS) printex ("unexpected read state, sig_set\n"); Fpseek = (FILE **) dat; Sigflag = BRK_READ; break; case BRK_SESS: if (Need_restart) term_set(RESTART); xfer = (int *) Jumploc; for (i=0; i < sizeof(Jumploc) / sizeof(int); ++i) xfer[i] = dat[i]; Sigflag = BRK_SESS; break; case BRK_RFIN: if (Sigflag != BRK_READ) printex ("unexpected finish state, sig_set\n"); Sigflag = BRK_SESS; break; default: printex ("bad state %d, sig_set\n",flag); } } fgprintf (fs,a,b,c,d,e) char *fs; int a,b,c,d,e; { if (Foreground) fprintf (stderr,fs,a,b,c,d,e); fflush (stderr); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'stat.c'" '(4226 characters)' if test -f 'stat.c' then echo shar: will not over-write existing file "'stat.c'" else cat << \SHAR_EOF > 'stat.c' /* ** vn news reader. ** ** stat.c - stat and log file collection ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <sys/types.h> #ifdef SYSV #include <fcntl.h> #endif #include <sys/file.h> #include <sys/stat.h> #include <pwd.h> #include "config.h" #include "node.h" extern NODE *hashfind(); extern char *strtok(); extern int Ncount; extern NODE **Newsorder; #ifdef VNLOGFILE static char Start[80]; #endif stat_start() { #ifdef VNLOGFILE char *ctime(); long now; time(&now); strcpy(Start,ctime(&now)); #endif } /* ** flag = 0, "NO NEWS" type session. ** = 1, regular session. ** = -1, aborted session ** ** CAUTION: routine CALLED from within printex() - do NOT ** call printex(). Simply do message to stderr on fail. */ stat_end(flag) int flag; { NODE *nd; char *nl,*index(); char *how; struct passwd *ptr, *getpwuid(); struct stat buf; long now; char bufr[80]; char name[60]; FILE *fp; int fd; long chk, rd, pg; int i; #ifdef VNLOGFILE if (stat(VNLOGFILE,&buf) == 0 && (fp = fopen(VNLOGFILE,"a")) != NULL) { time(&now); strcpy(bufr,ctime(&now)); if ((nl = index(bufr,'\n')) != NULL) *nl = '\0'; if ((nl = index(Start,'\n')) != NULL) *nl = '\0'; if (flag == 0) how = "NO NEWS"; else { if (flag > 0) how = "OK"; else how = "ABORTED"; } ptr = getpwuid (getuid()); fprintf(fp, "%s\t%s - %s %s\n", ptr->pw_name, Start, bufr, how); fclose (fp); } #endif #ifdef VNSTATFILE /* ** Stat file is done with a fixed record size, and maintaining the ** existing record order exactly so that concurrent users will do ** the least damage. If two users actually read & update a single ** record simultaneously, we should just lose one user's counts. ** Short of implementing a locking scheme, we probably won't do ** much better. Disadvantages are that deleted newsgroups never ** get cleaned out, order is set by the first user whose ** statistics are collected, it will break if anyone modifies it, ** and the file is a bit larger than it needs to be. ** ** record format: ** ** CCCCCC PPPPPP RRRRRR newsgroup name .... \n ** ^ ^ ^ ^ ^ ** 0 7 14 21 char 79 ** ** CCCCCC - count of sessions searching group ** PPPPPP - count of sessions actually finding pages for group ** RRRRRR - count of sessions actually accessing articles in group */ if ((fd = open(VNSTATFILE,O_RDWR)) > 0) { bufr[80] = '\0'; /* ** read a record, find the newsgroup, update counts. ** If changed, seek back & overwrite. By using fixed ** length records, we should only lose something on ** concurrent writes of the same record, and by writing ** the ENTIRE record, we keep it consistent */ while ((i = read(fd,bufr,80)) == 80 && bufr[79] == '\n') { chk = atoi(bufr); pg = atoi(bufr+7); rd = atoi(bufr+14); strcpy(name,bufr+21); nl = strtok(name," \n"); if (nl == NULL || (nd = hashfind(nl)) == NULL) continue; nd->flags |= FLG_STAT; if ((nd->flags & (FLG_SEARCH|FLG_ACC|FLG_PAGE)) == 0) continue; if ((nd->flags & FLG_SEARCH) != 0) ++chk; if ((nd->flags & FLG_PAGE) != 0) ++pg; if ((nd->flags & FLG_ACC) != 0) ++rd; if (chk > 999999L) chk = 999999L; if (pg > 999999L) pg = 999999L; if (rd > 999999L) rd = 999999L; sprintf(bufr,"%6ld",chk); bufr[6] = ' '; sprintf(bufr+7,"%6ld",pg); bufr[13] = ' '; sprintf(bufr+14,"%6ld",rd); bufr[20] = ' '; lseek(fd,-80L,1); write(fd,bufr,80); } /* format screwed up ? */ if (i != 0) { lseek(fd,(long) -i,1); fprintf(stderr,"bad data in %s\n",VNSTATFILE); } /* may have aborted during vns_news() */ if (Newsorder == NULL) Ncount = 0; /* now append any groups not in file yet */ for (i = 0; i < Ncount; ++i) { nd = Newsorder[i]; if ((nd->flags & FLG_STAT) != 0) continue; chk = rd = pg = 0; if ((nd->flags & FLG_SEARCH) != 0) chk = 1; if ((nd->flags & FLG_PAGE) != 0) pg = 1; if ((nd->flags & FLG_ACC) != 0) rd = 1; sprintf(bufr,"%6ld %6ld %6ld %-58s\n", chk, pg, rd, nd->nd_name); write(fd,bufr,80); } close(fd); } #endif } SHAR_EOF fi # end of overwriting check echo shar: extracting "'storage.c'" '(2261 characters)' if test -f 'storage.c' then echo shar: will not over-write existing file "'storage.c'" else cat << \SHAR_EOF > 'storage.c' /* ** vn news reader. ** ** storage.c - storage allocation routines ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "tune.h" #include "node.h" #include "page.h" extern char *malloc(); extern int L_allow; extern PAGE Page; /* Storage allocaters. */ char *str_store (s) char *s; { static unsigned av_len = 0; /* current storage available */ static char *avail; int len; if (s == NULL) s = ""; if ((len = strlen(s)+1) > av_len) { if (len > STRBLKSIZE) av_len = len; else av_len = STRBLKSIZE; if ((avail = malloc(av_len)) == NULL) printex ("can't allocate memory for string storage"); } strcpy (avail,s); s = avail; avail += len; av_len -= len; return (s); } /* ** called after number of terminal lines (L_allow) is known, to set ** up storage for Page. */ page_alloc () { char *body; if ((body = malloc(L_allow*sizeof(BODY))) == NULL) printex ("can't allocate memory for display storage"); Page.b = (BODY *) body; } NODE *node_store() { static int nd_avail = 0; static NODE *nd; NODE *ret; if (nd_avail <= 0) { if ((nd = (NODE *) malloc(sizeof(NODE)*NDBLKSIZE)) == NULL) printex ("can't allocate memory for newsgroup table"); nd_avail = NDBLKSIZE; } --nd_avail; ret = nd; ++nd; return(ret); } /* ** temp string storage */ typedef struct { int len; int idx; char **ptr; } STRINGPOOL; char * str_tpool(n) int n; { int size; STRINGPOOL *p; size = sizeof(STRINGPOOL) + n * sizeof(char **); if ((p = (STRINGPOOL *) malloc(size)) == NULL) printex("Cannot allocate temporary string storage"); p->ptr = (char **)(p+1); p->len = n; p->idx = 0; return((char *) p); } char * str_tstore(cp,s) char *cp; char *s; { STRINGPOOL *p; int len; p = (STRINGPOOL *) cp; if (p->idx >= p->len) printex("Temporary string storage overflow"); len = strlen(s)+1; if ((cp = malloc(len)) == NULL) printex("Cannot allocate copy of string"); strcpy(cp,s); (p->ptr)[p->idx] = cp; ++(p->idx); return(cp); } char ** str_taptr(cp) char *cp; { STRINGPOOL *p; p = (STRINGPOOL *) cp; return (p->ptr + p->idx); } str_tfree(cp) char *cp; { STRINGPOOL *p; int i; p = (STRINGPOOL *) cp; for (i=0; i < p->idx; ++i) free((p->ptr)[i]); free (cp); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'strings.c'" '(537 characters)' if test -f 'strings.c' then echo shar: will not over-write existing file "'strings.c'" else cat << \SHAR_EOF > 'strings.c' /* ** vn news reader. ** ** strings.c - read only character strings ** ** see copyright disclaimer / history in vn.c source file */ #include "tune.h" #include "node.h" #include "page.h" char *Version = "1/88"; char *No_msg = "No articles"; char *Hdon_msg = "Headers being printed"; char *Hdoff_msg = "Headers being suppressed"; char *Roton_msg = "ROT 13"; char *Rotoff_msg = "NO ROT"; char *Aformat = AFORMAT; char *Contstr = " ******** any key to continue ********"; char *Brk_fmt = "QUIT (signal %d)"; char *List_sep = " \t,"; SHAR_EOF fi # end of overwriting check echo shar: extracting "'strtok.c'" '(1082 characters)' if test -f 'strtok.c' then echo shar: will not over-write existing file "'strtok.c'" else cat << \SHAR_EOF > 'strtok.c' /* ** vn news reader. ** ** strtok.c - strtok() and strpbrk() string routines using UCB index(). ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> char *strpbrk (s,del) char *s, *del; { char *ptr,*index(); if (s == NULL) return (NULL); for (; *del != '\0'; ++del) if ((ptr = index(s,*del)) != NULL) return (ptr); return (NULL); } char *strtok(str,delim) char *str, *delim; { char *tokstart, *tokend, *first_ch (), *last_ch(); static char *save=NULL; if (str != NULL) save = str; if (save == NULL) return (NULL); tokstart = first_ch (save, delim); tokend = last_ch (tokstart, delim); save = first_ch (tokend, delim); *tokend = '\0'; if (*tokstart == '\0') return (NULL); return (tokstart); } static char *first_ch (str,delim) char *str,*delim; { char *index (); char *f; for (f = str; *f != '\0' && index(delim,*f) != NULL; ++f) ; return (f); } static char *last_ch (str,delim) char *str,*delim; { char *index (); char *f; for (f = str; *f != '\0' && index(delim,*f) == NULL; ++f) ; return (f); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'svart.c'" '(4156 characters)' if test -f 'svart.c' then echo shar: will not over-write existing file "'svart.c'" else cat << \SHAR_EOF > 'svart.c' /* ** vn news reader. ** ** svart.c - article save routine ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include <pwd.h> #include <sys/types.h> #include <sys/stat.h> #include "config.h" #include "tty.h" #include "tune.h" #include "node.h" #include "page.h" extern PAGE Page; extern int Digest; extern char *List_sep; extern char *Home; extern char *Savedir; /* ** save article in file. Called from reader and session both. ** handles "|" pipe convention. Caller passes in return message buffer. */ save_art(art,idest,msg) char *art; char *idest; char *msg; { char fn[L_tmpnam+1]; char cmd[RECLEN]; char *mode; char *dest, dstore[RECLEN]; struct stat sbuf; int rstat; char *colon; char *pcnt; char *index(); /* temporary copy so we don't overwrite saved string */ strcpy((dest = dstore),idest); if (*dest == '|') { tmpnam(fn); if (art_xfer(fn,art,"w") != 0) { strcpy(msg,"Can't open temporary file"); return (-1); } sprintf(cmd,"cat %s %s",fn,dest); tty_set (SAVEMODE); rstat = system (cmd); tty_set (RESTORE); sprintf(msg,"Command returns %d",rstat); return (rstat); } if ((colon = index(dest,':')) != NULL) { mode = dest; *colon = '\0'; dest = colon+1; } else mode = "a"; if (*dest == '~') { if (twiddle(dest,msg) < 0) return (-1); } if (*dest == '\0') strcpy(dest,"%d"); if (*dest != '/') { if (noslash(dest,msg) < 0) return (-1); } if ((pcnt = index(dest,'%')) != NULL && pcnt[1] == 'd') { if (Digest) sprintf(cmd,dest,Digest); else sprintf(cmd,dest,atoi(art)); dest = cmd; } rstat = stat(dest,&sbuf); if (art_xfer(dest,art,mode) != 0) { sprintf(msg,"Can't open %s with mode %s",dest,mode); return(-1); } if (rstat != 0) { sprintf(msg,"Created %s",dest); return(0); } if (strcmp(mode,"a") == 0) { sprintf(msg,"Appended %s",dest); return(0); } sprintf(msg,"Wrote (mode %s) %s",mode,dest); return(0); } static noslash(dest,msg) char *dest; char *msg; { char *pcnt; char buf[RECLEN]; char dir[RECLEN]; struct stat sbuf; strcpy(buf,Page.h.name); #ifdef SYSV buf[14] = '\0'; #endif if ((pcnt = index(Savedir,'%')) != NULL && pcnt[1] == 's') sprintf(dir,Savedir,buf); else strcpy(dir,Savedir); if (dir[0] == '~') { if (twiddle(dir,msg) < 0) return (-1); } if (stat(dir,&sbuf) != 0) { #ifdef SYSV /* ** late enough releases of SYSV may have a mkdir() call, but ** this is an obscure feature anyway. We'll accept the fork. */ sprintf(buf,"mkdir %s",dir); if (system(buf) != 0) #else if (mkdir(dir,0755) != 0) #endif { sprintf(msg,"Cannot make directory %s",dir); return (-1); } } sprintf(buf,"%s/%s",dir,dest); strcpy(dest,buf); return (0); } static twiddle(dest,msg) char *dest, *msg; { char *tail; char *name; char tmp; char buf[RECLEN]; struct passwd *ptr, *getpwnam(); for (tail=name=dest+1; *tail != '/' && *tail != '\0'; ++tail) ; if (*name == '\0' || *name == '/') sprintf(buf,"%s%s",Home,tail); else { tmp = *tail; *tail = '\0'; ptr = getpwnam(name); *tail = tmp; if (ptr == NULL) { sprintf(msg,"Can't interpret ~%s",name); return(-1); } sprintf(buf,"%s%s",ptr->pw_dir,tail); } strcpy(dest,buf); return (0); } /* ** transfer contents of a list of articles to a file. If Digest, this ** is simply a list of files. If not, it is a list of articles to be ** saved with vns_asave. Parses list destructively with ** strtok(). Return 0 for success, -1 for failure to open file. ** ** Called directly to copy a list of articles to a temp. file to ** direct to printer. */ art_xfer(fn,list,mode) char *fn, *list, *mode; { char *p; FILE *fout, *fin; int count; char buf[RECLEN]; char *strtok(); if ((fout = fopen(fn,mode)) == NULL) return (-1); count = 0; for (p = strtok(list,List_sep); p != NULL; p = strtok(NULL,List_sep)) { if (Digest) { fin = fopen(p,"r"); if (fin == NULL) continue; while (fgets(buf,RECLEN-1,fin) != NULL) fputs(buf,fout); fclose(fin); continue; } vns_asave(atoi(p),fout,count,fn,mode); ++count; } fclose(fout); return(0); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'term_set.c'" '(4953 characters)' if test -f 'term_set.c' then echo shar: will not over-write existing file "'term_set.c'" else cat << \SHAR_EOF > 'term_set.c' /* ** vn news reader. ** ** term_set.c - terminal control, hides termcap interface ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "tty.h" #include "config.h" #include "tune.h" #include "node.h" #include "page.h" extern int L_allow, C_allow; extern char *Ku, *Kd, *Kl, *Kr; static outc (c) char c; { putchar (c); } /* term_set controls terminal through termcap START sets global parameters related to terminal also, as well as allocating display buffer which depends on terminal lines, and allocating escape strings. RESTART simply re-issues the initialization - used following system calls that could have goofed up the terminal state. */ /* ** Escape strings. */ static char *Cm,*Cl,*So,*Se,*Te,*Bc,*Ce,*Ti,*Ks,*Ke; #ifdef USEVS static char *Vs,*Ve; #endif static int Backspace; /* backspace works */ static int Overstrike; /* terminal overstrikes */ static t_setup() { char *tgetstr(), *vn_env(), *str_store(); char *c, tc_buf[2048],optstr[2048]; char *tvar; tvar = vn_env("TERM",DEF_TERM); c = optstr; if (tgetent(tc_buf,tvar) != 1) printex ("%s - unknown terminal",tvar); /* get needed capabilities */ Cm = str_store(tgetstr("cm",&c)); Cl = str_store(tgetstr("cl",&c)); So = str_store(tgetstr("so",&c)); Se = str_store(tgetstr("se",&c)); Te = str_store(tgetstr("te",&c)); Ti = str_store(tgetstr("ti",&c)); Bc = str_store(tgetstr("bc",&c)); Ce = str_store(tgetstr("ce",&c)); Kd = str_store(tgetstr("kd",&c)); Ke = str_store(tgetstr("ke",&c)); Kl = str_store(tgetstr("kl",&c)); Kr = str_store(tgetstr("kr",&c)); Ks = str_store(tgetstr("ks",&c)); Ku = str_store(tgetstr("ku",&c)); #ifdef USEVS Vs = str_store(tgetstr("vs",&c)); Ve = str_store(tgetstr("ve",&c)); #endif Backspace = tgetflag("bs"); Overstrike = tgetflag("os"); if ( *Cm == '\0' || *Cl == '\0') { printex ("cursor control and erase capability needed"); } /* ** Checks for arrow keys which don't issue something beginning ** with <ESC>. This is more paranoid than we need to be, strictly ** speaking - we could get away with any string which didn't ** conflict with controls used for commands. However, that would ** be a maintenance headache - we will simply reserve <ESC> as the ** only char not to be used for commands, and punt on terminals ** which don't send reasonable arrow keys. It would be confusing ** to have keys work partially, also. I know of no terminal with ** one arrow key beginning with an escape, and another beginning ** with something else, but let's be safe. This also insists on ** definitions for all 4 arrows, which seems reasonable. */ if ((*Ku != '\0' && *Ku != '\033') || *Kl != *Ku || *Kr != *Ku || *Kd != *Ku) { fgprintf("WARNING: arrow keys will not work for this terminal"); Ku = Kd = Kl = Kr = Kd = Ke = ""; } if (Overstrike) fgprintf ("WARNING: terminal overstrikes - can't update display without erase\n"); if ((L_allow = tgetnum("li")) < REQLINES) { if (L_allow < 0) printex ("can't determine number of lines on terminal"); printex ("too few lines for display - %d needed", REQLINES); } /* ** C_allow set so as to not use extreme right column. ** Avoids "bad wraparound" problems - we're deciding it's best ** to ALWAYS assume no automargin, and take care of it ourselves */ if((C_allow = tgetnum("co")) > MAX_C) C_allow = MAX_C; else --C_allow; if (C_allow < MIN_C) { if (C_allow < 0) printex("can't determine number of columns on terminal."); printex ("too few columns for display - %d needed",MIN_C); } L_allow -= RECBIAS; page_alloc(); tputs(Ti,1,outc); tputs(Ks,1,outc); #ifdef USEVS tputs(Vs,1,outc); #endif } /* VARARGS */ term_set(cmd,x,y) int cmd,x,y; { char *tgoto(); int i; switch (cmd) { case MOVE: tputs (tgoto(Cm,x,y),1,outc); break; case ERASE: tputs(Cl,1,outc); break; case ONREVERSE: tputs(So,1,outc); break; case OFFREVERSE: tputs(Se,1,outc); break; case START: t_setup(); break; case RESTART: tputs(Ti,1,outc); tputs(Ks,1,outc); #ifdef USEVS tputs(Vs,1,outc); #endif break; case STOP: term_set (MOVE,0,L_allow+RECBIAS-1); printf ("\n"); tputs(Ke,1,outc); tputs(Te,1,outc); #ifdef USEVS tputs(Ve,1,outc); #endif break; case RUBSEQ: if (Overstrike) { /* space overprint is futile */ if (Backspace) putchar('\010'); else tputs(Bc,1,outc); break; } if (Backspace) printf("%c %c",'\010','\010'); else { tputs(Bc,1,outc); putchar(' '); tputs(Bc,1,outc); } break; case ZAP: if (Ce != NULL && *Ce != '\0') tputs(Ce,1,outc); else { if (Overstrike) break; /* punt */ for (i=x; i < y; ++i) putchar(' '); if (Backspace) { for (i=x; i < y; ++i) putchar('\010'); } else { for (i=x; i < y; ++i) tputs(Bc,1,outc); } } break; default: printex ("term_set unknown code (%d)",cmd); break; } return (0); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'tmpnam.c'" '(420 characters)' if test -f 'tmpnam.c' then echo shar: will not over-write existing file "'tmpnam.c'" else cat << \SHAR_EOF > 'tmpnam.c' /* ** vn news reader. ** ** tmpnam.c - tmpnam() replacement for UCB, also uses non-generic name. ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" char *tmpnam (buf) char *buf; { static char *ptr = VNTEMPNAME; /* depends on string initialized above */ sprintf (ptr+TMP_XOFFSET,"XXXXXX"); mktemp (ptr); if (buf != NULL) strcpy (buf,ptr); return (ptr); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'tty_set.c'" '(2552 characters)' if test -f 'tty_set.c' then echo shar: will not over-write existing file "'tty_set.c'" else cat << \SHAR_EOF > 'tty_set.c' /* ** vn news reader. ** ** tty_set.c - interface to ioctl (system tty interface) ** ** see copyright disclaimer / history in vn.c source file */ #ifdef SYSV #include <termio.h> #else #include <sgtty.h> #endif #include "tty.h" extern char Erasekey,Killkey; #ifdef SYSV static struct termio C_tp, O_tp; #else static struct sgttyb C_tp; static unsigned short O_lflag; #endif static unsigned S_flag=0; static int R_ignore=0; /* up/down counter of reset calls to ignore */ #define IO_GOT 1 /* have polled for original terminal mode */ #define IO_RAW 2 /* in RAW (CBREAK actually) mode */ /* tty_set handles ioctl calls. SAVEMODE, RESTORE are used around system calls and interrupts to assure cooked mode, and restore raw if raw on SAVEMODE. The pair results in no calls to ioctl if we are cooked already when SAVEMODE is called, and may be nested, provided we desire no "restore" of cooked mode after restoring raw. When we get the original terminal mode, we also save erase and kill. sig_set makes an ioctl call to get process group leader. Otherwise ioctl calls should come through here. */ tty_set(cmd) int cmd; { int rc; unsigned mask; switch (cmd) { case BACKSTOP: #ifdef JOBCONTROL if ((rc = ioctl(1,TIOCLGET,&mask)) != 0) break; mask |= LTOSTOP; rc = ioctl(1,TIOCLSET,&mask); #else rc = 0; #endif break; case RAWMODE: if ((S_flag & IO_RAW) != 0) { rc = 0; break; } if ((S_flag & IO_GOT) == 0) { /* Save original modes, get erase / kill */ #ifdef SYSV rc = ioctl(0,TCGETA,&C_tp); O_tp = C_tp; Erasekey = C_tp.c_cc[VERASE]; Killkey = C_tp.c_cc[VKILL]; #else rc = ioctl(0,TIOCGETP,&C_tp); O_lflag = C_tp.sg_flags; Erasekey = C_tp.sg_erase; Killkey = C_tp.sg_kill; #endif } #ifdef SYSV C_tp.c_lflag &= ~(ECHO | ICANON); C_tp.c_cc[VMIN] = 1; rc = ioctl(0,TCSETAW,&C_tp); #else C_tp.sg_flags |= CBREAK; C_tp.sg_flags &= ~ECHO; rc = ioctl(0,TIOCSETP,&C_tp); #endif S_flag = IO_GOT|IO_RAW; break; case COOKED: if ((S_flag & IO_RAW) != 0) { #ifdef SYSV C_tp = O_tp; rc = ioctl(0,TCSETAW,&C_tp); #else C_tp.sg_flags = O_lflag; rc = ioctl(0,TIOCSETP,&C_tp); #endif S_flag &= ~IO_RAW; } else rc = 0; break; case SAVEMODE: if ((S_flag & IO_RAW) != 0) { tty_set(COOKED); R_ignore = 0; } else ++R_ignore; rc = 0; break; case RESTORE: if (R_ignore <= 0) { tty_set(RAWMODE); } else --R_ignore; rc = 0; break; default: rc = -1; } if (rc < 0) printex ("ioctl failure, tty_set: %d",cmd); } SHAR_EOF fi # end of overwriting check echo shar: extracting "'userlist.c'" '(2130 characters)' if test -f 'userlist.c' then echo shar: will not over-write existing file "'userlist.c'" else cat << \SHAR_EOF > 'userlist.c' /* ** vn news reader. ** ** userlist.c - generate user's list of articles ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "tune.h" #include "node.h" #include "page.h" #include "vn.h" extern PAGE Page; extern char *List_sep; static char Pattern[MAX_C] = ""; /* generate user list of articles - either article numbers are input directly (numeric list), or input is a search string - invoke regular expression library and examine titles search string "*" reserved for marked articles. Strings may be prefixed with '!' for negation. */ userlist (list) char *list; { int i,j,anum[RECLEN/2],acount; char neg, *s, sbuf[MAX_C+1], *reg, *regex(), *regcmp(), *index(), *strtok(); user_str (sbuf,"Articles or title search string : ",1,Pattern); if (sbuf[0] == '!') { neg = '!'; s = sbuf+1; } else { neg = '\0'; s = sbuf; } for (i=0; s[i] != '\0'; ++i) { if (index(List_sep,s[i]) == NULL) { if (s[i] < '0' || s[i] > '9') break; } } acount = 0; if (s[i] == '\0') { for (s = strtok(s,List_sep); s != NULL; s = strtok(NULL,List_sep)) { anum[acount] = atoi(s); ++acount; } } else { /* we save old input only if NOT a list of article numbers */ strcpy(Pattern,sbuf); if (s[0] == ART_MARK) { for (i=0; i < Page.h.artnum; ++i) { if (Page.b[i].art_mark == ART_MARK) { anum[acount] = Page.b[i].art_id; ++acount; } } } else { reg = regcmp(s,(char *) 0); if (reg != NULL) { for (i=0; i < Page.h.artnum; ++i) { if (regex(reg,Page.b[i].art_t) != NULL) { anum[acount] = Page.b[i].art_id; ++acount; } } regfree (reg); } else preinfo ("bad regular expression syntax"); } } /* algorithm is inefficient, but we're only handling a few numbers */ *list = '\0'; for (i=0; i < Page.h.artnum; ++i) { for (j=0; j < acount && anum[j] != Page.b[i].art_id; ++j) ; if (neg == '!') { if (j < acount) continue; } else { if (j >= acount) continue; } sprintf (list,"%d ",Page.b[i].art_id); list += strlen(list); } } SHAR_EOF fi # end of overwriting check echo shar: extracting "'vnglob.c'" '(1473 characters)' if test -f 'vnglob.c' then echo shar: will not over-write existing file "'vnglob.c'" else cat << \SHAR_EOF > 'vnglob.c' /* ** vn news reader. ** ** vnglob.c - global variables - see string.c also ** ** see copyright disclaimer / history in vn.c source file */ #include <stdio.h> #include "config.h" #include "head.h" #include "tune.h" #include "node.h" #include "page.h" /* global data structure */ NODE **Newsorder = NULL; /* in order of fw_group calls */ char *Editor, *Ps1, *Printer; int (*Massage)() = NULL; int (*Headedit)() = NULL; int (*Postfunc)() = NULL; int (*Mailfunc)() = NULL; char Erasekey, Killkey; /* user keys from stty */ char *Orgdir; /* .newsrc file, and original pwd */ char *Savefile = DEF_SAVE; /* file in which to save articles */ char *Savedir; /* default directory for saved articles */ char *Ccfile; /* author_copy file, stored /bin/mail fmt */ char *Home; /* user's home */ int Rot; /* rotation */ int Headflag; /* header printing flag */ int Digest; /* if non-zero, digest article */ int More; /* if non-zero, clear screen between each page. Set by */ /* user's MORE environment variable; if `-c', then set true */ char *Ku, *Kd, *Kl, *Kr; /* Cursor movement capabilities */ /* character translation arrays for commands */ char Cxitop[128], Cxitor[128], Cxrtoi[128], Cxptoi[128]; /* cur_page - current page displayed; lrec - last record l_allow - lines allowed for article display c_allow - columns allowed ncount = newsorder index */ int Cur_page, Lrec, L_allow, C_allow, Ncount; int Nounsub, Listfirst; /* current page */ PAGE Page; SHAR_EOF fi # end of overwriting check # End of shell archive exit 0 -- For comp.sources.unix stuff, mail to sources@uunet.uu.net.