Subject: v08i060: A Unix/PC virtual terminal package, Part02/02 Newsgroups: mod.sources Approved: mirror!rs Submitted by: Guido van Rossum Mod.sources: Volume 8, Issue 60 Archive-name: vtrm/Part02 #! /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". # If all goes well, you will see the message "End of archive 2 (of 2)." # Contents: vtrm.c PATH=/bin:/usr/bin:/usr/ucb; export PATH echo shar: extracting "'vtrm.c'" '(38157 characters)' if test -f 'vtrm.c' ; then echo shar: will not over-write existing file "'vtrm.c'" else sed 's/^X//' >vtrm.c <<'@//E*O*F vtrm.c//' X/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1986. */ X X#define lenline len_line /* Avoid name conflict with lenline in tex.c */ X X/* X * Virtual TeRMinal package. X * (For a description see at the end of this file.) X * X * TO DO: X * - add interrupt handling (trminterrupt) X * - adapt to changed window size when suspended or at SIGWINCH X * (unfortunately, the calling module must be changed first -- X * it is not prepared for the changed window size...) X */ X X/* X * Includes and data definitions. X */ X X#include X#ifndef TERMIO X#include X#else X#include X#endif TERMIO X#include X#include X X#include "trm.h" X Xchar *getenv(); Xint tgetent(); Xint tgetnum(); Xint tgetflag(); Xchar *tgetstr(); X X#ifdef TRACE X#define Tprintf(args) fprintf args X#else X#define Tprintf(args) /*empty*/ X#endif X X#ifdef lint X#define VOID (void) X#else X#define VOID X#endif X X#define Forward X#define Visible X#define Hidden static X#define Procedure X Xtypedef char *string; Xtypedef int bool; X#define Yes 1 X#define No 0 X X#define Min(a,b) ((a) <= (b) ? (a) : (b)) X X/* tty modes */ X#ifndef TERMIO X X/* v7/BSD tty control */ XHidden struct sgttyb oldtty, newtty; X#ifdef TIOCSETN X/* Redefine stty to uses TIOCSETN, so type-ahead is not flushed */ X#define stty(fd, bp) VOID ioctl(fd, (int) TIOCSETN, (char *) bp) X#endif X X#ifdef TIOCSLTC /* BSD -- local special chars, must all be turned off */ Xstatic struct ltchars oldltchars; Xstatic struct ltchars newltchars= {-1, -1, -1, -1, -1, -1}; X#endif TIOCSLTC X X#ifdef TIOCSETC /* V7 -- standard special chars, some must be turned off too */ Xstatic struct tchars oldtchars; Xstatic struct tchars newtchars; X#endif TIOCSETC X X#else TERMIO X X/* AT&T tty control */ XHidden struct termio oldtty, newtty; X#define gtty(fd,bp) ioctl(fd, TCGETA, (char *) bp) X#define stty(fd,bp) VOID ioctl(fd, TCSETAW, (char *) bp) X X#endif TERMIO X XHidden bool know_ttys = No; X X/* visible data for termcap */ Xchar PC; Xchar *BC; Xchar *UP; Xshort ospeed; X XForward int outchar(); /* procedure for termcap's tputs */ X#define Putstr(str) tputs((str), 1, outchar) Xextern char *tgoto(); X X/* termcap terminal capabilities */ X XHidden int lines; XHidden int cols; X X/* X * String-valued capabilities are saved in one big array. X * Extend this only at the end (even though it disturbs the sorting) X * because otherwise you have to change all macros... X */ X X#define par_al_str strcaps[0] /* parametrized al (AL) */ X#define cap_cm_str strcaps[1] /* screen-relative cursor motion (CM) */ X#define par_dl_str strcaps[2] /* parametrized dl (DL) */ X#define al_str strcaps[3] /* add new blank line */ X#define cd_str strcaps[4] /* clear to end of display */ X#define ce_str strcaps[5] /* clear to end of line */ X#define cl_str strcaps[6] /* cursor home and clear screen */ X#define cm_str strcaps[7] /* cursor motion */ X#define cp_str strcaps[8] /* cursor position sense reply */ X#define cr_str strcaps[9] /* carriage return */ X#define cs_str strcaps[10] /* change scrolling region */ X#define dc_str strcaps[11] /* delete character */ X#define dl_str strcaps[12] /* delete line */ X#define dm_str strcaps[13] /* enter delete mode */ X#define do_str strcaps[14] /* cursor down one line */ X#define ed_str strcaps[15] /* end delete mode */ X#define ei_str strcaps[16] /* end insert mode */ X#define ho_str strcaps[17] /* cursor home */ X#define ic_str strcaps[18] /* insert character (if necessary; may pad) */ X#define im_str strcaps[19] /* enter insert mode */ X#define nd_str strcaps[20] /* cursor right (non-destructive space) */ X#define nl_str strcaps[21] /* newline */ X#define se_str strcaps[22] /* end standout mode */ X#define sf_str strcaps[23] /* scroll text up (from bottom of region) */ X#define so_str strcaps[24] /* begin standout mode */ X#define sp_str strcaps[25] /* sense cursor position */ X#define sr_str strcaps[26] /* scroll text down (from top of region) */ X#define te_str strcaps[27] /* end termcap */ X#define ti_str strcaps[28] /* start termcap */ X#define vb_str strcaps[29] /* visible bell */ X#define ve_str strcaps[30] /* make cursor visible again */ X#define vi_str strcaps[31] /* make cursor invisible */ X#define le_str strcaps[32] /* cursor left */ X#define bc_str strcaps[33] /* backspace character */ X#define up_str strcaps[34] /* cursor up */ X#define pc_str strcaps[35] /* pad character */ X#define ks_str strcaps[36] /* keypad mode start */ X#define ke_str strcaps[37] /* keypad mode end */ X/* Insert new entries here only! Don't forget to change the next line! */ X#define NSTRCAPS 38 /* One more than the last entry's index */ X XHidden char *strcaps[NSTRCAPS]; XHidden char strcapnames[] = X"ALCMDLalcdceclcmcpcrcsdcdldmdoedeihoicimndnlsesfsospsrtetivbvevilebcuppckske"; X X/* Same for Boolean-valued capabilities */ X X#define has_am flagcaps[0] /* has automatic margins */ X#define has_da flagcaps[1] /* display may be retained above screen */ X#define has_db flagcaps[2] /* display may be retained below screen */ X#define has_in flagcaps[3] /* not save to have null chars on the screen */ X#define has_mi flagcaps[4] /* move safely in insert (and delete?) mode */ X#define has_ms flagcaps[5] /* move safely in standout mode */ X#define has_xs flagcaps[6] /* standout not erased by overwriting */ X#define has_bs flagcaps[7] /* terminal can backspace */ X#define hardcopy flagcaps[8] /* hardcopy terminal */ X#define NFLAGS 9 X XHidden char flagcaps[NFLAGS]; XHidden char flagnames[]= "amdadbinmimsxsbshc"; X XHidden Procedure Xgetcaps(parea) X register char *parea; X{ X register char *capname; X register char **capvar; X register char *flagvar; X X for (capname= flagnames, flagvar= flagcaps; X *capname != '\0'; capname += 2, ++flagvar) X *flagvar= tgetflag(capname); X X for (capname= strcapnames, capvar= strcaps; X *capname != '\0'; capname += 2, ++capvar) X *capvar= tgetstr(capname, parea); X} X X/* terminal status */ X X/* calling order of Visible Procs */ XHidden bool started = No; X X/* to exports the capabilities mentioned in vtrm.h: */ XHidden int flags = 0; X X/* cost for impossible operations */ X#define Infinity 9999 X /* Allow for adding Infinity+Infinity within range */ X /* (Range is assumed at least 2**15 - 1) */ X X/* The following for all sorts of undefined things (except for UNKNOWN char) */ X#define Undefined (-1) X X/* current mode of putting char's */ X#define Normal 0 X#define Insert 1 X#define Delete 2 XHidden short mode = Normal; X X/* current standout mode */ X#define Off 0 X#define On 0200 XHidden short so_mode = Off; X X/* masks for char's and short's */ X#define NULCHAR '\000' X#define CHAR 0177 X#define SOBIT On X#define SOCHAR 0377 X/* if (has_xs) record cookies placed on screen in extra bit */ X/* type of cookie is determined by the SO bit */ X#define XSBIT 0400 X#define SOCOOK 0600 X#define COOKBITS SOCOOK X#define UNKNOWN 1 X#define NOCOOK UNKNOWN X X/* current cursor position */ XHidden short cur_y = Undefined, cur_x = Undefined; X X/* "line[y][x]" holds the char on the terminal, with the SOBIT and XSBIT. X * the SOBIT tells whether the character is standing out, the XSBIT whether X * there is a cookie on the screen at this position. X * In particular a standend-cookie may be recorded AFTER the line X * (just in case some trmputdata will write after that position). X * "lenline[y]" holds the length of the line. X * Unknown chars will be 1, so the optimising compare in putline will fail. X * (Partially) empty lines are distinghuished by "lenline[y] < cols". X */ XHidden short **line = 0, *lenline = 0; X X/* Clear the screen initially iff only memory cursor addressing available */ XHidden bool mustclear = No; X X/* Make the cursor invisible when trmsync() tries to move outside the screen */ XHidden bool no_cursor = No; X X/* Optimise cursor motion */ XHidden int abs_cost; /* cost of absolute cursor motion */ XHidden int cr_cost; /* cost of carriage return */ XHidden int do_cost; /* cost of down */ XHidden int le_cost; /* cost of left */ XHidden int nd_cost; /* cost of right */ XHidden int up_cost; /* cost of up */ X X/* Optimise trailing match in put_line, iff the terminal can insert and delete X * characters; the cost per n characters will be: X * n * MultiplyFactor + OverHead X */ XHidden int ins_mf, ins_oh, del_mf, del_oh; XHidden int ed_cost, ei_cost; /* used in move() */ X X/* The type of scrolling possible determines which routines get used; X * these may be: X * (1) with addline and deleteline (termcap: al_str & dl_str); X * (2) with a settable scrolling region, like VT100 (cs_str, sr_str, sf_str); X * (3) no scrolling available. (NOT YET IMPLEMENTED) X */ XHidden Procedure (*scr_up)(); XHidden Procedure (*scr_down)(); XForward Procedure scr1up(); XForward Procedure scr1down(); XForward Procedure scr2up(); XForward Procedure scr2down(); X/*Forward Procedure scr3up(); */ X/*Forward Procedure scr3down(); */ X X/* X * Starting, Ending and (fatal) Error. X */ X X/* X * Initialization call. X * Determine terminal capabilities from termcap. X * Set up tty modes. X * Start up terminal and internal administration. X * Return 0 if all well, error code if in trouble. X */ XVisible int Xtrmstart(plines, pcols, pflags) Xint *plines; Xint *pcols; Xint *pflags; X{ X register int err; X X Tprintf((stderr, "\ttrmstart(&li, &co, &fl);\n")); X if (started) X return TE_TWICE; X err= gettermcaps(); X if (err != TE_OK) X return err; X err= setttymode(); X if (err != TE_OK) X return err; X err= start_trm(); X if (err != TE_OK) { X trmend(); X return err; X } X X *plines = lines; X *pcols = cols; X *pflags = flags; X X started = Yes; X return TE_OK; X} X X/* X * Termination call. X * Reset tty modes, etc. X * Beware that it might be called by a caught interrupt even in the middle X * of trmstart()! X */ XVisible Procedure Xtrmend() X{ X Tprintf((stderr, "\ttrmend();\n")); X set_mode(Normal); X if (so_mode != Off) X standend(); X Putstr(ke_str); X Putstr(te_str); X VOID fflush(stdout); X resetttymode(); X X started = No; X} X X/* X * Set all internal statuses to undefined, especially the contents of X * the screen, so a hard redraw will not be optimised to heaven. X */ XVisible Procedure Xtrmundefined() X{ X register int y, x; X Tprintf((stderr, "\ttrmundefined();\n")); X X cur_y = cur_x = Undefined; X mode = so_mode = Undefined; X X for (y = 0; y < lines; y++) { X for (x = 0; x <= cols; x++) X line[y][x] = 1; /* impossible char, no so bits */ X lenline[y] = cols; X } X} X X#ifndef NDEBUG XHidden Procedure Xcheck_started(m) Xchar *m; X{ X if (!started) { X trmend(); X fprintf(stderr, "bad VTRM call\n"); X abort(); X } X} X#else X#define check_started(m) /*empty*/ X#endif X XHidden int ccc; X X/*ARGSUSED*/ XHidden Procedure Xcountchar(ch) Xchar ch; X{ X ccc++; X} X XHidden int Xstrcost(str) Xchar *str; X{ X if (str == NULL) X return Infinity; X return str0cost(str); X} X XHidden int Xstr0cost(str) Xchar *str; X{ X ccc = 0; X tputs(str, 1, countchar); X return ccc; X} X X/* X * Get terminal capabilities from termcap and compute related static X * properties. Return TE_OK if all well, error code otherwise. X * X * TO DO: X * - use the curses trick of a reading the capabilities in a loop X * rather than one at a time. X */ X XHidden int Xgettermcaps() X{ X string trmname; X char tc_buf[1024]; X static char strbuf[1024]; X char *area = strbuf; X int sg; X static bool tc_initialized = No; X#ifdef TIOCGWINSZ X struct winsize win; X#endif X X if (tc_initialized) X return TE_OK; X X trmname=getenv("TERM"); X if (trmname == NULL || trmname[0] == '\0') X return TE_NOTERM; X if (tgetent(tc_buf, trmname) != 1) X return TE_BADTERM; X X getcaps(&area); /* Read all flag and string type capabilities */ X if (hardcopy) X return TE_DUMB; X BC = le_str; X if (BC == NULL) { X BC = bc_str; X if (BC == NULL) { X if (has_bs) X BC = "\b"; X else X return TE_DUMB; X } X } X UP = up_str; X if (UP == NULL) X return TE_DUMB; X PC = (pc_str != NULL? pc_str[0] : NULCHAR); X X if (cm_str == NULL) { X cm_str = cap_cm_str; X if (cm_str == NULL) { X if (ho_str == NULL || do_str == NULL || nd_str == NULL) X return TE_DUMB; X } X else X mustclear = Yes; X } X if (al_str && dl_str) { X scr_up = scr1up; X scr_down = scr1down; X flags |= CAN_SCROLL; X } X else { X if (sf_str == NULL) X sf_str = "\n"; X if (cs_str && sr_str) { X scr_up = scr2up; X scr_down = scr2down; X flags |= CAN_SCROLL; X } X else X return TE_DUMB; X } X X lines = tgetnum("li"); X cols = tgetnum("co"); X#ifdef TIOCGWINSZ X if (ioctl(0, TIOCGWINSZ, &win) == 0) { X if (win.ws_col > 0) X cols = win.ws_col; X if (win.ws_row > 0) X lines = win.ws_row; X } X#endif X if (lines < 0) lines = 24; X if (cols < 0) cols = 80; X X if ((sg=tgetnum("sg")) == 0) X has_xs = Yes; X else if (sg > 0) X return TE_DUMB; X X if (!ce_str) X return TE_DUMB; X if (cr_str == NULL) cr_str = "\r"; X if (do_str == NULL) { X do_str = nl_str; X if (do_str == NULL) do_str = "\n"; X } X le_str = BC; X up_str = UP; X if (vb_str == NULL) /* then we will do with the audible bell */ X vb_str = "\007"; X X /* cursor sensing (non standard) */ X if (cp_str != NULL && sp_str != NULL) X flags |= CAN_SENSE; X X if (so_str != NULL && se_str != NULL) X flags |= HAS_STANDOUT; X X /* calculate costs of local and absolute cursor motions */ X if (cm_str == NULL) X abs_cost = Infinity; X else X abs_cost = strcost(tgoto(cm_str, 0, 0)); X cr_cost = strcost(cr_str); X do_cost = strcost(do_str); X le_cost = strcost(le_str); X nd_cost = strcost(nd_str); X up_cost = strcost(up_str); X X /* cost of leaving insert or delete mode, used in move() */ X ei_cost = str0cost(ei_str); X ed_cost = str0cost(ed_str); X X /* calculate insert and delete cost multiply_factor and overhead */ X if (((im_str && ei_str) || ic_str) && dc_str) { X flags |= CAN_OPTIMISE; X ins_mf = 1 + str0cost(ic_str); X ins_oh = str0cost(im_str) + ei_cost; X del_mf = str0cost(dc_str); X del_oh = str0cost(dm_str) + ed_cost; X } X X tc_initialized = Yes; X return TE_OK; X} X XHidden int Xsetttymode() X{ X if (!know_ttys) { X if (gtty(0, &oldtty) != 0 || gtty(0, &newtty) != 0) X return TE_NOTTY; X#ifndef TERMIO X ospeed = oldtty.sg_ospeed; X#ifdef PWB X newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS) X | RAW; X#else PWB X newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS) X | CBREAK; X#endif PWB X#ifdef TIOCSLTC X VOID ioctl(0, (int) TIOCGLTC, (char *) &oldltchars); X#endif X#ifdef TIOCSETC X VOID ioctl(0, (int) TIOCGETC, (char *) &oldtchars); X#endif X X#else TERMIO X ospeed= oldtty.c_lflag & CBAUD; X newtty.c_iflag &= ~ICRNL; /* No CR->NL mapping on input */ X newtty.c_oflag &= ~ONLCR; /* NL doesn't output CR */ X newtty.c_lflag &= ~(ICANON|ECHO); /* No line editing, no echo */ X newtty.c_cc[VMIN]= 3; /* wait for 3 characters */ X newtty.c_cc[VTIME]= 1; /* or 0.1 sec. */ X#endif TERMIO X know_ttys = Yes; X } X stty(0, &newtty); X#ifndef TERMIO X#ifdef TIOCSLTC X VOID ioctl(0, (int) TIOCSLTC, (char *) &newltchars); X#endif TIOCSLTC X#ifdef TIOCSETC X VOID ioctl(0, (int) TIOCGETC, (char *) &newtchars); X newtchars.t_intrc= -1; X#ifndef DEBUG X newtchars.t_quitc= -1; X#endif X newtchars.t_eofc= -1; X newtchars.t_brkc= -1; X VOID ioctl(0, (int) TIOCSETC, (char *) &newtchars); X#endif TIOCSETC X#endif TERMIO X return TE_OK; X} X XHidden Procedure Xresetttymode() X{ X if (know_ttys) { X stty(0, &oldtty); X#ifndef TERMIO X#ifdef TIOCSLTC X VOID ioctl(0, (int) TIOCSLTC, (char *) &oldltchars); X#endif TIOCSLTC X#ifdef TIOCSETC X VOID ioctl(0, (int) TIOCSETC, (char *) &oldtchars); X#endif TIOCSETC X#endif TERMIO X know_ttys= No; X } X} X XHidden int Xstart_trm() X{ X register int y; X X if (line == NULL) { X if ((line = (short**) malloc(lines * sizeof(short*))) == NULL) X return TE_NOMEM; X for (y = 0; y < lines; y++) { X if ((line[y] = (short*) X malloc((cols+1) * sizeof(short))) == NULL) X return TE_NOMEM; X } X } X if (lenline == NULL) { X if ((lenline = (short*) malloc(lines * sizeof(short))) == NULL) X return TE_NOMEM; X } X X trmundefined(); X X Putstr(ti_str); X Putstr(ks_str); X if (cs_str) X Putstr(tgoto(cs_str, lines-1, 0)); X if (mustclear) X clear_lines(0, lines-1); X return TE_OK; X} X X X/* X * Sensing and moving the cursor. X */ X X/* X * Sense the current (y, x) cursor position, after a possible manual X * change by the user with local cursor motions. X * If the terminal cannot be asked for the current cursor position, X * or if the string returned by the terminal is garbled, X * the position is made Undefined. X */ X XVisible Procedure Xtrmsense(py, px) X int *py; X int *px; X{ X bool getpos(); X X Tprintf((stderr, "\ttrmsense(&yy, &xx);\n")); X check_started("trmsense"); X X *py = *px = Undefined; X set_mode(Normal); X if (so_mode != Off) X standend(); X X if (flags&CAN_SENSE && getpos(py, px)) { X if (*py < 0 || lines <= *py || *px < 0 || cols <= *px) X *py = *px = Undefined; X } X cur_y = Undefined; X cur_x = Undefined; X} X XHidden bool Xgetpos(py, px) Xint *py, *px; X{ X char *format = cp_str; X int fc; /* current format character */ X int ic; /* current input character */ X int num; X int on_y = 1; X bool incr_orig = No; X int i, ni; X X Putstr(sp_str); X VOID fflush(stdout); X X while (fc = *format++) { X if (fc != '%') { X if (trminput() != fc) X return No; X } X else { X switch (fc = *format++) { X case '%': X if (trminput() != '%') X return No; X continue; X case 'r': X on_y = 1 - on_y; X continue; X case 'i': X incr_orig = Yes; X continue; X case 'd': X ic = trminput(); X if (!isdigit(ic)) X return No; X num = ic - '0'; X while (isdigit(ic=trminput())) X num = 10*num + ic - '0'; X trmpushback(ic); X break; X case '2': X case '3': X ni = fc - '0'; X num = 0; X for (i=0; i lenline[y]) X return No; X X plnyto = &line[y][xto]; X for (plnyx = &line[y][xfrom]; plnyx <= plnyto; plnyx++) X if (*plnyx == UNKNOWN X || X (!has_xs && (*plnyx & SOBIT) != so_mode) X ) X return No; X return Yes; X} X X/* X * Move to position y,x on the screen X */ X/* possible move types for y and x respectively: */ X#define None 0 X#define Down 1 X#define Up 2 X#define Right 1 X#define ReWrite 2 X#define Left 3 X#define CrWrite 4 X XHidden Procedure Xmove(y, x) Xint y, x; X{ X int dy, dx; X int y_cost, x_cost, y_move, x_move; X int mode_cost; X int xi; X X if (cur_y == y && cur_x == x) X return; X X if (!has_mi || mode == Undefined) X set_mode(Normal); X if (!has_xs && ((!has_ms && so_mode != Off) || so_mode == Undefined)) X standend(); X X if (cur_y == Undefined || cur_x == Undefined) X goto absmove; X X dy = y - cur_y; X dx = x - cur_x; X X if (dy > 0) { X y_move = Down; X y_cost = dy * do_cost; X } X else if (dy < 0) { X y_move = Up; X y_cost = -dy * up_cost; X } X else { X y_move = None; X y_cost = 0; X } X if (y_cost < abs_cost) { X switch (mode) { X case Normal: X mode_cost = 0; X break; X case Insert: X mode_cost = ei_cost; X break; X case Delete: X mode_cost = ed_cost; X break; X } X if (dx > 0) { X x_cost = dx + mode_cost; X if (dx*nd_cost < x_cost || !rewrite_ok(y, cur_x, x)) { X x_cost = dx * nd_cost; X x_move = Right; X } X else X x_move = ReWrite; X } X else if (dx < 0) { X x_cost = -dx * le_cost; X x_move = Left; X } X else { X x_cost = 0; X x_move = None; X } X if (cr_cost + x + mode_cost < x_cost && rewrite_ok(y, 0, x)) { X x_move = CrWrite; X x_cost = cr_cost + x + mode_cost; X } X } X else X x_cost = abs_cost; X X if (y_cost + x_cost < abs_cost) { X switch (y_move) { X case Down: X while (dy-- > 0) Putstr(do_str); X break; X case Up: X while (dy++ < 0) Putstr(up_str); X break; X } X switch (x_move) { X case Right: X while (dx-- > 0) Putstr(nd_str); X break; X case Left: X while (dx++ < 0) Putstr(le_str); X break; X case CrWrite: X Putstr(cr_str); X cur_x = 0; X /* FALL THROUGH */ X case ReWrite: X set_mode(Normal); X for (xi = cur_x; xi < x; xi++) X putchar(line[y][xi]); X break; X } X } X else X { X absmove: X if (cm_str == NULL) { X Putstr(ho_str); X for (cur_y = 0; cur_y < y; ++cur_y) X Putstr(do_str); X /* Should try to use tabs here: */ X for (cur_x = 0; cur_x < x; ++cur_x) X Putstr(nd_str); X } X else X Putstr(tgoto(cm_str, x, y)); X } X X cur_y = y; X cur_x = x; X} X X X/* X * Putting data on the screen. X */ X X/* X * Fill screen area with given data. X * Characters with the SO-bit (0200) set are put in standout mode. X */ XVisible Procedure Xtrmputdata(yfirst, ylast, indent, data) Xint yfirst; Xint ylast; Xregister int indent; Xregister string data; X{ X register int y; X int x, len, lendata, space; X X Tprintf((stderr, "\ttrmputdata(%d, %d, %d, \"%s\");\n", yfirst, ylast, indent, data)); X check_started("trmputdata"); X X if (yfirst < 0) X yfirst = 0; X if (ylast >= lines) X ylast = lines-1; X space = cols*(ylast-yfirst+1) - indent; X if (space <= 0) X return; X yfirst += indent/cols; X indent %= cols; X y= yfirst; X if (!data) X data= ""; /* Safety net */ X x = indent; X lendata = strlen(data); X if (ylast == lines-1 && lendata >= space) X lendata = space - 1; X len = Min(lendata, cols-x); X while (y <= ylast) { X put_line(y, x, data, len); X y++; X lendata -= len; X if (lendata > 0) { X x = 0; X data += len; X len = Min(lendata, cols); X } X else X break; X } X if (y <= ylast) X clear_lines(y, ylast); X} X X/* X * We will first try to get the picture: X * X * op>>>>>>>>>>>op oq<<<<<<<<<<<<<<<<<<<<<<<<-----m1----><----od-----><-----------m2-----------> X * OLD: "You're in a maze of twisty little pieces of code, all alike" X * NEW: "in a maze of little twisting pieces of code, all alike" X * <-----m1----><-----nd------><-----------m2-----------> X * ^ ^ ^ ^ X * np>>>>>>>>>>>np nq<<<<<<<<<<<<<<<<<<<<<<<: X * X * <---m1---><---d1---><---nb---><---d2---><---m2---> X * ^ ^ ^ ^ ^ X * np bp bq1 nq nend X * where X * bp, bq are pointers to start and AFTER end of blank piece, X * and X * d1 = length of differing part before blank piece, X * nb = length of blank piece to be skipped, X * d2 = length of differing part after blank piece. X * Remarks: X * d1 + nb + d2 == nd, X * and X * d2 maybe less than 0. X */ XHidden int Xput_line(y, xskip, data, len) Xint y, xskip; Xstring data; Xint len; X{ X register short *op, *oq; X register char *np, *nq, *nend; X char *bp, *bq1, *p, *q; X int m1, m2, od, nd, delta, dd, d1, nb, d2; X bool skipping; X int cost, o_cost; /* normal and optimising cost */ X X /* calculate the magic parameters */ X op = &line[y][xskip]; X oq = &line[y][lenline[y]-1]; X np = data; X nq = nend = data + len - 1; X m1 = m2 = 0; X while ((*op&SOCHAR) == (((short)*np)&SOCHAR) && op <= oq && np <= nq) X op++, np++, m1++; X if (flags & CAN_OPTIMISE) X while ((*oq&SOCHAR) == (((short)*nq)&SOCHAR) && op <= oq && np <= nq) X oq--, nq--, m2++; X od = oq - op + 1; X nd = nq - np + 1; X /* now we have the first picture above */ X X if (od==0 && nd==0) X return; X delta = nd - od; X X /* find the blank piece */ X p = q = bp = bq1 = np; X oq += m2; /* back to current eol */ X if (!has_in) { X while (p <= nend) { X while (q<=nend && *q==' ' && (op>oq || *op==' ')) X q++, op++; X if (q - p > bq1 - bp) X bp = p, bq1 = q; X p = ++q; X op++; X } X } X d1 = bp - np; X nb = bq1 - bp; X d2 = nq - bq1 + 1; X X /* what is cheapest: X * normal: put nd+m2; (dd = nd+m2) X * skipping: put d1, skip nb, put d2+m2; (dd = d2+m2) X * optimise: put dd, insert or delete delta. (dd = min(od,nd)) X */ X cost = nd + m2; /* normal cost */ X if (nb > abs_cost || (d1 == 0 && nb > 0)) { X skipping = Yes; X cost -= nb - (d1>0 ? abs_cost : 0); /* skipping cost */ X dd = d2; X } X else { X skipping = No; X dd = nd; X } X X if (m2 != 0) { X /* try optimising */ X o_cost = Min(od, nd); X if (delta > 0) X o_cost += delta * ins_mf + ins_oh; X else if (delta < 0) X o_cost += -delta * del_mf + del_oh; X if (o_cost >= cost) { X /* discard m2, no optimise */ X dd += m2; X m2 = 0; X } X else { X dd = Min(od, nd); X skipping = No; X } X } X X /* and now for the real work */ X if (!skipping || d1 > 0) X move(y, xskip + m1); X X if (has_xs) X get_so_mode(); X X if (skipping) { X if (d1 > 0) { X set_mode(Normal); X put_str(np, d1, No); X } X if (has_xs && so_mode != Off) X standend(); X set_blanks(y, xskip+m1+d1, xskip+m1+d1+nb); X if (dd != 0 || delta < 0) { X move(y, xskip+m1+d1+nb); X np = bq1; X } X } X X if (dd > 0) { X set_mode(Normal); X put_str(np, dd, No); X } X X if (m2 > 0) { X if (delta > 0) { X set_mode(Insert); X ins_str(np+dd, delta); X } X else if (delta < 0) { X if (so_mode != Off) X standend(); X /* Some terminals fill with standout spaces! */ X set_mode(Delete); X del_str(-delta); X } X } X else { X if (delta < 0) { X clr_to_eol(); X return; X } X } X X lenline[y] = xskip + len; X if (cur_x == cols) { X if (!has_mi) X set_mode(Normal); X if (!has_ms) X so_mode = Undefined; X if (has_am) X cur_y++; X else X Putstr(cr_str); X cur_x = 0; X } X else if (has_xs) { X if (m2 == 0) { X if (so_mode == On) X standend(); X } X else { X if (!(line[cur_y][cur_x] & XSBIT)) { X if (so_mode != (line[cur_y][cur_x] & SOBIT)) X (so_mode ? standend() : standout()); X } X } X } X} X XHidden Procedure Xset_mode(m) Xint m; X{ X if (m == mode) X return; X switch (mode) { X case Insert: X Putstr(ei_str); X break; X case Delete: X Putstr(ed_str); X break; X case Undefined: X Putstr(ei_str); X Putstr(ed_str); X break; X } X switch (m) { X case Insert: X Putstr(im_str); X break; X case Delete: X Putstr(dm_str); X break; X } X mode = m; X} X XHidden Procedure Xget_so_mode() X{ X if (cur_x >= lenline[cur_y] || line[cur_y][cur_x] == UNKNOWN) X so_mode = Off; X else X so_mode = line[cur_y][cur_x] & SOBIT; X} X XHidden Procedure Xstandout() X{ X Putstr(so_str); X so_mode = On; X if (has_xs) X line[cur_y][cur_x] |= SOCOOK; X} X XHidden Procedure Xstandend() X{ X Putstr(se_str); X so_mode = Off; X if (has_xs) X line[cur_y][cur_x] = (line[cur_y][cur_x] & ~SOBIT) | XSBIT; X} X XHidden Procedure Xput_str(data, n, inserting) Xchar *data; Xint n; Xbool inserting; X{ X register short c, so; X short *ln_y_x, *ln_y_end; X X so = so_mode; X if (has_xs) { X ln_y_x = &line[cur_y][cur_x]; X ln_y_end = &line[cur_y][lenline[cur_y]]; X } X while (n-- > 0) { X if (has_xs && ln_y_x <= ln_y_end && ((*ln_y_x)&XSBIT)) X so = so_mode = (*ln_y_x)&SOBIT; X /* this also checks for the standend cookie AFTER */ X /* the line because off the equals sign in <= */ X c = ((short)(*data++))&SOCHAR; X if ((c&SOBIT) != so) { X so = c&SOBIT; X so ? standout() : standend(); X } X if (inserting) X Putstr(ic_str); X put_c(c); X if (has_xs) X ln_y_x++; X } X} X XHidden Procedure Xins_str(data, n) Xchar *data; Xint n; X{ X int x; X X /* x will start AFTER the line, because there might be a cookie */ X for (x = lenline[cur_y]; x >= cur_x; x--) X line[cur_y][x+n] = line[cur_y][x]; X put_str(data, n, Yes); X} X XHidden Procedure Xdel_str(n) Xint n; X{ X int x, xto; X X xto = lenline[cur_y] - n; /* again one too far because of cookie */ X if (has_xs) { X for (x = cur_x + n; x >= cur_x; x--) { X if (line[cur_y][x] & XSBIT) X break; X } X if (x >= cur_x) X line[cur_y][cur_x+n] = X (line[cur_y][cur_x+n] & CHAR) X | X (line[cur_y][x] & COOKBITS); X } X for (x = cur_x; x <= xto; x++) X line[cur_y][x] = line[cur_y][x+n]; X while (n-- > 0) X Putstr(dc_str); X} X XHidden Procedure Xput_c(c) Xint c; X{ X char ch; X short xs_flag; X X ch = c&CHAR; X if (!isprint(ch) && ch != ' ') /* V7 isprint doesn't include blank */ X ch= '?'; X putchar(ch); X if (has_xs) X xs_flag = line[cur_y][cur_x]&XSBIT; X else X xs_flag = 0; X line[cur_y][cur_x] = (c&SOCHAR)|xs_flag; X cur_x++; X} X XHidden Procedure Xclear_lines(yfirst, ylast) Xint yfirst, ylast ; X{ X register int y; X X if (!has_xs && so_mode != Off) X standend(); X if (cl_str && yfirst == 0 && ylast == lines-1) { X Putstr(cl_str); X cur_y = cur_x = 0; X for (y = 0; y < lines; ++y) { X lenline[y] = 0; X if (has_xs) line[y][0] = NOCOOK; X } X return; X } X for (y = yfirst; y <= ylast; y++) { X if (lenline[y] > 0) { X move(y, 0); X if (ylast == lines-1 && cd_str) { X Putstr(cd_str); X while (y <= ylast) { X if (has_xs) line[y][0] = NOCOOK; X lenline[y++] = 0; X } X break; X } X else { X clr_to_eol(); X } X } X } X} X XHidden Procedure Xclr_to_eol() X{ X lenline[cur_y] = cur_x; X if (!has_xs && so_mode != Off) X standend(); X Putstr(ce_str); X if (has_xs) { X if (cur_x == 0) X line[cur_y][0] = NOCOOK; X else if (line[cur_y][cur_x-1]&SOBIT) X standend(); X } X} X XHidden Procedure Xset_blanks X(y, xfrom, xto) Xint y, xfrom, xto; X{ X register int x; X X for (x = xfrom; x < xto; x++) { X line[y][x] = (line[y][x]&XSBIT) | ' '; X } X} X X/* X * outchar() is used by termcap's tputs; X * we can't use putchar because that's probably a macro X */ XHidden int Xoutchar(ch) Xchar ch; X{ X putchar(ch); X} X X/* X * Scrolling (part of) the screen up (or down, dy<0). X */ X XVisible Procedure Xtrmscrollup(yfirst, ylast, by) Xregister int yfirst; Xregister int ylast; Xregister int by; X{ X Tprintf((stderr, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by)); X check_started("trmscrollup"); X X if (yfirst < 0) X yfirst = 0; X if (ylast >= lines) X ylast = lines-1; X X if (yfirst > ylast) X return; X X if (!has_xs && so_mode != Off) X standend(); X X if (by > 0 && yfirst + by > ylast X || X by < 0 && yfirst - by > ylast) X { X clear_lines(yfirst, ylast); X return; X } X X if (by > 0) { X (*scr_up)(yfirst, ylast, by); X scr_lines(yfirst, ylast, by, 1); X } X else if (by < 0) { X (*scr_down)(yfirst, ylast, -by); X scr_lines(ylast, yfirst, -by, -1); X } X} X XHidden Procedure Xscr_lines(yfrom, yto, n, dy) Xint yfrom, yto, n, dy; X{ X register int y; X short *saveln; X X while (n-- > 0) { X saveln = line[yfrom]; X for (y = yfrom; y != yto; y += dy) { X line[y] = line[y+dy]; X lenline[y] = lenline[y+dy]; X } X line[yto] = saveln; X lenline[yto] = 0; X if (has_xs) line[yto][0] = NOCOOK; X } X} X XHidden Procedure Xscr1up(yfirst, ylast, n) X int yfirst; X int ylast; X int n; X{ X move(yfirst, 0); X dellines(n); X if (ylast < lines-1) { X move(ylast-n+1, 0); X addlines(n); X } X} X X XHidden Procedure Xscr1down(yfirst, ylast, n) X int yfirst; X int ylast; X int n; X{ X if (ylast == lines-1) { X clear_lines(ylast-n+1, ylast); X } X else { X move(ylast-n+1, 0); X dellines(n); X } X move(yfirst, 0); X addlines(n); X} X X XHidden Procedure Xaddlines(n) Xregister int n; X{ X if (par_al_str && n > 1) X Putstr(tgoto(par_al_str, n, n)); X else { X while (n-- > 0) X Putstr(al_str); X } X} X X XHidden Procedure Xdellines(n) Xregister int n; X{ X if (par_dl_str && n > 1) X Putstr(tgoto(par_dl_str, n, n)); X else { X while (n-- > 0) X Putstr(dl_str); X } X} X X XHidden Procedure Xscr2up(yfirst, ylast, n) Xint yfirst, ylast, n; X{ X Putstr(tgoto(cs_str, ylast, yfirst)); X cur_y = cur_x = Undefined; X move(ylast, 0); X while (n-- > 0) { X Putstr(sf_str); X if (has_db && ylast == lines-1) X clr_to_eol(); X } X Putstr(tgoto(cs_str, lines-1, 0)); X cur_y = cur_x = Undefined; X} X X XHidden Procedure Xscr2down(yfirst, ylast, n) Xint yfirst, ylast, n; X{ X Putstr(tgoto(cs_str, ylast, yfirst)); X cur_y = cur_x = Undefined; X move(yfirst, 0); X while (n-- > 0) { X Putstr(sr_str); X if (has_da && yfirst == 0) X clr_to_eol(); X } X Putstr(tgoto(cs_str, lines-1, 0)); X cur_y = cur_x = Undefined; X} X X X/* X * Synchronization, move cursor to given position (or previous if < 0). X */ X XVisible Procedure Xtrmsync(y, x) X int y; X int x; X{ X Tprintf((stderr, "\ttrmsync(%d, %d);\n", y, x)); X check_started("trmsync"); X X if (0 <= y && y < lines && 0 <= x && x < cols) { X move(y, x); X if (no_cursor) { X Putstr(ve_str); X no_cursor = No; X } X } X else if (no_cursor == No) { X Putstr(vi_str); X no_cursor = Yes; X } X VOID fflush(stdout); X} X X X/* X * Send a bell, visible if possible. X */ X XVisible Procedure Xtrmbell() X{ X Tprintf((stderr, "\ttrmbell();\n")); X check_started("trmbell"); X X Putstr(vb_str); X VOID fflush(stdout); X} X X X#ifdef SHOW X X/* X * Show the current internal statuses of the screen on stderr. X * For debugging only. X */ X XVisible Procedure Xtrmshow(s) Xchar *s; X{ X int y, x; X X fprintf(stderr, "<<< %s >>>\n", s); X for (y = 0; y < lines; y++) { X for (x = 0; x <= lenline[y] /*** && x < cols-1 ***/ ; x++) { X fputc(line[y][x]&CHAR, stderr); X } X fputc('\n', stderr); X for (x = 0; x <= lenline[y] && x < cols-1; x++) { X if (line[y][x]&SOBIT) X fputc('-', stderr); X else X fputc(' ', stderr); X } X fputc('\n', stderr); X for (x = 0; x <= lenline[y] && x < cols-1; x++) { X if (line[y][x]&XSBIT) X fputc('+', stderr); X else X fputc(' ', stderr); X } X fputc('\n', stderr); X } X fprintf(stderr, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x); X VOID fflush(stderr); X} X#endif X X X/* X * Return the next input character, or -1 if read fails. X * Only the low 7 bits are returned, so reading in RAW mode is permissible X * (although CBREAK is preferred if implemented). X * To avoid having to peek in the input buffer for trmavail, we use the X * 'read' system call rather than getchar(). X * (The interface allows 8-bit characters to be returned, to accomodate X * larger character sets!) X */ X XHidden int pushback= -1; X Xint Xtrminput() X{ X char c; X X if (pushback >= 0) { X c= pushback; X pushback= -1; X return c; X } X if (read(0, &c, 1) <= 0) X return -1; X return c & 0177; X} X Xtrmpushback(c) X int c; X{ X pushback= c; X} X X X/* X * See if there's input available from the keyboard. X * The code to do this is dependent on the type of Unix you have X * (BSD, System V, ...). X * Return value: 0 -- no input; 1 -- input; -1 -- unimplementable. X * Note that each implementation form should first check pushback. X * X * TO DO: X * - Implement it for other than 4.x BSD! (notably System 5) X */ X X#ifdef SELECT X X#include X Xint Xtrmavail() X{ X int nfound, nfds, readfds; X static struct timeval timeout= {0, 0}; X X if (pushback >= 0) X return 1; X readfds= 1 << 0; X nfds= 0+1; X nfound= select(nfds, &readfds, (int*) NIL, (int*) NIL, &timeout); X return nfound > 0; X} X X#define TRMAVAIL_DEFINED X X#endif SELECT X X#if !defined(TRMAVAIL_DEFINED) && defined(FIONREAD) X Xint Xtrmavail() X{ X long n; X X if (pushback >= 0) X return 1; X ioctl(0, (int) FIONREAD, (char *) &n); X return n > 0; X} X X#define TRMAVAIL_DEFINED X X#endif FIONREAD X X#ifndef TRMAVAIL_DEFINED X Xint Xtrmavail() X{ X if (pushback >= 0) X return 1; X return -1; X} X X#endif X X X/* X * Suspend the editor. X * Should be called only after trmend and before trmstart! X */ X Xtrmsuspend() X{ X int (*oldsig)(); X X oldsig= signal(SIGTSTP, SIG_IGN); X if (oldsig == SIG_IGN) X return; /* Could spawn a subshell here... */ X trmend(); /* Safety net */ X signal(SIGTSTP, oldsig); X kill(0, SIGSTOP); X} X X X/* X * DESCRIPTION. X * X * This package uses termcap to determine the terminal capabilities. X * X * The lines and columns of our virtual terminal are numbered X * y = {0...lines-1} from top to bottom, and X * x = {0...cols-1} from left to right, X * respectively. X * X * The Visible Procedures in this package are: X * X * trmstart(&lines, &cols, &flags) X * Obligatory initialization call (sets tty modes etc.), X * Returns the height and width of the screen to the integers X * whose addresses are passed as parameters, and a flag that X * describes some capabilities. X * Function return value: 0 if all went well, an error code if there X * is any trouble. No messages are printed for errors. X * X * trmundefined() X * Sets internal representation of screen and attributes to undefined. X * This is necessary for a hard redraw, which would get optimised to X * oblivion, X * X * trmsense(&y, &x) X * Returns the cursor position through its parameters X * after a possible manual change by the user. X * X * trmputdata(yfirst, ylast, indent, data) X * Fill lines {yfirst..ylast} with data, after skipping the initial X * 'indent' positions. It is assumed that these positions do not contain X * anything dangerous (like standout cookies or null characters). X * X * trmscrollup(yfirst, ylast, by) X * Shift lines {yfirst..ylast} up by lines (down |by| if by < 0). X * X * trmsync(y, x) X * Call to output data to the terminal and set cursor position. X * X * trmbell() X * Send a (possibly visible) bell, immediately (flushing stdout). X * X * trmend() X * Obligatory termination call (resets tty modes etc.). X * X * You may call these as one or more cycles of: X * + trmstart X * + zero or more times any of the other routines X * + trmend X * Trmend may be called even in the middle of trmstart; this is necessary X * to make it possible to write an interrupt handler that resets the tty X * state before exiting the program. X * X * ADDITIONAL SPECIFICATIONS (ROUTINES FOR CHARACTER INPUT) X * X * trminput() X * Return the next input character (with its parity bit cleared X * if any). This value is a nonnegative int. Returns -1 if the X * input can't be read any more. X * X * trmavail() X * Return 1 if there is an input character immediately available, X * 0 if not. Return -1 if not implementable. X * X * trminterrupt() X * Return 1 if an interrupt has occurred since the last call to X * trminput or trmavail, 0 else. [Currently not implemented.] X * X * trmsuspend() X * When called in the proper environment (4BSD with job control X * enabled), suspends the editor, temporarily popping back to X * the calling shell. The caller should have called trmend() X * first, and must call trmstart again afterwards. X * BUG: there is a timing window where keyboard-generated X * signals (such as interrupt) can reach the program. X */ @//E*O*F vtrm.c// if test 38157 -ne "`wc -c <'vtrm.c'`"; then echo shar: error transmitting "'vtrm.c'" '(should have been 38157 characters)' fi fi # end of overwriting check echo shar: "End of archive 2 (of 2)." cp /dev/null ark2isdone DONE=true for I in 1 2; do if test -! f ark${I}isdone; then echo "You still need to run archive ${I}." DONE=false fi done case $DONE in true) echo "You have run all 2 archives." echo 'Now see the README' ;; esac ## End of shell archive. exit 0