/* Copyright (c) 1983 University of Maryland Computer Science Department */ /* terminal control module for terminals described by TERMCAP */ /* This thing needs a complete rewrite but I'm not ready for that yet. 30 Jun 1983 ACT */ /* Origincal code copyright (c) 1981,1980 James Gosling */ /* Modified 1-Dec-80 by Dan Hoey (DJH) to understand C100 underlines * Modified 2-Dec-80 (DJH) to turn off highlighting on insertline * Modified 4 Aug 81 by JQ Johnson: use "dm","ei","pc","mi" * Modified 24-Aug-81 by Jeff Mogul (JCM) at Stanford * - uses "nl" instead of \n in case \n is destructive * Modified 8-Sept-81 by JCM @ Stanford * - re-integrated changes from Gosling since July '81 * Modified by Chris Torek (ACT) @ Umcp-Cs * - can hack terminals without erase-to-eol (12-Aug-81) * - Sets NullsAreSpecial if :in: (12-June-82) * - Uses "cr" instead of \r (13-June-82) * - Rewrote cursor positioning routines, added: * "ms", "ho", "xr", "nc", "xt", "ta", "xn", "ll" (etc) * - Also cleaned up various things that were cleanable * - Rewrote all the cursor positioning stuff around 21-June-82 * - Handles :db: (11-Jul-82) * - Secured the cost algorithms from buffer overflow (16-Jul-82) * - Does not use standout if 'sg#' is nonzero. * Modified by Chris Torek (ACT): added %m cursor motion code */ /* * Notes on things not normally in TERMCAP: * :rn: indicates that \r acts like \r\n * :nn: indicates that \n doesn't work * :ds=: gives a "don't send" string; these characters will not * be written to the terminal for cursor motion (:cm=:). If this * is nonempty the terminal MUST have an upline and a backspace. * If defined, it is assumed to also have a null in it. If not * included in a termcap, it defaults to ^D\t\n\r. To get * real binary use ":ds=:" to say can send anything. * :bo=: is bold-start string * :be=: is bold-end string * :ws=: is wink-start (ie blink) string * :we=: is wink-end string */ #include #include #include "Trm.h" #include "win.h" /* ACT: This has gotta be at least 800 for some terminals (!!) */ #define CMBUFSIZ 1000 /* Cursor motion buffer size */ static int curX, curY; char *tgetstr (), *getenv (); char *UP, *BC, PC; short ospeed; static char *ILstr, *DLstr, *ICstr, *DCstr, *ELstr, *ESstr, *HLBstr, *HLEstr, *ICPstr, *ICPDstr, *CursStr, *CteoDstr, *TIstr, *TEstr, *VSstr, *VEstr, *HOstr, *TBstr, *LLstr, *ICEstr, *NDstr, *VBstr, *EDstr, *DMstr, *NLstr, *CRstr, *DSstr, *WSstr, *WEstr, *ULstr, *UEstr, *BSstr, *BEstr; static int ULflag, /* DJH -- 1 if terminal has underline */ MIflag, /* JQJ -- 1 if safe to move while in insert mode */ XRflag, /* ACT -- if (:xr:nc:) CR's don't work correctly */ XTflag, /* 1 if can't use tabs for cursor motion */ XNflag, /* 1 if can't use \n's for cursor motion */ HZflag, /* 1 if hazeltine, can't print tilde */ RNflag, /* 1 if \r does a \r\n */ DBflag, /* 1 if display retained below */ MSflag; /* 1 if safe to move while in standout */ static BOLDOFF, BLINKOFF, ULINEOFF, HLOFF; /* Bits that tell what modes arent independent */ static dumpchar (c) register c; { putchar (c); } static enum IDmode { m_insert = 1, m_overwrite = 0 } CurMode, DesMode; static INSmode (new) register enum IDmode new; { DesMode = new; }; static curmodes, desmodes; static modes (on) { desmodes = on; } /* This routine needs work! */ static setmodes () { register char *com; int bold, under, blink, hl; if (curmodes == desmodes) return; /* The "end" strings are often the same. All this is to get around that. */ bold = desmodes & WBOLD; under = desmodes & WULINE; blink = desmodes & WBLINK; hl = desmodes & WINVERSE; if (bold==0 && (curmodes & WBOLD)) {/* Then turn off bold */ if (com = BEstr) tputs (com, 0, dumpchar); curmodes &= ~BOLDOFF; /* May have turned others off too */ } if (under==0 && (curmodes & WULINE)) {/* etc */ if (com = UEstr) tputs (com, 0, dumpchar); curmodes &= ~ULINEOFF; } if (blink==0 && (curmodes & WBLINK)) { if (com = WEstr) tputs (com, 0, dumpchar); curmodes &= ~BLINKOFF; } if (hl==0 && (curmodes & WINVERSE)) { if (com = HLEstr) tputs (com, 0, dumpchar); curmodes &= ~HLOFF; } if (bold != 0 && (curmodes & WBOLD) == 0 && (com = BSstr)) tputs (com, 0, dumpchar); if (under != 0 && (curmodes & WULINE) == 0 && (com = ULstr)) tputs (com, 0, dumpchar); if (blink != 0 && (curmodes & WBLINK) == 0 && (com = WSstr)) tputs (com, 0, dumpchar); if (hl != 0 && (curmodes & WINVERSE) == 0 && (com = HLBstr)) tputs (com, 0, dumpchar); curmodes = desmodes; } static clearmodes () { if (curmodes) { register oldes = desmodes; desmodes = 0; setmodes (); desmodes = oldes; } } static setmode () { if (DesMode == CurMode) return; tputs (DesMode==m_insert ? ICstr : ICEstr, 0, dumpchar); CurMode = DesMode; }; static inslines (n) { clearmodes (); while (--n >= 0) tputs (ILstr, W_tt.t_length-curY, dumpchar); }; static dellines (n) { register save = n; while (--n >= 0) tputs(DLstr, W_tt.t_length-curY, dumpchar); if (DBflag) { /* Must clear new lines */ if (CteoDstr) { topos (W_tt.t_length - save + 1, 1); tputs (CteoDstr, W_tt.t_length-curY, dumpchar); } else { register i; for (i=W_tt.t_length-save+1;i<=W_tt.t_length;++i) { topos (i, 1); wipeline (0, W_tt.t_width); } } } }; static writechars (start, end) register char *start, *end; { setmode (); setmodes(); while (start <= end) { if(CurMode == m_insert && ICPstr) tputs(ICPstr, W_tt.t_width-curX, dumpchar); /* DJH -- blank out space before underlines */ if(*start == '_' && CurMode != m_insert && ULflag) { putchar (' '); putchar (*BC); } if (HZflag && *start == '~') putchar ('`'), ++start; else putchar (*start++); if (CurMode == m_insert && ICPDstr) tputs(ICPDstr, W_tt.t_width-curX, dumpchar); curX++; } }; static blanks (n) { setmode (); setmodes (); while (--n >= 0) { if (CurMode == m_insert && ICPstr) tputs (ICPstr, W_tt.t_width - curX, dumpchar); putchar (' '); if (CurMode == m_insert && ICPDstr) tputs (ICPDstr, W_tt.t_width - curX, dumpchar); curX++; } }; /* ACT 8-Sep-1982 Commented all the pad stuff out, termlib already takes care of it. */ static float BaudFactor; /* static pad(n,f) float f; { register k = n * f * BaudFactor; while (--k >= 0) putchar (W_tt.t_padc); }; */ static char *CM, *bufp, save[CMBUFSIZ], *bufbase; static CMcost, FixCM; #define SETSTUFF(buf) bufp=bufbase=buf #define SETBASE(buf) bufbase=buf stuffchar (c) register char c; { if (bufp - bufbase < CMBUFSIZ) *bufp++ = c; } /* * Compute cost for going from (sy, sx) to (dy, dx) with extra (add) * and if less than current least-cost, change current least-cost. * Algorithm is: * 1. If we can use tabs, then try that; get as close as possible * without going too far. Then try using cursor right from there, * and cursor left from one tab stop later. Be careful not to go * past the end of the line. * 2. Try using cursor right and cursor left. * 3. Use the least length version of 1 & 2 * 4. Add in up/down motion(s) */ static testcost (sy, sx, dy, dx, add) register sy, sx, dy, dx; char *add; { static char tbuf1[CMBUFSIZ], mbuf[CMBUFSIZ]; /* Buffers for cursor motion stuff */ int tabcost, movecost, tmp, tmp2, tmp3; if (sx == dx && sy == dy) { movecost = 0; goto done; } if (dy > sy && !NLstr || dy < sy && !UP) /* Can't get there from here */ return; if (!XTflag && sx < dx) { /* Try tabs */ tmp = sx; SETSTUFF (tbuf1); while ((tmp2 = ((tmp - 1) & ~7) + 9) <= dx) tputs (TBstr, 0, stuffchar), tmp = tmp2; if (NDstr) { bcopy (tbuf1, mbuf, movecost = bufp - tbuf1); tmp3 = tmp; } if (tmp < dx) tputs (TBstr, 0, stuffchar), tmp = tmp2; if (tmp > dx && !BC || tmp > W_tt.t_length) tabcost = 9999; else { while (tmp-- > dx) tputs (BC, 0, stuffchar); tabcost = bufp - tbuf1; } if (NDstr) { bufp = mbuf + movecost; SETBASE (mbuf); tmp = tmp3; while (tmp++ < dx) tputs (NDstr, 0, stuffchar); movecost = bufp - mbuf; if (movecost < tabcost) { bcopy (mbuf, tbuf1, tabcost = movecost); } } } else tabcost = 9999; if (sx < dx) { if (NDstr) { SETSTUFF (mbuf); while (sx++ < dx) tputs (NDstr, 0, stuffchar); movecost = bufp - mbuf; } else movecost = 9999; } else { if (BC) { SETSTUFF (mbuf); while (sx-- > dx) tputs (BC, 0, stuffchar); movecost = bufp - mbuf; } else movecost = 9999; } if (movecost == 9999 && tabcost == 9999) return; if (tabcost < movecost) bcopy (tbuf1, mbuf, movecost = tabcost); bufp = mbuf + movecost; SETBASE (mbuf); if (sy < dy) while (sy++ < dy) tputs (NLstr, 0, stuffchar); else while (sy-- > dy) tputs (UP, 0, stuffchar); movecost = bufp - mbuf; done: SETSTUFF (tbuf1); tputs (add, 0, stuffchar); tabcost = bufp - tbuf1; /* I know, it's the wrong variable name */ if (movecost + tabcost < CMcost) { bcopy (tbuf1, save, tabcost); bcopy (mbuf, save + tabcost, movecost); CM = save; CMcost = movecost + tabcost; } }; /* Perhaps a word of explanation ... in case we manage to get nulls into the cursor motion string, we change them to 0377s first, or all of the library routines used will stop after the null. (0377 is just an "unlikely" code, especially because it has the high bit on.) Anyway, these must be converted back after the library routines are done. This is a horrible hack and will probably cause someone untold trouble later. -ACT */ static topos (row, column) { char *tgoto (); if (curY == row && curX == column) return; if (CursStr) { CM = tgoto (CursStr, column-1, row-1); SETSTUFF (save); tputs (CM, 0, stuffchar); CM = save; CMcost = bufp - save; if (FixCM) { /* Must change 0377's to 0's */ register char *s = save; register len = CMcost; for (;len--;++s) if ((*s & 0377) == 0377) *s = 0; } } else CMcost = 9999; if (HOstr) testcost (1, 1, row, column, HOstr); if (!XRflag) testcost (curY, 1, row, column, CRstr); if (RNflag && curY < W_tt.t_length) testcost (curY + 1, 1, row, column, "\r"); testcost (curY, curX, row, column, ""); if (LLstr) testcost (W_tt.t_length, 1, row, column, LLstr); if (! MSflag) clearmodes (); /* many terminals can't hack highlighting around cursor positioning. Silly twits! */ if (CurMode==m_insert && ! MIflag) { tputs(ICEstr, 0, dumpchar); /* some terminals can't move in */ CurMode = m_overwrite; /* insert mode -- JQJ */ } while (CMcost--) putchar (*CM++); /* Now, wasn't that easy? */ curX = column; curY = row; }; static flash () { /* dump a visible bell */ tputs (VBstr, 0, dumpchar); } static init (BaudRate) { static char tbuf[1024]; static char combuf[1024]; register char *temp; extern struct sgttyb WOld; extern int WTtyFd; char *fill = combuf; static inited; if (!inited) if (tgetent (tbuf, getenv ("TERM")) <= 0) { return 1; /* stty (WTtyFd, &WOld); quit (1, "No environment-specified terminal type -- see TSET(1), sh(1)\n"); */ } inited = 1; W_tt.t_needspaces = tgetflag ("in"); XTflag = ((WOld.sg_flags & TBDELAY) == XTABS) || tgetflag ("xt"); XRflag = tgetflag ("nc") || tgetflag ("xr"); XNflag = tgetflag ("xn") || tgetflag ("nn"); MSflag = tgetflag ("ms"); HZflag = tgetflag ("hz"); DBflag = tgetflag ("db"); RNflag = tgetflag ("rn"); DSstr = (temp = tgetstr ("ds", &fill)) ? temp : "\004\t\n\r"; if (!*DSstr) DSstr = 0; if (!XRflag) CRstr = (temp = tgetstr ("cr", &fill)) ? temp : "\r"; if (!XTflag) TBstr = (temp = tgetstr ("ta", &fill)) ? temp : "\t"; ILstr = tgetstr ("al", &fill); HOstr = tgetstr ("ho", &fill); LLstr = tgetstr ("ll", &fill); DLstr = tgetstr ("dl", &fill); ICstr = tgetstr ("im", &fill); ICEstr = tgetstr ("ei", &fill); MIflag = tgetflag ("mi"); /* can move in insert mode */ DCstr = tgetstr ("dc", &fill); ELstr = tgetstr ("ce", &fill); ESstr = tgetstr ("cl", &fill); if (tgetnum ("sg") < 1) { HLBstr = tgetstr ("so", &fill); HLEstr = tgetstr ("se", &fill); } if (tgetnum ("ug") < 1) { ULstr = tgetstr ("us", &fill); UEstr = tgetstr ("ue", &fill); } WSstr = tgetstr ("ws", &fill); WEstr = tgetstr ("we", &fill); BSstr = tgetstr ("bo", &fill); BEstr = tgetstr ("be", &fill); ICPstr = tgetstr ("ic", &fill); ICPDstr = tgetstr ("ip", &fill); CursStr = tgetstr ("cm", &fill); if (DBflag) CteoDstr = tgetstr ("cd", &fill); UP = tgetstr ("up", &fill); NDstr = tgetstr ("nd", &fill); VBstr = tgetstr ("vb", &fill); TIstr = tgetstr ("ti", &fill); TEstr = tgetstr ("te", &fill); DMstr = tgetstr ("dm", &fill);/* start delete mode */ EDstr = tgetstr ("ed", &fill);/* end delete mode */ VSstr = tgetstr ("vs", &fill); VEstr = tgetstr ("ve", &fill);/* ``visual'' start/end -ACT */ if (tgetflag ("bs")) BC = "\b"; else BC = tgetstr ("bc", &fill); ULflag = tgetflag ("ul"); /* DJH -- Find out about underline */ if (!XNflag) NLstr = (temp = tgetstr ("nl", &fill)) ? temp : "\n"; PC = W_tt.t_padc = (temp = tgetstr ("pc", &fill)) ? *temp : 0; /* Where does he get this weird formula?? ACT */ /* BaudFactor = 1 / (1 - (.45 + .3*BaudRate/9600.)) * (BaudRate/10000.);*/ BaudFactor = ((float) BaudRate) / 10000.; if (!ESstr || (CursStr ? DSstr && !UP && !BC : !UP || !BC || !NLstr || !NDstr)) { return 1; /* stty (WTtyFd, &WOld); quit (1, "Sorry, this terminal isn't powerful enough to run windows.\n\ It is missing some important features.\n"); */ } W_tt.t_ILmf = BaudFactor * 0.75; if (ILstr) W_tt.t_ILov = 2; else { W_tt.t_ILov = MissingFeature; W_tt.t_inslines = W_tt.t_dellines = (int (*) ()) - 1; } if (VBstr) W_tt.t_flash = flash; if (ICstr) { W_tt.t_ICmf = 1; W_tt.t_ICov = strlen(ICstr) + (ICEstr ? strlen(ICEstr) : 0) + (ICPstr ? strlen(ICPstr) : 0) + (ICPDstr ? strlen(ICPDstr) : 0); } else W_tt.t_ICmf = W_tt.t_ICov = MissingFeature; if (DCstr) { W_tt.t_DCmf = strlen (DCstr); W_tt.t_DCov = (DMstr ? strlen(DMstr) : 0) + (EDstr ? strlen(EDstr) : 0); } else W_tt.t_DCmf = W_tt.t_DCov = MissingFeature; temp = getenv ("PL"); /* (ACT) Test for desired page len */ W_tt.t_length = temp && atoi (temp) ? atoi (temp) : tgetnum ("li"); temp = getenv ("COL"); /* (ACT) Test for desired line len */ W_tt.t_width = (temp && atoi (temp) ? atoi (temp) : tgetnum ("co")) - (tgetflag ("am") ? 1 : 0); BOLDOFF = WBOLD; /* Turning off bold turns off bold */ BLINKOFF = WBLINK; /* Turning off blink turns off blink */ ULINEOFF = WULINE; /* Turning off uline turns off uline */ HLOFF = WINVERSE; /* Turning off hl turns off hl */ if (lstrcmp (BEstr, WEstr) == 0) { BOLDOFF |= WBLINK; /* bold & blink interrelate */ BLINKOFF |= WBOLD; } if (lstrcmp (BEstr, UEstr) == 0) { BOLDOFF |= WULINE; /* bold & under interrelate */ ULINEOFF |= WBOLD; } if (lstrcmp (BEstr, HLEstr) == 0) { BOLDOFF |= WINVERSE; /* etc */ HLOFF |= WBOLD; } if (lstrcmp (WEstr, UEstr) == 0) { BLINKOFF |= WULINE; ULINEOFF |= WBLINK; } if (lstrcmp (WEstr, HLEstr) == 0) { BLINKOFF |= WINVERSE; HLOFF |= WBLINK; } if (lstrcmp (UEstr, HLEstr) == 0) { ULINEOFF |= WINVERSE; HLOFF |= WULINE; } return 0; }; static lstrcmp (s1, s2) /* Version of strcmp takes care of NULL */ register char *s1, *s2; { if (s1 == 0 || s2 == 0) return 1; /* Don't bother -- call 'em different */ while (*s1 == *s2++) if (*s1++ == 0) return 0; return 1; /* Order not important */ } static reset () { if (TIstr) tputs(TIstr, 0, dumpchar); if (VSstr) tputs(VSstr, 0, dumpchar); tputs(ESstr, 0, dumpchar); /* Also homes cursor: */ curX = curY = 1; CurMode = m_insert; DesMode = m_overwrite; }; static cleanup () { modes (0); setmodes (); DesMode = m_overwrite; setmode(); topos (W_tt.t_length, 1); if (VEstr) tputs(VEstr, 0, dumpchar); if (TEstr) tputs(TEstr, 0, dumpchar); }; static wipeline (z,p) register p; { clearmodes (); if (ELstr) tputs (ELstr, W_tt.t_width-curX, dumpchar); else { register i = p - curX + 1/*, oldX = curX*/; if (i < 1) return; if (CurMode == m_insert) { tputs(ICEstr, 0, dumpchar); /* We don't want these inserted */ CurMode = m_overwrite; } while (--i >= 0) putchar(' '), ++curX; /* topos (curY, oldX); /* This appears to be unnecessary */ } }; static wipescreen () { tputs(ESstr, 0, dumpchar); curX = curY = 1; }; static delchars (n) { if (DMstr) { /* we may have delete mode, or delete == insert */ if (strcmp(DMstr,ICstr) /*ACT*/ == 0 /*END*/) { if (CurMode == m_overwrite) { tputs(ICstr,0,dumpchar); CurMode = m_insert; /* we're now in both */ } } else { if (CurMode == m_insert) { tputs(ICEstr, 0, dumpchar); CurMode = m_overwrite; } tputs(DMstr,0,dumpchar); } } while (--n >= 0) { tputs(DCstr, W_tt.t_width-curX, dumpchar); } if (EDstr) { /* for some, insert mode == delete mode */ /* bug! /etc/termcap pads ICEstr but not EDstr */ if (strcmp(DMstr,ICstr) /*ACT*/ == 0 /*END*/) CurMode = m_insert; /* (ACT) But you already did this! */ else tputs(EDstr,0,dumpchar); } }; TrmTERM () { W_tt.t_INSmode = INSmode; W_tt.t_modes = modes; W_tt.t_inslines = inslines; W_tt.t_dellines = dellines; W_tt.t_blanks = blanks; W_tt.t_init = init; W_tt.t_cleanup = cleanup; W_tt.t_wipeline = wipeline; W_tt.t_wipescreen = wipescreen; W_tt.t_topos = topos; W_tt.t_reset = reset; W_tt.t_delchars = delchars; W_tt.t_writechars = writechars; W_tt.t_window = 0; W_tt.t_ILmf = 0; W_tt.t_ILov = 0; W_tt.t_ICmf = 0; W_tt.t_ICov = 0; W_tt.t_length = 24; W_tt.t_width = 80; return 0; }; /* * A home-brew "tgoto" with some special mods * If termlib ever changes this'll need updating * * %d decimal * %2 %2d * %3 %3d * %. binary, with "don't send"s * %+x Adds x, then like binary * %>xy if >x add y * %r next is col * %i Increment row/col * %% = % * %B BCD * %D Backwards BCD * %m xor with 0177 * % like %+ */ static char * tgoto (CM, col, line) char *CM; int col, line; { static char cmbuf[50], add[20]; register char *cp = CM, *op = cmbuf; register c; int val = line, toggle = 0; if (! cp) return "OOPS"; *add = 0; *op = 0; FixCM = 0; while (c = *cp++) { if (c != '%') { *op++ = c; continue; } switch (c = *cp++) { case 'm': col ^= 0177; line ^= 0177; goto setval; case 'n': col ^= 0140; line ^= 0140; goto setval; case 'd': if (val < 10) goto onedigit; if (val < 100) goto twodigit; case '3': *op++ = (val / 100) + '0'; val %= 100; case '2': twodigit: *op++ = (val / 10) + '0'; onedigit: *op++ = (val % 10) + '0'; swap: toggle = 1 - toggle; setval: val = toggle ? col : line; continue; case '>': if (val > *cp++) val += *cp++; else cp++; continue; case '+': val += *cp++; case '.': use: if (DSstr) { while (val == 0 || index (DSstr, val)) { strcat (add, toggle ? BC : UP); ++val; } } else if (val == 0) { *op++ = 0377; FixCM = 1; goto swap; } *op++ = val; goto swap; case 'r': toggle = 1; goto setval; case 'i': ++col, ++line, ++val; continue; case '%': *op++ = c; continue; case 'B': val = ((val / 10) << 4) + val % 10; continue; case 'D': val = val - 2 * (val % 16); continue; default: val += c; goto use; } } *op = 0; strcat (op, add); return cmbuf; }