[net.sources] built-in pager for readnews

borman@stolaf.UUCP (06/28/83)

I've had many requests for the mods to build a pager into readnews,
so here it is. (Note that in pager.c there are ifdefs for MOD_NTTY.
These are for our local ttydriver, which has an ioctl for resetting
^O. This allows people to ^O an article, and wonder of wonders, it gets
reset just in time to print the next header... very handy. If you
have such an ioctl on your system, just change the ioctl's as needed
and define MOD_NTTY.(there are many other places that this can be
put in, if you'd like a list of all the places, drop me a line))

These are the diffs to implement the built-in pager into readnews.
The files modified are digest.c, readr.c and rfuncs.c.  You also
have to change PAGE, the default pager listed in defs.h, to "My Own".
(If you don't like that, pick something unique and change all the
appropriate references to it) Besides implementing the pager, this
also makes the 'w' and 's' different, in that 's' puts on the header
when saving the article, and 'w' doesn't.

The main thrust of writing this pager has been to keep anything from
scrolling off the screen before you get a chance to read it. It does
an awfully good job at it. It knows if a new newsgroup is following,
and allows for the longer header. It looks at line lengths for long
lines that would cause the line count to be incorrect.

The biggest kludge about this is how it is implemented with the 'D'
option for decrypting things.

Things to note:
	The pager gives you the options of using +'s and -'s to look
	at various pages of the article, BUT... you can only skip around
	on pages that you have already seen. (e.g, you can's skip the
	first 5 pages by giving the command +++++, but if you've read the
	first 5 pages, you can use ---- to skip back 4 pages, and then
	+++ to skip forward 3 pages, etc.)  This could probably be changed,
	but it would involve looking at the whole article before presenting
	it, which it doesn't do right now.

	You can probably disable the ':' "feature" if you implement this pager.

It should be fairly easy to install, just make the following changes and
add pager.o to the ROBJECTS line in the makefile. If you have any questions,
comments or improvements, I'd be glad to see them.
				-Dave Borman, St. Olaf College
				{ihnp4, harpo}!stolaf!borman



*** ../vanilla/src/digest.c	Wed May 25 12:58:25 1983
--- digest.c	Fri Jun  3 16:38:37 1983
***************
*** 324,329
  	FILE		*pfp, *popen();
  
  	if (art && arts[art].a_blen > 23-arts[art+1].a_hlen && *PAGER) {
  		if (!index(PAGER, FMETA)) {
  			if ((pfp = popen(PAGER, "w"))==NULL)
  				(void) dprinta(art, ifp, ofp);

--- 327,338 -----
  	FILE		*pfp, *popen();
  
  	if (art && arts[art].a_blen > 23-arts[art+1].a_hlen && *PAGER) {
+ #ifdef	MOD_PAGE
+ 		if (strncmp(PAGER, "My Own") == 0) {
+ 			fseek(ifp, arts[art].a_bod, 0);
+ 			pprint(ifp, ofp, NULL, arts[art+1].a_hdr, art);
+ 		} else
+ #endif	MOD_PAGE
  		if (!index(PAGER, FMETA)) {
  			if ((pfp = popen(PAGER, "w"))==NULL)
  				(void) dprinta(art, ifp, ofp);
--- readr.c	Mon Jun 27 19:30:22 1983
***************
*** 204,209
  		if (!bptr[-1] && rfq)
  			return;
  		readmode = NEXT;
  		showtail(fp);
  		clear(bit);
  		saveart;

--- 210,219 -----
  		if (!bptr[-1] && rfq)
  			return;
  		readmode = NEXT;
+ #ifdef	MOD_PAGE
+ 		if (showtail(fp))
+ 			return;
+ #else
  		showtail(fp);
  #endif	MOD_PAGE
  		clear(bit);
***************
*** 205,210
  			return;
  		readmode = NEXT;
  		showtail(fp);
  		clear(bit);
  		saveart;
  		nextbit();

--- 215,221 -----
  			return;
  #else
  		showtail(fp);
+ #endif	MOD_PAGE
  		clear(bit);
  		saveart;
  		nextbit();
***************
*** 273,278
  	case 'w':
  		{
  		char *grn = groupdir;
  		tfilename = filename;
  		if (*bptr == '-') {
  			bptr++;

--- 293,301 -----
  	case 'w':
  		{
  		char *grn = groupdir;
+ #ifdef	MOD_PAGE
+ 		i = bptr[-1] == 's';
+ #endif	MOD_PAGE
  		tfilename = filename;
  		if (*bptr == '-') {
  			bptr++;
***************
*** 306,311
  			else
  				strcat(bptr, "Articles");
  		}
  		fwait(fsubr(save, tfilename, bptr));
  		}
  		break;

--- 329,337 -----
  			else
  				strcat(bptr, "Articles");
  		}
+ #ifdef	MOD_PAGE
+ 		rsave(tfilename, bptr, i);
+ #else
  		fwait(fsubr(save, tfilename, bptr));
  #endif	MOD_PAGE
  		}
***************
*** 307,312
  				strcat(bptr, "Articles");
  		}
  		fwait(fsubr(save, tfilename, bptr));
  		}
  		break;
  

--- 333,339 -----
  		rsave(tfilename, bptr, i);
  #else
  		fwait(fsubr(save, tfilename, bptr));
+ #endif	MOD_PAGE
  		}
  		break;
  
***************
*** 895,900
  	char	temp[100];
  	char	*pp = bptr;
  	FILE	*pfp, *popen();
  
  	fprintf(stderr, "Caesar decoding:\n");
  	strcpy(temp, CAESAR);

--- 956,966 -----
  	char	temp[100];
  	char	*pp = bptr;
  	FILE	*pfp, *popen();
+ #ifdef	MOD_PAGE
+ 	FILE	*pifp, *fdopen();
+ 	register int ttt;
+ 	int	p[2];
+ #endif	MOD_PAGE
  
  	fprintf(stderr, "Caesar decoding:\n");
  	strcpy(temp, CAESAR);
***************
*** 902,907
  		strcat(temp, " ");
  		strcat(temp, bptr);
  	}
  	if (NLINES(h, fp) > LNCNT && *PAGER) {
  		strcat(temp, " | ");
  		strcat(temp, PAGER);

--- 968,1008 -----
  		strcat(temp, " ");
  		strcat(temp, bptr);
  	}
+ #ifdef	MOD_PAGE
+ 	if (NLINES(h, fp) > (((rflag && bit < 1) || (bit > ngsize))? 12 : 17)
+ 		&& *PAGER) {
+ 		if (strcmp(PAGER, "My Own") == 0) {
+ 			pipe(p);
+ 			ttt = dup(1);
+ 			dup2(p[1], 1);
+ 			close(p[1]);
+ 			pifp = fdopen(p[0], "r");
+ 			pfp = popen(temp, "w");
+ 			dup2(ttt, 1);
+ 			close(ttt);
+ 			/* fsubr only takes function with 2 args, */
+ 			/* so and we need 3... */
+ 			while ((ttt = fork()) == -1)
+ 				sleep(1);
+ 			if (ttt == 0) {
+ 				tprint(fp, pfp, FALSE);
+ 				exit(0);
+ 			}
+ 			pclose(pfp);
+ 			pprint(pifp, ofp, &h, 0L, 0);
+ 			fclose(pifp);
+ 			fwait(ttt);
+ 		} else {
+ 			strcat(temp, " | ");
+ 			strcat(temp, PAGER);
+ 			pfp = popen(temp, "w");
+ 			tprint(fp, pfp, FALSE);
+ 		}
+ 	} else {
+ 		pfp = popen(temp, "w");
+ 		tprint(fp, pfp, FALSE);
+ 	}
+ #else	MOD_PAGE
  	if (NLINES(h, fp) > LNCNT && *PAGER) {
  		strcat(temp, " | ");
  		strcat(temp, PAGER);
***************
*** 908,913
  	}
  	pfp = popen(temp, "w");
  	tprint(fp, pfp, FALSE);
  	itsbeenseen(h.ident);
  	fclose(fp);
  	fp = NULL;

--- 1009,1015 -----
  	}
  	pfp = popen(temp, "w");
  	tprint(fp, pfp, FALSE);
+ #endif	MOD_PAGE
  	itsbeenseen(h.ident);
  	fclose(fp);
  	fp = NULL;
***************
*** 923,928
  FILE *fd;
  {
  	if (fd == NULL)
  		return;
  
  	if (dgest) {

--- 1025,1033 -----
  FILE *fd;
  {
  	if (fd == NULL)
+ #ifdef	MOD_PAGE
+ 		return(0);
+ #else
  		return;
  #endif	MOD_PAGE
  
***************
*** 924,929
  {
  	if (fd == NULL)
  		return;
  
  	if (dgest) {
  		digest(fd, ofp, &h);

--- 1029,1035 -----
  		return(0);
  #else
  		return;
+ #endif	MOD_PAGE
  
  	if (dgest) {
  		digest(fd, ofp, &h);
***************
*** 930,935
  	} else if (!lflag && !pflag && !eflag) {
  #ifdef PAGE
  		/* Filter the tail of long messages through PAGER. */
  		if (NLINES(h, fd) > LNCNT && *PAGER) {
  			if (!index(PAGER, FMETA)) {
  				FILE *pfp, *popen();

--- 1036,1042 -----
  	} else if (!lflag && !pflag && !eflag) {
  #ifdef PAGE
  		/* Filter the tail of long messages through PAGER. */
+ #ifndef	MOD_PAGE
  		if (NLINES(h, fd) > LNCNT && *PAGER) {
  #else	MOD_PAGE
  		if (NLINES(h, fd) >
***************
*** 931,936
  #ifdef PAGE
  		/* Filter the tail of long messages through PAGER. */
  		if (NLINES(h, fd) > LNCNT && *PAGER) {
  			if (!index(PAGER, FMETA)) {
  				FILE *pfp, *popen();
  				int	cnt;

--- 1038,1049 -----
  		/* Filter the tail of long messages through PAGER. */
  #ifndef	MOD_PAGE
  		if (NLINES(h, fd) > LNCNT && *PAGER) {
+ #else	MOD_PAGE
+ 		if (NLINES(h, fd) >
+ 		 (((rflag && bit < 1) || (bit > ngsize))? LNCNT - 5 : LNCNT)
+ 		 && *PAGER) {
+ 		    if ( strcmp(PAGER, "My Own") != 0 ) {
+ #endif	MOD_PAGE
  			if (!index(PAGER, FMETA)) {
  				FILE *pfp, *popen();
  				int	cnt;
***************
*** 951,958
  			else
  				pout(ofp);
  			holdup = TRUE;
  		}
  		else
  #endif

--- 1064,1077 -----
  			else
  				pout(ofp);
  			holdup = TRUE;
+ #ifdef	MOD_PAGE
+ 		    } else
+ 			/* PAGER == "My Own" */
+ 			if (pprint(fd, ofp, &h, 0L, 0)) {
+ 				fclose(fd);
+ 				return(1);
+ 			}
+ #endif	MOD_PAGE
  		}
  		else
  #endif
***************
*** 959,964
  			tprint(fd, ofp, FALSE), itsbeenseen(h.ident);
  	}
  	fclose(fd);
  }
  
  /*

--- 1078,1086 -----
  			tprint(fd, ofp, FALSE), itsbeenseen(h.ident);
  	}
  	fclose(fd);
+ #ifdef	MOD_PAGE
+ 	return(0);
+ #endif	MOD_PAGE
  }
  
  /*
*** ../vanilla/src/rfuncs2.c	Wed May 25 12:58:00 1983
--- rfuncs2.c	Sun Jun  5 18:20:48 1983
***************
*** 64,69
  }
  
  
  /*
   * Save the news item in the user's file.
   * Fri Mar 12 20:04:43 EST 1982: (ittvax!swatt)

--- 64,70 -----
  }
  
  
+ #ifndef	MOD_PAGE
  /*
   * Save the news item in the user's file.
   * Fri Mar 12 20:04:43 EST 1982: (ittvax!swatt)
***************
*** 138,143
  	if (!isprogram)
  		printf("%s: %s\n", to, isnew ? "New file" : "Appended");
  }
  
  
  /*

--- 139,145 -----
  	if (!isprogram)
  		printf("%s: %s\n", to, isnew ? "New file" : "Appended");
  }
+ #endif	MOD_PAGE
  
  
  /*



****** THE REST OF THIS ARTICLE IS THE FILE PAGER.C *******
#ifdef	MOD_PAGE
/*
 * This pager was originally written by Alan Hastings while system manager
 * at St. Olaf College, for version 2.3 of Bnews. It has been enhanced and
 * updated to run under successive versions of Bnews (currently 2.10) by
 * Dave Borman at St. Olaf.
 *
 */

#include "rparams.h"

#define	TABSIZE	8
#define	COL	80

pprint(ifp, ofp, h, end, art)
register FILE *ifp, *ofp;
struct hbuf *h;
long end;
int art;
{
	char ppbuf[BUFLEN], resbuf[BUFLEN];
	char *respons;
	long pgoffs[BUFLEN];
	long begin;
	int curpg = 0,
	    lastpg = 0;
	register int i = 0;
	int	isnew;

	begin = pgoffs[0] = ftell(ifp);
	pgoffs[1] = 0L;
	while (fgets(ppbuf, sizeof(ppbuf), ifp)) {
	    if (end && (ftell(ifp) > end))
		break;
	    if (sigtrap) {
		qfflush(ofp);
		break;
	    }
	    i += num_lines(ppbuf);
	    if (i > 23) {
		i = 1;
		if (++curpg > lastpg)
			lastpg = curpg;
		if (pgoffs[lastpg]==0L) {
			pgoffs[lastpg] = ftell(ifp);
			pgoffs[lastpg+1] = 0L;
		}
getmore:
#ifdef	MOD_NTTY
		fflush(ofp);
		ioctl(fileno(ofp), TIOXRESO, 0);
#endif	MOD_NTTY
		fprintf(ofp, "Page %d -- Press return for more-- [ynq] ", curpg);
		fflush(ofp);
		fgets(resbuf, sizeof(resbuf), stdin);
		respons = resbuf;
		nstrip(respons);
		while(*respons == ' ' || *respons == '\t')
			respons++;
		switch (*respons) {
		default:
			fprintf(ofp, "? for pager commands\n");
			goto getmore;
		case 'y':
		case 'Y':
		case '\0':
		case '\n':
			break;
		case 'n':
			sigtrap = FALSE;
			fprintf(ofp, "\n");
			return(0);
		case 'h':
			if (art)
				dhprint(art, ifp, ofp);
			else
				hprint(h, ofp, !cflag);
			goto getmore;
		case 'q':
			return(1);
		case '+':
		case '-': {
			int n = 1, nn;
			char *r = respons;
			while (*++r==respons[0])
				n++;
			nn = atoi(r);
			if (respons[0]=='-')
				curpg -= (nn==0) ? n+1 : n+nn;
			else
				curpg += (nn==0) ? n-1 : n+nn-2;
			curpg = (curpg < 0) ? 0 :
				(curpg > lastpg) ? lastpg : curpg;
			fseek(ifp, pgoffs[curpg], 0);
			fgets(ppbuf, sizeof(ppbuf), ifp);
			break;
		    }
		case '!':
			fwait(fsubr(ushell, &respons[1], (char *)NULL));
			fprintf(ofp, "!\n");
			goto getmore;
		case 'p':
			fseek(ifp, pgoffs[--curpg], 0);
			fgets(ppbuf, sizeof(ppbuf), ifp);
			break;
		case 'l':
			curpg = lastpg;
			fseek(ifp, pgoffs[curpg], 0);
			fgets(ppbuf, sizeof(ppbuf), ifp);
			break;
		case '#':
			fprintf(ofp, "%d more lines\n",
			   (end ? plinecnt(ifp, end) : linecnt(ifp))+1);
			goto getmore;
		case 's':
		case 'w':
			{
			char *rp = &respons[1];
			if (*rp != '\0' && *rp != ' ' && *rp != ' ') {
				fprintf(ofp, "Bad file name.\n");
				break;
			}
			while (*rp==' ' || *rp=='\t')
				rp++;
			if(*rp != '|' && *rp != '/') {
				char hetyped[BUFLEN];
				char *boxptr;
				strcpy(hetyped,rp);
				if(boxptr = getenv("NEWSBOX"))
					if (index(boxptr, '%'))
						sprintf(rp,boxptr,groupdir);
					else
						strcpy(rp, boxptr);
				else if (hetyped[0]=='~' && hetyped[1]=='/') {
					strcpy(hetyped, rp+2);
					strcpy(rp, userhome);
				} else
					strcpy(rp, ".");
				strcat(rp,"/");
				if(hetyped[0] != '\0')
					strcat(rp, hetyped);
#ifdef	MOD_SAVE
				else {
					char *ART = getenv("ARTICLES");
					strcpy(rp,ART?ART:"articles");
				}
#else
				else
					strcat(rp, "Articles");
#endif	MOD_SAVE
			}
			isnew = access( rp, 2 );
			if (art)
				dsaveart(art, ifp, ofp, rp);
			else if( psave(ifp, rp, *respons=='s' ? 1:0) == 1 )
				printf("%s: %s\n",
				    rp, isnew ? "New file": "Appended");
			goto getmore;
		    }
		case '?':
			fprintf(ofp,
	"(p)rint again, (n)ext article, (q)uit, (l)ast page, -n, +n, (y)et another page\n");
			fprintf(ofp,
			    "(#) of lines left, (s)ave, (w)rite, (h)eader\n");
			goto getmore;
		}
	    }
	    fprintf(ofp, "%s", ppbuf);
	}
	fflush(ofp);
	if (sigtrap)
		printf(ofp, "\n\n");
	else
	/* if new newsgroup header follows, allow five extra lines */
	   if (i > (((rflag && bit < 1) || (bit>ngsize)) ? LNCNT - 5 : LNCNT)) {
		while (i++ < 23)
			putc('\n', ofp);
#ifdef	MOD_NTTY
		ioctl(fileno(ofp), TIOXRESO, 0);
#endif	MOD_NTTY
		fprintf(ofp, "-- Press return --");
		fflush(ofp);
		fgets(resbuf, sizeof(resbuf), stdin);
	} else
		fprintf(ofp, "\n");
	fflush(ofp);
	sigtrap = FALSE;
	return(0);
}

plinecnt(fp, end)
FILE *fp;
long end;
{
	long curpos;
	long count;
	register int nlines = 0;
	register int c;

	if (!end)
		return(linecnt(fp));
	if (fp == NULL)
		return(0);
	curpos = ftell(fp);
	count = end - curpos;
	while (((c = getc(fp)) != EOF) && (--count >= 0))
		if (c == '\n')
			nlines++;
	fseek(fp, curpos, 0);
	return(nlines);
}

num_lines(line)
char	*line;
{
	register int count = 0;
	register char *p;

	for( p = line; *p != NULL; p++ )
		switch( *p ){
		case '\t':
			count += TABSIZE - count%TABSIZE;break;
		default:
			count++;
		}
	return( (count + COL - 1)/COL ); /* COL-1: linelength minus newline */
}

psave(ifp, fnm, flg)
char *fnm;
{	
	long savpos = ftell(ifp);
	FILE *ofp;
	int	isprogram = 0;
	struct hbuf savh;

	if (ifp == NULL)
		return ( NULL );

	if (*fnm == '|'){
		if ((ofp = popen (&fnm[1], "w")) == NULL) {
			printf ("Cannot execute %s\n", &fnm[1]);
			return (NULL);
		}
		isprogram++;
	} else
		if ((ofp = fopen(fnm, "a")) == NULL) {
			printf("Cannot append to %s\n", fnm);
			return (NULL);
		}

	fseek(ifp, 0, 0);			/* back to beginning */
	if (hread(&savh, ifp, TRUE) == NULL) {	/* grab header */
		printf("Article is garbled.\n");
		return (NULL);
	}
#ifdef	V7MAIL
	savh.subtime = cgtdate(savh.subdate);
	fprintf(ofp, "From %s %s",
#ifdef	INTERNET
			savh.from,
#else
			savh.path,
#endif	INTERNET
				ctime(&savh.subtime));
#endif	V7MAIL
	if (flg)
		hprint(&savh, ofp, 1);
#ifdef	V7MAIL
	putc('\n', ofp);	/* force blank line before article */
	tprint(ifp, ofp, TRUE);
	putc('\n', ofp);	/* force blank line at end (ugh) */
#else
	tprint(ifp, ofp, FALSE);
#endif	V7MAIL
	if (isprogram)
		pclose(ofp);
	else
		fclose(ofp);
	fseek(ifp, savpos, 0);
	return(!isprogram);
}

rsave(ifnm, fnm, flg)
char *ifnm, *fnm;
{
	FILE *ifp = fopen(ifnm, "r");
	int	isnew;

	if( ifp == NULL ){
		printf("Can't get Article.\n");
		return;
	}
	isnew = access( fnm, 2 );
	if( psave(ifp, fnm, flg) == 1 )
		printf("%s: %s\n", fnm, isnew ? "New file" : "Appended");

	fclose(ifp);
}
#endif	MOD_PAGE