home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / disp.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  36KB  |  1,638 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
  3.  * provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is *
  5.  * included in all the files.                                           *
  6.  ************************************************************************/
  7.  
  8. #include "jove.h"
  9. #include "jctype.h"
  10. #include "chars.h"
  11. #include "fp.h"
  12. #include "disp.h"
  13. #include "ask.h"
  14. #include "extend.h"
  15. #include "fmt.h"
  16. #include "insert.h"
  17. /* #include "io.h" */    /* for pwd() */
  18. #ifdef IPROCS
  19. # include "sysprocs.h"
  20. # include "iproc.h"
  21. #endif
  22. #include "move.h"
  23. #include "macros.h"
  24. #include "screen.h"
  25. #include "term.h"
  26. #include "wind.h"
  27.  
  28. #ifdef MAC
  29. # include "mac.h"
  30. #else
  31. # include <sys/stat.h>
  32. #endif
  33.  
  34. /* define a couple of unique daddrs */
  35. #define    NOWHERE_DADDR    (~NULL_DADDR | DDIRTY)    /* not in tmp file */
  36. #define    UNSAVED_CURLINE_DADDR    ((NOWHERE_DADDR - 1) | DDIRTY)    /* not yet in tmp file */
  37.  
  38. struct screenline
  39.     *Screen = NULL,    /* the screen (a bunch of screenline) */
  40.     *Curline = NULL;    /* current line */
  41.  
  42. private void
  43.     DeTab proto((char *, int, char *, char *, bool)),
  44.     DoIDline proto((int)),
  45.     do_cl_eol proto((int)),
  46.     ModeLine proto((Window *, char *, int)),
  47.     GotoDot proto((void)),
  48.     UpdLine proto((int)),
  49.     UpdWindow proto((Window *, int));
  50.  
  51. #ifdef ID_CHAR
  52. private void
  53.     DelChar proto((int, int, int)),
  54.     InsChar proto((int, int, int, char *));
  55.  
  56. private bool
  57.     IDchar proto ((char *, int)),
  58.     OkayDelete proto ((int, int, bool)),
  59.     OkayInsert proto ((int, int));
  60.  
  61. private int
  62.     NumSimilar proto ((char *, char *, int)),
  63.     IDcomp proto ((char *, char *, int));
  64. #endif /* ID_CHAR */
  65.  
  66. private bool
  67.     AddLines proto((int, int)),
  68.     DelLines proto((int, int));
  69.  
  70. bool    DisabledRedisplay = NO;
  71.  
  72. /* Kludge windows gets called by the routines that delete lines from the
  73.    buffer.  If the w->w_line or w->w_top are deleted and this procedure
  74.    is not called, the redisplay routine will barf. */
  75.  
  76. void
  77. ChkWindows(line1, line2)
  78. LinePtr    line1,
  79.     line2;
  80. {
  81.     register Window    *w = fwind;
  82.     register LinePtr    lp,
  83.             lend = line2->l_next;
  84.  
  85.     do {
  86.         if (w->w_bufp == curbuf) {
  87.             for (lp = line1->l_next; lp != lend; lp = lp->l_next) {
  88.                 if (lp == w->w_top)
  89.                     w->w_flags |= W_TOPGONE;
  90.                 if (lp == w->w_line)
  91.                     w->w_flags |= W_CURGONE;
  92.             }
  93.         }
  94.         w = w->w_next;
  95.     } while (w != fwind);
  96. }
  97.  
  98.  
  99. #ifdef WINRESIZE
  100.  
  101. volatile bool
  102.     ResizePending = NO;    /* asynch request for screen resize */
  103.  
  104. private void
  105. resize()
  106. {
  107.     bool    oldDisabledRedisplay = DisabledRedisplay;
  108.     int
  109.         oldILI = ILI,
  110.         oldCO = CO;
  111.  
  112.     DisabledRedisplay = YES;    /* prevent tragedy */
  113.     ResizePending = NO;    /* early & safe */
  114.     ttsize();    /* update line (LI and ILI) and col (CO) info. */
  115.  
  116.     if (oldILI != ILI || oldCO != CO) {
  117.         register int    total;
  118.         register Window    *wp;
  119.  
  120.         /* Go through the window list, changing each window size in
  121.          * proportion to the resize.  If the window would become too
  122.          * small, we delete it.
  123.          *
  124.          * Actually, we must do the deletion in
  125.          * a separate pass because del_wind donates the space to either
  126.          * neighbouring window.  We test the windows in a funny order:
  127.          * top-most, then bottom-up.  Although it isn't necessary
  128.          * for correctness, it means that we consider any donated
  129.          * space, cutting down the number of windows we decide to delete.
  130.          * Loop termination is tricky: fwind may have changed due to a
  131.          * del_wind.  As a simple fix, we start over whenever we
  132.          * delete a window.  Although this is O(n**2), it can't
  133.          * really be expensive.
  134.          *
  135.          * After scaling all the windows, we
  136.          * give any space remaining to the current window (which would
  137.          * have changed if the old current window had been deleted).
  138.          *
  139.          * This seems fairer than just resizing the current window.
  140.          */
  141.         wp = fwind;
  142.         for (;;) {
  143.             int    newsize = ILI * wp->w_height / oldILI;
  144.  
  145.             if (newsize < 2) {
  146.                 del_wind(wp);
  147.                 wp = fwind;
  148.             } else {
  149.                 wp = wp->w_prev;
  150.                 if (wp == fwind)
  151.                     break;
  152.             }
  153.         }
  154.  
  155.         total = 0;
  156.         do {
  157.             int    newsize = ILI * wp->w_height / oldILI;
  158.  
  159.             wp->w_height = newsize;
  160.             total += newsize;
  161.             wp = wp->w_next;
  162.         } while (wp != fwind);
  163.  
  164.         curwind->w_height += ILI - total;
  165.  
  166.         /* Make a new screen structure */
  167.         make_scr();
  168.  
  169.         /* Do a 'hard' update on the screen - clear and redraw */
  170.         ClAndRedraw();
  171. #ifdef WIN32
  172.         ResizeWindow();
  173. #endif
  174.     }
  175.     DisabledRedisplay = oldDisabledRedisplay;
  176. }
  177.  
  178. #endif /* WINRESIZE */
  179.  
  180. private bool    RingBell;    /* So if we have a lot of errors ...
  181.                   ring the bell only ONCE */
  182.  
  183. void
  184. redisplay()
  185. {
  186.     if (DisabledRedisplay)
  187.         return;
  188. #ifdef WINRESIZE
  189.     do
  190. #endif
  191.     {
  192.         register Window    *w;
  193.         int
  194.             lineno,
  195.             i;
  196.         bool
  197.             done_ID = NO,
  198.             old_UpdModLine;
  199.         register struct scrimage
  200.             *des_p,
  201.             *phys_p;
  202.  
  203. #ifdef WINRESIZE
  204.         if (ResizePending)
  205.             resize();
  206. #endif
  207.         curwind->w_line = curwind->w_bufp->b_dot;
  208.         curwind->w_char = curwind->w_bufp->b_char;
  209. #ifdef MAC
  210.         /* To avoid calling redisplay() recursively,
  211.          * we must avoid calling CheckEvent(),
  212.          * so we must avoid calling charp().
  213.          */
  214.         InputPending = NO;
  215. #else
  216.         if (PreEmptOutput())
  217.             return;
  218. #endif
  219.         if (RingBell) {
  220.             dobell(1);
  221.             RingBell = NO;
  222.         }
  223.         AbortCnt = ScrBufSize;        /* initialize this now */
  224.         if (UpdMesg)
  225.             DrawMesg(YES);
  226.  
  227.         for (lineno = 0, w = fwind; lineno < ILI; w = w->w_next) {
  228.             UpdWindow(w, lineno);
  229.             lineno += w->w_height;
  230.         }
  231.  
  232.         /* Now that we've called update window, we can
  233.            assume that the modeline will be updated.  But
  234.            if while redrawing the modeline the user types
  235.            a character, ModeLine() is free to set this on
  236.            again so that the modeline will be fully drawn
  237.            at the next redisplay.  Furthermore, if output
  238.            is preempted, we'll restore the old value because
  239.            we can't be sure that the updating has happened. */
  240.  
  241.         old_UpdModLine = UpdModLine;
  242.         UpdModLine = NO;
  243.  
  244.         des_p = DesiredScreen;
  245.         phys_p = PhysScreen;
  246.         for (i = 0; i < ILI; i++, des_p++, phys_p++) {
  247.             if (!done_ID && (des_p->s_id != phys_p->s_id)) {
  248.                 DoIDline(i);
  249.                 done_ID = YES;
  250.             }
  251.             if ((des_p->s_flags & (s_DIRTY | s_L_MOD))
  252.             || des_p->s_id != phys_p->s_id
  253.             || des_p->s_vln != phys_p->s_vln
  254.             || des_p->s_offset != phys_p->s_offset)
  255.                 UpdLine(i);
  256.             if (CheapPreEmptOutput()) {
  257.                 if (old_UpdModLine)
  258.                     UpdModLine = YES;
  259.                 goto suppress;
  260.             }
  261.         }
  262.  
  263.         if (Asking) {
  264.             Placur(ILI, min(CO - 2, calc_pos(mesgbuf, AskingWidth)));
  265.                 /* Nice kludge */
  266.             flushscreen();
  267.         } else {
  268.             GotoDot();
  269.         }
  270. suppress: ;
  271.     }
  272. #ifdef WINRESIZE
  273.     /**/ while (ResizePending);
  274. #endif
  275. #ifdef MAC
  276.     if (Windchange)
  277.         docontrols();
  278. #endif
  279. }
  280.  
  281. /* find_pos() returns the position on the line, that C_CHAR represents
  282.    in LINE */
  283.  
  284. private int
  285. find_pos(line, c_char)
  286. LinePtr    line;
  287. int    c_char;
  288. {
  289.     return calc_pos(lcontents(line), c_char);
  290. }
  291.  
  292. /* calc_pos calculates the screen column of character c_char.
  293.  *
  294.  * Note: the calc_pos, how_far, and DeTab must be in synch --
  295.  * each thinks it knows how characters are displayed.
  296.  */
  297.  
  298. int
  299. calc_pos(lp, c_char)
  300. register char    *lp;
  301. register int    c_char;
  302. {
  303.     register int    pos = 0;
  304.     register ZXchar    c;
  305.  
  306.     while ((--c_char >= 0) && (c = ZXC(*lp++)) != 0) {
  307.         if (c == '\t' && tabstop != 0) {
  308.             pos += TABDIST(pos);
  309.         } else if (jisprint(c)) {
  310.             pos += 1;
  311.         } else {
  312.             if (c <= DEL)
  313.                 pos += 2;
  314.             else
  315.                 pos += 4;
  316.         }
  317.     }
  318.     return pos;
  319. }
  320.  
  321. volatile bool    UpdModLine = NO;
  322. bool    UpdMesg = NO;
  323.  
  324. private void
  325. DoIDline(start)
  326. int    start;
  327. {
  328.     register struct scrimage    *des_p = &DesiredScreen[start];
  329.     struct scrimage    *phys_p = &PhysScreen[start];
  330.     register int    i,
  331.             j;
  332.  
  333.     /* Some changes have been made.  Try for insert or delete lines.
  334.        If either case has happened, Addlines and/or DelLines will do
  335.        necessary scrolling, also CONVERTING PhysScreen to account for the
  336.        physical changes.  The comparison continues from where the
  337.        insertion/deletion takes place; this doesn't happen very often,
  338.        usually it happens with more than one window with the same
  339.        buffer. */
  340.  
  341. #ifdef TERMCAP
  342.     if (!CanScroll)
  343.         return;        /* We should never have been called! */
  344. #endif
  345.  
  346.     for (i = start; i < ILI; i++, des_p++, phys_p++)
  347.         if (des_p->s_id != phys_p->s_id)
  348.             break;
  349.  
  350.     for (; i < ILI; i++) {
  351.         for (j = i + 1; j < ILI; j++) {
  352.             des_p = &DesiredScreen[j];
  353.             phys_p = &PhysScreen[j];
  354.             if (des_p->s_id != NULL_DADDR && des_p->s_id == phys_p->s_id)
  355.                 break;
  356.             if (des_p->s_id == PhysScreen[i].s_id) {
  357.                 if (des_p->s_id == NULL_DADDR)
  358.                     continue;
  359.                 if (AddLines(i, j - i)) {
  360.                     DoIDline(j);
  361.                     return;
  362.                 }
  363.                 break;
  364.             }
  365.             if ((des_p = &DesiredScreen[i])->s_id == phys_p->s_id) {
  366.                 if (des_p->s_id == NULL_DADDR)
  367.                     continue;
  368.                 if (DelLines(i, j - i)) {
  369.                     DoIDline(i);
  370.                     return;
  371.                 }
  372.                 break;
  373.             }
  374.         }
  375.     }
  376. }
  377.  
  378. /* Make DesiredScreen reflect what the screen should look like when we are done
  379.    with the redisplay.  This deals with horizontal scrolling.  Also makes
  380.    sure the current line of the Window is in the window. */
  381.  
  382. bool    ScrollAll = NO;    /* VAR: when current line scrolls, scroll whole window? */
  383. int    ScrollWidth = 10;    /* VAR: unit of horizontal scrolling */
  384.  
  385. private void
  386. UpdWindow(w, start)
  387. register Window    *w;
  388. int    start;
  389. {
  390.     LinePtr    lp;
  391.     int    i,
  392.         upper,        /* top of window */
  393.         lower,        /* bottom of window */
  394.         strt_col,    /* starting print column of current line */
  395.         ntries = 0;    /* # of tries at updating window */
  396.     register struct scrimage    *des_p,
  397.                     *phys_p;
  398.     Buffer    *bp = w->w_bufp;
  399.  
  400.     do {
  401.         if (w->w_flags & W_CURGONE) {
  402.             w->w_line = bp->b_dot;
  403.             w->w_char = bp->b_char;
  404.         }
  405.         if (w->w_flags & W_TOPGONE)
  406.             CentWind(w);    /* reset topline of screen */
  407.         w->w_flags &= ~(W_CURGONE | W_TOPGONE);
  408.  
  409.         /* make sure that the current line is in the window */
  410.         upper = start;
  411.         lower = upper + WSIZE(w);
  412.         for (i = upper, lp = w->w_top; ; lp = lp->l_next, i++) {
  413.             if (i == lower || lp == NULL) {
  414.                 /* we've run out of window without finding dot */
  415.                 ntries += 1;
  416.                 if (ntries == 1) {
  417.                     CalcWind(w);
  418.                 } else if (ntries == 2) {
  419.                     w->w_top = w->w_line = w->w_bufp->b_first;
  420.                     writef("\rERROR in redisplay: I got hopelessly lost!");
  421.                     dobell(2);
  422.                 } else {
  423.                     writef("\n\rOops, still lost, quitting ...\r\n");
  424.                     finish(-1);    /* die! */
  425.                     /*NOTREACHED*/
  426.                 }
  427.                 break;
  428.             }
  429.             if (lp == w->w_line) {
  430.                 ntries = 0;    /* happiness: dot is in window */
  431.                 break;
  432.             }
  433.         }
  434.     } while (ntries != 0);
  435.  
  436.     /* first do some calculations for the current line */
  437.     {
  438.         int
  439.             nw = W_NUMWIDTH(w),
  440.             dot_col,
  441.             end_col;
  442.  
  443.         strt_col = ScrollAll? w->w_LRscroll : PhysScreen[i].s_offset;
  444.         end_col = strt_col + (CO - 1) - (nw + SIWIDTH(strt_col));
  445.         /* Right now we are displaying from strt_col to
  446.          * end_col of the buffer line.  These are PRINT
  447.          * columns, not actual characters.
  448.          */
  449.         dot_col = w->w_dotcol = find_pos(w->w_line, w->w_char);
  450.         /* if the new dotcol is out of range, reselect
  451.          * a horizontal window
  452.          */
  453.         if (PhysScreen[i].s_offset == -1
  454.         || !(strt_col <= dot_col && dot_col < end_col))
  455.         {
  456.             /* If dot_col is within first step left of screen, step left.
  457.              * Otherwise, if ditto for right.
  458.              * Otherwise, if it is in first screenwidth, start from begining.
  459.              * Otherwise, center dot_col.
  460.              * Fudge: if a scroll left would work except for the necessary
  461.              * appearance of an ! on the left, we scroll an extra column.
  462.              */
  463.             int
  464.                 step = min(ScrollWidth, end_col - strt_col);
  465.  
  466.             strt_col =
  467.                 strt_col > dot_col && strt_col - step <= dot_col
  468.                     ? max(strt_col - step, 0)
  469.                 : dot_col >= end_col && dot_col < end_col + step
  470.                     ? min(strt_col + step
  471.                       + (strt_col == 0 && dot_col == end_col + step - 1? 1 : 0)
  472.                       , dot_col)
  473.                 : dot_col < ((CO - 1) - nw)
  474.                     ? 0
  475.                 : dot_col - ((CO - nw) / 2);
  476.  
  477.             if (ScrollAll) {
  478.                 if (w->w_LRscroll != strt_col)
  479.                     UpdModLine = YES;
  480.                 w->w_LRscroll = strt_col;
  481.             }
  482.         }
  483.         w->w_dotline = i;
  484.         w->w_dotcol = dot_col + nw + SIWIDTH(strt_col);
  485.     }
  486.  
  487.     lp = w->w_top;
  488.     des_p = &DesiredScreen[upper];
  489.     phys_p = &PhysScreen[upper];
  490.     for (i = upper; i < lower; i++, des_p++, phys_p++) {
  491.         if (lp != NULL) {
  492.             des_p->s_offset = (lp == w->w_line)? strt_col : w->w_LRscroll;
  493.             des_p->s_flags = isdirty(lp) ? s_L_MOD : 0;
  494.             des_p->s_vln = (w->w_flags & W_NUMLINES)?
  495.                 w->w_topnum + (i - upper) : 0;
  496.             des_p->s_id = (lp == curline && DOLsave)?
  497.                 UNSAVED_CURLINE_DADDR : lp->l_dline & ~DDIRTY;
  498.             des_p->s_lp = lp;
  499.             lp = lp->l_next;
  500.         } else {
  501.             /* display line beyond end of buffer */
  502.             static const struct scrimage
  503.                 clean_plate = { 0, 0, 0, NULL_DADDR, NULL, NULL };
  504.  
  505.             *des_p = clean_plate;
  506.             if (phys_p->s_id != NULL_DADDR)
  507.                 des_p->s_flags = s_DIRTY;
  508.         }
  509.         des_p->s_window = w;
  510.     }
  511.  
  512.     /* mode line: */
  513.  
  514.     /* ??? The following assignment to des_p->s_id is very questionable:
  515.      * it stores a pointer in a daddr variable!
  516.      *
  517.      * We count on the cast pointer value being distinct from
  518.      * any other daddr, but equal to itself.  Turning
  519.      * the "DDIRTY" bit on should ensure that it is distinct
  520.      * from legitimate daddr values (except for NOWHERE_DADDR
  521.      * and UNSAVED_CURLINE_DADDR).
  522.      * If sizeof(Buffer *)>sizeof(daddr), nothing ensures that
  523.      * these pointers are even distinct from each other.
  524.      *
  525.      * There also seems to be an assumption that every modeline
  526.      * for a particular buffer will be the same.  This is not
  527.      * always the case: the last modeline on the screen is usually
  528.      * different from any other modeline, even for the same buffer.
  529.      * Currently, I think that only very contrived cases could cause
  530.      * problems (probably involving window resizing).
  531.      * Further problems will arise if JOVE is changed so that there are
  532.      * other ways in which a modeline can reflect the window state
  533.      * (instead of just the buffer state).
  534.      *
  535.      * -- DHR
  536.      */
  537.     des_p->s_window = w;
  538.     des_p->s_id = (daddr) w->w_bufp | DDIRTY;
  539.     des_p->s_flags = (des_p->s_id != phys_p->s_id || UpdModLine)?
  540.         s_MODELINE | s_DIRTY : 0;
  541.     des_p->s_offset = 0;
  542.     des_p->s_vln = 0;
  543.     des_p->s_lp = NULL;
  544.  
  545. #ifdef MAC
  546.     if (UpdModLine)
  547.         Modechange = YES;
  548.     if (w == curwind && w->w_control != NULL)
  549.         SetScrollBar(w);
  550. #endif
  551. }
  552.  
  553. /* Write whatever is in mesgbuf (maybe we are Asking, or just printed
  554.    a message).  Turns off the UpdateMesg line flag. */
  555.  
  556. void
  557. DrawMesg(abortable)
  558. bool    abortable;
  559. {
  560.     char    outbuf[MAXCOLS + PPWIDTH];    /* assert(CO <= MAXCOLS); */
  561.  
  562. #ifndef MAC        /* same reason as in redisplay() */
  563.     if (PreEmptOutput())
  564.         return;
  565. #endif
  566.     i_set(ILI, 0);
  567.     DeTab(mesgbuf, 0, outbuf, outbuf + CO, NO);
  568.     if (swrite(outbuf, NOEFFECT, abortable)) {
  569.         cl_eol();
  570.         UpdMesg = NO;
  571.     }
  572.     flushscreen();
  573. }
  574.  
  575. /* Goto the current position in the current window.  Presumably redisplay()
  576.    has already been called, and curwind->{w_dotline,w_dotcol} have been set
  577.    correctly. */
  578.  
  579. private void
  580. GotoDot()
  581. {
  582.     if (!CheapPreEmptOutput()) {
  583.         Placur(curwind->w_dotline,
  584.             curwind->w_dotcol - PhysScreen[curwind->w_dotline].s_offset);
  585.         flushscreen();
  586.     }
  587. }
  588.  
  589. private int
  590. UntilEqual(start)
  591. register int    start;
  592. {
  593.     register struct scrimage    *des_p = &DesiredScreen[start],
  594.                     *phys_p = &PhysScreen[start];
  595.  
  596.     while ((start < ILI) && (des_p->s_id != phys_p->s_id)) {
  597.         des_p += 1;
  598.         phys_p += 1;
  599.         start += 1;
  600.     }
  601.  
  602.     return start;
  603. }
  604.  
  605. /* Calls the routine to do the physical changes, and changes PhysScreen to
  606.    reflect those changes. */
  607.  
  608. private bool
  609. AddLines(at, num)
  610. register int    at,
  611.         num;
  612. {
  613.     register int    i;
  614.     int    bottom = UntilEqual(at + num);
  615.  
  616.     if (num == 0 || num >= ((bottom - 1) - at))
  617.         return NO;                /* we did nothing */
  618.     v_ins_line(num, at, bottom - 1);
  619.  
  620.     /* Now change PhysScreen to account for the physical change. */
  621.  
  622.     for (i = bottom - 1; i - num >= at; i--)
  623.         PhysScreen[i] = PhysScreen[i - num];
  624.     for (i = 0; i < num; i++)
  625.         PhysScreen[at + i].s_id = NULL_DADDR;
  626.     return YES;                    /* we did something */
  627. }
  628.  
  629. private bool
  630. DelLines(at, num)
  631. register int    at,
  632.         num;
  633. {
  634.     register int    i;
  635.     int    bottom = UntilEqual(at + num);
  636.  
  637.     if (num == 0 || num >= ((bottom - 1) - at))
  638.         return NO;
  639.     v_del_line(num, at, bottom - 1);
  640.  
  641.     for (i = at; num + i < bottom; i++)
  642.         PhysScreen[i] = PhysScreen[num + i];
  643.     for (i = bottom - num; i < bottom; i++)
  644.         PhysScreen[i].s_id = NULL_DADDR;
  645.     return YES;
  646. }
  647.  
  648. bool    MarkHighlighting = YES;    /* VAR: highlight mark when visible */
  649.  
  650. /* Update line linenum in window w.  Only set PhysScreen to DesiredScreen
  651.    if the swrite or cl_eol works, that is nothing is interrupted by
  652.    characters typed. */
  653.  
  654. private void
  655. UpdLine(linenum)
  656. register int    linenum;
  657. {
  658.     register struct scrimage    *des_p = &DesiredScreen[linenum];
  659.     register Window    *w = des_p->s_window;
  660.     char    outbuf[MAXCOLS + PPWIDTH];    /* assert(CO <= MAXCOLS); */
  661.  
  662.     i_set(linenum, 0);
  663.     if (des_p->s_flags & s_MODELINE) {
  664.         ModeLine(w, outbuf, linenum);
  665.     } else if (des_p->s_id != NULL_DADDR) {
  666.         char    *lptr;
  667.         int    fromcol = W_NUMWIDTH(w);
  668. #ifdef HIGHLIGHTING
  669.         static struct LErange lr = {0, 0, NULL, US_effect};
  670.         Mark    *mark = b_curmark(w->w_bufp);
  671.         bool    marked_line = (MarkHighlighting
  672. # ifdef TERMCAP
  673.                    && US != NULL
  674. # endif
  675.                    && mark != NULL
  676.                    && mark->m_line == des_p->s_lp);
  677. #endif /* HIGHLIGHTING */
  678.  
  679.         des_p->s_lp->l_dline &= ~DDIRTY;
  680.         des_p->s_flags &= ~(s_DIRTY | s_L_MOD);
  681.  
  682.         if (w->w_flags & W_NUMLINES)
  683.             swritef(outbuf, sizeof(outbuf), "%6d  ", des_p->s_vln);
  684.         if (des_p->s_offset != 0) {
  685.             outbuf[fromcol++] = '!';
  686.             outbuf[fromcol] = '\0';
  687.         }
  688.         lptr = lcontents(des_p->s_lp);
  689.         DeTab(lptr, des_p->s_offset, outbuf + fromcol,
  690.               outbuf + CO, (w->w_flags & W_VISSPACE) != 0);
  691. #ifdef HIGHLIGHTING
  692.         if (marked_line) {
  693.             lr.start = calc_pos(lptr, mark->m_char)
  694.                     - des_p->s_offset + fromcol;
  695.             lr.width = 1;
  696.             if (lr.start < sizeof(outbuf) - 1 && outbuf[lr.start] == '\0') {
  697.                 outbuf[lr.start] = ' ';
  698.                 outbuf[lr.start + 1] = '\0';
  699.             }
  700.         }
  701. #endif /* HIGHLIGHTING */
  702. #ifdef ID_CHAR
  703.         /* REMIND: This code, along with the rest of the
  704.            ID_CHAR, belongs in the screen driver for
  705.            termcap based systems.  mac and pc's and other
  706.            window-based drivers don't give a hoot about
  707.            ID_CHAR. */
  708.  
  709.         /* attempt to exploit insert or delete character capability
  710.          * but only if not highlighting some part of the line
  711.          */
  712.         if (UseIC && Curline->s_effects == NOEFFECT
  713. # ifdef HIGHLIGHTING
  714.         && !marked_line
  715. # endif /* HIGHLIGHTING */
  716.         ) {
  717.             if (IDchar(outbuf, linenum)) {
  718.                 /* success: clean up and go home */
  719.                 PhysScreen[linenum] = *des_p;
  720.                 return;
  721.             }
  722.             /* failure: re-initialize various cursors */
  723.             i_set(linenum, 0);
  724.         }
  725. #endif /* ID_CHAR */
  726.  
  727.         if (swrite(outbuf,
  728. #ifdef HIGHLIGHTING
  729.             marked_line ? &lr : NOEFFECT,
  730. #else
  731.             NOEFFECT,
  732. #endif
  733.             YES))
  734.         {
  735.             do_cl_eol(linenum);
  736.         } else {
  737.             /* interrupted: mark indeterminate state */
  738.             PhysScreen[linenum].s_id = NOWHERE_DADDR;
  739.         }
  740.     } else if (PhysScreen[linenum].s_id != NULL_DADDR) {
  741.         /* not the same ... make sure */
  742.         do_cl_eol(linenum);
  743.     }
  744. }
  745.  
  746. private void
  747. do_cl_eol(linenum)
  748. register int    linenum;
  749. {
  750.     cl_eol();
  751.     PhysScreen[linenum] = DesiredScreen[linenum];
  752. }
  753.  
  754. /* Expand tabs (and other funny characters) of a section of "buf"
  755.  * into "outbuf".
  756.  *
  757.  * Note: outbuf must allow for at least PPWIDTH extra characters.
  758.  * This is sufficient room for one extra character to be displayed,
  759.  * streamlining the code.
  760.  *
  761.  * Note: the calc_pos, how_far, and DeTab must be in synch --
  762.  * each thinks it knows how characters are displayed.
  763.  */
  764.  
  765. private void
  766. DeTab(src, start_offset, dst, dst_limit, visspace)
  767. char    *src;
  768. int    start_offset;
  769. char    *dst;
  770. char    *dst_limit;
  771. bool    visspace;
  772. {
  773.     ZXchar    c;
  774.     int    offset = start_offset;
  775.  
  776.     /* At any time, the number of characters we've output is
  777.        start_offset - offset.  This is needed to correctly
  778.        calculate TABDIST() without having to add another
  779.        variable (pos) to be incremented for each call to addc. */
  780.  
  781. #define addc(ch) { if (--offset < 0) *dst++ = (ch); }
  782.  
  783.     while ((c = ZXC(*src++)) != '\0') {
  784.         if (c == '\t' && tabstop != 0) {
  785.             int    nchars = TABDIST(start_offset - offset);
  786.  
  787.             c = visspace? '>' : ' ';
  788.             while (--nchars > 0 && dst < dst_limit) {
  789.                 addc(c);
  790.                 c = ' ';
  791.             }
  792.         } else if (jisprint(c)) {
  793.             if (visspace && c == ' ')
  794.                 c = '_';
  795.         } else {
  796.             char    buf[PPWIDTH];
  797.             char    *p;
  798.  
  799.             PPchar(c, buf);
  800.             /* assert(buf[0] != '\0'); */
  801.             for (p = buf; (c = *p++), *p != '\0'; )
  802.                 addc(c);
  803.         }
  804.         if (--offset < 0) {
  805.             *dst++ = c;
  806.             if (dst >= dst_limit) {
  807.                 /* we've run out of real estate: truncate and flag it */
  808.                 dst = dst_limit-1;
  809.                 *dst++ = '!';
  810.                 break;
  811.             }
  812.         }
  813.     }
  814. #undef    addc
  815.     *dst = '\0';
  816. }
  817.  
  818.  
  819. #ifdef ID_CHAR
  820.  
  821. /* From here to the end of the file is code that tries to utilize the
  822.    insert/delete character feature on some terminals.  It is very confusing
  823.    and not so well written code, AND there is a lot of it.  You may want
  824.    to use the space for something else. */
  825.  
  826. bool    IN_INSmode = NO;
  827.  
  828. void
  829. INSmode(on)
  830. bool    on;
  831. {
  832.     if (on != IN_INSmode) {
  833.         putpad(on? IM : EI, 1);
  834.         IN_INSmode = on;
  835.     }
  836. }
  837.  
  838. /* ID character routines full of special cases and other fun stuff like that.
  839.    It actually works though ...
  840.  
  841.     Returns Non-Zero if you are finished (no differences left). */
  842.  
  843. private bool
  844. IDchar(new, lineno)
  845. register char    *new;
  846. int    lineno;
  847. {
  848.     register int    col = 0;
  849.     struct screenline    *sline = &Screen[lineno];
  850.     register char    *old = sline->s_line;
  851.     int    newlen = strlen(new);
  852.  
  853.     for (;;) {
  854.         int    oldlen = sline->s_roof - old;
  855.         int    i;
  856.  
  857.         for (; ; col++) {
  858.             if (col == oldlen || col == newlen)
  859.                 return oldlen == newlen;    /* one ended; happy if both ended */
  860.  
  861.             if (old[col] != new[col])
  862.                 break;
  863.         }
  864.  
  865.         /* col now is first difference, and not the end of either */
  866.  
  867.         /* see if an insertion will help */
  868.  
  869.         for (i = col + 1; i < newlen; i++) {
  870.             if (new[i] == old[col]) {
  871.                 /* The number of saved characters is (roughly)
  872.                  * the number of characters we can retain after
  873.                  * the insertion, minus the number that we
  874.                  * could have salvaged without moving them.
  875.                  */
  876.                 int    NumSaved = IDcomp(new + i, old + col, oldlen-col)
  877.                         - NumSimilar(new + col, old + col, min(i, oldlen)-col);
  878.  
  879.                 if (OkayInsert(NumSaved, i - col)) {
  880.                     InsChar(lineno, col, i - col, new);
  881.                     col = i;
  882.                     break;
  883.                 }
  884.             }
  885.         }
  886.         if (i != newlen)
  887.             continue;
  888.  
  889.         /* see if a deletion will help */
  890.  
  891.         for (i = col + 1; i < oldlen; i++) {
  892.             if (new[col] == old[i]) {
  893.                 int    NumSaved = IDcomp(new + col, old + i, oldlen - i);
  894.  
  895.                 if (OkayDelete(NumSaved, i - col, newlen == oldlen)) {
  896.                     DelChar(lineno, col, i - col);
  897.                     break;
  898.                 }
  899.             }
  900.         }
  901.         if (i != oldlen)
  902.             continue;
  903.         return NO;
  904.     }
  905. }
  906.  
  907. private int
  908. NumSimilar(s, t, n)
  909. register char    *s,
  910.         *t;
  911. int    n;
  912. {
  913.     register int    num = 0;
  914.  
  915.     while (n--)
  916.         if (*s++ == *t++)
  917.             num += 1;
  918.     return num;
  919. }
  920.  
  921. private int
  922. IDcomp(s, t, len)
  923. register char    *s,    /* NUL terminated */
  924.         *t;    /* len chars */
  925. int    len;
  926. {
  927.     register int    i;
  928.     int    num = 0,
  929.         nonspace = 0;
  930.  
  931.     for (i = 0; i < len; i++) {
  932.         char    c = *s++;
  933.  
  934.         if (c == '\0' || c != *t++)
  935.             break;
  936.         if (c != ' ')
  937.             nonspace = 1;
  938.         num += nonspace;
  939.     }
  940.  
  941.     return num;
  942. }
  943.  
  944. private bool
  945. OkayDelete(Saved, num, samelength)
  946. int    Saved,
  947.     num;
  948. bool    samelength;
  949. {
  950.     /* If the old and the new have different lengths, then the competition
  951.      * will have to clear to end of line.  We take that into consideration.
  952.      */
  953.     return Saved + (samelength ? 0 : CElen) > min(MDClen, DClen * num);
  954. }
  955.  
  956. private bool
  957. OkayInsert(Saved, num)
  958. int    Saved,
  959.     num;
  960. {
  961.     register int    n = 0;
  962.  
  963.     /* Note: the way termcap/terminfo is defined, we must use *both*
  964.      * IC and IM to insert, but normally only one will be defined.
  965.      * See terminfo(5), under the heading "Insert/Delete Character".
  966.      */
  967.     if (IC != NULL)        /* Per character prefixes */
  968.         n = min(num * IClen, MIClen);
  969.  
  970.     if (!IN_INSmode)
  971.         n += IMlen;
  972.  
  973.     n += num;    /* The characters themselves */
  974.  
  975.     return Saved > n;
  976. }
  977.  
  978. private void
  979. DelChar(lineno, col, num)
  980. int    lineno,
  981.     col,
  982.     num;
  983. {
  984.     register char    *from,
  985.             *to;
  986.     struct screenline *sp = (&Screen[lineno]);
  987.  
  988.     Placur(lineno, col);
  989.     putmulti(DC, M_DC, num, 1);
  990.  
  991.     to = sp->s_line + col;
  992.     from = to + num;
  993.  
  994.     byte_copy(from, to, (size_t) (sp->s_roof - from));
  995.     clrline(sp->s_roof - num, sp->s_roof);
  996.     sp->s_roof -= num;
  997. }
  998.  
  999. private void
  1000. InsChar(lineno, col, num, new)
  1001. int    lineno,
  1002.     col,
  1003.     num;
  1004. char    *new;
  1005. {
  1006.     register char    *sp1,
  1007.             *sp2,    /* To push over the array. */
  1008.             *sp3;    /* Last character to push over. */
  1009.     int    i;
  1010.  
  1011.     i_set(lineno, 0);
  1012.     sp2 = Curline->s_roof + num;
  1013.  
  1014.     if (sp2 > cursend) {
  1015.         i_set(lineno, CO - num - 1);
  1016.         cl_eol();
  1017.         sp2 = cursend;
  1018.     }
  1019.     Curline->s_roof = sp2;
  1020.     sp1 = sp2 - num;
  1021.     sp3 = Curline->s_line + col;
  1022.  
  1023.     while (sp1 > sp3)
  1024.         *--sp2 = *--sp1;
  1025.  
  1026.     new += col;
  1027.     byte_copy(new, sp3, (size_t) num);
  1028.  
  1029.     /* The internal screen is correct, and now we have to do
  1030.        the physical stuff. */
  1031.  
  1032.     Placur(lineno, col);
  1033.  
  1034.     /* Note: the way termcap/terminfo is defined, we must use *both*
  1035.      * IC and IM, but normally only one will be defined.
  1036.      * See terminfo(5), under the heading "Insert/Delete Character".
  1037.      */
  1038.     if (IC != NULL)
  1039.         putmulti(IC, M_IC, num, 1);
  1040.     if (IM != NULL)
  1041.         INSmode(YES);
  1042.  
  1043.     for (i = 0; i < num; i++) {
  1044.         scr_putchar(new[i]);
  1045.         if (IN_INSmode)
  1046.             putpad(IP, 1);
  1047.     }
  1048.     CapCol += num;
  1049. }
  1050.  
  1051. #endif /* ID_CHAR */
  1052.  
  1053. #ifdef UNIX        /* obviously ... no mail today if not Unix*/
  1054.  
  1055. /* chkmail() returns YES if there is new mail since the
  1056.    last time we checked. */
  1057.  
  1058. char    Mailbox[FILESIZE];    /* VAR: mailbox name */
  1059. int    MailInt = 60;        /* VAR: mail check interval (seconds) */
  1060.  
  1061. bool
  1062. chkmail(force)
  1063. bool    force;
  1064. {
  1065.     time_t    now;
  1066.     static bool    state = NO;    /* assume unknown */
  1067.     static time_t    last_chk = 0,
  1068.             mbox_time = 0;
  1069.     struct stat    stbuf;
  1070.  
  1071.     if (MailInt == 0 || Mailbox[0] == '\0')
  1072.         return NO;
  1073.     time(&now);
  1074.     if ((force == NO) && (now < last_chk + MailInt))
  1075.         return state;
  1076.     last_chk = now;
  1077.     if (stat(Mailbox, &stbuf) < 0) {
  1078.         state = NO;        /* no mail */
  1079.         return NO;
  1080.     }
  1081.     if ((stbuf.st_atime > stbuf.st_mtime && stbuf.st_atime > mbox_time)
  1082.     || stbuf.st_size == 0)
  1083.     {
  1084.         mbox_time = stbuf.st_atime;
  1085.         state = NO;
  1086.     } else if (stbuf.st_mtime > mbox_time) {
  1087.         if (mbox_time > 0)
  1088.             dobell(2);        /* announce the change */
  1089.         mbox_time = stbuf.st_mtime;
  1090.         state = YES;
  1091.     }
  1092.     return state;
  1093. }
  1094.  
  1095. #endif /* UNIX */
  1096.  
  1097. /* Print the mode line. */
  1098.  
  1099. private char    *mode_p,
  1100.         *mend_p;
  1101. bool    BriteMode = YES;        /* VAR: make the mode line inverse? */
  1102.  
  1103. private void
  1104. mode_app(str)
  1105. register const char    *str;
  1106. {
  1107.     ZXchar    c;
  1108.  
  1109.     while (mode_p < mend_p && (c = ZXC(*str++)) != '\0') {
  1110.         /* don't expand tabs: treat them as suspects */
  1111.         if (jisprint(c)) {
  1112.             *mode_p++ = c;
  1113.         } else {
  1114.             char    buf[PPWIDTH];
  1115.  
  1116.             PPchar(c, buf);
  1117.             mode_app(buf);
  1118.         }
  1119.     }
  1120.  
  1121. }
  1122.  
  1123. /* VAR: mode line format string */
  1124. char    ModeFmt[120] = "%3c %w %[%sJOVE (%M)   Buffer: %b  \"%f\" %]%s%i#-%m*- %((%t)%s%)%e";
  1125.  
  1126. private void
  1127. ModeLine(w, line, linenum)
  1128. register Window    *w;
  1129. char    *line;    /* scratch space of at least CO chars */
  1130. int    linenum;
  1131. {
  1132.     int    n,
  1133.         glue = 0;
  1134.     bool    ign_some = NO;
  1135.     bool    td = NO;    /* is time (kludge: or mail status) displayed? */
  1136.     char
  1137.         *fmt = ModeFmt,
  1138.         fillc,
  1139.         c;
  1140.     register Buffer    *thisbuf = w->w_bufp;
  1141.     register Buffer *bp;
  1142.     LineEffects highlighting;
  1143.  
  1144.     mode_p = line;
  1145.     mend_p = &line[CO - 1];
  1146.  
  1147. #ifdef TERMCAP
  1148.     if (SO == NULL)
  1149.         BriteMode = NO;    /* we can't do it */
  1150. #endif
  1151.     /* ??? On Mac, perhaps '_' looks better than '-' */
  1152.     fillc = BriteMode? ' ' : '-';
  1153.  
  1154.     while ((c = *fmt++)!='\0' && mode_p<mend_p) {
  1155.         if (c != '%') {
  1156.             if (c == '\\')
  1157.                 if ((c = *fmt++) == '\0')
  1158.                     break;
  1159.             if (!ign_some) {
  1160.                 static char    x[] = "x";
  1161.  
  1162.                 x[0] = c;
  1163.                 mode_app(x);
  1164.             }
  1165.             continue;
  1166.         }
  1167.         if ((c = *fmt++) == '\0')    /* char after the '%' */
  1168.             break;
  1169.         if (ign_some && c != ')')
  1170.             continue;
  1171.         n = 1;
  1172.         if (c >= '0' && c <= '9') {
  1173.             n = 0;
  1174.             while (c >= '0' && c <= '9') {
  1175.                 n = n * 10 + (c - '0');
  1176.                 c = *fmt++;
  1177.             }
  1178.             if (c == '\0')
  1179.                 break;
  1180.         }
  1181.         switch (c) {
  1182.         case '%':
  1183.             mode_app("%");
  1184.             break;
  1185.  
  1186.         case '(':
  1187.             if (w->w_next != fwind)    /* Not bottom window. */
  1188.                 ign_some = YES;
  1189.             break;
  1190.  
  1191.         case ')':
  1192.             ign_some = NO;
  1193.             break;
  1194.  
  1195.         case '[':
  1196.         case ']':
  1197.             for (n=RecDepth; n>0 && mode_p<mend_p; n--)
  1198.                 *mode_p++ = c;
  1199.             break;
  1200.  
  1201. #ifdef UNIX
  1202.         case 'C':    /* check mail here */
  1203.             td = YES;    /* kludge: reflect old behaviour where alarm could trigger mail check */
  1204.             if (chkmail(NO))
  1205.                 mode_app("[New mail]");
  1206.             break;
  1207. #endif /* UNIX */
  1208.  
  1209.         case 'M':
  1210.             {
  1211.             static const char    *const mmodes[] = {
  1212.                 "Fundamental ",
  1213.                 "Text ",
  1214.                 "C ",
  1215. #ifdef LISP
  1216.                 "Lisp ",
  1217. #endif
  1218.                 NULL
  1219.             };
  1220.  
  1221.             mode_app(mmodes[thisbuf->b_major]);
  1222.  
  1223.             if (BufMinorMode(thisbuf, Fill))
  1224.                 mode_app("Fill ");
  1225.             if (BufMinorMode(thisbuf, Abbrev))
  1226.                 mode_app("Abbrev ");
  1227.             if (BufMinorMode(thisbuf, OverWrite))
  1228.                 mode_app("OvrWt ");
  1229.             if (BufMinorMode(thisbuf, Indent))
  1230.                 mode_app("Indent ");
  1231.             if (BufMinorMode(thisbuf, ReadOnly))
  1232.                 mode_app("RO ");
  1233.             if (InMacDefine)
  1234.                 mode_app("Def ");
  1235.             mode_p -= 1;    /* Back over the extra space. */
  1236.             break;
  1237.             }
  1238.  
  1239.         case 'c':
  1240.             while (--n>=0 && mode_p<mend_p)
  1241.                 *mode_p++ = fillc;
  1242.             break;
  1243.  
  1244.         case 'd':    /* print working directory */
  1245.             mode_app(pr_name(pwd(), YES));
  1246.             break;
  1247.  
  1248.         case 'e':    /* stretchable glue */
  1249.             *mode_p++ = '\0';    /* glue marker */
  1250.             glue++;
  1251.             break;
  1252.  
  1253.         case 'b':
  1254.             mode_app(thisbuf->b_name);
  1255.             break;
  1256.  
  1257.         case 'f':
  1258.         case 'F':
  1259.             if (thisbuf->b_fname == NULL)
  1260.                 mode_app("[No file]");
  1261.             else {
  1262.                 if (c == 'f')
  1263.                     mode_app(pr_name(thisbuf->b_fname, YES));
  1264.                 else
  1265.                     mode_app(basename(thisbuf->b_fname));
  1266.             }
  1267.             break;
  1268.  
  1269.         case 'i':
  1270.             {
  1271.             char    yea = (*fmt == '\0') ? '#' : *fmt++;
  1272.             char    nay = (*fmt == '\0') ? ' ' : *fmt++;
  1273.  
  1274.             *mode_p++ = w->w_bufp->b_diverged ? yea : nay;
  1275.             break;
  1276.             }
  1277.  
  1278.         case 'm':
  1279.             {
  1280.             char    yea = (*fmt == '\0') ? '*' : *fmt++;
  1281.             char    nay = (*fmt == '\0') ? ' ' : *fmt++;
  1282.  
  1283.             *mode_p++ = IsModified(w->w_bufp) ? yea : nay;
  1284.             break;
  1285.             }
  1286.  
  1287.         case 'n':
  1288.             {
  1289.             char    tmp[16];
  1290.  
  1291.             for (bp = world, n = 1; bp != NULL; bp = bp->b_next, n++)
  1292.                 if (bp == thisbuf)
  1293.                     break;
  1294.  
  1295.             swritef(tmp, sizeof(tmp), "%d", n);
  1296.             mode_app(tmp);
  1297.             break;
  1298.             }
  1299.  
  1300. #ifdef IPROCS
  1301.         case 'p':
  1302.             if (thisbuf->b_type == B_PROCESS) {
  1303.                 char    tmp[40];
  1304.                 Process    p = thisbuf->b_process;
  1305.  
  1306.                 swritef(tmp, sizeof(tmp), "(%s%s)",
  1307.                     dbxness(p), pstate(p));
  1308.                 mode_app(tmp);
  1309.             }
  1310.             break;
  1311. #endif
  1312.  
  1313.         case 's':
  1314.             if (mode_p[-1] != ' ')
  1315.                 *mode_p++ = ' ';
  1316.             break;
  1317.  
  1318.         case 't':
  1319.             {
  1320.             char    timestr[12];
  1321.  
  1322.             td = YES;
  1323.             mode_app(get_time((time_t *)NULL, timestr, 11, 16));
  1324.             break;
  1325.             }
  1326.  
  1327.         case 'w':
  1328.             if (w->w_LRscroll > 0)
  1329.                 mode_app(">");
  1330.             break;
  1331.  
  1332.         default:
  1333.             mode_app("?");
  1334.             break;
  1335.         }
  1336.     }
  1337.  
  1338.     /* Glue (Knuth's term) is a field that expands to fill
  1339.      * any leftover space.  Multiple glue fields compete
  1340.      * on an equal basis.  This is a generalization of a
  1341.      * mechanism to allow centring and right-justification.
  1342.      * The original meaning of %e (fill the rest of the
  1343.      * line) has also been generalized.  %e can now
  1344.      * meaningfully be used 0 or more times.
  1345.      */
  1346.  
  1347.     if  (glue) {
  1348.         /* 1 space unused, plus padding for magic cookies */
  1349.         register char    *to = &line[CO - 1 - (4 * SG)],
  1350.                 *from = mode_p;
  1351.  
  1352.         if (to < from)
  1353.             to = from;
  1354.         mode_p = to;
  1355.         while (from != line) {
  1356.             if ((*--to = *--from) == '\0') {
  1357.                 register int    portion = (to-from) / glue;
  1358.  
  1359.                 glue--;
  1360.                 *to = fillc;
  1361.                 while (--portion >= 0)
  1362.                     *--to = fillc;
  1363.             }
  1364.         }
  1365.     } else {
  1366.         while (mode_p < &line[CO - 1 - (4 * SG)])
  1367.             *mode_p++ = fillc;
  1368.     }
  1369.  
  1370.     *mode_p = '\0';
  1371.  
  1372.     /* Highlight mode line. */
  1373.     highlighting = NOEFFECT;
  1374.     if (BriteMode) {
  1375.         highlighting = WindowRange(w);
  1376. #ifdef HIGHLIGHTING
  1377.         {
  1378.             char
  1379.                 *p = &line[highlighting->start],
  1380.                 *e = p + highlighting->width;
  1381.  
  1382.             for (; p != e; p++)
  1383.                 if (*p == ' ')
  1384.                     *p = '-';
  1385.         }
  1386. #endif
  1387.     }
  1388.     if (w->w_next == fwind && TimeDisplayed != td) {
  1389.         TimeDisplayed = td;
  1390. #ifdef UNIX
  1391.         SetClockAlarm(YES);
  1392. #endif
  1393.     }
  1394. #ifdef ID_CHAR
  1395.     INSmode(NO);
  1396. #endif
  1397.     if (swrite(line, highlighting, YES))
  1398.         do_cl_eol(linenum);
  1399.     else
  1400.         UpdModLine = YES;
  1401. }
  1402.  
  1403. /* This tries to place the current line of the current window in the
  1404.    center of the window, OR to place it at the arg'th line of the window.
  1405.    This also causes the horizontal position of the line to be centered,
  1406.    if the line needs scrolling, or moved all the way back to the left,
  1407.    if that's possible. */
  1408. void
  1409. RedrawDisplay()
  1410. {
  1411.     int    line;
  1412.     LinePtr    newtop = prev_line((curwind->w_line = curline),
  1413.                 arg_or_default(WSIZE(curwind)/2));
  1414.  
  1415.     if ((line = in_window(curwind, curwind->w_line)) != -1)
  1416.         PhysScreen[line].s_offset = -1;
  1417.     if (newtop == curwind->w_top)
  1418.         ClAndRedraw();
  1419.     else
  1420.         SetTop(curwind, newtop);
  1421. }
  1422.  
  1423. void
  1424. ClAndRedraw()
  1425. {
  1426.     cl_scr(YES);
  1427. }
  1428.  
  1429. void
  1430. NextPage()
  1431. {
  1432.     LinePtr    newline;
  1433.  
  1434.     if (Asking) {
  1435.         /* don't do it */
  1436.     } else if (arg_value() < 0) {
  1437.         negate_arg();
  1438.         PrevPage();
  1439.     } else if (is_non_minus_arg()) {
  1440.         UpScroll();
  1441.     } else {
  1442.         if (in_window(curwind, curwind->w_bufp->b_last) != -1) {
  1443.             rbell();
  1444.             return;
  1445.         }
  1446.         newline = next_line(curwind->w_top, max(1, WSIZE(curwind) - 1));
  1447.         SetTop(curwind, curwind->w_line = newline);
  1448.         if (curwind->w_bufp == curbuf)
  1449.             SetLine(newline);
  1450.     }
  1451. }
  1452.  
  1453. void
  1454. PrevPage()
  1455. {
  1456.     LinePtr    newline;
  1457.  
  1458.     if (Asking) {
  1459.         /* don't do it */
  1460.     } else if (arg_value() < 0) {
  1461.         negate_arg();
  1462.         NextPage();
  1463.     } else if (is_non_minus_arg()) {
  1464.         DownScroll();
  1465.     } else {
  1466.         newline = prev_line(curwind->w_top, max(1, WSIZE(curwind) - 1));
  1467.         SetTop(curwind, curwind->w_line = newline);
  1468.         if (curwind->w_bufp == curbuf)
  1469.             SetLine(newline);
  1470.     }
  1471. }
  1472.  
  1473. void
  1474. UpScroll()
  1475. {
  1476.     SetTop(curwind, next_line(curwind->w_top, arg_value()));
  1477.     if (curwind->w_bufp == curbuf
  1478.     && in_window(curwind, curline) == -1)
  1479.         SetLine(curwind->w_top);
  1480. }
  1481.  
  1482. void
  1483. DownScroll()
  1484. {
  1485.     SetTop(curwind, prev_line(curwind->w_top, arg_value()));
  1486.     if (curwind->w_bufp == curbuf
  1487.     && in_window(curwind, curline) == -1)
  1488.         SetLine(curwind->w_top);
  1489. }
  1490.  
  1491. bool    VisBell = NO;    /* VAR: use visible bell (if possible) */
  1492.  
  1493. void
  1494. rbell()
  1495. {
  1496.     RingBell = YES;
  1497. }
  1498.  
  1499. /* Message prints the null terminated string onto the bottom line of the
  1500.    terminal. */
  1501.  
  1502. void
  1503. message(str)
  1504. char    *str;
  1505. {
  1506.     if (InJoverc)
  1507.         return;
  1508.     UpdMesg = YES;
  1509.     stickymsg = NO;
  1510.     if (str != mesgbuf)
  1511.         null_ncpy(mesgbuf, str, (sizeof mesgbuf) - 1);
  1512. }
  1513.  
  1514. /* End of Window */
  1515.  
  1516. void
  1517. Eow()
  1518. {
  1519.     if (Asking)
  1520.         return;
  1521.     SetLine(next_line(curwind->w_top, WSIZE(curwind) - 1 -
  1522.             min(WSIZE(curwind) - 1, arg_value() - 1)));
  1523.     if (!is_an_arg())
  1524.         Eol();
  1525. }
  1526.  
  1527. /* Beginning of Window */
  1528.  
  1529. void
  1530. Bow()
  1531. {
  1532.     if (Asking)
  1533.         return;
  1534.     SetLine(next_line(curwind->w_top, min(WSIZE(curwind) - 1, arg_value() - 1)));
  1535. }
  1536.  
  1537. /* Typeout Mechanism */
  1538.  
  1539. bool    UseBuffers = NO,    /* VAR: use buffers with Typeout() */
  1540.     TOabort = NO;
  1541.  
  1542. private int    LineNo;    /* screen line for Typeout (if not UseBuffers) */
  1543.  
  1544. private Window    *old_wind;    /* curwind before preempted by typeout to buffer */
  1545.  
  1546. /* This initializes the typeout.  If send-typeout-to-buffers is set
  1547.    the buffer NAME is created (emptied if it already exists) and output
  1548.    goes to the buffer.  Otherwise output is drawn on the screen and
  1549.    erased by TOstop() */
  1550.  
  1551. void
  1552. TOstart(name)
  1553. char    *name;
  1554. {
  1555.     if (UseBuffers) {
  1556.         old_wind = curwind;
  1557.         pop_wind(name, YES, B_SCRATCH);
  1558.     } else
  1559.         DisabledRedisplay = YES;
  1560.     TOabort = NO;
  1561.     LineNo = 0;
  1562. }
  1563.  
  1564. private void
  1565. TOlineFits(s)
  1566. char    *s;
  1567. {
  1568.     i_set(LineNo, 0);
  1569.     (void) swrite(s, NOEFFECT, NO);
  1570.     PhysScreen[LineNo].s_id = NOWHERE_DADDR;
  1571.     cl_eol();
  1572.     flushscreen();
  1573. }
  1574.  
  1575. private void
  1576. TOprompt(s)
  1577. char    *s;
  1578. {
  1579.     if (!TOabort) {
  1580.         register ZXchar    c;
  1581.  
  1582.         TOlineFits(s);
  1583.         c = kbd_getch();
  1584.         TOlineFits("");
  1585.         if (c != ' ') {
  1586.             TOabort = YES;
  1587.             if (c != AbortChar)
  1588.                 kbd_ungetch(c);
  1589.         }
  1590.     }
  1591. }
  1592.  
  1593. #ifdef STDARGS
  1594. void
  1595. Typeout(char *fmt, ...)
  1596. #else
  1597. /*VARARGS1*/ void
  1598. Typeout(fmt, va_alist)
  1599.     char    *fmt;
  1600.     va_dcl
  1601. #endif
  1602. {
  1603.     char    string[MAX_TYPEOUT+1];
  1604.     va_list    ap;
  1605.  
  1606.     va_init(ap, fmt);
  1607.     format(string, sizeof string, fmt, ap);
  1608.     va_end(ap);
  1609.     if (UseBuffers) {
  1610.         ins_str(string);
  1611.         ins_str("\n");
  1612.     } else {
  1613.         char    outbuf[MAXCOLS + PPWIDTH];    /* assert(CO <= MAXCOLS); */
  1614.  
  1615.         if (LineNo == ILI - 2) {
  1616.             TOprompt("--more--");
  1617.             LineNo = 0;
  1618.         }
  1619.         if (!TOabort) {
  1620.             DeTab(string, 0, outbuf, outbuf + CO, NO);
  1621.             TOlineFits(outbuf);
  1622.             LineNo += 1;
  1623.         }
  1624.     }
  1625. }
  1626.  
  1627. void
  1628. TOstop()
  1629. {
  1630.     if (UseBuffers) {
  1631.         ToFirst();
  1632.         SetWind(old_wind);
  1633.     } else {
  1634.         TOprompt("--end--");
  1635.         DisabledRedisplay = NO;
  1636.     }
  1637. }
  1638.