home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / vtrm / part02 / vtrm.c
Encoding:
C/C++ Source or Header  |  1987-02-11  |  37.3 KB  |  1,803 lines

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1986. */
  2.  
  3. #define lenline len_line /* Avoid name conflict with lenline in tex.c */
  4.  
  5. /*
  6.  * Virtual TeRMinal package.
  7.  * (For a description see at the end of this file.)
  8.  *
  9.  * TO DO:
  10.  *    - add interrupt handling (trminterrupt)
  11.  *    - adapt to changed window size when suspended or at SIGWINCH
  12.  *      (unfortunately, the calling module must be changed first --
  13.  *      it is not prepared for the changed window size...)
  14.  */
  15.  
  16. /*
  17.  * Includes and data definitions.
  18.  */
  19.  
  20. #include <stdio.h>
  21. #ifndef TERMIO
  22. #include <sgtty.h>
  23. #else
  24. #include <termio.h>
  25. #endif TERMIO
  26. #include <signal.h>
  27. #include <ctype.h>
  28.  
  29. #include "trm.h"
  30.  
  31. char *getenv();
  32. int tgetent();
  33. int tgetnum();
  34. int tgetflag();
  35. char *tgetstr();
  36.  
  37. #ifdef TRACE
  38. #define Tprintf(args) fprintf args
  39. #else
  40. #define Tprintf(args) /*empty*/
  41. #endif
  42.  
  43. #ifdef lint
  44. #define VOID (void)
  45. #else
  46. #define VOID
  47. #endif
  48.  
  49. #define Forward
  50. #define Visible
  51. #define Hidden static
  52. #define Procedure
  53.  
  54. typedef char *string;
  55. typedef int bool;
  56. #define Yes 1
  57. #define No  0
  58.  
  59. #define Min(a,b) ((a) <= (b) ? (a) : (b))
  60.  
  61. /* tty modes */
  62. #ifndef TERMIO
  63.  
  64. /* v7/BSD tty control */
  65. Hidden struct sgttyb oldtty, newtty;
  66. #ifdef TIOCSETN
  67. /* Redefine stty to uses TIOCSETN, so type-ahead is not flushed */
  68. #define stty(fd, bp) VOID ioctl(fd, (int) TIOCSETN, (char *) bp)
  69. #endif
  70.  
  71. #ifdef TIOCSLTC /* BSD -- local special chars, must all be turned off */
  72. static struct ltchars oldltchars;
  73. static struct ltchars newltchars= {-1, -1, -1, -1, -1, -1};
  74. #endif TIOCSLTC
  75.  
  76. #ifdef TIOCSETC /* V7 -- standard special chars, some must be turned off too */
  77. static struct tchars oldtchars;
  78. static struct tchars newtchars;
  79. #endif TIOCSETC
  80.  
  81. #else TERMIO
  82.  
  83. /* AT&T tty control */
  84. Hidden struct termio oldtty, newtty;
  85. #define gtty(fd,bp) ioctl(fd, TCGETA, (char *) bp)
  86. #define stty(fd,bp) VOID ioctl(fd, TCSETAW, (char *) bp)
  87.  
  88. #endif TERMIO
  89.  
  90. Hidden bool know_ttys = No;
  91.  
  92. /* visible data for termcap */
  93. char PC;
  94. char *BC;
  95. char *UP;
  96. short ospeed;
  97.  
  98. Forward int outchar();         /* procedure for termcap's tputs */
  99. #define Putstr(str)    tputs((str), 1, outchar)
  100. extern char *tgoto();
  101.  
  102. /* termcap terminal capabilities */
  103.  
  104. Hidden int lines;
  105. Hidden int cols;
  106.  
  107. /*
  108.  * String-valued capabilities are saved in one big array.
  109.  * Extend this only at the end (even though it disturbs the sorting)
  110.  * because otherwise you have to change all macros...
  111.  */
  112.  
  113. #define par_al_str strcaps[0]    /* parametrized al (AL) */
  114. #define cap_cm_str strcaps[1]    /* screen-relative cursor motion (CM) */
  115. #define par_dl_str strcaps[2]    /* parametrized dl (DL) */
  116. #define al_str strcaps[3]     /* add new blank line */
  117. #define cd_str strcaps[4]     /* clear to end of display */
  118. #define ce_str strcaps[5]     /* clear to end of line */
  119. #define cl_str strcaps[6]     /* cursor home and clear screen */
  120. #define cm_str strcaps[7]     /* cursor motion */
  121. #define cp_str strcaps[8]    /* cursor position sense reply */
  122. #define cr_str strcaps[9]     /* carriage return */
  123. #define cs_str strcaps[10]     /* change scrolling region */
  124. #define dc_str strcaps[11]     /* delete character */
  125. #define dl_str strcaps[12]     /* delete line */
  126. #define dm_str strcaps[13]     /* enter delete mode */
  127. #define do_str strcaps[14]     /* cursor down one line */
  128. #define ed_str strcaps[15]     /* end delete mode */
  129. #define ei_str strcaps[16]     /* end insert mode */
  130. #define ho_str strcaps[17]    /* cursor home */
  131. #define ic_str strcaps[18]     /* insert character (if necessary; may pad) */
  132. #define im_str strcaps[19]     /* enter insert mode */
  133. #define nd_str strcaps[20]     /* cursor right (non-destructive space) */
  134. #define nl_str strcaps[21]    /* newline */
  135. #define se_str strcaps[22]     /* end standout mode */
  136. #define sf_str strcaps[23]     /* scroll text up (from bottom of region) */
  137. #define so_str strcaps[24]     /* begin standout mode */
  138. #define sp_str strcaps[25]    /* sense cursor position */
  139. #define sr_str strcaps[26]     /* scroll text down (from top of region) */
  140. #define te_str strcaps[27]     /* end termcap */
  141. #define ti_str strcaps[28]     /* start termcap */
  142. #define vb_str strcaps[29]     /* visible bell */
  143. #define ve_str strcaps[30]     /* make cursor visible again */
  144. #define vi_str strcaps[31]     /* make cursor invisible */
  145. #define le_str strcaps[32]     /* cursor left */
  146. #define bc_str strcaps[33]    /* backspace character */
  147. #define up_str strcaps[34]     /* cursor up */
  148. #define pc_str strcaps[35]    /* pad character */
  149. #define ks_str strcaps[36]    /* keypad mode start */
  150. #define ke_str strcaps[37]    /* keypad mode end */
  151. /* Insert new entries here only! Don't forget to change the next line! */
  152. #define NSTRCAPS 38 /* One more than the last entry's index */
  153.  
  154. Hidden char *strcaps[NSTRCAPS];
  155. Hidden char strcapnames[] =
  156. "ALCMDLalcdceclcmcpcrcsdcdldmdoedeihoicimndnlsesfsospsrtetivbvevilebcuppckske";
  157.  
  158. /* Same for Boolean-valued capabilities */
  159.  
  160. #define has_am flagcaps[0]    /* has automatic margins */
  161. #define has_da flagcaps[1]    /* display may be retained above screen */
  162. #define has_db flagcaps[2]    /* display may be retained below screen */
  163. #define has_in flagcaps[3]    /* not save to have null chars on the screen */
  164. #define has_mi flagcaps[4]    /* move safely in insert (and delete?) mode */
  165. #define has_ms flagcaps[5]    /* move safely in standout mode */
  166. #define has_xs flagcaps[6]    /* standout not erased by overwriting */
  167. #define has_bs flagcaps[7]    /* terminal can backspace */
  168. #define hardcopy flagcaps[8]    /* hardcopy terminal */
  169. #define NFLAGS 9
  170.  
  171. Hidden char flagcaps[NFLAGS];
  172. Hidden char flagnames[]= "amdadbinmimsxsbshc";
  173.  
  174. Hidden Procedure
  175. getcaps(parea)
  176.     register char *parea;
  177. {
  178.     register char *capname;
  179.     register char **capvar;
  180.     register char *flagvar;
  181.  
  182.     for (capname= flagnames, flagvar= flagcaps;
  183.             *capname != '\0'; capname += 2, ++flagvar)
  184.         *flagvar= tgetflag(capname);
  185.  
  186.     for (capname= strcapnames, capvar= strcaps;
  187.             *capname != '\0'; capname += 2, ++capvar)
  188.         *capvar= tgetstr(capname, parea);
  189. }
  190.  
  191. /* terminal status */
  192.  
  193. /* calling order of Visible Procs */
  194. Hidden bool started = No;
  195.  
  196. /* to exports the capabilities mentioned in vtrm.h: */
  197. Hidden int flags = 0;
  198.  
  199. /* cost for impossible operations */
  200. #define Infinity 9999
  201.     /* Allow for adding Infinity+Infinity within range */
  202.     /* (Range is assumed at least 2**15 - 1) */
  203.  
  204. /* The following for all sorts of undefined things (except for UNKNOWN char) */
  205. #define Undefined (-1)
  206.  
  207. /* current mode of putting char's */
  208. #define Normal    0
  209. #define Insert    1
  210. #define    Delete    2
  211. Hidden short mode = Normal;
  212.  
  213. /* current standout mode */
  214. #define Off    0
  215. #define On    0200
  216. Hidden short so_mode = Off;
  217.  
  218. /* masks for char's and short's */
  219. #define NULCHAR    '\000'
  220. #define CHAR    0177
  221. #define SOBIT    On
  222. #define SOCHAR    0377
  223. /* if (has_xs) record cookies placed on screen in extra bit */
  224. /* type of cookie is determined by the SO bit */
  225. #define XSBIT    0400
  226. #define SOCOOK    0600
  227. #define COOKBITS SOCOOK
  228. #define UNKNOWN    1
  229. #define NOCOOK    UNKNOWN
  230.  
  231. /* current cursor position */
  232. Hidden short cur_y = Undefined, cur_x = Undefined;
  233.  
  234. /* "line[y][x]" holds the char on the terminal, with the SOBIT and XSBIT.
  235.  * the SOBIT tells whether the character is standing out, the XSBIT whether
  236.  * there is a cookie on the screen at this position.
  237.  * In particular a standend-cookie may be recorded AFTER the line
  238.  * (just in case some trmputdata will write after that position).
  239.  * "lenline[y]" holds the length of the line.
  240.  * Unknown chars will be 1, so the optimising compare in putline will fail.
  241.  * (Partially) empty lines are distinghuished by "lenline[y] < cols".
  242.  */
  243. Hidden short **line = 0, *lenline = 0;
  244.  
  245. /* Clear the screen initially iff only memory cursor addressing available */
  246. Hidden bool mustclear = No;
  247.  
  248. /* Make the cursor invisible when trmsync() tries to move outside the screen */
  249. Hidden bool no_cursor = No;
  250.  
  251. /* Optimise cursor motion */
  252. Hidden int abs_cost;         /* cost of absolute cursor motion */
  253. Hidden int cr_cost;         /* cost of carriage return */
  254. Hidden int do_cost;         /* cost of down */
  255. Hidden int le_cost;         /* cost of left */
  256. Hidden int nd_cost;         /* cost of right */
  257. Hidden int up_cost;         /* cost of up */
  258.  
  259. /* Optimise trailing match in put_line, iff the terminal can insert and delete
  260.  * characters; the cost per n characters will be:
  261.  *     n * MultiplyFactor + OverHead
  262.  */
  263. Hidden int ins_mf, ins_oh, del_mf, del_oh;
  264. Hidden int ed_cost, ei_cost;         /* used in move() */
  265.  
  266. /* The type of scrolling possible determines which routines get used;
  267.  * these may be:
  268.  * (1) with addline and deleteline (termcap: al_str & dl_str);
  269.  * (2) with a settable scrolling region, like VT100 (cs_str, sr_str, sf_str);
  270.  * (3) no scrolling available. (NOT YET IMPLEMENTED)
  271.  */
  272. Hidden Procedure (*scr_up)();
  273. Hidden Procedure (*scr_down)();
  274. Forward Procedure scr1up();
  275. Forward Procedure scr1down();
  276. Forward Procedure scr2up();
  277. Forward Procedure scr2down();
  278. /*Forward Procedure scr3up(); */
  279. /*Forward Procedure scr3down(); */
  280.  
  281. /*
  282.  * Starting, Ending and (fatal) Error.
  283.  */
  284.  
  285. /* 
  286.  * Initialization call.
  287.  * Determine terminal capabilities from termcap.
  288.  * Set up tty modes.
  289.  * Start up terminal and internal administration.
  290.  * Return 0 if all well, error code if in trouble.
  291.  */
  292. Visible int
  293. trmstart(plines, pcols, pflags)
  294. int *plines;
  295. int *pcols;
  296. int *pflags;
  297. {
  298.     register int err;
  299.     
  300.     Tprintf((stderr, "\ttrmstart(&li, &co, &fl);\n"));
  301.     if (started)
  302.         return TE_TWICE;
  303.     err= gettermcaps();
  304.     if (err != TE_OK)
  305.         return err;
  306.     err= setttymode();
  307.     if (err != TE_OK)
  308.         return err;
  309.     err= start_trm();
  310.     if (err != TE_OK) {
  311.         trmend();
  312.         return err;
  313.     }
  314.  
  315.     *plines = lines;
  316.     *pcols = cols;
  317.     *pflags = flags;
  318.  
  319.     started = Yes;
  320.     return TE_OK;
  321. }
  322.  
  323. /*
  324.  * Termination call.
  325.  * Reset tty modes, etc.
  326.  * Beware that it might be called by a caught interrupt even in the middle
  327.  * of trmstart()!
  328.  */
  329. Visible Procedure
  330. trmend()
  331. {
  332.     Tprintf((stderr, "\ttrmend();\n"));
  333.     set_mode(Normal);
  334.     if (so_mode != Off)
  335.         standend();
  336.     Putstr(ke_str);
  337.     Putstr(te_str);
  338.     VOID fflush(stdout);
  339.     resetttymode();
  340.  
  341.     started = No;
  342. }
  343.  
  344. /*
  345.  * Set all internal statuses to undefined, especially the contents of
  346.  * the screen, so a hard redraw will not be optimised to heaven.
  347.  */
  348. Visible Procedure
  349. trmundefined()
  350. {
  351.     register int y, x;
  352.     Tprintf((stderr, "\ttrmundefined();\n"));
  353.  
  354.     cur_y = cur_x = Undefined;
  355.     mode = so_mode = Undefined;
  356.     
  357.     for (y = 0; y < lines; y++) {
  358.         for (x = 0; x <= cols; x++)
  359.             line[y][x] = 1; /* impossible char, no so bits */
  360.         lenline[y] = cols;
  361.     }
  362. }
  363.  
  364. #ifndef NDEBUG
  365. Hidden Procedure
  366. check_started(m)
  367. char *m;
  368. {
  369.     if (!started) {
  370.         trmend();
  371.         fprintf(stderr, "bad VTRM call\n");
  372.         abort();
  373.     }
  374. }
  375. #else
  376. #define check_started(m) /*empty*/
  377. #endif
  378.  
  379. Hidden int ccc;
  380.  
  381. /*ARGSUSED*/
  382. Hidden Procedure
  383. countchar(ch)
  384. char ch;
  385. {
  386.     ccc++;
  387. }
  388.  
  389. Hidden int
  390. strcost(str)
  391. char *str;
  392. {
  393.     if (str == NULL)
  394.         return Infinity;
  395.     return str0cost(str);
  396. }
  397.  
  398. Hidden int
  399. str0cost(str)
  400. char *str;
  401. {
  402.     ccc = 0;
  403.     tputs(str, 1, countchar);
  404.     return ccc;
  405. }
  406.  
  407. /*
  408.  * Get terminal capabilities from termcap and compute related static
  409.  * properties.  Return TE_OK if all well, error code otherwise.
  410.  *
  411.  * TO DO:
  412.  *    - use the curses trick of a reading the capabilities in a loop
  413.  *      rather than one at a time.
  414.  */
  415.  
  416. Hidden int
  417. gettermcaps() 
  418. {
  419.     string trmname;
  420.     char tc_buf[1024];
  421.     static char strbuf[1024];
  422.     char *area = strbuf;
  423.     int sg;
  424.     static bool tc_initialized = No;
  425. #ifdef TIOCGWINSZ
  426.     struct winsize win;
  427. #endif
  428.  
  429.     if (tc_initialized)
  430.         return TE_OK;
  431.     
  432.     trmname=getenv("TERM");
  433.     if (trmname == NULL || trmname[0] == '\0')
  434.         return TE_NOTERM;
  435.     if (tgetent(tc_buf, trmname) != 1)
  436.         return TE_BADTERM;
  437.  
  438.     getcaps(&area); /* Read all flag and string type capabilities */
  439.     if (hardcopy)
  440.         return TE_DUMB;
  441.     BC = le_str;
  442.     if (BC == NULL) {
  443.         BC = bc_str;
  444.         if (BC == NULL) {
  445.             if (has_bs)
  446.                 BC = "\b";
  447.             else
  448.                 return TE_DUMB;
  449.         }
  450.     }
  451.     UP = up_str;
  452.     if (UP == NULL)
  453.         return TE_DUMB;
  454.     PC = (pc_str != NULL? pc_str[0] : NULCHAR);
  455.  
  456.     if (cm_str == NULL) {
  457.         cm_str = cap_cm_str;
  458.         if (cm_str == NULL) {
  459.             if (ho_str == NULL || do_str == NULL || nd_str == NULL)
  460.                 return TE_DUMB;
  461.         }
  462.         else
  463.             mustclear = Yes;
  464.     }
  465.     if (al_str && dl_str) {
  466.         scr_up = scr1up;
  467.         scr_down = scr1down;
  468.         flags |= CAN_SCROLL;
  469.     }
  470.     else {
  471.         if (sf_str == NULL)
  472.             sf_str = "\n";
  473.         if (cs_str && sr_str) {
  474.             scr_up = scr2up;
  475.             scr_down = scr2down;
  476.             flags |= CAN_SCROLL;
  477.         }
  478.         else
  479.             return TE_DUMB;
  480.     }
  481.         
  482.     lines = tgetnum("li");
  483.     cols = tgetnum("co");
  484. #ifdef TIOCGWINSZ
  485.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  486.         if (win.ws_col > 0)
  487.             cols = win.ws_col;
  488.         if (win.ws_row > 0)
  489.             lines = win.ws_row;
  490.     }
  491. #endif
  492.     if (lines < 0) lines = 24;
  493.     if (cols < 0) cols = 80;
  494.     
  495.     if ((sg=tgetnum("sg")) == 0)
  496.         has_xs = Yes;
  497.     else if (sg > 0)
  498.         return TE_DUMB;
  499.     
  500.     if (!ce_str)
  501.         return TE_DUMB;
  502.     if (cr_str == NULL) cr_str = "\r";
  503.     if (do_str == NULL) {
  504.         do_str = nl_str;
  505.         if (do_str == NULL) do_str = "\n";
  506.     }
  507.     le_str = BC;
  508.     up_str = UP;
  509.     if (vb_str == NULL)     /* then we will do with the audible bell */
  510.         vb_str = "\007";
  511.     
  512.     /* cursor sensing (non standard) */
  513.     if (cp_str != NULL && sp_str != NULL)
  514.         flags |= CAN_SENSE;
  515.  
  516.     if (so_str != NULL && se_str != NULL)
  517.         flags |= HAS_STANDOUT;
  518.  
  519.     /* calculate costs of local and absolute cursor motions */
  520.     if (cm_str == NULL)
  521.         abs_cost = Infinity;
  522.     else
  523.         abs_cost = strcost(tgoto(cm_str, 0, 0));
  524.     cr_cost = strcost(cr_str);
  525.     do_cost = strcost(do_str);
  526.     le_cost = strcost(le_str);
  527.     nd_cost = strcost(nd_str);
  528.     up_cost = strcost(up_str);
  529.  
  530.     /* cost of leaving insert or delete mode, used in move() */
  531.     ei_cost = str0cost(ei_str);
  532.     ed_cost = str0cost(ed_str);
  533.     
  534.     /* calculate insert and delete cost multiply_factor and overhead */
  535.     if (((im_str && ei_str) || ic_str) && dc_str) {
  536.         flags |= CAN_OPTIMISE;
  537.         ins_mf = 1 + str0cost(ic_str);
  538.         ins_oh = str0cost(im_str) + ei_cost;
  539.         del_mf = str0cost(dc_str);
  540.         del_oh = str0cost(dm_str) + ed_cost;
  541.     }
  542.         
  543.     tc_initialized = Yes;
  544.     return TE_OK;
  545. }
  546.  
  547. Hidden int
  548. setttymode()
  549. {
  550.     if (!know_ttys) {
  551.         if (gtty(0, &oldtty) != 0 || gtty(0, &newtty) != 0)
  552.             return TE_NOTTY;
  553. #ifndef TERMIO
  554.         ospeed = oldtty.sg_ospeed;
  555. #ifdef PWB
  556.         newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS)
  557.                   | RAW;
  558. #else PWB
  559.         newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS)
  560.                   | CBREAK;
  561. #endif PWB
  562. #ifdef TIOCSLTC
  563.     VOID ioctl(0, (int) TIOCGLTC, (char *) &oldltchars);
  564. #endif
  565. #ifdef TIOCSETC
  566.     VOID ioctl(0, (int) TIOCGETC, (char *) &oldtchars);
  567. #endif
  568.  
  569. #else TERMIO
  570.         ospeed= oldtty.c_lflag & CBAUD;
  571.         newtty.c_iflag &= ~ICRNL; /* No CR->NL mapping on input */
  572.         newtty.c_oflag &= ~ONLCR; /* NL doesn't output CR */
  573.         newtty.c_lflag &= ~(ICANON|ECHO); /* No line editing, no echo */
  574.         newtty.c_cc[VMIN]= 3; /* wait for 3 characters */
  575.         newtty.c_cc[VTIME]= 1; /* or 0.1 sec. */
  576. #endif TERMIO
  577.         know_ttys = Yes;
  578.     }
  579.     stty(0, &newtty);
  580. #ifndef TERMIO
  581. #ifdef TIOCSLTC
  582.     VOID ioctl(0, (int) TIOCSLTC, (char *) &newltchars);
  583. #endif TIOCSLTC
  584. #ifdef TIOCSETC
  585.     VOID ioctl(0, (int) TIOCGETC, (char *) &newtchars);
  586.     newtchars.t_intrc= -1;
  587. #ifndef DEBUG
  588.     newtchars.t_quitc= -1;
  589. #endif
  590.     newtchars.t_eofc= -1;
  591.     newtchars.t_brkc= -1;
  592.     VOID ioctl(0, (int) TIOCSETC, (char *) &newtchars);
  593. #endif TIOCSETC
  594. #endif TERMIO
  595.     return TE_OK;
  596. }
  597.  
  598. Hidden Procedure
  599. resetttymode()
  600. {
  601.     if (know_ttys) {
  602.         stty(0, &oldtty);
  603. #ifndef TERMIO
  604. #ifdef TIOCSLTC
  605.         VOID ioctl(0, (int) TIOCSLTC, (char *) &oldltchars);
  606. #endif TIOCSLTC
  607. #ifdef TIOCSETC
  608.         VOID ioctl(0, (int) TIOCSETC, (char *) &oldtchars);
  609. #endif TIOCSETC
  610. #endif TERMIO
  611.         know_ttys= No;
  612.     }
  613. }
  614.  
  615. Hidden int
  616. start_trm()
  617. {
  618.     register int y;
  619.  
  620.     if (line == NULL) {
  621.         if ((line = (short**) malloc(lines * sizeof(short*))) == NULL)
  622.             return TE_NOMEM;
  623.         for (y = 0; y < lines; y++) {
  624.             if ((line[y] = (short*)
  625.                 malloc((cols+1) * sizeof(short))) == NULL)
  626.                 return TE_NOMEM;
  627.         }
  628.     }
  629.     if (lenline == NULL) {
  630.         if ((lenline = (short*) malloc(lines * sizeof(short))) == NULL)
  631.             return TE_NOMEM;
  632.     }
  633.  
  634.     trmundefined();
  635.  
  636.     Putstr(ti_str);
  637.     Putstr(ks_str);
  638.     if (cs_str)
  639.         Putstr(tgoto(cs_str, lines-1, 0));
  640.     if (mustclear)
  641.         clear_lines(0, lines-1);
  642.     return TE_OK;
  643. }
  644.  
  645.  
  646. /*
  647.  * Sensing and moving the cursor.
  648.  */
  649.  
  650. /*
  651.  * Sense the current (y, x) cursor position, after a possible manual
  652.  * change by the user with local cursor motions.
  653.  * If the terminal cannot be asked for the current cursor position,
  654.  * or if the string returned by the terminal is garbled,
  655.  * the position is made Undefined.
  656.  */
  657.  
  658. Visible Procedure
  659. trmsense(py, px)
  660.     int *py;
  661.     int *px;
  662. {
  663.     bool getpos();
  664.  
  665.     Tprintf((stderr, "\ttrmsense(&yy, &xx);\n"));
  666.     check_started("trmsense");
  667.  
  668.     *py = *px = Undefined;
  669.     set_mode(Normal);
  670.     if (so_mode != Off)
  671.         standend();
  672.     
  673.     if (flags&CAN_SENSE && getpos(py, px)) {
  674.         if (*py < 0 || lines <= *py || *px < 0 || cols <= *px)
  675.             *py = *px = Undefined;
  676.     }
  677.     cur_y = Undefined;
  678.     cur_x = Undefined;
  679. }
  680.  
  681. Hidden bool
  682. getpos(py, px)
  683. int *py, *px;
  684. {
  685.     char *format = cp_str;
  686.     int fc;         /* current format character */
  687.     int ic;         /* current input character */
  688.     int num;
  689.     int on_y = 1;
  690.     bool incr_orig = No;
  691.     int i, ni;
  692.  
  693.     Putstr(sp_str);
  694.     VOID fflush(stdout);
  695.  
  696.     while (fc = *format++) {
  697.         if (fc != '%') {
  698.             if (trminput() != fc)
  699.                 return No;
  700.         }
  701.         else {
  702.             switch (fc = *format++) {
  703.             case '%':
  704.                 if (trminput() != '%')
  705.                     return No;
  706.                 continue;
  707.             case 'r':
  708.                 on_y = 1 - on_y;
  709.                 continue;
  710.             case 'i':
  711.                 incr_orig = Yes;
  712.                 continue;
  713.             case 'd':
  714.                 ic = trminput();
  715.                 if (!isdigit(ic))
  716.                     return No;
  717.                 num = ic - '0';
  718.                 while (isdigit(ic=trminput()))
  719.                     num = 10*num + ic - '0';
  720.                 trmpushback(ic);
  721.                 break;
  722.             case '2':
  723.             case '3':
  724.                 ni = fc - '0';
  725.                     num = 0;
  726.                 for (i=0; i<ni; i++) {
  727.                     ic = trminput();
  728.                     if (isdigit(ic))
  729.                         num = 10*num + ic - '0';
  730.                     else
  731.                         return No;
  732.                 }
  733.                 break;
  734.             case '+':
  735.                 num = trminput() - *format++;
  736.                 break;
  737.             case '-':
  738.                 num = trminput() + *format++;
  739.                 break;
  740.             default:
  741.                 return No;
  742.             }
  743.             /* assign num to parameter */
  744.             if (incr_orig)
  745.                 num--;
  746.             if (on_y)
  747.                 *py = num;
  748.             else
  749.                 *px = num;
  750.             on_y = 1 - on_y;
  751.         }
  752.     }
  753.  
  754.     return Yes;
  755. }
  756.         
  757. /* 
  758.  * To move over characters by rewriting them, we have to check:
  759.  * (1) that the screen has been initialised on these positions;
  760.  * (2) we do not screw up characters
  761.  * when rewriting line[y] from x_from upto x_to
  762.  */
  763. Hidden bool
  764. rewrite_ok(y, xfrom, xto)
  765. int y, xfrom, xto;
  766. {
  767.     register short *plnyx, *plnyto;
  768.     
  769.     if (xto > lenline[y])
  770.         return No;
  771.  
  772.     plnyto = &line[y][xto];
  773.     for (plnyx = &line[y][xfrom]; plnyx <= plnyto; plnyx++)
  774.         if (*plnyx == UNKNOWN
  775.             ||
  776.             (!has_xs && (*plnyx & SOBIT) != so_mode)
  777.            )
  778.             return No;
  779.     return Yes;
  780. }
  781.         
  782. /*
  783.  * Move to position y,x on the screen
  784.  */
  785. /* possible move types for y and x respectively: */
  786. #define None    0
  787. #define Down    1
  788. #define Up    2
  789. #define Right    1
  790. #define ReWrite    2
  791. #define Left    3
  792. #define CrWrite    4
  793.  
  794. Hidden Procedure
  795. move(y, x)
  796. int y, x;
  797. {
  798.     int dy, dx;
  799.     int y_cost, x_cost, y_move, x_move;
  800.     int mode_cost;
  801.     int xi;
  802.     
  803.     if (cur_y == y && cur_x == x)
  804.         return;
  805.     
  806.     if (!has_mi || mode == Undefined)
  807.         set_mode(Normal);
  808.     if (!has_xs && ((!has_ms && so_mode != Off) || so_mode == Undefined))
  809.         standend();
  810.     
  811.     if (cur_y == Undefined || cur_x == Undefined)
  812.         goto absmove;
  813.     
  814.     dy = y - cur_y;
  815.     dx = x - cur_x;
  816.  
  817.     if (dy > 0) {
  818.         y_move = Down;
  819.         y_cost = dy * do_cost;
  820.     }
  821.     else if (dy < 0) {
  822.         y_move = Up;
  823.         y_cost = -dy * up_cost;
  824.     }
  825.     else {
  826.         y_move = None;
  827.         y_cost = 0;
  828.     }
  829.     if (y_cost < abs_cost) {
  830.         switch (mode) {
  831.         case Normal:
  832.             mode_cost = 0;
  833.             break;
  834.         case Insert:
  835.             mode_cost = ei_cost;
  836.             break;
  837.         case Delete:
  838.             mode_cost = ed_cost;
  839.             break;
  840.         }
  841.         if (dx > 0) {
  842.             x_cost = dx + mode_cost;
  843.             if (dx*nd_cost < x_cost || !rewrite_ok(y, cur_x, x)) {
  844.                 x_cost = dx * nd_cost;
  845.                 x_move = Right;
  846.             }
  847.             else
  848.                 x_move = ReWrite;
  849.         }
  850.         else if (dx < 0) {
  851.             x_cost = -dx * le_cost;
  852.             x_move = Left;
  853.         }
  854.         else {
  855.             x_cost = 0;
  856.             x_move = None;
  857.         }
  858.         if (cr_cost + x + mode_cost < x_cost && rewrite_ok(y, 0, x)) {
  859.             x_move = CrWrite;
  860.             x_cost = cr_cost + x + mode_cost;
  861.         }
  862.     }
  863.     else
  864.         x_cost = abs_cost;
  865.  
  866.     if (y_cost + x_cost < abs_cost) {
  867.         switch (y_move) {
  868.         case Down:
  869.             while (dy-- > 0) Putstr(do_str);
  870.             break;
  871.         case Up:
  872.             while (dy++ < 0) Putstr(up_str);
  873.             break;
  874.         }
  875.         switch (x_move) {
  876.         case Right:
  877.             while (dx-- > 0) Putstr(nd_str);
  878.             break;
  879.         case Left:
  880.             while (dx++ < 0) Putstr(le_str);
  881.             break;
  882.         case CrWrite:
  883.             Putstr(cr_str);
  884.             cur_x = 0;
  885.             /* FALL THROUGH */
  886.         case ReWrite:
  887.             set_mode(Normal);
  888.             for (xi = cur_x; xi < x; xi++)
  889.                 putchar(line[y][xi]);
  890.             break;
  891.         }
  892.     }
  893.     else
  894.     {
  895.     absmove:
  896.         if (cm_str == NULL) {
  897.             Putstr(ho_str);
  898.             for (cur_y = 0; cur_y < y; ++cur_y)
  899.                 Putstr(do_str);
  900.             /* Should try to use tabs here: */
  901.             for (cur_x = 0; cur_x < x; ++cur_x)
  902.                 Putstr(nd_str);
  903.         }
  904.         else
  905.             Putstr(tgoto(cm_str, x, y));
  906.     }
  907.     
  908.     cur_y = y;
  909.     cur_x = x;
  910. }
  911.  
  912.  
  913. /*
  914.  * Putting data on the screen.
  915.  */
  916.  
  917. /*
  918.  * Fill screen area with given data.
  919.  * Characters with the SO-bit (0200) set are put in standout mode.
  920.  */
  921. Visible Procedure
  922. trmputdata(yfirst, ylast, indent, data)
  923. int yfirst;
  924. int ylast;
  925. register int indent;
  926. register string data;
  927. {
  928.     register int y;
  929.     int x, len, lendata, space;
  930.         
  931.     Tprintf((stderr, "\ttrmputdata(%d, %d, %d, \"%s\");\n", yfirst, ylast, indent, data));
  932.     check_started("trmputdata");
  933.     
  934.     if (yfirst < 0)
  935.         yfirst = 0;
  936.     if (ylast >= lines)
  937.         ylast = lines-1;
  938.     space = cols*(ylast-yfirst+1) - indent;
  939.     if (space <= 0)
  940.         return;
  941.     yfirst += indent/cols;
  942.     indent %= cols;
  943.     y= yfirst;
  944.     if (!data)
  945.         data= ""; /* Safety net */
  946.     x = indent;
  947.     lendata = strlen(data);
  948.     if (ylast == lines-1 && lendata >= space)
  949.         lendata = space - 1;
  950.     len = Min(lendata, cols-x);
  951.     while (y <= ylast) {
  952.         put_line(y, x, data, len);
  953.         y++;
  954.         lendata -= len;
  955.         if (lendata > 0) {
  956.             x = 0;
  957.             data += len;
  958.             len = Min(lendata, cols);
  959.         }
  960.         else
  961.             break;
  962.     }
  963.     if (y <= ylast)
  964.         clear_lines(y, ylast);
  965. }
  966.  
  967. /* 
  968.  * We will first try to get the picture:
  969.  *
  970.  *                  op>>>>>>>>>>>op          oq<<<<<<<<<<<<<<<<<<<<<<<<oq
  971.  *                  ^            ^           ^                         ^
  972.  *           <xskip><-----m1----><----od-----><-----------m2----------->
  973.  *   OLD:   "You're in a maze of twisty little pieces of code, all alike"
  974.  *   NEW:          "in a maze of little twisting pieces of code, all alike"
  975.  *                  <-----m1----><-----nd------><-----------m2----------->
  976.  *                  ^            ^             ^                         ^
  977.  *                  np>>>>>>>>>>>np            nq<<<<<<<<<<<<<<<<<<<<<<<<nq
  978.  * where
  979.  *    op, oq, np, nq are pointers to start and end of Old and New data,
  980.  * and
  981.  *    xskip = length of indent to be skipped,
  982.  *    m1 = length of Matching part at start,
  983.  *    od = length of Differing mid on screen,
  984.  *    nd = length of Differing mid in data to be put,
  985.  *    m2 = length of Matching trail.
  986.  *
  987.  * Then we will try to find a long blank-or-cleared piece in <nd+m2>:
  988.  *
  989.  *    <---m1---><---d1---><---nb---><---d2---><---m2--->
  990.  *              ^         ^         ^        ^         ^
  991.  *              np        bp        bq1      nq        nend
  992.  * where
  993.  *    bp, bq are pointers to start and AFTER end of blank piece,
  994.  * and
  995.  *    d1 = length of differing part before blank piece,
  996.  *    nb = length of blank piece to be skipped,
  997.  *    d2 = length of differing part after blank piece.
  998.  * Remarks:
  999.  *    d1 + nb + d2 == nd,
  1000.  * and
  1001.  *    d2 maybe less than 0.
  1002.  */
  1003. Hidden int
  1004. put_line(y, xskip, data, len)
  1005. int y, xskip;
  1006. string data;
  1007. int len;
  1008. {
  1009.     register short *op, *oq;
  1010.     register char *np, *nq, *nend;
  1011.     char *bp, *bq1, *p, *q;
  1012.     int m1, m2, od, nd, delta, dd, d1, nb, d2;
  1013.     bool skipping;
  1014.     int cost, o_cost;     /* normal and optimising cost */
  1015.     
  1016.     /* calculate the magic parameters */
  1017.     op = &line[y][xskip];
  1018.     oq = &line[y][lenline[y]-1];
  1019.     np = data;
  1020.     nq = nend = data + len - 1;
  1021.     m1 = m2 = 0;
  1022.     while ((*op&SOCHAR) == (((short)*np)&SOCHAR) && op <= oq && np <= nq)
  1023.         op++, np++, m1++;
  1024.     if (flags & CAN_OPTIMISE)
  1025.         while ((*oq&SOCHAR) == (((short)*nq)&SOCHAR) && op <= oq && np <= nq)
  1026.             oq--, nq--, m2++;
  1027.     od = oq - op + 1;
  1028.     nd = nq - np + 1;
  1029.     /* now we have the first picture above */
  1030.  
  1031.     if (od==0 && nd==0)
  1032.         return;
  1033.     delta = nd - od;
  1034.  
  1035.     /* find the blank piece */
  1036.     p = q = bp = bq1 = np;
  1037.     oq += m2;         /* back to current eol */
  1038.     if (!has_in) {
  1039.         while (p <= nend) {
  1040.             while (q<=nend && *q==' ' && (op>oq || *op==' '))
  1041.                 q++, op++;
  1042.             if (q - p > bq1 - bp)
  1043.                 bp = p, bq1 = q;
  1044.             p = ++q;
  1045.             op++;
  1046.         }
  1047.     }
  1048.     d1 = bp - np;
  1049.     nb = bq1 - bp;
  1050.     d2 = nq - bq1 + 1;
  1051.     
  1052.     /* what is cheapest:
  1053.      *    normal: put nd+m2;                         (dd = nd+m2)
  1054.      *    skipping: put d1, skip nb, put d2+m2;      (dd = d2+m2)
  1055.      *    optimise: put dd, insert or delete delta.  (dd = min(od,nd))
  1056.      */
  1057.     cost = nd + m2;     /* normal cost */
  1058.     if (nb > abs_cost || (d1 == 0 && nb > 0)) {
  1059.         skipping = Yes;
  1060.         cost -= nb - (d1>0 ? abs_cost : 0); /* skipping cost */
  1061.         dd = d2;
  1062.     }
  1063.     else {
  1064.         skipping = No;
  1065.         dd = nd;
  1066.     }
  1067.     
  1068.     if (m2 != 0) {
  1069.         /* try optimising */
  1070.         o_cost = Min(od, nd);
  1071.         if (delta > 0)
  1072.             o_cost += delta * ins_mf + ins_oh;
  1073.         else if (delta < 0)
  1074.             o_cost += -delta * del_mf + del_oh;
  1075.         if (o_cost >= cost) {
  1076.             /* discard m2, no optimise */
  1077.             dd += m2;
  1078.             m2 = 0;
  1079.         }
  1080.         else {
  1081.             dd = Min(od, nd);
  1082.             skipping = No;
  1083.         }
  1084.     }
  1085.  
  1086.     /* and now for the real work */
  1087.     if (!skipping || d1 > 0)
  1088.         move(y, xskip + m1);
  1089.  
  1090.     if (has_xs)
  1091.         get_so_mode();
  1092.     
  1093.     if (skipping) {
  1094.         if (d1 > 0) {
  1095.             set_mode(Normal);
  1096.             put_str(np, d1, No);
  1097.         }
  1098.         if (has_xs && so_mode != Off)
  1099.             standend();
  1100.         set_blanks(y, xskip+m1+d1, xskip+m1+d1+nb);
  1101.         if (dd != 0 || delta < 0) {
  1102.             move(y, xskip+m1+d1+nb);
  1103.             np = bq1;
  1104.         }
  1105.     }
  1106.     
  1107.     if (dd > 0) {
  1108.         set_mode(Normal);
  1109.         put_str(np, dd, No);
  1110.     }
  1111.     
  1112.     if (m2 > 0) {
  1113.         if (delta > 0) {
  1114.             set_mode(Insert);
  1115.             ins_str(np+dd, delta);
  1116.         }
  1117.         else if (delta < 0) {
  1118.             if (so_mode != Off)
  1119.                 standend();
  1120.                 /* Some terminals fill with standout spaces! */
  1121.             set_mode(Delete);
  1122.             del_str(-delta);
  1123.         }
  1124.     }
  1125.     else {
  1126.         if (delta < 0) {
  1127.             clr_to_eol();
  1128.             return;
  1129.         }
  1130.     }
  1131.     
  1132.     lenline[y] = xskip + len;
  1133.     if (cur_x == cols) {
  1134.         if (!has_mi)
  1135.             set_mode(Normal);
  1136.         if (!has_ms)
  1137.             so_mode = Undefined;
  1138.         if (has_am)
  1139.             cur_y++;
  1140.         else
  1141.             Putstr(cr_str);
  1142.         cur_x = 0;
  1143.     }
  1144.     else if (has_xs) {
  1145.         if (m2 == 0) {
  1146.             if (so_mode == On)
  1147.                 standend();
  1148.         }
  1149.         else {
  1150.             if (!(line[cur_y][cur_x] & XSBIT)) {
  1151.                 if (so_mode != (line[cur_y][cur_x] & SOBIT))
  1152.                     (so_mode ? standend() : standout());
  1153.             }
  1154.         }
  1155.     }
  1156. }
  1157.  
  1158. Hidden Procedure
  1159. set_mode(m)
  1160. int m;
  1161. {
  1162.     if (m == mode)
  1163.         return;
  1164.     switch (mode) {
  1165.     case Insert:
  1166.         Putstr(ei_str);
  1167.         break;
  1168.     case Delete:
  1169.         Putstr(ed_str);
  1170.         break;
  1171.     case Undefined:
  1172.         Putstr(ei_str);
  1173.         Putstr(ed_str);
  1174.         break;
  1175.     }
  1176.     switch (m) {
  1177.     case Insert:
  1178.         Putstr(im_str);
  1179.         break;
  1180.     case Delete:
  1181.         Putstr(dm_str);
  1182.         break;
  1183.     }
  1184.     mode = m;
  1185. }
  1186.  
  1187. Hidden Procedure
  1188. get_so_mode()
  1189. {
  1190.     if (cur_x >= lenline[cur_y] || line[cur_y][cur_x] == UNKNOWN)
  1191.         so_mode = Off;
  1192.     else
  1193.         so_mode = line[cur_y][cur_x] & SOBIT;
  1194. }
  1195.  
  1196. Hidden Procedure
  1197. standout()
  1198. {
  1199.     Putstr(so_str);
  1200.     so_mode = On;
  1201.     if (has_xs)
  1202.         line[cur_y][cur_x] |= SOCOOK;
  1203. }
  1204.  
  1205. Hidden Procedure
  1206. standend()
  1207. {
  1208.     Putstr(se_str);
  1209.     so_mode = Off;
  1210.     if (has_xs)
  1211.         line[cur_y][cur_x] = (line[cur_y][cur_x] & ~SOBIT) | XSBIT;
  1212. }
  1213.  
  1214. Hidden Procedure
  1215. put_str(data, n, inserting)
  1216. char *data;
  1217. int n;
  1218. bool inserting;
  1219. {
  1220.     register short c, so;
  1221.     short *ln_y_x, *ln_y_end;
  1222.     
  1223.     so = so_mode;
  1224.     if (has_xs) {
  1225.         ln_y_x = &line[cur_y][cur_x];
  1226.         ln_y_end = &line[cur_y][lenline[cur_y]];
  1227.     }
  1228.     while (n-- > 0) {
  1229.         if (has_xs && ln_y_x <= ln_y_end && ((*ln_y_x)&XSBIT))
  1230.             so = so_mode = (*ln_y_x)&SOBIT;
  1231.             /* this also checks for the standend cookie AFTER */
  1232.             /* the line because off the equals sign in <= */
  1233.         c = ((short)(*data++))&SOCHAR;
  1234.         if ((c&SOBIT) != so) {
  1235.             so = c&SOBIT;
  1236.             so ? standout() : standend();
  1237.          }
  1238.         if (inserting)
  1239.             Putstr(ic_str);
  1240.         put_c(c);
  1241.         if (has_xs)
  1242.             ln_y_x++;
  1243.     }
  1244. }
  1245.  
  1246. Hidden Procedure
  1247. ins_str(data, n)
  1248. char *data;
  1249. int n;
  1250. {
  1251.     int x;
  1252.     
  1253.     /* x will start AFTER the line, because there might be a cookie */
  1254.     for (x = lenline[cur_y]; x >= cur_x; x--)
  1255.         line[cur_y][x+n] = line[cur_y][x];
  1256.     put_str(data, n, Yes);
  1257. }
  1258.  
  1259. Hidden Procedure
  1260. del_str(n)
  1261. int n;
  1262. {
  1263.     int x, xto;
  1264.     
  1265.     xto = lenline[cur_y] - n; /* again one too far because of cookie */
  1266.     if (has_xs) {
  1267.         for (x = cur_x + n; x >= cur_x; x--) {
  1268.             if (line[cur_y][x] & XSBIT)
  1269.                 break;
  1270.         }
  1271.         if (x >= cur_x)
  1272.             line[cur_y][cur_x+n] =
  1273.                 (line[cur_y][cur_x+n] & CHAR)
  1274.                 |
  1275.                 (line[cur_y][x] & COOKBITS);
  1276.     }
  1277.     for (x = cur_x; x <= xto; x++)
  1278.         line[cur_y][x] = line[cur_y][x+n];
  1279.     while (n-- > 0)
  1280.         Putstr(dc_str);
  1281. }
  1282.  
  1283. Hidden Procedure
  1284. put_c(c)
  1285. int c;
  1286. {
  1287.     char ch;
  1288.     short xs_flag;
  1289.     
  1290.     ch = c&CHAR;
  1291.     if (!isprint(ch) && ch != ' ') /* V7 isprint doesn't include blank */
  1292.         ch= '?';
  1293.     putchar(ch);
  1294.     if (has_xs)
  1295.         xs_flag = line[cur_y][cur_x]&XSBIT;
  1296.     else
  1297.         xs_flag = 0;
  1298.     line[cur_y][cur_x] = (c&SOCHAR)|xs_flag;
  1299.     cur_x++;
  1300. }
  1301.  
  1302. Hidden Procedure
  1303. clear_lines(yfirst, ylast)
  1304. int yfirst, ylast ;
  1305. {
  1306.     register int y;
  1307.     
  1308.     if (!has_xs && so_mode != Off)
  1309.         standend();
  1310.     if (cl_str && yfirst == 0 && ylast == lines-1) {
  1311.         Putstr(cl_str);
  1312.         cur_y = cur_x = 0;
  1313.         for (y = 0; y < lines; ++y) {
  1314.             lenline[y] = 0;
  1315.             if (has_xs) line[y][0] = NOCOOK;
  1316.         }
  1317.         return;
  1318.     }
  1319.     for (y = yfirst; y <= ylast; y++) {
  1320.         if (lenline[y] > 0) {
  1321.             move(y, 0);
  1322.             if (ylast == lines-1 && cd_str) {
  1323.                 Putstr(cd_str);
  1324.                 while (y <= ylast) {
  1325.                     if (has_xs) line[y][0] = NOCOOK;
  1326.                     lenline[y++] = 0;
  1327.                 }
  1328.                 break;
  1329.             }
  1330.             else {
  1331.                 clr_to_eol();
  1332.             }
  1333.         }
  1334.     }
  1335. }
  1336.  
  1337. Hidden Procedure
  1338. clr_to_eol()
  1339. {
  1340.     lenline[cur_y] = cur_x;
  1341.     if (!has_xs && so_mode != Off)
  1342.         standend();
  1343.     Putstr(ce_str);
  1344.     if (has_xs) {
  1345.         if (cur_x == 0)
  1346.             line[cur_y][0] = NOCOOK;
  1347.         else if (line[cur_y][cur_x-1]&SOBIT)
  1348.             standend();
  1349.     }
  1350. }
  1351.  
  1352. Hidden Procedure
  1353. set_blanks
  1354. (y, xfrom, xto)
  1355. int y, xfrom, xto;
  1356. {
  1357.     register int x;
  1358.     
  1359.     for (x = xfrom; x < xto; x++) {
  1360.         line[y][x] = (line[y][x]&XSBIT) | ' ';
  1361.     }
  1362. }
  1363.  
  1364. /* 
  1365.  * outchar() is used by termcap's tputs;
  1366.  * we can't use putchar because that's probably a macro
  1367.  */
  1368. Hidden int
  1369. outchar(ch)
  1370. char ch;
  1371. {
  1372.     putchar(ch);
  1373. }
  1374.  
  1375. /*
  1376.  * Scrolling (part of) the screen up (or down, dy<0).
  1377.  */
  1378.  
  1379. Visible Procedure
  1380. trmscrollup(yfirst, ylast, by)
  1381. register int yfirst;
  1382. register int ylast;
  1383. register int by;
  1384. {
  1385.     Tprintf((stderr, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by));
  1386.     check_started("trmscrollup");
  1387.     
  1388.     if (yfirst < 0)
  1389.         yfirst = 0;
  1390.     if (ylast >= lines)
  1391.         ylast = lines-1;
  1392.  
  1393.     if (yfirst > ylast)
  1394.         return;
  1395.  
  1396.     if (!has_xs && so_mode != Off)
  1397.         standend();
  1398.     
  1399.     if (by > 0 && yfirst + by > ylast
  1400.         ||
  1401.         by < 0 && yfirst - by > ylast)
  1402.     {
  1403.         clear_lines(yfirst, ylast);
  1404.         return;
  1405.     }
  1406.     
  1407.     if (by > 0) {
  1408.         (*scr_up)(yfirst, ylast, by);
  1409.         scr_lines(yfirst, ylast, by, 1);
  1410.     }
  1411.     else if (by < 0) {
  1412.         (*scr_down)(yfirst, ylast, -by);
  1413.         scr_lines(ylast, yfirst, -by, -1);
  1414.     }
  1415. }
  1416.  
  1417. Hidden Procedure
  1418. scr_lines(yfrom, yto, n, dy)
  1419. int yfrom, yto, n, dy;
  1420. {
  1421.     register int y;
  1422.     short *saveln;
  1423.     
  1424.     while (n-- > 0) {
  1425.         saveln = line[yfrom];
  1426.         for (y = yfrom; y != yto; y += dy) {
  1427.             line[y] = line[y+dy];
  1428.             lenline[y] = lenline[y+dy];
  1429.         }
  1430.         line[yto] = saveln;
  1431.         lenline[yto] = 0;
  1432.         if (has_xs) line[yto][0] = NOCOOK;
  1433.     }
  1434. }
  1435.  
  1436. Hidden Procedure
  1437. scr1up(yfirst, ylast, n)
  1438.     int yfirst;
  1439.     int ylast;
  1440.     int n;
  1441. {
  1442.     move(yfirst, 0);
  1443.     dellines(n);
  1444.     if (ylast < lines-1) {
  1445.         move(ylast-n+1, 0);
  1446.         addlines(n);
  1447.     }
  1448. }
  1449.  
  1450.  
  1451. Hidden Procedure
  1452. scr1down(yfirst, ylast, n)
  1453.     int yfirst;
  1454.     int ylast;
  1455.     int n;
  1456. {
  1457.     if (ylast == lines-1) {
  1458.         clear_lines(ylast-n+1, ylast);
  1459.     }
  1460.     else {
  1461.         move(ylast-n+1, 0);
  1462.         dellines(n);
  1463.     }
  1464.     move(yfirst, 0);
  1465.     addlines(n);
  1466. }
  1467.  
  1468.  
  1469. Hidden Procedure
  1470. addlines(n)
  1471. register int n;
  1472. {
  1473.     if (par_al_str && n > 1)
  1474.             Putstr(tgoto(par_al_str, n, n));
  1475.     else {
  1476.         while (n-- > 0)
  1477.             Putstr(al_str);
  1478.     }
  1479. }
  1480.  
  1481.  
  1482. Hidden Procedure
  1483. dellines(n)
  1484. register int n;
  1485. {
  1486.     if (par_dl_str && n > 1)
  1487.         Putstr(tgoto(par_dl_str, n, n));
  1488.     else {
  1489.         while (n-- > 0)
  1490.             Putstr(dl_str);
  1491.     }
  1492. }
  1493.  
  1494.  
  1495. Hidden Procedure
  1496. scr2up(yfirst, ylast, n)
  1497. int yfirst, ylast, n;
  1498. {
  1499.     Putstr(tgoto(cs_str, ylast, yfirst));
  1500.     cur_y = cur_x = Undefined;
  1501.     move(ylast, 0);
  1502.     while (n-- > 0) {
  1503.         Putstr(sf_str);
  1504.         if (has_db && ylast == lines-1)
  1505.             clr_to_eol();
  1506.     }
  1507.     Putstr(tgoto(cs_str, lines-1, 0));
  1508.     cur_y = cur_x = Undefined;
  1509. }
  1510.  
  1511.  
  1512. Hidden Procedure
  1513. scr2down(yfirst, ylast, n)
  1514. int yfirst, ylast, n;
  1515. {
  1516.     Putstr(tgoto(cs_str, ylast, yfirst));
  1517.     cur_y = cur_x = Undefined;
  1518.     move(yfirst, 0);
  1519.     while (n-- > 0) {
  1520.         Putstr(sr_str);
  1521.         if (has_da && yfirst == 0)
  1522.             clr_to_eol();
  1523.     }
  1524.     Putstr(tgoto(cs_str, lines-1, 0));
  1525.     cur_y = cur_x = Undefined;
  1526. }
  1527.  
  1528.  
  1529. /*
  1530.  * Synchronization, move cursor to given position (or previous if < 0).
  1531.  */
  1532.  
  1533. Visible Procedure
  1534. trmsync(y, x)
  1535.     int y;
  1536.     int x;
  1537. {
  1538.     Tprintf((stderr, "\ttrmsync(%d, %d);\n", y, x));
  1539.     check_started("trmsync");
  1540.     
  1541.     if (0 <= y && y < lines && 0 <= x && x < cols) {
  1542.         move(y, x);
  1543.         if (no_cursor) {
  1544.             Putstr(ve_str);
  1545.             no_cursor = No;
  1546.         }
  1547.     }
  1548.     else if (no_cursor == No) {
  1549.         Putstr(vi_str);
  1550.         no_cursor = Yes;
  1551.     }
  1552.     VOID fflush(stdout);
  1553. }
  1554.  
  1555.  
  1556. /*
  1557.  * Send a bell, visible if possible.
  1558.  */
  1559.  
  1560. Visible Procedure
  1561. trmbell()
  1562. {
  1563.     Tprintf((stderr, "\ttrmbell();\n"));
  1564.     check_started("trmbell");
  1565.     
  1566.     Putstr(vb_str);
  1567.     VOID fflush(stdout);
  1568. }
  1569.  
  1570.  
  1571. #ifdef SHOW
  1572.  
  1573. /*
  1574.  * Show the current internal statuses of the screen on stderr.
  1575.  * For debugging only.
  1576.  */
  1577.  
  1578. Visible Procedure
  1579. trmshow(s)
  1580. char *s;
  1581. {
  1582.     int y, x;
  1583.     
  1584.     fprintf(stderr, "<<< %s >>>\n", s);
  1585.     for (y = 0; y < lines; y++) {
  1586.         for (x = 0; x <= lenline[y] /*** && x < cols-1 ***/ ; x++) {
  1587.             fputc(line[y][x]&CHAR, stderr);
  1588.         }
  1589.         fputc('\n', stderr);
  1590.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  1591.             if (line[y][x]&SOBIT)
  1592.                 fputc('-', stderr);
  1593.             else
  1594.                 fputc(' ', stderr);
  1595.         }
  1596.         fputc('\n', stderr);
  1597.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  1598.             if (line[y][x]&XSBIT)
  1599.                 fputc('+', stderr);
  1600.             else
  1601.                 fputc(' ', stderr);
  1602.         }
  1603.         fputc('\n', stderr);
  1604.     }
  1605.     fprintf(stderr, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x);
  1606.     VOID fflush(stderr);
  1607. }
  1608. #endif
  1609.  
  1610.  
  1611. /*
  1612.  * Return the next input character, or -1 if read fails.
  1613.  * Only the low 7 bits are returned, so reading in RAW mode is permissible
  1614.  * (although CBREAK is preferred if implemented).
  1615.  * To avoid having to peek in the input buffer for trmavail, we use the
  1616.  * 'read' system call rather than getchar().
  1617.  * (The interface allows 8-bit characters to be returned, to accomodate
  1618.  * larger character sets!)
  1619.  */
  1620.  
  1621. Hidden int pushback= -1;
  1622.  
  1623. int
  1624. trminput()
  1625. {
  1626.     char c;
  1627.  
  1628.     if (pushback >= 0) {
  1629.         c= pushback;
  1630.         pushback= -1;
  1631.         return c;
  1632.     }
  1633.     if (read(0, &c, 1) <= 0)
  1634.         return -1;
  1635.     return c & 0177;
  1636. }
  1637.  
  1638. trmpushback(c)
  1639.     int c;
  1640. {
  1641.     pushback= c;
  1642. }
  1643.  
  1644.  
  1645. /*
  1646.  * See if there's input available from the keyboard.
  1647.  * The code to do this is dependent on the type of Unix you have
  1648.  * (BSD, System V, ...).
  1649.  * Return value: 0 -- no input; 1 -- input; -1 -- unimplementable.
  1650.  * Note that each implementation form should first check pushback.
  1651.  *
  1652.  * TO DO:
  1653.  *    - Implement it for other than 4.x BSD! (notably System 5)
  1654.  */
  1655.  
  1656. #ifdef SELECT
  1657.  
  1658. #include <sys/time.h>
  1659.  
  1660. int
  1661. trmavail()
  1662. {
  1663.     int nfound, nfds, readfds;
  1664.     static struct timeval timeout= {0, 0};
  1665.  
  1666.     if (pushback >= 0)
  1667.         return 1;
  1668.     readfds= 1 << 0;
  1669.     nfds= 0+1;
  1670.     nfound= select(nfds, &readfds, (int*) NIL, (int*) NIL, &timeout);
  1671.     return nfound > 0;
  1672. }
  1673.  
  1674. #define TRMAVAIL_DEFINED
  1675.  
  1676. #endif SELECT
  1677.  
  1678. #if !defined(TRMAVAIL_DEFINED) && defined(FIONREAD)
  1679.  
  1680. int
  1681. trmavail()
  1682. {
  1683.     long n;
  1684.  
  1685.     if (pushback >= 0)
  1686.         return 1;
  1687.     ioctl(0, (int) FIONREAD, (char *) &n);
  1688.     return n > 0;
  1689. }
  1690.  
  1691. #define TRMAVAIL_DEFINED
  1692.  
  1693. #endif FIONREAD
  1694.  
  1695. #ifndef TRMAVAIL_DEFINED
  1696.  
  1697. int
  1698. trmavail()
  1699. {
  1700.     if (pushback >= 0)
  1701.         return 1;
  1702.     return -1;
  1703. }
  1704.  
  1705. #endif
  1706.  
  1707.  
  1708. /*
  1709.  * Suspend the editor.
  1710.  * Should be called only after trmend and before trmstart!
  1711.  */
  1712.  
  1713. trmsuspend()
  1714. {
  1715.     int (*oldsig)();
  1716.     
  1717.     oldsig= signal(SIGTSTP, SIG_IGN);
  1718.     if (oldsig == SIG_IGN)
  1719.         return; /* Could spawn a subshell here... */
  1720.     trmend(); /* Safety net */
  1721.     signal(SIGTSTP, oldsig);
  1722.     kill(0, SIGSTOP);
  1723. }
  1724.  
  1725.  
  1726. /*
  1727.  * DESCRIPTION.
  1728.  *
  1729.  * This package uses termcap to determine the terminal capabilities.
  1730.  *
  1731.  * The lines and columns of our virtual terminal are numbered 
  1732.  *    y = {0...lines-1} from top to bottom, and
  1733.  *    x = {0...cols-1} from left to right,
  1734.  * respectively.
  1735.  *
  1736.  * The Visible Procedures in this package are:
  1737.  *
  1738.  * trmstart(&lines, &cols, &flags)
  1739.  *     Obligatory initialization call (sets tty modes etc.),
  1740.  *     Returns the height and width of the screen to the integers
  1741.  *     whose addresses are passed as parameters, and a flag that
  1742.  *    describes some capabilities.
  1743.  *    Function return value: 0 if all went well, an error code if there
  1744.  *    is any trouble.  No messages are printed for errors.
  1745.  *
  1746.  * trmundefined()
  1747.  *    Sets internal representation of screen and attributes to undefined.
  1748.  *    This is necessary for a hard redraw, which would get optimised to
  1749.  *    oblivion,
  1750.  *
  1751.  * trmsense(&y, &x)
  1752.  *    Returns the cursor position through its parameters
  1753.  *    after a possible manual change by the user.
  1754.  *
  1755.  * trmputdata(yfirst, ylast, indent, data)
  1756.  *     Fill lines {yfirst..ylast} with data, after skipping the initial
  1757.  *    'indent' positions. It is assumed that these positions do not contain
  1758.  *    anything dangerous (like standout cookies or null characters).
  1759.  *
  1760.  * trmscrollup(yfirst, ylast, by)
  1761.  *     Shift lines {yfirst..ylast} up by lines (down |by| if by < 0).
  1762.  *
  1763.  * trmsync(y, x)
  1764.  *     Call to output data to the terminal and set cursor position.
  1765.  *
  1766.  * trmbell()
  1767.  *    Send a (possibly visible) bell, immediately (flushing stdout).
  1768.  *
  1769.  * trmend()
  1770.  *     Obligatory termination call (resets tty modes etc.).
  1771.  *
  1772.  * You may call these as one or more cycles of:
  1773.  *     + trmstart
  1774.  *     +    zero or more times any of the other routines
  1775.  *     + trmend
  1776.  * Trmend may be called even in the middle of trmstart; this is necessary
  1777.  * to make it possible to write an interrupt handler that resets the tty
  1778.  * state before exiting the program.
  1779.  *
  1780.  * ADDITIONAL SPECIFICATIONS (ROUTINES FOR CHARACTER INPUT)
  1781.  *
  1782.  * trminput()
  1783.  *    Return the next input character (with its parity bit cleared
  1784.  *    if any).  This value is a nonnegative int.  Returns -1 if the
  1785.  *    input can't be read any more.
  1786.  *
  1787.  * trmavail()
  1788.  *    Return 1 if there is an input character immediately available,
  1789.  *    0 if not.  Return -1 if not implementable.
  1790.  *
  1791.  * trminterrupt()
  1792.  *    Return 1 if an interrupt has occurred since the last call to
  1793.  *    trminput or trmavail, 0 else.  [Currently not implemented.]
  1794.  *
  1795.  * trmsuspend()
  1796.  *    When called in the proper environment (4BSD with job control
  1797.  *    enabled), suspends the editor, temporarily popping back to
  1798.  *    the calling shell.  The caller should have called trmend()
  1799.  *    first, and must call trmstart again afterwards.
  1800.  *    BUG: there is a timing window where keyboard-generated
  1801.  *    signals (such as interrupt) can reach the program.
  1802.  */
  1803.