/*
	MORE emulates the function of the MORE utility of UNIX. It types text
much like the TYPE command, but it pauses after each screen of data. This
version is designed to work with VT52 and VT100 terminals, as it uses reverse
video and cursor positioning. Other terminals could easily be handled by
making minor changes.

	Once MORE pauses, it displays the name of the file and the percentage
of the file that has been read. This percentage is not too accurate for small
files but is still in the ballpark. At the pause, the user may type the
following commands;

		<space>		Advances 1 page (23 lines)
		<return>	Advances 1 line
		D		Advances half a page
		Q		Quits the program

	MORE does not currently handle wildcarding, but may be changed at some
future release. Other handy things which could be added are reverse and search
functions.

	A compile/link command file "MORE.COM" is provided. Note that MORE
is written in C.

*/
#include <stdio.h>
#include iodef.h
#include descrip
#include ssdef
#include "stdedc.h"
#include "rms.h"

#define	EOF	RMS$_EOF
#define	FNSZ	80
#define	SIGNAL(s) if (s != RMS$_NORMAL) exit(s)

struct	DESCRIPTOR_T
	{
	short	l;
	char	t;
	char	c;
	char	*p;
	};
#define	DESCRIPTOR(name,string) struct DESCRIPTOR_T name =		\
	{sizeof(string),DSC$K_DTYPE_T,DSC$K_CLASS_S,string}
 
#define BUFSZ 256
#define SCREENSIZE 23		/* lines per screen */
#define LINESIZE 80		/* chars per line */
int Pause;
int LineLength;			/* length of (tab expanded) line */
main(argc,argv)
	int argc;
	char *argv[];
	{
	int inp;		/* input file */
	char buf[BUFSZ];	/* input buffer */
	int sz;			/* size of line read */
	int nolines=SCREENSIZE;	/* number of lines left on the screen*/
	int lines;		/* number of terminal lines this line needs */
	short response;		/* what the user typed */
	double chars_read;	/* number of characters read so far */
	double file_size;	/* float representation of filsz * 1000 */
	short percent_read;	/* percentage of file read */
	float float_percent;	/* intermediate result for percentage calc */
	char	rev_video[5];
	char	norm_video[4];
	char	foreground[3];
	char	background[3];
	short ttchan;		/* channel id for terminal */
	short getchartt();	/* gets a single character from the terminal */
	short ttopen();		/* opens TT: for getchartt */
	BOOL	eof = {FALSE};
	INT	fd;
	INT	filsz;
	INT	fn[FNSZ];
	INT	maxrec = {0};
	INT	s;
	INT	recnbr = {0};
	COUNT	recsz;
	INT	term_flag,dev_type,line_width,lines_page;
	DESCRIPTOR(fnd,fn);
	lib$screen_info(&term_flag,&dev_type,&line_width,&lines_page);
	rev_video[0] = norm_video[0] = foreground[0] = background[0] = ' ';
	rev_video[1] = norm_video[1] = foreground[1] = background[1] = 0;
	if (dev_type == 64)
	    {
	    rev_video[0] = norm_video[0] = foreground[0] = background[0] = 27;
	    rev_video[1] = norm_video[1] = '9';
	    rev_video[2] = '@';
	    norm_video[2] = 'P';
	    foreground[1] = 'T';
	    background[1] = 'U';
	    rev_video[3] = norm_video[3] = foreground[2] = background[2] =  0;
	    }
	if (dev_type == 96)
	    {
	    rev_video[0] = norm_video[0] = 27;
	    rev_video[1] = norm_video[1] = '[';
	    rev_video[2] = '7';
	    rev_video[3] = norm_video[2] = 'm';
	    rev_video[4] = norm_video[3] =  0;
	    }
	chars_read = 0;
	strcpy(fnd.p,argv[1]);
	fnd.l = strlen(fnd.p);
	*(fnd.p + fnd.l) = 0;
	fd = alocfd();
	s = openf(&fnd,&fd,&recsz,&filsz,&1);
	SIGNAL(s);
	closef(fd);
	file_size = (filsz * 1000) - 768;
	if (argc > 1)
		inp = open(argv[1],0);
	else
		{
		inp = dup(0);
		dup2(open("/dev/tty",0),0);
		}
	if (inp == -1)
		perror(argv[1]);
	else
		{
		ttchan = ttopen();
		while ((sz = getline(inp,buf)) != 0)
			{
			chars_read += sz;
			lines = (LineLength + 78) / 80;
			if (lines == 0) lines = 1;
			nolines -= lines;
			if (nolines <= 0)
				{
ask:
				float_percent = chars_read / file_size;
				percent_read = float_percent * 100;
				if (percent_read > 99.0)
					percent_read = 99.0;
				printf("%s%s - %s  %2d%% -%s%s",rev_video,
					background,argv[1],percent_read,
					foreground,norm_video);
				response = getchartt(ttchan);
				write(1,
				"\r                                         \r"
					,43);
				if (response == ' ')
					nolines = SCREENSIZE - lines;
				else if (response == '\n')
					nolines = lines;
				else if ((response & 0x5f) == 'D')
					nolines = (SCREENSIZE-lines)/2;
				else if ((response & 0x5f) == 'Q')
					{
					exit(1);
					}
				else
					{
					printf("\007");
					goto ask;
					}
				}
			write(1,buf,sz);
			if (Pause)
				{
				nolines = 0;	/* pause after this line */
				Pause = 0;
				}
			}
		}
	}
getline(fd,buf)
	int fd;
	char *buf;
	{
	register int linesz=0;
	register int c;
	LineLength = 1;
	while ((c=getcharacter(fd)) != -1 && c != '\n')
		{
		buf[linesz++] = c;
		if (c == '\t')
			LineLength = (LineLength + 8) & (~7);
		else if (c == '\f')
			{
			Pause = 1;
			buf[linesz-1] = '^';
			buf[linesz++] = 'L';
			}
		else
			LineLength++;
		}
	if (c != -1)
		buf[linesz++] = '\n';
	return(linesz);
	}
getcharacter(fd)
	int fd;
	{
	static char buf[512];
	static int offset = 512;
	static int bufsz = 512;
	if (offset >= bufsz)
		{
		if ((bufsz=read(fd,buf,512)) == 0)
			return(-1);
		offset = 0;
		}
	return(buf[offset++]);
	}
 
short getchartt(chan)
	short chan;
	{
	int status;
	short iosb[4];
	char buf[1];
	status = sys$qiow(0,chan,IO$_READLBLK | IO$M_NOECHO,
		iosb,0,0,buf,1,32767,0,0,0,0);
	if (status != SS$_NORMAL)
		LIB$STOP(status);
	if (iosb[0] != SS$_NORMAL)
		LIB$STOP(iosb[0]);
	if (buf[0] == '\r')
		buf[0] = '\n';	/* emulate UNIX */
	return(buf[0]);
	}
 
short ttopen()
	{
	short chan;
	register int status;
	struct dsc$descriptor dev_name;
 
	dev_name.dsc$w_length = 4;
	dev_name.dsc$a_pointer = "TT:";
	dev_name.dsc$b_class = DSC$K_CLASS_S;
	dev_name.dsc$b_dtype = DSC$K_DTYPE_T;
	status = sys$assign(&dev_name,&chan,0,0,0);
	if (status != SS$_NORMAL)
		LIB$STOP(status);
	return(chan);
	}
 
