|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T r
Length: 18610 (0x48b2)
Types: TextFile
Names: »reader.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/euug-87hel/sec1/vn/reader.c«
/*
** vn news reader.
**
** reader.c - article reading interface - "more" like.
**
** see copyright disclaimer / history in vn.c source file
*/
#include <stdio.h>
#include <sys/types.h>
#include "tty.h"
#include "config.h"
#include "vn.h"
#include "head.h"
#include "reader.h"
#define PERTAB 8 /* tab expansion factor */
#define BACKTRACK 24
extern char *Printer,*Editor,*Mailer,*Poster,*Orgdir,*Savefile,*Savedir,*Ccfile;
extern int L_allow;
extern int C_allow;
extern int Rot;
extern int Headflag;
extern int Digest;
extern char *No_msg;
extern char *Roton_msg;
extern char *Rotoff_msg;
extern char *Hdon_msg;
extern char *Hdoff_msg;
extern char *T_head, *FT_head, *N_head, *L_head, *RT_head, *DIS_head;
extern char *TO_head, *F_head, *P_head, *M_head, *R_head;
extern char Cxrtoi[], Cxitor[];
static FILE *Fpread;
static char *Fname;
static char *Lookahead;
static int Rlines;
static int Hlines;
#ifdef ADDRMUNGE
static int Newaddr;
#endif
/*
readstr routine is the "funnel" to the reading state,
and controls signal setting. Some "session" context is passed
along to allow jumping back to a different display
WARNING:
NOTHING below readstr should call strtok()
*/
readstr (s,crec,highrec,count)
char *s;
int *crec, *highrec;
int count;
{
char *fnext, *strtok();
int pc;
Fname = strtok(s,LIST_SEP);
if (Fname != NULL)
{
term_set (ERASE);
sig_set (BRK_READ,&Fpread);
fnext = strtok(NULL,LIST_SEP);
while (Fname != NULL && readfile(fnext,&pc) >= 0)
{
if (Digest)
unlink (Fname);
Fname = fnext;
fnext = strtok (NULL,LIST_SEP);
}
if (Digest && Fname != NULL)
unlink (Fname);
if (pc != 0)
forward (pc, crec, highrec);
else
{
*crec += count;
if (*crec >= *highrec)
*crec = *highrec - 1;
}
sig_set (BRK_RFIN);
show ();
term_set (MOVE, 0, *crec);
}
else
{
preinfo ("%s",No_msg);
term_set (MOVE, 0, *crec);
}
}
\f
/*
readfile presents article:
sn - name of next article, NULL if last.
pages - pages to advance on return, if applicable
returns 0 for "continue", <0 for "quit"
*/
static readfile (sn,pages)
char *sn;
int *pages;
{
FILE *fopen();
int lines,percent,artlin;
long rew_pos, ftell();
char c, buf[RECLEN], mid[RECLEN], ngrp[RECLEN], dist[RECLEN];
char from[RECLEN], title[RECLEN], flto[RECLEN], reply[RECLEN];
char pstr[24], dgname[48], getpgch(), *index(), *digest_extract();
char *tgetstr();
*pages = 0;
term_set(ERASE);
if (Digest)
{
lines = atoi(Fname);
if ((Fname = digest_extract(dgname,lines)) == NULL)
{
printf ("couldn't extract article %d",lines);
return (0);
}
}
if ((Fpread = fopen(Fname,"r")) == NULL)
{
printf ("couldn't open article %s",Fname);
return (0);
}
Hlines = gethead (mid, from, title, ngrp, flto, reply, dist, &artlin);
printf (ANFORM,Fname,Cxrtoi[PG_HELP]);
lines = 1;
rew_pos = ftell(Fpread);
if (Headflag)
{
rewind(Fpread);
Rlines = 0;
}
else
{
/* use do_out to guard against control sequences */
Rlines = Hlines;
sprintf (buf,"%s%s\n",T_head,title);
lines += do_out(buf,1);
sprintf (buf,"%s%s\n",F_head,from);
lines += do_out(buf,1);
if (index(ngrp,',') != NULL)
{
sprintf (buf,"%s%s\n",N_head,ngrp);
lines += do_out(buf,1);
}
if (*flto != '\0')
{
sprintf (buf,"%s%s\n",FT_head,flto);
lines += do_out(buf,1);
}
printf ("%s%d\n",L_head,artlin); /* no controls */
++lines;
}
\f
/* will return out of outer while loop */
Lookahead = NULL;
while (1)
{
/*
** lines counts folded lines from do_out.
** globals Hlines and Rlines refer to records.
** If Lookahead is null after this loop, we've
** hit EOF.
*/
lines += do_out(Lookahead,L_allow-lines);
while (1)
{
if (Lookahead == NULL)
{
if (fgets(buf,RECLEN-1,Fpread) == NULL)
break;
Lookahead = buf;
if (Rot != 0 && Rlines >= Hlines)
rot_line(buf);
++Rlines;
}
if (lines >= L_allow)
break;
lines += do_out(buf,L_allow-lines);
}
if (Lookahead != NULL)
{
/*
** calculation is truncated rather than rounded,
** so we shouldn't get "100%". Subtract 2 for
** 1 line lookahead and empty line at beginning
** of article.
*/
if (Headflag)
percent = ((Rlines-2)*100)/(artlin+Hlines);
else
percent = ((Rlines-Hlines-2)*100)/artlin;
sprintf (pstr,PAGE_MID,percent);
}
else
{
if (sn == NULL)
strcpy (pstr,PAGE_END);
else
strcpy (pstr,PAGE_NEXT);
}
c = getpgch(pstr,mid,from,reply,title,ngrp,flto,dist);
\f
/*
handle user input:
CAUTION!! return cases must close Fpread.
*/
switch (c)
{
case PG_NEXT:
fclose (Fpread);
return (0);
case PG_FLIP:
*pages = 1; /* fall through */
case PG_QUIT:
fclose (Fpread);
return (-1);
case PG_REWIND:
if (Headflag)
{
Rlines = 0;
rewind (Fpread);
}
else
{
fseek (Fpread,rew_pos,0);
Rlines = Hlines;
}
Lookahead = NULL;
lines = 2 - RECBIAS;
break;
case PG_SEARCH:
searcher(buf);
lines = 2 - RECBIAS;
lines += do_out(buf,L_allow-lines);
break;
case PG_WIND:
fseek (Fpread,0L,2);
lines = 2 - RECBIAS;
Lookahead = NULL;
break;
case PG_STEP:
if (Lookahead == NULL)
{
fclose (Fpread);
return (0);
}
lines = L_allow - 1;
break;
default:
if (Lookahead == NULL)
{
fclose (Fpread);
return (0);
}
lines = 2 - RECBIAS;
break;
}
}
}
\f
/*
gethead obtains subject, reply, message id, from, lines, newsgroup and
followup-to lines of article for later use in mailing replies and
posting followups, does not rewind, but leaves file at end of header
lines. Returns number of header lines.
*/
static gethead (mid, from, title, ngrp, flto, reply, dist, lin)
char *mid, *from, *title, *ngrp, *flto, *reply, *dist;
int *lin;
{
int count;
char buf [RECLEN], *index();
long pos,ftell();
#ifdef ADDRMUNGE
Newaddr = 1;
#endif
*lin = 0;
*dist = *mid = *from = *title = *ngrp = *flto = *reply = '\0';
/* for conditional is abnormal - expected exit is break */
for (count = 0; count < HDR_LINES && fgets(buf,RECLEN-1,Fpread) != NULL; ++count)
{
/* reset position and bail out at first non-header line */
if (index(buf,':') == NULL)
{
pos = ftell(Fpread);
pos -= strlen(buf);
fseek (Fpread,pos,0);
break;
}
#ifdef MAILSMART
if (strncmp(buf,RT_head,RTHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (reply,buf+RTHDLEN);
continue;
}
#else
if (strncmp(buf,P_head,PHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (reply,buf+PHDLEN);
continue;
}
#endif
if (strncmp(buf,DIS_head,DISHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (dist,buf+DISHDLEN);
continue;
}
if (strncmp(buf,M_head,MHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (mid,buf+MHDLEN);
continue;
}
if (strncmp(buf,F_head,FHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (from,buf+FHDLEN);
continue;
}
if (strncmp(buf,T_head,THDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (title,buf+THDLEN);
continue;
}
if (strncmp(buf,N_head,NHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (ngrp,buf+NHDLEN);
continue;
}
if (strncmp(buf,FT_head,FTHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
strcpy (flto,buf+FTHDLEN);
continue;
}
if (strncmp(buf,L_head,LHDLEN) == 0)
{
buf [strlen(buf)-1] = '\0';
*lin = atoi(buf+LHDLEN);
continue;
}
}
#ifdef MAILSMART
if (*reply == '\0')
strcpy(reply,from);
#endif
return (count);
}
\f
/*
getpgch prints prompt and gets command from user
handles "mail", "save" and "followup" internally
as well as flag resets.
*/
static char getpgch(prompt,mid,from,reply,title,ngrp,flto,dist)
char *prompt, *mid, *from, *reply, *title, *ngrp, *flto, *dist;
{
char c;
int ic;
term_set (ONREVERSE);
printf("%s\015",prompt);
term_set (OFFREVERSE);
while ((ic=getnoctl()) != EOF)
{
switch (c = Cxitor[ic])
{
case SETROT:
term_set (ZAP,0,PPR_MAX);
if (Rot == 0)
{
Rot = 13;
printf ("%s\n",Roton_msg);
}
else
{
Rot = 0;
printf ("%s\n",Rotoff_msg);
}
if (Lookahead != NULL && Rlines > Hlines)
rot_line(Lookahead);
break;
case HEADTOG:
term_set (ZAP,0,PPR_MAX);
if (Headflag)
{
Headflag = FALSE;
printf ("%s\n",Hdoff_msg);
}
else
{
Headflag = TRUE;
printf ("%s\n",Hdon_msg);
}
break;
case PG_HELP:
term_set (ZAP,0,PPR_MAX);
help_pg ();
break;
case PG_REPLY:
mail (reply,title,from);
break;
case PG_FOLLOW:
followup (mid,title,ngrp,flto,from,dist);
break;
case SAVE:
saver ();
break;
case PRINT:
printr ();
break;
default:
term_set (ZAP,0,PPR_MAX);
return (c);
}
term_set (ONREVERSE);
printf("%s\015",prompt);
term_set (OFFREVERSE);
}
term_set (ZAP,0,PPR_MAX);
return (c);
}
\f
/*
save article
Like the savestr routine, it "loses" some storage every time
the user specifies a new file, but this should not be significant
*/
static saver ()
{
char *fn,cmd[RECLEN],*str_store(),*rprompt();
tty_set (SAVEMODE);
sprintf (cmd,SAVFORM,Savefile);
fn = rprompt(cmd,cmd);
if (fn != NULL)
Savefile = str_store(fn);
if (*Savefile != '/' && *Savefile != '$')
sprintf (cmd,"cat %s >>%s/%s",Fname,Savedir,Savefile);
else
sprintf (cmd,"cat %s >>%s",Fname,Savefile);
system (cmd);
tty_set (RESTORE);
}
\f
/*
invoke editor on new temp file, mail using reply line,
possibly first allowing user to overide the reply (not INLETTER)
*/
static mail (p, t, f)
char *p, *t, *f;
{
char *new, fn[L_tmpnam], cmd [RECLEN+60], *rprompt ();
FILE *fp, *fopen();
tmpnam (fn);
if ((fp = fopen(fn,"w")) == NULL)
printex ("can't open %s\n",fn);
if ((new = index(p, '(')) != NULL)
*new = '\0'; /* a poor way of deleting comments */
#ifdef ADDRMUNGE
if (Newaddr)
{
Newaddr = 0;
ADDRMUNGE(p);
}
#endif
if (strncmp(t, FPFIX, FPFLEN) == 0)
t += FPFLEN; /* don't add multiple Re:s */
#ifdef INLETTER
fprintf (fp,"%s%s\n%s%s%s\n\n%s:\n", TO_head, p, T_head, FPFIX, t, f);
#else
fprintf (fp,"%s%s%s\n\n%s:\n", T_head, FPFIX, t, f);
#endif
edcopy (fp);
fclose (fp);
tty_set (SAVEMODE);
#ifndef INLETTER
sprintf (cmd,"ADDRESS: %s\nreturn to accept, or input new address: ",p);
if ((new = rprompt(cmd,cmd)) != NULL)
strcpy (p,new);
#endif
sprintf (cmd,"%s %s", Editor, fn);
chdir (Orgdir);
system (cmd);
cd_group ();
new = rprompt ("still want to mail it ? ",cmd);
if (new != NULL && (*new == 'y' || *new == 'Y'))
{
#ifndef INLETTER
sprintf (cmd,"%s '%s' <%s", Mailer, p, fn);
#else
sprintf (cmd,"%s <%s", Mailer, fn);
#endif
system (cmd);
printf ("given to mailer\n");
}
else
printf ("not mailed\n");
unlink (fn);
tty_set (RESTORE);
term_set (RESTART);
}
\f
/*
post a followup article, invoking editor for user after creating
new temp file. remove after posting. Hack in ".followup" if posting
newsgroup ends in ".general" - similar hack for preventing mod &
announce groups - should really be more thorough and parse the
whole string. User can change, anyway.
*/
static followup (m,t,n,ft,oa,dist)
char *m, *t, *n, *ft, *oa, *dist;
{
char fn[L_tmpnam], *new, cmd [RECLEN], *rprompt();
FILE *f, *fopen();
char *rindex();
if (*ft != '\0')
strcpy (cmd,ft);
else
strcpy (cmd,n);
new = rindex(cmd,'.');
if (new != NULL && strcmp(new,".general") == 0)
strcpy (new,".followup");
if ( strncmp(cmd, "mod.", 4) == 0 || strcmp(new, ".announce") == 0)
{
term_set (ONREVERSE);
printf("Cannot post a follow-up to \"%s\", reply with mail to moderator\007\n",
cmd);
term_set (OFFREVERSE);
return;
}
tmpnam (fn);
if ((f = fopen(fn,"w")) == NULL)
printex ("can't open %s\n",fn);
if (strncmp(t, FPFIX, FPFLEN) == 0)
t += FPFLEN; /* don't add multiple Re:s */
fprintf (f,"%s%s%s\n%s%s\n%s%s\n",T_head,FPFIX,t,N_head,cmd,R_head,m);
if (*dist != '\0')
fprintf(f,"%s%s\n",DIS_head,dist);
fprintf (f,"\nin article %s, %s says:\n",m,oa);
edcopy (f);
fclose (f);
tty_set (SAVEMODE);
sprintf (cmd,"%s %s", Editor, fn);
chdir (Orgdir);
system (cmd);
cd_group ();
new = rprompt("still want to post it ? ",cmd);
if (new != NULL && (*new == 'y' || *new == 'Y'))
{
sprintf (cmd,"%s <%s", Poster, fn);
system (cmd);
printf ("given to posting program\n");
save_article (fn);
}
else
printf ("not posted\n");
unlink (fn);
tty_set (RESTORE);
term_set (RESTART);
}
\f
/*
get user buffer, return whitespace delimited token
without using strtok(). buffer is allowed to overwrite
prompt string.
*/
static char *
rprompt(s,buf)
char *s,*buf;
{
printf("%s",s);
fgets (buf,RECLEN-1,stdin);
while (*buf == ' ' || *buf == '\t')
++buf;
if (*buf == '\n' || *buf == '\0')
return (NULL);
for (s = buf; *s != ' ' && *s != '\t' && *s != '\n' && *s != '\0'; ++s)
;
*s = '\0';
return (buf);
}
\f
/*
edcopy copies article to file which user is editting for
a reply or followup, so it may be referenced. It places
ED_MARK in the left hand margin.
*/
edcopy(fp)
FILE *fp;
{
long current;
char buf[RECLEN];
int i;
/* save position, rewind and skip over header lines */
current = ftell(Fpread);
rewind (Fpread);
for (i=0; i < HDR_LINES; ++i)
{
if (fgets(buf,RECLEN-1,Fpread) == NULL)
break;
if (strncmp(buf,L_head,LHDLEN) == 0)
break;
}
/* if line already begins with ED_MARK, forget about the space */
while (fgets(buf,RECLEN-1,Fpread) != NULL)
{
if (buf[0] == ED_MARK)
fprintf(fp,"%c%s",ED_MARK,buf);
else
fprintf(fp,"%c %s",ED_MARK,buf);
}
/* restore position */
fseek(Fpread,current,0);
}
\f
/*
help menus
*/
static help_pg()
{
h_print (Cxrtoi[PG_NEXT],HPG_NEXT);
h_print (Cxrtoi[PG_QUIT],HPG_QUIT);
h_print (Cxrtoi[PG_FLIP],HPG_FLIP);
h_print (Cxrtoi[PG_REWIND],HPG_REWIND);
h_print (Cxrtoi[PG_WIND],HPG_WIND);
h_print (Cxrtoi[PG_SEARCH],HPG_SEARCH);
h_print (Cxrtoi[PG_STEP],HPG_STEP);
h_print (Cxrtoi[PG_REPLY],HPG_REPLY);
h_print (Cxrtoi[PG_FOLLOW],HPG_FOLLOW);
h_print (Cxrtoi[SAVE],HPG_SAVE);
h_print (Cxrtoi[PRINT],HPG_PRINT);
h_print (Cxrtoi[SETROT],HPG_ROT);
h_print (Cxrtoi[HEADTOG],HPG_HEAD);
h_print (Cxrtoi[PG_HELP],HPG_HELP);
printf ("%s\n",HPG_DEF);
}
rot_line (s)
unsigned char *s;
{
for ( ; *s != '\0'; ++s)
{
if (*s >= 'A' && *s <= 'Z')
{
*s += Rot;
if (*s > 'Z')
*s -= 26;
continue;
}
if (*s >= 'a' && *s <= 'z')
{
*s += Rot;
if (*s > 'z')
*s -= 26;
}
}
}
\f
/*
** output record. folds record to terminal width on word boundaries,
** returning number of lines output. If limit is reached, remainder
** of buffer waiting to be output is returned. Processes several
** special characters:
** form-feed - return "lim" lines so we stop on that line
** tabs - counts "expanded" width
** backspace - assumes they work, -1 width unless in first col.
** bell - pass through with zero width
** newline - end of record.
** del - turns into '_'
** other control - 'A' - 1 added ('01' = ctl-A). Makes escape = "[".
** (prevents "letter bombs" containing inappropriate control
** sequences for the terminal).
**
** Sets Lookahead pointer to remainder of line or NULL.
*/
static do_out(s,lim)
char *s;
int lim;
{
int len,i;
char cs,*word,*start;
Lookahead = NULL;
if (s == NULL)
return(0);
len = 0;
start = word = s;
/*
** NOTE: "normal" return is buried inside switch, at newline
** ending record
*/
for (i=0; i < lim; ++i)
{
for ( ; len < C_allow; ++s)
{
switch (*s)
{
case '\n':
*s = '\0'; /* fall through */
case '\0':
printf("%s\n",start);
return(i+1);
case '\t':
len = ((len/PERTAB)+1)*PERTAB;
word = s;
break;
case '\b':
if (len > 0)
--len;
break;
case '\014':
*s = ' ';
i = lim-1; /* fall through */
case ' ':
word = s+1;
++len;
break;
case '\177':
*s = '_';
++len;
break;
default:
if (*s < ' ')
*s += 'A' - 1;
++len; /* fall through */
case '\07':
break;
}
}
cs = *s;
*s = '\0';
if ((len = strlen(word)) < BACKTRACK)
{
*s = cs;
s = word;
cs = *s;
*s = '\0';
}
else
len = 0;
printf("%s\n",start);
start = s;
*s = cs;
}
Lookahead = start;
return(lim);
}
\f
save_article(tempfname)
char *tempfname;
{
FILE *in, *out;
int c;
time_t timenow, time();
char *today, *ctime();
if ((in = fopen(tempfname, "r")) == NULL)
return;
if ((out = fopen(Ccfile, "a")) == NULL)
{
fclose(in);
return;
}
timenow = time((time_t)0);
today = ctime(&timenow);
fprintf(out,"From vn %s",today);
while ((c=getc(in)) != EOF)
putc(c, out);
putc('\n', out);
fclose(in);
fclose(out);
printf ("a copy has been saved in %s\n", Ccfile);
}
\f
/*
send article to printer
*/
static printr ()
{
char cmd[RECLEN];
tty_set (SAVEMODE);
printf("Sent to printer\n");
sprintf (cmd,"%s %s 2>/dev/null",Printer,Fname);
system (cmd);
tty_set (RESTORE);
}
\f
/*
search article for specified search pattern, returning the line on which
it is found in buf, a null buffer otherwise. The input file will
be positioned either after the line on which the pattern is
found, or unaaltered if match fails.
*/
searcher (buf)
char *buf;
{
static char searchstr[RECLEN] = "";
char lasave[RECLEN];
char *s, *reg, *rprompt(), *regcmp(), *regex();
long current;
int orlines;
/* save position, then request search pattern */
current = ftell(Fpread);
orlines = Rlines;
tty_set (SAVEMODE);
sprintf (lasave,SEARCHFORM,searchstr);
s = rprompt(lasave,lasave);
tty_set (RESTORE);
if (s != NULL)
strcpy(searchstr, lasave);
/* Now compile the search string */
if(( reg = regcmp(searchstr, (char *)0)) == NULL) {
printf("Invalid search string \"%s\"\n", searchstr);
*buf = '\0';
return;
}
/* try lookahead buffer first */
if (Lookahead != NULL && regex(reg,Lookahead) != NULL)
{
strcpy(buf,Lookahead);
regfree(reg);
return;
}
/* Lookahead can point into buf */
if (Lookahead != NULL)
strcpy(lasave,Lookahead);
/* now start reading lines, rotating if necessary and do search */
while (fgets(buf,RECLEN-1,Fpread) != NULL)
{
if (Rot != 0 && Rlines >= Hlines)
rot_line(buf);
++Rlines;
if( regex(reg, buf) != NULL ){ /* Got it */
term_set (ONREVERSE);
printf("\n\tSkipping ....\n\n");
term_set (OFFREVERSE);
regfree(reg);
return;
}
}
/* no dice, so restore position */
regfree(reg);
term_set (ONREVERSE);
printf("Cannot find string \"%s\" in remainder of article\007\n",
searchstr);
term_set (OFFREVERSE);
fseek(Fpread,current,0);
Rlines = orlines;
if (Lookahead != NULL)
strcpy(buf,lasave);
else
*buf = '\0';
return(0.0);
}