]> code.delx.au - gnu-emacs/blobdiff - src/xdisp.c
(SET_RAW_SYNTAX_ENTRY, SYNTAX_ENTRY): Adjusted for the change of
[gnu-emacs] / src / xdisp.c
index f56a16a7a829d598a226db1bf8a6c00a70f46114..c9aa763edbb3c9c62cbe7a951ba64cfa42d4e44a 100644 (file)
@@ -29,6 +29,7 @@ Boston, MA 02111-1307, USA.  */
 #include "termchar.h"
 #include "dispextern.h"
 #include "buffer.h"
+#include "charset.h"
 #include "indent.h"
 #include "commands.h"
 #include "macros.h"
@@ -36,6 +37,8 @@ Boston, MA 02111-1307, USA.  */
 #include "termhooks.h"
 #include "intervals.h"
 #include "keyboard.h"
+#include "coding.h"
+#include "process.h"
 
 #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI)
 extern void set_frame_menubar ();
@@ -45,6 +48,8 @@ extern int pending_menu_activation;
 extern int interrupt_input;
 extern int command_loop_level;
 
+extern int minibuffer_auto_raise;
+
 extern Lisp_Object Qface;
 
 extern Lisp_Object Voverriding_local_map;
@@ -139,6 +144,14 @@ static int highlight_nonselected_windows;
    Try scrolling this many lines up or down if that will bring it back.  */
 static int scroll_step;
 
+/* Non-0 means scroll just far enough to bring point back on the screen,
+   when appropriate.  */
+static int scroll_conservatively;
+
+/* Recenter the window whenever point gets within this many lines
+   of the top or bottom of the window.  */
+int scroll_margin;
+
 /* Nonzero if try_window_id has made blank lines at window bottom
  since the last redisplay that paused */
 static int blank_end_of_window;
@@ -431,6 +444,9 @@ message2_nolog (m, len)
        {
          echo_area_glyphs = m;
          echo_area_glyphs_length = len;
+
+         if (minibuffer_auto_raise)
+           Fraise_frame  (WINDOW_FRAME (XWINDOW (mini_window)));
        }
       else
        echo_area_glyphs = previous_echo_glyphs = 0;
@@ -535,10 +551,10 @@ message (m, a1, a2, a3)
              a[2] = a3;
 
              len = doprnt (FRAME_MESSAGE_BUF (f),
-                           (int) FRAME_WIDTH (f), m, (char *)0, 3, a);
+                           FRAME_MESSAGE_BUF_SIZE (f), m, (char *)0, 3, a);
 #else
              len = doprnt (FRAME_MESSAGE_BUF (f),
-                           (int) FRAME_WIDTH (f), m, (char *)0, 3, &a1);
+                           FRAME_MESSAGE_BUF_SIZE (f), m, (char *)0, 3, &a1);
 #endif /* NO_ARG_ARRAY */
 
              message2 (FRAME_MESSAGE_BUF (f), len);
@@ -762,7 +778,7 @@ prepare_menu_bars ()
       Lisp_Object tail, frame;
       int count = specpdl_ptr - specpdl;
 
-      record_unwind_protect (Fstore_match_data, Fmatch_data ());
+      record_unwind_protect (Fstore_match_data, Fmatch_data (Qnil, Qnil));
 
       FOR_EACH_FRAME (tail, frame)
        {
@@ -974,20 +990,49 @@ redisplay_internal (preserve_echo_area)
              && end_unchanged >= tlendpos
              && Z - GPT >= tlendpos)))
     {
-      if (tlbufpos > BEGV && FETCH_CHAR (tlbufpos - 1) != '\n'
+      if (tlbufpos > BEGV && FETCH_BYTE (tlbufpos - 1) != '\n'
          && (tlbufpos == ZV
-             || FETCH_CHAR (tlbufpos) == '\n'))
+             || FETCH_BYTE (tlbufpos) == '\n'))
        /* Former continuation line has disappeared by becoming empty */
        goto cancel;
       else if (XFASTINT (w->last_modified) < MODIFF
               || XFASTINT (w->last_overlay_modified) < OVERLAY_MODIFF
               || MINI_WINDOW_P (w))
        {
+         /* We have to handle the case of continuation around a
+            wide-column character (See the comment in indent.c around
+            line 885).
+
+            For instance, in the following case:
+
+            --------  Insert  --------
+            K_A_N_\\   `a'    K_A_N_a\         `X_' are wide-column chars.
+            J_I_       ==>    J_I_             `^^' are cursors.
+            ^^                ^^
+            --------          --------
+
+            As we have to redraw the line above, we should goto cancel.  */
+
+         struct position val;
+         int prevline;
+
+         prevline = find_next_newline (tlbufpos, -1);
+         val = *compute_motion (prevline, 0,
+                                XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0,
+                                0,
+                                tlbufpos,
+                                1 << (BITS_PER_SHORT - 1),
+                                1 << (BITS_PER_SHORT - 1),
+                                window_internal_width (w) - 1,
+                                XINT (w->hscroll), 0, w);
+         if (val.hpos != this_line_start_hpos)
+           goto cancel;
+
          cursor_vpos = -1;
          overlay_arrow_seen = 0;
          zv_strings_seen = 0;
          display_text_line (w, tlbufpos, this_line_vpos, this_line_start_hpos,
-                            pos_tab_offset (w, tlbufpos));
+                            pos_tab_offset (w, tlbufpos), 0);
          /* If line contains point, is not continued,
                 and ends at same distance from eob as before, we win */
          if (cursor_vpos >= 0 && this_line_bufpos
@@ -1383,7 +1428,7 @@ update_menu_bar (f, save_match_data)
 
          set_buffer_internal_1 (XBUFFER (w->buffer));
          if (save_match_data)
-           record_unwind_protect (Fstore_match_data, Fmatch_data ());
+           record_unwind_protect (Fstore_match_data, Fmatch_data (Qnil, Qnil));
          if (NILP (Voverriding_local_map_menu_flag))
            {
              specbind (Qoverriding_terminal_local_map, Qnil);
@@ -1578,6 +1623,30 @@ redisplay_window (window, just_this_one, preserve_echo_area)
 
   startp = marker_position (w->start);
 
+  /* If someone specified a new starting point but did not insist,
+     check whether it can be used.  */
+  if (!NILP (w->optional_new_start))
+    {
+      w->optional_new_start = Qnil;
+      /* Check whether this start pos is usable given where point is.  */
+
+      pos = *compute_motion (startp, 0,
+                            (((EQ (window, minibuf_window)
+                               && startp == BEG)
+                              ? minibuf_prompt_width : 0)
+                             + (hscroll ? 1 - hscroll : 0)),
+                            0,
+                            PT, height, 
+                            /* BUG FIX: See the comment of
+                                Fpos_visible_in_window_p (window.c).  */
+                            - (1 << (BITS_PER_SHORT - 1)),
+                            width, hscroll, pos_tab_offset (w, startp), w);
+      /* If PT does fit on the screen, we will use this start pos,
+        so do so by setting force_start.  */
+      if (pos.bufpos == PT)
+       w->force_start = Qt;
+    }
+
   /* Handle case where place to start displaying has been specified,
      unless the specified location is outside the accessible range.  */
   if (!NILP (w->force_start))
@@ -1674,11 +1743,27 @@ redisplay_window (window, just_this_one, preserve_echo_area)
       && XFASTINT (w->window_end_vpos) < XFASTINT (w->height)
       && !EQ (window, minibuf_window))
     {
-      pos = *compute_motion (startp, 0, (hscroll ? 1 - hscroll : 0), 0,
-                           PT, height, 0, width, hscroll,
-                           pos_tab_offset (w, startp), w);
+      int this_scroll_margin = scroll_margin;
 
-      if (pos.vpos < height)
+      pos = *compute_motion (startp, 0, (hscroll ? 1 - hscroll : 0), 0,
+                            PT, height,
+                            /* BUG FIX: See the comment of
+                                Fpos_visible_in_window_p (window.c).  */
+                            - (1 << (BITS_PER_SHORT - 1)),
+                            width, hscroll,
+                            pos_tab_offset (w, startp), w);
+
+      /* Don't use a scroll margin that is negative or too large.  */
+      if (this_scroll_margin < 0)
+       this_scroll_margin = 0;
+
+      if (XINT (w->height) < 4 * scroll_margin)
+       this_scroll_margin = XINT (w->height) / 4;
+
+      /* If point fits on the screen, and not within the scroll margin,
+        we are ok.  */
+      if (pos.vpos < height - this_scroll_margin
+         && (pos.vpos >= this_scroll_margin || startp == BEGV))
        {
          /* Ok, point is still on frame */
          if (w == XWINDOW (FRAME_SELECTED_WINDOW (f)))
@@ -1704,7 +1789,7 @@ redisplay_window (window, just_this_one, preserve_echo_area)
      but no longer is, find a new starting point.  */
   else if (!NILP (w->start_at_line_beg)
           && !(startp <= BEGV
-               || FETCH_CHAR (startp - 1) == '\n'))
+               || FETCH_BYTE (startp - 1) == '\n'))
     {
       goto recenter;
     }
@@ -1778,6 +1863,83 @@ redisplay_window (window, just_this_one, preserve_echo_area)
 
   /* Try to scroll by specified few lines */
 
+  if (scroll_conservatively && !current_buffer->clip_changed
+      && startp >= BEGV && startp <= ZV)
+    {
+      int this_scroll_margin = scroll_margin;
+
+      /* Don't use a scroll margin that is negative or too large.  */
+      if (this_scroll_margin < 0)
+       this_scroll_margin = 0;
+
+      if (XINT (w->height) < 4 * scroll_margin)
+       this_scroll_margin = XINT (w->height) / 4;
+
+      if (PT >= Z - XFASTINT (w->window_end_pos))
+       {
+         struct position pos;
+         pos = *compute_motion (Z - XFASTINT (w->window_end_pos), 0, 0, 0,
+                                PT, XFASTINT (w->height), 0,
+                                XFASTINT (w->width), XFASTINT (w->hscroll),
+                                pos_tab_offset (w, startp), w);
+         if (pos.vpos > scroll_conservatively)
+           goto scroll_fail_1;
+
+         pos = *vmotion (startp, pos.vpos + 1 + this_scroll_margin, w);
+
+         if (! NILP (Vwindow_scroll_functions))
+           {
+             Fset_marker (w->start, make_number (pos.bufpos), Qnil);
+             run_hook_with_args_2 (Qwindow_scroll_functions, window,
+                                   make_number (pos.bufpos));
+             pos.bufpos = marker_position (w->start);
+           }
+         try_window (window, pos.bufpos);
+         if (cursor_vpos >= 0)
+           {
+             if (!just_this_one || current_buffer->clip_changed
+                 || beg_unchanged < startp)
+               /* Forget any recorded base line for line number display.  */
+               w->base_line_number = Qnil;
+             goto done;
+           }
+         else
+           cancel_my_columns (w);
+       }
+      if (PT < startp)
+       {
+         struct position pos;
+         pos = *compute_motion (PT, 0, 0, 0,
+                                startp, XFASTINT (w->height), 0,
+                                XFASTINT (w->width), XFASTINT (w->hscroll),
+                                pos_tab_offset (w, startp), w);
+         if (pos.vpos >= scroll_conservatively)
+           goto scroll_fail_1;
+
+         pos = *vmotion (startp, - pos.vpos - this_scroll_margin, w);
+
+         if (! NILP (Vwindow_scroll_functions))
+           {
+             Fset_marker (w->start, make_number (pos.bufpos), Qnil);
+             run_hook_with_args_2 (Qwindow_scroll_functions, window,
+                                   make_number (pos.bufpos));
+             pos.bufpos = marker_position (w->start);
+           }
+         try_window (window, pos.bufpos);
+         if (cursor_vpos >= 0)
+           {
+             if (!just_this_one || current_buffer->clip_changed
+                 || beg_unchanged < startp)
+               /* Forget any recorded base line for line number display.  */
+               w->base_line_number = Qnil;
+             goto done;
+           }
+         else
+           cancel_my_columns (w);
+       }
+    scroll_fail_1: ;
+    }
+
   if (scroll_step && !current_buffer->clip_changed
       && startp >= BEGV && startp <= ZV)
     {
@@ -1834,7 +1996,7 @@ recenter:
 
   startp = marker_position (w->start);
   w->start_at_line_beg
-    = (startp == BEGV || FETCH_CHAR (startp - 1) == '\n') ? Qt : Qnil;
+    = (startp == BEGV || FETCH_BYTE (startp - 1) == '\n') ? Qt : Qnil;
 
 done:
   if ((update_mode_line
@@ -1920,30 +2082,33 @@ try_window (window, pos)
   register int height = window_internal_height (w);
   register int vpos = XFASTINT (w->top);
   register int last_text_vpos = vpos;
-  int tab_offset = pos_tab_offset (w, pos);
   FRAME_PTR f = XFRAME (w->frame);
   int width = window_internal_width (w) - 1;
   struct position val;
 
+  /* POS should never be out of range!  */
+  if (pos < XBUFFER (w->buffer)->begv
+      || pos > XBUFFER (w->buffer)->zv)
+    abort ();
+
   Fset_marker (w->start, make_number (pos), Qnil);
   cursor_vpos = -1;
   overlay_arrow_seen = 0;
   zv_strings_seen = 0;
   val.hpos = XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0;
+  val.ovstring_chars_done = 0;
+  val.tab_offset = pos_tab_offset (w, pos);
 
   while (--height >= 0)
     {
-      val = *display_text_line (w, pos, vpos, val.hpos, tab_offset);
+      val = *display_text_line (w, pos, vpos, val.hpos, val.tab_offset,
+                               val.ovstring_chars_done);
+      /* The following code is omitted because we maintain tab_offset
+         in VAL.  */
+#if 0
       tab_offset += width;
-      /* For the first line displayed, display_text_line
-        subtracts the prompt width from the tab offset.
-        But it does not affect the value of our variable tab_offset.
-        So we do the subtraction again,
-        for the sake of continuation lines of that first line.  */
-      if (MINI_WINDOW_P (w) && vpos == XFASTINT (w->top))
-       tab_offset -= minibuf_prompt_width;
-
       if (val.vpos) tab_offset = 0;
+#endif  /* 0 */
       vpos++;
       if (pos != val.bufpos)
        {
@@ -1957,7 +2122,7 @@ try_window (window, pos)
          last_text_vpos
            /* Next line, unless prev line ended in end of buffer with no cr */
            = vpos - (val.vpos
-                     && (FETCH_CHAR (val.bufpos - 1) != '\n' || invis));
+                     && (FETCH_BYTE (val.bufpos - 1) != '\n' || invis));
        }
       pos = val.bufpos;
     }
@@ -2011,7 +2176,7 @@ try_window_id (window)
   struct position val, bp, ep, xp, pp;
   int scroll_amount = 0;
   int delta;
-  int tab_offset, epto, old_tick;
+  int epto, old_tick;
 
   if (GPT - BEG < beg_unchanged)
     beg_unchanged = GPT - BEG;
@@ -2023,7 +2188,10 @@ try_window_id (window)
 
   /* Find position before which nothing is changed.  */
   bp = *compute_motion (start, 0, lmargin, 0,
-                       min (ZV, beg_unchanged + BEG), height, 0,
+                       min (ZV, beg_unchanged + BEG), height,
+                       /* BUG FIX: See the comment of
+                           Fpos_visible_in_window_p() (window.c).  */
+                       - (1 << (BITS_PER_SHORT - 1)),
                        width, hscroll, pos_tab_offset (w, start), w);
   if (bp.vpos >= height)
     {
@@ -2034,7 +2202,10 @@ try_window_id (window)
             But we need to update window_end_pos to account for
             any change in buffer size.  */
          bp = *compute_motion (start, 0, lmargin, 0,
-                               ZV, height, 0,
+                               ZV, height,
+                               /* BUG FIX: See the comment of
+                                   Fpos_visible_in_window_p() (window.c).  */
+                               - (1 << (BITS_PER_SHORT - 1)),
                                width, hscroll, pos_tab_offset (w, start), w);
          XSETFASTINT (w->window_end_vpos, height);
          XSETFASTINT (w->window_end_pos, Z - bp.bufpos);
@@ -2066,12 +2237,14 @@ try_window_id (window)
       --vpos;
       pos = bp.bufpos;
     }
+  val.tab_offset = bp.tab_offset; /* Update tab offset.  */
 
   if (bp.contin && bp.hpos != lmargin)
     {
       val.hpos = bp.prevhpos - width + lmargin;
+      val.tab_offset = bp.tab_offset + bp.prevhpos - width;
       did_motion = 1;
-      pos--;
+      DEC_POS (pos);
     }
 
   bp.vpos = vpos;
@@ -2085,7 +2258,9 @@ try_window_id (window)
   /* Compute the cursor position after that newline.  */
   ep = *compute_motion (pos, vpos, val.hpos, did_motion, tem,
                        height, - (1 << (BITS_PER_SHORT - 1)),
-                       width, hscroll, pos_tab_offset (w, bp.bufpos), w);
+                       width, hscroll,
+                       /* We have tab offset in VAL, use it.  */
+                       val.tab_offset, w); 
 
   /* If changes reach past the text available on the frame,
      just display rest of frame.  */
@@ -2101,7 +2276,7 @@ try_window_id (window)
      newline before it, so the following line must be redrawn. */
   if (stop_vpos == ep.vpos
       && (ep.bufpos == BEGV
-         || FETCH_CHAR (ep.bufpos - 1) != '\n'
+         || FETCH_BYTE (ep.bufpos - 1) != '\n'
          || ep.bufpos == Z - end_unchanged))
     stop_vpos = ep.vpos + 1;
 
@@ -2114,17 +2289,20 @@ try_window_id (window)
   if (stop_vpos < height)
     {
       /* Now determine how far up or down the rest of the window has moved */
-      epto = pos_tab_offset (w, ep.bufpos);
       xp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos, 1,
                            Z - XFASTINT (w->window_end_pos),
-                           10000, 0, width, hscroll, epto, w);
+                           /* Don't care for VPOS... */
+                           1 << (BITS_PER_SHORT - 1),
+                           /* ... nor HPOS.  */
+                           1 << (BITS_PER_SHORT - 1),
+                           width, hscroll, ep.tab_offset, w);
       scroll_amount = xp.vpos - XFASTINT (w->window_end_vpos);
 
       /* Is everything on frame below the changes whitespace?
         If so, no scrolling is really necessary.  */
       for (i = ep.bufpos; i < xp.bufpos; i++)
        {
-         tem = FETCH_CHAR (i);
+         tem = FETCH_BYTE (i);
          if (tem != ' ' && tem != '\n' && tem != '\t')
            break;
        }
@@ -2135,20 +2313,23 @@ try_window_id (window)
                   XFASTINT (w->window_end_vpos) + scroll_amount);
 
       /* Before doing any scrolling, verify that point will be on frame. */
-      if (PT > ep.bufpos && !(PT <= xp.bufpos && xp.bufpos < height))
+      if (PT > ep.bufpos && !(PT <= xp.bufpos && xp.vpos < height))
        {
          if (PT <= xp.bufpos)
            {
              pp = *compute_motion (ep.bufpos, ep.vpos, ep.hpos, 1,
                                    PT, height, - (1 << (BITS_PER_SHORT - 1)),
-                                   width, hscroll, epto, w);
+                                   width, hscroll,
+                                   /* We have tab offset in EP, use it.  */
+                                   ep.tab_offset, w);
            }
          else
            {
              pp = *compute_motion (xp.bufpos, xp.vpos, xp.hpos, 1,
                                    PT, height, - (1 << (BITS_PER_SHORT - 1)),
                                    width, hscroll,
-                                   pos_tab_offset (w, xp.bufpos), w);
+                                   /* We have tab offset in XP, use it. */
+                                   xp.tab_offset, w);
            }
          if (pp.bufpos < PT || pp.vpos == height)
            return 0;
@@ -2251,26 +2432,35 @@ try_window_id (window)
 
   /* Redisplay the lines where the text was changed */
   last_text_vpos = vpos;
+  /* The following code is omitted because we maintain tab offset in
+     val.tab_offset.  */
+#if 0
   tab_offset = pos_tab_offset (w, pos);
   /* If we are starting display in mid-character, correct tab_offset
      to account for passing the line that that character really starts in.  */
   if (val.hpos < lmargin)
     tab_offset += width;
+#endif /* 0 */
   old_tick = MODIFF;
   while (vpos < stop_vpos)
     {
-      val = *display_text_line (w, pos, top + vpos++, val.hpos, tab_offset);
+      val = *display_text_line (w, pos, top + vpos++, val.hpos, val.tab_offset,
+                               val.ovstring_chars_done);
       /* If display_text_line ran a hook and changed some text,
         redisplay all the way to bottom of buffer
         So that we show the changes.  */
       if (old_tick != MODIFF)
        stop_vpos = height;
+      /* The following code is omitted because we maintain tab offset
+        in val.tab_offset.  */
+#if 0
       tab_offset += width;
       if (val.vpos) tab_offset = 0;
+#endif
       if (pos != val.bufpos)
        last_text_vpos
          /* Next line, unless prev line ended in end of buffer with no cr */
-           = vpos - (val.vpos && FETCH_CHAR (val.bufpos - 1) != '\n');
+           = vpos - (val.vpos && FETCH_BYTE (val.bufpos - 1) != '\n');
       pos = val.bufpos;
     }
 
@@ -2297,27 +2487,41 @@ try_window_id (window)
       FRAME_SCROLL_BOTTOM_VPOS (f) = xp.vpos;
       vpos = xp.vpos;
       pos = xp.bufpos;
-      val.hpos = lmargin;
+      val.hpos = xp.hpos;
+      val.tab_offset = xp.tab_offset;
       if (pos == ZV)
-       vpos = height + scroll_amount;
+       { /* Display from next line */
+         vpos = height + scroll_amount;
+         val.hpos = lmargin;
+         val.tab_offset = 0;
+       }
       else if (xp.contin && xp.hpos != lmargin)
        {
          val.hpos = xp.prevhpos - width + lmargin;
-         pos--;
+         val.tab_offset = xp.tab_offset + bp.prevhpos - width;
+         DEC_POS (pos);
        }
 
       blank_end_of_window = 1;
+      /* The following code is omitted because we maintain tab offset
+        in val.tab_offset.  */
+#if 0
       tab_offset = pos_tab_offset (w, pos);
       /* If we are starting display in mid-character, correct tab_offset
         to account for passing the line that that character starts in.  */
       if (val.hpos < lmargin)
        tab_offset += width;
-
+#endif
       while (vpos < height)
        {
-         val = *display_text_line (w, pos, top + vpos++, val.hpos, tab_offset);
+         val = *display_text_line (w, pos, top + vpos++, val.hpos,
+                                   val.tab_offset, val.ovstring_chars_done);
+         /* The following code is omitted because we maintain tab
+            offset in val.tab_offset.  */
+#if 0
          tab_offset += width;
          if (val.vpos) tab_offset = 0;
+#endif /* 0 */
          pos = val.bufpos;
        }
 
@@ -2354,7 +2558,11 @@ try_window_id (window)
   if (cursor_vpos < 0)
     {
     findpoint:
-      val = *compute_motion (start, 0, lmargin, 0, PT, 10000, 10000,
+      val = *compute_motion (start, 0, lmargin, 0, PT, 
+                            /* Don't care for VPOS...  */
+                            1 << (BITS_PER_SHORT - 1),
+                            /* ... nor HPOS.  */
+                            1 << (BITS_PER_SHORT - 1),
                             width, hscroll, pos_tab_offset (w, start), w);
       /* Admit failure if point is off frame now */
       if (val.vpos >= height)
@@ -2409,8 +2617,13 @@ copy_part_of_rope (f, to, s, from, len, face)
   if (! FRAME_TERMCAP_P (f))
     while (n--)
       {
-       int glyph = (INTEGERP (*fp) ? XFASTINT (*fp) : 0);
+       GLYPH glyph = (INTEGERP (*fp) ? XFASTINT (*fp) : 0);
        int facecode;
+       unsigned int c = FAST_GLYPH_CHAR (glyph);
+
+       if (c > MAX_CHAR)
+         /* For an invalid character code, use space.  */
+         c = ' ';
 
        if (FAST_GLYPH_FACE (glyph) == 0)
          /* If GLYPH has no face code, use FACE.  */
@@ -2426,7 +2639,7 @@ copy_part_of_rope (f, to, s, from, len, face)
          }
 
        if (to >= s)
-         *to = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (glyph), facecode);
+         *to = FAST_MAKE_GLYPH (c, facecode);
        ++to;
        ++fp;
       }
@@ -2473,6 +2686,9 @@ fix_glyph (f, glyph, cface)
 
    TABOFFSET is an offset for ostensible hpos, used in tab stop calculations.
 
+   OVSTR_DONE is the number of chars of overlay before/after strings
+   at this position which have already been processed.
+
    Display on position VPOS on the frame.  It is origin 0, relative to
    the top of the frame, not W.
 
@@ -2483,12 +2699,13 @@ fix_glyph (f, glyph, cface)
 struct position val_display_text_line;
 
 static struct position *
-display_text_line (w, start, vpos, hpos, taboffset)
+display_text_line (w, start, vpos, hpos, taboffset, ovstr_done)
      struct window *w;
      int start;
      int vpos;
      int hpos;
      int taboffset;
+     int ovstr_done;
 {
   register int pos = start;
   register int c;
@@ -2545,10 +2762,21 @@ display_text_line (w, start, vpos, hpos, taboffset)
        : default_invis_vector);
 
   GLYPH truncator = (dp == 0 || !INTEGERP (DISP_TRUNC_GLYPH (dp))
+                    || !GLYPH_CHAR_VALID_P (XINT (DISP_TRUNC_GLYPH (dp)))
                     ? '$' : XINT (DISP_TRUNC_GLYPH (dp)));
   GLYPH continuer = (dp == 0 || !INTEGERP (DISP_CONTINUE_GLYPH (dp))
+                    || !GLYPH_CHAR_VALID_P (XINT (DISP_CONTINUE_GLYPH (dp)))
                     ? '\\' : XINT (DISP_CONTINUE_GLYPH (dp)));
 
+  /* If 1, we must handle multibyte characters.  */
+  int multibyte = !NILP (current_buffer->enable_multibyte_characters);
+  /* Length of multibyte form of each character.  */
+  int len;
+  /* Glyphs generated should be set this bit mask if text must be
+     displayed from right to left.  */
+  GLYPH rev_dir_bit = (NILP (current_buffer->direction_reversed)
+                      ? 0 : GLYPH_MASK_REV_DIR);
+
   /* The next buffer location at which the face should change, due
      to overlays or text property changes.  */
   int next_face_change;
@@ -2632,7 +2860,10 @@ display_text_line (w, start, vpos, hpos, taboffset)
       if (left_edge->vpos > vpos
           || left_edge->hpos > 0)
         {
-          pos = left_edge->bufpos - 1;
+          pos = left_edge->bufpos;
+         /* Since this should not be a valid multibyte character, we
+             can decrease POS by 1.  */
+         pos--;
           hpos = left_edge->prevhpos;
         }
       else
@@ -2685,11 +2916,45 @@ display_text_line (w, start, vpos, hpos, taboffset)
                  int ovlen;
                  unsigned char *ovstr;
                  ovlen = overlay_strings (pos, w, &ovstr);
-                 for (; ovlen; ovlen--, ovstr++)
+
+                 if (ovlen > 0)
                    {
-                     if (p1 >= leftmargin && p1 < endp)
-                       *p1 = MAKE_GLYPH (f, *ovstr, current_face);
-                     p1++;
+                     /* Skip the ones we did in a previous line.  */
+                     ovstr += ovstr_done;
+                     ovlen -= ovstr_done;
+
+                     while (ovlen > 0)
+                       {
+                         int charset, cols;
+                         GLYPH g;
+
+                         if (multibyte)
+                           {
+                             c = STRING_CHAR_AND_LENGTH (ovstr, ovlen, len);
+                             ovstr += len, ovlen -= len, ovstr_done += len;
+                             charset = CHAR_CHARSET (c);
+                             cols = (charset == CHARSET_COMPOSITION
+                                     ? cmpchar_table[COMPOSITE_CHAR_ID (c)]->width
+                                     : CHARSET_WIDTH (charset));
+                           }
+                         else
+                           {
+                             c = *ovstr++, ovlen--, ovstr_done++;
+                             cols = 1;
+                           }
+                         g = MAKE_GLYPH (f, c, current_face) | rev_dir_bit;
+                         while (cols-- > 0)
+                           {
+                             if (p1 >= leftmargin && p1 < endp)
+                               *p1 = g, g |= GLYPH_MASK_PADDING;
+                             p1++;
+                           }
+                       }
+                     /* If we did all the overlay strings
+                        and we have room for text, clear ovstr_done
+                        just for neatness' sake.  */
+                     if (ovlen == 0 && p1 < endp)
+                       ovstr_done = 0;
                    }
                }
 
@@ -2709,7 +2974,12 @@ display_text_line (w, start, vpos, hpos, taboffset)
              /* This is just an estimate to give reasonable
                 performance; nothing should go wrong if it is too small.  */
              if (XFASTINT (limit) > pos + 50)
-               XSETFASTINT (limit, pos + 50);
+               {
+                 int limitpos = pos + 50;
+                 if (limitpos < Z)
+                   INC_POS (limitpos); /* Adjust to character boundary.  */
+                 XSETFASTINT (limit, limitpos);
+               }
              limit = Fnext_single_property_change (position, Qinvisible,
                                                    Fcurrent_buffer (), limit);
 #endif
@@ -2790,9 +3060,15 @@ display_text_line (w, start, vpos, hpos, taboffset)
             loop, to see what face we should start with.  */
          if (pos >= next_face_change
              && (FRAME_WINDOW_P (f) || FRAME_MSDOS_P (f)))
-           current_face = compute_char_face (f, w, pos,
-                                             region_beg, region_end,
-                                             &next_face_change, pos + 50, 0);
+           {
+             int limit = pos + 50;
+
+             if (limit < Z && !CHAR_HEAD_P (POS_ADDR (limit)))
+               INC_POS (limit); /* Adjust to character boundary.  */
+             current_face = compute_char_face (f, w, pos,
+                                               region_beg, region_end,
+                                               &next_face_change, limit, 0);
+           }
 #endif
 
          /* Compute the next place we need to stop
@@ -2815,7 +3091,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
          if (pos < GPT && GPT < pause)
            pause = GPT;
 
-         p = &FETCH_CHAR (pos);
+         p = POS_ADDR (pos);
        }
 
       if (p1 >= endp)
@@ -2823,19 +3099,23 @@ display_text_line (w, start, vpos, hpos, taboffset)
 
       p1prev = p1;
 
-      c = *p++;
+      if (multibyte)
+       /* PAUSE is surely at character boundary.  */
+       c = STRING_CHAR_AND_LENGTH (p, pause - pos, len), p += len;
+      else
+       c = *p++, len = 1;
       /* Let a display table override all standard display methods.  */
       if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
        {
          p1 = copy_part_of_rope (f, p1, leftmargin,
                                  XVECTOR (DISP_CHAR_VECTOR (dp, c))->contents,
                                  XVECTOR (DISP_CHAR_VECTOR (dp, c))->size,
-                                 current_face);
+                                 current_face, rev_dir_bit);
        }
       else if (c >= 040 && c < 0177)
        {
          if (p1 >= leftmargin)
-           *p1 = MAKE_GLYPH (f, c, current_face);
+           *p1 = MAKE_GLYPH (f, c, current_face) | rev_dir_bit;
          p1++;
        }
       else if (c == '\n')
@@ -2850,7 +3130,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
            {
              invis = 1;
              pos = find_next_newline (pos + 1, 1);
-             if (FETCH_CHAR (pos - 1) == '\n')
+             if (FETCH_BYTE (pos - 1) == '\n')
                pos--;
            }
          if (invis && selective_rlen > 0 && p1 >= leftmargin)
@@ -2859,7 +3139,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
              if (p1 - leftmargin > width)
                p1 = endp;
              copy_part_of_rope (f, p1prev, p1prev, invis_vector_contents,
-                                (p1 - p1prev), current_face);
+                                (p1 - p1prev), current_face, rev_dir_bit);
            }
 #ifdef HAVE_FACES
          /* Draw the face of the newline character as extending all the 
@@ -2869,7 +3149,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
              if (p1 < leftmargin)
                p1 = leftmargin;
              while (p1 < endp)
-               *p1++ = FAST_MAKE_GLYPH (' ', current_face);
+               *p1++ = FAST_MAKE_GLYPH (' ', current_face) | rev_dir_bit;
            }
 #endif
 
@@ -2898,7 +3178,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
          do
            {
              if (p1 >= leftmargin && p1 < endp)
-               *p1 = MAKE_GLYPH (f, ' ', current_face);
+               *p1 = MAKE_GLYPH (f, ' ', current_face) | rev_dir_bit;
              p1++;
            }
          while ((p1 - leftmargin + taboffset + hscroll - (hscroll > 0))
@@ -2907,7 +3187,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
       else if (c == Ctl ('M') && selective == -1)
        {
          pos = find_next_newline (pos, 1);
-         if (FETCH_CHAR (pos - 1) == '\n')
+         if (FETCH_BYTE (pos - 1) == '\n')
            pos--;
          if (selective_rlen > 0)
            {
@@ -2915,7 +3195,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
              if (p1 - leftmargin > width)
                p1 = endp;
              copy_part_of_rope (f, p1prev, p1prev, invis_vector_contents,
-                                (p1 - p1prev), current_face);
+                                (p1 - p1prev), current_face, rev_dir_bit);
            }
 #ifdef HAVE_FACES
          /* Draw the face of the newline character as extending all the 
@@ -2925,7 +3205,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
              if (p1 < leftmargin)
                p1 = leftmargin;
              while (p1 < endp)
-               *p1++ = FAST_MAKE_GLYPH (' ', current_face);
+               *p1++ = FAST_MAKE_GLYPH (' ', current_face) | rev_dir_bit;
            }
 #endif
 
@@ -2951,34 +3231,58 @@ display_text_line (w, start, vpos, hpos, taboffset)
       else if (c < 0200 && ctl_arrow)
        {
          if (p1 >= leftmargin)
-           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_CTRL_GLYPH (dp))
-                                ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
-                            current_face);
+           *p1 = (fix_glyph
+                  (f, (dp && INTEGERP (DISP_CTRL_GLYPH (dp))
+                       && GLYPH_CHAR_VALID_P (XINT (DISP_CTRL_GLYPH (dp)))
+                       ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
+                   current_face)
+                  | rev_dir_bit);
          p1++;
          if (p1 >= leftmargin && p1 < endp)
-           *p1 = MAKE_GLYPH (f, c ^ 0100, current_face);
+           *p1 = MAKE_GLYPH (f, c ^ 0100, current_face) | rev_dir_bit;
          p1++;
        }
-      else
+      else if (len == 1)
        {
+         /* C is not a multibyte character.  */
          if (p1 >= leftmargin)
-           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_ESCAPE_GLYPH (dp))
-                                ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
-                            current_face);
+           *p1 = (fix_glyph
+                  (f, (dp && INTEGERP (DISP_ESCAPE_GLYPH (dp))
+                       && GLYPH_CHAR_VALID_P (DISP_ESCAPE_GLYPH (dp))
+                       ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
+                   current_face)
+                  | rev_dir_bit);
          p1++;
          if (p1 >= leftmargin && p1 < endp)
-           *p1 = MAKE_GLYPH (f, (c >> 6) + '0', current_face);
+           *p1 = MAKE_GLYPH (f, (c >> 6) + '0', current_face) | rev_dir_bit;
          p1++;
          if (p1 >= leftmargin && p1 < endp)
-           *p1 = MAKE_GLYPH (f, (7 & (c >> 3)) + '0', current_face);
+           *p1 = (MAKE_GLYPH (f, (7 & (c >> 3)) + '0', current_face)
+                  | rev_dir_bit);
          p1++;
          if (p1 >= leftmargin && p1 < endp)
-           *p1 = MAKE_GLYPH (f, (7 & c) + '0', current_face);
+           *p1 = MAKE_GLYPH (f, (7 & c) + '0', current_face) | rev_dir_bit;
          p1++;
        }
+      else
+       {
+         /* C is a multibyte character.  */
+         int charset = CHAR_CHARSET (c);
+         int columns = (charset == CHARSET_COMPOSITION
+                        ? cmpchar_table[COMPOSITE_CHAR_ID (c)]->width
+                        : CHARSET_WIDTH (charset));
+         GLYPH g = MAKE_GLYPH (f, c, current_face) | rev_dir_bit;
+
+         while (columns--)
+           {
+             if (p1 >= leftmargin && p1 < endp)
+               *p1 = g, g |= GLYPH_MASK_PADDING;
+             p1++;
+           }
+       }
 
       prevpos = pos;
-      pos++;
+      pos += len;
 
       /* Update charstarts for the character just output.  */
 
@@ -3031,10 +3335,37 @@ display_text_line (w, start, vpos, hpos, taboffset)
         This occurs when the minibuffer prompt takes up the whole line.  */
       if (p1prev)
        {
-         /* Start the next line with that same character */
-         pos--;
-         /* but at negative hpos, to skip the columns output on this line.  */
-         val.hpos += p1prev - endp;
+         /* Start the next line with that same character whose
+             character code is C and the length of multi-byte form is
+             LEN.  */
+         pos = prevpos;
+
+         if (len == 1)
+           /* C is not a multi-byte character.  We can break it and
+              start from the middle column in the next line.  So,
+              adjust VAL.HPOS to skip the columns output on this
+              line.  */
+           val.hpos += p1prev - endp;
+         else
+           {
+             /* C is a multibyte character.  Since we can't broke it
+                in the middle, the whole character should be driven
+                into the next line.  */
+             /* As the result, the actual columns occupied by the
+                text on this line is less than WIDTH.  VAL.TAB_OFFSET
+                must be adjusted.  */
+             taboffset = taboffset + (p1prev - endp);
+             /* Let's fill unused columns with TRUNCATOR or CONTINUER.  */
+             {
+               GLYPH g = fix_glyph (f, truncate ? truncator : continuer, 0);
+               while (p1prev < endp)
+                 *p1prev++ = g;
+             }
+             /* If POINT is at POS, cursor should not on this line.  */
+             lastpos = pos;
+             if (PT == pos)
+               cursor_vpos = -1;
+           }
        }
 
       /* Keep in this line everything up to the continuation column.  */
@@ -3048,10 +3379,11 @@ display_text_line (w, start, vpos, hpos, taboffset)
 
   if (pos < ZV)
     {
-      if (FETCH_CHAR (pos) == '\n')
+      if (FETCH_BYTE (pos) == '\n')
        {
          /* If stopped due to a newline, start next line after it */
          pos++;
+         val.tab_offset = 0;
          /* Check again for hidden lines, in case the newline occurred exactly
             at the right margin.  */
          while (pos < ZV && selective > 0
@@ -3073,16 +3405,20 @@ display_text_line (w, start, vpos, hpos, taboffset)
                     && indented_beyond_p (pos, selective));
              val.hpos = XINT (w->hscroll) ? 1 - XINT (w->hscroll) : 0;
 
-             lastpos = pos - (FETCH_CHAR (pos - 1) == '\n');
+             lastpos = pos - (FETCH_BYTE (pos - 1) == '\n');
+             val.tab_offset = 0;
            }
          else
            {
              *p1++ = fix_glyph (f, continuer, 0);
              val.vpos = 0;
              lastpos--;
+             val.tab_offset = taboffset + width;
            }
        }
     }
+  else
+    val.tab_offset = 0;
 
   /* If point is at eol or in invisible text at eol,
      record its frame location now.  */
@@ -3130,9 +3466,16 @@ display_text_line (w, start, vpos, hpos, taboffset)
   /* If hscroll and line not empty, insert truncation-at-left marker */
   if (hscroll && lastpos != start)
     {
-      *leftmargin = fix_glyph (f, truncator, 0);
+      GLYPH g = fix_glyph (f, truncator, 0);
+      *leftmargin = g;
       if (p1 <= leftmargin)
        p1 = leftmargin + 1;
+      else                     /* MULE: it may be a wide-column character */
+       {
+         p1prev = leftmargin + 1;
+         while (p1prev < p1 && *p1prev & GLYPH_MASK_PADDING)
+           *p1prev++ = g;
+       }
     }
 
   if (!WINDOW_RIGHTMOST_P (w))
@@ -3208,6 +3551,7 @@ display_text_line (w, start, vpos, hpos, taboffset)
     }
 
   val.bufpos = pos;
+  val.ovstring_chars_done = ovstr_done;
   val_display_text_line = val;
   return &val_display_text_line;
 }
@@ -3310,10 +3654,15 @@ display_mode_line (w)
     {
       /* For a partial width window, explicitly set face of each glyph. */
       int i;
+      unsigned int padding;
       GLYPH *ptr = FRAME_DESIRED_GLYPHS (f)->glyphs[vpos];
       for (i = left; i < right; ++i)
-       ptr[i] = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (ptr[i]), 1);
+       {
+         padding = ptr[i] & GLYPH_MASK_PADDING;
+         ptr[i] = FAST_MAKE_GLYPH (FAST_GLYPH_CHAR (ptr[i]), 1) | padding;
+       }
     }
+  else
 #endif
 
   /* Make the mode line inverse video if the entire line
@@ -3322,8 +3671,8 @@ display_mode_line (w)
      or if it is the child of a full width window
      (which implies that that window is split side-by-side
      and the rest of this line is mode lines of the sibling windows).  */
-  else if (WINDOW_FULL_WIDTH_P (w)
-          || WINDOW_FULL_WIDTH_P (XWINDOW (w->parent)))
+  if (WINDOW_FULL_WIDTH_P (w)
+      || WINDOW_FULL_WIDTH_P (XWINDOW (w->parent)))
     FRAME_DESIRED_GLYPHS (f)->highlight[vpos] = mode_line_inverse_video;
 }
 
@@ -3586,6 +3935,46 @@ pint2str (buf, width, d)
   }
 }
 
+/* Set a mnemonic character for CODING_SYSTEM (Lisp symbol) in BUF.
+   If EOL_FLAG is 1, set also a mnemonic character for end-of-line
+   type of CODING_SYSTEM.  Return updated pointer into BUF.  */
+
+static char *
+decode_mode_spec_coding (coding_system, buf, eol_flag)
+     Lisp_Object coding_system;
+     register char *buf;
+     int eol_flag;
+{
+  register Lisp_Object val = coding_system;
+
+  if (NILP (val))              /* Not yet decided.  */
+    {
+      *buf++ = '-';
+      if (eol_flag) *buf++ = eol_mnemonic_undecided;
+    }
+  else
+    {
+      while (!NILP (val) && SYMBOLP (val))
+       val = Fget (val, Qcoding_system);
+      *buf++ = XFASTINT (XVECTOR (val)->contents[1]);
+      if (eol_flag)
+       {
+         val = Fget (coding_system, Qeol_type);
+
+         if (NILP (val))       /* Not yet decided.  */
+           *buf++ = eol_mnemonic_undecided;
+         else if (VECTORP (val)) /* Not yet decided.  */
+           *buf++ = eol_mnemonic_undecided;
+         else                  /* INTEGERP (val) -- 1:LF, 2:CRLF, 3:CR */
+           *buf++ = (XFASTINT (val) == 1
+                     ? eol_mnemonic_unix
+                     : (XFASTINT (val) == 2
+                        ? eol_mnemonic_dos : eol_mnemonic_mac));
+       }
+    }
+  return buf;
+}
+
 /* Return a string for the output of a mode line %-spec for window W,
    generated by character C.  SPEC_WIDTH is the field width when
    padding to the left (%c, %l).  The value returned from this
@@ -3735,6 +4124,9 @@ decode_mode_spec (w, c, spec_width, maxwidth)
           don't forget that too fast.  */
        if (EQ (w->base_line_pos, w->buffer))
          goto no_value;
+       /* But do forget it, if the window shows a different buffer now.  */
+       else if (BUFFERP (w->base_line_pos))
+         w->base_line_pos = Qnil;
 
        /* If the buffer is very big, don't waste time.  */
        if (BUF_ZV (b) - BUF_BEGV (b) > line_number_display_limit)
@@ -3905,6 +4297,36 @@ decode_mode_spec (w, c, spec_width, maxwidth)
 #else
       return "T";
 #endif
+
+    case 'z':
+      /* coding-system (not including end-of-line format) */
+    case 'Z':
+      /* coding-system (including end-of-line type) */
+      {
+       int eol_flag = (c == 'Z');
+       char *p;
+
+       p = decode_mode_spec_coding
+         (find_symbol_value (Qbuffer_file_coding_system),
+          decode_mode_spec_buf, eol_flag);
+       if (FRAME_TERMCAP_P (f))
+         {
+           p = decode_mode_spec_coding (keyboard_coding.symbol, p, eol_flag);
+           p = decode_mode_spec_coding (terminal_coding.symbol, p, eol_flag);
+         }
+#ifdef subprocesses
+       obj = Fget_buffer_process (Fcurrent_buffer ());
+       if (PROCESSP (obj))
+         {
+           p = decode_mode_spec_coding (XPROCESS (obj)->decode_coding_system,
+                                        p, eol_flag);
+           p = decode_mode_spec_coding (XPROCESS (obj)->encode_coding_system,
+                                        p, eol_flag);
+         }
+#endif /* subprocesses */
+       *p = 0;
+       return decode_mode_spec_buf;
+      }
     }
 
   if (STRINGP (obj))
@@ -3957,8 +4379,8 @@ display_scan_buffer (start, count, shortage)
       {
        ceiling =  BUFFER_CEILING_OF (start);
        ceiling = min (limit, ceiling);
-       ceiling_addr = &FETCH_CHAR (ceiling) + 1;
-       base = (cursor = &FETCH_CHAR (start));
+       ceiling_addr = POS_ADDR (ceiling) + 1;
+       base = (cursor = POS_ADDR (start));
        while (1)
          {
            while (*cursor != '\n' && *cursor != 015 && ++cursor != ceiling_addr)
@@ -3986,8 +4408,8 @@ display_scan_buffer (start, count, shortage)
        {                       /* we WILL scan under start */
          ceiling =  BUFFER_FLOOR_OF (start);
          ceiling = max (limit, ceiling);
-         ceiling_addr = &FETCH_CHAR (ceiling) - 1;
-         base = (cursor = &FETCH_CHAR (start));
+         ceiling_addr = POS_ADDR (ceiling) - 1;
+         base = (cursor = POS_ADDR (start));
          cursor++;
          while (1)
            {
@@ -4088,6 +4510,8 @@ display_string (w, vpos, string, length, hpos, truncate,
   struct frame_glyphs *desired_glyphs = FRAME_DESIRED_GLYPHS (f);
   GLYPH *p1start = desired_glyphs->glyphs[vpos] + hpos;
   int window_width = XFASTINT (w->width);
+  /* If 1, we must display multibyte characters.  */
+  int multibyte = !NILP (XBUFFER (w->buffer)->enable_multibyte_characters);
 
   /* Use the standard display table, not the window's display table.
      We don't want the mode line in rot13.  */
@@ -4131,20 +4555,25 @@ display_string (w, vpos, string, length, hpos, truncate,
   if (maxcol >= 0 && mincol > maxcol)
     mincol = maxcol;
 
+  if (length < 0)
+    /* We need this value for multibyte characters.  */
+    length = strlen (string);
+
   /* We set truncated to 1 if we get stopped by trying to pass END
      (that is, trying to pass MAXCOL.)  */
   truncated = 0;
   while (1)
     {
-      if (length == 0)
-       break;
-      c = *string++;
-      /* Specified length.  */
-      if (length >= 0)
-       length--;
-      /* Unspecified length (null-terminated string).  */
-      else if (c == 0)
+      int len;
+
+      if (length <= 0)
        break;
+      if (multibyte)
+       c = STRING_CHAR_AND_LENGTH (string, length, len);
+      else
+       c = *string, len = 1;
+
+      string += len, length -= len;
 
       if (p1 >= end)
        {
@@ -4178,20 +4607,25 @@ display_string (w, vpos, string, length, hpos, truncate,
       else if (c < 0200 && ! NILP (buffer_defaults.ctl_arrow))
        {
          if (p1 >= start)
-           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_CTRL_GLYPH (dp))
-                                ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
-                            0);
+           *p1 = (fix_glyph
+                  (f, (dp && INTEGERP (DISP_CTRL_GLYPH (dp))
+                       && GLYPH_CHAR_VALID_P (XINT (DISP_CTRL_GLYPH (dp)))
+                       ? XINT (DISP_CTRL_GLYPH (dp)) : '^'),
+                   0));
          p1++;
          if (p1 >= start && p1 < end)
            *p1 = c ^ 0100;
          p1++;
        }
-      else
+      else if (len == 1)
        {
+         /* C is a control character or a binary byte data.  */
          if (p1 >= start)
-           *p1 = fix_glyph (f, (dp && INTEGERP (DISP_ESCAPE_GLYPH (dp))
-                                ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
-                            0);
+           *p1 = (fix_glyph
+                  (f, (dp && INTEGERP (DISP_ESCAPE_GLYPH (dp))
+                       && GLYPH_CHAR_VALID_P (XINT (DISP_ESCAPE_GLYPH (dp)))
+                       ? XINT (DISP_ESCAPE_GLYPH (dp)) : '\\'),
+                   0));
          p1++;
          if (p1 >= start && p1 < end)
            *p1 = (c >> 6) + '0';
@@ -4203,6 +4637,48 @@ display_string (w, vpos, string, length, hpos, truncate,
            *p1 = (7 & c) + '0';
          p1++;
        }
+      else
+       {
+         /* C is a multibyte character.  */      
+         int charset = CHAR_CHARSET (c);
+         int columns = (charset == CHARSET_COMPOSITION
+                        ? cmpchar_table[COMPOSITE_CHAR_ID (c)]->width
+                        : CHARSET_WIDTH (charset));
+
+         if (p1 < start)
+           {
+             /* Since we can't show the left part of C, fill all
+                 columns with spaces.  */
+             columns -= start - p1;
+             p1 = start;
+             while (columns--)
+               {
+                 if (p1 < end)
+                   *p1 = SPACEGLYPH;
+                 p1++;
+               }
+           }
+         else if (p1 + columns > end)
+           {
+             /* Since we can't show the right part of C, fill all
+                 columns with TRUNCATE if TRUNCATE is specified.  */
+             if (truncate)
+               {
+                 while (p1 < end)
+                   *p1++ = fix_glyph (f, truncate, 0);
+                 /* And tell the line is truncated.  */
+                 truncated = 1;
+               }
+             break;
+           }
+         else
+           {
+             /* We can show the whole glyph of C.  */
+             *p1++ = c;
+             while (--columns)
+               *p1++ = c | GLYPH_MASK_PADDING;
+           }
+       }
     }
 
   if (truncated)
@@ -4350,6 +4826,16 @@ See also `overlay-arrow-string'.");
 If that fails to bring point back on frame, point is centered instead.\n\
 If this is zero, point is always centered after it moves off frame.");
 
+  DEFVAR_INT ("scroll-conservatively", &scroll_conservatively,
+    "*Scroll up to this many lines, to bring point back on screen.");
+  scroll_conservatively = 0;
+
+  DEFVAR_INT ("scroll-margin", &scroll_margin,
+    "*Number of lines of margin at the top and bottom of a window.\n\
+Recenter the window whenever point gets within this many lines\n\
+of the top or bottom of the window.");
+  scroll_margin = 0;
+
   DEFVAR_INT ("debug-end-pos", &debug_end_pos, "Don't ask");
 
   DEFVAR_BOOL ("truncate-partial-width-windows",