|
|
BSD 4.3
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
static char rcsid[] = "$Header: /var/lib/cvsd/repos/CSRG/43BSD/contrib/B/src/bed/vtrm.c,v 1.1.1.1 2018/04/24 16:12:54 root Exp $";
/* History:
* 21-aug-85 GvR added support for AL and DL (parametrized al and dl).
* The Epoch tk created and modified.
*/
/*
* Virtual TeRMinal package.
*
* This package uses termcap to determine the terminal capabilities.
*
* 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:
*
* trmstart(&lines, &cols, &flags)
* Obligatory initialization call (sets tty modes etc.),
* Returns the height and width of the screen to the integers
* whose addresses are passed as parameters, and a flag that
* describes some capabilities.
* Function return value: Yes if all went well, No if the terminal
* is not supported. An error message has already been displayed.
*
* trmundefined()
* Sets internal representation of screen and attributes to undefined.
* This is necessary for a hard redraw, which would get optimised to
* oblivion,
*
* trmsense(&y, &x)
* Returns the cursor position through its parameters
* after a possible manual change by the user.
*
* trmputdata(yfirst, ylast, indent, data)
* Fill lines {yfirst..ylast} with data, after skipping the initial
* 'indent' positions. It is assumed that these positions do not contain
* anything dangerous (like standout cookies or null characters).
*
* trmscrollup(yfirst, ylast, by)
* Shift lines {yfirst..ylast} up by lines (down |by| if by < 0).
*
* trmsync(y, x)
* Call to output data to the terminal and set cursor position.
*
* trmbell()
* Send a (possibly visible) bell, immediately (flushing stdout).
*
* trmend()
* Obligatory termination call (resets tty modes etc.).
*
* You may call these as one or more cycles of:
* + trmstart
* + zero or more times any of the other routines
* + trmend
* To catch interrupts and the like, you may call trmend even in the middle
* of trmstart.
*/
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* Includes and data definitions. */
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include <stdio.h>
#include <setjmp.h>
#ifndef TERMIO
#include <sgtty.h>
#else
#include <termio.h>
#endif TERMIO
#include <signal.h>
#include <ctype.h> /* for isprint() */
#include "vtrm.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 ((bool) 1)
#define No ((bool) 0)
#define Min(a,b) ((a) <= (b) ? (a) : (b))
/* tty modes */
#ifndef TERMIO
/* v7/BSD tty control */
Hidden struct sgttyb oldtty, newtty;
/* to enable type ahead for abled persons on systems that provide this: */
#ifdef TIOCSETN
#define stty(fd,bp) VOID ioctl(fd, TIOCSETN, bp)
#endif
#else
/* AT&T tty control */
Hidden struct termio oldtty, newtty;
#define gtty(fd,bp) ioctl(fd, TCGETA, bp)
#define stty(fd,bp) VOID ioctl(fd, TCSETAW, bp)
#endif TERMIO
Hidden bool know_ttys = No;
/* visible data for termcap */
char PC;
char *BC;
char *UP;
short ospeed;
Forward int outchar(); /* procedure for termcap's tputs */
#define Putstr(str) tputs((str), 1, outchar)
extern char *tgoto();
/* termcap terminal capabilities */
Hidden int lines;
Hidden int cols;
Hidden bool has_am; /* has automatic margins */
Hidden bool has_da; /* display may be retained above screen */
Hidden bool has_db; /* display may be retained below screen */
Hidden bool has_in; /* not save to have null chars on the screen */
Hidden bool has_mi; /* move safely in insert (and delete?) mode */
Hidden bool has_ms; /* move safely in standout mode */
Hidden bool has_xs; /* standout not erased by overwriting */
Hidden char *al_str; /* add new blank line */
Hidden char *par_al_str; /* parametrized al (AL) */
Hidden char *cd_str; /* clear to end of display */
Hidden char *ce_str; /* clear to end of line */
Hidden char *cl_str; /* cursor home and clear screen */
Hidden char *cm_str; /* cursor motion */
Hidden char *cr_str; /* carriage return */
Hidden char *cs_str; /* change scrolling region */
Hidden char *dc_str; /* delete character */
Hidden char *dl_str; /* delete line */
Hidden char *par_dl_str; /* parametrized dl (DL) */
Hidden char *do_str; /* cursor down one line */
Hidden char *dm_str; /* enter delete mode */
Hidden char *ed_str; /* end delete mode */
Hidden char *ei_str; /* end insert mode */
Hidden char *ho_str; /* cursor home */
Hidden char *ic_str; /* insert character (iff necessary, maybe pad) */
Hidden char *im_str; /* enter insert mode */
Hidden char *le_str; /* cursor left */
Hidden char *nd_str; /* cursor right (non-destructive space) */
Hidden char *se_str; /* end standout mode */
Hidden char *sf_str; /* scroll text up (from bottom of region) */
Hidden char *so_str; /* begin standout mode */
Hidden char *sr_str; /* scroll text down (from top of region) */
Hidden char *te_str; /* end termcap */
Hidden char *ti_str; /* start termcap */
Hidden char *up_str; /* cursor up */
Hidden char *vb_str; /* visible bell */
Hidden char *ve_str; /* make cursor visible again */
Hidden char *vi_str; /* make cursor invisible */
/* sense cursor position, addition to termcap */
Hidden char *cp_str; /* format of returned Cursor Position string */
Hidden char *sp_str; /* Sense cursor Position from terminal */
/* terminal status */
/* calling order of Visible Procs */
Hidden bool started = No;
/* to exports the capabilities mentioned in vtrm.h: */
Hidden int flags = 0;
/* cost for impossible operations */
#define Infinity 9999
/* Allow for adding Infinity+Infinity within range */
/* (Range is assumed at least 2**15 - 1) */
/* The following for all sorts of undefined things (except for UNKNOWN char) */
#define Undefined (-1)
/* current mode of putting char's */
#define Normal 0
#define Insert 1
#define Delete 2
Hidden short mode = Normal;
/* current standout mode */
#define Off 0
#define On 0200
Hidden short so_mode = Off;
/* masks for char's and intlet's */
#define NULCHAR '\000'
#define CHAR 0177
#define SOBIT On
#define SOCHAR 0377
/* if (has_xs) record cookies placed on screen in extra bit */
/* type of cookie is determined by the SO bit */
#define XSBIT 0400
#define SOCOOK 0600
#define COOKBITS SOCOOK
#define UNKNOWN 1
#define NOCOOK UNKNOWN
/* current cursor position */
Hidden intlet cur_y = Undefined, cur_x = Undefined;
/* "line[y][x]" holds the char on the terminal, with the SOBIT and XSBIT.
* the SOBIT tells whether the character is standing out, the XSBIT whether
* there is a cookie on the screen at this position.
* In particular a standend-cookie may be recorded AFTER the line
* (just in case some trmputdata will write after that position).
* "lenline[y]" holds the length of the line.
* Unknown chars will be 1, so the optimising compare in putline will fail.
* (Partially) empty lines are distinghuished by "lenline[y] < cols".
*/
Hidden intlet **line = 0, *lenline = 0;
/* Clear the screen initially iff only memory cursor addressing available */
Hidden bool mustclear = No;
/* Make the cursor invisible when trmsync() tries to move outside the screen */
Hidden bool no_cursor = No;
/* Optimise cursor motion */
Hidden int abs_cost; /* cost of absolute cursor motion */
Hidden int cr_cost; /* cost of carriage return */
Hidden int do_cost; /* cost of down */
Hidden int le_cost; /* cost of left */
Hidden int nd_cost; /* cost of right */
Hidden int up_cost; /* cost of up */
/* Optimise trailing match in put_line, iff the terminal can insert and delete
* characters; the cost per n characters will be:
* n * MultiplyFactor + OverHead
*/
Hidden int ins_mf, ins_oh, del_mf, del_oh;
Hidden int ed_cost, ei_cost; /* used in move() */
/* The type of scrolling possible determines which routines get used;
* these may be:
* (1) with addline and deleteline (termcap: al_str & dl_str);
* (2) with a settable scrolling region, like VT100 (cs_str, sr_str, sf_str);
* (3) no scrolling available. (NOT YET IMPLEMENTED)
*/
Hidden Procedure (*scr_up)();
Hidden Procedure (*scr_down)();
Forward Procedure scr1up();
Forward Procedure scr1down();
Forward Procedure scr2up();
Forward Procedure scr2down();
/*Forward Procedure scr3up(); */
/*Forward Procedure scr3down(); */
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* Starting, Ending and (fatal) Error. */
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/*
* Initialization call.
* Determine terminal capabilities from termcap.
* Set up tty modes.
* Start up terminal and internal administration.
* Return Yes if succeeded, No if trouble (e.g., bad terminal type).
*/
Visible int
trmstart(plines, pcols, pflags)
int *plines;
int *pcols;
int *pflags;
{
#ifdef TRACE
fprintf(stderr, "\ttrmstart(&li, &co, &fl);\n");
#endif
if (started)
trmerr("trmstart called twice in succession");
if (!gettermcaps())
return No;
if (!setttymode())
return No;
start_trm();
*plines = lines;
*pcols = cols;
*pflags = flags;
started = Yes;
return Yes;
}
/*
* Termination call.
* Reset tty modes, etc.
* 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
set_mode(Normal);
if (so_mode != Off)
standend();
Putstr(te_str);
VOID fflush(stdout);
resetttymode();
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;
mode = so_mode = Undefined;
for (y = 0; y < lines; y++) {
for (x = 0; x <= cols; x++)
line[y][x] = 1; /* impossible char, no so bits */
lenline[y] = cols;
}
}
/*
* Give an error message, and abort.
* The abort can be catched by the calling process.
*/
Hidden Procedure
trmerr(mess)
string mess;
{
trmreset();
fprintf(stderr,
"*** System error in screen output module:\n*** %s\n", mess);
VOID fflush(stderr);
abort();
}
/*
* Give an error message and reset the tty modes (but don't abort).
*/
Hidden Procedure trmmess(mess)
string mess;
{
trmreset();
fprintf(stderr, "*** Fatal error: %s\n", mess);
VOID fflush(stderr);
}
/*
* Complain about a missing terminal feature. Otherwise like trmmess.
*/
Hidden Procedure
trmsorry(mess)
string mess;
{
trmreset();
fprintf(stderr, (
#ifdef BED
"*** Sorry, this terminal isn't powerful enough to run the B editor.\n"
#else
"*** Sorry, this terminal isn't powerful emough.\n"
#endif
));
fprintf(stderr, "*** The problem is: %s.\n", mess);
#ifdef BED
fprintf(stderr,
"*** (You might try 'b -e' to use a standard editor instead.)\n");
#endif
VOID fflush(stderr);
}
/*
* Prepare for giving a (more or less fatal) error message.
*/
Hidden Procedure
trmreset()
{
if (started) {
move(lines-1, 0);
clear_lines(lines-1, lines-1);
}
VOID fflush(stdout);
resetttymode();
}
Hidden Procedure
check_started(m)
char *m;
{
char s[80];
if (!started) {
VOID sprintf(s, "%s called outside trmstart/trmend", m);
trmerr(s);
}
}
int ccc;
/*ARGSUSED*/
Hidden Procedure
countchar(ch)
char ch;
{
ccc++;
}
Hidden int
strcost(str)
char *str;
{
if (str == NULL)
return Infinity;
return str0cost(str);
}
Hidden int
str0cost(str)
char *str;
{
ccc = 0;
tputs(str, 1, countchar);
return ccc;
}
Hidden int
gettermcaps() /* get terminal capabilities from termcap
* and related static properties
*/
{
string trmname;
char tc_buf[1024];
static char strbuf[1024];
char *area = strbuf;
char *xPC;
char *getenv();
int tgetent();
int tgetnum();
int tgetflag();
char *tgetstr();
int sg;
static bool tc_initialized = No;
#ifdef TIOCGWINSZ
struct winsize win;
#endif
if (tc_initialized)
return Yes;
if ((trmname=getenv("TERM")) == NULL) {
trmmess("terminal type not exported in $TERM variable");
return No;
}
if (tgetent(tc_buf, trmname) != 1) {
trmmess("unknown terminal type in $TERM envariable");
return No;
}
if (tgetflag("hc")) {
trmsorry("can't use a hardcopy terminal");
return No;
}
BC = tgetstr("le", &area);
if (BC == NULL)
BC = tgetstr("bc", &area);
if (BC == NULL)
if (tgetflag("bs"))
BC="\b";
else {
trmsorry("no LEFT cursor motion");
return No;
}
UP = tgetstr("up", &area);
if (UP == NULL) {
trmsorry("no UP cursor motion");
return No;
}
xPC = tgetstr("pc", &area);
PC = (xPC != NULL? xPC[0] : NULCHAR);
ho_str = tgetstr("ho", &area);
do_str = tgetstr("do", &area);
nd_str = tgetstr("nd", &area);
cm_str = tgetstr("cm", &area);
if (cm_str == NULL) {
cm_str = tgetstr("CM", &area);
if (cm_str == NULL) {
if (ho_str == NULL || do_str == NULL || nd_str == NULL) {
trmsorry("no absolute cursor motion");
return No;
}
}
else
mustclear = Yes;
}
al_str = tgetstr("al", &area);
dl_str = tgetstr("dl", &area);
par_al_str = tgetstr("AL", &area);
par_dl_str = tgetstr("DL", &area);
if (al_str && dl_str) {
scr_up = scr1up;
scr_down = scr1down;
flags |= CAN_SCROLL;
}
else {
cs_str = tgetstr("cs", &area);
sf_str = tgetstr("sf", &area);
if (sf_str == NULL)
sf_str = "\n";
sr_str = tgetstr("sr", &area);
if (cs_str && sr_str) {
scr_up = scr2up;
scr_down = scr2down;
flags |= CAN_SCROLL;
}
else {
trmsorry("can't scroll");
return No;
}
}
lines = tgetnum("li");
cols = tgetnum("co");
#ifdef TIOCGWINSZ
if (ioctl (0, TIOCGWINSZ, &win) == 0) {
if (win.ws_col)
cols = win.ws_col;
if (win.ws_row)
lines = win.ws_row;
}
#endif
if (lines == -1) lines = 24;
if (cols == -1) cols = 80;
has_am = tgetflag("am");
has_db = tgetflag("db");
has_in = tgetflag("in");
has_mi = tgetflag("mi");
has_ms = tgetflag("ms");
has_xs = tgetflag("xs");
if ((sg=tgetnum("sg")) == 0)
has_xs = Yes;
else if (sg > 0) {
trmsorry("video attributes take up space on the screen");
return No;
}
cd_str = tgetstr("cd", &area);
ce_str = tgetstr("ce", &area);
if (!ce_str) {
trmsorry("can't clear to end of line");
return No;
}
cl_str = tgetstr("cl", &area);
cr_str = tgetstr("cr", &area);
if (cr_str == NULL) cr_str = "\r";
dc_str = tgetstr("dc", &area);
dm_str = tgetstr("dm", &area);
if (do_str == NULL) do_str = tgetstr("nl", &area);
if (do_str == NULL) do_str = "\n";
ed_str = tgetstr("ed", &area);
ei_str = tgetstr("ei", &area);
ic_str = tgetstr("ic", &area);
im_str = tgetstr("im", &area);
le_str = BC;
se_str = tgetstr("se", &area);
so_str = tgetstr("so", &area);
te_str = tgetstr("te", &area);
ti_str = tgetstr("ti", &area);
up_str = UP;
vb_str = tgetstr("vb", &area);
if (vb_str == NULL) /* then we will do with the audible bell */
vb_str = "\007";
ve_str = tgetstr("ve", &area);
vi_str = tgetstr("vi", &area);
/* cursor sensing (non standard) */
cp_str = tgetstr("cp", &area);
sp_str = tgetstr("sp", &area);
if (cp_str != NULL && sp_str != NULL)
flags |= CAN_SENSE;
if (so_str != NULL && se_str != NULL)
flags |= HAS_STANDOUT;
/* calculate costs of local and absolute cursor motions */
if (cm_str == NULL)
abs_cost = Infinity;
else
abs_cost = strcost(tgoto(cm_str, 0, 0));
cr_cost = strcost(cr_str);
do_cost = strcost(do_str);
le_cost = strcost(le_str);
nd_cost = strcost(nd_str);
up_cost = strcost(up_str);
/* cost of leaving insert or delete mode, used in move() */
ei_cost = str0cost(ei_str);
ed_cost = str0cost(ed_str);
/* calculate insert and delete cost multiply_factor and overhead */
if (((im_str && ei_str) || ic_str) && dc_str) {
flags |= CAN_OPTIMISE;
ins_mf = 1 + str0cost(ic_str);
ins_oh = str0cost(im_str) + ei_cost;
del_mf = str0cost(dc_str);
del_oh = str0cost(dm_str) + ed_cost;
}
tc_initialized = Yes;
return Yes;
}
Hidden int
setttymode()
{
if (!know_ttys) {
if (gtty(1, &oldtty) != 0 || gtty(1, &newtty) != 0) {
trmmess("can't get tty modes (output not a terminal)");
return No;
}
#ifndef TERMIO
ospeed = oldtty.sg_ospeed;
#ifdef PWB
newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS)
| RAW;
#else PWB
newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS)
| CBREAK;
#endif PWB
#else TERMIO
ospeed= oldtty.c_lflag & CBAUD;
newtty.c_iflag &= ~ICRNL; /* No CR->NL mapping on input */
newtty.c_oflag &= ~ONLCR; /* NL doesn't output CR */
newtty.c_lflag &= ~(ICANON|ECHO); /* No line editing, no echo */
newtty.c_cc[VMIN]= 3; /* wait for 3 characters */
newtty.c_cc[VTIME]= 1; /* or 0.1 sec. */
#endif TERMIO
know_ttys = Yes;
}
stty(1, &newtty);
return Yes;
}
Hidden Procedure
resetttymode()
{
if (know_ttys)
stty(1, &oldtty);
}
Hidden char*
lalloc(size)
unsigned size;
{
char *l;
char *malloc();
l = malloc(size);
if (l == NULL)
trmerr("not enough memory for screen buffer");
return l;
}
Hidden Procedure
start_trm()
{
register int y;
if (line == 0) {
line = (intlet**) lalloc((unsigned) lines * sizeof(intlet*));
for (y = 0; y < lines; y++)
line[y] = (intlet*) lalloc((unsigned) ((cols+1)*sizeof(intlet)));
}
if (lenline == 0)
lenline = (intlet*) lalloc((unsigned) lines * sizeof(intlet));
trmundefined();
Putstr(ti_str);
if (cs_str)
Putstr(tgoto(cs_str, lines-1, 0));
if (mustclear)
clear_lines(0, lines-1);
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* Sensing and moving 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("trmsense");
*py = *px = Undefined;
set_mode(Normal);
if (so_mode != Off)
standend();
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;
}
Hidden bool
getpos(py, px)
int *py, *px;
{
char *format = cp_str;
int fc; /* current format character */
int ic; /* current input character */
int num;
int on_y = 1;
bool incr_orig = No;
int i, ni;
Putstr(sp_str);
VOID fflush(stdout);
while (fc = *format++) {
if (fc != '%') {
if (getchar() != fc)
return No;
}
else {
switch (fc = *format++) {
case '%':
if (getchar() != '%')
return No;
continue;
case 'r':
on_y = 1 - on_y;
continue;
case 'i':
incr_orig = Yes;
continue;
case 'd':
ic = getchar();
if (!isdigit(ic))
return No;
num = ic - '0';
while (isdigit(ic=getchar()))
num = 10*num + ic - '0';
VOID ungetc(ic, stdin);
break;
case '2':
case '3':
ni = fc - '0';
num = 0;
for (i=0; i<ni; i++) {
ic = getchar();
if (isdigit(ic))
num = 10*num + ic - '0';
else
return No;
}
break;
case '+':
num = getchar() - *format++;
break;
case '-':
num = getchar() + *format++;
break;
default:
return No;
}
/* assign num to parameter */
if (incr_orig)
num--;
if (on_y)
*py = num;
else
*px = num;
on_y = 1 - on_y;
}
}
return Yes;
}
/*
* To move over characters by rewriting them, we have to check:
* (1) that the screen has been initialised on these positions;
* (2) we do not screw up characters
* when rewriting line[y] from x_from upto x_to
*/
Hidden bool
rewrite_ok(y, xfrom, xto)
int y, xfrom, xto;
{
register intlet *plnyx, *plnyto;
if (xto > lenline[y])
return No;
plnyto = &line[y][xto];
for (plnyx = &line[y][xfrom]; plnyx <= plnyto; plnyx++)
if (*plnyx == UNKNOWN
||
(!has_xs && (*plnyx & SOBIT) != so_mode)
)
return No;
return Yes;
}
/*
* Move to position y,x on the screen
*/
/* possible move types for y and x respectively: */
#define None 0
#define Down 1
#define Up 2
#define Right 1
#define ReWrite 2
#define Left 3
#define CrWrite 4
Hidden Procedure
move(y, x)
int y, x;
{
int dy, dx;
int y_cost, x_cost, y_move, x_move;
int mode_cost;
int xi;
if (cur_y == y && cur_x == x)
return;
if (!has_mi || mode == Undefined)
set_mode(Normal);
if (!has_xs && ((!has_ms && so_mode != Off) || so_mode == Undefined))
standend();
if (cur_y == Undefined || cur_x == Undefined)
goto absmove;
dy = y - cur_y;
dx = x - cur_x;
if (dy > 0) {
y_move = Down;
y_cost = dy * do_cost;
}
else if (dy < 0) {
y_move = Up;
y_cost = -dy * up_cost;
}
else {
y_move = None;
y_cost = 0;
}
if (y_cost < abs_cost) {
switch (mode) {
case Normal:
mode_cost = 0;
break;
case Insert:
mode_cost = ei_cost;
break;
case Delete:
mode_cost = ed_cost;
break;
}
if (dx > 0) {
x_cost = dx + mode_cost;
if (dx*nd_cost < x_cost || !rewrite_ok(y, cur_x, x)) {
x_cost = dx * nd_cost;
x_move = Right;
}
else
x_move = ReWrite;
}
else if (dx < 0) {
x_cost = -dx * le_cost;
x_move = Left;
}
else {
x_cost = 0;
x_move = None;
}
if (cr_cost + x + mode_cost < x_cost && rewrite_ok(y, 0, x)) {
x_move = CrWrite;
x_cost = cr_cost + x + mode_cost;
}
}
else
x_cost = abs_cost;
if (y_cost + x_cost < abs_cost) {
switch (y_move) {
case Down:
while (dy-- > 0) Putstr(do_str);
break;
case Up:
while (dy++ < 0) Putstr(up_str);
break;
}
switch (x_move) {
case Right:
while (dx-- > 0) Putstr(nd_str);
break;
case Left:
while (dx++ < 0) Putstr(le_str);
break;
case CrWrite:
Putstr(cr_str);
cur_x = 0;
/* FALL THROUGH */
case ReWrite:
set_mode(Normal);
for (xi = cur_x; xi < x; xi++)
putchar(line[y][xi]);
break;
}
}
else
{
absmove:
if (cm_str == NULL) {
Putstr(ho_str);
for (cur_y = 0; cur_y < y; ++cur_y)
Putstr(do_str);
/* Should try to use tabs here: */
for (cur_x = 0; cur_x < x; ++cur_x)
Putstr(nd_str);
}
else
Putstr(tgoto(cm_str, x, y));
}
cur_y = y;
cur_x = x;
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* Putting data on the screen. */
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/*
* Fill screen area with given data.
* Characters with the SO-bit (0200) set are put in standout mode.
*/
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("trmputdata");
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;
if (data) {
x = indent;
lendata = strlen(data);
if (ylast == lines-1 && lendata >= space)
lendata = space - 1;
len = Min(lendata, cols-x);
for (y = yfirst; 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 first try to get the picture:
*
* op>>>>>>>>>>>op oq<<<<<<<<<<<<<<<<<<<<<<<<oq
* ^ ^ ^ ^
* <xskip><-----m1----><----od-----><-----------m2----------->
* 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------><-----------m2----------->
* ^ ^ ^ ^
* np>>>>>>>>>>>np nq<<<<<<<<<<<<<<<<<<<<<<<<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 mid on screen,
* nd = length of Differing mid in data to be put,
* m2 = length of Matching trail.
*
* Then we will try to find a long blank-or-cleared piece in <nd+m2>:
*
* <---m1---><---d1---><---nb---><---d2---><---m2--->
* ^ ^ ^ ^ ^
* np bp bq1 nq nend
* where
* bp, bq are pointers to start and AFTER end of blank piece,
* and
* d1 = length of differing part before blank piece,
* nb = length of blank piece to be skipped,
* d2 = length of differing part after blank piece.
* Remarks:
* d1 + nb + d2 == nd,
* and
* d2 maybe less than 0.
*/
Hidden int
put_line(y, xskip, data, len)
int y, xskip;
string data;
int len;
{
register intlet *op, *oq;
register char *np, *nq, *nend;
char *bp, *bq1, *p, *q;
int m1, m2, od, nd, delta, dd, d1, nb, d2;
bool skipping;
int cost, o_cost; /* normal and optimising cost */
/* calculate the magic parameters */
op = &line[y][xskip];
oq = &line[y][lenline[y]-1];
np = data;
nq = nend = data + len - 1;
m1 = m2 = 0;
while ((*op&SOCHAR) == (((intlet)*np)&SOCHAR) && op <= oq && np <= nq)
op++, np++, m1++;
if (flags & CAN_OPTIMISE)
while ((*oq&SOCHAR) == (((intlet)*nq)&SOCHAR) && op <= oq && np <= nq)
oq--, nq--, m2++;
od = oq - op + 1;
nd = nq - np + 1;
/* now we have the first picture above */
if (od==0 && nd==0)
return;
delta = nd - od;
/* find the blank piece */
p = q = bp = bq1 = np;
oq += m2; /* back to current eol */
if (!has_in) {
while (p <= nend) {
while (q<=nend && *q==' ' && (op>oq || *op==' '))
q++, op++;
if (q - p > bq1 - bp)
bp = p, bq1 = q;
p = ++q;
op++;
}
}
d1 = bp - np;
nb = bq1 - bp;
d2 = nq - bq1 + 1;
/* what is cheapest:
* normal: put nd+m2; (dd = nd+m2)
* skipping: put d1, skip nb, put d2+m2; (dd = d2+m2)
* optimise: put dd, insert or delete delta. (dd = min(od,nd))
*/
cost = nd + m2; /* normal cost */
if (nb > abs_cost || (d1 == 0 && nb > 0)) {
skipping = Yes;
cost -= nb - (d1>0 ? abs_cost : 0); /* skipping cost */
dd = d2;
}
else {
skipping = No;
dd = nd;
}
if (m2 != 0) {
/* try optimising */
o_cost = Min(od, nd);
if (delta > 0)
o_cost += delta * ins_mf + ins_oh;
else if (delta < 0)
o_cost += -delta * del_mf + del_oh;
if (o_cost >= cost) {
/* discard m2, no optimise */
dd += m2;
m2 = 0;
}
else {
dd = Min(od, nd);
skipping = No;
}
}
/* and now for the real work */
if (!skipping || d1 > 0)
move(y, xskip + m1);
if (has_xs)
get_so_mode();
if (skipping) {
if (d1 > 0) {
set_mode(Normal);
put_str(np, d1, No);
}
if (has_xs && so_mode != Off)
standend();
set_blanks(y, xskip+m1+d1, xskip+m1+d1+nb);
if (dd != 0 || delta < 0) {
move(y, xskip+m1+d1+nb);
np = bq1;
}
}
if (dd > 0) {
set_mode(Normal);
put_str(np, dd, No);
}
if (m2 > 0) {
if (delta > 0) {
set_mode(Insert);
ins_str(np+dd, delta);
}
else if (delta < 0) {
set_mode(Delete);
del_str(-delta);
}
}
else {
if (delta < 0) {
clr_to_eol();
return;
}
}
lenline[y] = xskip + len;
if (cur_x == cols) {
if (!has_mi)
set_mode(Normal);
if (!has_ms)
so_mode = Undefined;
if (has_am)
cur_y++;
else
Putstr(cr_str);
cur_x = 0;
}
else if (has_xs) {
if (m2 == 0) {
if (so_mode == On)
standend();
}
else {
if (!(line[cur_y][cur_x] & XSBIT)) {
if (so_mode != (line[cur_y][cur_x] & SOBIT))
(so_mode ? standend() : standout());
}
}
}
}
Hidden Procedure
set_mode(m)
int m;
{
if (m == mode)
return;
switch (mode) {
case Insert:
Putstr(ei_str);
break;
case Delete:
Putstr(ed_str);
break;
case Undefined:
Putstr(ei_str);
Putstr(ed_str);
break;
}
switch (m) {
case Insert:
Putstr(im_str);
break;
case Delete:
Putstr(dm_str);
break;
}
mode = m;
}
Hidden Procedure
get_so_mode()
{
if (cur_x >= lenline[cur_y] || line[cur_y][cur_x] == UNKNOWN)
so_mode = Off;
else
so_mode = line[cur_y][cur_x] & SOBIT;
}
Hidden Procedure
standout()
{
Putstr(so_str);
so_mode = On;
if (has_xs)
line[cur_y][cur_x] |= SOCOOK;
}
Hidden Procedure
standend()
{
Putstr(se_str);
so_mode = Off;
if (has_xs)
line[cur_y][cur_x] = (line[cur_y][cur_x] & ~SOBIT) | XSBIT;
}
Hidden Procedure
put_str(data, n, inserting)
char *data;
int n;
bool inserting;
{
register intlet c, so;
intlet *ln_y_x, *ln_y_end;
so = so_mode;
if (has_xs) {
ln_y_x = &line[cur_y][cur_x];
ln_y_end = &line[cur_y][lenline[cur_y]];
}
while (n-- > 0) {
if (has_xs && ln_y_x <= ln_y_end && ((*ln_y_x)&XSBIT))
so = so_mode = (*ln_y_x)&SOBIT;
/* this also checks for the standend cookie AFTER */
/* the line because off the equals sign in <= */
c = ((intlet)(*data++))&SOCHAR;
if ((c&SOBIT) != so) {
so = c&SOBIT;
so ? standout() : standend();
}
if (inserting)
Putstr(ic_str);
put_c(c);
if (has_xs)
ln_y_x++;
}
}
Hidden Procedure
ins_str(data, n)
char *data;
int n;
{
int x;
/* x will start AFTER the line, because there might be a cookie */
for (x = lenline[cur_y]; x >= cur_x; x--)
line[cur_y][x+n] = line[cur_y][x];
put_str(data, n, Yes);
}
Hidden Procedure
del_str(n)
int n;
{
int x, xto;
xto = lenline[cur_y] - n; /* again one too far because of cookie */
if (has_xs) {
for (x = cur_x + n; x >= cur_x; x--) {
if (line[cur_y][x] & XSBIT)
break;
}
if (x >= cur_x)
line[cur_y][cur_x+n] =
(line[cur_y][cur_x+n] & CHAR)
|
(line[cur_y][x] & COOKBITS);
}
for (x = cur_x; x <= xto; x++)
line[cur_y][x] = line[cur_y][x+n];
while (n-- > 0)
Putstr(dc_str);
}
Hidden Procedure
put_c(c)
intlet c;
{
char ch;
intlet xs_flag;
ch = c&CHAR;
if (!isprint(ch) && ch != ' ') { /* V7 isprint doesn't include blank */
ch = '?';
c = (c&SOBIT)|'?';
}
putchar(ch);
if (has_xs)
xs_flag = line[cur_y][cur_x]&XSBIT;
else
xs_flag = 0;
line[cur_y][cur_x] = (c&SOCHAR)|xs_flag;
cur_x++;
}
Hidden Procedure
clear_lines(yfirst, ylast)
int yfirst, ylast ;
{
register int y;
if (!has_xs && so_mode != Off)
standend();
if (cl_str && yfirst == 0 && ylast == lines-1) {
Putstr(cl_str);
cur_y = cur_x = 0;
return;
}
for (y = yfirst; y <= ylast; y++) {
if (lenline[y] > 0) {
move(y, 0);
if (ylast == lines-1 && cd_str) {
Putstr(cd_str);
while (y <= ylast) {
if (has_xs) line[y][0] = NOCOOK;
lenline[y++] = 0;
}
break;
}
else {
clr_to_eol();
}
}
}
}
Hidden Procedure
clr_to_eol()
{
lenline[cur_y] = cur_x;
if (!has_xs && so_mode != Off)
standend();
Putstr(ce_str);
if (has_xs) {
if (cur_x == 0)
line[cur_y][0] = NOCOOK;
else if (line[cur_y][cur_x-1]&SOBIT)
standend();
}
}
Hidden Procedure
set_blanks
(y, xfrom, xto)
int y, xfrom, xto;
{
register int x;
for (x = xfrom; x < xto; x++) {
line[y][x] = (line[y][x]&XSBIT) | ' ';
}
}
/*
* outchar() is used by termcap's tputs;
* we can't use putchar because that's probably a macro
*/
Hidden int
outchar(ch)
char ch;
{
putchar(ch);
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* 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("trmscrollup");
if (yfirst < 0)
yfirst = 0;
if (ylast >= lines)
ylast = lines-1;
if (yfirst > ylast)
return;
if (!has_xs && so_mode != Off)
standend();
if (by > 0 && yfirst + by > ylast
||
by < 0 && yfirst - by > ylast)
{
clear_lines(yfirst, ylast);
return;
}
if (by > 0) {
(*scr_up)(yfirst, ylast, by);
scr_lines(yfirst, ylast, by, 1);
}
else if (by < 0) {
(*scr_down)(yfirst, ylast, -by);
scr_lines(ylast, yfirst, -by, -1);
}
}
Hidden Procedure
scr_lines(yfrom, yto, n, dy)
int yfrom, yto, n, dy;
{
register int y;
intlet *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;
lenline[yto] = 0;
if (has_xs) line[yto][0] = NOCOOK;
}
}
Hidden Procedure
scr1up(yfirst, ylast, n)
int yfirst;
int ylast;
int n;
{
move(yfirst, 0);
dellines(n);
if (ylast < lines-1) {
move(ylast-n+1, 0);
addlines(n);
}
}
Hidden Procedure
scr1down(yfirst, ylast, n)
int yfirst;
int ylast;
int n;
{
if (ylast == lines-1) {
clear_lines(ylast-n+1, ylast);
}
else {
move(ylast-n+1, 0);
dellines(n);
}
move(yfirst, 0);
addlines(n);
}
Hidden Procedure
addlines(n)
register int n;
{
if (par_al_str && n > 1)
Putstr(tgoto(par_al_str, n, n));
else {
while (n-- > 0)
Putstr(al_str);
}
}
Hidden Procedure
dellines(n)
register int n;
{
if (par_dl_str && n > 1)
Putstr(tgoto(par_dl_str, n, n));
else {
while (n-- > 0)
Putstr(dl_str);
}
}
Hidden Procedure
scr2up(yfirst, ylast, n)
int yfirst, ylast, n;
{
Putstr(tgoto(cs_str, ylast, yfirst));
cur_y = cur_x = Undefined;
move(ylast, 0);
while (n-- > 0) {
Putstr(sf_str);
if (has_db && ylast == lines-1)
clr_to_eol();
}
Putstr(tgoto(cs_str, lines-1, 0));
cur_y = cur_x = Undefined;
}
Hidden Procedure
scr2down(yfirst, ylast, n)
int yfirst, ylast, n;
{
Putstr(tgoto(cs_str, ylast, yfirst));
cur_y = cur_x = Undefined;
move(yfirst, 0);
while (n-- > 0) {
Putstr(sr_str);
if (has_da && yfirst == 0)
clr_to_eol();
}
Putstr(tgoto(cs_str, lines-1, 0));
cur_y = cur_x = Undefined;
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* 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("trmsync");
if (0 <= y && y < lines && 0 <= x && x < cols) {
move(y, x);
if (no_cursor) {
Putstr(ve_str);
no_cursor = No;
}
}
else if (no_cursor == No) {
Putstr(vi_str);
no_cursor = Yes;
}
VOID fflush(stdout);
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* Send a bell, visible if possible. */
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
Visible Procedure
trmbell()
{
#ifdef TRACE
fprintf(stderr, "\ttrmbell();\n");
#endif
check_started("trmbell");
Putstr(vb_str);
VOID fflush(stdout);
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* 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-1 ***/ ; 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);
for (x = 0; x <= lenline[y] && x < cols-1; x++) {
if (line[y][x]&XSBIT)
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.