guido@cwi.nl (Guido van Rossum) (03/04/91)
Archive-name: stdwin/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 19)." # Contents: Appls/bed/2x2 Appls/tetris/tetris.c Ports/vtrm/vtrm.c # Wrapped by guido@voorn.cwi.nl on Mon Mar 4 12:37:23 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'Appls/bed/2x2' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Appls/bed/2x2'\" else echo shar: Extracting \"'Appls/bed/2x2'\" \(272 characters\) sed "s/^X//" >'Appls/bed/2x2' <<'END_OF_FILE' X#define 2x2_width 16 X#define 2x2_height 16 Xstatic char 2x2_bits[] = { X 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, X 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, X 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33}; END_OF_FILE if test 272 -ne `wc -c <'Appls/bed/2x2'`; then echo shar: \"'Appls/bed/2x2'\" unpacked with wrong size! fi # end of 'Appls/bed/2x2' fi if test -f 'Appls/tetris/tetris.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Appls/tetris/tetris.c'\" else echo shar: Extracting \"'Appls/tetris/tetris.c'\" \(16117 characters\) sed "s/^X//" >'Appls/tetris/tetris.c' <<'END_OF_FILE' X/* Tetris. X X A simple but challenging game where pieces of different shapes X falling with a constant speed must be manoeuvered to a final X docking position. Piece shapes are randomly chosen from all possible X ways to connect four squares. The manipulations allowed are moving X the piece left or right and rotating it; every clock tick it moves X down one step, until it cannot move further, or the player decides X to "drop" it to earn more points. The game is made more or less X challenging by making the clock tick faster or slower. A score is X kept. Points earned per piece depend on the height where it is X dropped or stopped; extra points are awarded for choosing a higher X speed ("level"). To allow the play to continue indefinitely, X complete rows (i.e., horizontal rows where all squares X are filled) are removed from the board and its contents above that X row shifted down. X X The user interface uses mostly the left, right and up keys, for X moving the piece left and right and rotating it. For Unix adepts, X 'h'=left, 'k'=up and 'l'=right also work. Space bar or Return X drops the piece. The Cancel key (Command-Period on the Mac, X Control-C on most other systems) restarts the game; close the window X or type 'q' to quit the game. X X The origin of the game appears to be in the East Block; I've heard X that the (original?) Macintosh version was by a Hungarian programmer. X X This code is hereby put in the public domain, but the package X STDWIN used as portable window interface is copyrighted. STDWIN X is available from me, too, provided you respect the copyright etc. X X Guido van Rossum, CWI, Kruislaan 413, 1098 SJ Amsterdam, X The Netherlands X Internet e-mail address: guido@cwi.nl X April 1989 X*/ X X/* TO DO: X - show next piece coming up in a side window X - change chance distribution of pieces? (too few bars) X - advanced level (what should it do? just faster?) X - rethink level <--> delay relation X X - improve "game over" behavior X X - display high score X - score and status display next to the board X - statistics X X - cute graphics X*/ X X/* Standard include files */ X X#include "stdwin.h" X#include <stdio.h> X X/* Parametrizations */ X X/* Piece size. It only makes sense to change this if you also X change the initialization of the 'shapes' array. X Since we rotate pieces, their max size must always be square. */ X#define PSIZE 4 X X/* Game dimensions. Traditionally it is played on a 10x20 board. */ X#ifndef BWIDTH X#define BWIDTH 10 X#endif X#ifndef BHEIGHT X#define BHEIGHT 20 X#endif X X/* Initial timer delay. This affects initial difficulty and scoring. X The current value is kept in variable 'delay'. */ X#ifndef DELAY X#define DELAY 10 X#endif X X/* Individual 'square' sizes. X These can be adjusted according to taste and the size of pixels X on your screen. (You can also fine-tune window and document size X in main() below.) X For the alfa version of STDWIN, where pixel size == character size, X a fixed size of 2x1 is forced later. */ X#ifndef SQWIDTH X#define SQWIDTH 12 X#endif X#ifndef SQHEIGHT X#define SQHEIGHT 12 X#endif X X/* Left, top of board image in window */ X#ifndef BLEFT X#define BLEFT 0 X#endif X#ifndef BTOP X#define BTOP 4 X#endif X X/* Some useful macros (predefined on some but not all systems) */ X X#ifndef MIN X#define MIN(a, b) ((a) < (b) ? (a) : (b)) X#endif X X#ifndef MAX X#define MAX(a, b) ((a) > (b) ? (a) : (b)) X#endif X X/* Available shapes. X Relnext is the offset to the next piece after rotation. X The pieces are aligned with the bottom so their scoring values are X comparable, and the delay before their start is minimal; they are X centered horizontally so the random placement appears even. X Remember C's aggregate initialization rules; the initializer below X is completely bracketed, but trailing zeros are sometimes elided. */ X Xstruct shapedef { X int relnext; X char piece[PSIZE][PSIZE]; X} shapes[] = { X X /* "Four in a row" (2 orientations) */ X X {1, {{0, 0, 0, 0}, X {0, 0, 0, 0}, X {0, 0, 0, 0}, X {1, 1, 1, 1}}}, X X {-1, {{0, 1, 0, 0}, X {0, 1, 0, 0}, X {0, 1, 0, 0}, X {0, 1, 0, 0}}}, X X /* "L shape" (4 orientations) */ X X {1, {{0, 0, 0}, X {0, 1, 0}, X {0, 1, 0}, X {0, 1, 1}}}, X X {1, {{0, 0, 0}, X {0, 0, 0}, X {0, 0, 1}, X {1, 1, 1}}}, X X {1, {{0, 0, 0}, X {0, 1, 1}, X {0, 0, 1}, X {0, 0, 1}}}, X X {-3, {{0, 0, 0}, X {0, 0, 0}, X {1, 1, 1}, X {1, 0, 0}}}, X X /* "Inverse L shape" (4 orientations) */ X X {1, {{0, 0, 0}, X {0, 1, 1}, X {0, 1, 0}, X {0, 1, 0}}}, X X {1, {{0, 0, 0}, X {0, 0, 0}, X {1, 0, 0}, X {1, 1, 1}}}, X X {1, {{0, 0, 0}, X {0, 0, 1}, X {0, 0, 1}, X {0, 1, 1}}}, X X {-3, {{0, 0, 0}, X {0, 0, 0}, X {1, 1, 1}, X {0, 0, 1}}}, X X /* "Z shape" (2 orientations) */ X X {1, {{0, 0, 0}, X {0, 0, 0}, X {1, 1, 0}, X {0, 1, 1}}}, X X {-1, {{0, 0, 0}, X {0, 0, 1}, X {0, 1, 1}, X {0, 1, 0}}}, X X /* "S shape" (2 orientations) */ X X {1, {{0, 0, 0}, X {0, 1, 0}, X {0, 1, 1}, X {0, 0, 1}}}, X X {-1, {{0, 0, 0}, X {0, 0, 0}, X {0, 1, 1}, X {1, 1, 0}}}, X X /* "T shape" (4 orientations) */ X X {1, {{0, 0, 0}, X {0, 0, 0}, X {1, 1, 1}, X {0, 1, 0}}}, X X {1, {{0, 0, 0}, X {0, 1, 0}, X {0, 1, 1}, X {0, 1, 0}}}, X X {1, {{0, 0, 0}, X {0, 0, 0}, X {0, 1, 0}, X {1, 1, 1}}}, X X {-3, {{0, 0, 0}, X {0, 0, 1}, X {0, 1, 1}, X {0, 0, 1}}}, X X /* "Block" (1 orientation) */ X X {0, {{0, 0, 0}, X {0, 0, 0}, X {0, 1, 1}, X {0, 1, 1}}}, X X}; X X/* Global variables */ X Xint alfa; /* Nonzero if using alfa STDWIN */ Xint sqwidth = SQWIDTH; /* Width of squares, in pixels */ Xint sqheight = SQHEIGHT; /* Height */ Xint bleft = BLEFT; Xint btop = BTOP; XWINDOW *win; /* The window where we do our drawing */ Xint delay; /* Current delay */ X /* NB: level = MAX(0, DELAY-delay) */ Xchar board[BHEIGHT][BWIDTH]; /* Contents of board, except current piece */ Xchar (*piece)[PSIZE]; /* Piece currently being manoeuvered */ Xint pindex; /* Index in the shape array of current piece */ Xint pleft, ptop; /* Position of current piece */ Xlong score; /* Score of current game */ X X/* Generate an informative title from level and/or score. X (By putting it in the title bar we don't need an info window.) */ X Xsettitle() X{ X char buf[100]; X int level = MAX(0, DELAY-delay); X X if (level == 0) { X if (score == 0) X strcpy(buf, "Tetris"); X else X sprintf(buf, "Score %ld", score); X } X else { X if (score == 0) X sprintf(buf, "Level %d", level); X else X sprintf(buf, "Sc %ld Lv %d", score, level); X } X wsettitle(win, buf); X} X X/* Erase a portion of the board on the screen. X Call only within wbegin/enddrawing. */ X Xeraseboard(ileft, itop, iright, ibottom) X int ileft, itop, iright, ibottom; X{ X werase(bleft + ileft*sqwidth, btop + itop*sqheight, X bleft + iright*sqwidth, btop + ibottom*sqheight); X} X X/* Draw a portion of the board, and a border around it. X Call only within wbegin/enddrawing. X This may be called with out-of-range parameters. X Draw those squares of the game that lie (partly) in the rectangle X given by the parameters. Assume the background is blank. X This contains #ifdef'ed code for the alfa version of STDWIN, X which only supports text output. */ X Xdrawboard(ileft, itop, iright, ibottom) X int ileft, itop, iright, ibottom; X{ X int ih, iv; X int h, v; X int flag; X X ileft = MAX(0, ileft); X itop = MAX(0, itop); X iright = MIN(BWIDTH, iright); X ibottom = MIN(BHEIGHT, ibottom); X for (iv = itop, v = btop + iv*sqheight; iv < ibottom; X ++iv, v += sqheight) { X for (ih = ileft, h = bleft + ih*sqwidth; ih < iright; X ++ih, h += sqwidth) { X flag = board[iv][ih]; X if (!flag && pleft <= ih && ih < pleft+PSIZE && X ptop <= iv && iv < ptop+PSIZE) X flag = piece[iv-ptop][ih-pleft]; X if (flag) { X if (alfa) X wdrawchar(h, v, '#'); X else X wshade(h+1, v+1, X h+sqwidth, v+sqheight, 50); X } X } X } X if (alfa) { X /* Draw markers at the right margin */ X wdrawchar(bleft + BWIDTH*sqwidth, btop, '|'); X wdrawchar(bleft + BWIDTH*sqwidth, btop + (BHEIGHT-1)*sqheight, X '|'); X } X else { X /* Draw a box around the board */ X wdrawbox(bleft - 1, btop - 1, bleft + BWIDTH*sqwidth + 2, X btop + BHEIGHT*sqheight + 2); X } X} X X/* Erase and redraw part of the board. X Unlike eraseboard and drawboard above, this includes calls to X wbegin/enddrawing. */ X Xredrawboard(ileft, itop, iright, ibottom) X int ileft, itop, iright, ibottom; X{ X wbegindrawing(win); X eraseboard(ileft, itop, iright, ibottom); X drawboard(ileft, itop, iright, ibottom); X wenddrawing(win); X} X X/* Draw procedure, passed to STDWIN's wopen */ X Xvoid Xdrawproc(win, left, top, right, bottom) X WINDOW *win; X int left, top, right, bottom; X{ X drawboard((left-bleft)/sqwidth, (top-btop)/sqheight, X (right-bleft+sqwidth-1)/sqwidth, X (bottom-btop+sqheight-1)/sqheight); X} X X/* Check if the piece can be at (dh, dv). X This is used to check for legal moves. X No part of the piece can be on a filled spot in the board or X be outside it, but it can stick out above the top. */ X Xint Xallowed(dh, dv) X int dh, dv; X{ X int ih, iv; X X for (iv = 0; iv < PSIZE; ++iv) { X for (ih = 0; ih < PSIZE; ++ih) { X if (piece[iv][ih]) { X if (ih+dh < 0 || ih+dh >= BWIDTH) X return 0; X if (iv+dv < 0) X continue; X if (iv+dv >= BHEIGHT) X return 0; X if (board[iv+dv][ih+dh]) X return 0; X } X } X } X return 1; X} X X/* Return a random integer in the range [0..n-1] */ X Xint Xuniform(n) X int n; X{ X return rand() % n; X} X X/* Rotate the piece 90 degrees counterclockwise. No drawing is done. X The implementation is trivial: just take the "next" element from the X shape array. */ X Xleftrotate() X{ X pindex += shapes[pindex].relnext; X piece = shapes[pindex].piece; X} X X/* Move the piece by the given vector (dh, dv), if this is a legal move.. X Return 1 if moved, 0 if not (then no changes were made). */ X Xint Xmoveby(dh, dv) X int dh, dv; X{ X int ileft, itop, iright, ibottom; X X if (!allowed(pleft+dh, ptop+dv)) { X return 0; X } X ileft = pleft + MIN(dh, 0); X itop = ptop + MIN(dv, 0); X iright = pleft + PSIZE + MAX(dh, 0); X ibottom = ptop + PSIZE + MAX(dv, 0); X pleft += dh; X ptop += dv; X redrawboard(ileft, itop, iright, ibottom); X return 1; X} X X/* Rotate the piece n quarter left turns, if this is a legal move. X Return 1 if moved, 0 if not (then no changes were made). */ X Xint Xrotateby(n) X int n; X{ X int i; X X for (i = 0; i < n; ++i) X leftrotate(); X if (!allowed(pleft, ptop)) { X for (i = 0; i < 4-n; ++i) X leftrotate(); X return 0; X } X redrawboard(pleft, ptop, (pleft+PSIZE), (ptop+PSIZE)); X return 1; X} X X X/* Trivial routines to implement the commands. */ X Xleft() X{ X (void) moveby(-1, 0); X} X Xright() X{ X (void) moveby(1, 0); X} X Xrot() X{ X (void) rotateby(1); X} X X/* Generate a new piece. Its initial position is just above the top of X the board, so that a single move down will show its bottom row. X (This is one reason why the pieces are aligned with the bottom in the X 'shapes' array.) */ X Xgenerate() X{ X pindex = uniform(sizeof shapes / sizeof shapes[0]); X X piece = shapes[pindex].piece; X pleft = (BWIDTH-PSIZE) / 2; X ptop = -PSIZE; X} X X/* Start a new game. X Reset deley/level, score, board and title; generate a new piece. X The game is not restarted immediately. */ X Xreset() X{ X int ih, iv; X X wsettimer(win, 0); X delay = DELAY; X score = 0; X for (iv = 0; iv < BHEIGHT; ++iv) { X for (ih = 0; ih < BWIDTH; ++ih) X board[iv][ih] = 0; X } X generate(); X redrawboard(0, 0, BWIDTH, BHEIGHT); X settitle(); X} X X/* Remove any full rows found, shifting the board above down */ X Xremoverows() X{ X int ih, iv; X int jv; X X for (iv = 0; iv < BHEIGHT; ++iv) { X for (ih = 0; ih < BWIDTH; ++ih) { X if (!board[iv][ih]) X goto next; /* Two-level continue */ X } X for (jv = iv; jv > 0; --jv) { X for (ih = 0; ih < BWIDTH; ++ih) X board[jv][ih] = board[jv-1][ih]; X } X for (ih = 0; ih < BWIDTH; ++ih) X board[jv][ih] = 0; X wscroll(win, X bleft, btop, X bleft + BWIDTH*sqwidth, btop + (iv+1)*sqheight, X 0, sqheight); X next: ; X } X} X X/* Add the score for the current piece to the total score. X The title is not regenerated; that is done later in finish(). */ X Xaddscore() X{ X int level = MAX(0, DELAY-delay); X int height = MAX(0, BHEIGHT-ptop); X X score += height + 2*level /* *(advanced?2:1) */ ; X} X X/* Finish a piece off by dropping it; score and generate a new one. X Called by the user and from timer of the piece can't move down. X This also contains a hack to detect the end of the game: X if the new piece can't move one step, it is over. */ X Xfinish() X{ X int ih, iv; X X addscore(); X while (moveby(0, 1)) X ; X for (iv = 0; iv < PSIZE; ++iv) { X for (ih = 0; ih < PSIZE; ++ih) { X if (piece[iv][ih] && iv+ptop >= 0) X board[iv+ptop][ih+pleft] = 1; X } X } X removerows(); X generate(); X settitle(); X if (moveby(0, 1)) X wsettimer(win, delay); X else { X if (alfa) { X /* Alfa STDWIN's wmessage doesn't wait for an OK */ X char buffer[10]; X strcpy(buffer, "Game over"); X (void) waskstr("", buffer, strlen(buffer)); X } X else X wmessage("Game over"); X reset(); X } X} X X/* The clock has ticked. X Try to move down; if it can't, finish it off and start a new one. */ X Xtimer() X{ X if (moveby(0, 1)) X wsettimer(win, delay); X else X finish(); X} X X/* Make the clock tick faster (increase level) */ X Xfaster() X{ X if (delay > 1) X --delay; X settitle(); X} X X/* Make the clock tick slower (decrease level) */ X Xslower() X{ X ++delay; X settitle(); X} X X/* Quit the program. X Note that wdone() MUST be called before a STDWIN application exits. */ X Xquit() X{ X wdone(); X exit(0); X} X X/* Main event loop. X React on commands, ignoring illegal ones, and react to clock ticks. X Call various routines to execute the commands. X Never return; calls quit() to exit. */ X Xmainloop() X{ X EVENT e; X X for (;;) { X wgetevent(&e); X X switch (e.type) { X X case WE_TIMER: X timer(); X break; X X case WE_CHAR: X switch (e.u.character) { X case '+': X faster(); X break; X case '-': X slower(); X break; X case 'g': X timer(); X break; X case ' ': X finish(); X break; X case 'h': X left(); X break; X case 'k': X rot(); X break; X case 'l': X right(); X break; X case 'q': X quit(); X break; X case 'r': X reset(); X break; X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X delay = DELAY-(e.u.character-'0'); X delay = MAX(1, delay); X settitle(); X break; X } X break; X X case WE_CLOSE: X quit(); X break; X X case WE_COMMAND: X switch (e.u.command) { X case WC_RETURN: X timer(); X break; X case WC_CANCEL: X reset(); X break; X case WC_CLOSE: X quit(); X break; X case WC_LEFT: X left(); X break; X case WC_UP: X rot(); X break; X case WC_RIGHT: X right(); X break; X } X break; X X } X } X /*NOTREACHED*/ X} X X/* Add a menu, only used as a cheap way to display some help */ X Xaddhelpmenu() X{ X MENU *mp; X X mp = wmenucreate(1, "Help"); X wmenuadditem(mp, "g or return starts the game", -1); X wmenuadditem(mp, "", -1); X wmenuadditem(mp, "h or left arrow moves left", -1); X wmenuadditem(mp, "l or right arrow moves right", -1); X wmenuadditem(mp, "k or up arrow rotates", -1); X wmenuadditem(mp, "space drops", -1); X wmenuadditem(mp, "", -1); X wmenuadditem(mp, "+/- increases/decreases level (speed)", -1); X wmenuadditem(mp, "0-9 chooses level directly", -1); X wmenuadditem(mp, "", -1); X wmenuadditem(mp, "r or cancel restarts", -1); X wmenuadditem(mp, "close or q quits the game", -1); X} X X/* Main program. X Initialize STDWIN, create the window and the help menu, X reset the game and call 'mainloop()' to play it. */ X Xmain(argc, argv) X int argc; X char **argv; X{ X long t; X X time(&t); X srand((short)t ^ (short)(t>>16)); X winitargs(&argc, &argv); X if (wlineheight() == 1) { X alfa = 1; X sqwidth = 2; X sqheight = 1; X bleft = btop = 0; X } X wsetdefwinsize(bleft + BWIDTH*sqwidth + 1, X btop + BHEIGHT*sqheight + 5); X win = wopen("Tetris", drawproc); X if (win == NULL) { X printf("Can't create window\n"); X wdone(); X exit(1); X } X wsetdocsize(win, X bleft + BWIDTH*sqwidth + 1, btop + BHEIGHT*sqheight + 1); X addhelpmenu(); X reset(); X mainloop(); X /*NOTREACHED*/ X} END_OF_FILE if test 16117 -ne `wc -c <'Appls/tetris/tetris.c'`; then echo shar: \"'Appls/tetris/tetris.c'\" unpacked with wrong size! fi # end of 'Appls/tetris/tetris.c' fi if test -f 'Ports/vtrm/vtrm.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Ports/vtrm/vtrm.c'\" else echo shar: Extracting \"'Ports/vtrm/vtrm.c'\" \(34702 characters\) sed "s/^X//" >'Ports/vtrm/vtrm.c' <<'END_OF_FILE' 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#include "tools.h" X Xint tgetent(); Xint tgetnum(); Xint tgetflag(); Xchar *tgetstr(); Xchar *tgoto(); X X#include "vtrm.h" 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; X#ifndef bool X#define bool int X#endif X#define Yes 1 X#define No 0 X X#define Min(a,b) ((a) <= (b) ? (a) : (b)) X X/* Variables needed by termcap's tgoto() or tputs(): */ 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) 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 safe 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 has_xn flagcaps[9] /* Vt100 / Concept glitch */ X#define NFLAGS 10 X XHidden char flagcaps[NFLAGS]; XHidden char flagnames[]= "amdadbinmimsxsbshcxn"; 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 X/*ARGSUSED*/ XHidden Procedure Xcheck_started(m) X char *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 XHidden int Xgettermcaps() X{ X string trmname; X static char tc_buf[1024]; /* Static so main program can get keydefs */ X static char strbuf[1024]; /* Buffer where our caps are saved */ X char *area = strbuf; X int sg; X static bool tc_initialized = No; 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 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 Xstart_trm() X{ X register int y; X int newlines, newcols; X X gettruewinsize(&newlines, &newcols); X if (newcols != 0 && newcols != cols || X newlines != 0 && newlines != lines) { X /* Window size has changed. X Release previously allocated buffers. */ X if (line != NULL) { X for (y= 0; y < lines; ++y) X free((char *) line[y]); X free((char *) line); X line= NULL; X } X if (lenline != NULL) { X free((char *) lenline); X lenline= NULL; X } X if (newcols != 0) X cols = newcols; X if (newlines != 0) X lines = newlines; X } X X if (line == NULL) { X line = (short **) malloc((unsigned) (lines * sizeof(short*))); X if (line == NULL) X return TE_NOMEM; X for (y = 0; y < lines; y++) { X line[y] = (short *) X malloc((unsigned) ((cols+1) * sizeof(short))); X if (line == NULL) X return TE_NOMEM; X } X } X if (lenline == NULL) { X lenline = (short *) malloc((unsigned) (lines * sizeof(short))); X if (lenline == 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<ni; i++) { X ic = trminput(); X if (isdigit(ic)) X num = 10*num + ic - '0'; X else X return No; X } X break; X case '+': X num = trminput() - *format++; X break; X case '-': X num = trminput() + *format++; X break; X default: X return No; X } X /* assign num to parameter */ X if (incr_orig) X num--; X if (on_y) X *py = num; X else X *px = num; X on_y = 1 - on_y; X } X } X X return Yes; X} X X/* X * To move over characters by rewriting them, we have to check: X * (1) that the screen has been initialised on these positions; X * (2) we do not screw up characters X * when rewriting line[y] from x_from upto x_to X */ XHidden bool Xrewrite_ok(y, xfrom, xto) Xint y, xfrom, xto; X{ X register short *plnyx, *plnyto; X X if (xto > 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<<<<<<<<<<<<<<<<<<<<<<<<oq X * ^ ^ ^ ^ X * <xskip><-----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<<<<<<<<<<<<<<<<<<<<<<<<nq X * where X * op, oq, np, nq are pointers to start and end of Old and New data, X * and X * xskip = length of indent to be skipped, X * m1 = length of Matching part at start, X * od = length of Differing mid on screen, X * nd = length of Differing mid in data to be put, X * m2 = length of Matching trail. X * X * Then we will try to find a long blank-or-cleared piece in <nd+m2>: 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 /* Bugfix GvR 19-June-87: */ X while (lenline[y] < xskip) X line[y][lenline[y]++] = ' '; 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 if (has_xn) X cur_y= Undefined; X else X cur_y++; X } 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 an error message corresponding to an error code from trmstart. X */ X Xchar * Xtrmwhy(err) X int err; X{ X static char *errlist[] = { X "no error", X "trmstart called twice", X "$TERM not set or empty", X "$TERM not found in termcap database", X "terminal too dumb", X "stdout not a tty device", X "can't get enough memory", X }; X static char buf[50]; X X if (err < TE_OK || err >= TE_OTHER) { X sprintf(buf, "trmstart error %d", err); X return buf; X } X else { X return errlist[err]; X } 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 * X * char *trmwhy() X * Return error string corresponding to trmstart() error code. X */ END_OF_FILE if test 34702 -ne `wc -c <'Ports/vtrm/vtrm.c'`; then echo shar: \"'Ports/vtrm/vtrm.c'\" unpacked with wrong size! fi # end of 'Ports/vtrm/vtrm.c' fi echo shar: End of archive 3 \(of 19\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 19 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0