[comp.sources.misc] v07i054: simple vt100 emulator for Apollos

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (08/08/89)

Posting-number: Volume 7, Issue 54
Submitted-by: s8730679@spectrum.eecs.unsw.oz.au ( The Inquisitor )
Archive-name: tm

	I wrote this program after becoming frustrated at the
regular failure on the part of the apollo vt100 emulator to
come up. It is a simple terminal emulator which is sufficient
to run vn. Hack crashes under it and there are bugs, but it is
the shell of a decent emulator.

		Troy Rollo
	avenger@runx.ips.oz.au
	s8730679@spectrum.eecs.unsw.oz.au
	8730679@elec70a.eecs.unsw.oz.au
	troy_rollo@712.502@fidogate.fido.oz.au

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# README Makefile termcap tm.c

echo x - README
cat > "README" << '//E*O*F README//'
		 Thisterm terminal emulator

	By Troy Rollo 1989

	The thisterm terminal emulator is a minimum terminal
	emulator for the apollo domains. This version has only
	two control sequences:
		Ctrl-L (ascii 0xc) clears the screen (pad)
		An escape character followed by x+' ', y+' ' moves
	to the given location in the pad.

	Additional control sequences can be created by modifying
	the xfread routine. The ones already installed can, of course
	be modified.

	Thisterm uses the pseudo tty device entries /dev/ptypb and
	/dev/ttypb. An improved version would start at [pt]typ0 and work
	up towards [pt]typf, allowing more than one thisterm
	terminal emulator to run at once. It might also remove borders,
	and use pad_$set_full_window and the current scale factor to
	set the size of the window to match the size of the pseudo
	screen.

	To use this, first run thisterm, then set the environment variables:

	MORE=-c
	TERM=this
	TERMCAP=your_termcap_path

	avenger@runx.ips.oz.au
	s8730679@spectrum.eecs.unsw.oz.au (sometimes)
	8730679@elec70a.eecs.unsw.oz.au (sometimes)
	troy_rollo@712.502@fidogate.fido.oz.au (can't reply)
//E*O*F README//

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
# Makefile for thisterm
# Very easy:

thisterm: tm.c
	cc -o thisterm tm.c
//E*O*F Makefile//

echo x - termcap
cat > "termcap" << '//E*O*F termcap//'
#
# Termcap file for my news reading emulator
#
# run tm, then set:
#
#	MORE=-c
#	TERM=this
#	TERMCAP=$HOME/termcap
#
th|this|this emulator:\
	:cm=\E%r%+ %+ :cl=\14:li#25:co#80:bs:
//E*O*F termcap//

echo x - tm.c
cat > "tm.c" << '//E*O*F tm.c//'
/* thisterm terminal emulator
	By Troy Rollo 1989

	The thisterm terminal emulator is a minimum terminal
	emulator for the apollo domains. This version has only
	two control sequences:
		Ctrl-L (ascii 0xc) clears the screen (pad)
		An escape character followed by x+' ', y+' ' moves
	to the given location in the pad.

	Additional control sequences can be created by modifying
	the xfread routine. The ones already installed can, of course
	be modified.

	Thisterm uses the pseudo tty device entries /dev/ptypb and
	/dev/ttypb. An improved version would start at [pt]typ0 and work
	up towards [pt]typf, allowing more than one thisterm
	terminal emulator to run at once. It might also remove borders,
	and use pad_$set_full_window and the current scale factor to
	set the size of the window to match the size of the pseudo
	screen.

	avenger@runx.ips.oz.au
	s8730679@spectrum.eecs.unsw.oz.au (sometimes)
	8730679@elec70a.eecs.unsw.oz.au (sometimes)
	troy_rollo@712.502@fidogate.fido.oz.au (can't reply)
*/

#include <netdb.h>
#include <stdio.h>
#include <signal.h> 
#include <fcntl.h>
#include <apollo/base.h>
#include <apollo/pad.h>
#include <apollo/error.h>

#define COLUMNS 80
#define ROWS 25

/* I think fd2p is a relic from a previous life. This program evolved
	from an rexec patron */
int fd2p = 1;
status_$t status;
stream_$sk_t sk = {
		0l,
		0l,
		0l
	};

/* Hmm... interesting... I don't actually use argc or argv */
main(int argc, char **argv)
{
        char c[256];	
        int cc;
        int n;
        int pn, pid;
	int rem, proc;
        

	/* put the pad in raw mode. This is a requirement for any terminal
	   emulator */
	pad_$raw(1, &status);
	/* Now open the master side of the pseudo tty */
	rem=open("/dev/ptypb", O_RDWR);
	/* And the slave side */
	proc=open("/dev/ttypb", O_RDWR);
        if (rem==-1 || proc==-1)
		/* Complain if unable to open either - probably should
		   try to find another one */
		exit(1);
	if((pid=fork())==-1) {
		fprintf(stderr, "%s: can't fork\n", argv[0]);
		exit(1);
	}
	if (pid==0) {
		/* This process lives on the slave side of the pseudo tty
		   Make the pseudo tty stdin, stdout and stderr */
		close(0);
		close(1);
		close(2);
		dup(proc);
		dup(proc);
		dup(proc);
		close(proc);
		close(rem);
		/* I use ksh - you might prefer another shell */
		execl("/bin/ksh", "ksh", "-i", 0);
	}
	close(proc);
	/* Close the slave side so that when the last slave side process dies
	   or closes its last file descriptor we hear about it on the master side */
        if ((pn=fork())==-1) {
		fprintf(stderr, "%s: Can't fork\n", argv[0]);
		kill(pid,9);
		exit(1);
        }
	/* Create a frame to allow the cursor to roam around the pseudo screen */
	pad_$create_frame(1, COLUMNS, ROWS, &status);
	/* The parent reads on the master side (output of the slace side) until
	   there are no more processes writing on the pseudo tty. At this point
	   it enters a suicide pact - killing the child with a machine gun (signal
	   9) and dieing itself.
	   Intil it dies, the slave side transfers all stdin to the pseudo tty	*/
        if (pn!=0) {
		while (xfread(rem) !=-1);
		pad_$cooked(1, &status);
		pad_$delete_frame(1, &status);
		kill(pn, 9);
		exit(0);
        }
        else {
        	while ((n=read(0, c, 255)) != 0)
			write(rem, c, n);
        }
}


/* xfread reads from the pseudo tty, and decides what to do about the output.
   It's fairly straight forward - checks for any specified control character.
   There is a bug in the newline sequence - if the user presses the page up or
   shift-uparrow keys, you can't get back on the current frame.... perhaps these
   keys should be redefined within the region of the pad.
	Some enhancements might be:
   A partial screen clearing option.
   Scroll forward and back on demand.
   Changing font (perhaps loading an inverse font and using it for highlight.
	the following fonts could also prove useful as built-ins:
	  italics
	  bold
	  times_roman
	as well as a pure graphics font (say, a 7x1 font with each character
	representing a sequence of pixels in a vertical line)
   This routine doesn't return until there are no more slaves using the pseudo
   tty
*/
int xfread(f)
int f;
{
	char b;
	short x, y;
	long line, eof;
	short xo, yo;

	while (kread(f,&b)!=-1)
		if (b=='\033') {
			kread(f,&b);
			x=(short) (b-' '+1);
			kread(f,&b);
			y=(short) (b-' '+1);
			pad_$inq_view(1, 1, &line, &eof, &xo, &yo, &status);
			pad_$move(1,pad_$absolute,x,y+yo-1,&status);
			write(1,"\0",2);
		}
		else if (b=='\014') {
			pad_$clear_frame(1, sk, &status);
			write(1,"\0",2);
		}
		else {
			write(1,&b,1);
			if (b=='\n') {
				pad_$inq_view(1, 1, &line, &eof, &xo, &yo, &status);
				pad_$inq_position(1, &x, &y, &status);
				if (y-yo+1==25) {
					pad_$set_view(1, 1, line, 1, yo+2, &status);
					pad_$create_frame(1, COLUMNS, ROWS, &status);
				}
			}
		}
	return (-1);
}

/* A buffered read - returns 1 on success, -1 on failure (just to be difficult for
   anybody using it as the argument to if! And also for compatibility with the
   values returned by Domain/IX
   returns one character at a time, buffering them in a static 2048 byte buffer.
   interesting things would happen if two different files were used as arguments
   int different parts of the program, but it's probably not worth fixing */
int kread(file, buffer)
int file;
char *buffer;
{
	static char BIGBUF[2048];
	static int bbcnt = 0;
	static int bbptr = 0;

	if (bbptr>=bbcnt) {
		bbcnt=read(file, BIGBUF, 2048);
		if (bbcnt==-1)
			return(-1);
		bbptr=0;
	}
	*buffer=BIGBUF[bbptr];
	++bbptr;
	return(1);
}
//E*O*F tm.c//

exit 0