home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / display.c < prev    next >
C/C++ Source or Header  |  1998-10-02  |  77KB  |  3,568 lines

  1. /*
  2.  * The functions in this file handle redisplay. There are two halves, the
  3.  * ones that update the virtual display screen, and the ones that make the
  4.  * physical display screen the same as the virtual display screen. These
  5.  * functions use hints that are left in the windows by the commands.
  6.  *
  7.  *
  8.  * $Header: /usr/build/vile/vile/RCS/display.c,v 1.259 1998/10/03 01:40:37 tom Exp $
  9.  *
  10.  */
  11.  
  12. #include    "estruct.h"
  13. #include        "edef.h"
  14. #include        "pscreen.h"
  15.  
  16. #define    NU_WIDTH 8
  17.  
  18. #define    MRK_EMPTY        '~'
  19. #define    MRK_EXTEND_LEFT  '<'
  20. #define    MRK_EXTEND_RIGHT '>'
  21.  
  22. VIDEO    **vscreen;            /* Virtual screen. */
  23. VIDEO    **pscreen;            /* Physical screen. */
  24. #if    MEMMAP
  25. #define PSCREEN vscreen
  26. #else
  27. #define PSCREEN pscreen
  28. #endif
  29. static    int *lmap;
  30.  
  31. #if DISP_IBMPC
  32. #define PScreen(n) scread((VIDEO *)0,n)
  33. #else
  34. #define    PScreen(n) pscreen[n]
  35. #endif
  36.  
  37. #if OPT_SCROLLCODE && (DISP_IBMPC || !MEMMAP)
  38. #define CAN_SCROLL 1
  39. #else
  40. #define CAN_SCROLL 0
  41. #endif
  42.  
  43. static    int    i_displayed;        /* false until we're in screen-mode */
  44. static    int    im_displaying;        /* flag set during screen updates */
  45. static    int    mpresf;            /* zero if message-line empty */
  46. #if OPT_WORKING
  47. static    int    im_timing;
  48. #endif
  49.  
  50. /*
  51.  * MARK2COL may be greater than mark2col if the mark does not point to the end
  52.  * of the line, and if it points to a nonprinting character.  We use that value
  53.  * when setting visible attributes, to keep tabs and other nonprinting
  54.  * characters looking 'right'.
  55.  */
  56. #define MARK2COL(wp, mk)  offs2col(wp, mk.l, mk.o + 1) - 1
  57. #define mark2col(wp, mk)  offs2col(wp, mk.l, mk.o)
  58.  
  59. #ifdef WMDLINEWRAP
  60. #define TopRow(wp) (wp)->w_toprow + (wp)->w_line.o
  61. static    int    allow_wrap;
  62. #else
  63. #define TopRow(wp) (wp)->w_toprow
  64. #endif
  65.  
  66. /* for window size changes */
  67. static    int chg_width, chg_height;
  68.  
  69. /******************************************************************************/
  70.  
  71. typedef    void    (*OutFunc) (int c);
  72.  
  73. static    OutFunc    dfoutfn;
  74.  
  75. static    int    endofline(char *s, int n);
  76. static    int    texttest (int vrow, int prow);
  77. static    int    updext_before(int col);
  78. static    int    updext_past(int col, int excess);
  79. static    int    updpos(int *screenrowp, int *screencolp);
  80. static    int    vtalloc (void);
  81. static    void    l_to_vline(WINDOW *wp, LINEPTR lp, int sline);
  82. static    void    mlmsg(const char *fmt, va_list *app);
  83. static    void    modeline(WINDOW *wp);
  84. static    void    reframe(WINDOW *wp);
  85. static    void    scrscroll(int from, int to, int count);
  86. static    void    updall (WINDOW *wp);
  87. static    void    updateline (int row, int colfrom, int colto);
  88. static    void    upddex (void);
  89. static    void    updgar (void);
  90. static    void    updone (WINDOW *wp);
  91. static    void    updupd (int force);
  92. static    void    vtlistc (int c);
  93.  
  94. #if OPT_VIDEO_ATTRS
  95. static    void    updattrs(WINDOW *wp);
  96. #endif
  97.  
  98. #if    OPT_UPBUFF
  99. static    void    recompute_buffer(BUFFER *bp);
  100. #endif
  101.  
  102. #if CAN_SCROLL
  103. static    int    scrolls (int inserts);
  104. #endif
  105.  
  106. /*--------------------------------------------------------------------------*/
  107.  
  108. /*
  109.  * Format a number, right-justified, returning a pointer to the formatted
  110.  * buffer.
  111.  */
  112. static char *
  113. right_num (char *buffer, int len, long value)
  114. {
  115.     char    temp[NSTRING];
  116.     register char    *p = lsprintf(temp, "%D", value);
  117.     register char    *q = buffer + len;
  118.  
  119.     *q = EOS;
  120.     while (q != buffer)
  121.         *(--q) = (p != temp) ? *(--p) : ' ';
  122.     return buffer;
  123. }
  124.  
  125. /*
  126.  * Do format a string.
  127.  */
  128. static int
  129. dfputsn(OutFunc outfunc, const char *s, int n)
  130. {
  131.     register int c = 0;
  132.     register int l = 0;
  133.     TRACE(("...str=%.*s\n", n > 0 ? n : (int)strlen(s), s))
  134.     while ((n-- != 0) && ((c = *s++) != EOS)) {
  135.         (*outfunc)(c);
  136.         l++;
  137.     }
  138.     return l;
  139. }
  140.  
  141. /* as above, but uses null-terminated string's length */
  142. static int
  143. dfputs(OutFunc outfunc, const char *s)
  144. {
  145.     return dfputsn(outfunc, s, -1);
  146. }
  147.  
  148. /*
  149.  * Do format an integer, in the specified radix.
  150.  */
  151. #define vMAXINT ((int)((unsigned)(~0)>>1))    /* 0x7fffffff */
  152. #define vMAXNEG (-vMAXINT)            /* 0x80000001 */
  153. static int
  154. dfputi(OutFunc outfunc, int i, int r)
  155. {
  156.     register int q;
  157.  
  158.     TRACE(("...int=%d\n", i))
  159.     if (i < 0) {
  160.         if (i < vMAXNEG) {
  161.             return dfputs(outfunc,"OVFL");
  162.         }
  163.         (*outfunc)('-');
  164.         return dfputi(outfunc, -i, r) + 1;
  165.     }
  166.  
  167.     q = (i >= r) ? dfputi(outfunc, i/r, r) : 0;
  168.  
  169.     (*outfunc)(hexdigits[i%r]);
  170.     return q + 1; /* number of digits printed */
  171. }
  172.  
  173. /*
  174.  * do the same except as a long integer.
  175.  */
  176. static int
  177. dfputli(OutFunc outfunc, long l, int r)
  178. {
  179.     register int q;
  180.  
  181.     TRACE(("...long=%ld\n", l))
  182.     if (l < 0) {
  183.         (*outfunc)('-');
  184.         return dfputli(outfunc, -l, r) + 1;
  185.     }
  186.  
  187.     q = (l >= r) ? dfputli(outfunc, (long)(l/r), r) : 0;
  188.  
  189.     return q + dfputi(outfunc, (int)(l%r), r);
  190. }
  191.  
  192. /*
  193.  *    Do format a scaled integer with two decimal places
  194.  */
  195. static int
  196. dfputf(OutFunc outfunc, int s)
  197. {
  198.     register int i;    /* integer portion of number */
  199.     register int f;    /* fractional portion of number */
  200.  
  201.     /* break it up */
  202.     i = s / 100;
  203.     f = s % 100;
  204.  
  205.     /* send out the integer portion */
  206.     i = dfputi(outfunc, i, 10);
  207.     (*outfunc)('.');
  208.     (*outfunc)((f / 10) + '0');
  209.     (*outfunc)((f % 10) + '0');
  210.     return i + 3;
  211. }
  212.  
  213. /*
  214.  * Generic string formatter.  Takes printf-like args, and calls
  215.  * the global function (*dfoutfn)(c) for each c
  216.  */
  217. static void
  218. dofmt(const char *fmt, va_list *app)
  219. {
  220.     register int c;        /* current char in format string */
  221.     register int wid;
  222.     register int n;
  223.     register int nchars = 0;
  224.     int islong;
  225.     int radix;
  226.     OutFunc outfunc = dfoutfn;  /* local copy, for recursion */
  227.  
  228.     TRACE(("dofmt fmt='%s'\n", fmt))
  229.     while ((c = *fmt++) != 0 ) {
  230.         if (c != '%') {
  231.             (*outfunc)(c);
  232.             nchars++;
  233.             continue;
  234.         }
  235.         c = *fmt++;
  236.         wid = 0;
  237.         islong = FALSE;
  238.         if (c == '*') {
  239.             wid = va_arg(*app,int);
  240.             c = *fmt++;
  241.         } else while (isDigit(c)) {
  242.             wid = (wid * 10) + c - '0';
  243.             c = *fmt++;
  244.         }
  245.         if (c == 'l') {
  246.             islong = TRUE;
  247.             c = *fmt++;
  248.         }
  249.         switch (c) {
  250.             case EOS:
  251.                 n = 0;
  252.                 break;
  253.             case 'c':
  254.                 (*outfunc)(va_arg(*app,int));
  255.                 n = 1;
  256.                 break;
  257.  
  258.             case 'd':
  259.                 if (!islong) {
  260.                     n = dfputi(outfunc, va_arg(*app,int), 10);
  261.                     break;
  262.                 }
  263.                 /* FALLTHROUGH */
  264.             case 'D':
  265.                 n = dfputli(outfunc, va_arg(*app,long), 10);
  266.                 break;
  267.  
  268.             case 'o':
  269.                 n = dfputi(outfunc, va_arg(*app,int), 8);
  270.                 break;
  271.  
  272.             case 'x':
  273.                 if (!islong) {
  274.                     n = dfputi(outfunc, va_arg(*app,int), 16);
  275.                     break;
  276.                 }
  277.                 /* FALLTHROUGH */
  278.             case 'X':
  279.                 n = dfputli(outfunc, va_arg(*app,long), 16);
  280.                 break;
  281.  
  282.             case 'r':
  283.             case 'R':
  284.                 radix = va_arg(*app, int);
  285.                 if (radix < 2 || radix > 36) radix = 10;
  286.                 if (islong || c == 'R')
  287.                     n = dfputli(outfunc,
  288.                         va_arg(*app,long), radix);
  289.                 else
  290.                     n = dfputi(outfunc,
  291.                         va_arg(*app,int), radix);
  292.                 break;
  293.  
  294.             case 's':
  295.                 n = dfputs(outfunc, va_arg(*app,char *));
  296.                 break;
  297.  
  298.             case 'S': /* use wid as max width */
  299.                 n = dfputsn(outfunc, va_arg(*app,char *),wid);
  300.                 break;
  301.  
  302.             case 'f':
  303.                 n = dfputf(outfunc, va_arg(*app,int));
  304.                 break;
  305.  
  306.             case 'P': /* output padding -- pads total output to
  307.                     "wid" chars, using c as the pad char */
  308.                 wid -= nchars;
  309.                 /* FALLTHROUGH */
  310.  
  311.             case 'p': /* field padding -- puts out "wid"
  312.                     copies of c */
  313.                 n = 0;
  314.                 c = va_arg(*app,int);
  315.                 while (n < wid) {
  316.                     (*outfunc)(c);
  317.                     n++;
  318.                 }
  319.                 break;
  320.  
  321.             default:
  322.                 (*outfunc)(c);
  323.                 n = 1;
  324.         }
  325.         wid -= n;
  326.         nchars += n;
  327.         while (wid-- > 0) {
  328.             (*outfunc)(' ');
  329.             nchars++;
  330.         }
  331.     }
  332.  
  333. }
  334.  
  335. /******************************************************************************/
  336.  
  337. /*
  338.  * Line-number mode
  339.  */
  340. int
  341. nu_width(WINDOW *wp)
  342. {
  343.     return w_val(wp,WMDNUMBER) ? NU_WIDTH : 0;
  344. }
  345.  
  346. int
  347. col_limit(WINDOW *wp)
  348. {
  349. #ifdef WMDLINEWRAP
  350.     if (w_val(wp,WMDLINEWRAP))
  351.         return curcol + 1;    /* effectively unlimited */
  352. #endif
  353.     return term.t_ncol - 1 - nu_width(wp);
  354. }
  355.  
  356. /*
  357.  * Initialize the data structures used by the display code. The edge vectors
  358.  * used to access the screens are set up.
  359.  */
  360. int
  361. vtinit(void)
  362. {
  363.     register int i;
  364.     register VIDEO *vp;
  365.  
  366. #if    OPT_MLFORMAT
  367.     if (!modeline_format)
  368.         modeline_format = strmalloc(
  369.         "%-%i%- %b %m:: :%f:is : :%=%F: : :%l:(:,:%c::) :%p::% :%S%-%-%|"
  370.     );
  371. #endif
  372.  
  373.     /* allocate new display memory */
  374.     if (vtalloc() == FALSE) /* if we fail, only serious if not a realloc */
  375.         return (vscreen != NULL);
  376.  
  377.     for (i = 0; i < term.t_mrow; ++i) {
  378.     vp = vscreen[i];
  379.     vp->v_flag = 0;
  380. #if OPT_COLOR
  381.     ReqFcolor(vp) = gfcolor;
  382.     ReqBcolor(vp) = gbcolor;
  383. #endif
  384.     }
  385. #if OPT_WORKING
  386.     if (!i_displayed && !im_displaying)
  387.     imworking(0);
  388. #endif
  389.     return TRUE;
  390. }
  391.  
  392. #if OPT_VIDEO_ATTRS
  393. static void
  394. set_vattrs(int row, int col, VIDEO_ATTR attr, size_t len)
  395. {
  396.     while (len--)
  397.         vscreen[row]->v_attrs[col++] = attr;
  398. }
  399. #else
  400. #define set_vattrs(row, col, attr, len) /*nothing*/
  401. #endif
  402.  
  403. static void
  404. freeVIDEO(register VIDEO *vp)
  405. {
  406.     if (vp != 0) {
  407. #if OPT_VIDEO_ATTRS
  408.         FreeIfNeeded (vp->v_attrs);
  409. #endif
  410.         free((char *)vp);
  411.     }
  412. }
  413.  
  414. int
  415. video_alloc(VIDEO **vpp)
  416. {
  417.     register VIDEO *vp;
  418.     /* struct VIDEO already has 4 of the bytes */
  419.     vp = typeallocplus(VIDEO, term.t_mcol - 4);
  420.     if (vp == 0)
  421.         return FALSE;
  422.     (void)memset((char *)vp, 0, sizeof(VIDEO) + term.t_mcol - 4);
  423.  
  424. #if OPT_VIDEO_ATTRS
  425.     vp->v_attrs = typecallocn(VIDEO_ATTR, (ALLOC_T)term.t_mcol);
  426.     if (vp->v_attrs == 0) {
  427.         free((char *)vp);
  428.         return FALSE;
  429.     }
  430. #endif
  431.     freeVIDEO(*vpp);
  432.     *vpp = vp;
  433.     return TRUE;
  434. }
  435.  
  436. static int
  437. vtalloc(void)
  438. {
  439.     register int i, first;
  440.     static int vcols, vrows;
  441.  
  442.     if (term.t_mrow > vrows) {
  443.         GROW(vscreen, VIDEO *, vrows, term.t_mrow);
  444. #if    ! MEMMAP
  445.         GROW(pscreen, VIDEO *, vrows, term.t_mrow);
  446. #endif
  447.         GROW(lmap, int, vrows, term.t_mrow);
  448.     } else {
  449.         for (i = term.t_mrow; i < vrows; i++) {
  450.             freeVIDEO(vscreen[i]);
  451. #if    ! MEMMAP
  452.             freeVIDEO(pscreen[i]);
  453. #endif
  454.         }
  455.     }
  456.  
  457.     first = (term.t_mcol > vcols) ? 0 : vrows;
  458.  
  459.     for (i = first; i < term.t_mrow; ++i) {
  460.         if (!video_alloc(&vscreen[i]))
  461.             return FALSE;
  462. #if    ! MEMMAP
  463.         if (!video_alloc(&pscreen[i]))
  464.             return FALSE;
  465. #endif    /* !MEMMAP */
  466.     }
  467.     vcols = term.t_mcol;
  468.     vrows = term.t_mrow;
  469.  
  470.     return TRUE;
  471. }
  472.  
  473. /* free all video memory, in anticipation of a (growing) resize */
  474. #if NO_LEAKS
  475. static void
  476. vtfree(void)
  477. {
  478.     register int i;
  479.  
  480.     if (vscreen) {
  481.         for (i = 0; i < term.t_mrow; ++i) {
  482.             freeVIDEO(vscreen[i]);
  483.         }
  484.         free ((char *)vscreen);
  485.         vscreen = 0;
  486.     }
  487.  
  488. #if    ! MEMMAP
  489.     if (pscreen) {
  490.         for (i = 0; i < term.t_mrow; ++i) {
  491.             freeVIDEO(pscreen[i]);
  492.         }
  493.         free ((char *)pscreen);
  494.         pscreen = 0;
  495.     }
  496. #endif
  497.     FreeIfNeeded (lmap);
  498. }
  499. #endif
  500.  
  501.  
  502. /*
  503.  * Set the virtual cursor to the specified row and column on the virtual
  504.  * screen. There is no checking for nonsense values.
  505.  */
  506. static void
  507. vtmove(int row, int col)
  508. {
  509.     vtrow = row;
  510.     vtcol = col;
  511. }
  512.  
  513. /* Write a character to the virtual screen. The virtual row and
  514.    column are updated. If we are not yet on left edge, don't print
  515.    it yet. If the line is too long put a ">" in the last column.
  516.    This routine only puts printing characters into the virtual
  517.    terminal buffers. Only column overflow is checked.
  518. */
  519.  
  520. static void
  521. vtputc(int c)
  522. {
  523.     /* since we don't allow wrapping on the message line, we only need
  524.      * to evaluate this once.  */
  525.     int lastcol = vtrow == term.t_nrow-1 ?  term.t_ncol-1 : term.t_ncol;
  526.     register VIDEO *vp;    /* ptr to line being updated */
  527.  
  528. #ifdef WMDLINEWRAP
  529.     if (vtrow < 0) {
  530.         static VIDEO *fake_line;
  531.         static int   length;
  532.         if (length != term.t_mcol || fake_line == 0) {
  533.             if (!video_alloc(&fake_line))
  534.                 return;
  535.             length = term.t_mcol;
  536.         }
  537.         vp = fake_line;
  538.     }
  539.     else
  540. #endif
  541.      vp = vscreen[vtrow];
  542.  
  543.     if (isPrint(c) && vtcol >= 0 && vtcol < lastcol) {
  544.         VideoText(vp)[vtcol++] = (c & (N_chars-1));
  545. #ifdef WMDLINEWRAP
  546.         if ((allow_wrap != 0)
  547.          && (vtcol == lastcol)
  548.          && (vtrow <  allow_wrap)) {
  549.             vtcol = 0;
  550.             if (++vtrow >= 0)
  551.                 vscreen[vtrow]->v_flag |= VFCHG;
  552.             taboff += lastcol;
  553.         }
  554. #endif
  555.         return;
  556.     }
  557.  
  558.     if (vtcol >= lastcol) {
  559.         VideoText(vp)[lastcol - 1] = MRK_EXTEND_RIGHT;
  560.     } else if (c == '\t') {
  561.         do {
  562.             vtputc(' ');
  563.         } while (((vtcol + taboff)%curtabval) != 0
  564.                   && vtcol < lastcol);
  565.     } else if (c == '\n') {
  566.         return;
  567.     } else if (isPrint(c)) {
  568.         ++vtcol;
  569.     } else {
  570.         vtlistc(c);
  571.     }
  572. }
  573.  
  574. /* how should high-bit unprintable chars be shown? */
  575. static int vt_octal;
  576.  
  577. /* shows non-printing character */
  578. static void
  579. vtlistc(int c)
  580. {
  581.     if (isPrint(c)) {
  582.         vtputc(c);
  583.         return;
  584.     }
  585.  
  586.     if (c & HIGHBIT) {
  587.         vtputc('\\');
  588.         if (vt_octal) {
  589.         vtputc(((c>>6)&3)+'0');
  590.         vtputc(((c>>3)&7)+'0');
  591.         vtputc(((c   )&7)+'0');
  592.         } else {
  593.         vtputc('x');
  594.         vtputc(hexdigits[(c>>4) & 0xf]);
  595.         vtputc(hexdigits[(c   ) & 0xf]);
  596.         }
  597.     } else {
  598.         vtputc('^');
  599.         vtputc(toalpha(c));
  600.     }
  601. }
  602.  
  603. static int
  604. vtgetc(int col)
  605. {
  606.     return vscreen[vtrow]->v_text[col];
  607. }
  608.  
  609. static void
  610. vtputsn(const char *s, int n)
  611. {
  612.     int c = 0;
  613.     while (n-- > 0 && (c = *s++) != EOS)
  614.         vtputc(c);
  615. }
  616.  
  617. /*
  618.  * Write a line to the screen at the current video coordinates, allowing for
  619.  * line-wrap or right-shifting.
  620.  */
  621. static void
  622. vtset(LINEPTR lp, WINDOW *wp)
  623. {
  624.     register char *from;
  625.     register int n = llength(lp);
  626.     BUFFER    *bp  = wp->w_bufp;
  627.     int    skip = -vtcol,
  628.         list = w_val(wp,WMDLIST);
  629.  
  630.     vt_octal = w_val(wp,WMDNONPRINTOCTAL);
  631.  
  632. #ifdef WMDLINEWRAP
  633.     /*
  634.      * If the window's offset is negative, we've got a case of linewrap
  635.      * where the line's beginning is forced before the beginning of the
  636.      * window.
  637.      */
  638.     if (wp->w_line.o < 0) {
  639.         vtrow -= wp->w_line.o;
  640.         skip = col2offs(wp, lp, -(wp->w_line.o * term.t_ncol));
  641.         n -= skip;
  642.     }
  643.     else
  644. #endif
  645.     if (w_val(wp,WMDNUMBER)) {
  646.         register int j, k, jk;
  647.         L_NUM    line = line_no(bp, lp);
  648.         int    fill = ' ';
  649.         char    temp[NU_WIDTH+2];
  650.  
  651.         vtcol = 0;    /* make sure we always see line numbers */
  652.         vtputsn(right_num(temp, NU_WIDTH-2, (long)line), NU_WIDTH-2);
  653.         vtputsn("  ", 2);
  654.         taboff = skip - vtcol;
  655.  
  656.         /* account for leading fill; this repeats logic in vtputc so
  657.          * I don't have to introduce a global variable... */
  658.         from = lp->l_text;
  659.         for (j = k = jk = 0; (j < n) && (k < skip); j++) {
  660.             register int    c = from[j];
  661.             if ((list || (c != '\t')) && !isPrint(c)) {
  662.                     if (c & HIGHBIT) {
  663.                     k += 4;
  664.                     fill = '\\';  /* FIXXXX */
  665.                 } else {
  666.                     k += 2;
  667.                     fill = toalpha(c);
  668.                 }
  669.             } else {
  670.                 if (c == '\t')
  671.                     k += (curtabval - (k % curtabval));
  672.                 else if (isPrint(c))
  673.                     k++;
  674.                 fill = ' ';
  675.             }
  676.             jk = j+1;
  677.         }
  678.         while (k-- > skip)
  679.             vtputc(fill);
  680.         if ((skip = jk) < 0)
  681.             skip = 0;
  682.         n -= skip;
  683.     } else
  684.         skip = 0;
  685.  
  686. #if OPT_B_LIMITS
  687.     taboff -= w_left_margin(wp);
  688. #endif
  689.     from = lp->l_text + skip;
  690. #ifdef WMDLINEWRAP
  691.     allow_wrap = w_val(wp,WMDLINEWRAP) ? mode_row(wp)-1 : 0;
  692. #endif
  693.  
  694.     /*
  695.      * The loops below are split up for a reason - there's a hidden side effect
  696.      * which makes the final increment of vtcol inconsistent, so it won't
  697.      * terminate until the end of the line.  For very long lines, that's a
  698.      * performance hit.
  699.      */
  700. #define VTSET_PUT(ch,count) if(list) vtlistc(ch); else vtputc(ch); count
  701. #ifdef WMDLINEWRAP
  702.     if (w_val(wp,WMDLINEWRAP))
  703.     {
  704.         while ((vtcol < term.t_ncol)
  705.           &&   ((vtrow == term.t_nrow-1) || (vtrow < mode_row(wp)))
  706.           &&   (n > 0)) {
  707.             VTSET_PUT(*from++,n--);
  708.         }
  709.         if ((vtcol <= term.t_ncol)
  710.           &&   ((vtrow == term.t_nrow-1) || (vtrow < mode_row(wp)))
  711.           &&   (n > 0)) {
  712.             VTSET_PUT(*from++,n--);
  713.         }
  714.     }
  715.     else
  716. #endif
  717.     {
  718.         while ((vtcol < term.t_ncol)
  719.           &&   (n > 0)) {
  720.             VTSET_PUT(*from++,n--);
  721.         }
  722.         if ((vtcol <= term.t_ncol)
  723.           &&   (n > 0)) {
  724.             VTSET_PUT(*from++,n--);
  725.         }
  726.     }
  727.  
  728.     /* Display a "^J" if 'list' mode is active, unless we've suppressed
  729.      * it for some reason.
  730.      */
  731.     if (list && (n >= 0)) {
  732.         if (b_is_scratch(bp) && listrimmed(lp))
  733.             /*EMPTY*/;
  734.         else if (!b_val(bp,MDNEWLINE) && (lforw(lp) == buf_head(bp)))
  735.             /*EMPTY*/;
  736.         else
  737.             vtlistc('\n');
  738.     }
  739. #ifdef WMDLINEWRAP
  740.     allow_wrap = 0;
  741. #endif
  742. }
  743.  
  744. /*
  745.  * Erase from the end of the software cursor to the end of the line on which
  746.  * the software cursor is located.
  747.  */
  748. static void
  749. vteeol(void)
  750. {
  751.     if (vtcol < term.t_ncol) {
  752.         int n = (vtcol >= 0) ? vtcol : 0;
  753. #ifdef WMDLINEWRAP
  754.         if (vtrow >= 0)
  755. #endif
  756.         if (n >= 0) {
  757.             (void)memset(&vscreen[vtrow]->v_text[n],
  758.                 ' ', (SIZE_T)(term.t_ncol-n));
  759.         }
  760.         vtcol = term.t_ncol;
  761.     }
  762. }
  763.  
  764. /* upscreen:    user routine to force a screen update
  765.         always finishes complete update        */
  766. #if !SMALLER
  767. /* ARGSUSED */
  768. int
  769. upscreen(int f GCC_UNUSED, int n GCC_UNUSED)
  770. {
  771.     return update(TRUE);
  772. }
  773. #endif
  774.  
  775. static    UINT    scrflags;
  776.  
  777. /* line to virtual column */
  778. int
  779. mk_to_vcol (MARK mark, int expanded, int base)
  780. {
  781.     int    col = 0;
  782.     int    i = base;
  783.     int    c;
  784.     int    lim;
  785.     LINEPTR lp;
  786.  
  787.     lp = mark.l;
  788.     lim = mark.o
  789.         + ((!global_g_val(GMDALTTABPOS) && !insertmode) ? 1 : 0);
  790.     if (lim > llength(lp))
  791.         lim = llength(lp);
  792.  
  793.     while (i < lim) {
  794.         c = lgetc(lp, i++);
  795.         if (c == '\t' && !expanded) {
  796.             col += curtabval - (col%curtabval);
  797.         } else {
  798.             if (!isPrint(c)) {
  799.                 col += (c & HIGHBIT) ? 3 : 1;
  800.             }
  801.             ++col;
  802.         }
  803.  
  804.     }
  805.     col += base;
  806.     if (!global_g_val(GMDALTTABPOS) && !insertmode &&
  807.             col != 0 && mark.o < llength(lp))
  808.         col--;
  809.     return col;
  810. }
  811.  
  812. void
  813. kbd_openup(void)
  814. {
  815. #if !MEMMAP && !OPT_PSCREEN
  816.     int i;
  817.     size_t alen = sizeof(VIDEO_ATTR) * term.t_ncol;
  818. #endif
  819.     kbd_flush();
  820.     bottomleft();
  821.     TTputc('\n');
  822.     TTputc('\r');
  823.     TTflush();
  824. #if !MEMMAP && !OPT_PSCREEN
  825.     if (pscreen != 0) {
  826.         for (i = 0; i < term.t_nrow-1; ++i) {
  827.             (void)memcpy(
  828.                 pscreen[i]->v_text,
  829.                 pscreen[i+1]->v_text,
  830.                 (SIZE_T)(term.t_ncol));
  831. #if OPT_VIDEO_ATTRS
  832.             (void)memcpy(
  833.                 pscreen[i]->v_attrs,
  834.                 pscreen[i+1]->v_attrs,
  835.                 alen);
  836. #endif
  837.         }
  838.         (void)memset(pscreen[i]->v_text, ' ', (SIZE_T)(term.t_ncol));
  839. #if OPT_VIDEO_ATTRS
  840.         (void)memset(pscreen[i]->v_attrs, VADIRTY, alen);
  841. #endif
  842.     }
  843. #endif
  844. }
  845.  
  846. /* cannot be allocated since it's used by OPT_RAMSIZE */
  847. static char my_overlay[20];
  848.  
  849. /* save/erase text for the overlay on the message line */
  850. void
  851. kbd_overlay(const char *s)
  852. {
  853.     my_overlay[0] = EOS;
  854.     if ((mpresf = (s != 0 && *s != EOS)) != 0) {
  855.         strncpy(my_overlay, s, sizeof(my_overlay)-1);
  856.     }
  857. }
  858.  
  859. void
  860. kbd_flush(void)
  861. {
  862.     int ok;
  863.  
  864.     beginDisplay();
  865.     if (vscreen != 0) {
  866.         int row = term.t_nrow - 1;
  867.  
  868.         vtmove(row, -w_val(wminip,WVAL_SIDEWAYS));
  869.  
  870.         ok = (wminip != 0
  871.            && wminip->w_dot.l != 0
  872.            && wminip->w_dot.l->l_text != 0);
  873.         if (ok) {
  874.             TRACE(("SHOW:%2d:%.*s\n",
  875.                 llength(wminip->w_dot.l),
  876.                 llength(wminip->w_dot.l),
  877.                 wminip->w_dot.l->l_text));
  878.             lsettrimmed(wminip->w_dot.l);
  879.             vtset(wminip->w_dot.l, wminip);
  880.         }
  881.  
  882.         vteeol();
  883.         set_vattrs(row, 0,
  884.             (VIDEO_ATTR) (miniedit
  885.                     ? global_g_val(GVAL_MINI_HILITE)
  886.                     : 0),
  887.             term.t_ncol);
  888.         if (my_overlay[0] != EOS) {
  889.             int n = term.t_ncol - strlen(my_overlay) - 1;
  890.             if (n > 0) {
  891.                 (void)memcpy(&vscreen[row]->v_text[n],
  892.                     my_overlay,
  893.                     strlen(my_overlay));
  894.             }
  895.         }
  896.         vscreen[row]->v_flag |= VFCHG;
  897.         updateline(row, 0, term.t_ncol);
  898.         if (ok)
  899.             movecursor(row,
  900.                 offs2col(wminip, wminip->w_dot.l, wminip->w_dot.o));
  901.     }
  902.     TTflush();
  903.     endofDisplay();
  904. }
  905.  
  906. static int
  907. TypeAhead(int force)
  908. {
  909.     if (force != TRUE) {
  910.         if ((force == FALSE && !global_g_val(GMDSMOOTH_SCROLL))
  911.          || (force == SORTOFTRUE))
  912.             return (keystroke_avail());
  913.     }
  914.     return FALSE;
  915. }
  916.  
  917. /*
  918.  * Make sure that the display is right. This is a three part process. First,
  919.  * scan through all of the windows looking for dirty ones. Check the framing,
  920.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  921.  * correct for the current window. Third, make the virtual and physical
  922.  * screens the same.
  923.  */
  924. int
  925. update(
  926. int force)    /* force update past type ahead? */
  927. {
  928.     register WINDOW *wp;
  929.     int origrow, origcol;
  930.     int screenrow, screencol;
  931.  
  932.     /* Get row and column prior to doing the update in case we are
  933.      * reading the message line.
  934.      */
  935.     origrow = ttrow;
  936.     origcol = ttcol;
  937.  
  938.     if (!curbp || !vscreen) /* not initialized */
  939.         return FALSE;
  940.     if (TypeAhead(force))
  941.         return SORTOFTRUE;
  942. #if    OPT_VISIBLE_MACROS == 0
  943.     if (force == FALSE && kbd_replaying(TRUE) && (get_recorded_char(FALSE) != -1))
  944.         return SORTOFTRUE;
  945. #endif
  946.  
  947.     beginDisplay();
  948.  
  949.     /* first, propagate mode line changes to all instances of
  950.         a buffer displayed in more than one window */
  951.     for_each_visible_window(wp) {
  952.         if (wp->w_flag & WFMODE) {
  953.             if (wp->w_bufp->b_nwnd > 1) {
  954.                 /* make sure all previous windows have this */
  955.                 register WINDOW *owp;
  956.                 for_each_visible_window(owp)
  957.                     if (owp->w_bufp == wp->w_bufp)
  958.                         owp->w_flag |= WFMODE;
  959.             }
  960.         }
  961.     }
  962.  
  963.     /* look for scratch-buffers that should be recomputed.  */
  964. #if    OPT_UPBUFF
  965.     for_each_visible_window(wp)
  966.         if (b_is_obsolete(wp->w_bufp))
  967.             recompute_buffer(wp->w_bufp);
  968. #endif
  969.  
  970.     /* look for windows that need the ruler updated */
  971. #ifdef WMDRULER
  972.     for_each_visible_window(wp) {
  973.         if (w_val(wp,WMDRULER)) {
  974.             int    line  = line_no(wp->w_bufp, wp->w_dot.l);
  975.             int    col   = mk_to_vcol(wp->w_dot, w_val(wp,WMDLIST), 0) + 1;
  976.  
  977.             if (line != wp->w_ruler_line
  978.              || col  != wp->w_ruler_col) {
  979.                 wp->w_ruler_line = line;
  980.                 wp->w_ruler_col  = col;
  981.                 wp->w_flag |= WFMODE;
  982.             }
  983.         } else if (wp->w_flag & WFSTAT) {
  984.             wp->w_flag |= WFMODE;
  985.         }
  986.         wp->w_flag &= ~WFSTAT;
  987.     }
  988. #endif
  989.  
  990.     do {
  991.         /* update any windows that need refreshing */
  992.         for_each_visible_window(wp) {
  993.             if (wp->w_flag) {
  994.                 curtabval = tabstop_val(wp->w_bufp);
  995.                 /* if the window has changed, service it */
  996.                 reframe(wp);    /* check the framing */
  997.                 if (wp->w_flag & (WFKILLS|WFINS)) {
  998.                     scrflags |= (wp->w_flag & (WFINS|WFKILLS));
  999.                     wp->w_flag &= ~(WFKILLS|WFINS);
  1000.                 }
  1001.                 if ((wp->w_flag & ~(WFMODE)) == WFEDIT)
  1002.                     updone(wp);    /* update EDITed line */
  1003.                 else if (wp->w_flag & ~(WFMOVE))
  1004.                     updall(wp);    /* update all lines */
  1005. #if OPT_SCROLLBARS
  1006.                 if (wp->w_flag & (WFHARD | WFSBAR))
  1007.                     gui_update_scrollbar(wp);
  1008. #endif /* OPT_SCROLLBARS */
  1009.  
  1010. #if OPT_VIDEO_ATTRS
  1011.                 if (wp->w_flag & (WFHARD | WFEDIT))
  1012.                     updattrs(wp);
  1013. #endif
  1014.                 if (scrflags || (wp->w_flag & (WFMODE|WFCOLR)))
  1015.                     modeline(wp);    /* update modeline */
  1016.                 wp->w_flag = 0;
  1017.                 wp->w_force = 0;
  1018.             }
  1019.         }
  1020.         curtabval = tabstop_val(curbp);
  1021.  
  1022.     /* Recalculate the current hardware cursor location.  If true, we've
  1023.      * done a horizontal scroll.
  1024.      */
  1025.     } while (updpos(&screenrow, &screencol));
  1026.  
  1027.     /* check for lines to de-extend */
  1028.     upddex();
  1029.  
  1030.     /* if screen is garbage, re-plot it */
  1031.     if (sgarbf)
  1032.         updgar();
  1033.  
  1034.     /* update the virtual screen to the physical screen */
  1035.     updupd(force);
  1036.  
  1037.     /* update the cursor and flush the buffers */
  1038.     if (reading_msg_line)
  1039.         movecursor(origrow, origcol);
  1040.     else
  1041.         movecursor(screenrow, screencol);
  1042.  
  1043.     TTflush();
  1044.     endofDisplay();
  1045.     i_displayed = TRUE;
  1046.  
  1047.     while (chg_width || chg_height)
  1048.         newscreensize(chg_height,chg_width);
  1049.     return(TRUE);
  1050. }
  1051.  
  1052. /*    reframe:    check to see if the cursor is on in the window
  1053.             and re-frame it if needed or wanted        */
  1054. static void
  1055. reframe(WINDOW *wp)
  1056. {
  1057.     register LINEPTR dlp;
  1058.     register LINEPTR lp;
  1059.     register int i = 0;
  1060.     register int rows;
  1061.     int    founddot = FALSE;    /* set to true iff we find dot */
  1062.     int    tildecount;
  1063.  
  1064.     /* if not a requested reframe, check for a needed one */
  1065.     if ((wp->w_flag & WFFORCE) == 0) {
  1066.         /* initial update in main.c may not set these first... */
  1067.         if (wp->w_dot.l == (LINE *)0) {
  1068.             wp->w_dot.l = lforw(win_head(wp));
  1069.             wp->w_dot.o = 0;
  1070.         }
  1071.         if (wp->w_line.l == (LINE *)0) {
  1072.             wp->w_line.l = wp->w_dot.l;
  1073.             wp->w_line.o = 0;
  1074.         }
  1075. #if CAN_SCROLL
  1076.         /* loop from one line above the window to one line after */
  1077.         lp = lback(wp->w_line.l);
  1078.         i  = -line_height(wp,lp);
  1079. #else
  1080.         /* loop through the window */
  1081.         lp = wp->w_line.l;
  1082.         i  = 0;
  1083. #endif
  1084.         for_ever {
  1085.             /* if the line is in the window, no reframe */
  1086.             if (lp == wp->w_dot.l) {
  1087.                 founddot = TRUE;
  1088. #if CAN_SCROLL
  1089.                 /* if not _quite_ in, we'll reframe gently */
  1090.                 if ( i < 0 || i >= wp->w_ntrows) {
  1091.                     /* if the terminal can't help, then
  1092.                         we're simply outside */
  1093.                     if (term.t_scroll == null_t_scroll)
  1094.                         i = wp->w_force;
  1095.                     break;
  1096.                 }
  1097. #endif
  1098. #ifdef WMDLINEWRAP
  1099.                 if (w_val(wp,WMDLINEWRAP)
  1100.                  && i > 0
  1101.                  && i + line_height(wp,lp) > wp->w_ntrows) {
  1102.                     i = wp->w_ntrows;
  1103.                     break;
  1104.                 }
  1105. #endif
  1106.                 lp = wp->w_line.l;
  1107.                 goto kill_tildes;
  1108.             }
  1109.  
  1110.             /* if we are at the end of the file, reframe */
  1111.             if (i >= 0 && lp == win_head(wp))
  1112.                 break;
  1113.  
  1114.             /* on to the next line */
  1115.             if (i >= wp->w_ntrows) {
  1116.                 i = 0;    /* dot-not-found */
  1117.                 break;
  1118.             }
  1119.             i += line_height(wp,lp);
  1120.             lp = lforw(lp);
  1121.         }
  1122.     }
  1123.  
  1124. #if CAN_SCROLL
  1125.     if (i < 0) {    /* we're just above the window */
  1126.         i = 1;    /* put dot at first line */
  1127.         scrflags |= WFINS;
  1128.     } else if (founddot && (i >= wp->w_ntrows)) {
  1129.         /* we're just below the window */
  1130.         i = -1;    /* put dot at last line */
  1131.         scrflags |= WFKILLS;
  1132.     } else /* put dot where requested */
  1133. #endif
  1134.         i = wp->w_force;  /* (is 0, unless reposition() was called) */
  1135.  
  1136.     wp->w_flag |= WFMODE;
  1137.     wp->w_line.o = 0;
  1138.  
  1139.     /* w_force specifies which line of the window dot should end up on */
  1140.     /*     positive --> lines from the top                */
  1141.     /*     negative --> lines from the bottom            */
  1142.     /*     zero --> middle of window                */
  1143.  
  1144.     lp = wp->w_dot.l;
  1145.  
  1146. #ifdef WMDLINEWRAP
  1147.     /*
  1148.      * Center dot in middle of screen with line-wrapping
  1149.      */
  1150.     if (i == 0 && w_val(wp,WMDLINEWRAP)) {
  1151.         rows = (wp->w_ntrows - line_height(wp,lp) + 2) / 2;
  1152.         while (rows > 0) {
  1153.             dlp = lback(lp);
  1154.             if (dlp == win_head(wp))
  1155.                 break;
  1156.             if ((rows -= line_height(wp, dlp)) < 0)
  1157.                 break;
  1158.             lp = dlp;
  1159.         }
  1160.     } else
  1161. #endif
  1162.     {
  1163.         rows = (i != 0)
  1164.             ? wp->w_ntrows
  1165.             : wp->w_ntrows / 2;
  1166.         while (rows > 0) {
  1167.             if ((i > 0)
  1168.              && (--i <= 0))
  1169.                 break;
  1170.             dlp = lback(lp);
  1171.             if (dlp == win_head(wp))
  1172.                 break;
  1173.             if ((rows -= line_height(wp, lp)) < 0)
  1174.                 break;
  1175.             lp = dlp;
  1176.         }
  1177.         if (rows < line_height(wp, lp)
  1178.          && (lp != wp->w_dot.l)) {
  1179.             while (i++ < 0) {
  1180.                 dlp = lforw(lp);
  1181.                 if (dlp == win_head(wp))
  1182.                     break;
  1183.                 else
  1184.                     lp = dlp;
  1185.             }
  1186.         }
  1187.     }
  1188. kill_tildes:
  1189.     /* Eliminate as many tildes as possible from bottom */
  1190.     dlp = lp;
  1191.     rows = wp->w_ntrows;
  1192.     while (rows > 0 && (dlp != win_head(wp))) {
  1193.         rows -= line_height(wp, dlp);
  1194.         dlp = lforw(dlp);
  1195.     }
  1196.     dlp = lback(lp);
  1197.  
  1198.     tildecount = (wp->w_ntrows * ntildes)/100;
  1199.     if (tildecount == wp->w_ntrows)
  1200.         tildecount--;
  1201.  
  1202.     while (dlp != win_head(wp)
  1203.            && (rows -= line_height(wp, dlp)) >= tildecount) {
  1204.         lp = dlp;
  1205.         dlp = lback(lp);
  1206.     }
  1207.  
  1208.     /* and reset the current line-at-top-of-window */
  1209.     if (lp != win_head(wp) /* mouse click could be past end */
  1210.      && lp != wp->w_line.l) { /* no need to set it if already there */
  1211.         wp->w_line.l = lp;
  1212.         wp->w_flag |= WFHARD;
  1213.         wp->w_flag &= ~WFFORCE;
  1214.     }
  1215. #ifdef WMDLINEWRAP
  1216.     /*
  1217.      * Ensure that dot will be visible, by adjusting the w_line.o value if
  1218.      * necessary.  That's used to start the beginning of the first line in
  1219.      * a window "before" the start of the window.
  1220.      */
  1221.     if (w_val(wp,WMDLINEWRAP)
  1222.      && sameline(wp->w_line, wp->w_dot)) {
  1223.         int want = mark2col(wp, wp->w_dot) / term.t_ncol;
  1224.         if (want + wp->w_line.o >= wp->w_ntrows) {
  1225.             wp->w_line.o = wp->w_ntrows - want - 1;
  1226.             wp->w_flag |= WFHARD;
  1227.             wp->w_flag &= ~WFFORCE;
  1228.         }
  1229.         else if (want + wp->w_line.o < 0) {
  1230.             wp->w_line.o = -want;
  1231.             wp->w_flag |= WFHARD;
  1232.             wp->w_flag &= ~WFFORCE;
  1233.         }
  1234.     }
  1235. #endif
  1236. }
  1237.  
  1238. /*    updone:    update the current line    to the virtual screen        */
  1239.  
  1240. static void
  1241. updone(
  1242. WINDOW *wp)    /* window to update current line in */
  1243. {
  1244.     register LINEPTR lp;    /* line to update */
  1245.     register int sline;    /* physical screen line to update */
  1246.  
  1247.     /* search down the line we want */
  1248.     lp = wp->w_line.l;
  1249.     sline = TopRow(wp);
  1250.     while (lp != wp->w_dot.l) {
  1251.         sline += line_height(wp,lp);
  1252.         lp = lforw(lp);
  1253.     }
  1254.  
  1255.     l_to_vline(wp,lp,sline);
  1256.     vteeol();
  1257. }
  1258.  
  1259. /*    updall:    update all the lines in a window on the virtual screen */
  1260.  
  1261. static void
  1262. updall(
  1263. WINDOW *wp)    /* window to update lines in */
  1264. {
  1265.     register LINEPTR lp;    /* line to update */
  1266.     register int sline;    /* physical screen line to update */
  1267.  
  1268.     /* search down the lines, updating them */
  1269.     lp = wp->w_line.l;
  1270.     sline = TopRow(wp);
  1271.     while (sline < mode_row(wp)) {
  1272.         l_to_vline(wp,lp,sline);
  1273.         vteeol();
  1274.         sline += line_height(wp,lp);
  1275.         if (lp != win_head(wp))
  1276.             lp = lforw(lp);
  1277.     }
  1278. }
  1279.  
  1280. /* line to virtual screen line */
  1281. static void
  1282. l_to_vline(
  1283. WINDOW *wp,    /* window to update lines in */
  1284. LINEPTR lp,
  1285. int sline)
  1286. {
  1287.     C_NUM    left;
  1288.  
  1289.     /*
  1290.      * Mark the screen lines changed, resetting the requests for reverse
  1291.      * video.  Set the global 'taboff' to the amount of horizontal
  1292.      * scrolling.
  1293.      */
  1294. #ifdef WMDLINEWRAP
  1295.     if (w_val(wp,WMDLINEWRAP)) {
  1296.         int top_line = sline - wp->w_line.o;
  1297.         register int    m = (top_line >= 0) ? top_line : 0;
  1298.         register int    n = top_line + line_height(wp, lp);
  1299.         while (n > m)
  1300.             if (--n < mode_row(wp)) {
  1301.                 vscreen[n]->v_flag |= VFCHG;
  1302.                 vscreen[n]->v_flag &= ~VFREQ;
  1303.             }
  1304.         taboff = 0;
  1305.     } else
  1306. #endif
  1307.     {
  1308.         vscreen[sline]->v_flag |= VFCHG;
  1309.         vscreen[sline]->v_flag &= ~VFREQ;
  1310.         if (w_val(wp,WVAL_SIDEWAYS))
  1311.             taboff = w_val(wp,WVAL_SIDEWAYS);
  1312.     }
  1313.     left = taboff;
  1314.  
  1315.     if (lp != win_head(wp)) {
  1316.         vtmove(sline, -left);
  1317.         vtset(lp, wp);
  1318.         if (left && sline >= 0) {
  1319.             register int    zero = nu_width(wp);
  1320.             vscreen[sline]->v_text[zero] = MRK_EXTEND_LEFT;
  1321.             if (vtcol <= zero) vtcol = zero+1;
  1322.         }
  1323.     } else {
  1324.         vtmove(sline, 0);
  1325.         vtputc(MRK_EMPTY);
  1326.     }
  1327.     taboff = 0;
  1328. #if    OPT_COLOR
  1329.     if (sline >= 0) {
  1330.         ReqFcolor(vscreen[sline]) = gfcolor;
  1331.         ReqBcolor(vscreen[sline]) = gbcolor;
  1332.     }
  1333. #endif
  1334. }
  1335.  
  1336. /*    updpos:    update the position of the hardware cursor and handle extended
  1337.         lines. This is the only update for simple moves.
  1338.         returns the screen column for the cursor, and
  1339.         a boolean indicating if full sideways scroll was necessary */
  1340. static int
  1341. updpos(
  1342. int *screenrowp,
  1343. int *screencolp)
  1344. {
  1345.     register LINEPTR lp;
  1346. #ifdef WMDLINEWRAP
  1347.     register int i;
  1348. #endif
  1349.     register int col, excess;
  1350.     register int collimit;
  1351.     int moved = FALSE;
  1352.     int nuadj = is_empty_buf(curwp->w_bufp) ? 0 : nu_width(curwp);
  1353.     int liadj = (w_val(curwp,WMDLIST)) ? 1 : 0;
  1354.  
  1355.     /* find the current row */
  1356.     lp = curwp->w_line.l;
  1357.     currow = TopRow(curwp);
  1358.     while (lp != DOT.l) {
  1359.         currow += line_height(curwp,lp);
  1360.         lp = lforw(lp);
  1361.         if (lp == curwp->w_line.l
  1362.          || currow > mode_row(curwp)) {
  1363.             mlforce("BUG:  lost dot updpos().  setting at top");
  1364.             lp = curwp->w_line.l = DOT.l = lforw(buf_head(curbp));
  1365.             currow = TopRow(curwp);
  1366.         }
  1367.     }
  1368.  
  1369.     /* find the current column */
  1370.     col = mk_to_vcol(DOT, w_val(curwp,WMDLIST), w_left_margin(curwp));
  1371.  
  1372. #ifdef WMDLINEWRAP
  1373.     if (w_val(curwp,WMDLINEWRAP)) {
  1374.         curcol = col;
  1375.         collimit = term.t_ncol - nuadj;
  1376.         *screenrowp = currow;
  1377.         if (col >= collimit) {
  1378.             col -= collimit;
  1379.             *screenrowp += 1;
  1380.             if (col >= term.t_ncol)
  1381.                 *screenrowp += (col / term.t_ncol);
  1382.             *screencolp = col % term.t_ncol;
  1383.         } else {
  1384.             *screencolp = col + nuadj;
  1385.         }
  1386.         /* kludge to keep the cursor within the window */
  1387.         i = mode_row(curwp) - 1;
  1388.         if (*screenrowp > i) {
  1389.             *screenrowp = i;
  1390.             *screencolp = term.t_ncol - 1;
  1391.         }
  1392.         return FALSE;
  1393.     } else
  1394. #endif
  1395.      *screenrowp = currow;
  1396.  
  1397.     /* ...adjust to offset from shift-margin */
  1398.     curcol = col - w_val(curwp,WVAL_SIDEWAYS);
  1399.     *screencolp = curcol;
  1400.  
  1401.     /* if extended, flag so and update the virtual line image */
  1402.     collimit = col_limit(curwp);
  1403.     excess = curcol - collimit + liadj;
  1404.     if ((excess > 0) || (excess == 0 &&
  1405.             (DOT.o < llength(DOT.l) - 1 ))) {
  1406.         if (w_val(curwp,WMDHORSCROLL)) {
  1407.             (void)mvrightwind(TRUE, excess + collimit/2 );
  1408.             moved = TRUE;
  1409.         } else {
  1410.             *screencolp = updext_past(col, excess);
  1411.         }
  1412.     } else if (w_val(curwp,WVAL_SIDEWAYS) && (curcol < 1)) {
  1413.         if (w_val(curwp,WMDHORSCROLL)) {
  1414.             (void)mvleftwind(TRUE, -curcol + collimit/2 + 1);
  1415.             moved = TRUE;
  1416.         } else {
  1417.             *screencolp = updext_before(col);
  1418.         }
  1419.     } else {
  1420.         if (vscreen[currow]->v_flag & VFEXT) {
  1421.             l_to_vline(curwp,lp,currow);
  1422.             vteeol();
  1423.             /* this line no longer is extended */
  1424.             vscreen[currow]->v_flag &= ~VFEXT;
  1425.         }
  1426.     }
  1427.     if (!moved)
  1428.         *screencolp += nuadj;
  1429.     return moved;
  1430. }
  1431.  
  1432. /*    upddex:    de-extend any line that deserves it        */
  1433.  
  1434. static void
  1435. upddex(void)
  1436. {
  1437.     register WINDOW *wp;
  1438.     register LINEPTR lp;
  1439.     register int i;
  1440.  
  1441.     for_each_visible_window(wp) {
  1442.         lp = wp->w_line.l;
  1443.         i = TopRow(wp);
  1444.  
  1445.         curtabval = tabstop_val(wp->w_bufp);
  1446.  
  1447.         while (i < mode_row(wp)) {
  1448.             if (i >= 0
  1449.              && vscreen[i]->v_flag & VFEXT) {
  1450.                 if ((wp != curwp)
  1451.                  || (lp != wp->w_dot.l)
  1452.                  || ((i != currow)
  1453.                   && (curcol < col_limit(wp)))) {
  1454.                     l_to_vline(wp,lp,i);
  1455.                     vteeol();
  1456.                     /* this line no longer is extended */
  1457.                     vscreen[i]->v_flag &= ~VFEXT;
  1458.                 }
  1459.             }
  1460.             i += line_height(wp,lp);
  1461.             lp = lforw(lp);
  1462.         }
  1463.     }
  1464.     curtabval = tabstop_val(curbp);
  1465. }
  1466.  
  1467. /*    updgar:    if the screen is garbage, clear the physical screen and
  1468.         the virtual screen and force a full update        */
  1469.  
  1470. static void
  1471. updgar(void)
  1472. {
  1473. #if !MEMMAP && !OPT_PSCREEN
  1474.     register int j;
  1475. #endif
  1476.     register int i;
  1477.  
  1478.     for (i = 0; i < term.t_nrow; ++i) {
  1479.         vscreen[i]->v_flag |= VFCHG;
  1480. #if    OPT_REVSTA
  1481.         vscreen[i]->v_flag &= ~VFREV;
  1482. #endif
  1483. #if    OPT_COLOR
  1484.         CurFcolor(vscreen[i]) = -1;
  1485.         CurBcolor(vscreen[i]) = -1;
  1486. #endif
  1487. #if    ! MEMMAP && ! OPT_PSCREEN
  1488.         for (j = 0; j < term.t_ncol; ++j) {
  1489.             CELL_TEXT(i,j) = ' ';
  1490. #if OPT_VIDEO_ATTRS
  1491.             CELL_ATTR(i,j) = 0;
  1492. #endif /* OPT_VIDEO_ATTRS */
  1493.         }
  1494. #endif
  1495.     }
  1496. #if !OPT_PSCREEN
  1497. #if    OPT_COLOR
  1498.     TTforg(gfcolor);         /* Need to set before erasing. */
  1499.     TTbacg(gbcolor);
  1500. #endif
  1501.     movecursor(0, 0);         /* Erase the screen. */
  1502.     TTeeop();
  1503. #else
  1504.     kbd_erase_to_end(0);
  1505. #endif
  1506.     sgarbf = FALSE;             /* Erase-page clears */
  1507.     kbd_flush();
  1508. }
  1509.  
  1510. /*    updupd:    update the physical screen from the virtual screen    */
  1511.  
  1512. static void
  1513. updupd(
  1514. int force GCC_UNUSED)    /* forced update flag */
  1515. {
  1516.     register int i;
  1517.  
  1518. #if CAN_SCROLL
  1519.     if (scrflags & WFKILLS)
  1520.         (void)scrolls(FALSE);
  1521.     if (scrflags & WFINS)
  1522.         (void)scrolls(TRUE);
  1523.     scrflags = 0;
  1524. #endif
  1525.  
  1526.     for (i = 0; i < term.t_nrow; ++i) {
  1527.         /* for each line that needs to be updated*/
  1528.         if ((vscreen[i]->v_flag & (VFCHG|VFCOL)) != 0) {
  1529. #if !DISP_X11
  1530.             if (TypeAhead(force))
  1531.                 return;
  1532. #endif
  1533.             updateline(i, 0, term.t_ncol);
  1534.         }
  1535.     }
  1536. }
  1537.  
  1538. #if OPT_VIDEO_ATTRS
  1539. static void
  1540. updattrs(WINDOW *wp)
  1541. {
  1542.     AREGION *ap;
  1543.     int i;
  1544.  
  1545.     L_NUM start_wlnum, end_wlnum;
  1546.     LINEPTR lp;
  1547.     int rows;
  1548.  
  1549.     /*
  1550.      * Clear portion of virtual screen associated with window pointer
  1551.      * of all attributes.
  1552.      */
  1553.     /* FIXME: color; need to set to value indicating fg and bg for window */
  1554.     for (i = wp->w_toprow + wp->w_ntrows - 1; i >= wp->w_toprow; i--)
  1555.         set_vattrs(i, 0, 0, term.t_ncol);
  1556.  
  1557.     /*
  1558.      * No need to do any more work on this window if there are no
  1559.      * attributes.
  1560.      */
  1561.     if (wp->w_bufp->b_attribs == NULL)
  1562.     return;
  1563.  
  1564.     /*
  1565.      * Compute starting and ending line numbers for the window.  We
  1566.      * also fill in lmap which is used for mapping line numbers to
  1567.      * screen row numbers.
  1568.      */
  1569.     lp = wp->w_line.l;
  1570.     start_wlnum =
  1571.     end_wlnum = line_no(wp->w_bufp, lp);
  1572.     rows = wp->w_ntrows;
  1573.     lmap[end_wlnum - start_wlnum] = TopRow(wp);
  1574.     while ( (rows -= line_height(wp,lp)) > 0) {
  1575.     lp = lforw(lp);
  1576.     end_wlnum++;
  1577.     lmap[end_wlnum - start_wlnum] = TopRow(wp) + wp->w_ntrows - rows;
  1578.     }
  1579.  
  1580.     /*
  1581.      * Set current attributes in virtual screen associated with window
  1582.      * pointer.
  1583.      */
  1584.     for (ap = wp->w_bufp->b_attribs; ap != NULL;) {
  1585.     VIDEO_ATTR attr;
  1586.     C_NUM start_col, end_col;
  1587.     C_NUM rect_start_col = 0, rect_end_col = 0;
  1588.     L_NUM start_rlnum, end_rlnum, lnum, start_lnum, end_lnum;
  1589.     start_rlnum = line_no(wp->w_bufp, ap->ar_region.r_orig.l);
  1590.     end_rlnum = line_no(wp->w_bufp, ap->ar_region.r_end.l);
  1591.  
  1592.     /* Garbage collect empty visible regions */
  1593.     if (start_rlnum == end_rlnum
  1594.      && VATTRIB(ap->ar_vattr) != 0
  1595.      && ap->ar_region.r_orig.o >= ap->ar_region.r_end.o) {
  1596.         AREGION *nap = ap->ar_next;
  1597.         free_attrib(wp->w_bufp, ap);
  1598.         ap = nap;
  1599.         continue;
  1600.     }
  1601.  
  1602.     if (start_rlnum > start_wlnum) {
  1603.         start_lnum = start_rlnum;
  1604.         lp = ap->ar_region.r_orig.l;
  1605.     }
  1606.     else {
  1607.         start_lnum = start_wlnum;
  1608.         lp = wp->w_line.l;
  1609.     }
  1610.     end_lnum = (end_rlnum < end_wlnum) ? end_rlnum : end_wlnum;
  1611.     attr = ap->ar_vattr;
  1612.     if (ap->ar_shape == RECTANGLE) {
  1613.         int n;
  1614.         rect_start_col = mark2col(wp, ap->ar_region.r_orig);
  1615.         rect_end_col   = mark2col(wp, ap->ar_region.r_end);
  1616.         if (rect_end_col < rect_start_col) {
  1617.             C_NUM col = rect_end_col;
  1618.             rect_end_col = rect_start_col;
  1619.             rect_start_col = col;
  1620.             n = MARK2COL(wp, ap->ar_region.r_orig);
  1621.         } else {
  1622.             n = MARK2COL(wp, ap->ar_region.r_end);
  1623.         }
  1624.         if (rect_end_col < n)
  1625.             rect_end_col = n;
  1626.     }
  1627.     for (lnum = start_lnum; lnum <= end_lnum; lnum++, lp = lforw(lp)) {
  1628.         int row, col;
  1629.         if (ap->ar_shape == RECTANGLE) {
  1630.         start_col = rect_start_col;
  1631.         } else if (lnum == start_rlnum) {
  1632.         start_col = mark2col(wp, ap->ar_region.r_orig);
  1633.         } else {
  1634.         start_col = w_left_margin(wp) + nu_width(wp);
  1635.         }
  1636.  
  1637.         if (start_col < w_left_margin(wp))
  1638.         start_col = (lnum == start_rlnum)
  1639.             ? w_left_margin(wp) + nu_width(wp)
  1640.             : w_left_margin(wp);
  1641.  
  1642.         if (ap->ar_shape == RECTANGLE) {
  1643.         end_col = rect_end_col;
  1644.         } else if (lnum == end_rlnum) {
  1645.         int n = mark2col(wp, ap->ar_region.r_end) - 1;
  1646.         end_col = offs2col(wp, ap->ar_region.r_end.l,
  1647.                    ap->ar_region.r_end.o - 1);
  1648.         if (end_col < n)
  1649.             end_col = n;
  1650.         } else {
  1651.         end_col = offs2col(wp, lp, llength(lp));
  1652. #ifdef WMDLINEWRAP
  1653.         if (w_val(wp,WMDLINEWRAP)
  1654.          && (end_col % term.t_ncol) == 0)
  1655.             end_col--;    /* cannot highlight the newline */
  1656. #endif
  1657.         }
  1658.         row = lmap[lnum - start_wlnum];
  1659. #ifdef WMDLINEWRAP
  1660.         if (w_val(wp,WMDLINEWRAP))
  1661.         {
  1662.         for (col = start_col; col <= end_col; col++) {
  1663.             int x = row + col / term.t_ncol;
  1664.             if  (x < 0)
  1665.             continue;
  1666.             if (x < mode_row(wp)) {
  1667.             int y = col % term.t_ncol;
  1668.             vscreen[x]->v_attrs[y] =
  1669.                 (vscreen[x]->v_attrs[y] | (attr & ~VAREV))
  1670.                 ^ (attr & VAREV);
  1671.             }
  1672.             else
  1673.             break;
  1674.         }
  1675.         }
  1676.         else
  1677. #endif
  1678.         {
  1679.         if (end_col >= term.t_ncol)
  1680.             end_col = term.t_ncol-1;
  1681.         for (col = start_col; col <= end_col; col++)
  1682.             vscreen[row]->v_attrs[col] =
  1683.             (vscreen[row]->v_attrs[col] | (attr & ~VAREV))
  1684.             ^ (attr & VAREV);
  1685.         }
  1686.     }
  1687.     ap = ap->ar_next;
  1688.     }
  1689. }
  1690. #endif /* OPT_VIDEO_ATTRS */
  1691.  
  1692. /*
  1693.  * Translate offset (into a line's text) into the display-column, taking into
  1694.  * account the tabstop, sideways, number- and list-modes.
  1695.  */
  1696. int
  1697. offs2col(
  1698. WINDOW    *wp,
  1699. LINEPTR    lp,
  1700. C_NUM    offset)
  1701. {
  1702.     int    length = llength(lp);
  1703.     int    column = 0;
  1704.     int    tabs = tabstop_val(wp->w_bufp);
  1705.     int    list = w_val(wp,WMDLIST);
  1706.     int    left =
  1707. #ifdef WMDLINEWRAP    /* overrides left/right scrolling */
  1708.             w_val(wp,WMDLINEWRAP) ? 0 :
  1709. #endif
  1710.             w_val(wp,WVAL_SIDEWAYS);
  1711.  
  1712.     register C_NUM    n, c;
  1713.  
  1714.     /* this makes the how-much-to-select calculation easier above */
  1715.     if (offset < 0)
  1716.         return offset;
  1717.  
  1718.     if (lp == win_head(wp)) {
  1719.         column = 0;
  1720.     } else {
  1721.         for (n = w_left_margin(wp); (n < offset) && (n <= length); n++) {
  1722.             c = (n >= length) ? '\n' : lp->l_text[n];
  1723.             if (isPrint(c)) {
  1724.                 column++;
  1725.             } else if (list || (c != '\t')) {
  1726.                 column += (c & HIGHBIT) ? 4 : 2;
  1727.             } else if (c == '\t') {
  1728.                 column = ((column / tabs) + 1) * tabs;
  1729.             }
  1730.         }
  1731.         column = column - left + nu_width(wp) + w_left_margin(wp);
  1732.     }
  1733.     return column;
  1734. }
  1735.  
  1736. /*
  1737.  * Translate a display-column (assuming an infinitely-wide display) into the
  1738.  * line's offset, taking into account the tabstop, sideways, number and list
  1739.  * modes.
  1740.  */
  1741. #if OPT_MOUSE || defined(WMDLINEWRAP)
  1742. int
  1743. col2offs(
  1744. WINDOW    *wp,
  1745. LINEPTR    lp,
  1746. C_NUM    col)
  1747. {
  1748.     int    tabs = tabstop_val(wp->w_bufp);
  1749.     int    list = w_val(wp,WMDLIST);
  1750.     int    left =
  1751. #ifdef WMDLINEWRAP    /* overrides left/right scrolling */
  1752.             w_val(wp,WMDLINEWRAP) ? 0 :
  1753. #endif
  1754.             w_val(wp,WVAL_SIDEWAYS);
  1755.     int    goal = col + left - nu_width(wp) - w_left_margin(wp);
  1756.  
  1757.     register C_NUM    n;
  1758.     register C_NUM    offset;
  1759.     register C_NUM    len    = llength(lp);
  1760.     register char    *text    = lp->l_text;
  1761.  
  1762.     if (lp == win_head(wp)) {
  1763.         offset = 0;
  1764.     } else {
  1765.         for (offset = w_left_margin(wp), n = 0;
  1766.             (offset < len) && (n < goal);
  1767.                 offset++) {
  1768.             register int c = text[offset];
  1769.             if (isPrint(c)) {
  1770.                 n++;
  1771.             } else if (list || (c != '\t')) {
  1772.                 n += (c & HIGHBIT) ? 4 : 2;
  1773.             } else if (c == '\t') {
  1774.                 n = ((n / tabs) + 1) * tabs;
  1775.             }
  1776.             if (n > goal)
  1777.                 break;
  1778.         }
  1779.     }
  1780.     return offset;
  1781. }
  1782. #endif
  1783.  
  1784. /*
  1785.  * Compute the number of rows required for displaying a line.
  1786.  */
  1787. #ifdef WMDLINEWRAP
  1788. int
  1789. line_height(
  1790. WINDOW    *wp,
  1791. LINEPTR    lp)
  1792. {
  1793.     int hi = 1;
  1794.     if (w_val(wp,WMDLINEWRAP)) {
  1795.         int    len = llength(lp);
  1796.         if (len > 0) {
  1797.             int col = offs2col(wp,lp,len) - 1;
  1798.             if (ins_mode(wp) != FALSE
  1799.              && lp == DOT.l
  1800.              && len <= DOT.o) {
  1801.                 col++;
  1802.                 if (w_val(wp,WMDLIST))
  1803.                     col++;
  1804.             } else if (w_val(wp,WMDLIST)) {
  1805.                 col += 2;
  1806.             }
  1807.             hi = (col / term.t_ncol) + 1;
  1808.         }
  1809.     }
  1810.     return hi;
  1811. }
  1812. #endif
  1813.  
  1814. /*
  1815.  * Given a row on the screen, determines which window it belongs to.  Returns
  1816.  * null only for the message line.
  1817.  */
  1818. #if defined(WMDLINEWRAP) || OPT_MOUSE
  1819. WINDOW *
  1820. row2window (int row)
  1821. {
  1822.     register WINDOW *wp;
  1823.  
  1824.     for_each_visible_window(wp)
  1825.         if (row >= wp->w_toprow && row <= mode_row(wp))
  1826.             return wp;
  1827.     return 0;
  1828. }
  1829. #endif
  1830.  
  1831. /*
  1832.  * Highlight the requested portion of the screen.  We're mucking with the video
  1833.  * attributes on the line here, so this is NOT good code - it would be better
  1834.  * if there was an individual colour attribute per character, rather than per
  1835.  * row, but I didn't write the original code.  Anyway, hilite is called only
  1836.  * once so far, so it's not that big a deal.
  1837.  */
  1838. void
  1839. hilite(
  1840. int    row,        /* row to start highlighting */
  1841. int    colfrom,    /* column to start highlighting */
  1842. int    colto,        /* column to end highlighting */
  1843. int    on)        /* start highlighting */
  1844. {
  1845. #if !OPT_VIDEO_ATTRS
  1846.     register VIDEO *vp1 = vscreen[row];
  1847. #endif
  1848. #ifdef WMDLINEWRAP
  1849.     WINDOW    *wp = row2window(row);
  1850.     if (w_val(wp,WMDLINEWRAP)) {
  1851.         if (colfrom < 0)
  1852.             colfrom = 0;
  1853.         if (colfrom > term.t_ncol) {
  1854.             do {
  1855.                 row++;
  1856.                 colfrom -= term.t_ncol;
  1857.                 colto   -= term.t_ncol;
  1858.                 hilite(row, colfrom, colto, on);
  1859.             } while (colto > term.t_ncol);
  1860.             return;
  1861.         }
  1862.     }
  1863. #endif
  1864.     if (row < term.t_nrow-1 && (colfrom >= 0 || colto <= term.t_ncol)) {
  1865.         if (colfrom < 0)
  1866.             colfrom = 0;
  1867.         if (colto > term.t_ncol)
  1868.             colto = term.t_ncol;
  1869. #if OPT_VIDEO_ATTRS
  1870.         if (on) {
  1871.             int col;
  1872.             for (col=colfrom; col<colto; col++)
  1873.             vscreen[row]->v_attrs[col] |= VAREV;
  1874.         }
  1875.         else {
  1876.             int col;
  1877.             for (col=colfrom; col<colto; col++)
  1878.             vscreen[row]->v_attrs[col] &= ~VAREV;
  1879.         }
  1880.         vscreen[row]->v_flag |= VFCHG;
  1881.         updateline(row, 0, term.t_ncol);
  1882. #else /* OPT_VIDEO_ATTRS */
  1883.         if (on) {
  1884.             vp1->v_flag |= VFREQ;
  1885.         } else {
  1886.             vp1->v_flag &= ~VFREQ;
  1887.         }
  1888.         updateline(row, colfrom, colto);
  1889. #endif /* OPT_VIDEO_ATTRS */
  1890.     }
  1891. }
  1892.  
  1893. #if CAN_SCROLL
  1894. /* optimize out scrolls (line breaks, and newlines) */
  1895. /* arg. chooses between looking for inserts or deletes */
  1896. static int
  1897. scrolls(int inserts)    /* returns true if it does something */
  1898. {
  1899.     struct    VIDEO *vpv ;    /* virtual screen image */
  1900.     struct    VIDEO *vpp ;    /* physical screen image */
  1901.     int    i, j, k ;
  1902.     int    rows, cols ;
  1903.     int    first, match, count, ptarget = 0, vtarget = 0;
  1904.     SIZE_T    end;
  1905.     int    longmatch, longcount;
  1906.     int    longinplace, inplace;    /* count of lines which are already
  1907.                        in the right place */
  1908.     int    from, to;
  1909.  
  1910.     if (term.t_scroll == null_t_scroll) /* no way to scroll */
  1911.         return FALSE;
  1912.  
  1913.     rows = term.t_nrow -1;
  1914.     cols = term.t_ncol ;
  1915.  
  1916.     first = -1 ;
  1917.     for (i = 0; i < rows; i++) {    /* find first wrong line */
  1918.         if (!texttest(i,i)) {
  1919.             first = i;
  1920.             break;
  1921.         }
  1922.     }
  1923.  
  1924.     if (first < 0)
  1925.         return FALSE;        /* no text changes */
  1926.  
  1927.     vpv = vscreen[first] ;
  1928.     vpp = PScreen(first) ;
  1929.  
  1930.     if (inserts) {
  1931.         /* determine types of potential scrolls */
  1932.         end = endofline(vpv->v_text,cols) ;
  1933.         if ( end == 0 )
  1934.             ptarget = first ;        /* newlines */
  1935.         else if ( memcmp(vpp->v_text, vpv->v_text, end) == 0 )
  1936.             ptarget = first + 1 ;    /* broken line newlines */
  1937.         else
  1938.             ptarget = first ;
  1939.         from = ptarget;
  1940.     } else {
  1941.         from = vtarget = first + 1 ;
  1942.     }
  1943.  
  1944.     /* find the matching shifted area */
  1945.     longmatch = -1;
  1946.     longcount = 0;
  1947.     longinplace = 0;
  1948.     for (i = from+1; i < rows; i++) {
  1949.         if (inserts ? texttest(i,from) : texttest(from,i) ) {
  1950.             match = i ;
  1951.             count = 1 ;
  1952.             inplace = texttest(match, match) ? 1 : 0;
  1953.             for (j=match+1, k=from+1; j<rows && k<rows; j++, k++) {
  1954.                 if (inserts ? texttest(j,k) : texttest(k,j)) {
  1955.                     count++ ;
  1956.                     if (texttest(j,j))
  1957.                         inplace++;
  1958.                 }
  1959.                 else
  1960.                     break ;
  1961.             }
  1962.             if (longcount - longinplace < count - inplace) {
  1963.                 longcount = count;
  1964.                 longmatch = match;
  1965.                 longinplace = inplace;
  1966.             }
  1967.         }
  1968.     }
  1969.     match = longmatch;
  1970.     count = longcount;
  1971.  
  1972.     if (!inserts) {
  1973.         /* full kill case? */
  1974.         if (match > 0 && texttest(first, match-1)) {
  1975.             vtarget-- ;
  1976.             match-- ;
  1977.             count++ ;
  1978.         }
  1979.     }
  1980.  
  1981.     /* do the scroll */
  1982.     if (match>0 && count>2) {         /* got a scroll */
  1983.         /* move the count lines starting at ptarget to match */
  1984.         /* mlwrite("scrolls: move the %d lines starting at %d to %d",
  1985.                         count,ptarget,match);
  1986.         */
  1987.         if (inserts) {
  1988.             from = ptarget;
  1989.             to = match;
  1990.         } else {
  1991.             from = match;
  1992.             to = vtarget;
  1993.         }
  1994. #if OPT_PSCREEN
  1995.         /*
  1996.          * Update lines _before_ the scroll so that they will
  1997.          * be available for any updates which need to be done
  1998.          * (due to a GraphicsExpose event in X11...these occur
  1999.          * when scrolling a partially obscured window).  Note
  2000.          * that in the typical case of scrolling a line or two
  2001.          * that very few memory accesses are performed.  We
  2002.          * mostly shuffle pointers around.
  2003.          */
  2004. #define SWAP_PLINE(a, b) do { VIDEO *temp = pscreen[a];    \
  2005.                   pscreen[a] = pscreen[b];    \
  2006.                   pscreen[b] = temp; } one_time
  2007. #define CLEAR_PLINE(a)  do {                        \
  2008.                 MARK_LINE_DIRTY(a);                \
  2009.                 for (j = 0; j < term.t_ncol; j++) {        \
  2010.                 CELL_TEXT(a,j) = ' ';            \
  2011.                 CELL_ATTR(a,j) = 0;            \
  2012.                 }                        \
  2013.               } one_time
  2014.         if (from < to) {
  2015.             /* FIXME: color */
  2016.             for (i = from; i < to; i++)
  2017.             CLEAR_PLINE(i+count);
  2018.             for (i = count-1; i >= 0; i--)
  2019.             SWAP_PLINE(from+i, to+i);
  2020.         }
  2021.         else {
  2022.             /* FIXME: color */
  2023.             for (i = to; i < from; i++)
  2024.             CLEAR_PLINE(i);
  2025.             for (i = 0; i < count; i++)
  2026.             SWAP_PLINE(from+i, to+i);
  2027.         }
  2028. #endif /* OPT_PSCREEN */
  2029.         scrscroll(from, to, count) ;
  2030. #if !OPT_PSCREEN
  2031.         for (i = 0; i < count; i++) {
  2032.             vpp = PScreen(to+i) ;
  2033.             vpv = vscreen[to+i];
  2034.             (void)memcpy(vpp->v_text, vpv->v_text, (SIZE_T)cols) ;
  2035.         }
  2036. #if OPT_VIDEO_ATTRS && !MEMMAP
  2037. #define SWAP_ATTR_PTR(a, b) do { VIDEO_ATTR *temp = pscreen[a]->v_attrs;  \
  2038.                      pscreen[a]->v_attrs = pscreen[b]->v_attrs; \
  2039.                      pscreen[b]->v_attrs = temp; } one_time
  2040.         if (from < to) {
  2041.             /* FIXME: color */
  2042.             for (i = from; i < to; i++)
  2043.             for (j = 0; j < term.t_ncol; j++)
  2044.                 CELL_ATTR(i+count,j) = 0;
  2045.             for (i = count-1; i >= 0; i--)
  2046.             SWAP_ATTR_PTR(from+i, to+i);
  2047.  
  2048.         }
  2049.         else {
  2050.             /* FIXME: color */
  2051.             for (i = to; i < from; i++)
  2052.             for (j = 0; j < term.t_ncol; j++)
  2053.                 CELL_ATTR(i,j) = 0;
  2054.             for (i = 0; i < count; i++)
  2055.             SWAP_ATTR_PTR(from+i, to+i);
  2056.         }
  2057. #undef SWAP_ATTR_PTR
  2058. #endif /* OPT_VIDEO_ATTRS */
  2059.         if (inserts) {
  2060.             from = ptarget;
  2061.             to = match;
  2062.         } else {
  2063.             from = vtarget+count;
  2064.             to = match+count;
  2065.         }
  2066.         for (i = from; i < to; i++) {
  2067.             char *txt;
  2068.             txt = PScreen(i)->v_text;
  2069.             for (j = 0; j < term.t_ncol; ++j)
  2070.                 txt[j] = ' ';
  2071.             vscreen[i]->v_flag |= VFCHG;
  2072.         }
  2073. #endif /* !OPT_PSCREEN */
  2074.         return(TRUE) ;
  2075.     }
  2076.     return(FALSE) ;
  2077. }
  2078.  
  2079. /* move the "count" lines starting at "from" to "to" */
  2080. static void
  2081. scrscroll(int from, int to, int count)
  2082. {
  2083.     beginDisplay();
  2084.     ttrow = ttcol = -1;
  2085.     TTscroll(from,to,count);
  2086.     endofDisplay();
  2087. }
  2088.  
  2089. static int
  2090. texttest(        /* return TRUE on text match */
  2091. int    vrow,        /* virtual row */
  2092. int    prow)        /* physical row */
  2093. {
  2094.     struct    VIDEO *vpv = vscreen[vrow] ;    /* virtual screen image */
  2095.     struct    VIDEO *vpp = PScreen(prow)  ;    /* physical screen image */
  2096.  
  2097.     return (!memcmp(vpv->v_text, vpp->v_text, (SIZE_T)term.t_ncol)) ;
  2098. }
  2099.  
  2100. /* return the index of the first blank of trailing whitespace */
  2101. static int
  2102. endofline(char *s, int n)
  2103. {
  2104.     int    i;
  2105.     for (i = n - 1; i >= 0; i--)
  2106.         if (s[i] != ' ') return(i+1) ;
  2107.     return(0) ;
  2108. }
  2109.  
  2110. #endif /* CAN_SCROLL */
  2111.  
  2112.  
  2113. /* Update the extended line which the cursor is currently on at a column
  2114.  * greater than the terminal width.  The line will be scrolled right or left to
  2115.  * let the user see where the cursor is.
  2116.  */
  2117. static int
  2118. updext_past(int col, int excess)
  2119. {
  2120.     register int rcursor;
  2121.     register int zero = nu_width(curwp);
  2122.  
  2123.     /* calculate what column the real cursor will end up in */
  2124.     rcursor = ((excess - 1) % term.t_scrsiz) + term.t_margin;
  2125.     taboff = col - rcursor;
  2126.  
  2127.     /* Scan through the line outputting characters to the virtual screen
  2128.      * once we reach the left edge.  */
  2129.  
  2130.     /* start scanning offscreen */
  2131.     vtmove(currow, -taboff);
  2132.     vtset(DOT.l, curwp);
  2133.  
  2134.     /* truncate the virtual line, restore tab offset */
  2135.     vteeol();
  2136.     taboff = 0;
  2137.  
  2138.     /* and put a marker in column 1 */
  2139.     vscreen[currow]->v_text[zero] = MRK_EXTEND_LEFT;
  2140.     vscreen[currow]->v_flag |= (VFEXT | VFCHG);
  2141.     return rcursor;
  2142. }
  2143.  
  2144. /* Update the extended line which the cursor is currently on at a column less
  2145.  * than the terminal width.  The line will be scrolled right or left to let the
  2146.  * user see where the cursor is.
  2147.  */
  2148. static int
  2149. updext_before(int col)
  2150. {
  2151.     register int rcursor;
  2152.  
  2153.     curcol = col;
  2154.  
  2155.     /* calculate what column the real cursor will end up in */
  2156.     rcursor = (col % (term.t_ncol - term.t_margin));
  2157.     taboff = col - rcursor;
  2158.  
  2159.     /* Scan through the line outputting characters to the virtual screen
  2160.      * once we reach the left edge.  */
  2161.     vtmove(currow, -taboff);    /* start scanning offscreen */
  2162.     vtset(DOT.l, curwp);
  2163.  
  2164.     /* truncate the virtual line, restore tab offset */
  2165.     vteeol();
  2166.     taboff = 0;
  2167.  
  2168.     if (col != rcursor) { /* ... put a marker in column 1 */
  2169.         vscreen[currow]->v_text[nu_width(curwp)] = MRK_EXTEND_LEFT;
  2170.         vscreen[currow]->v_flag |= VFEXT;
  2171.     }
  2172.     vscreen[currow]->v_flag |= (VFEXT|VFCHG);
  2173.     return rcursor;
  2174. }
  2175.  
  2176.  
  2177.  
  2178. /*
  2179.  * Update a single line. This does not know how to use insert or delete
  2180.  * character sequences; we are using VT52 functionality. Update the physical
  2181.  * row and column variables. It does try an exploit erase to end of line.
  2182.  */
  2183. #if    MEMMAP
  2184. /*    UPDATELINE specific code for the IBM-PC and other compatibles */
  2185.  
  2186. static void
  2187. updateline(
  2188.  
  2189. int    row,        /* row of screen to update */
  2190. int    colfrom,    /* first column on screen */
  2191. int    colto)        /* last column on screen */
  2192.  
  2193. {
  2194.     register struct VIDEO *vp1 = vscreen[row];    /* virtual screen image */
  2195.     register int    req = (vp1->v_flag & VFREQ) == VFREQ;
  2196.  
  2197. #if    OPT_COLOR
  2198.     CurFcolor(vp1) = ReqFcolor(vp1);
  2199.     CurBcolor(vp1) = ReqBcolor(vp1);
  2200. #endif
  2201. #if OPT_VIDEO_ATTRS
  2202.     scwrite(row, colfrom, colto - colfrom,
  2203.         VideoText(vp1),
  2204.         VideoAttr(vp1),
  2205.         ReqFcolor(vp1),
  2206.         ReqBcolor(vp1));
  2207. #else    /* highlighting, anyway */
  2208.     scwrite(row, colfrom, colto - colfrom,
  2209.         VideoText(vp1),
  2210.         (VIDEO_ATTR *)0,
  2211.         req ? ReqBcolor(vp1) : ReqFcolor(vp1),
  2212.         req ? ReqFcolor(vp1) : ReqBcolor(vp1));
  2213. #endif
  2214.     vp1->v_flag &= ~(VFCHG | VFCOL); /* flag this line as updated */
  2215.     if (req)
  2216.         vp1->v_flag |= VFREV;
  2217.     else
  2218.         vp1->v_flag &= ~VFREV;
  2219. }
  2220.  
  2221. #else    /* !MEMMAP */
  2222. #if    OPT_PSCREEN
  2223. static void
  2224. updateline(
  2225.     int    row,        /* row of screen to update */
  2226.     int    colfrom,    /* col to start updating from */
  2227.     int    colto)        /* col to go to */
  2228. {
  2229.     register char *vc, *pc, *evc;
  2230.     register VIDEO_ATTR *va, *pa;
  2231.     int nchanges = 0;
  2232.  
  2233.     if ((vscreen[row]->v_flag & VFCHG) == 0)
  2234.     return;
  2235.  
  2236.     vc  = &vscreen[row]->v_text[colfrom];
  2237.     evc = &vscreen[row]->v_text[colto];
  2238.     va  = &vscreen[row]->v_attrs[colfrom];
  2239.     pc  = &CELL_TEXT(row,colfrom);
  2240.     pa  = &CELL_ATTR(row,colfrom);
  2241.  
  2242.     while (vc < evc) {
  2243.     if (*vc != *pc || VATTRIB(*va) != VATTRIB(*pa)) {
  2244.         *pc = *vc;
  2245.         *pa = *va | VADIRTY;
  2246.         nchanges++;
  2247.     }
  2248.     vc++;
  2249.     pc++;
  2250.     va++;
  2251.     pa++;
  2252.     }
  2253.  
  2254.     if (nchanges > 0)
  2255.     MARK_LINE_DIRTY(row);
  2256.     vscreen[row]->v_flag &= ~(VFCHG | VFCOL); /* mark virtual line updated */
  2257. }
  2258.  
  2259. #else  /* !OPT_PSCREEN */
  2260.  
  2261. /*    UPDATELINE code for all other versions        */
  2262.  
  2263. #define TTattr(a) TTrev(a) /* FIXME */
  2264.  
  2265. static void
  2266. updateline(
  2267.  
  2268. int    row,        /* row of screen to update */
  2269. int    colfrom,    /* first column on screen */
  2270. int    colto)        /* first column on screen */
  2271.  
  2272. {
  2273.     struct VIDEO *vp1 = vscreen[row];    /* virtual screen image */
  2274.     struct VIDEO *vp2 = PSCREEN[row];    /* physical screen image */
  2275.     register int xl = colfrom;
  2276.     register int xr = colto;
  2277.     register int xx;
  2278.  
  2279.     register char *cp1 = VideoText(vp1);
  2280.     register char *cp2 = VideoText(vp2);
  2281.     register int nbflag;    /* non-blanks to the right flag? */
  2282.  
  2283. #if OPT_VIDEO_ATTRS
  2284.     register VIDEO_ATTR *ap1 = VideoAttr(vp1);
  2285.     register VIDEO_ATTR *ap2 = VideoAttr(vp2);
  2286.     VIDEO_ATTR Blank = 0;    /* FIXME: Color? */
  2287. #else
  2288.     UINT rev;        /* reverse video flag */
  2289.     UINT req;        /* reverse video request flag */
  2290. #endif
  2291.  
  2292. #if !OPT_VIDEO_ATTRS
  2293. #if    OPT_COLOR
  2294.     TTforg(ReqFcolor(vp1));
  2295.     TTbacg(ReqBcolor(vp1));
  2296. #endif
  2297.  
  2298. #if    OPT_REVSTA || OPT_COLOR
  2299.     /* if we need to change the reverse video status of the
  2300.        current line, we need to re-write the entire line     */
  2301.     rev = (vp1->v_flag & VFREV) == VFREV;
  2302.     req = (vp1->v_flag & VFREQ) == VFREQ;
  2303.     if ((rev != req)
  2304. #if    OPT_COLOR
  2305.         || (CurFcolor(vp1) != ReqFcolor(vp1))
  2306.         || (CurBcolor(vp1) != ReqBcolor(vp1))
  2307. #endif
  2308.             ) {
  2309.         movecursor(row, colfrom);    /* Go to start of line. */
  2310.         /* set rev video if needed */
  2311.         if (req)
  2312.             TTrev(req);
  2313.  
  2314.         /* scan through the line and dump it to the screen and
  2315.            the virtual screen array                */
  2316.         for (; xl < colto; xl++) {
  2317.             TTputc(cp1[xl]);
  2318.             ++ttcol;
  2319.             cp2[xl] = cp1[xl];
  2320.         }
  2321.         /* turn rev video off */
  2322.         if (req)
  2323.             TTrev(FALSE);
  2324.  
  2325.         /* update the needed flags */
  2326.         vp1->v_flag &= ~(VFCHG|VFCOL);
  2327.         if (req)
  2328.             vp1->v_flag |= VFREV;
  2329.         else
  2330.             vp1->v_flag &= ~VFREV;
  2331. #if    OPT_COLOR
  2332.         CurFcolor(vp1) = ReqFcolor(vp1);
  2333.         CurBcolor(vp1) = ReqBcolor(vp1);
  2334. #endif
  2335.         return;
  2336.     }
  2337. #else
  2338.     rev = FALSE;
  2339. #endif    /* OPT_REVSTA || OPT_COLOR */
  2340. #endif    /* !OPT_VIDEO_ATTRS */
  2341.  
  2342.     /* advance past any common chars at the left */
  2343. #if !OPT_VIDEO_ATTRS
  2344.     if (!rev)
  2345. #endif    /* !OPT_VIDEO_ATTRS */
  2346.         while (xl != colto
  2347.             && cp1[xl] == cp2[xl]
  2348. #if OPT_VIDEO_ATTRS
  2349.             && VATTRIB(ap1[xl]) == VATTRIB(ap2[xl])
  2350. #endif    /* OPT_VIDEO_ATTRS */
  2351.               ) {
  2352.             ++xl;
  2353.         }
  2354.  
  2355. /* This can still happen, even though we only call this routine on changed
  2356.  * lines. A hard update is always done when a line splits, a massive
  2357.  * change is done, or a buffer is displayed twice. This optimizes out most
  2358.  * of the excess updating. A lot of computes are used, but these tend to
  2359.  * be hard operations that do a lot of update, so I don't really care.
  2360.  */
  2361.     /* if both lines are the same, no update needs to be done */
  2362.     if (xl == colto) {
  2363.         vp1->v_flag &= ~VFCHG;    /* flag this line unchanged */
  2364.         return;
  2365.     }
  2366.  
  2367.     /* find out if there is a match on the right */
  2368.     nbflag = FALSE;
  2369.  
  2370. #if !OPT_VIDEO_ATTRS
  2371.     if (!rev)
  2372. #endif
  2373.         while (cp1[xr-1] == cp2[xr-1]
  2374. #if OPT_VIDEO_ATTRS
  2375.             && VATTRIB(ap1[xr-1]) == VATTRIB(ap2[xr-1])
  2376. #endif
  2377.          ) {
  2378.             --xr;
  2379.             /* Note if any nonblank in right match */
  2380.             if (cp1[xr] != ' '
  2381. #if OPT_VIDEO_ATTRS
  2382.              || VATTRIB(ap1[xr] != Blank)
  2383. #endif
  2384.               )
  2385.                 nbflag = TRUE;
  2386.         }
  2387.  
  2388.     xx = xr;
  2389.  
  2390.     /* Erase to EOL ? */
  2391.     if (nbflag == FALSE
  2392.      && eolexist == TRUE
  2393. #if    OPT_REVSTA && !OPT_VIDEO_ATTRS
  2394.      && (req != TRUE)
  2395. #endif
  2396.        ) {
  2397.         while ((xx != xl)
  2398.             && cp1[xx-1] == ' '
  2399. #if OPT_VIDEO_ATTRS
  2400.             && VATTRIB(ap1[xx-1]) == Blank
  2401. #endif
  2402.         )
  2403.             xx--;
  2404.  
  2405.         if ((xr - xx) <= 3)        /* Use only if erase is */
  2406.             xx = xr;        /* fewer characters. */
  2407.     }
  2408.  
  2409.     movecursor(row, xl - colfrom);        /* Go to start of line. */
  2410. #if OPT_VIDEO_ATTRS
  2411.     while (xl < xx) {
  2412.         register int j = xl;
  2413.         VIDEO_ATTR attr = VATTRIB(ap1[j]);
  2414.         while ((j < xx) && (attr == VATTRIB(ap1[j])))
  2415.             j++;
  2416.         TTattr(attr);
  2417.         for (; xl < j; xl++) {
  2418.             TTputc(cp1[xl]);
  2419.             ++ttcol;
  2420.             cp2[xl] = cp1[xl];
  2421.             ap2[xl] = ap1[xl];
  2422.         }
  2423.     }
  2424.     TTattr(0);
  2425.  
  2426.     if (xx != xr) {                /* Erase. */
  2427.         TTeeol();
  2428.         for (; xl < xr; xl++) {
  2429.             if (cp2[xl] != cp1[xl]
  2430.              || VATTRIB(ap2[xl]) != VATTRIB(ap1[xl]))
  2431.                 ap2[xl] = ap1[xl];
  2432.             cp2[xl] = cp1[xl];
  2433.         }
  2434.     }
  2435. #else /* OPT_VIDEO_ATTRS */
  2436. #if    OPT_REVSTA
  2437.     TTrev(rev);
  2438. #endif
  2439.  
  2440.     for (; xl < xr; xl++) {        /* Ordinary. */
  2441.         TTputc(cp1[xl]);
  2442.         ++ttcol;
  2443.         cp2[xl] = cp1[xl];
  2444.     }
  2445.  
  2446.     if (xx != xr) {        /* Erase. */
  2447.         TTeeol();
  2448.         for (; xl < xr; xl++) {
  2449.             cp2[xl] = cp1[xl];
  2450.         }
  2451.     }
  2452. #if    OPT_REVSTA
  2453.     TTrev(FALSE);
  2454. #endif
  2455. #endif /* OPT_VIDEO_ATTRS */
  2456.     vp1->v_flag &= ~(VFCHG|VFCOL);    /* flag this line as updated */
  2457.     return;
  2458. }
  2459. #endif  /* OPT_PSCREEN(updateline) */
  2460. #endif    /* MEMMAP(updateline) */
  2461.  
  2462. /*
  2463.  * Redisplay the mode line for the window pointed to by the "wp".
  2464.  * modeline() is the only routine that has any idea of how the modeline is
  2465.  * formatted.  You can change the modeline format by hacking at this
  2466.  * routine.  Called by "update" any time there is a dirty window.
  2467.  */
  2468. #if OPT_MLFORMAT
  2469. static void
  2470. mlfs_prefix(
  2471.     char **fsp,
  2472.     char **msp,
  2473.     int lchar)
  2474. {
  2475.     register char *fs = *fsp;
  2476.     register char *ms = *msp;
  2477.     if (*fs == ':') {
  2478.     fs++;
  2479.     while (*fs && *fs != ':') {
  2480.         if (*fs != '%')
  2481.         *ms++ = *fs++;
  2482.         else {
  2483.         fs++;
  2484.         switch(*fs++) {
  2485.             case EOS :
  2486.             fs--;
  2487.             break;
  2488.             case '%' :
  2489.             *ms++ = '%';
  2490.             break;
  2491.             case ':' :
  2492.             *ms++ = ':';
  2493.             break;
  2494.             case '-' :
  2495.             *ms++ = (char)lchar;
  2496.             break;
  2497.             default :
  2498.             *ms++ = '%';
  2499.             *ms++ = *(fs-1);
  2500.             break;
  2501.         }
  2502.         }
  2503.     }
  2504.     }
  2505.     *fsp = fs;
  2506.     *msp = ms;
  2507. }
  2508.  
  2509. static void
  2510. mlfs_suffix(
  2511.     char **fsp,
  2512.     char **msp,
  2513.     int lchar)
  2514. {
  2515.     mlfs_prefix(fsp, msp, lchar);
  2516.     if (**fsp == ':')
  2517.     (*fsp)++;
  2518. }
  2519.  
  2520. static void
  2521. mlfs_skipfix(char **fsp)
  2522. {
  2523.     register char *fs = *fsp;
  2524.     if (*fs == ':') {
  2525.     for (fs++;*fs && *fs != ':'; fs++);
  2526.     if (*fs == ':')
  2527.         fs++;
  2528.     for (;*fs && *fs != ':'; fs++);
  2529.     if (*fs == ':')
  2530.         fs++;
  2531.     }
  2532.     *fsp = fs;
  2533. }
  2534. #endif /* OPT_MLFORMAT */
  2535.  
  2536. #define PutModename(format, name) { \
  2537.         if (ms != 0) { \
  2538.             ms = lsprintf(ms, format, \
  2539.                 mcnt ? ' ' : '[', \
  2540.                 name); \
  2541.         } \
  2542.         mcnt++; \
  2543.     }
  2544.  
  2545. #define PutMode(mode,name) \
  2546.     if (b_val(bp, mode)) PutModename("%c%s", name)
  2547.  
  2548. #if OPT_MAJORMODE
  2549. #define PutMajormode(bp) if (bp->majr != 0) PutModename("%c%smode", bp->majr->name)
  2550. #else
  2551. #define PutMajormode(bp) /*nothing*/
  2552. #endif
  2553.  
  2554. static int
  2555. modeline_modes(
  2556. BUFFER *bp,
  2557. char    **msptr)
  2558. {
  2559.     register char *ms = msptr ? *msptr : 0;
  2560.     register SIZE_T mcnt = 0;
  2561.  
  2562. #if CC_CANNOT_OFFSET_CASES
  2563.     PutMajormode(bp)
  2564. #if !OPT_MAJORMODE
  2565.     PutMode(MDCMOD,        "cmode")
  2566. #endif
  2567. #if OPT_ENCRYPT
  2568.     PutMode(MDCRYPT,    "crypt")
  2569. #endif
  2570.     PutMode(MDDOS,        "dos-style")
  2571.     PutMode(MDREADONLY,    "read-only")
  2572.     PutMode(MDVIEW,        "view-only")
  2573. #if OPT_LCKFILES
  2574.     PutMode(MDLOCKED,    "locked by")
  2575. #endif
  2576. #else
  2577.     static    const    struct {
  2578.         int   mode;
  2579.         const char *name;
  2580.     } table[] = {
  2581. #if !OPT_MAJORMODE
  2582.         {MDCMOD,    "cmode"},
  2583. #endif
  2584. #if OPT_ENCRYPT
  2585.         {MDCRYPT,   "crypt"},
  2586. #endif
  2587.         {MDDOS,     "dos-style"},
  2588.         {MDREADONLY,"read-only"},
  2589.         {MDVIEW,    "view-only"},
  2590. #if OPT_LCKFILES
  2591.         {MDLOCKED,  "locked by"},  /* keep this last */
  2592. #endif
  2593.     };
  2594.     register SIZE_T j;
  2595.  
  2596.     PutMajormode(bp)
  2597.     for (j = 0; j < TABLESIZE(table); j++) {
  2598.         PutMode(table[j].mode, table[j].name)
  2599.     }
  2600. #endif
  2601. #if OPT_LCKFILES
  2602.     if (ms != 0 && b_val(bp, MDLOCKED))
  2603.         ms = lsprintf(ms, " %s", b_val_ptr(bp,VAL_LOCKER));
  2604. #endif
  2605.     if (mcnt && ms)
  2606.         *ms++ = ']';
  2607.     if (b_is_changed(bp)) {
  2608.         if (ms != 0)
  2609.             ms = lsprintf(ms, "%s[modified]", mcnt ? " " : "");
  2610.         mcnt++;
  2611.     }
  2612.     if (ms != 0)
  2613.         *msptr = ms;
  2614.     return (mcnt != 0);
  2615. }
  2616.  
  2617. static int
  2618. modeline_show(
  2619. WINDOW *wp,
  2620. int lchar)
  2621. {
  2622.     register int ic = lchar;
  2623.     register BUFFER *bp = wp->w_bufp;
  2624.  
  2625.     if (b_val(bp, MDSHOWMODE)) {
  2626. #ifdef insertmode    /* insert mode is a trait for each window */
  2627.         if (wp->w_traits.insmode == INSERT)
  2628.             ic = 'I';
  2629.         else if (wp->w_traits.insmode == REPLACECHAR)
  2630.             ic = 'R';
  2631.         else if (wp->w_traits.insmode == OVERWRITE)
  2632.             ic = 'O';
  2633. #else             /* insertmode is a variable global to all windows */
  2634.         if (wp == curwp) {
  2635.             if (insertmode == INSERT)
  2636.                 ic = 'I';
  2637.             else if (insertmode == REPLACECHAR)
  2638.                 ic = 'R';
  2639.             else if (insertmode == OVERWRITE)
  2640.                 ic = 'O';
  2641.         }
  2642. #endif /* !defined(insertmode) */
  2643. #if OPT_ICURSOR
  2644.         TTicursor(ic != lchar);
  2645. #endif
  2646.     }
  2647.     return ic;
  2648. }
  2649.  
  2650. static const char *
  2651. rough_position(WINDOW *wp)
  2652. {
  2653.     LINE *lp = wp->w_line.l;
  2654.     int rows = wp->w_ntrows;
  2655.     const char *msg = 0;
  2656.  
  2657.     while (rows-- > 0) {
  2658.         lp = lforw(lp);
  2659.         if (lp == win_head(wp)) {
  2660.             msg = "bot";
  2661.             break;
  2662.         }
  2663.     }
  2664.     if (lback(wp->w_line.l) == win_head(wp)) {
  2665.         if (msg) {
  2666.             if (wp->w_line.l == win_head(wp))
  2667.                 msg = "emp";
  2668.             else
  2669.                 msg = "all";
  2670.         } else {
  2671.             msg = "top";
  2672.         }
  2673.     }
  2674.     if (!msg)
  2675.         msg = "mid";
  2676.     return msg;
  2677. }
  2678.  
  2679. static void
  2680. modeline(WINDOW *wp)
  2681. {
  2682. #if OPT_MLFORMAT
  2683.     char *fs = modeline_format;
  2684.     int fc;
  2685. #endif
  2686.     char temp[NFILEN];
  2687.     char left_ms[NFILEN*2];
  2688.     char right_ms[NFILEN*2];
  2689.     char *ms;
  2690.     register int n;
  2691.     int lchar;
  2692.     int col;
  2693.     int right_len;
  2694.     int need_eighty_column_indicator = FALSE;
  2695.     register BUFFER *bp;
  2696.  
  2697.     left_ms[0] = right_ms[0] = EOS;
  2698.     ms = left_ms;
  2699.  
  2700.     n = mode_row(wp);          /* Location. */
  2701. #if OPT_VIDEO_ATTRS
  2702.     {
  2703.     VIDEO_ATTR attr;
  2704.     if (wp == curwp)
  2705.         attr = VAMLFOC;
  2706.     else
  2707.         attr = VAML;
  2708. #if    OPT_REVSTA
  2709.     attr |= VAREV;
  2710. #endif
  2711.     vscreen[n]->v_flag |= VFCHG;
  2712.     set_vattrs(n, 0, attr, term.t_ncol);
  2713.     }
  2714. #else
  2715.     vscreen[n]->v_flag |= VFCHG | VFREQ | VFCOL;/* Redraw next time. */
  2716. #endif
  2717. #if    OPT_COLOR
  2718.     ReqFcolor(vscreen[n]) = gfcolor;
  2719.     ReqBcolor(vscreen[n]) = gbcolor;
  2720. #endif
  2721.     bp = wp->w_bufp;
  2722.     vtmove(n, 0);                           /* Seek to right line. */
  2723.     if (wp == curwp) {                /* mark the current buffer */
  2724.     lchar = '=';
  2725.     } else {
  2726. #if    OPT_REVSTA
  2727.     if (revexist)
  2728.         lchar = ' ';
  2729.     else
  2730. #endif
  2731.         lchar = '-';
  2732.     }
  2733.  
  2734. #if OPT_MLFORMAT
  2735.     while (*fs) {
  2736.     if (*fs != '%')
  2737.         *ms++ = *fs++;
  2738.     else {
  2739.         fs++;
  2740.         switch ((fc = *fs++)) {
  2741.         case EOS :            /* Null character ! */
  2742.             fs--;
  2743.             break;
  2744.         case '%' :
  2745.         case ':' :
  2746.             *ms++ = *(fs-1);
  2747.             break;
  2748.         case '|' :
  2749.             need_eighty_column_indicator = TRUE;
  2750.             break;
  2751.         case '-' :
  2752.             *ms++ = (char)lchar;
  2753.             break;
  2754.         case '=' :
  2755.             *ms = EOS;
  2756.             ms = right_ms;
  2757.             break;
  2758.         case 'i' :            /* insert mode indicator */
  2759.             *ms++ = (char)modeline_show(wp, lchar);
  2760.             break;
  2761.         case 'b' :
  2762.             ms = lsprintf(ms, "%s", bp->b_bname);
  2763.             break;
  2764.         case 'm' :
  2765.             if (modeline_modes(bp, (char **)0)) {
  2766.             mlfs_prefix(&fs, &ms, lchar);
  2767.             (void)modeline_modes(bp, &ms);
  2768.             mlfs_suffix(&fs, &ms, lchar);
  2769.             }
  2770.             else
  2771.             mlfs_skipfix(&fs);
  2772.             break;
  2773.         case 'f' :
  2774.         case 'F' : {
  2775.             char *p = 0;
  2776.             if (bp->b_fname != 0
  2777.              && (p = shorten_path(strcpy(temp,bp->b_fname), FALSE)) != 0
  2778.              && *p
  2779.              && !eql_bname(bp,p)
  2780.              && (fc == 'f' ? !is_internalname(p)
  2781.                        : is_internalname(p))) {
  2782.             mlfs_prefix(&fs, &ms, lchar);
  2783.             for (; *p == ' '; p++);
  2784.             ms = lsprintf(ms, "%s", p);
  2785.             mlfs_suffix(&fs, &ms, lchar);
  2786.             }
  2787.             else
  2788.             mlfs_skipfix(&fs);
  2789.             break;
  2790.         }
  2791. #ifdef WMDRULER
  2792.         case 'l' :        /* line number */
  2793.         case 'c' :        /* column number */
  2794.         case 'p' :        /* percentage */
  2795.         case 'L' :        /* number of lines in buffer */
  2796.  
  2797.             if (w_val(wp,WMDRULER) && !is_empty_buf(wp->w_bufp)) {
  2798.             int val = 0;
  2799.             switch (fc) {
  2800.                 case 'l' : val = wp->w_ruler_line; break;
  2801.                 case 'L' : val = line_count(wp->w_bufp); break;
  2802.                 case 'c' : val = wp->w_ruler_col; break;
  2803.                 case 'p' : val = wp->w_ruler_line*100
  2804.                                  / line_count(wp->w_bufp); break;
  2805.             }
  2806.             mlfs_prefix(&fs, &ms, lchar);
  2807.             ms = lsprintf(ms, "%d", val);
  2808.             mlfs_suffix(&fs, &ms, lchar);
  2809.             }
  2810.             else
  2811.             mlfs_skipfix(&fs);
  2812.             break;
  2813.  
  2814. #endif
  2815.         case 'S' :
  2816.             if (
  2817. #ifdef WMDRULER
  2818.             !w_val(wp, WMDRULER) ||
  2819. #endif
  2820.                 is_empty_buf(wp->w_bufp)) {
  2821.             mlfs_prefix(&fs, &ms, lchar);
  2822.             ms = lsprintf(ms, " %s ", rough_position(wp));
  2823.             mlfs_suffix(&fs, &ms, lchar);
  2824.             }
  2825.             else
  2826.             mlfs_skipfix(&fs);
  2827.             break;
  2828.         default :
  2829.             *ms++ = '%';
  2830.             *ms++ = *(fs-1);
  2831.             break;
  2832.         }
  2833.     }
  2834.     }
  2835. #else    /* hard-coded format */
  2836.     ms = lsprintf(ms, "%c%c%c %s ",
  2837.         lchar, modeline_show(wp, lchar), lchar, bp->b_bname);
  2838.     if (modeline_modes(bp, &ms))
  2839.         *ms++ = ' ';
  2840.     if (bp->b_fname != 0
  2841.     && (shorten_path(strcpy(temp,bp->b_fname), FALSE))
  2842.     && !eql_bname(bp,temp)) {
  2843.     if (is_internalname(temp)) {
  2844.         for (n = term.t_ncol - (13 + strlen(temp) + (int)(ms - left_ms));
  2845.             n > 0; n--)
  2846.         *ms++ = lchar;
  2847.     } else {
  2848.         ms = lsprintf(ms, "is");
  2849.     }
  2850.     ms = lsprintf(ms, " %s ", temp);
  2851.     }
  2852. #ifdef WMDRULER
  2853.     if (w_val(wp, WMDRULER))
  2854.     (void)lsprintf(right_ms, " (%d,%d) %3p",
  2855.         wp->w_ruler_line, wp->w_ruler_col, lchar);
  2856.     else
  2857. #endif
  2858.      (void) lsprintf(right_ms, " %s %3p", rough_position(wp), lchar);
  2859. #endif /* OPT_MLFORMAT */
  2860.  
  2861.     *ms++ = EOS;
  2862.     right_len = strlen(right_ms);
  2863.     vtputsn(left_ms, term.t_ncol);
  2864.     for (n = term.t_ncol - strlen(left_ms) - right_len; n > 0; n--)
  2865.     vtputc(lchar);
  2866.     vtcol = term.t_ncol - right_len;
  2867.     if (vtcol < 0) {
  2868.     n = -vtcol;
  2869.     vtcol = 0;
  2870.     }
  2871.     else
  2872.     n = 0;
  2873.     vtputsn(right_ms+n, term.t_ncol - vtcol);
  2874.     if (need_eighty_column_indicator) {        /* mark column 80 */
  2875.     int left = -nu_width(wp);
  2876. #ifdef WMDLINEWRAP
  2877.     if (!w_val(wp,WMDLINEWRAP))
  2878. #endif
  2879.      left += w_val(wp,WVAL_SIDEWAYS);
  2880.     n = term.t_ncol + left;
  2881.     col = 80 - left;
  2882.  
  2883.     if ((n > 80) && (col >= 0) && (vtgetc(col) == lchar)) {
  2884.         vtcol = col;
  2885.         vtputc('|');
  2886.     }
  2887.     }
  2888. }
  2889.  
  2890. void
  2891. upmode(void)    /* update all the mode lines */
  2892. {
  2893.     register WINDOW *wp;
  2894.  
  2895.     for_each_window(wp)
  2896.         wp->w_flag |= WFMODE;
  2897. }
  2898.  
  2899. /*
  2900.  * Recompute the given buffer. Save/restore its modes and position information
  2901.  * so that a redisplay will show as little change as possible.
  2902.  */
  2903. #if    OPT_UPBUFF
  2904. typedef    struct    {
  2905.     WINDOW    *wp;
  2906.     struct VAL w_vals[MAX_W_VALUES];
  2907.     int    top;
  2908.     int    line;
  2909.     int    col;
  2910.     } SAVEWIN;
  2911.  
  2912. static    SAVEWIN    *recomp_tbl;
  2913. static    ALLOC_T    recomp_len;
  2914.  
  2915. static void
  2916. recompute_buffer(BUFFER *bp)
  2917. {
  2918.     register WINDOW *wp;
  2919.     register SAVEWIN *tbl;
  2920.  
  2921.     struct VAL b_vals[MAX_B_VALUES];
  2922.     ALLOC_T    num = 0;
  2923.     BUFFER *savebp = curbp;
  2924.     WINDOW *savewp = curwp;
  2925.     int    mygoal = curgoal;
  2926.  
  2927.     if (!b_val(bp,MDUPBUFF)) {
  2928.         b_clr_obsolete(bp);
  2929.         return;
  2930.     }
  2931.     if (recomp_len < bp->b_nwnd) {
  2932.         recomp_len = bp->b_nwnd + 1;
  2933.         recomp_tbl = (recomp_tbl != 0)
  2934.             ? typereallocn(SAVEWIN,recomp_tbl,recomp_len)
  2935.             : typeallocn(SAVEWIN,recomp_len);
  2936.         if (recomp_tbl == 0) {
  2937.             recomp_len = 0;
  2938.             return;
  2939.         }
  2940.     }
  2941.     tbl = recomp_tbl;
  2942.  
  2943.     /* remember where we are, to reposition */
  2944.     /* ...in case line is deleted from buffer-list */
  2945.     relisting_b_vals = 0;
  2946.     relisting_w_vals = 0;
  2947.     if (curbp == bp) {
  2948.         relisting_b_vals = b_vals;
  2949.      } else {
  2950.         curbp = bp;
  2951.         curwp = bp2any_wp(bp);
  2952.     }
  2953.     for_each_visible_window(wp) {
  2954.         if (wp->w_bufp == bp) {
  2955.             if (wp == savewp)
  2956.                 relisting_w_vals = tbl[num].w_vals;
  2957.             curwp = wp;    /* to make 'getccol()' work */
  2958.             curbp = curwp->w_bufp;
  2959.             tbl[num].wp   = wp;
  2960.             tbl[num].top  = line_no(bp, wp->w_line.l);
  2961.             tbl[num].line = line_no(bp, wp->w_dot.l);
  2962.             tbl[num].col  = getccol(FALSE);
  2963.             save_vals(NUM_W_VALUES, global_w_values.wv,
  2964.                 tbl[num].w_vals, wp->w_values.wv);
  2965.             if (++num >= recomp_len)
  2966.                 break;
  2967.         }
  2968.     }
  2969.     curwp = savewp;
  2970.     curbp = savebp;
  2971.  
  2972.     save_vals(NUM_B_VALUES, global_b_values.bv, b_vals, bp->b_values.bv);
  2973.     (bp->b_upbuff)(bp);
  2974.     copy_mvals(NUM_B_VALUES, bp->b_values.bv, b_vals);
  2975.  
  2976.     /* reposition and restore */
  2977.     while (num-- != 0) {
  2978.         curwp = wp = tbl[num].wp;
  2979.         curbp = curwp->w_bufp;
  2980.         (void)gotoline(TRUE, tbl[num].top);
  2981.         wp->w_line.l = wp->w_dot.l;
  2982.         wp->w_line.o = 0;
  2983.         if (tbl[num].line != tbl[num].top)
  2984.             (void)gotoline(TRUE, tbl[num].line);
  2985.         (void)gocol(tbl[num].col);
  2986.             wp->w_flag |= WFMOVE;
  2987.         copy_mvals(NUM_W_VALUES, wp->w_values.wv, tbl[num].w_vals);
  2988.     }
  2989.     curwp = savewp;
  2990.     curbp = savebp;
  2991.     curgoal = mygoal;
  2992.     b_clr_obsolete(bp);
  2993.     relisting_b_vals = 0;
  2994.     relisting_w_vals = 0;
  2995. }
  2996. #endif    /* OPT_UPBUFF */
  2997.  
  2998. /*
  2999.  * Send a command to the terminal to move the hardware cursor to row "row"
  3000.  * and column "col". The row and column arguments are origin 0. Optimize out
  3001.  * random calls. Update "ttrow" and "ttcol".
  3002.  */
  3003. void
  3004. movecursor(int row, int col)
  3005. {
  3006.     beginDisplay();
  3007.     if ((row!=ttrow || col!=ttcol)
  3008.      && (row >= 0 && row < term.t_nrow)
  3009.      && (col >= 0 && col < term.t_ncol))
  3010.         {
  3011.             ttrow = row;
  3012.             ttcol = col;
  3013.             TTmove(row, col);
  3014.         }
  3015.     endofDisplay();
  3016. }
  3017.  
  3018. void
  3019. bottomleft(void)
  3020. {
  3021.     movecursor(term.t_nrow-1, 0);
  3022. }
  3023.  
  3024. /*
  3025.  * Erase the message line. This is a special routine because the message line
  3026.  * is not considered to be part of the virtual screen. It always works
  3027.  * immediately; the terminal buffer is flushed via a call to the flusher.
  3028.  */
  3029. void
  3030. mlerase(void)
  3031. {
  3032.     beginDisplay();
  3033.     kbd_erase_to_end(0);
  3034.     kbd_flush();
  3035.     endofDisplay();
  3036. }
  3037.  
  3038. static char *mlsavep;
  3039.  
  3040. void
  3041. mlsavec(int c)
  3042. {
  3043.     if (mlsavep - mlsave < NSTRING-1) {
  3044.         *mlsavep++ = (char)c;
  3045.         *mlsavep = EOS;
  3046.     }
  3047. }
  3048.  
  3049. /*
  3050.  * Write a message into the message line only if appropriate.
  3051.  */
  3052. /* VARARGS1 */
  3053. void
  3054. mlwrite(const char *fmt, ...)
  3055. {
  3056.     va_list ap;
  3057.     /* if we are not currently echoing on the command line, abort this */
  3058.     if (global_b_val(MDTERSE) || kbd_replaying(FALSE) || discmd == FALSE) {
  3059.         bottomleft();
  3060.         return;
  3061.     }
  3062.     va_start(ap,fmt);
  3063.     mlmsg(fmt,&ap);
  3064.     va_end(ap);
  3065. }
  3066.  
  3067. /*    Put a string out to the message line regardless of the
  3068.     current $discmd setting. This is needed when $debug is TRUE
  3069.     and for the write-message and clear-message-line commands
  3070.     Also used for most errors, to be sure they're seen.
  3071. */
  3072. /* VARARGS1 */
  3073. void
  3074. mlforce(const char *fmt, ...)
  3075. {
  3076.     va_list ap;
  3077.     va_start(ap,fmt);
  3078.     mlmsg(fmt,&ap);
  3079.     va_end(ap);
  3080. }
  3081.  
  3082. /* VARARGS1 */
  3083. void
  3084. mlprompt(const char *fmt, ...)
  3085. {
  3086.     va_list ap;
  3087.     int osgarbf = sgarbf;
  3088.     if (discmd == FALSE) {
  3089.         bottomleft();
  3090.         return;
  3091.     }
  3092.     sgarbf = FALSE;
  3093.     va_start(ap,fmt);
  3094.     mlmsg(fmt,&ap);
  3095.     va_end(ap);
  3096.     sgarbf = osgarbf;
  3097. }
  3098.  
  3099. /* VARARGS */
  3100. void
  3101. dbgwrite(const char *fmt, ...)
  3102. {
  3103.     char temp[80];
  3104.  
  3105.     va_list ap;    /* ptr to current data field */
  3106.     va_start(ap,fmt);
  3107.     lsprintf(temp, "[press ^G to continue] %s", fmt);
  3108.     mlmsg(temp,&ap);
  3109.     va_end(ap);
  3110.     beginDisplay();
  3111.     while (TTgetc() != '\007')
  3112.         ;
  3113.     endofDisplay();
  3114. }
  3115.  
  3116. /*
  3117.  * Do the real message-line work.  Keep track of the physical cursor
  3118.  * position. A small class of printf like format items is handled.
  3119.  * Set the "message line" flag TRUE.
  3120.  */
  3121. static void
  3122. mlmsg(const char *fmt, va_list *app)
  3123. {
  3124.     static    int    recur;
  3125.     int    end_at;
  3126.     int    do_crlf = (strchr(fmt, '\n') != 0
  3127.             || strchr(fmt, '\r') != 0);
  3128.  
  3129.     if (recur++) {
  3130.         /*EMPTY*/;
  3131.     } else if (sgarbf) {
  3132.         /* then we'll lose the message on the next update(), so save it now */
  3133.         mlsavep = mlsave;
  3134. #if    OPT_POPUP_MSGS
  3135.         if (global_g_val(GMDPOPUP_MSGS) || (curwp == 0)) {
  3136.             TRACE(("mlmsg popup_msgs #1 for '%s'\n", fmt))
  3137.             popup_msgs();
  3138.             msg_putc('\n');
  3139.             dfoutfn = msg_putc;
  3140.         } else
  3141. #endif
  3142.           dfoutfn = mlsavec;
  3143.         dofmt(fmt,app);
  3144.     } else {
  3145.         beginDisplay();
  3146.  
  3147.         kbd_expand = -1;
  3148. #if    OPT_POPUP_MSGS
  3149.         if (global_g_val(GMDPOPUP_MSGS)) {
  3150.             TRACE(("mlmsg popup_msgs #2 for '%s'\n", fmt))
  3151.             popup_msgs();
  3152.             if (mlsave[0] == EOS) {
  3153.                 msg_putc('\n');
  3154.                 dfoutfn = msg_putc;
  3155.             } else {
  3156.                 dfoutfn = kbd_putc;
  3157.             }
  3158.         } else
  3159. #endif
  3160.           dfoutfn = kbd_putc;
  3161.  
  3162.         if (*fmt != '\n') {
  3163.             kbd_erase_to_end(0);
  3164.             dofmt(fmt,app);
  3165.             kbd_expand = 0;
  3166.  
  3167.             /* if we can, erase to the end of screen */
  3168.             end_at = wminip->w_dot.o;
  3169.             kbd_erase_to_end(end_at);
  3170.             mlsave[0] = EOS;
  3171.             kbd_flush();
  3172.         }
  3173.         if (do_crlf) {
  3174.             kbd_openup();
  3175.         }
  3176.         endofDisplay();
  3177.     }
  3178.     recur--;
  3179. }
  3180.  
  3181. /*
  3182.  * Do the equivalent of 'perror()' on the message line
  3183.  */
  3184. void
  3185. mlerror(const char *s)
  3186. {
  3187. #if HAVE_STRERROR
  3188.     if (errno > 0)
  3189.         mlwarn("[Error %s: %s]", s, strerror(errno));
  3190.     else
  3191.         mlwarn("[Error %s: unknown system error %d]", s, errno);
  3192. #else
  3193. #if HAVE_SYS_ERRLIST
  3194.     if (errno > 0 && errno < sys_nerr)
  3195.         mlwarn("[Error %s: %s]", s, sys_errlist[errno]);
  3196.     else
  3197.         mlwarn("[Error %s: unknown system error %d]", s, errno);
  3198. #else
  3199.     mlwarn("[Error %s, errno=%d]", s, errno);
  3200. #endif /* HAVE_SYS_ERRLIST */
  3201. #endif /* HAVE_STRERROR */
  3202. }
  3203.  
  3204. /*
  3205.  * Emit a warning message (with alarm)
  3206.  */
  3207. /* VARARGS1 */
  3208. void
  3209. mlwarn(const char *fmt, ...)
  3210. {
  3211.     va_list ap;
  3212.     va_start(ap,fmt);
  3213.     mlmsg(fmt,&ap);
  3214.     va_end(ap);
  3215.     kbd_alarm();
  3216. }
  3217.  
  3218. /*
  3219.  * Local sprintf -- similar to standard libc, but
  3220.  *  returns pointer to null character at end of buffer, so it can
  3221.  *  be called repeatedly, as in:
  3222.  *    cp = lsprintf(cp, fmt, args...);
  3223.  *
  3224.  */
  3225.  
  3226. static    char *lsp;
  3227.  
  3228. static void
  3229. lspputc(int c)
  3230. {
  3231.     *lsp++ = (char)c;
  3232. }
  3233.  
  3234. /* VARARGS1 */
  3235. char *
  3236. lsprintf(char *buf, const char *fmt, ...)
  3237. {
  3238.     va_list ap;
  3239.     va_start(ap,fmt);
  3240.  
  3241.     lsp = buf;
  3242.     dfoutfn = lspputc;
  3243.  
  3244.     dofmt(fmt,&ap);
  3245.     va_end(ap);
  3246.  
  3247.     *lsp = EOS;
  3248.     return lsp;
  3249. }
  3250.  
  3251.  
  3252. /*
  3253.  * Buffer printf -- like regular printf, but puts characters
  3254.  *    into the BUFFER.
  3255.  */
  3256. void
  3257. bputc(int c)
  3258. {
  3259.     if (c == '\n')
  3260.         (void)lnewline();
  3261.     else
  3262.         (void)linsert(1,c);
  3263. }
  3264.  
  3265. /* printf into curbp, at DOT */
  3266. /* VARARGS */
  3267. void
  3268. bprintf(const char *fmt, ...)
  3269. {
  3270.     va_list ap;
  3271.  
  3272.     dfoutfn = bputc;
  3273.  
  3274.     va_start(ap,fmt);
  3275.     dofmt(fmt,&ap);
  3276.     va_end(ap);
  3277.  
  3278. }
  3279.  
  3280. #if defined( SIGWINCH) && ! DISP_X11
  3281. /* ARGSUSED */
  3282. SIGT
  3283. sizesignal (int ACTUAL_SIG_ARGS GCC_UNUSED)
  3284. {
  3285.     int w, h;
  3286.     int old_errno = errno;
  3287.  
  3288.     getscreensize (&w, &h);
  3289.  
  3290.     if ((h > 1 && h != term.t_nrow) || (w > 1 && w != term.t_ncol))
  3291.         newscreensize(h, w);
  3292.  
  3293.     setup_handler(SIGWINCH, sizesignal);
  3294.     errno = old_errno;
  3295.     SIGRET;
  3296. }
  3297. #endif
  3298.  
  3299. void
  3300. newscreensize (int h, int w)
  3301. {
  3302.     /* do the change later */
  3303.     if (im_displaying
  3304. #if OPT_WORKING
  3305.     || !i_displayed
  3306. #endif
  3307.     ) {
  3308.         chg_width = w;
  3309.         chg_height = h;
  3310.         return;
  3311.     }
  3312.     chg_width = chg_height = 0;
  3313.     if ((h > term.t_mrow) || (w > term.t_mcol)) {
  3314.         int or, oc;
  3315.         or = term.t_mrow;
  3316.         oc = term.t_mcol;
  3317.         term.t_mrow = h;
  3318.         term.t_mcol = w;
  3319.         if (!vtinit()) { /* allocation failure */
  3320.             term.t_mrow = or;
  3321.             term.t_mcol = oc;
  3322.             return;
  3323.         }
  3324.     }
  3325.     if (!newlength(TRUE,h) || !newwidth(TRUE,w))
  3326.         return;
  3327.  
  3328.     (void)update(TRUE);
  3329. }
  3330.  
  3331. #if OPT_WORKING
  3332. /*
  3333.  * Start the timer that controls the "working..." message.
  3334.  */
  3335. static void
  3336. start_working(void)
  3337. {
  3338.     setup_handler(SIGALRM,imworking);
  3339.     (void)alarm(1);
  3340.     im_timing = TRUE;
  3341. }
  3342.  
  3343. /*
  3344.  * When we stop the timer, we should cleanup the "working..." message.
  3345.  */
  3346. static void
  3347. stop_working(void)
  3348. {
  3349.     if (mpresf) {    /* erase leftover working-message */
  3350.         int    save_row = ttrow;
  3351.         int    save_col = ttcol;
  3352.         kbd_overlay(0);
  3353.         kbd_flush();
  3354.         movecursor(save_row, save_col);
  3355.         TTflush();
  3356.     }
  3357. }
  3358.  
  3359. /*
  3360.  * Displays alternate
  3361.  *    "working..." and
  3362.  *    "...working"
  3363.  * at the end of the message line if it has been at least a second since
  3364.  * displaying anything or waiting for keyboard input.  The cur_working and
  3365.  * max_working values are used in 'slowreadf()' to show the progress of reading
  3366.  * large files.
  3367.  */
  3368.  
  3369. /*ARGSUSED*/
  3370. SIGT
  3371. imworking (int ACTUAL_SIG_ARGS GCC_UNUSED)
  3372. {
  3373.     static    const    char *const msg[] = {"working", "..."};
  3374.     static    int    flip;
  3375.     static    int    skip;
  3376.  
  3377.     signal_was = SIGALRM;    /* remember this was an alarm */
  3378.  
  3379.     if (no_working) /* brute force, for debugging */
  3380.         return;
  3381.  
  3382.     /* (if GMDWORKING is _not_ set, or MDTERSE is set, we're allowed
  3383.      * to erase, but not to write.  and if we do erase, we don't
  3384.      * reschedule the alarm, since setting the mode will call us
  3385.      * again to start things up)
  3386.      */
  3387.  
  3388.     if (im_displaying || !i_displayed) { /* look at the semaphore first! */
  3389.         /*EMPTY*/;
  3390.     } else if (im_waiting(-1)) {
  3391.         im_timing = FALSE;
  3392.         stop_working();
  3393.         return;
  3394.     } else if (ShowWorking()) {
  3395.         if (skip) {
  3396.             skip = FALSE;
  3397.         } else {
  3398. #if DISP_X11
  3399.             x_working();
  3400. #else
  3401.             char    result[20];
  3402.             result[0] = EOS;
  3403.             if (cur_working != 0
  3404.              && cur_working != old_working) {
  3405.                 char    temp[20];
  3406.                 int    len = cur_working > 999999L ? 10 : 6;
  3407.  
  3408.                 old_working = cur_working;
  3409.                 strcat(result, right_num(temp, len, cur_working));
  3410.                 if (len == 10)
  3411.                     /*EMPTY*/;
  3412.                 else if (max_working != 0) {
  3413.                     strcat(result, " ");
  3414.                     strcat(result, right_num(temp, 2,
  3415.                         (100 * cur_working) / max_working));
  3416.                     strcat(result, "%");
  3417.                 } else
  3418.                     strcat(result, " ...");
  3419.             } else {
  3420.                 strcat(result, msg[ flip]);
  3421.                 strcat(result, msg[!flip]);
  3422.             }
  3423.             kbd_overlay(result);
  3424.             kbd_flush();
  3425. #endif
  3426.         }
  3427.     } else {
  3428.         stop_working();
  3429.         skip = TRUE;
  3430.         return;
  3431.     }
  3432.     start_working();
  3433.     flip = !flip;
  3434. }
  3435. #endif    /* OPT_WORKING */
  3436.  
  3437. /*
  3438.  * Maintain a flag that records whether we're waiting for keyboard input.  As a
  3439.  * side-effect, restart the 'working' timer if we see that it's been made
  3440.  * inactive while we were waiting.
  3441.  */
  3442. int
  3443. im_waiting(int flag)
  3444. {
  3445.     static int waiting;
  3446.     if (flag >= 0) {    /* TRUE or FALSE set, negative used to query */
  3447.         waiting = flag;
  3448. #if OPT_WORKING
  3449.         if (!waiting && !im_timing && ShowWorking())
  3450.             start_working();
  3451. #endif
  3452.     }
  3453.     return waiting;
  3454. }
  3455.  
  3456. #if defined(SIGWINCH) || OPT_WORKING
  3457. /*
  3458.  * Set the semaphore so that we don't try to do I/O while we're being
  3459.  * interrupted.
  3460.  */
  3461. void
  3462. beginDisplay (void)
  3463. {
  3464.     im_displaying++;
  3465. }
  3466.  
  3467. /*
  3468.  * Reset the semaphore.
  3469.  */
  3470. void
  3471. endofDisplay (void)
  3472. {
  3473.     if (im_displaying)
  3474.         im_displaying--;
  3475. }
  3476. #endif
  3477.  
  3478. #if    OPT_PSCREEN
  3479. /* Most of the code in this section is for making the message line work
  3480.  * right...it shouldn't be called to display the rest of the screen.
  3481.  */
  3482. static int psc_row;
  3483. static int psc_col;
  3484.  
  3485. #define SWAP_INT(x,y) \
  3486.     do { (x) = (x)+(y); (y) = (x)-(y); (x) = (x)-(y); } one_time
  3487. #define SWAP_VT_PSC \
  3488.     do { SWAP_INT(vtcol, psc_col); SWAP_INT(vtrow, psc_row); } one_time
  3489.  
  3490. OUTC_DCL
  3491. psc_putchar(OUTC_ARGS)
  3492. {
  3493.     if (c == '\b') {
  3494.     if (psc_col > 0)
  3495.         psc_col--;
  3496.     }
  3497.     else {
  3498.     SWAP_VT_PSC;
  3499.     vtputc(c);
  3500.     vscreen[vtrow]->v_flag |= VFCHG;
  3501.     SWAP_VT_PSC;
  3502.     }
  3503.     OUTC_RET c;
  3504. }
  3505.  
  3506. void
  3507. psc_flush(void)
  3508. {
  3509.     updateline(term.t_nrow-1, 0, term.t_ncol);
  3510.     TTpflush();
  3511. }
  3512.  
  3513. void
  3514. psc_move(int row, int col)
  3515. {
  3516.     psc_row = row;
  3517.     psc_col = col;
  3518. }
  3519.  
  3520. void
  3521. psc_eeol(void)
  3522. {
  3523.     if (ttrow >= 0 && ttrow < term.t_nrow && ttcol >= 0) {
  3524.     VIDEO_ATTR *vp = &vscreen[ttrow]->v_attrs[ttcol];
  3525.     char *cp = &vscreen[ttrow]->v_text[ttcol];
  3526.     char *cplim = &vscreen[ttrow]->v_text[term.t_ncol];
  3527.     vscreen[ttrow]->v_flag |= VFCHG;
  3528.     while (cp < cplim) {
  3529.         *vp++ = 0;
  3530.         *cp++ = ' ';
  3531.     }
  3532.     }
  3533. }
  3534.  
  3535. void
  3536. psc_eeop(void)
  3537. {
  3538.     int saverow = ttrow;
  3539.     int savecol = ttcol;
  3540.     while (ttrow < term.t_nrow) {
  3541.     psc_eeol();
  3542.     ttrow++;
  3543.     ttcol = 0;
  3544.     }
  3545.     ttrow = saverow;
  3546.     ttcol = savecol;
  3547. }
  3548.  
  3549. /* ARGSUSED */
  3550. void
  3551. psc_rev(UINT huh GCC_UNUSED)
  3552. {
  3553.     /* do nothing */
  3554. }
  3555.  
  3556. #endif    /* OPT_PSCREEN */
  3557.  
  3558. /* For memory-leak testing (only!), releases all display storage. */
  3559. #if NO_LEAKS
  3560. void    vt_leaks(void)
  3561. {
  3562.     vtfree();
  3563. #if OPT_UPBUFF
  3564.     FreeIfNeeded(recomp_tbl);
  3565. #endif
  3566. }
  3567. #endif
  3568.