home *** CD-ROM | disk | FTP | other *** search
- /*
- ** 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 "tune.h"
- #include "head.h"
- #include "reader.h"
- #include "brk.h"
- #include "node.h"
- #include "page.h"
- #include "vn.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 int More;
- extern char *No_msg;
- extern char *Roton_msg;
- extern char *Rotoff_msg;
- extern char *Hdon_msg;
- extern char *Hdoff_msg;
-
- extern PAGE Page;
-
- extern int (*Massage)();
- extern int (*Postfunc)();
- extern int (*Mailfunc)();
-
- extern char Cxrtoi[], Cxitor[];
-
- static FILE *Fpread;
- static char *Fname;
- static char *Lookahead;
- static int Rlines;
- static int Hlines;
- static long Rew_pos;
-
- /*
- readfile presents article:
- fn = name of article.
- sn - name of next article, NULL if last.
- pages - pages to advance on return, if applicable
- returns 0 for "continue", <0 for "quit"
-
- calls sig_set(BRK_READ / BRK_RFIN) around reading article.
- */
- readfile (fn,sn,pages)
- char *fn;
- char *sn;
- int *pages;
- {
- ARTHEADER hdr;
- FILE *fopen();
- int lines,percent;
- int i;
- int top, bottom;
- int step;
- char c, buf[RECLEN];
- char lasave[RECLEN];
- char pstr[24], dgname[48];
- char getpgch(), *index(), *digest_extract(), *tgetstr();
- char *any;
- FILE *vns_aopen();
- long ftell();
-
- Fname = fn;
- *pages = 0;
- step = FALSE; /* Boolean; to indicate user input a <RET> (PG_STEP) */
-
- term_set(ERASE);
- sig_set(BRK_READ,&Fpread);
-
- any = "any key to continue .... ";
-
- if (Digest)
- {
- lines = atoi(Fname);
- if ((Fname = digest_extract(dgname,lines,&hdr,&Rew_pos)) == NULL)
- {
- rerrmsg("couldn't extract article %d from digest",lines);
- printf(any);
- getnoctl();
- sig_set(BRK_RFIN);
- return (0);
- }
- if ((Fpread = fopen(Fname,"r")) == NULL)
- {
- rerrmsg("couldn't open %s",Fname);
- printf(any);
- getnoctl();
- sig_set(BRK_RFIN);
- return (0);
- }
- fseek(Fpread,Rew_pos,0);
- }
- else
- {
- if ((Fpread = vns_aopen(atoi(Fname),&hdr)) == NULL)
- {
- rerrmsg("couldn't open article %s",Fname);
- printf(any);
- getnoctl();
- sig_set(BRK_RFIN);
- return (0);
- }
- Rew_pos = ftell(Fpread);
- }
-
- Hlines = hdr.hlines;
- printf (ANFORM,Fname,Cxrtoi[PG_HELP]);
-
- lines = 0;
-
- if (Headflag)
- {
- rewind(Fpread);
- Rlines = 0;
- }
- else
- {
- /* use do_out to guard against control sequences */
- Rlines = Hlines;
- for (i=0; i < hdr.show_num; ++i)
- lines += do_out((hdr.show)[i],L_allow-lines);
- lines += do_out("",L_allow-lines);
- }
-
- /* will return out of outer while loop */
- Lookahead = NULL;
- while (1)
- {
- /*
- ** lines counts folded lines from do_out.
- ** globals Hlines and Rlines refer to records.
- ** If Lookahead is null after this loop, we've
- ** hit EOF.
- */
- if (Lookahead != NULL && More && !step)
- {
- char *looktmp;
-
- /*
- ** Save Lookahead because `do_out' nukes it.
- ** Perhaps we could just use `printf' here
- ** but for now we'll play it safe. - GKE 12/26/87
- */
- looktmp = Lookahead;
- term_set(ERASE);
- /*
- ** The following presents the last line of the
- ** previous page in reversed video, as the first line
- ** of the current page, `rn'-stylee. Since `rn'
- ** uses an option to enable/disable this, remove the
- ** two `term_set's if not to your liking.
- */
- term_set(ONREVERSE);
- do_out(lasave,1);
- term_set(OFFREVERSE);
- Lookahead = looktmp;
- }
- 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;
- if (More)
- strcpy(lasave,Lookahead);
- lines += do_out(Lookahead,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)
- {
- top = (Rlines-2)*100;
- bottom = hdr.lines + Hlines;
- }
- else
- {
- top = (Rlines-Hlines-2)*100;
- bottom = hdr.lines;
- }
- /*
- ** protect against division by zero -
- ** shouldn't actually come up zero at this
- ** point if vns_aopen is sane. 999 will let user
- ** know the percentage is obviously wrong.
- */
- if (bottom != 0)
- percent = top/bottom;
- else
- percent = 999;
- sprintf (pstr,PAGE_MID,percent);
- }
- else
- {
- if (sn == NULL)
- strcpy (pstr,PAGE_END);
- else
- strcpy (pstr,PAGE_NEXT);
- }
- c = getpgch(pstr,&hdr);
-
- /*
- handle user input:
- CAUTION!! return cases must close Fpread.
- */
- step = FALSE;
- switch (c)
- {
- case PG_NEXT:
- if (Digest)
- {
- fclose (Fpread);
- unlink (Fname);
- }
- else
- vns_aclose (Fpread);
- sig_set(BRK_RFIN);
- return (0);
- case PG_FLIP:
- *pages = 1; /* fall through */
- case PG_QUIT:
- if (Digest)
- {
- fclose (Fpread);
- unlink (Fname);
- }
- else
- vns_aclose (Fpread);
- sig_set(BRK_RFIN);
- 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)
- {
- if (Digest)
- {
- fclose (Fpread);
- unlink (Fname);
- }
- else
- vns_aclose (Fpread);
- sig_set(BRK_RFIN);
- return (0);
- }
- lines = L_allow - 1;
- step = TRUE; /* Temporarily disable paging */
- break;
- default:
- if (Lookahead == NULL)
- {
- if (Digest)
- {
- fclose (Fpread);
- unlink (Fname);
- }
- else
- vns_aclose (Fpread);
- sig_set(BRK_RFIN);
- return (0);
- }
- lines = 2 - RECBIAS;
- break;
- }
- }
- }
-
- /*
- getpgch prints prompt and gets command from user
- handles "mail", "save" and "followup" internally
- as well as flag resets.
- */
- static char
- getpgch(prompt,hdr)
- char *prompt;
- ARTHEADER *hdr;
- {
- 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_rd ();
- break;
- case PG_REPLY:
- mail (hdr);
- break;
- case PG_FOLLOW:
- followup (hdr);
- break;
- case SAVE:
- saver ();
- break;
- case PRINT:
- printr ();
- break;
- default:
- term_set (ZAP,0,PPR_MAX);
- return (c);
- }
-
- term_set (ONREVERSE);
- printf("%s\015",prompt);
- term_set (OFFREVERSE);
- }
- term_set (ZAP,0,PPR_MAX);
- return (c);
- }
-
- /*
- save article. Like its "session" counterpart, this loses storage
- if the user specifies a new file, but it should not be significant
- */
- static
- saver ()
- {
- char buf[RECLEN],msg[RECLEN],*str_store();
-
- user_str (buf,"save file ? ",0,Savefile);
- if (buf[0] != '\0' && buf[0] != '|')
- Savefile = str_store(buf);
- if (save_art(Fname,buf,msg) != 0)
- rerrmsg(msg);
- else
- printf("%s\n",msg);
- }
-
- /*
- invoke editor on new temp file, mail using reply line,
- possibly first allowing user to overide the reply (not INLETTER)
- */
- static
- mail (hdr)
- ARTHEADER *hdr;
- {
- char *new, fn[L_tmpnam], cmd [RECLEN+60], *rprompt ();
- int i;
- FILE *fp, *fopen();
-
- if (Massage != NULL)
- (*Massage)(hdr);
-
- if (hdr->mail_err != NULL)
- {
- rerrmsg(hdr->mail_err);
- return;
- }
-
- tmpnam (fn);
- if ((fp = fopen(fn,"w")) == NULL)
- {
- rerrmsg("can't open temp file, %s",fn);
- return;
- }
-
- for (i = 0; i < hdr->mail_num; ++i)
- fprintf(fp,"%s\n",(hdr->mail)[i]);
- fprintf(fp,"\n");
-
- fprintf(fp,"%s:\n",hdr->from);
- edcopy (fp);
- fclose (fp);
-
- tty_set (SAVEMODE);
-
- sprintf (cmd,"%s %s", Editor, fn);
- chdir (Orgdir);
- system (cmd);
- vns_gset (Page.h.name);
-
- new = rprompt ("still want to mail it ? ",cmd);
- if (new != NULL && (*new == 'y' || *new == 'Y'))
- {
- if (Mailfunc == NULL)
- {
- sprintf (cmd, hdr->mailcmd, fn);
- system (cmd);
- printf ("given to mailer\n");
- }
- else
- (*Mailfunc)(hdr,fn);
- }
- else
- printf ("not mailed\n");
-
- unlink (fn);
-
- tty_set (RESTORE);
- term_set (RESTART);
- }
-
- /*
- post a followup article, invoking editor for user after creating
- new temp file. remove after posting.
- */
- static
- followup (hdr)
- ARTHEADER *hdr;
- {
- char fn[L_tmpnam], *new, cmd [RECLEN], *rprompt();
- int i;
- FILE *f, *fopen();
- char *rindex();
-
- if (hdr->post_err != NULL)
- {
- rerrmsg(hdr->post_err);
- return;
- }
-
- tmpnam (fn);
- if ((f = fopen(fn,"w")) == NULL)
- {
- rerrmsg("can't open temp file, %s",fn);
- return;
- }
-
- for (i=0; i < hdr->post_num; ++i)
- fprintf(f,"%s\n",(hdr->post)[i]);
- fprintf(f,"\n");
-
- fprintf(f,"From article %s, by %s:\n", hdr->artid, hdr->from);
-
- edcopy (f);
- fclose (f);
- tty_set (SAVEMODE);
- sprintf (cmd,"%s %s", Editor, fn);
- chdir (Orgdir);
- system (cmd);
- vns_gset (Page.h.name);
-
- new = rprompt("still want to post it ? ",cmd);
- if (new != NULL && (*new == 'y' || *new == 'Y'))
- {
- if (Postfunc == NULL)
- {
- sprintf (cmd, hdr->postcmd, fn);
- system (cmd);
- printf ("given to posting program\n");
- }
- else
- (*Postfunc)(hdr,fn);
- save_article (fn);
- }
- else
- printf ("not posted\n");
- unlink (fn);
- tty_set (RESTORE);
- term_set (RESTART);
- }
-
- /*
- get user buffer, return whitespace delimited token
- buffer is allowed to overwrite prompt string. This routine
- should only be used when the terminal is in cooked mode.
- In raw, use user_str().
- */
- static char *
- rprompt(s,buf)
- char *s,*buf;
- {
- char *strtok();
-
- printf("%s",s);
- fgets (buf,RECLEN-1,stdin);
- return (strtok(buf," \t\n"));
- }
-
- /*
- 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.
- */
- static
- edcopy(fp)
- FILE *fp;
- {
- long current;
- char buf[RECLEN];
-
- /* save position, and seek to top of article */
- current = ftell(Fpread);
- fseek (Fpread,Rew_pos,0);
-
- /* 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);
- }
-
- static
- rot_line (s)
- unsigned char *s;
- {
- for ( ; *s != '\0'; ++s)
- {
- if (*s >= 'A' && *s <= 'Z')
- {
- *s += Rot;
- if (*s > 'Z')
- *s -= 26;
- continue;
- }
- if (*s >= 'a' && *s <= 'z')
- {
- *s += Rot;
- if (*s > 'z')
- *s -= 26;
- }
- }
- }
-
- /*
- ** output record. folds record to terminal width on word boundaries,
- ** returning number of lines output. If limit is reached, remainder
- ** of buffer waiting to be output is returned. Processes several
- ** special characters:
- ** form-feed - return "lim" lines so we stop on that line
- ** tabs - counts "expanded" width
- ** backspace - assumes they work, -1 width unless in first col.
- ** bell - pass through with zero width
- ** newline - end of record.
- ** del - turns into '_'
- ** other control - 'A' - 1 added ('01' = ctl-A). Makes escape = "[".
- ** (prevents "letter bombs" containing inappropriate control
- ** sequences for the terminal).
- **
- ** Sets Lookahead pointer to remainder of line or NULL.
- */
- static
- do_out(s,lim)
- char *s;
- int lim;
- {
- int len,i;
- char cs,*word,*start;
-
- Lookahead = NULL;
- if (s == NULL)
- return(0);
- len = 0;
- start = word = s;
-
- /*
- ** NOTE: "normal" return is buried inside switch, at newline
- ** ending record
- */
- for (i=0; i < lim; ++i)
- {
- for ( ; len < C_allow; ++s)
- {
- switch (*s)
- {
- case '\n':
- *s = '\0'; /* fall through */
- case '\0':
- printf("%s\n",start);
- return(i+1);
- case '\t':
- len = ((len/PERTAB)+1)*PERTAB;
- word = s;
- break;
- case '\b':
- if (len > 0)
- --len;
- break;
- case '\014':
- *s = ' ';
- i = lim-1; /* fall through */
- case ' ':
- word = s+1;
- ++len;
- break;
- case '\177':
- *s = '_';
- ++len;
- break;
- default:
- if (*s < ' ')
- *s += 'A' - 1;
- ++len; /* fall through */
- case '\07':
- break;
- }
- }
- cs = *s;
- *s = '\0';
- if ((len = strlen(word)) < BACKTRACK)
- {
- *s = cs;
- s = word;
- cs = *s;
- *s = '\0';
- }
- else
- len = 0;
- printf("%s\n",start);
- start = s;
- *s = cs;
- }
- Lookahead = start;
- return(lim);
- }
-
- static
- save_article(tempfname)
- char *tempfname;
- {
- FILE *in, *out;
- int c;
- time_t timenow, time();
- char *today, *ctime();
-
- if ((in = fopen(tempfname, "r")) == NULL)
- return;
- if ((out = fopen(Ccfile, "a")) == NULL)
- {
- fclose(in);
- return;
- }
- timenow = time((time_t)0);
- today = ctime(&timenow);
- fprintf(out,"From vn %s",today);
- while ((c=getc(in)) != EOF)
- putc(c, out);
- putc('\n', out);
- fclose(in);
- fclose(out);
- printf ("a copy has been saved in %s\n", Ccfile);
- }
-
- /*
- send article to printer
- */
- static
- printr ()
- {
- char cmd[RECLEN];
- char fn[L_tmpnam];
- long savepos;
- FILE *f;
-
- tmpnam (fn);
- if ((f = fopen(fn,"w")) == NULL)
- {
- rerrmsg("can't open temp file, %s",fn);
- return;
- }
-
- savepos = ftell(Fpread);
- rewind(Fpread);
- while (fgets(cmd,RECLEN-1,Fpread) != NULL)
- fputs(cmd,f);
- fclose(f);
- fseek(Fpread,savepos,0);
-
- tty_set (SAVEMODE);
- printf("Sent to printer\n");
- sprintf (cmd,"%s %s 2>/dev/null",Printer,fn);
- system (cmd);
- tty_set (RESTORE);
- unlink (fn);
- }
-
- /*
- 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.
- */
- static
- searcher (buf)
- char *buf;
- {
- static char searchstr[RECLEN] = "";
- char lasave[RECLEN];
- char *reg, *regcmp(), *regex();
- long current;
- int orlines;
-
- /* save position, then request search pattern */
- current = ftell(Fpread);
- orlines = Rlines;
-
- sprintf (lasave,SEARCHFORM,searchstr);
- user_str (searchstr,lasave,0,searchstr);
-
- /* Now compile the search string */
- if(( reg = regcmp(searchstr, (char *)0)) == NULL) {
- rerrmsg("Invalid search string \"%s\"", 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 */
- rerrmsg("\n\tSkipping ....\n\n");
- regfree(reg);
- return;
- }
- }
-
- /* no dice, so restore position */
- regfree(reg);
- rerrmsg("Cannot find string \"%s\" in remainder of article", searchstr);
- fseek(Fpread,current,0);
- Rlines = orlines;
- if (Lookahead != NULL)
- strcpy(buf,lasave);
- else
- *buf = '\0';
- }
-
- /*
- ** print a reverse video error message while reading an article.
- */
- static
- rerrmsg(s,a,b,c,d,e)
- char *s;
- long a,b,c,d,e;
- {
- term_set (ONREVERSE);
- printf("\n ");
- printf(s,a,b,c,d,e);
- printf(" \07\n");
- term_set (OFFREVERSE);
- }
-