[comp.os.minix] Rqst for info about eds, here's termcap

agc@ist.UUCP (Alistair G. Crooks) (07/30/87)

In article <1539@botter.cs.vu.nl>, ast@cs.vu.nl (Andy Tanenbaum) writes:
>
> 1. I couldn't make a termcap entry for the MINIX "terminal".  MINIX numbers
>    the screen lines with (0, 0) in the lower left hand corner.  Perhaps it
>    was arrogant of me to choose this co-ordinate system.  I just thought that
>    since the entire world has been doing things this way since the time of
>    Rene Descartes (1596-1650), it might be ok.  As far as I can tell, termcap
>    can't hack it.
>
> Andy Tanenbaum (ast@cs.vu.nl)

I don't think it was arrogant, just unusual. Anyway, what's wrong with an
'inverter' macro definition or function call for the y-coordinate just
before the tgoto() call? (Although the keypad stuff is a bit harder, granted).
So, onto termcap...

Please find enclosed the wherewithal to make the termcap library.
If the serial line driver ever works, this should prove quite useful, and
it may allow mined to be compiled without some compiler definitions
(i.e. more compact mined source files). The files are...

Makefile 	makefile for termcap and show
dotprofile 	a copy of my .profile, includes the TERM and TERMCAP
		definitions that I use (can be set from the shell, but
		I'm lazy).
etc_termcap  	a copy of the termcap file I have used.
getenv.c	the library file - must be used and replaced in library,
		due to a bug found in the original getenv which matches
		the string "TERMCAP" at the environment entry "TERM=..."
logo		a sample file for show
show.c		the browser program, try "show -v logo" (Ctl-C to quit)
termcap.c	the library routines.

The show program works under BSD as well. There will be a curses library
to follow, if anyone's interested.

Alistair G. Crooks (agc@ist.co.uk)

#!/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:
# Makefile dotprofile etc_termcap getenv.c getopt.c logo show.c termcap.c

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
LIB = /usr/lib/libtermcap.a
CFLAGS = -O -F -T.

all : $(LIB) show

$(LIB) : termcap.s
	ar r $(LIB) termcap.s

termcap.s : termcap.c
	cc -S $(CFLAGS) -LIB termcap.c

show : show.c $(LIB) getopt.s
	cc $(CFLAGS) show.c getopt.s -ltermcap -o show

//E*O*F Makefile//

echo x - dotprofile
cat > "dotprofile" << '//E*O*F dotprofile//'
HOME=/user/agc
PS1="minpc!agc\$ "
PATH=:$HOME/bin:/bin:/usr/bin:/lib
TERMCAP=$HOME/etc/termcap
TERM=ibmpc
l=/usr/lib
m="/etc/mount /dev/fd0 /usr"
u="/etc/umount /dev/fd0"
m1="/etc/mount /dev/fd1 /user"
u1="/etc/umount /dev/fd1"
s="stty"
sd="stty default"
stty kill ""
echo -n "Last logged in at "
cat .lastlogin
echo ""
date > .lastlogin
export PATH HOME PS1 PS2 TERMCAP TERM l
//E*O*F dotprofile//

echo x - etc_termcap
cat > "etc_termcap" << '//E*O*F etc_termcap//'
pc|ibmpc|ibmpcmono:\
	bs:\
	cd=\E~0:\
	cl=\E 8\E~0:\
	cm=\E%+ %+ :\
	co#80:\
	ho=\E 8:\
	li#24:\
	se=\Ez\007:\
	so=\Ezp:\
	sr=\E~1

//E*O*F etc_termcap//

echo x - getenv.c
cat > "getenv.c" << '//E*O*F getenv.c//'
#define NULL  (char *) 0
char *getenv(name)
register char *name;
{
  extern char **environ;
  register char **v = environ, *p, *q;

  while ((p = *v++) != NULL) {
	for (q = name ; *p && *q && *p == *q ; p++, q++)
		;
	if (*p != '=' || *q != 0)
		continue;
	return(++p);
  }
  return(0);
}
//E*O*F getenv.c//

echo x - getopt.c
cat > "getopt.c" << '//E*O*F getopt.c//'
#include <stdio.h>

extern char	*index();

/*
 *	getopt - parse the arguments given.
 *	retrieved from net.sources
 */
int	opterr = 1;
int	optind = 1;
int	optopt;
char	*optarg;

#define BADCH	(int)'?'
#define EMSG	""
#define TELL(s)	fputs(*nargv, stderr); fputs(s, stderr);\
	fputc(optopt, stderr); fputc('\n', stderr);\
	return(BADCH);

int getopt(nargc, nargv, ostr)
int	nargc;
char	**nargv;
char	*ostr;
{
  register char	*oli;
  static char	*place = EMSG;

  if (!*place) {
	if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
		return(EOF);
	if (*place == '-') {
		++optind;
		return(EOF);
	}
  }
  if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr, optopt))) {
	if (!*place)
		++optind;
	TELL(": illegal option -- ");
  }
  if (*++oli != ':') {
	optarg = NULL;
	if (!*place)
		++optind;
  } else {
	if (*place)
		optarg = place;
	else if (nargc <= ++optind) {
		place = EMSG;
		TELL(": option requires an argument -- ");
	} else
		optarg = nargv[optind];
	place = EMSG;
	++optind;
  }
  return(optopt);
}
//E*O*F getopt.c//

echo x - logo
cat > "logo" << '//E*O*F logo//'
MMMMM        MMMMM   IIIII   NNNNN         NNN   IIIII    XXXXX        XXXXX
MMMMMM      MMMMMM   IIIII   NNNNNN        NNN   IIIII     XXXXX      XXXXX
MMMMMMM    MMMMMMM    III    NNNNNNN       NNN    III       XXXXX    XXXXX
MMM MMMM  MMMM MMM    III    NNN NNNN      NNN    III        XXXXX  XXXXX
MMM  MMMMMMMM  MMM    III    NNN  NNNN     NNN    III         XXXXXXXXXX
MMM   MMMMMM   MMM    III    NNN   NNNN    NNN    III          XXXXXXXX
MMM    MMMM    MMM    III    NNN    NNNN   NNN    III          XXXXXXXX 
MMM     MM     MMM    III    NNN     NNNN  NNN    III         XXXXXXXXXX
MMM            MMM    III    NNN      NNNN NNN    III        XXXXX  XXXXX
MMM            MMM    III    NNN       NNNNNNN    III       XXXXX    XXXXX
MMM            MMM   IIIII   NNN        NNNNNN   IIIII     XXXXX      XXXXX
MMM            MMM   IIIII   NNN         NNNNN   IIIII    XXXXX        XXXXX

The MINIX documentation is contained in the appendices of the following book:
	Title:     Operating Systems: Design and Implementation
	Author:    Andrew S. Tanenbaum
	Publisher: Prentice-Hall (1987)

For additional information, see the "doc" directory on the /us(e)r diskette.

Welcome to MINIX.

And welcome to the show....
//E*O*F logo//

echo x - show.c
cat > "show.c" << '//E*O*F show.c//'
/*
 *	show.c	1.1	20/7/87		agc	Joypace Ltd
 *
 *	Copyright Joypace Ltd, London, UK, 1987. All rights reserved.
 *	This file may be freely distributed provided that this notice
 *	remains attached.
 *
 *	A small browser program(!), the idea from Chris Torek's floop
 *	program, the bad coding from agc. (None of the code is his as
 *	I can't find my copy of his program). Designed to test the
 *	termcap(3) routines.
 */
#include <stdio.h>
#include <signal.h>

extern char	*tgetstr();	/* termcap getstring capability */
extern char	*tgoto();	/* termcap goto (x, y) */
extern FILE	*fopen();	/* open a file for buffered io */
extern char	*getenv();	/* get an environment variable */

#define	MAXX	79		/* Width of display */
#define	MAXY	23		/* Height of display */

#define TOTAL	MAXX * MAXY * 5	/* Total number of tries */

short		buf[MAXY][MAXX];/* buffer to hold characters */

char		*prog;		/* program name */
char		*optstr = "v";	/* -v for ever */
extern int	optind;
extern char	*optarg;

char		termcap[1024];	/* termcap buffer */
char		tc[100];	/* area to hold string capabilities */
char		*ttytype;	/* terminal type from env */
char		*arp;		/* pointer for use in tgetstr */
char		*cp;		/* character pointer */

char		*cl;		/* clear screen capability */
char		*cm;		/* cursor motion capability */
char		*so;		/* start standout capability */
char		*se;		/* end standout capability */

/*
 *	outc - call putchar, necessary because putchar is a macro.
 */
int
outc(c)
int	c;
{
	putchar(c);
}

/*
 *	onint - what to do if you get an interrupt.
 */
int
onint()
{
	tputs(se, 1, outc);
	cp = tgoto(cm, 0, 0);
	tputs(cp, 1, outc);
	fflush(stdout);
	exit(0);
}

/*
 *	fillbuf - fill the buffer with the beginning of file s, and
 *	expand the tabs as we go. Returns 0 if file not found, 1 otherwise.
 */
int
fillbuf(s)
char	*s;
{
	register int	lno;
	register int	cno;
	FILE		*fp;
	int		i;

	if ((fp = fopen(s, "r")) == (FILE *) NULL)
		return(0);
	for (lno = 0 ; lno < MAXY ; lno++)
		for (cno = 0 ; cno < MAXX ; cno++)
			switch (buf[lno][cno] = getc(fp)) {
			case EOF :
				buf[lno][cno] = ' ';
				cno = MAXX;
				lno = MAXY;
				break;
			case '\t' :
				for (i = cno ; i < ((cno + 8) & ~07) && i < MAXX ; i++)
					buf[lno][i] = ' ';
				cno = i;
				break;
			case '\r' :
			case '\n' :
				cno = MAXX;
			}
	fclose(fp);
	return(1);
}

/*
 *	display - go to the x and y coords on the screen, and display the
 *	character c. Note that origin on PC is at bottom left of screen,
 *	rather than top left.
 */
void
display(c, y, x)
char	c;
int	y;
int	x;
{
	if (ttytype && strcmp(ttytype, "ibmpc") == 0)
		y = MAXY - y;
	cp = tgoto(cm, x, y);
	tputs(cp, 1, outc);
	putchar(c & 0177);
	fflush(stdout);
}


void
doscreen(blank)
char	blank;
{
	int	c;
	int	tot;
	int	x;
	int	y;

	for (tot = 0 ; tot < TOTAL ; tot++) {
		x = rand() % MAXX;
		y = rand() % MAXY;
		if ((c = buf[y][x]) & ~0177)
			continue;
		c &= 0177;
		if (c == 0 || c == ' ')
			continue;
		display((blank) ? ' ' : c, y, x);
		buf[y][x] |= ~0177;
	}
	for (y = 0 ; y < MAXY ; y++)
		for (x = 0 ; x < MAXX ; x++) {
			if ((c = buf[y][x]) & ~0177)
				continue;
			c &= 0177;
			if (c != 0 && c != ' ')
				display((blank) ? ' ' : c, y, x);
		}
}

/*
 *	fatal - report error and die. Never returns
 */
void
fatal(s)
char	*s;
{
	(void) fprintf(stderr, "%s: %s\n", prog, s);
	exit(1);
}


/*
 *	Chocks away...
 */
main(argc, argv)
int	argc;
char	**argv;
{
	int	ever = 0;
	int	i;
	int	s = 0;
	int	x;
	int	y;

	if (argc < 2) {
		(void) fprintf(stderr, "Usage: %s filename\n", *argv);
		exit(1);
	}
	if ((ttytype = getenv("TERM")) == NULL)
		fatal("No terminal type set in environment");
		
	if (tgetent(termcap, ttytype) != 1)
		fatal("No termcap entry for terminal");
	arp = tc;
	cl = tgetstr("cl", &arp);
	tputs(cl, 1, outc);
	so = tgetstr("so", &arp);
	se = tgetstr("se", &arp);
	cm = tgetstr("cm", &arp);
	while ((i = getopt(argc, argv, optstr)) != EOF)
		switch(i) {
		case 'v' :
			ever = 1;
			break;
		default :
			fatal("bad option");
		}
	if (!fillbuf(argv[optind]))
		fatal("file not found");
	signal(SIGINT, onint);
	do {
		doscreen(s);
		cp = tgoto(cm, 0, 0);
		tputs(cp, 1, outc);
		fflush(stdout);
		for (y = 0 ; y < MAXY ; y++)
			for (x = 0 ; x < MAXX ; x++)
				buf[y][x] &= 0177;
		if (ever)
			sleep(5);
		s = ~s;
	} while (ever);
	exit(0);
}


//E*O*F show.c//

echo x - termcap.c
cat > "termcap.c" << '//E*O*F termcap.c//'
/*
 *	termcap.c	1.1	20/7/87		agc	Joypace Ltd
 *
 *	Copyright Joypace Ltd, London, UK, 1987. All rights reserved.
 *	This file may be freely distributed provided that this notice
 *	remains attached.
 *
 *	A public domain implementation of the termcap(3) routines.
 */
#include <stdio.h>

#define CAPABLEN	2

#define ISSPACE(c)	((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
#define ISDIGIT(x)	((x) >= '0' && (x) <= '9')

extern short	ospeed;		/* output speed */
extern char	PC;		/* padding character */
extern char	*BC;		/* back cursor movement */
extern char	*UP;		/* up cursor movement */

char		*capab;		/* the capability itself */

extern char	*getenv();	/* new, improved getenv */
extern FILE	*fopen();	/* old fopen */

/*
 *	tgetent - get the termcap entry for terminal name, and put it
 *	in bp (which must be an array of 1024 chars). Returns 1 if
 *	termcap entry found, 0 if not found, and -1 if file not found.
 */
int
tgetent(bp, name)
char	*bp;
char	*name;
{
	FILE	*fp;
	char	*file;
	char	*cp;
	short	len = strlen(name);

	capab = bp;
	if ((file = getenv("TERMCAP")) != (char *) NULL) {
		if (*file != '/' &&
		    (cp = getenv("TERM")) != NULL && strcmp(name, cp) == 0) {
			(void) strcpy(bp, file);
			return(1);
		}
	} else
		file = "/etc/termcap";
	if ((fp = fopen(file, "r")) == (FILE *) NULL)
		return(-1); 
	while (fgets(bp, 1024, fp) != NULL) {
		/* skip V6 two letter name */
		for (cp = bp ; *cp != '|' ; cp++)
			;
		for (++cp ; ISSPACE(*cp) ; cp++)
			;
		if (strncmp(name, cp, len) == 0) {
			while (*(cp = &bp[strlen(bp) - 2]) == '\\')
				fgets(cp, 1024, fp);
			fclose(fp);
			return(1);
		}
	}
	fclose(fp);
	return(0);
	
}

/*
 *	tgetnum - get the numeric terminal capability corresponding
 *	to id. Returns the value, -1 if invalid.
 */
int
tgetnum(id)
char	*id;
{
	char	*cp;
	int	ret;

	if ((cp = capab) == NULL || id == NULL)
		return(-1);
	while (*++cp != ':')
		;
	for (++cp ; *cp ; cp++) {
		while (ISSPACE(*cp))
			cp++;
		if (strncmp(cp, id, CAPABLEN) == 0) {
			while (*cp && *cp != ':' && *cp != '#')
				cp++;
			if (*cp != '#')
				return(-1);
			for (ret = 0, cp++ ; *cp && ISDIGIT(*cp) ; cp++)
				ret = ret * 10 + *cp - '0';
			return(ret);
		}
		while (*cp && *cp != ':')
			cp++;
	}
	return(-1);
}

/*
 *	tgetflag - get the boolean flag corresponding to id. Returns -1
 *	if invalid, 0 if the flag is not in termcap entry, or 1 if it is
 *	present.
 */
int
tgetflag(id)
char	*id;
{
	char	*cp;

	if ((cp = capab) == NULL || id == NULL)
		return(-1);
	while (*++cp != ':')
		;
	for (++cp ; *cp ; cp++) {
		while (ISSPACE(*cp))
			cp++;
		if (strncmp(cp, id, CAPABLEN) == 0)
			return(1);
		while (*cp && *cp != ':')
			cp++;
	}
	return(0);
}

/*
 *	tgetstr - get the string capability corresponding to id and place
 *	it in area (advancing area at same time). Expand escape sequences
 *	etc. Returns the string, or NULL if it can't do it.
 */
char *
tgetstr(id, area)
char	*id;
char	**area;
{
	char	*cp;
	char	*ret;
	int	i;

	if ((cp = capab) == NULL || id == NULL)
		return(NULL);
	while (*++cp != ':')
		;
	for (++cp ; *cp ; cp++) {
		while (ISSPACE(*cp))
			cp++;
		if (strncmp(cp, id, CAPABLEN) == 0) {
			while (*cp && *cp != ':' && *cp != '=')
				cp++;
			if (*cp != '=')
				return(NULL);
			for (ret = *area, cp++; *cp && *cp != ':' ; (*area)++, cp++)
				switch(*cp) {
				case '^' :
					**area = *++cp - 'A';
					break;
				case '\\' :
					switch(*++cp) {
					case 'E' :
						**area = '\033';
						break;
					case 'n' :
						**area = '\n';
						break;
					case 'r' :
						**area = '\r';
						break;
					case 't' :
						**area = '\t';
						break;
					case 'b' :
						**area = '\b';
						break;
					case 'f' :
						**area = '\f';
						break;
					case '0' :
					case '1' :
					case '2' :
					case '3' :
						for (i=0 ; *cp && ISDIGIT(*cp) ; cp++)
							i = i * 8 + *cp - '0';
						**area = i;
						cp--;
						break;
					case '^' :
					case '\\' :
						**area = *cp;
						break;
					}
					break;
				default :
					**area = *cp;
				}
			*(*area)++ = '\0';
			return(ret);
		}
		while (*cp && *cp != ':')
			cp++;
	}
	return(NULL);
}

/*
 *	tgoto - given the cursor motion string cm, make up the string
 *	for the cursor to go to (destcol, destline), and return the string.
 *	Returns "OOPS" if something's gone wrong, or the string otherwise.
 */
char *
tgoto(cm, destcol, destline)
char	*cm;
int	destcol;
int	destline;
{
	register char	*rp;
	static char	ret[24];
	int		*dp = &destcol;

	for (rp = ret ; *cm ; cm++) {
		switch(*cm) {
		case '%' :
			switch(*++cm) {
			case '+' :
				if (dp == NULL)
					return("OOPS");
				*rp++ = *dp + *++cm;
				dp = (dp == &destcol) ? &destline : NULL;
				break;
			case '%' :
				*rp++ = '%';
				break;
			}
			break;
		default :
			*rp++ = *cm;
		}
	}
	*rp = '\0';
	return(ret);
}

/*
 *	tputs - put the string cp out onto the terminal, using the function
 *	outc. This should do padding for the terminal, but I can't find a
 *	terminal that needs padding at the moment...
 */
int
tputs(cp, affcnt, outc)
register char	*cp;
int		affcnt;
int		(*outc)();
{
	if (cp == NULL)
		return(1);
	/* do any padding interpretation - left null for MINIX just now */
	while (*cp)
		(*outc)(*cp++);
	return(1);
}

/*
 *	That's all, folks...
 */
//E*O*F termcap.c//

exit 0