home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / applications / wp / xvi.lha / Xvi_V1.0_Src / screen.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-04  |  34.8 KB  |  1,508 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)screen.c    2.3 (Chris & John Downey) 9/4/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     screen.c
  14. * module function:
  15.     Screen handling functions.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.     Amiga specific optimizations added by Dan Schmelzer
  22.  
  23. ***/
  24.  
  25. #include "xvi.h"
  26.  
  27. /*
  28.  * Size of command buffer - we won't allow anything more
  29.  * to be typed when we get to this limit.
  30.  */
  31. #define    CMDSZ    80
  32.  
  33. /*
  34.  * The following is used to optimise screen updating; we
  35.  * keep a record of the real screen state and compare it
  36.  * with the new version before actually doing any updating.
  37.  *
  38.  * The l_line part is guaranteed to be always null-terminated.
  39.  */
  40. typedef    struct    line_struct    {
  41.     char        *l_line;    /* storage for characters in line */
  42.     int            l_used;        /* number of bytes actually used */
  43.     unsigned int    l_flags;    /* information bits */
  44. } Sline;
  45.  
  46. /*
  47.  * Bit definitions for l_flags.
  48.  */
  49. #define    L_TEXT        0x01        /* is an ordinary text line */
  50. #define    L_MARKER    0x02        /* is a marker line ('@' or '~') */
  51. #define    L_DIRTY        0x04        /* has been modified */
  52. #define    L_MESSAGE    0x08        /* is a message line */
  53. #define    L_COMMAND    0x10        /* is a command line */
  54. #define    L_READONLY    0x20        /* message line for readonly buffer */
  55.  
  56. #define    L_STATUS    (L_MESSAGE | L_COMMAND)        /* is a status line */
  57.  
  58. static    Sline    *new_screen;        /* screen being updated */
  59. static    Sline    *real_screen;        /* state of real screen */
  60.  
  61. /*
  62.  * Status line glitch handling.
  63.  *
  64.  * Some terminals leave a space when changing colour. The number of spaces
  65.  * left is returned by the v_colour_cost() method within the VirtScr, and
  66.  * stored in the colour_cost variable herein - this is not perfect, it should
  67.  * really be in the Xviwin structure, but what the hell.
  68.  *
  69.  * "st_spare_cols" is the number of columns which are not used at the
  70.  * end of the status line; this is to prevent wrapping on this line,
  71.  * as this can do strange things to some terminals.
  72.  */
  73.  
  74. static    int    colour_cost = 0;
  75. static    int    st_spare_cols = 1;
  76.  
  77. static    int    line_to_new P((Xviwin *, Line *, int, long));
  78. static    void    file_to_new P((Xviwin *));
  79. static    void    new_to_screen P((VirtScr *, int, int));
  80. static    void    do_sline P((Xviwin *));
  81. static    void    clrline P((int));
  82.  
  83. /*
  84.  * This routine must be called to set up the screen memory -
  85.  * if it is not, we will probably get a core dump.
  86.  *
  87.  * Note that, at the moment, it must be called with a whole-screen
  88.  * window, i.e. the first window, and only that window, so that the
  89.  * nrows and ncols fields represent the whole screen.
  90.  */
  91. /*ARGSUSED*/
  92. void
  93. init_screen(win)
  94. Xviwin    *win;
  95. {
  96.     static char        *real_area, *new_area;
  97.     register int    count;
  98.     VirtScr        *vs;
  99.     vs = win->w_vs;
  100.     colour_cost = VScolour_cost(vs);
  101.     st_spare_cols = 1 + (colour_cost * 2);
  102.  
  103.     /*
  104.      * If we're changing the size of the screen, free the old stuff.
  105.      */
  106.     if (real_screen != NULL) {
  107.     free((char *) real_screen);
  108.     free((char *) new_screen);
  109.     free(real_area);
  110.     free(new_area);
  111.     }
  112.  
  113.     /*
  114.      * Allocate space for the lines, and for the structure holding
  115.      * information about each line. Notice that we allocate an
  116.      * extra byte at the end of each line for null termination.
  117.      */
  118.     real_screen = (Sline *) malloc((unsigned) VSrows(vs) * sizeof(Sline));
  119.     new_screen = (Sline *) malloc((unsigned) VSrows(vs) * sizeof(Sline));
  120.     real_area = malloc((unsigned) VSrows(vs) * (VScols(vs) + 1));
  121.     new_area = malloc((unsigned) VSrows(vs) * (VScols(vs) + 1));
  122.  
  123.     if (real_screen == NULL || new_screen == NULL ||
  124.     real_area == NULL || new_area == NULL) {
  125.     /* What to do now? */
  126.  
  127.     sys_exit(0);
  128.     }
  129.  
  130.     /*
  131.      * Now assign all the rows ...
  132.      */
  133.     for (count = 0; count < VSrows(vs); count++) {
  134.     register Sline    *rp, *np;
  135.     register int    offset;
  136.  
  137.     rp = &real_screen[count];
  138.     np = &new_screen[count];
  139.  
  140.     offset = count * (VScols(vs) + 1);
  141.  
  142.     rp->l_line = real_area + offset;
  143.     np->l_line = new_area + offset;
  144.     rp->l_line[0] = np->l_line[0] = '\0';
  145.     rp->l_used = np->l_used = 0;
  146.     rp->l_flags = np->l_flags = 0;
  147.     }
  148. }
  149.  
  150. /*
  151.  * Set the L_DIRTY bit for a given line in both real_screen &
  152.  * new_screen if the stored representations are in fact different:
  153.  * otherwise clear it.
  154.  */
  155. static void
  156. mark_dirty(row)
  157.     int        row;
  158. {
  159.     Sline    *rp;
  160.     Sline    *np;
  161.     int        used;
  162.  
  163.     rp = &real_screen[row];
  164.     np = &new_screen[row];
  165.     if (
  166.  
  167.     (rp->l_flags & ~L_DIRTY) != (np->l_flags & ~L_DIRTY)
  168.     ||
  169.     (used = rp->l_used) != np->l_used
  170.     ||
  171.     strncmp(rp->l_line, np->l_line, used) != 0
  172.     ) {
  173.     /*
  174.      * The lines are different.
  175.      */
  176.     np->l_flags |= L_DIRTY;
  177.     rp->l_flags |= L_DIRTY;
  178.     } else {
  179.     rp->l_flags = (np->l_flags &= ~L_DIRTY);
  180.     }
  181. }
  182.  
  183. /*
  184.  * Transfer the specified window line into the "new" screen array, at
  185.  * the given row. Returns the number of screen lines taken up by the
  186.  * logical buffer line lp, or 0 if the line would not fit; this happens
  187.  * with longlines at the end of the screen. In this case, the lines
  188.  * which could not be displayed will have been marked with an '@'.
  189.  */
  190. static int
  191. line_to_new(window, lp, start_row, line)
  192. Xviwin        *window;
  193. Line        *lp;
  194. int        start_row;
  195. long        line;
  196. {
  197.     register unsigned    c;    /* next character from file */
  198.     register Sline    *curr_line;    /* output line - used for efficiency */
  199.     register char    *ltext;        /* pointer to text of line */
  200.     register int    curr_index;    /* current index in line */
  201.     bool_t        eoln;        /* true when line is done */
  202.     char        extra[MAX_TABSTOP];
  203.                     /* Stack for extra characters. */
  204.     int            nextra = 0;    /* index into stack */
  205.     int            srow, scol;    /* current screen row and column */
  206.     int            vcol;        /* virtual column */
  207.  
  208.     ltext = lp->l_text;
  209.     srow = start_row;
  210.     scol = vcol = 0;
  211.     curr_line = &new_screen[srow];
  212.     curr_index = 0;
  213.     eoln = FALSE;
  214.  
  215.     if (Pb(P_number)) {
  216.     static Flexbuf    ftmp;
  217.  
  218.     flexclear(&ftmp);
  219.     (void) lformat(&ftmp, NUM_FMT, line);
  220.     (void) strcpy(curr_line->l_line, flexgetstr(&ftmp));
  221.     scol += NUM_SIZE;
  222.     }
  223.  
  224.     while (!eoln) {
  225.     /*
  226.      * Get the next character to put on the screen.
  227.      */
  228.  
  229.     /*
  230.      * "extra" is a stack containing any extra characters
  231.      * we have to put on the screen - this is for chars
  232.      * which have a multi-character representation, and
  233.      * for the $ at end-of-line in list mode.
  234.      */
  235.  
  236.     if (nextra > 0) {
  237.         c = extra[--nextra];
  238.     } else {
  239.         unsigned    n;
  240.  
  241.         c = (unsigned char) (ltext[curr_index++]);
  242.  
  243.         /*
  244.          * Deal with situations where it is not
  245.          * appropriate just to copy characters
  246.          * straight onto the screen.
  247.          */
  248.         if (c == '\0') {
  249.  
  250.         if (Pb(P_list)) {
  251.             /*
  252.              * Have to show a '$' sign in list mode.
  253.              */
  254.             extra[nextra++] = '\0';
  255.             c = '$';
  256.         }
  257.  
  258.         } else {
  259.         char    *p;
  260.  
  261.         n = vischar((int) c, &p, vcol);
  262.         /*
  263.          * This is a bit paranoid assuming
  264.          * that Pn(P_tabstop) can never be
  265.          * greater than sizeof (extra), but
  266.          * so what.
  267.          */
  268.         if (nextra + n > sizeof extra)
  269.             n = (sizeof extra - nextra);
  270.         /*
  271.          * Stack the extra characters so that
  272.          * they appear in the right order.
  273.          */
  274.         while (n > 1) {
  275.             extra[nextra++] = p[--n];
  276.         }
  277.         c = p[0];
  278.         }
  279.     }
  280.  
  281.     if (c == '\0') {
  282.         /*
  283.          * End of line. Terminate it and finish.
  284.          */
  285.         eoln = TRUE;
  286.         curr_line->l_flags = L_TEXT;
  287.         curr_line->l_used = scol;
  288.         curr_line->l_line[scol] = '\0';
  289.         mark_dirty(srow);
  290.         break;
  291.     } else {
  292.         /*
  293.          * Sline folding.
  294.          */
  295.         if (scol >= window->w_ncols) {
  296.         curr_line->l_flags = L_TEXT;
  297.         curr_line->l_used = scol;
  298.         curr_line->l_line[scol] = '\0';
  299.         mark_dirty(srow);
  300.         srow += 1;
  301.         scol = 0;
  302.         curr_line = &new_screen[srow];
  303.         }
  304.  
  305.         if (srow >= window->w_cmdline) {
  306.         for (srow = start_row; srow < window->w_cmdline; srow++) {
  307.             curr_line = &new_screen[srow];
  308.  
  309.             curr_line->l_flags = L_MARKER;
  310.             curr_line->l_used = 1;
  311.             curr_line->l_line[0] = '@';
  312.             curr_line->l_line[1] = '\0';
  313.             mark_dirty(srow);
  314.         }
  315.         return(0);
  316.         }
  317.  
  318.         /*
  319.          * Store the character in new_screen.
  320.          */
  321.         curr_line->l_line[scol++] = c;
  322.         vcol++;
  323.     }
  324.     }
  325.  
  326.     return((srow - start_row) + 1);
  327. }
  328.  
  329. /*
  330.  * file_to_new()
  331.  *
  332.  * Based on the current value of topline, transfer a screenful
  333.  * of stuff from file to new_screen, and update botline.
  334.  */
  335. static void
  336. file_to_new(win)
  337. register Xviwin    *win;
  338. {
  339.     register int    row;
  340.     register Line    *line;
  341.     register Buffer    *buffer;
  342.     long        lnum;
  343.  
  344.     buffer = win->w_buffer;
  345.     row = win->w_winpos;
  346.     line = win->w_topline;
  347.     lnum = lineno(buffer, line);
  348.  
  349.     while (row < win->w_cmdline && line != buffer->b_lastline) {
  350.     int nlines;
  351.  
  352.     nlines = line_to_new(win, line, row, lnum);
  353.     if (nlines == 0) {
  354.         /*
  355.          * Make it look like we have updated
  356.          * all the screen lines, since they
  357.          * have '@' signs on them.
  358.          */
  359.         row = win->w_cmdline;
  360.         break;
  361.     } else {
  362.         row += nlines;
  363.         line = line->l_next;
  364.         lnum++;
  365.     }
  366.     }
  367.  
  368.     win->w_botline = line;
  369.  
  370.     /*
  371.      * If there are any lines remaining, fill them in
  372.      * with '~' characters.
  373.      */
  374.     for ( ; row < win->w_cmdline; row++) {
  375.     register Sline    *curr_line;
  376.  
  377.     curr_line = &new_screen[row];
  378.  
  379.     curr_line->l_flags = L_MARKER;
  380.     curr_line->l_used = 1;
  381.     curr_line->l_line[0] = '~';
  382.     curr_line->l_line[1] = '\0';
  383.     mark_dirty(row);
  384.     }
  385. }
  386.  
  387. /*
  388.  * new_to_screen
  389.  *
  390.  * Transfer the contents of new_screen to the screen,
  391.  * starting at "start_row", for "nlines" lines,
  392.  * using real_screen to avoid unnecessary output.
  393.  */
  394. static void
  395. new_to_screen(vs, start_row, nlines)
  396. VirtScr        *vs;
  397. int        start_row;
  398. int        nlines;
  399. {
  400.     int         row;                    /* current row */
  401.     int         end_row;                /* row after last one to be updated */
  402.     int         columns;
  403.  
  404.     columns = VScols(vs);
  405.  
  406.     if (!(echo & e_CHARUPDATE)) {
  407.     return;
  408.     }
  409.  
  410.     end_row = start_row + nlines;
  411.  
  412.     VSset_colour(vs, Pn(P_colour));
  413.  
  414.     for (row = start_row; row < end_row; row++) {
  415.     register int            ncol;   /* current column in new_screen */
  416.     register Sline          *new,
  417.                 *real;  /* pointers to current lines */
  418.     register unsigned       nflags;
  419.     unsigned                rflags; /* flags for current lines */
  420.     register char           *ntextp,
  421.                 *rtextp;
  422.                     /* pointers to line text */
  423.     register int            nc;     /* current character in new_screen */
  424.     int            n_used,
  425.                 r_used;
  426.  
  427.     nflags = (new = &new_screen[row])->l_flags;
  428.     rflags = (real = &real_screen[row])->l_flags;
  429.  
  430.     /*
  431.      * If the real and new screens are both "clean",
  432.      * don't bother.
  433.      */
  434.     if (!((nflags & L_DIRTY) || (rflags & L_DIRTY))) {
  435.         continue;
  436.     }
  437.  
  438.     ntextp = new->l_line;
  439.     rtextp = real->l_line;
  440.  
  441.     n_used = new->l_used;
  442.     r_used = real->l_used;
  443.  
  444.     if ((nflags & L_MESSAGE) ||
  445.                 (rflags & L_STATUS) != (nflags & L_STATUS)) {
  446.         /*
  447.          * If it's a message line, or its status (text line,
  448.          * command line or message line) has changed, and either
  449.          * the real line or the new line is "dirty", better update
  450.          * the whole thing; if any colour changes are required,
  451.          * the effects of cursor movements may not be predictable
  452.          * on some terminals.
  453.          */
  454.         VSgoto(vs, row, 0);
  455.         if (nflags & L_STATUS) {
  456.         VSset_colour(vs, (nflags & L_READONLY) ? Pn(P_roscolour) :
  457.                         Pn(P_statuscolour));
  458.         }
  459.         if ((nc = ntextp[0]) != '\0') {
  460. #ifdef OPTIMIZE_AMIGA
  461.         outchar(nc);
  462. #else
  463.         VSputc(vs, row, 0, nc);
  464. #endif
  465.         }
  466.         /*
  467.          * For command lines, only the first character should be
  468.          * highlighted.
  469.          */
  470.         if (nflags & L_COMMAND) {
  471.         VSset_colour(vs, Pn(P_colour));
  472.         }
  473.         if (nc != '\0') {
  474. #ifdef OPTIMIZE_AMIGA
  475.         outstr(&ntextp[1]);
  476. #else
  477.         VSwrite(vs, row, 1, &ntextp[1]);
  478. #endif
  479.         }
  480.  
  481.         /*
  482.          * Clear the rest of the line, if
  483.          * there is any left to be cleared.
  484.          */
  485.         if (n_used < columns) {
  486. #ifdef OPTIMIZE_AMIGA
  487.         erase_line();
  488. #else
  489.         VSclear_line(vs, row, n_used);
  490. #endif
  491.         }
  492.  
  493.         /*
  494.          * Change back to text colour if we have to.
  495.          */
  496.         if ((nflags & L_MESSAGE) != 0) {
  497.         VSset_colour(vs, Pn(P_colour));
  498.         }
  499.  
  500.         (void) strncpy(rtextp, ntextp, (int) (columns - st_spare_cols));
  501.     } else {
  502. #ifdef OPTIMIZE_AMIGA /*______________________________________________*/
  503.         /*
  504.          * First check for identical lines since it will occur
  505.          * under certain circumstances (with the * dirty bit set).
  506.          * We skip updating the line if this is the case.
  507.          * Do only character by character checking with the new line
  508.          * and the line on screen until the first mismatch is found.
  509.          * Then just write the rest of the line.  On the Amiga
  510.          * tty_goto() causes about 9 characters to be sent to the
  511.          * Amiga console device on the average for each character
  512.          * put out using the original algorithm.  It's just faster
  513.          * to send a whole line at a time.
  514.          * Note that we don't use scol here since for the Amiga
  515.          * there are no hidden characters for color, etc; so we use
  516.          * ncol instead.  DAS.
  517.          */
  518.         if (strcmp(ntextp, rtextp) != 0) {
  519.         ncol = 0;
  520.         if ((nflags & L_COMMAND) != 0) {
  521.             /*
  522.              * A command line should have the first character
  523.              * - and only the first character - highlighted.
  524.              */
  525.             VSgoto(vs, row, 0);
  526.             VSset_colour(vs, Pn(P_statuscolour));
  527.             outchar(ntextp[0]);
  528.             VSset_colour(vs, Pn(P_colour));
  529.             ncol++;
  530.         } else {
  531.             /*
  532.              * Skip over any initial matching characters.
  533.              */
  534.             while (ntextp[ncol] == rtextp[ncol])
  535.             ncol++;
  536.             VSgoto(vs, row, ncol);
  537.         }
  538.         (void) strcpy(&rtextp[ncol], &ntextp[ncol]);
  539.  
  540.         /*
  541.          * Write the line to the display.
  542.          */
  543.         if (ncol < columns)
  544.             outstr(&ntextp[ncol]);
  545.         /*
  546.          * Check the old line to see if we still have stuff on 
  547.          * it.  If so we must clear it from the display.
  548.          * We use erase_line() instead of VSclear_line()
  549.          * because we are already at the correct place and
  550.          * VSclear_line() will call tty_goto() unnecessarily.
  551.          */
  552.         if (r_used > n_used)
  553.             erase_line();
  554.         }
  555. #else /*______________________________________________________________*/
  556.         /*
  557.          * Look at each character in the line, comparing
  558.          * the new version with the one on the screen.
  559.          * If they differ, put it out.
  560.          *
  561.          * There is some optimisation here to avoid large
  562.          * use of tty_goto.
  563.          */
  564.         register int        scol;
  565.                 /* current column on physical screen */
  566.         register int        last_ncol;
  567.                 /* last column to be updated */
  568.  
  569.         for (ncol = scol = last_ncol = 0;
  570.                  ncol < n_used && ncol < r_used;
  571.                  (ncol++, scol++)) {
  572.         if ((nc = ntextp[ncol]) != rtextp[ncol]) {
  573.             /*
  574.              * They are different. Get to the right
  575.              * place before putting out the char.
  576.              */
  577.             if (ncol != 0) {
  578.             VSadvise(vs, row, last_ncol + 1,
  579.                         ncol - last_ncol - 1,
  580.                         ntextp + last_ncol + 1);
  581.             } else {
  582.             VSgoto(vs, row, scol);
  583.             /*
  584.              * A command line should have the first character
  585.              * - and only the first character - highlighted.
  586.              */
  587.             if (ncol == 0 && (nflags & L_STATUS) != 0) {
  588.                 VSset_colour(vs, (nflags & L_READONLY) ?
  589.                     Pn(P_roscolour) : Pn(P_statuscolour));
  590.             }
  591.             }
  592.  
  593.             VSputc(vs, row, ncol, nc);
  594.  
  595.             if (ncol == 0 && (nflags & L_COMMAND) != 0) {
  596.             VSset_colour(vs, Pn(P_colour));
  597.             }
  598.             last_ncol = ncol;
  599.             rtextp[ncol] = nc;
  600.         }
  601.         if (ncol == 0 && (nflags & L_COMMAND) != 0) {
  602.             scol += (colour_cost * 2);
  603.         }
  604.         }
  605.  
  606.         if (n_used > r_used) {
  607.         /*
  608.          * We have got to the end of the previous
  609.          * screen line; if there is anything left,
  610.          * we should just display it.
  611.          */
  612.         (void) strcpy(&rtextp[ncol], &ntextp[ncol]);
  613.         if (ncol == 0 && (nflags & L_COMMAND) != 0) {
  614.             /*
  615.              * A command line should have the first character
  616.              * - and only the first character - highlighted.
  617.              */
  618.             VSgoto(vs, row, 0);
  619.             VSset_colour(vs, Pn(P_statuscolour));
  620.             VSputc(vs, row, 0, ntextp[0]);
  621.             VSset_colour(vs, Pn(P_colour));
  622.             ncol = 1;
  623.         } else {
  624.             /*
  625.              * Skip over initial whitespace.
  626.              */
  627.             while (ntextp[ncol] == ' ') {
  628.             ncol++;
  629.             scol++;
  630.             }
  631.         }
  632.         if (ncol < columns)
  633.             VSwrite(vs, row, scol, &ntextp[ncol]);
  634.         } else if (r_used > n_used) {
  635.         /*
  636.          * We have got to the end of the new screen
  637.          * line, but the old one still has stuff on
  638.          * it. We must therefore clear it.
  639.          */
  640.         VSclear_line(vs, row, scol);
  641.         }
  642. #endif /*_____________________________________________________________*/
  643.     }
  644.  
  645.     real->l_line[n_used] = '\0';
  646.     real->l_used = n_used;
  647.  
  648.     /*
  649.      * The real screen line is a message or command line if the
  650.      * newly-updated one was. Otherwise, it isn't.
  651.      *
  652.      * Both the new and real screens may now be considered
  653.      * "clean".
  654.      */
  655.     real->l_flags = (
  656.               /*
  657.                * Turn these flags off first ...
  658.                */
  659.               (rflags & ~(L_STATUS | L_DIRTY))
  660.               /*
  661.                * ... then set whatever L_STATUS flags are
  662.                * set in new_screen.
  663.                */
  664.               | (nflags & L_STATUS)
  665.             );
  666.     new->l_flags &= ~L_DIRTY;
  667.     }
  668. }
  669.  
  670. /*
  671.  * Update the status line of the given window, and cause the status
  672.  * line to be written out. Note that we call new_to_screen() to cause
  673.  * the output to be generated; since there will be no other changes,
  674.  * only the status line will be changed on the screen.
  675.  */
  676. void
  677. update_sline(win)
  678. Xviwin    *win;
  679. {
  680.     do_sline(win);
  681.     new_to_screen(win->w_vs, (int) win->w_cmdline, 1);
  682.     VSflush(win->w_vs);
  683. }
  684.  
  685. /*
  686.  * Update the status line of the given window,
  687.  * from the one in win->w_statusline.
  688.  */
  689. static void
  690. do_sline(win)
  691. Xviwin    *win;
  692. {
  693.     register char    *from;
  694.     register char    *to;
  695.     register char    *end;
  696.     Sline        *slp;
  697.  
  698.     from = flexgetstr(&win->w_statusline);
  699.     slp = &new_screen[win->w_cmdline];
  700.     to = slp->l_line;
  701.     end = to + win->w_ncols - st_spare_cols;
  702.  
  703.     while (*from != '\0' && to < end) {
  704.     *to++ = *from++;
  705.     }
  706.  
  707.     /*
  708.      * Fill with spaces, and null-terminate.
  709.      */
  710.     while (to < end) {
  711.     *to++ = ' ';
  712.     }
  713.     *to = '\0';
  714.  
  715.     slp->l_used = win->w_ncols - st_spare_cols;
  716.     slp->l_flags = L_MESSAGE;
  717.     if (is_readonly(win->w_buffer)) {
  718.     slp->l_flags |= L_READONLY;
  719.     }
  720.     mark_dirty(win->w_cmdline);
  721. }
  722.  
  723. void
  724. update_cline(win)
  725. Xviwin    *win;
  726. {
  727.     Sline    *clp;
  728.     unsigned width, maxwidth;
  729.  
  730.     clp = &new_screen[win->w_cmdline];
  731.  
  732.     maxwidth = win->w_ncols - st_spare_cols;
  733.     if ((width = flexlen(&win->w_statusline)) > maxwidth) {
  734.     width = maxwidth;
  735.     }
  736.     (void) strncpy(clp->l_line, flexgetstr(&win->w_statusline),
  737.                 (int) width);
  738.     clp->l_used = width;
  739.     clp->l_line[width] = '\0';
  740.     clp->l_flags = (L_COMMAND | L_DIRTY);
  741.     /*
  742.      * We don't bother calling mark_dirty() here: it isn't worth
  743.      * it because the line's contents have almost certainly
  744.      * changed.
  745.      */
  746.     new_to_screen(win->w_vs, (int) win->w_cmdline, 1);
  747.     VSflush(win->w_vs);
  748. }
  749.  
  750. /*
  751.  * updateline() - update the line the cursor is on
  752.  *
  753.  * Updateline() is called after changes that only affect the line that
  754.  * the cursor is on. This improves performance tremendously for normal
  755.  * insert mode operation. The only thing we have to watch for is when
  756.  * the cursor line grows or shrinks around a row boundary. This means
  757.  * we have to repaint other parts of the screen appropriately.
  758.  */
  759. void
  760. updateline(window)
  761. Xviwin    *window;
  762. {
  763.     Line    *currline;
  764.     int        nlines;
  765.     int        curs_row;
  766.  
  767.     currline = window->w_cursor->p_line;
  768.  
  769.     /*
  770.      * Find out which screen line the cursor line starts on.
  771.      * This is not necessarily the same as window->w_row,
  772.      * because longlines are different.
  773.      */
  774.     if (plines(window, currline) > 1) {
  775.     curs_row = (int) cntplines(window, window->w_topline, currline);
  776.     } else {
  777.     curs_row = window->w_row;
  778.     }
  779.  
  780.     nlines = line_to_new(window, currline,
  781.             (int) (curs_row + window->w_winpos),
  782.             (long) lineno(window->w_buffer, currline));
  783.  
  784.     if (nlines != window->w_c_line_size) {
  785.     update_buffer(window->w_buffer);
  786.     } else {
  787.     new_to_screen(window->w_vs,
  788.             (int) (curs_row + window->w_winpos), nlines);
  789.     VSflush(window->w_vs);
  790.     }
  791. }
  792.  
  793. /*
  794.  * Completely update the representation of the given window.
  795.  */
  796. void
  797. update_window(window)
  798. Xviwin    *window;
  799. {
  800.     if (window->w_nrows > 1) {
  801.     file_to_new(window);
  802.     new_to_screen(window->w_vs,
  803.             (int) window->w_winpos, (int) window->w_nrows);
  804.     VSflush(window->w_vs);
  805.     }
  806. }
  807.  
  808. /*
  809.  * Update all windows.
  810.  */
  811. void
  812. update_all()
  813. {
  814.     Xviwin    *w = curwin;
  815.  
  816.     do {
  817.     if (w->w_nrows > 1) {
  818.         file_to_new(w);
  819.     }
  820.     if (w->w_nrows > 0) {
  821.         do_sline(w);
  822.     }
  823.     } while ((w = next_window(w)) != curwin);
  824.  
  825.     new_to_screen(w->w_vs, 0, (int) VSrows(w->w_vs));
  826.     VSflush(w->w_vs);
  827. }
  828.  
  829. /*
  830.  * Totally redraw the screen.
  831.  */
  832. void
  833. redraw_screen()
  834. {
  835.     if (curwin != NULL) {
  836.     clear(curwin);
  837.     update_all();
  838.     }
  839. }
  840.  
  841. void
  842. clear(win)
  843. Xviwin    *win;
  844. {
  845.     register int    row;
  846.     int        nrows;
  847.  
  848.     nrows = VSrows(win->w_vs);
  849.  
  850.     VSset_colour(win->w_vs, Pn(P_colour));
  851.     VSclear_all(win->w_vs);
  852.  
  853.     /*
  854.      * Clear the real screen lines, and mark them as modified.
  855.      */
  856.     for (row = 0; row < nrows; row++) {
  857.     clrline(row);
  858.     }
  859. }
  860.  
  861. /*
  862.  * The rest of the routines in this file perform screen manipulations.
  863.  * The given operation is performed physically on the screen. The
  864.  * corresponding change is also made to the internal screen image. In
  865.  * this way, the editor anticipates the effect of editing changes on
  866.  * the appearance of the screen. That way, when we call screenupdate a
  867.  * complete redraw isn't usually necessary. Another advantage is that
  868.  * we can keep adding code to anticipate screen changes, and in the
  869.  * meantime, everything still works.
  870.  */
  871.  
  872. /*
  873.  * s_ins(win, row, nlines) - insert 'nlines' lines at 'row'
  874.  */
  875. void
  876. s_ins(win, row, nlines)
  877. Xviwin        *win;
  878. register int    row;
  879. int        nlines;
  880. {
  881.     register int    from, to;
  882.     int            count;
  883.     VirtScr        *vs;
  884.  
  885.     if (!(echo & e_SCROLL))
  886.     return;
  887.  
  888. #ifdef OPTIMIZE_AMIGA
  889.     /* On Amiga multiple line scrolling is slow, limit it to 2 lines. */
  890.     if (nlines > 2)
  891.     return;
  892. #endif
  893.  
  894.     /*
  895.      * There's no point in scrolling more lines than there are
  896.      * (below row) in the window, or in scrolling 0 lines.
  897.      */
  898.     if (nlines == 0 || nlines + row >= win->w_nrows - 1)
  899.     return;
  900.  
  901.     /*
  902.      * The row specified is relative to the top of the window;
  903.      * add the appropriate offset to make it into a screen row.
  904.      */
  905.     row += win->w_winpos;
  906.  
  907.     /*
  908.      * Note that we avoid the use of 1-line scroll regions; these
  909.      * only ever occur at the bottom of a window, and it is better
  910.      * just to leave the line to be updated in the best way by
  911.      * update{line,screen}.
  912.      */
  913.     if (nlines == 1 && row + 1 == win->w_cmdline) {
  914.     return;
  915.     }
  916.  
  917.     vs = win->w_vs;
  918.  
  919.     if (vs->v_scroll != NULL) {
  920.     if (!VSscroll(vs, row, (int) win->w_cmdline - 1, -nlines)) {
  921.         /*
  922.          * Can't scroll what we were asked to - try scrolling
  923.          * the whole window including the status line.
  924.          */
  925.         VSclear_line(vs, (int) win->w_cmdline, 0);
  926.         clrline(win->w_cmdline);
  927.         if (!VSscroll(vs, row, (int) win->w_cmdline, -nlines)) {
  928.         /*
  929.          * Failed.
  930.          */
  931.         return;
  932.         }
  933.     }
  934.     } else {
  935.     return;
  936.     }
  937.  
  938.     /*
  939.      * Update the stored screen image so it matches what has
  940.      * happened on the screen.
  941.      */
  942.  
  943.     /*
  944.      * Move section of text down to the bottom.
  945.      *
  946.      * We do this by rearranging the pointers within the Slines,
  947.      * rather than copying the characters.
  948.      */
  949.     for (to = win->w_cmdline - 1, from = to - nlines; from >= row;
  950.                             --from, --to) {
  951.     register char    *temp;
  952.  
  953.     temp = real_screen[to].l_line;
  954.     real_screen[to].l_line = real_screen[from].l_line;
  955.     real_screen[from].l_line = temp;
  956.     real_screen[to].l_used = real_screen[from].l_used;
  957.     }
  958.  
  959.     /*
  960.      * Clear the newly inserted lines.
  961.      */
  962.     for (count = row; count < row + nlines; count++) {
  963.     clrline(count);
  964.     }
  965. }
  966.  
  967. /*
  968.  * s_del(win, row, nlines) - delete 'nlines' lines starting at 'row'.
  969.  */
  970. void
  971. s_del(win, row, nlines)
  972. register Xviwin        *win;
  973. int            row;
  974. int            nlines;
  975. {
  976.     register int    from, to;
  977.     int            count;
  978.     VirtScr        *vs;
  979.  
  980.     if (!(echo & e_SCROLL))
  981.     return;
  982.  
  983. #ifdef OPTIMIZE_AMIGA
  984.     if (nlines > 1)
  985.     return;        /* On Amiga multiple line scrolling is slow. */
  986. #endif
  987.  
  988.     /*
  989.      * There's no point in scrolling more lines than there are
  990.      * (below row) in the window, or in scrolling 0 lines.
  991.      */
  992.     if (nlines == 0 || nlines + row >= win->w_nrows - 1)
  993.     return;
  994.  
  995.     /*
  996.      * The row specified is relative to the top of the window;
  997.      * add the appropriate offset to make it into a screen row.
  998.      */
  999.     row += win->w_winpos;
  1000.  
  1001.     /*
  1002.      * We avoid the use of 1-line scroll regions, since they don't
  1003.      * work with many terminals, especially if we are using
  1004.      * (termcap) DO to scroll the region.
  1005.      */
  1006.     if (nlines == 1 && row + 1 == win->w_cmdline) {
  1007.     return;
  1008.     }
  1009.  
  1010.     vs = win->w_vs;
  1011.  
  1012.     if (vs->v_scroll != NULL) {
  1013.     if (!VSscroll(vs, row, (int) win->w_cmdline - 1, nlines)) {
  1014.         /*
  1015.          * Can't scroll what we were asked to - try scrolling
  1016.          * the whole window including the status line.
  1017.          */
  1018.         VSclear_line(vs, (int) win->w_cmdline, 0);
  1019.         clrline(win->w_cmdline);
  1020.         if (!VSscroll(vs, row, (int) win->w_cmdline, nlines)) {
  1021.         /*
  1022.          * Failed.
  1023.          */
  1024.         return;
  1025.         }
  1026.     }
  1027.     } else {
  1028.     return;
  1029.     }
  1030.  
  1031.     /*
  1032.      * Update the stored screen image so it matches what has
  1033.      * happened on the screen.
  1034.      */
  1035.  
  1036.     /*
  1037.      * Move section of text up from the bottom.
  1038.      *
  1039.      * We do this by rearranging the pointers within the Slines,
  1040.      * rather than copying the characters.
  1041.      */
  1042.     for (to = row, from = to + nlines;
  1043.      from < win->w_cmdline;
  1044.      from++, to++) {
  1045.     register char    *temp;
  1046.  
  1047.     temp = real_screen[to].l_line;
  1048.     real_screen[to].l_line = real_screen[from].l_line;
  1049.     real_screen[from].l_line = temp;
  1050.     real_screen[to].l_used = real_screen[from].l_used;
  1051.     }
  1052.  
  1053.     /*
  1054.      * Clear the deleted lines.
  1055.      */
  1056.     for (count = win->w_cmdline - nlines; count < win->w_cmdline; count++) {
  1057.     clrline(count);
  1058.     }
  1059. }
  1060.  
  1061. /*
  1062.  * Insert a character at the cursor position, updating the screen as
  1063.  * necessary. Note that this routine doesn't have to do anything, as
  1064.  * the screen will eventually be correctly updated anyway; it's just
  1065.  * here for speed of screen updating.
  1066.  */
  1067. void
  1068. s_inschar(window, newchar)
  1069. Xviwin            *window;
  1070. int            newchar;
  1071. {
  1072.     register char    *curp;
  1073.     register char    *cp;
  1074.     register char    *sp;
  1075.     Sline        *rp;
  1076.     Posn        *pp;
  1077.     VirtScr        *vs;        /* the VirtScr for this window */
  1078.     char        *newstr;    /* printable string for newchar */
  1079.     register unsigned    nchars;        /* number of  chars in newstr */
  1080.     unsigned        currow;
  1081.     unsigned        curcol;
  1082.     unsigned        columns;
  1083.  
  1084.     vs = window->w_vs;
  1085.     if (vs->v_insert == NULL)
  1086.     return;
  1087.  
  1088.     if (!(echo & e_CHARUPDATE))
  1089.     return;
  1090.  
  1091.     pp = window->w_cursor;
  1092.  
  1093.     /*
  1094.      * If we are at (or near) the end of the line, it's not worth
  1095.      * the bother. Define near as 0 or 1 characters to be moved.
  1096.      */
  1097.     cp = pp->p_line->l_text + pp->p_index;
  1098.     if (*cp == '\0' || *(cp+1) == '\0')
  1099.     return;
  1100.  
  1101.     curcol = window->w_col;
  1102.  
  1103.     /*
  1104.      * If the cursor is on a longline, and not on the last actual
  1105.      * screen line of that longline, we can't do it.
  1106.      */
  1107.     if (window->w_c_line_size > 1 && curcol != window->w_virtcol)
  1108.     return;
  1109.  
  1110.     nchars = vischar(newchar, &newstr, curcol);
  1111.  
  1112.     /*
  1113.      * And don't bother if we are (or will be) at the last screen column.
  1114.      */
  1115.     columns = window->w_ncols;
  1116.     if (curcol + nchars >= columns)
  1117.     return;
  1118.  
  1119.     /*
  1120.      * Also, trying to push tabs rightwards doesn't work very
  1121.      * well. It's usually better not to use the insert character
  1122.      * sequence because in most cases we'll only have to update
  1123.      * the line as far as the tab anyway.
  1124.      */
  1125.     if ((!Pb(P_list) && Pb(P_tabs)) && strchr(cp, '\t') != NULL) {
  1126.     return;
  1127.     }
  1128.  
  1129.     /*
  1130.      * Okay, we can do it.
  1131.      */
  1132.     currow = window->w_row;
  1133.  
  1134.     VSinsert(vs, window->w_winpos + currow, curcol, newstr);
  1135.  
  1136.     /*
  1137.      * Update real_screen.
  1138.      */
  1139.     rp = &real_screen[window->w_winpos + currow];
  1140.     curp = &rp->l_line[curcol];
  1141.     if ((rp->l_used += nchars) > columns)
  1142.     rp->l_used = columns;
  1143.     cp = &rp->l_line[rp->l_used - 1];
  1144.     cp[1] = '\0';
  1145.     if (cp - curp >= nchars)
  1146.     {
  1147.     sp = cp - nchars;
  1148.     for (;;) {
  1149.         *cp-- = *sp;
  1150.         if (sp-- <= curp)
  1151.         break;
  1152.     }
  1153.     }
  1154.  
  1155.     /*
  1156.      * This is the string we've just inserted.
  1157.      */
  1158.     sp = newstr;
  1159.     while (nchars-- > 0) {
  1160.     *curp++ = *sp++;
  1161.     }
  1162. }
  1163.  
  1164. void
  1165. wind_goto(win)
  1166. Xviwin    *win;
  1167. {
  1168.     VirtScr    *vs;
  1169.  
  1170.     if (echo & e_CHARUPDATE) {
  1171.     vs = win->w_vs;
  1172.     VSgoto(vs, (int) win->w_winpos + win->w_row, win->w_col);
  1173.     VSflush(vs);
  1174.     }
  1175. }
  1176.  
  1177. static    char        inbuf[CMDSZ];        /* command input buffer */
  1178. static    unsigned int    inpos = 0;        /* posn of next input char */
  1179. static    unsigned char    colposn[CMDSZ];        /* holds n chars per char */
  1180.  
  1181. /*
  1182.  * cmd_init(window, firstch)
  1183.  *
  1184.  * Initialise command line input.
  1185.  */
  1186. void
  1187. cmd_init(win, firstch)
  1188. Xviwin    *win;
  1189. int    firstch;
  1190. {
  1191.     if (inpos > 0) {
  1192.     show_error(win, "Internal error: re-entered command line input mode");
  1193.     return;
  1194.     }
  1195.  
  1196.     State = CMDLINE;
  1197.  
  1198.     flexclear(&win->w_statusline);
  1199.     (void) flexaddch(&win->w_statusline, firstch);
  1200.     inbuf[0] = firstch;
  1201.     inpos = 1;
  1202.     update_cline(win);
  1203.     colposn[0] = 0;
  1204. }
  1205.  
  1206. /*
  1207.  * cmd_input(window, character)
  1208.  *
  1209.  * Deal with command line input. Takes an input character and returns
  1210.  * one of cmd_CANCEL (meaning they typed ESC or deleted past the
  1211.  * prompt character), cmd_COMPLETE (indicating that the command has
  1212.  * been completely input), or cmd_INCOMPLETE (indicating that command
  1213.  * line is still the right mode to be in).
  1214.  *
  1215.  * Once cmd_COMPLETE has been returned, it is possible to call
  1216.  * get_cmd(win) to obtain the command line.
  1217.  */
  1218. Cmd_State
  1219. cmd_input(win, ch)
  1220. Xviwin    *win;
  1221. int    ch;
  1222. {
  1223.     static bool_t    literal_next = FALSE;
  1224.  
  1225.     if (!literal_next) {
  1226.     switch (ch) {
  1227.     case CTRL('V'):
  1228.         literal_next = TRUE;
  1229.         return(cmd_INCOMPLETE);
  1230.  
  1231.     case '\n':        /* end of line */
  1232.     case '\r':
  1233.         inbuf[inpos] = '\0';    /* terminate input line */
  1234.         inpos = 0;
  1235.         State = NORMAL;        /* return state to normal */
  1236.         do_sline(win);        /* line is now a message line */
  1237.         return(cmd_COMPLETE);    /* and indicate we are done */
  1238.  
  1239.     case '\b':        /* backspace or delete */
  1240.     case DEL:
  1241.     {
  1242.         unsigned len;
  1243.  
  1244.         inbuf[--inpos] = '\0';
  1245.         len = colposn[inpos - 1] + 1;
  1246.         while (flexlen(&win->w_statusline) > len)
  1247.         (void) flexrmchar(&win->w_statusline);
  1248.         update_cline(win);
  1249.         if (inpos == 0) {
  1250.         /*
  1251.          * Deleted past first char;
  1252.          * go back to normal mode.
  1253.          */
  1254.         State = NORMAL;
  1255.         return(cmd_CANCEL);
  1256.         }
  1257.         return(cmd_INCOMPLETE);
  1258.     }
  1259.  
  1260.     case '\033':
  1261. #ifdef AMIGA
  1262.         /* This seems to what comment above really meant for ESC! */
  1263.         inpos = 0;
  1264.         inbuf[0] = '\0';
  1265.         flexclear(&win->w_statusline);
  1266.         (void) flexaddch(&win->w_statusline, inbuf[0]);
  1267.         update_cline(win);
  1268.         State = NORMAL;
  1269.         return(cmd_CANCEL);
  1270. #endif
  1271.     case EOF:
  1272.     case CTRL('U'):        /* line kill */
  1273.         inpos = 1;
  1274.         inbuf[1] = '\0';
  1275.         flexclear(&win->w_statusline);
  1276.         (void) flexaddch(&win->w_statusline, inbuf[0]);
  1277.         update_cline(win);
  1278.         return(cmd_INCOMPLETE);
  1279.  
  1280.     default:
  1281.         break;
  1282.     }
  1283.     }
  1284.  
  1285.     literal_next = FALSE;
  1286.  
  1287.     if (inpos >= sizeof(inbuf) - 1) {
  1288.     /*
  1289.      * Must not overflow buffer.
  1290.      */
  1291.     beep(win);
  1292.     } else {
  1293.     unsigned    curposn;
  1294.     unsigned    w;
  1295.     char        *p;
  1296.  
  1297.     curposn = colposn[inpos - 1];
  1298.     w = vischar(ch, &p, (int) curposn);
  1299.     if (curposn + w >= win->w_ncols - 1) {
  1300.         beep(win);
  1301.     } else {
  1302.         colposn[inpos] = curposn + w;
  1303.         inbuf[inpos++] = ch;
  1304.         (void) lformat(&win->w_statusline, "%s", p);
  1305.         update_cline(win);
  1306.     }
  1307.     }
  1308.  
  1309.     return(cmd_INCOMPLETE);
  1310. }
  1311.  
  1312. /*ARGSUSED*/
  1313. char *
  1314. get_cmd(win)
  1315. Xviwin    *win;
  1316. {
  1317.     return(inbuf);
  1318. }
  1319.  
  1320. /*ARGSUSED*/
  1321. void
  1322. gotocmd(win, clr)
  1323. Xviwin    *win;
  1324. bool_t    clr;
  1325. {
  1326.     VirtScr    *vs;
  1327.  
  1328.     vs = win->w_vs;
  1329.     if (clr) {
  1330.     VSclear_line(vs, (int) win->w_cmdline, 0);
  1331.     }
  1332.     VSgoto(vs, (int) win->w_cmdline, 0);
  1333.     VSflush(vs);
  1334. }
  1335.  
  1336. /*
  1337.  * Display a prompt on the bottom line of the screen.
  1338.  */
  1339. void
  1340. prompt(message)
  1341.     char    *message;
  1342. {
  1343.     VirtScr    *vs;
  1344.     int    row;
  1345.  
  1346.     vs = curwin->w_vs;
  1347.  
  1348.     row = VSrows(vs) - 1;
  1349.     VSgoto(vs, row, 0);
  1350.     VSset_colour(vs, Pn(P_statuscolour));
  1351.     VSwrite(vs, row, 0, message);
  1352.     VSset_colour(vs, Pn(P_colour));
  1353.     VSgoto(vs, row, strlen(message));
  1354.     VSflush(vs);
  1355. }
  1356.  
  1357. /*
  1358.  * Sound the alert.
  1359.  */
  1360. void
  1361. beep(window)
  1362. register Xviwin *window;
  1363. {
  1364.     VSbeep(window->w_vs);
  1365. }
  1366.  
  1367. static char    *(*disp_func) P((void));
  1368. static int    disp_colwidth;
  1369. static int    disp_maxcol;
  1370. static bool_t    disp_listmode;
  1371.  
  1372. /*
  1373.  * Start off "display" mode. The "func" argument is a function pointer
  1374.  * which will be called to obtain each subsequent string to display.
  1375.  * The function returns NULL when no more lines are available.
  1376.  */
  1377. void
  1378. disp_init(win, func, colwidth, listmode)
  1379. Xviwin        *win;
  1380. char        *(*func) P((void));
  1381. int        colwidth;
  1382. bool_t        listmode;
  1383. {
  1384.     State = DISPLAY;
  1385.     disp_func = func;
  1386.     if (colwidth > win->w_ncols)
  1387.     colwidth = win->w_ncols;
  1388.     disp_colwidth = colwidth;
  1389.     disp_maxcol = (win->w_ncols / colwidth) * colwidth;
  1390.     disp_listmode = listmode;
  1391.     (void) disp_screen(win);
  1392. }
  1393.  
  1394. /*
  1395.  * Display text in glass-teletype mode, in approximately the style of
  1396.  * the more(1) program.
  1397.  *
  1398.  * If the return value from (*disp_func)() is NULL, it means we've got
  1399.  * to the end of the text to be displayed, so we wait for another key
  1400.  * before redisplaying our editing screen.
  1401.  */
  1402. bool_t
  1403. disp_screen(win)
  1404. Xviwin    *win;
  1405. {
  1406.     int        row;    /* current screen row */
  1407.     int        col;    /* current screen column */
  1408.     static bool_t    finished = FALSE;
  1409.     VirtScr        *vs;
  1410.  
  1411.     vs = win->w_vs;
  1412.  
  1413.     if (finished || kbdintr) {
  1414.     /*
  1415.      * Clear the screen, and then ensure that the window
  1416.      * on the current buffer is in the right place and
  1417.      * updated; finally update the whole screen.
  1418.      */
  1419.     clear(win);
  1420.     move_window_to_cursor(win);
  1421.     update_all();
  1422.     State = NORMAL;
  1423.     finished = FALSE;
  1424.     if (kbdintr) {
  1425.         imessage = TRUE;
  1426.     }
  1427.     return(TRUE);
  1428.     }
  1429.  
  1430.     VSclear_all(vs);
  1431.  
  1432.     for (col = 0; col < disp_maxcol; col += disp_colwidth) {
  1433.     for (row = 0; row < VSrows(vs) - 1; row++) {
  1434.         static char    *line;
  1435.         int        width;
  1436.  
  1437.         if (line == NULL && (line = (*disp_func)()) == NULL) {
  1438.         /*
  1439.          * We've got to the end.
  1440.          */
  1441.         prompt("[Hit return to continue] ");
  1442.         finished = TRUE;
  1443.         return(FALSE);
  1444.         }
  1445.  
  1446.         for (width = 0; *line != '\0'; line++) {
  1447.         char        *p;
  1448.         unsigned    w;
  1449.  
  1450.         w = vischar(*line, &p, disp_listmode ? -1 : width);
  1451.  
  1452.         if ((width += w) <= disp_colwidth) {
  1453.             VSwrite(vs, row, col + width - w, p);
  1454.         } else {
  1455.             /*
  1456.              * The line is too long, so we
  1457.              * have to wrap around to the
  1458.              * next screen line.
  1459.              */
  1460.             break;
  1461.         }
  1462.         }
  1463.  
  1464.         if (*line == '\0') {
  1465.         if (disp_listmode) {
  1466.             /*
  1467.              * In list mode, we have to
  1468.              * display a '$' to show the
  1469.              * end of a line.
  1470.              */
  1471.             if (width < disp_colwidth) {
  1472.             VSputc(vs, row, col + width, '$');
  1473.             } else {
  1474.             /*
  1475.              * We have to wrap it
  1476.              * to the next screen
  1477.              * line.
  1478.              */
  1479.             continue;
  1480.             }
  1481.         }
  1482.         /*
  1483.          * If we're not in list mode, or we
  1484.          * were able to display the '$', we've
  1485.          * finished with this line.
  1486.          */
  1487.         line = NULL;
  1488.         }
  1489.     }
  1490.     }
  1491.  
  1492.     prompt("[More] ");
  1493.  
  1494.     return(FALSE);
  1495. }
  1496.  
  1497. /*
  1498.  * Clear the given line, marking it as dirty.
  1499.  */
  1500. static void
  1501. clrline(line)
  1502. int    line;
  1503. {
  1504.     real_screen[line].l_used = 0;
  1505.     real_screen[line].l_line[0] = '\0';
  1506.     mark_dirty(line);
  1507. }
  1508.