]> code.delx.au - gnu-emacs/blobdiff - src/xdisp.c
Avoid crashes due to insanely large columns in tabulated-list-format
[gnu-emacs] / src / xdisp.c
index 157fa729fcfe3b12ce2ecaaecd46217dbcade661..cc2c951ce5c17997470b5b0807940e839cc9b7e8 100644 (file)
@@ -7,8 +7,8 @@ This file is part of GNU Emacs.
 
 GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -825,6 +825,7 @@ static Lisp_Object redisplay_window_1 (Lisp_Object);
 static bool set_cursor_from_row (struct window *, struct glyph_row *,
                                 struct glyph_matrix *, ptrdiff_t, ptrdiff_t,
                                 int, int);
+static bool cursor_row_fully_visible_p (struct window *, bool, bool);
 static bool update_menu_bar (struct frame *, bool, bool);
 static bool try_window_reusing_current_matrix (struct window *);
 static int try_window_id (struct window *);
@@ -2945,7 +2946,7 @@ init_iterator (struct it *it, struct window *w,
         character properties needed for reordering are not yet
         available.  */
       it->bidi_p =
-       NILP (Vpurify_flag)
+       !redisplay__inhibit_bidi
        && !NILP (BVAR (current_buffer, bidi_display_reordering))
        && it->multibyte_p;
 
@@ -6640,7 +6641,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string,
      loading loadup.el, as the necessary character property tables are
      not yet available.  */
   it->bidi_p =
-    NILP (Vpurify_flag)
+    !redisplay__inhibit_bidi
     && !NILP (BVAR (&buffer_defaults, bidi_display_reordering));
 
   if (s == NULL)
@@ -7228,30 +7229,72 @@ get_next_display_element (struct it *it)
                {
                  ptrdiff_t ignore;
                  int next_face_id;
+                 bool text_from_string = false;
+                 /* Normally, the next buffer location is stored in
+                    IT->current.pos...  */
                  struct text_pos pos = it->current.pos;
 
-                 /* For a string from a display property, the next
-                    buffer position is stored in the 'position'
+                 /* ...but for a string from a display property, the
+                    next buffer position is stored in the 'position'
                     member of the iteration stack slot below the
                     current one, see handle_single_display_spec.  By
-                    contrast, it->current.pos was is not yet updated
-                    to point to that buffer position; that will
-                    happen in pop_it, after we finish displaying the
-                    current string.  Note that we already checked
-                    above that it->sp is positive, so subtracting one
-                    from it is safe.  */
+                    contrast, it->current.pos was not yet updated to
+                    point to that buffer position; that will happen
+                    in pop_it, after we finish displaying the current
+                    string.  Note that we already checked above that
+                    it->sp is positive, so subtracting one from it is
+                    safe.  */
                  if (it->from_disp_prop_p)
-                   pos = (it->stack + it->sp - 1)->position;
+                   {
+                     int stackp = it->sp - 1;
+
+                     /* Find the stack level with data from buffer.  */
+                     while (stackp >= 0
+                            && STRINGP ((it->stack + stackp)->string))
+                       stackp--;
+                     if (stackp < 0)
+                       {
+                         /* If no stack slot was found for iterating
+                            a buffer, we are displaying text from a
+                            string, most probably the mode line or
+                            the header line, and that string has a
+                            display string on some of its
+                            characters.  */
+                         text_from_string = true;
+                         pos = it->stack[it->sp - 1].position;
+                       }
+                     else
+                       pos = (it->stack + stackp)->position;
+                   }
                  else
                    INC_TEXT_POS (pos, it->multibyte_p);
 
-                 if (CHARPOS (pos) >= ZV)
+                 if (text_from_string)
+                   {
+                     Lisp_Object base_string = it->stack[it->sp - 1].string;
+
+                     if (CHARPOS (pos) >= SCHARS (base_string) - 1)
+                       it->end_of_box_run_p = true;
+                     else
+                       {
+                         next_face_id
+                           = face_at_string_position (it->w, base_string,
+                                                      CHARPOS (pos), 0,
+                                                      &ignore, face_id, false);
+                         it->end_of_box_run_p
+                           = (FACE_FROM_ID (it->f, next_face_id)->box
+                              == FACE_NO_BOX);
+                       }
+                   }
+                 else if (CHARPOS (pos) >= ZV)
                    it->end_of_box_run_p = true;
                  else
                    {
-                     next_face_id = face_at_buffer_position
-                       (it->w, CHARPOS (pos), &ignore,
-                        CHARPOS (pos) + TEXT_PROP_DISTANCE_LIMIT, false, -1);
+                     next_face_id =
+                       face_at_buffer_position (it->w, CHARPOS (pos), &ignore,
+                                                CHARPOS (pos)
+                                                + TEXT_PROP_DISTANCE_LIMIT,
+                                                false, -1);
                      it->end_of_box_run_p
                        = (FACE_FROM_ID (it->f, next_face_id)->box
                           == FACE_NO_BOX);
@@ -10514,8 +10557,8 @@ ensure_echo_area_buffers (void)
    suitable buffer from echo_buffer[] and clear it.
 
    If WHICH < 0, set echo_area_buffer[1] to echo_area_buffer[0], so
-   that the current message becomes the last displayed one, make
-   choose a suitable buffer for echo_area_buffer[0], and clear it.
+   that the current message becomes the last displayed one, choose a
+   suitable buffer for echo_area_buffer[0], and clear it.
 
    Value is what FN returns.  */
 
@@ -10549,7 +10592,7 @@ with_echo_area_buffer (struct window *w, int which,
        echo_area_buffer[this_one] = Qnil;
     }
 
-  /* Choose a suitable buffer from echo_buffer[] is we don't
+  /* Choose a suitable buffer from echo_buffer[] if we don't
      have one.  */
   if (NILP (echo_area_buffer[this_one]))
     {
@@ -11222,6 +11265,7 @@ clear_garbaged_frames (void)
   if (frame_garbaged)
     {
       Lisp_Object tail, frame;
+      struct frame *sf = SELECTED_FRAME ();
 
       FOR_EACH_FRAME (tail, frame)
        {
@@ -11229,7 +11273,13 @@ clear_garbaged_frames (void)
 
          if (FRAME_VISIBLE_P (f) && FRAME_GARBAGED_P (f))
            {
-             if (f->resized_p)
+             if (f->resized_p
+                 /* It makes no sense to redraw a non-selected TTY
+                    frame, since that will actually clear the
+                    selected frame, and might leave the selected
+                    frame with corrupted display, if it happens not
+                    to be marked garbaged.  */
+                 && !(f != sf && (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f))))
                redraw_frame (f);
              else
                clear_current_matrices (f);
@@ -13139,7 +13189,7 @@ text_outside_line_unchanged_p (struct window *w,
         beginning of a paragraph, before the first strong directional
         character, can change the base direction of the paragraph (unless
         the buffer specifies a fixed paragraph direction), which will
-        require to redisplay the whole paragraph.  It might be worthwhile
+        require redisplaying the whole paragraph.  It might be worthwhile
         to find the paragraph limits and widen the range of redisplayed
         lines to that, but for now just give up this optimization.  */
       if (!NILP (BVAR (XBUFFER (w->contents), bidi_display_reordering))
@@ -13872,11 +13922,16 @@ redisplay_internal (void)
              eassert (this_line_vpos == it.vpos);
              eassert (this_line_y == it.current_y);
              set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
+             if (cursor_row_fully_visible_p (w, false, true))
+               {
 #ifdef GLYPH_DEBUG
-             *w->desired_matrix->method = 0;
-             debug_method_add (w, "optimization 3");
+                 *w->desired_matrix->method = 0;
+                 debug_method_add (w, "optimization 3");
 #endif
-             goto update;
+                 goto update;
+               }
+             else
+               goto cancel;
            }
          else
            goto cancel;
@@ -16905,7 +16960,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       if (scroll_conservatively > SCROLL_LIMIT)
        {
          int window_total_lines
-           = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) * frame_line_height;
+           = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height;
          int margin =
            scroll_margin > 0
            ? min (scroll_margin, window_total_lines / 4)
@@ -17027,7 +17082,16 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
            ignore_mouse_drag_p = true;
 #endif
         }
+      ptrdiff_t count1 = SPECPDL_INDEX ();
+      /* x_consider_frame_title calls select-frame, which calls
+        resize_mini_window, which could resize the mini-window and by
+        that undo the effect of this redisplay cycle wrt minibuffer
+        and echo-area display.  Binding inhibit-redisplay to t makes
+        the call to resize_mini_window a no-op, thus avoiding the
+        adverse side effects.  */
+      specbind (Qinhibit_redisplay, Qt);
       x_consider_frame_title (w->frame);
+      unbind_to (count1, Qnil);
 #endif
     }
 
@@ -18067,7 +18131,7 @@ try_window_id (struct window *w)
      beginning of a paragraph, before the first strong directional
      character, can change the base direction of the paragraph (unless
      the buffer specifies a fixed paragraph direction), which will
-     require to redisplay the whole paragraph.  It might be worthwhile
+     require redisplaying the whole paragraph.  It might be worthwhile
      to find the paragraph limits and widen the range of redisplayed
      lines to that, but for now just give up this optimization and
      redisplay from scratch.  */
@@ -18827,12 +18891,11 @@ dump_glyph (struct glyph_row *row, struct glyph *glyph, int area)
     }
   else if (glyph->type == XWIDGET_GLYPH)
     {
+#ifndef HAVE_XWIDGETS
+      eassume (false);
+#else
       fprintf (stderr,
-#ifdef HAVE_XWIDGETS
               "  %5d %4c %6d %c %3d 0x%05x %c %4d %1.1d%1.1d\n",
-#else
-              "  %5d %4c %6d %c %3d %c %4d %1.1d%1.1d\n",
-#endif
               glyph - row->glyphs[TEXT_AREA],
               'X',
               glyph->charpos,
@@ -18842,14 +18905,12 @@ dump_glyph (struct glyph_row *row, struct glyph *glyph, int area)
                   ? 'S'
                   : '-')),
               glyph->pixel_width,
-#ifdef HAVE_XWIDGETS
               glyph->u.xwidget,
-#endif
               '.',
               glyph->face_id,
               glyph->left_box_line_p,
               glyph->right_box_line_p);
-
+#endif
     }
 }
 
@@ -21211,7 +21272,7 @@ See also `bidi-paragraph-direction'.  */)
       || NILP (BVAR (buf, enable_multibyte_characters))
       /* When we are loading loadup.el, the character property tables
         needed for bidi iteration are not yet available.  */
-      || !NILP (Vpurify_flag))
+      || redisplay__inhibit_bidi)
     return Qleft_to_right;
   else if (!NILP (BVAR (buf, bidi_paragraph_direction)))
     return BVAR (buf, bidi_paragraph_direction);
@@ -21335,7 +21396,7 @@ the `bidi-class' property of a character.  */)
          /* When we are loading loadup.el, the character property
             tables needed for bidi iteration are not yet
             available.  */
-         || !NILP (Vpurify_flag))
+         || redisplay__inhibit_bidi)
        return Qnil;
 
       validate_subarray (object, from, to, SCHARS (object), &from_pos, &to_pos);
@@ -21363,7 +21424,7 @@ the `bidi-class' property of a character.  */)
          /* When we are loading loadup.el, the character property
             tables needed for bidi iteration are not yet
             available.  */
-         || !NILP (Vpurify_flag))
+         || redisplay__inhibit_bidi)
        return Qnil;
 
       set_buffer_temp (buf);
@@ -25767,6 +25828,7 @@ append_glyph (struct it *it)
       glyph->object = it->object;
       if (it->pixel_width > 0)
        {
+         eassert (it->pixel_width <= SHRT_MAX);
          glyph->pixel_width = it->pixel_width;
          glyph->padding_p = false;
        }
@@ -25847,6 +25909,7 @@ append_composite_glyph (struct it *it)
        }
       glyph->charpos = it->cmp_it.charpos;
       glyph->object = it->object;
+      eassert (it->pixel_width <= SHRT_MAX);
       glyph->pixel_width = it->pixel_width;
       glyph->ascent = it->ascent;
       glyph->descent = it->descent;
@@ -26056,7 +26119,7 @@ produce_image_glyph (struct it *it)
        {
          glyph->charpos = CHARPOS (it->position);
          glyph->object = it->object;
-         glyph->pixel_width = it->pixel_width;
+         glyph->pixel_width = clip_to_bounds (-1, it->pixel_width, SHRT_MAX);
          glyph->ascent = glyph_ascent;
          glyph->descent = it->descent;
          glyph->voffset = it->voffset;
@@ -26160,7 +26223,7 @@ produce_xwidget_glyph (struct it *it)
        {
          glyph->charpos = CHARPOS (it->position);
          glyph->object = it->object;
-         glyph->pixel_width = it->pixel_width;
+         glyph->pixel_width = clip_to_bounds (-1, it->pixel_width, SHRT_MAX);
          glyph->ascent = glyph_ascent;
          glyph->descent = it->descent;
          glyph->voffset = it->voffset;
@@ -26246,7 +26309,9 @@ append_stretch_glyph (struct it *it, Lisp_Object object,
        }
       glyph->charpos = CHARPOS (it->position);
       glyph->object = object;
-      glyph->pixel_width = width;
+      /* FIXME: It would be better to use TYPE_MAX here, but
+        __typeof__ is not portable enough...  */
+      glyph->pixel_width = clip_to_bounds (-1, width, SHRT_MAX);
       glyph->ascent = ascent;
       glyph->descent = height - ascent;
       glyph->voffset = it->voffset;
@@ -26697,6 +26762,7 @@ append_glyphless_glyph (struct it *it, int face_id, bool for_no_font, int len,
        }
       glyph->charpos = CHARPOS (it->position);
       glyph->object = it->object;
+      eassert (it->pixel_width <= SHRT_MAX);
       glyph->pixel_width = it->pixel_width;
       glyph->ascent = it->ascent;
       glyph->descent = it->descent;
@@ -31370,8 +31436,11 @@ Value is a number or a cons (WIDTH-DPI . HEIGHT-DPI).  */);
               Vtruncate_partial_width_windows,
     doc: /* Non-nil means truncate lines in windows narrower than the frame.
 For an integer value, truncate lines in each window narrower than the
-full frame width, provided the window width is less than that integer;
-otherwise, respect the value of `truncate-lines'.
+full frame width, provided the total window width in column units is less
+than that integer; otherwise, respect the value of `truncate-lines'.
+The total width of the window is as returned by `window-total-width', it
+includes the fringes, the continuation and truncation glyphs, the
+display margins (if any), and the scroll bar
 
 For any other non-nil value, truncate lines in all windows that do
 not span the full frame width.
@@ -31406,7 +31475,7 @@ This variable is not guaranteed to be accurate except while processing
 
   DEFVAR_LISP ("frame-title-format", Vframe_title_format,
     doc: /* Template for displaying the title bar of visible frames.
-(Assuming the window manager supports this feature.)
+\(Assuming the window manager supports this feature.)
 
 This variable has the same structure as `mode-line-format', except that
 the %c and %l constructs are ignored.  It is used only on frames for
@@ -31414,10 +31483,10 @@ which no explicit name has been set (see `modify-frame-parameters').  */);
 
   DEFVAR_LISP ("icon-title-format", Vicon_title_format,
     doc: /* Template for displaying the title bar of an iconified frame.
-(Assuming the window manager supports this feature.)
+\(Assuming the window manager supports this feature.)
 This variable has the same structure as `mode-line-format' (which see),
 and is used only on frames for which no explicit name has been set
-(see `modify-frame-parameters').  */);
+\(see `modify-frame-parameters').  */);
   Vicon_title_format
     = Vframe_title_format
     = listn (CONSTYPE_PURE, 3,
@@ -31579,7 +31648,12 @@ A value of t means resize them to fit the text displayed in them.
 A value of `grow-only', the default, means let mini-windows grow only;
 they return to their normal size when the minibuffer is closed, or the
 echo area becomes empty.  */);
-  Vresize_mini_windows = Qgrow_only;
+  /* Contrary to the doc string, we initialize this to nil, so that
+     loading loadup.el won't try to resize windows before loading
+     window.el, where some functions we need to call for this live.
+     We assign the 'grow-only' value right after loading window.el
+     during loadup.  */
+  Vresize_mini_windows = Qnil;
 
   DEFVAR_LISP ("blink-cursor-alist", Vblink_cursor_alist,
     doc: /* Alist specifying how to blink the cursor off.
@@ -31787,6 +31861,12 @@ display table takes effect; in this case, Emacs does not consult
   DEFVAR_LISP ("redisplay--variables", Vredisplay__variables,
      doc: /* A hash-table of variables changing which triggers a thorough redisplay.  */);
   Vredisplay__variables = Qnil;
+
+  DEFVAR_BOOL ("redisplay--inhibit-bidi", redisplay__inhibit_bidi,
+     doc: /* Non-nil means it is not safe to attempt bidi reordering for display.  */);
+  /* Initialize to t, since we need to disable reordering until
+     loadup.el successfully loads charprop.el.  */
+  redisplay__inhibit_bidi = true;
 }