(root)/
texinfo-7.1/
info/
display.c
       1  /* display.c -- How to display Info windows.
       2  
       3     Copyright 1993-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17  
      18     Originally written by Brian Fox. */
      19  
      20  #include "info.h"
      21  #include "display.h"
      22  #include "session.h"
      23  #include "util.h"
      24  #include "tag.h"
      25  #include "signals.h"
      26  #include "variables.h"
      27  
      28  static void free_display (DISPLAY_LINE **display);
      29  static DISPLAY_LINE **make_display (int width, int height);
      30  
      31  /* An array of display lines which tell us what is currently visible on
      32     the display.  */
      33  DISPLAY_LINE **the_display = NULL;
      34  
      35  /* Non-zero means do no output. */
      36  int display_inhibited = 0;
      37  
      38  /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */
      39  void
      40  display_initialize_display (int width, int height)
      41  {
      42    free_display (the_display);
      43    the_display = make_display (width, height);
      44    display_clear_display (the_display);
      45  }
      46  
      47  /* Clear all of the lines in DISPLAY making the screen blank. */
      48  void
      49  display_clear_display (DISPLAY_LINE **display)
      50  {
      51    register int i;
      52  
      53    signal_block_winch ();
      54    for (i = 0; display[i]; i++)
      55      {
      56        display[i]->text[0] = '\0';
      57        display[i]->textlen = 0;
      58        display[i]->inverse = 0;
      59      }
      60    signal_unblock_winch ();
      61  }
      62  
      63  /* Non-zero if we didn't completely redisplay a window. */
      64  int display_was_interrupted_p = 0;
      65  
      66  /* Check each window on the screen, and update it if it needs updating. */
      67  void
      68  display_update_display (void)
      69  {
      70    register WINDOW *win;
      71  
      72    /* Block window resize signals (SIGWINCH) while accessing the the_display
      73       object, because the signal handler may reallocate it out from under our
      74       feet. */
      75    signal_block_winch ();
      76    display_was_interrupted_p = 0;
      77  
      78    for (win = windows; win; win = win->next)
      79      {
      80        /* Only re-display visible windows which need updating. */
      81        if ((win->flags & W_WindowVisible) == 0
      82            || (win->flags & W_UpdateWindow) == 0
      83            || win->height == 0)
      84          continue;
      85  
      86        display_update_one_window (win);
      87        if (display_was_interrupted_p)
      88          break;
      89      }
      90  
      91    /* Always update the echo area. */
      92    display_update_one_window (the_echo_area);
      93    signal_unblock_winch ();
      94  }
      95  
      96  /* Return the screen column of where to write to screen to update line to
      97     match A, given that B contains the current state of the line.  *PPOS gets
      98     the offset into the string A to write from. */
      99  static int
     100  find_diff (const char *a, size_t alen, const char *b, size_t blen, int *ppos)
     101  {
     102    mbi_iterator_t itra, itrb;
     103    int i;
     104    int pos = 0;
     105    int first_escape = -1;
     106    int escape_pos = -1;
     107    
     108    for (i = 0, mbi_init (itra, a, alen), mbi_init (itrb, b, blen);
     109         mbi_avail (itra) && mbi_avail (itrb);
     110         i += wcwidth (itra.cur.wc), mbi_advance (itra), mbi_advance (itrb))
     111      {
     112        if (mb_cmp (mbi_cur (itra), mbi_cur (itrb)))
     113          break;
     114  
     115        if (first_escape == -1 && *mbi_cur_ptr (itra) == '\033')
     116          {
     117            first_escape = i;
     118            escape_pos = pos;
     119          }
     120        pos += mb_len (mbi_cur (itra));
     121      }
     122  
     123    if (mbi_avail (itra) || mbi_avail (itrb))
     124      {
     125        if (first_escape != -1)
     126          {
     127            *ppos = escape_pos;
     128            return first_escape;
     129          }
     130        else
     131          {
     132            /* If there was a difference in the line, and there was an escape
     133               character, return the position of the escape character, as it could
     134               start a terminal escape sequence. */
     135            *ppos = pos;
     136            return i;
     137          }
     138      }
     139  
     140    /* Otherwise, no redrawing is required. */
     141    return -1;
     142  }
     143  
     144  /* Update line PL_NUM of the screen to be PRINTED_LINE, which is PL_BYTES long
     145     and takes up PL_CHARS columns. */
     146  static int
     147  display_update_line (long pl_num, char *printed_line,
     148                       long pl_bytes, long pl_chars)
     149  {
     150    DISPLAY_LINE **display = the_display;
     151    DISPLAY_LINE *entry;
     152  
     153   entry = display[pl_num];
     154  
     155    /* We have the exact line as it should appear on the screen.
     156       Check to see if this line matches the one already appearing
     157       on the screen. */
     158  
     159    /* If the window is very small, entry might be NULL. */
     160    if (entry)
     161      {
     162        int i, off;
     163  	      
     164        /* If the screen line is inversed, or if the entry is marked as
     165           invalid, then clear the line from the screen first. */
     166        if (entry->inverse)
     167  	{
     168  	  terminal_goto_xy (0, pl_num);
     169  	  terminal_clear_to_eol ();
     170  	  entry->inverse = 0;
     171  	  entry->text[0] = '\0';
     172  	  entry->textlen = 0;
     173  	}
     174  
     175        i = find_diff (printed_line, pl_bytes,
     176  		     entry->text, strlen (entry->text), &off);
     177  
     178        /* If the lines differed at all, we must do some redrawing. */
     179        if (i != -1)
     180  	{
     181  	  /* Move to the proper point on the terminal. */
     182  	  terminal_goto_xy (i, pl_num);
     183  
     184  	  /* If there is any text to print, print it. */
     185            terminal_put_text (printed_line + off);
     186  	  
     187  	  /* If the printed text didn't extend all the way to the edge
     188  	     of the screen, and text was appearing between here and the
     189  	     edge of the screen, clear from here to the end of the
     190  	     line. */
     191  	  if ((pl_chars < screenwidth && pl_chars < entry->textlen)
     192  	      || entry->inverse)
     193  	    terminal_clear_to_eol ();
     194  	  
     195  	  fflush (stdout);
     196  	  
     197  	  /* Update the display text buffer. */
     198  	  if (strlen (printed_line) > (unsigned int) screenwidth)
     199  	    /* printed_line[] can include more than screenwidth
     200  	       characters, e.g. if multibyte encoding is used or
     201  	       if we are under -R and there are escape sequences
     202  	       in it.  However, entry->text was allocated (in
     203  	       display_initialize_display) for screenwidth
     204  	       bytes only.  */
     205  	    entry->text = xrealloc (entry->text, strlen (printed_line) + 1);
     206  	  strcpy (entry->text + off, printed_line + off);
     207  	  entry->textlen = pl_chars;
     208  	  
     209  	  /* Lines showing node text are not in inverse.  Only modelines
     210  	     have that distinction. */
     211  	  entry->inverse = 0;
     212  	}
     213      }
     214  
     215    /* A line has been displayed, and the screen reflects that state.
     216       If there is typeahead pending, then let that typeahead be read
     217       now, instead of continuing with the display. */
     218    if (info_any_buffered_input_p ())
     219      {
     220        display_was_interrupted_p = 1;
     221        return 1;
     222      }
     223    return 0;
     224  }
     225  
     226  
     227  /* Similar to decide_if_in_match, but used for reference highlighting.
     228     Given an array REFERENCES with regions, starting at *REF_INDEX decide
     229     if we are inside a region at offset OFF.  The regions are assumed not
     230     to overlap and to be in order. */
     231  static void
     232  decide_if_in_reference (long off, int *in_ref, REFERENCE **references,
     233                          int *ref_index)
     234  {
     235    int i = *ref_index;
     236    int m = *in_ref;
     237  
     238    for (; (references[i]); i++)
     239      {
     240        if (references[i]->start > off)
     241          break;
     242  
     243        m = 1;
     244  
     245        if (references[i]->end > off)
     246          break;
     247  
     248        m = 0;
     249      }
     250  
     251    *ref_index = i;
     252    *in_ref = m;
     253  }
     254  
     255  /* Used when processing a line to be displayed from a node.  DEFAULT is the
     256     value when the line has no special styles like underlined references or
     257     highlighted search matches.  Otherwise, a line is processed once with
     258     COLLECT as the value, and if it differs to what is on the display already,
     259     it is processed with WRITEOUT and written to the display. */
     260  static int writing_out;
     261  #define DEFAULT 0
     262  #define COLLECT 1
     263  #define WRITEOUT 2 /* Values for writing_out global. */
     264  
     265  /* Combine rendition masks that are active, in order of priority,
     266     then check what's currently active on the display, and output
     267     the necessary codes to switch.  The list of rendition masks is
     268     the complete information about what the style should now be.
     269     RENDITION3 takes priority over RENDITION2, which in turn takes
     270     priority over RENDITION1. */
     271  static void
     272  wrap_terminal_switch_rendition (struct text_buffer *printed_line,
     273                                   RENDITION rendition1,
     274                                   RENDITION rendition2,
     275                                   RENDITION rendition3)
     276  {
     277    long int desired_rendition = 0;
     278    desired_rendition = rendition1.value;
     279    desired_rendition &= ~rendition2.mask;
     280    desired_rendition |= rendition2.value;
     281    desired_rendition &= ~rendition3.mask;
     282    desired_rendition |= rendition3.value;
     283  
     284    if (writing_out == WRITEOUT)
     285      terminal_switch_rendition (desired_rendition);
     286    else
     287      {
     288        /* Guarantee that each byte is non-zero, by having at least one
     289           non-zero bit in it.  See ZERO1_MASK symbol in display.c. */
     290        desired_rendition = ~desired_rendition;
     291  
     292        /* The text added here is only used internally to see when the
     293           display has changed, and is not output to the terminal. */
     294        text_buffer_add_string (printed_line, "\033", 1);
     295        text_buffer_add_string (printed_line, (char *) &desired_rendition,
     296                                sizeof (long));
     297      }
     298  }
     299  
     300  /* Set in display_update_node_text if matches or references are to be 
     301     distinguished with terminal appearance modes. */
     302  static MATCH_STATE *matches;
     303  static REFERENCE **refs;
     304  static size_t match_index;
     305  static int ref_index;
     306  
     307  /* Number of screen columns output so far in a line. */
     308  static int pl_chars;
     309  
     310  /* Whether we are currently outputting a highlighted reference.  This can be 
     311     carried over from one line to another. */
     312  static int ref_highlighted;
     313  
     314  static int pl_num; /* Number of printed lines done so far. */
     315  
     316  RENDITION ref_rendition = {UNDERLINE_MASK, UNDERLINE_MASK};
     317  RENDITION hl_ref_rendition = {BOLD_MASK, BOLD_MASK};
     318  RENDITION match_rendition = {STANDOUT_MASK, STANDOUT_MASK};
     319  
     320  
     321  /* Process a line from the node in WIN starting at ITER, and advancing ITER
     322     to the end of the line.  What is done with the line depends on the value
     323     of WRITING_OUT.
     324     If the line ends in a newline character, set *DELIM to 1. */
     325  static void
     326  display_process_line (WINDOW *win,
     327                   mbi_iterator_t *iter_inout,
     328                   struct text_buffer *tb_printed_line,
     329                   int *delim)
     330  {
     331    mbi_iterator_t iter;
     332    const char *cur_ptr;
     333    size_t pchars = 0; /* Printed chars */
     334    size_t pbytes = 0; /* Bytes to output. */
     335    char *rep;
     336    int in_match = 0;
     337    int in_ref = 0, in_ref_proper = 0;
     338    RENDITION empty = {0, 0};
     339  
     340    int point_in_line;
     341  
     342    if (win->point >= win->line_starts[win->pagetop + pl_num]
     343        && win->point < win->line_starts[win->pagetop + pl_num + 1])
     344      point_in_line = 1;
     345    else
     346      point_in_line = 0;
     347  
     348    iter = *iter_inout;
     349  
     350    while (1)
     351      {
     352        int was_in_ref_proper = in_ref_proper;
     353        int was_in_match = in_match;
     354  
     355        if (!mbi_avail (iter))
     356          break;
     357        cur_ptr = mbi_cur_ptr (iter);
     358  
     359        if (matches && matches_ready (matches)
     360            && !at_end_of_matches (matches, match_index))
     361          {
     362            int was_in_match = in_match;
     363            decide_if_in_match (cur_ptr - win->node->contents,
     364                                &in_match, matches, &match_index);
     365  
     366            if (!was_in_match && in_match && writing_out == DEFAULT)
     367              writing_out = COLLECT;
     368          }
     369  
     370        if (refs && refs[ref_index])
     371          {
     372            int was_in_ref = in_ref;
     373            decide_if_in_reference (cur_ptr - win->node->contents,
     374                                    &in_ref, refs, &ref_index);
     375  
     376            if (was_in_ref && !in_ref)
     377              {
     378                in_ref_proper = ref_highlighted = 0;
     379              }
     380            else if (!was_in_ref && in_ref)
     381              {
     382                if (writing_out == DEFAULT)
     383                  writing_out = COLLECT;
     384  
     385                /* Decide if this reference should be highlighted. */
     386                if (point_in_line && win->point < refs[ref_index]->end)
     387                  {
     388                    /* The reference in is the part of the line after
     389                       the cursor, or the reference contains the cursor. */
     390                    point_in_line = 0;
     391                    ref_highlighted = 1;
     392                  }
     393                else if (point_in_line
     394                         && (!refs[ref_index + 1]
     395                             || refs[ref_index + 1]->start
     396                                >= win->line_starts[win->pagetop + pl_num + 1]))
     397                  {
     398                    /* The reference label is before the cursor in
     399                       the current line and none occurs after it in
     400                       the current line. */
     401                    point_in_line = 0;
     402                    ref_highlighted = 1;
     403                  }
     404                else if (win->point >= refs[ref_index]->start
     405                         && win->point < refs[ref_index]->end)
     406                  {
     407                    /* The point is in a cross-reference, but not in the 
     408                       current line. */
     409                    ref_highlighted = 1;
     410                  }
     411                else if (win->point >= win->line_starts
     412                                                    [win->pagetop + pl_num + 1]
     413                         && win->point < win->line_starts
     414                                                      [win->pagetop + pl_num + 2]
     415                         && refs[ref_index]->end
     416                            >= win->line_starts[win->pagetop + pl_num + 1]
     417                         && (!refs[ref_index + 1]
     418                             || refs[ref_index + 1]->start
     419                                >= win->line_starts[win->pagetop + pl_num + 2]))
     420                  {
     421                    /* Point is in the next line, not inside this reference,
     422                       but this reference continues onto the next line and
     423                       no other reference follows it in the line. */
     424                    ref_highlighted = 1;
     425                  }
     426              }
     427          }
     428  
     429        if (in_ref && !in_ref_proper && !strchr (" \t", *cur_ptr))
     430          in_ref_proper = 1;
     431  
     432        if (was_in_ref_proper != in_ref_proper || was_in_match != in_match)
     433          {
     434            /* Calculate the new rendition for output characters, and call
     435               the function to switch to it. */
     436            RENDITION ref = {0, 0};
     437            RENDITION match = {0, 0};
     438  
     439            if (in_ref_proper)
     440              ref = ref_highlighted && hl_ref_rendition.mask
     441                      ? hl_ref_rendition : ref_rendition;
     442            if (in_match)
     443              match = match_rendition;
     444            if (!ref_highlighted)
     445              {
     446                wrap_terminal_switch_rendition (tb_printed_line,
     447                                                ref, match, empty);
     448              }
     449            else
     450              {
     451                wrap_terminal_switch_rendition (tb_printed_line,
     452                                                match, ref, empty);
     453              }
     454          }
     455  
     456        rep = printed_representation (&iter, delim, pl_chars,
     457                                      &pchars, &pbytes);
     458  
     459        /* If a newline character has been seen, or we have reached the
     460           edge of the display.  */
     461        if (*delim || pl_chars + pchars >= win->width)
     462          break;
     463  
     464        if (rep)
     465          {
     466            if (writing_out != WRITEOUT)
     467              text_buffer_add_string (tb_printed_line, rep, pbytes);
     468            else
     469              terminal_write_chars (rep, pbytes);
     470  
     471            pl_chars += pchars;
     472          }
     473        mbi_advance (iter);
     474      }
     475  
     476    if (writing_out != DEFAULT)
     477      wrap_terminal_switch_rendition (tb_printed_line, empty, empty, empty);
     478  
     479    *iter_inout = iter;
     480  }
     481  
     482  /* Update the part of WIN containing text from a node, i.e. not the blank
     483     part at the end or a modeline.
     484     Print each line in the window into our local buffer, and then
     485     check the contents of that buffer against the display.  If they
     486     differ, update the display.  Return number of lines printed. */
     487  int
     488  display_update_node_text (WINDOW *win)
     489  {
     490    static struct text_buffer tb_printed_line;  /* Buffer for a printed line.  */
     491  
     492    mbi_iterator_t iter;  /* Used to iterate through part of node displayed.  */
     493    mbi_iterator_t bol_iter; /* Keep reference to beginning of each line.  */
     494    int bol_ref_index = 0, bol_match_index = 0;
     495    int bol_ref_highlighted;
     496  
     497    int finish;
     498  
     499    matches = 0;
     500    refs = 0;
     501    if (match_rendition.mask)
     502      matches = &win->matches;
     503    if (ref_rendition.mask || hl_ref_rendition.mask)
     504      refs = win->node->references;
     505  
     506    pl_num = 0;
     507  
     508    ref_highlighted = 0;
     509  
     510    writing_out = DEFAULT; /* Global variable, declared above. */
     511    ref_index = match_index = 0;
     512  
     513    mbi_init (iter, win->node->contents + win->line_starts[win->pagetop],
     514              win->node->nodelen - win->line_starts[win->pagetop]);
     515    mbi_avail (iter);
     516    while (1)
     517      {
     518        int delim;
     519        mbi_copy (&bol_iter, &iter);
     520        bol_ref_index = ref_index;
     521        bol_match_index = match_index;
     522        bol_ref_highlighted = ref_highlighted;
     523  
     524        /* Come back here at end of line when write_out == COLLECT */
     525  start_of_line:
     526        pl_chars = 0;
     527  
     528        text_buffer_reset (&tb_printed_line);
     529  
     530        delim = 0;
     531        /* Check if we have processed all the lines in the window. */
     532        if (pl_num == win->height)
     533          break;
     534  
     535        /* Check if this line of the window is off the screen.  This might 
     536           happen if the screen was resized very small. */
     537        if (win->first_row + pl_num >= screenheight)
     538          break;
     539  
     540        display_process_line (win, &iter, &tb_printed_line, &delim);
     541  
     542        /* End of printed line. */
     543        text_buffer_add_char (&tb_printed_line, '\0');
     544  
     545        finish = 0;
     546        /* If there are no highlighted regions in a line, we output the line with
     547           display_update_line, which does some optimization of the redisplay.
     548           Otherwise, the entire line is output in this function. */
     549        if (writing_out == DEFAULT)
     550          {
     551            finish = display_update_line (win->first_row + pl_num,
     552                        text_buffer_base (&tb_printed_line),
     553                        text_buffer_off (&tb_printed_line) - 1,
     554                        pl_chars);
     555          }
     556        else if (writing_out == COLLECT)
     557          {
     558            /* Check if the line differs from what is already on the display,
     559               and if so, go back to the start of the line and display it for
     560               real. */
     561            DISPLAY_LINE *entry = the_display[win->first_row + pl_num];
     562            if (strcmp (tb_printed_line.base,
     563                        the_display[win->first_row + pl_num]->text))
     564              {
     565                if (tb_printed_line.off > screenwidth)
     566                  {
     567                    entry->text = xrealloc (entry->text,
     568                                            tb_printed_line.off + 1);
     569                  }
     570                strcpy (entry->text, tb_printed_line.base);
     571                /* Record that the contents of this DISPLAY_LINE isn't
     572                   literally what is on the display. */
     573                entry->textlen = 0;
     574                entry->inverse = 1;
     575                mbi_copy (&iter, &bol_iter);
     576                mbi_avail (bol_iter);
     577                ref_index = bol_ref_index;
     578                match_index = bol_match_index;
     579                terminal_goto_xy (0, win->first_row + pl_num);
     580                ref_highlighted = bol_ref_highlighted;
     581                writing_out = WRITEOUT;
     582                goto start_of_line;
     583              }
     584            else
     585              writing_out = DEFAULT;
     586          }
     587        else /* writing_out == WRITEOUT */
     588          {
     589            /* We have just written out this line to the display. */
     590            terminal_clear_to_eol ();
     591            writing_out = DEFAULT;
     592          }
     593  
     594        /* Check if a line continuation character should be displayed.
     595           Don't print one on the very last line of the display, as this could 
     596           cause it to scroll. */
     597        if (delim)
     598          mbi_advance (iter);
     599        else if (win->first_row + pl_num <= the_screen->height - 2)
     600          {
     601            terminal_goto_xy (win->width - 1, win->first_row + pl_num);
     602  
     603            if (!(win->flags & W_NoWrap))
     604              terminal_put_text ("\\");
     605            else
     606              {
     607                terminal_put_text ("$");
     608  
     609                /* If this window has chosen not to wrap lines, skip to the
     610                   end of the logical line in the buffer, and start a new
     611                   line here. */
     612                for (; mbi_avail (iter); mbi_advance (iter))
     613                  if (mb_len (mbi_cur (iter)) == 1
     614                      && *mbi_cur_ptr (iter) == '\n')
     615                    {
     616                      mbi_advance (iter);
     617                      break;
     618                    }
     619              }
     620            fflush (stdout);
     621          }
     622  
     623        pl_num++;
     624        if (finish)
     625          break; /* Display was interrupted by typed input. */
     626  
     627        if (!mbi_avail (iter))
     628          break;
     629      }
     630  
     631    /* Unlike search match highlighting, we always turn reference highlighting
     632       off at the end of each line, so the following isn't needed. */
     633    /* terminal_end_underline (); */
     634  
     635    return pl_num;
     636  }
     637  #undef DEFAULT
     638  #undef COLLECT
     639  #undef WRITEOUT /* values for writing_out global */
     640  
     641  /* Update one window on the screen. */
     642  void
     643  display_update_one_window (WINDOW *win)
     644  {
     645    size_t line_index = 0;
     646    DISPLAY_LINE **display = the_display;
     647  
     648    signal_block_winch ();
     649  
     650    /* If display is inhibited, that counts as an interrupted display. */
     651    if (display_inhibited)
     652      {
     653        display_was_interrupted_p = 1;
     654        goto funexit;
     655      }
     656  
     657    /* If the window has no height, quit now.  Strictly speaking, it
     658       should only be necessary to test if the values are equal to zero, since
     659       window_new_screen_size should ensure that the window height/width never
     660       becomes negative, but since historically this has often been the culprit
     661       for crashes, do our best to be doubly safe.  */
     662    if (win->height <= 0 || win->width <= 0)
     663      goto funexit;
     664  
     665    /* If the window's first row doesn't appear in the_screen, then it
     666       cannot be displayed.  This can happen when the_echo_area is the
     667       window to be displayed, and the screen has shrunk to less than one
     668       line. */
     669    if ((win->first_row < 0) || (win->first_row > the_screen->height))
     670      goto funexit;
     671  
     672    /* If this window has a modeline, it might need to be redisplayed.  Do
     673       this before the rest of the window to aid in navigation in case the
     674       rest of the window is slow to update (for example, if it has lots of
     675       search matches to be displayed). */
     676    if (!(win->flags & W_InhibitMode))
     677      {
     678        window_make_modeline (win);
     679        line_index = win->first_row + win->height;
     680  
     681        /* This display line must both be in inverse, and have the same
     682           contents. */
     683        if ((!display[line_index]->inverse
     684             || (strcmp (display[line_index]->text, win->modeline) != 0))
     685            /* Check screen isn't very small. */
     686            && line_index < the_screen->height)
     687          {
     688            terminal_goto_xy (0, line_index);
     689            terminal_begin_inverse ();
     690            terminal_put_text (win->modeline);
     691            terminal_end_inverse ();
     692            strcpy (display[line_index]->text, win->modeline);
     693            display[line_index]->inverse = 1;
     694            display[line_index]->textlen = strlen (win->modeline);
     695          }
     696      }
     697  
     698    if (win->node)
     699      {
     700        if (!win->line_starts)
     701          calculate_line_starts (win);
     702        line_index = display_update_node_text (win);
     703  
     704        if (display_was_interrupted_p)
     705  	goto funexit;
     706      }
     707  
     708    /* We have reached the end of the node or the end of the window.  If it
     709       is the end of the node, then clear the lines of the window from here
     710       to the end of the window. */
     711    for (; line_index < win->height; line_index++)
     712      {
     713        DISPLAY_LINE *entry = display[win->first_row + line_index];
     714  
     715        /* If this line has text on it, or if we don't know what is on the line,
     716           clear this line. */
     717        if (entry->textlen || entry->inverse)
     718          {
     719            entry->textlen = 0;
     720            entry->text[0] = '\0';
     721            entry->inverse = 0;
     722  
     723            terminal_goto_xy (0, win->first_row + line_index);
     724            terminal_clear_to_eol ();
     725            fflush (stdout);
     726  
     727            if (info_any_buffered_input_p ())
     728              {
     729                display_was_interrupted_p = 1;
     730                goto funexit;
     731              }
     732          }
     733      }
     734  
     735    fflush (stdout);
     736  
     737    /* Okay, this window doesn't need updating anymore. */
     738    win->flags &= ~W_UpdateWindow;
     739  funexit:
     740    signal_unblock_winch ();
     741  }
     742  
     743  /* Scroll screen lines from START inclusive to END exclusive down
     744     by AMOUNT lines.  Negative AMOUNT means move them up. */
     745  static void
     746  display_scroll_region (int start, int end, int amount)
     747  {
     748    int i;
     749    DISPLAY_LINE *temp;
     750  
     751    /* Do it on the screen. */
     752    terminal_scroll_region (start, end, amount);
     753  
     754    /* Now do it in the display buffer so our contents match the screen. */
     755    if (amount > 0)
     756      {
     757        for (i = end - 1; i >= start + amount; i--)
     758          {
     759            /* Swap rows i and (i - amount). */
     760            temp = the_display[i];
     761            the_display[i] = the_display[i - amount];
     762            the_display[i - amount] = temp;
     763          }
     764  
     765        /* Clear vacated lines */
     766        for (i = start; i < start + amount && i < end; i++)
     767          {
     768            the_display[i]->text[0] = '\0';
     769            the_display[i]->textlen = 0;
     770            the_display[i]->inverse = 0;
     771          }
     772      }
     773    else
     774      {
     775        amount *= -1;
     776        for (i = start; i <= end - 1 - amount; i++)
     777          {
     778            /* Swap rows i and (i + amount). */
     779            temp = the_display[i];
     780            the_display[i] = the_display[i + amount];
     781            the_display[i + amount] = temp;
     782          }
     783  
     784        /* Clear vacated lines */
     785        for (i = end - 1; i >= end - amount && i >= start; i--)
     786          {
     787            the_display[i]->text[0] = '\0';
     788            the_display[i]->textlen = 0;
     789            the_display[i]->inverse = 0;
     790          }
     791      }
     792  }
     793  
     794  /* Scroll the region of the_display starting at START, ending at END, and
     795     moving the lines AMOUNT lines.  If AMOUNT is less than zero, the lines
     796     are moved up in the screen, otherwise down.  Actually, it is possible
     797     for no scrolling to take place in the case that the terminal doesn't
     798     support it.  This doesn't matter to us. */
     799  void
     800  display_scroll_display (int start, int end, int amount)
     801  {
     802    register int i, last;
     803    DISPLAY_LINE *temp;
     804  
     805    /* If this terminal cannot do scrolling, give up now. */
     806    if (!terminal_can_scroll && !terminal_can_scroll_region)
     807      return;
     808  
     809    /* If there isn't anything displayed on the screen because it is too
     810       small, quit now. */
     811    if (!the_display[0])
     812      return;
     813  
     814    /* If there is typeahead pending, then don't actually do any scrolling. */
     815    if (info_any_buffered_input_p ())
     816      return;
     817  
     818    /* Use scrolling region if we can because it doesn't affect the area
     819       below the area we want to scroll. */
     820    if (terminal_can_scroll_region)
     821      {
     822        display_scroll_region (start, end, amount);
     823        return;
     824      }
     825  
     826    /* Otherwise scroll by deleting and inserting lines. */
     827  
     828    if (amount < 0)
     829      start -= amount;
     830    else
     831      end -= amount;
     832  
     833    /* Do it on the screen. */
     834    terminal_scroll_terminal (start, end, amount);
     835  
     836    /* Now do it in the display buffer so our contents match the screen. */
     837    if (amount > 0)
     838      {
     839        last = end + amount;
     840  
     841        /* Shift the lines to scroll right into place. */
     842        for (i = 1; i <= (end - start); i++)
     843          {
     844            temp = the_display[last - i];
     845            the_display[last - i] = the_display[end - i];
     846            the_display[end - i] = temp;
     847          }
     848  
     849        /* The lines have been shifted down in the buffer.  Clear all of the
     850           lines that were vacated. */
     851        for (i = start; i != (start + amount); i++)
     852          {
     853            the_display[i]->text[0] = '\0';
     854            the_display[i]->textlen = 0;
     855            the_display[i]->inverse = 0;
     856          }
     857      }
     858    else
     859      {
     860        last = start + amount;
     861        for (i = 0; i < (end - start); i++)
     862          {
     863            temp = the_display[last + i];
     864            the_display[last + i] = the_display[start + i];
     865            the_display[start + i] = temp;
     866          }
     867  
     868        /* The lines have been shifted up in the buffer.  Clear all of the
     869           lines that are left over. */
     870        for (i = end + amount; i != end; i++)
     871          {
     872            the_display[i]->text[0] = '\0';
     873            the_display[i]->textlen = 0;
     874            the_display[i]->inverse = 0;
     875          }
     876      }
     877  }
     878  
     879  /* Try to scroll lines in WINDOW.  OLD_PAGETOP is the pagetop of WINDOW before
     880     having had its line starts recalculated.  OLD_STARTS is the list of line
     881     starts that used to appear in this window.  OLD_COUNT is the number of lines
     882     that appear in the OLD_STARTS array. */
     883  void
     884  display_scroll_line_starts (WINDOW *window, int old_pagetop,
     885      long *old_starts, int old_count)
     886  {
     887    register int i, old, new;     /* Indices into the line starts arrays. */
     888    int last_new, last_old;       /* Index of the last visible line. */
     889    int old_first, new_first;     /* Index of the first changed line. */
     890    int unchanged_at_top = 0;
     891    int already_scrolled = 0;
     892  
     893    /* Locate the first line which was displayed on the old window. */
     894    old_first = old_pagetop;
     895    new_first = window->pagetop;
     896  
     897    /* Find the last line currently visible in this window. */
     898    last_new = window->pagetop + (window->height - 1);
     899    if (last_new > window->line_count)
     900      last_new = window->line_count - 1;
     901  
     902    /* Find the last line which used to be currently visible in this window. */
     903    last_old = old_pagetop + (window->height - 1);
     904    if (last_old > old_count)
     905      last_old = old_count - 1;
     906  
     907    for (old = old_first, new = new_first;
     908         old < last_old && new < last_new;
     909         old++, new++)
     910      if (old_starts[old] != window->line_starts[new])
     911        break;
     912      else
     913        unchanged_at_top++;
     914  
     915    /* Loop through the old lines looking for a match in the new lines. */
     916    for (old = old_first + unchanged_at_top; old < last_old; old++)
     917      {
     918        for (new = new_first; new < last_new; new++)
     919          if (old_starts[old] == window->line_starts[new])
     920            {
     921              /* Find the extent of the matching lines. */
     922              for (i = 0; (old + i) < last_old; i++)
     923                if (old_starts[old + i] != window->line_starts[new + i])
     924                  break;
     925  
     926              /* Scroll these lines if there are enough of them. */
     927              {
     928                int start, end, amount;
     929  
     930                start = (window->first_row
     931                         + ((old + already_scrolled) - old_pagetop));
     932                amount = new - (old + already_scrolled);
     933                end = window->first_row + window->height;
     934  
     935                /* If we are shifting the block of lines down, then the last
     936                   AMOUNT lines will become invisible.  Thus, don't bother
     937                   scrolling them. */
     938                if (amount > 0)
     939                  end -= amount;
     940  
     941                if ((end - start) > 0)
     942                  {
     943                    display_scroll_display (start, end, amount);
     944  
     945                    /* Some lines have been scrolled.  Simulate the scrolling
     946                       by offsetting the value of the old index. */
     947                    old += i;
     948                    already_scrolled += amount;
     949                  }
     950              }
     951            }
     952      }
     953  }
     954  
     955  /* Move the screen cursor to directly over the current character in WINDOW. */
     956  void
     957  display_cursor_at_point (WINDOW *window)
     958  {
     959    int vpos, hpos;
     960  
     961    vpos = window_line_of_point (window) - window->pagetop + window->first_row;
     962    hpos = window_get_cursor_column (window);
     963    terminal_goto_xy (hpos, vpos);
     964    fflush (stdout);
     965  }
     966  
     967  /* **************************************************************** */
     968  /*                                                                  */
     969  /*                   Functions Static to this File                  */
     970  /*                                                                  */
     971  /* **************************************************************** */
     972  
     973  /* Make a DISPLAY_LINE ** with width and height. */
     974  static DISPLAY_LINE **
     975  make_display (int width, int height)
     976  {
     977    register int i;
     978    DISPLAY_LINE **display;
     979  
     980    display = xmalloc ((1 + height) * sizeof (DISPLAY_LINE *));
     981  
     982    for (i = 0; i < height; i++)
     983      {
     984        display[i] = xmalloc (sizeof (DISPLAY_LINE));
     985        display[i]->text = xmalloc (1 + width);
     986        display[i]->textlen = 0;
     987        display[i]->inverse = 0;
     988      }
     989    display[i] = NULL;
     990    return display;
     991  }
     992  
     993  /* Free the storage allocated to DISPLAY. */
     994  static void
     995  free_display (DISPLAY_LINE **display)
     996  {
     997    register int i;
     998    register DISPLAY_LINE *display_line;
     999  
    1000    if (!display)
    1001      return;
    1002  
    1003    for (i = 0; (display_line = display[i]); i++)
    1004      {
    1005        free (display_line->text);
    1006        free (display_line);
    1007      }
    1008    free (display);
    1009  }
    1010