DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T p

⟦59396de0d⟧ TextFile

    Length: 21268 (0x5314)
    Types: TextFile
    Names: »p1trm.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/vtrm/p1trm.c« 

TextFile

/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */

/*
 * ibm Pc virtual TeRMinal package.
 *
 * An implementation of the VTRM interface for MS-DOS machines.
 *
 * This code supports two MODE's of accessing the screen.
 * The first one (BIOS) will be used, unless the user overwrites this
 * by setting the SCREEN environment variable.
 * This variable can also be used to convey a screen size that differs
 * from the default 25 lines and 80 columns. See below.
 *
 * The two modes are:
 *
 * 1) IBM BIOS interrupt 10 hex, video io.
 *    (See IBM PC XT Technical Reference 6936833, May 1983,
 *     Appendix A, pages A46-A47).
 *    This is what you really want to use, since it's the only one that
 *    can decently scroll. It cannot insert or delete characters, so
 *    most optimisations from vtrm.c are useless and taken out.
 *    Unfortunately, not every PC-compatible machine supports this BIOS
 *    interrupt, so for these unlucky souls there is the following escape:
 *
 * 2) The ANSI.SYS driver.
 *    (See IBM MS-DOS 6936839, Jan 1983, Version 2.00, Chapter 13.)
 *    (Some compatibles don't have a separate ANSI.SYS driver but do the
 *    same escape interpretation by default.)
 *    This works reasonably, apart from scrolling downward, or part of
 *    the screen, which is clumsy.
 *    (The ANSI standard provides an escape sequence for scrolling
 *    but ANSI.SYS does not support it, nor any other way of scrolling.)
 *
 * The rest of the interface is the same as described in vtrm.c,
 * with the following exceptions:
 *    - to ease coding for ansi scrolls, the terminal is supposed to
 *	contain blanks at positions that were not written yet;
 *	the unknown rubbish that is initially on the screen can
 *	only be cleared by the caller by scrolling the whole screen up
 *	by one or more lines;
 *    - the number of lines on the terminal is assumed to be 25;
 *	the number of columns is (1) determined by a BIOS function, or
 *	(2) assumed to be 80 for ANSI;
 *	the user can overwrite this by setting the environment variable:
 *
 *		SET SCREEN=BIOS x y
 *	or
 *		SET SCREEN=ANSI x y
 *
 *	where x and y are the number of lines and columns respectively.
 *
 * The lines and columns of our virtual terminal are numbered
 *	y = {0...lines-1} from top to bottom, and
 *	x = {0...cols-1} from left to right,
 * respectively.
 *
 * The Visible Procedures in this package are as described in vtrm.c.
 *
 */

/*
 * Includes and data definitions.
 */

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <dos.h>

char *malloc();

#include "trm.h"

#ifdef lint
#define VOID (void)
#else
#define VOID
#endif

#define Forward
#define Visible
#define Hidden static
#define Procedure

typedef short intlet;
typedef char *string;
typedef char bool;
#define Yes '\1'
#define No  '\0'
#define Undefined (-1)

#define Min(a,b) ((a) <= (b) ? (a) : (b))

#define MESS(number, text) text

#ifdef GFX
#include "gfx.h"
#endif

/* terminal status */

Hidden int started = No;

Hidden int scr_mode = 0;
#define ANSI 'A'
#define BIOS 'B'

#define Nlines	25
#define Ncols	80
Hidden int lines = Nlines;
Hidden int cols = Ncols;
Hidden int flags = 0;

/* current standout mode */
#define Off	0
#define On	0200
Hidden int so_mode = Off;

/* masks for char's and intlet's */
#define NULCHAR '\000'
#define CHAR	0177
#define SOBIT	On
#define SOCHAR	0377

/* current cursor position */
Hidden intlet cur_y = Undefined, cur_x = Undefined;

/* "line[y][x]" holds the char on the terminal, with the SOBIT.
 * the SOBIT tells whether the character is standing out.
 * "lenline[y]" holds the length of the line.
 * (Partially) empty lines are distinghuished by "lenline[y] < cols".
 * Unknown chars will be ' ', so the scrolling routines for ANSI
 * can use "unwritten" chars (with indent > 0 in trmputdata).
 * To make the optimising compare in putline fail, lenline[y] is initially 0.
 * The latter implies that if a line is first addressed with trmputdata,
 * any rubbish that is on the screen beyond the data that gets put, will
 * remain there.
 */
Hidden char **line = 0;
Hidden intlet *lenline = 0;

/* Make the cursor invisible when trmsync() tries to move outside the screen */
Hidden bool no_cursor = No;

/*
 * Starting, Ending and (fatal) Error.
 */

/*
 * Initialization call.
 * Determine terminal mode.
 * Start up terminal and internal administration.
 * Return Yes if succeeded, No if trouble (which doesn't apply here).
 */
Visible int
trmstart(plines, pcols, pflags)
int *plines;
int *pcols;
int *pflags;
{
	static char setup = No;
	int err;

#ifdef TRACE
if (!setup) freopen("TRACE.DAT", "a", stderr);
fprintf(stderr, "\ttrmstart(&li, &co, &fl);\n");
#endif

	if (started)
		return TE_TWICE;

#ifdef GFX
	if (gfx_mode != TEXT_MODE)
		gfx_mode= SPLIT_MODE;
#endif

	if (!setup) {
		err= set_screen_up();
		if (err != TE_OK)
			return err;
		setup = Yes;
	}

	err= start_trm();		/* internal administration */
	if (err != TE_OK)
		return err;

	*plines = lines;
	*pcols = cols;
	*pflags = flags;

	set_handler();
	started = Yes;
	return TE_OK;
}

Hidden int
set_screen_up()
{
	int height;
	int width;
	int get_screen_env();
	int get_cols();

	height = width = 0;
	scr_mode = get_screen_env(&height, &width);

	switch (scr_mode) {
	case BIOS:
	case TE_OK:
		cols = get_cols();
		flags = HAS_STANDOUT|CAN_SCROLL;
		break;
	case ANSI:
		flags = HAS_STANDOUT;
		break;
	default:
		return scr_mode; /* Error flag */
	}

	/* allow x and y in environment variable SCREEN to override */
	if (height > 0)
		lines = height;
	if (width > 0)
		cols = width;
	return TE_OK;
}

Hidden int
get_screen_env(pheight, pwidth)
	int *pheight, *pwidth;
{
	string s;
	int mode;
	char screrr;
	string getenv();
	string strip();
	string skip();

	screrr = No;
	s = getenv("SCREEN");
	if (s == NULL)
		return BIOS;

	s = strip(s);
	switch (*s) {
	case '\0':
		return BIOS;
	case 'a':
		mode = ANSI;
		s = skip(s, "ansi");
		break;
	case 'A':
		mode = ANSI;
		s = skip(s, "ANSI");
		break;
	case 'b':
		mode = BIOS;
		s = skip(s, "bios");
		break;
	case 'B':
		mode = BIOS;
		s = skip(s, "BIOS");
		break;
	default:
		mode = BIOS;
		screrr = Yes;
	}

	/* *pheight and *pwidth were set to 0 above */
	s = strip(s);
	while (isdigit(*s)) {
		*pheight = *pheight * 10 + (*s++ - '0');
	}
	s = strip(s);
	while (isdigit(*s)) {
		*pwidth = *pwidth * 10 + (*s++ -'0');
	}
	s = strip(s);
	if (screrr || *s != '\0')
		return TE_BADTERM;

	return mode;
}

Hidden string strip(s)
string s;
{
	while (*s == ' ' || *s == '\t')
		++s;
	return s;
}

Hidden string skip(s, pat)
string s, pat;
{
	while (*s == *pat)
		++s, ++pat;
	return s;
}

Hidden int		/* initialise internal administration */
start_trm()
{
	register int y;

	if (line == 0) {
		if ((line = (char**) malloc(lines * sizeof(char*))) == NULL)
			return TE_NOMEM;
		for (y = 0; y < lines; y++) {
			if ((line[y] = malloc(cols * sizeof(char))) == NULL)
				return TE_NOMEM;
		}
	}
	if (lenline == 0) {
		if ((lenline = (intlet *)
				malloc(lines * sizeof(intlet))) == NULL)
			return TE_NOMEM;
	}

	trmundefined();
	return TE_OK;
}

/*
 * Termination call.
 * Beware that it might be called by a catched interrupt even in the middle
 * of trmstart()!
 */

Visible Procedure
trmend()
{
#ifdef TRACE
fprintf(stderr, "\ttrmend();\n");
#endif
	if (started && so_mode != Off)
		standend();
	if (scr_mode == ANSI) {
		VOID fflush(stdout);
	}

	started = No;
}

/*
 * Set all internal statuses to undefined, especially the contents of
 * the screen, so a hard redraw will not be optimised to heaven.
 */

Visible Procedure
trmundefined()
{
	register int y, x;
#ifdef TRACE
fprintf(stderr, "\ttrmundefined();\n");
#endif

	cur_y = cur_x = Undefined;
	so_mode = Undefined;

	for (y = 0; y < lines; y++) {
		for (x = 0; x < cols; x++)
			line[y][x] = ' ';
			/* they may get printed in scrolling */
		lenline[y] = 0;
	}
}

#ifdef DEBUG
Hidden Procedure
check_started(m)
	char *m;
{
	if (!started) {
		printf("Not started: %s\n", m);
		exit(TE_TWICE);
	}
}
#else
#define check_started(m) /*empty*/
#endif

/*
 * Sensing the cursor.
 * (NOT IMPLEMENTED, since there is no way to locally move the cursor.)
 */

/*
 * Sense the current (y, x) cursor position, after a possible manual
 * change by the user with local cursor motions.
 * If the terminal cannot be asked for the current cursor position,
 * or if the string returned by the terminal is garbled,
 * the position is made Undefined.
 */
Visible Procedure
trmsense(py, px)
	int *py;
	int *px;
{
/*	bool getpos(); */
#ifdef TRACE
fprintf(stderr, "\ttrmsense(&yy, &xx);\n");
#endif
	check_started(MESS(7904, "trmsense called outside trmstart/trmend"));

	*py = *px = Undefined;

/*
 *	if (flags&CAN_SENSE && getpos(py, px)) {
 *		if (*py < 0 || lines <= *py || *px < 0 || cols <= *px)
 *			*py = *px = Undefined;
 *	}
 */
	cur_y = *py;
	cur_x = *px;
}

/*
 * Putting data on the screen.
 */

/*
 * Fill screen area with given data.
 * Characters with the SO-bit (0200) set are put in standout mode.
 * (Unfortunately this makes it impossible to display accented characters.
 * The interface should change.)
 */
Visible Procedure
trmputdata(yfirst, ylast, indent, data)
int yfirst;
int ylast;
register int indent;
register string data;
{
	register int y;
	int x, len, lendata, space;

#ifdef TRACE
fprintf(stderr, "\ttrmputdata(%d, %d, %d, \"%s\");\n", yfirst, ylast, indent, data);
#endif
	check_started(MESS(7905, "trmputdata called outside trmstart/trmend"));

	if (yfirst < 0)
		yfirst = 0;
	if (ylast >= lines)
		ylast = lines-1;
	space = cols*(ylast-yfirst+1) - indent;
	if (space <= 0)
		return;
	yfirst += indent/cols;
	indent %= cols;
	y = yfirst;
	if (data) {
		x = indent;
		lendata = strlen(data);
		if (ylast == lines-1 && lendata >= space)
			lendata = space - 1;
		len = Min(lendata, cols-x);
		while (y <= ylast) {
			put_line(y, x, data, len);
			y++;
			lendata -= len;
			if (lendata > 0) {
				x = 0;
				data += len;
				len = Min(lendata, cols);
			}
			else
				break;
		}
	}
	if (y <= ylast)
		clear_lines(y, ylast);
}

/*
 * We will try to get the picture:
 *
 *		    op>>>>>>>>>>>op				       oq
 *		    ^		 ^				       ^
 *	     <xskip><-----m1----><---------------od-------------------->
 *   OLD:   "You're in a maze of twisty little pieces of code, all alike"
 *   NEW:	   "in a maze of little twisting pieces of code, all alike"
 *		    <-----m1----><----------------nd--------------------->
 *		    ^		 ^					 ^
 *		    np>>>>>>>>>>>np					 nq
 * where
 *	op, oq, np, nq are pointers to start and end of Old and New data,
 * and
 *	xskip = length of indent to be skipped,
 *	m1 = length of Matching part at start,
 *	od = length of Differing end on screen,
 *	nd = length of Differing end in data to be put.
 */
Hidden int
put_line(y, xskip, data, len)
int y, xskip;
string data;
int len;
{
	register char *op, *oq, *np, *nq;
	int m1, od, nd, delta;

	/* calculate the magic parameters */
	op = &line[y][xskip];
	oq = &line[y][lenline[y]-1];
	np = data;
	nq = data + len - 1;
	m1 = 0;
	while ((*op&SOCHAR) == (*np&SOCHAR) && op <= oq && np <= nq)
		op++, np++, m1++;
	od = oq - op + 1;
	nd = nq - np + 1;
	/* now we have the picture above */

	if (od==0 && nd==0)
		return;

	delta = nd - od;
	move(y, xskip + m1);
	if (nd > 0) {
		put_str(np, nd);
	}
	if (delta < 0) {
		clr_to_eol();
		return;
	}
	lenline[y] = xskip + len;
	if (cur_x == cols) {
		cur_y++;
		cur_x = 0;
	}
}

/*
 * Scrolling (part of) the screen up (or down, dy<0).
 */

Visible Procedure
trmscrollup(yfirst, ylast, by)
register int yfirst;
register int ylast;
register int by;
{
#ifdef TRACE
fprintf(stderr, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by);
#endif
	check_started(MESS(7906, "trmscrollup called outside trmstart/trmend"));

	if (by == 0)
		return;

	if (yfirst < 0)
		yfirst = 0;
	if (ylast >= lines)
		ylast = lines-1;

	if (yfirst > ylast)
		return;

	if (so_mode != Off)
		standend();

	if (by > 0 && yfirst + by > ylast
	    ||
	    by < 0 && yfirst - by > ylast)
	{
		clear_lines(yfirst, ylast);
		return;
	}

	switch (scr_mode) {
	case BIOS:
		biosscrollup(yfirst, ylast, by);
		break;
	case ANSI:
		if (by > 0 && yfirst == 0) {
			lf_scroll(ylast, by);
		}
		else if (by > 0) {
			move_lines(yfirst+by, yfirst, ylast-yfirst+1-by, 1);
			clear_lines(ylast-by+1, ylast);
		}
		else {
			move_lines(ylast+by, ylast, ylast-yfirst+1+by, -1);
			clear_lines(yfirst, yfirst-by-1);
		}
		break;
	}
}

/*
 * Synchronization, move cursor to given position (or previous if < 0).
 */

Visible Procedure
trmsync(y, x)
	int y;
	int x;
{
#ifdef TRACE
fprintf(stderr, "\ttrmsync(%d, %d);\n", y, x);
#endif
	check_started(MESS(7907, "trmsync called outside trmstart/trmend"));

	if (0 <= y && y < lines && 0 <= x && x < cols) {
		move(y, x);
	}
	VOID fflush(stdout);
}

/*
 * Send a bell, visible if possible.
 */

Visible Procedure
trmbell()
{
#ifdef TRACE
fprintf(stderr, "\ttrmbell();\n");
#endif
	check_started(MESS(7908, "trmbell called outside trmstart/trmend"));
	ring_bell();
}

/*
 * Now for the real work: here are all low level routines that really
 * differ for BIOS or ANSI mode.
 */

/*
 * BIOS video io is called by generating an 8086 software interrupt,
 * using lattice's int86() function.
 * To ease coding, all routines fill in the apropriate parameters in regs,
 * and than call bios10(code), where code is to be placed in ah.
 */

Hidden union REGS regs, outregs;

/* A macro for speed  */
#define bios10(code) (regs.h.ah = (code), int86(0x10, &regs, &regs))
#define nbios10(code) (regs.h.ah = (code), int86(0x10, &regs, &outregs))

/* Video attributes: (see the BASIC manual) (used for standout mode) */

Hidden int video_attr;
#ifndef GFX
#define V_NORMAL 7
#else
#define V_NORMAL (gfx_mode == TEXT_MODE ? 7 : 0)
#endif
#define V_STANDOUT (7<<4)

/* Some BIOS only routines */

Hidden get_cols()
{
	bios10(15);
	return regs.h.ah;
}

/*
 * ANSI escape sequences
 */
#define A_CUP	"\033[%d;%dH"   /* cursor position */
#define A_SGR0	"\033[0m"       /* set graphics rendition to normal */
#define A_SGR7	"\033[7m"       /* set graphics rendition to standout */
#define A_ED	"\033[2J"       /* erase display (and cursor home) */
#define A_EL	"\033[K"        /* erase (to end of) line */

/*
 * The following routine is the time bottleneck, I believe!
 */

Hidden Procedure
put_str(data, n)
char *data;
int n;
{
	register char c, so;

	so = so_mode;
	if (scr_mode == BIOS) {
		regs.x.cx = 1;	/* repition count */
		regs.h.bh = 0;	/* page number */
		regs.h.bl = video_attr;
		while (--n >= 0) {
			c = (*data++)&SOCHAR;
			if ((c&SOBIT) != so) {
				so = c&SOBIT;
				so ? standout() : standend();
				regs.h.bl = video_attr;
			}
			regs.h.al = c&CHAR;
			nbios10(9);
			if (cur_x >= cols-1) {
				line[cur_y][cols-1] = c;
				continue;
			}
			regs.h.dh = cur_y;
			regs.h.dl = cur_x + 1;
			nbios10(2);
			line[cur_y][cur_x] = c;
			cur_x++;
		}
	}
	else {
		while (--n >= 0) {
			c = (*data++)&SOCHAR;
			if ((c&SOBIT) != so) {
				so = c&SOBIT;
				so ? standout() : standend();
			}
			putch(c&CHAR);
			line[cur_y][cur_x] = c;
			cur_x++;
		}
	}
}

/*
 * Move to position y,x on the screen
 */

Hidden Procedure
move(y, x)
int y, x;
{
	if (scr_mode != BIOS && cur_y == y && cur_x == x)
		return;
	switch (scr_mode) {
	case BIOS:
		regs.h.dh = y;
		regs.h.dl = x;
		regs.h.bh = 0; /* Page; must be 0 for graphics */
		bios10(2);
		break;
	case ANSI:
		cprintf(A_CUP, y+1, x+1);
		break;
	}
	cur_y = y;
	cur_x = x;
}

Hidden Procedure
standout()
{
	so_mode = On;
	switch (scr_mode) {
	case BIOS:
		video_attr = V_STANDOUT;
		break;
	case ANSI:
		cputs(A_SGR7);
		break;
	}
}

Hidden Procedure
standend()
{
	so_mode = Off;
	switch (scr_mode) {
	case BIOS:
		video_attr = V_NORMAL;
		break;
	case ANSI:
		cputs(A_SGR0);
		break;
	}
}

#ifdef UNUSED
Hidden Procedure
put_c(c)
int c;
{
	int ch;

	ch = c&CHAR;
#ifndef NDEBUG
	if (!isprint(ch)) {
		ch = '?';
		c = (c&SOBIT)|'?';
	}
#endif
	switch (scr_mode) {
	case BIOS:
		regs.h.al = ch;
		regs.h.bl = video_attr;
		regs.x.cx = 1;	/* repition count */
		regs.h.bh = 0;	/* page number */
		bios10(9);
		if (cur_x >= cols-1) {
			line[cur_y][cols-1] = c;
			return;
		}
		regs.h.dh = cur_y;
		regs.h.dl = cur_x + 1;
		bios10(2);
		break;
	case ANSI:
		putch(ch);
		break;
	}
	line[cur_y][cur_x] = c;
	cur_x++;
}
#endif UNUSED

Hidden Procedure
clear_lines(yfirst, ylast)
int yfirst, ylast ;
{
	register int y;

	if (scr_mode == BIOS) {
		regs.h.al = 0;	/* scroll with al = 0 means blank window */
		regs.h.ch = yfirst;
		regs.h.cl = 0;
		regs.h.dh = ylast;
		regs.h.dl = cols-1;
		regs.h.bh = V_NORMAL;
		bios10(6);
		for (y = yfirst; y <= ylast; y++)
			lenline[y] = 0;
		return;
	}
	/* scr_mode == ANSI */
	if (yfirst == 0 && ylast == lines-1) {
		if (so_mode == On)
			standend();
		move(0, 0);		/* since some ANSI'd don't move */
		cputs(A_ED);
		cur_y = cur_x = 0;
		for (y = yfirst; y < ylast; y++)
			lenline[y] = 0;
		return;
	}
	for (y = yfirst; y <= ylast; y++) {
		if (lenline[y] > 0) {
			move(y, 0);
			clr_to_eol();
		}
	}
}

Hidden Procedure
clr_to_eol()
{
	if (so_mode == On)
		standend();
	switch (scr_mode) {
	case BIOS:
		regs.h.bh = 0;	/* page */
		regs.x.cx = lenline[cur_y] - cur_x;
		regs.h.al = ' ';
		regs.h.bl = V_NORMAL;
		bios10(9);
		break;
	case ANSI:
		cputs(A_EL);
		break;
	}
	lenline[cur_y] = cur_x;
}

Hidden Procedure		/* scrolling for BIOS */
biosscrollup(yfirst, ylast, by)
int yfirst;
int ylast;
int by;
{
	regs.h.al = (by < 0 ? -by : by);
	regs.h.ch = yfirst;
	regs.h.cl = 0;
	regs.h.dh = ylast;
	regs.h.dl = cols-1;
	regs.h.bh= V_NORMAL;
	bios10(by < 0 ? 7 : 6);
	cur_y = cur_x = Undefined;
	if (by > 0)
		scr_lines(yfirst, ylast, by, 1);
	else
		scr_lines(ylast, yfirst, -by, -1);
}

Hidden Procedure		/* Reset internal administration accordingly */
scr_lines(yfrom, yto, n, dy)
int yfrom, yto, n, dy;
{
	register int y, x;
	char *saveln;

	while (n-- > 0) {
		saveln = line[yfrom];
		for (y = yfrom; y != yto; y += dy) {
			line[y] = line[y+dy];
			lenline[y] = lenline[y+dy];
		}
		line[yto] = saveln;
		for (x = 0; x < cols; x++ )
			line[yto][x] = ' ';
		lenline[yto] = 0;
	}
}

Hidden Procedure
lf_scroll(yto, by)
int yto;
int by;
{
	register int n = by;

	move(lines-1, 0);
	while (n-- > 0) {
		putch('\n');
	}
	scr_lines(0, lines-1, by, 1);
	move_lines(lines-1-by, lines-1, lines-1-yto, -1);
	clear_lines(yto-by+1, yto);
}

Hidden Procedure		/* for dumb scrolling, uses and updates */
move_lines(yfrom, yto, n, dy)	/* internal administration */
int yfrom;
int yto;
int n;
int dy;
{
	while (n-- > 0) {
		put_line(yto, 0, line[yfrom], lenline[yfrom]);
		yfrom += dy;
		yto += dy;
	}
}

Hidden Procedure ring_bell()
{
	switch (scr_mode) {
	case BIOS:
		regs.h.al = '\007';
		regs.h.bl = V_NORMAL;
		bios10(14);
		break;
	case ANSI:
		putch('\007');
		break;
	}
}

/*
 * Show the current internal statuses of the screen on stderr.
 * For debugging only.
 */

#ifdef SHOW
Visible Procedure
trmshow(s)
char *s;
{
	int y, x;

	fprintf(stderr, "<<< %s >>>\n", s);
	for (y = 0; y < lines; y++) {
		for (x = 0; x <= lenline[y] /*** && x < cols ***/ ; x++) {
			fputc(line[y][x]&CHAR, stderr);
		}
		fputc('\n', stderr);
		for (x = 0; x <= lenline[y] && x < cols-1; x++) {
			if (line[y][x]&SOBIT)
				fputc('-', stderr);
			else
				fputc(' ', stderr);
		}
		fputc('\n', stderr);
	}
	fprintf(stderr, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x);
	VOID fflush(stderr);
}
#endif

/*
 * Interrupt handling.
 *
 * (This has not properly been tested, nor is it clear that
 * this interface is what we want.  Anyway, it's here for you
 * to experiment with.  What does it do, you may ask?
 * Assume an interactive program which reads its characters
 * through trminput.  Assume ^C is the interrupt character.
 * Normally, ^C is treated just like any other character: when
 * typed, it turns up in the input.  The program may understand
 * input ^C as "quit from the current mode".
 * Occasionally, the program goes into a long period of computation.
 * Now it would be uninterruptible, except if it calls trminterrupt
 * at times in its computational loop.  Trminterrupt magically looks
 * ahead in the input queue, and if it sees a ^C, discards all input
 * before that point and returns Yes.  It also sets a flag, so that
 * the interupt "sticks around" until either trminput or trmavail
 * is called.  It is undefined whether typing ^C several times in
 * a row is seen as one interrupt, or an interrupt followed by input
 * of ^C's.  A program should be prepared for either.)
 */

static bool intrflag= No;

static
handler(sig)
	int sig;
{
	signal(sig, handler);
	intrflag= Yes;
}

static
set_handler()
{
	signal(SIGINT, handler);
}

bool
trminterrupt()
{
	/* Force a check for Control-break which will call handler. */
	kbhit();
	return intrflag;
}

/*
 * Terminal input without echo.
 * (This is a recent addition to this module, but will soon be standard).
 */

trminput()
{
	intrflag= No;
	return bdos(0x7, 0, 0) & 0377; /* Input, no echo, no ^C checks */
}

/*
 * Check for character available.
 *
 * To do this properly, should call DOS function 6,
 * but the relevant bit (the Z flag) can't be checked from C.
 * For now, say we don't know.
 */

trmavail()
{
	intrflag= No;
	return -1;
}

trmsuspend()
{
	/* Not implementable on MS-DOS */
}