Revision: miles@gnu.org--gnu-2004/emacs--unicode--0--patch-15
[bpt/emacs.git] / src / insdel.c
index 101e742..5a4dc5b 100644 (file)
@@ -1,5 +1,6 @@
 /* Buffer insertion/deletion and gap motion for GNU Emacs.
-   Copyright (C) 1985, 86,93,94,95,97,98, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1985, 86,93,94,95,97,98, 1999, 2000, 01, 2003
+   Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -23,7 +24,7 @@ Boston, MA 02111-1307, USA.  */
 #include "lisp.h"
 #include "intervals.h"
 #include "buffer.h"
-#include "charset.h"
+#include "character.h"
 #include "window.h"
 #include "blockinput.h"
 #include "region-cache.h"
@@ -32,9 +33,6 @@ Boston, MA 02111-1307, USA.  */
 #define NULL 0
 #endif
 
-#define min(x, y) ((x) < (y) ? (x) : (y))
-#define max(x, y) ((x) > (y) ? (x) : (y))
-
 static void insert_from_string_1 P_ ((Lisp_Object, int, int, int, int, int, int));
 static void insert_from_buffer_1 ();
 static void gap_left P_ ((int, int, int));
@@ -65,6 +63,9 @@ Lisp_Object combine_after_change_list;
 
 /* Buffer which combine_after_change_list is about.  */
 Lisp_Object combine_after_change_buffer;
+
+Lisp_Object Qinhibit_modification_hooks;
+
 \f
 /* Check all markers in the current buffer, looking for something invalid.  */
 
@@ -78,23 +79,19 @@ static int check_markers_debug_flag;
 void
 check_markers ()
 {
-  register Lisp_Object tail;
+  register struct Lisp_Marker *tail;
   int multibyte = ! NILP (current_buffer->enable_multibyte_characters);
 
-  tail = BUF_MARKERS (current_buffer);
-
-  while (! NILP (tail))
+  for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
     {
-      if (XMARKER (tail)->buffer->text != current_buffer->text)
+      if (tail->buffer->text != current_buffer->text)
        abort ();
-      if (XMARKER (tail)->charpos > Z)
+      if (tail->charpos > Z)
        abort ();
-      if (XMARKER (tail)->bytepos > Z_BYTE)
+      if (tail->bytepos > Z_BYTE)
        abort ();
-      if (multibyte && ! CHAR_HEAD_P (FETCH_BYTE (XMARKER (tail)->bytepos)))
+      if (multibyte && ! CHAR_HEAD_P (FETCH_BYTE (tail->bytepos)))
        abort ();
-
-      tail = XMARKER (tail)->chain;
     }
 }
 \f
@@ -349,11 +346,8 @@ adjust_markers_for_delete (from, from_byte, to, to_byte)
   register struct Lisp_Marker *m;
   register int charpos;
 
-  marker = BUF_MARKERS (current_buffer);
-
-  while (!NILP (marker))
+  for (m = BUF_MARKERS (current_buffer); m; m = m->next)
     {
-      m = XMARKER (marker);
       charpos = m->charpos;
 
       if (charpos > Z)
@@ -366,16 +360,37 @@ adjust_markers_for_delete (from, from_byte, to, to_byte)
          m->charpos -= to - from;
          m->bytepos -= to_byte - from_byte;
        }
-
       /* Here's the case where a marker is inside text being deleted.  */
       else if (charpos > from)
        {
-         record_marker_adjustment (marker, from - charpos);
+         if (! m->insertion_type)
+           { /* Normal markers will end up at the beginning of the
+              re-inserted text after undoing a deletion, and must be
+              adjusted to move them to the correct place.  */
+             XSETMISC (marker, m);
+           record_marker_adjustment (marker, from - charpos);
+           }
+         else if (charpos < to)
+           { /* Before-insertion markers will automatically move forward
+              upon re-inserting the deleted text, so we have to arrange
+              for them to move backward to the correct position.  */
+             XSETMISC (marker, m);
+           record_marker_adjustment (marker, charpos - to);
+           }
          m->charpos = from;
          m->bytepos = from_byte;
        }
-
-      marker = m->chain;
+      /* Here's the case where a before-insertion marker is immediately
+        before the deleted region.  */
+      else if (charpos == from && m->insertion_type)
+       {
+         /* Undoing the change uses normal insertion, which will
+            incorrectly make MARKER move forward, so we arrange for it
+            to then move backward to the correct place at the beginning
+            of the deleted region.  */
+         XSETMISC (marker, m);
+         record_marker_adjustment (marker, to - from);
+       }
     }
 }
 
@@ -393,17 +408,13 @@ adjust_markers_for_insert (from, from_byte, to, to_byte, before_markers)
      register int from, from_byte, to, to_byte;
      int before_markers;
 {
-  Lisp_Object marker;
+  struct Lisp_Marker *m;
   int adjusted = 0;
   int nchars = to - from;
   int nbytes = to_byte - from_byte;
 
-  marker = BUF_MARKERS (current_buffer);
-
-  while (!NILP (marker))
+  for (m = BUF_MARKERS (current_buffer); m; m = m->next)
     {
-      register struct Lisp_Marker *m = XMARKER (marker);
-
       /* In a single-byte buffer, a marker's two positions must be
         equal.  */
       if (Z == Z_BYTE)
@@ -427,14 +438,16 @@ adjust_markers_for_insert (from, from_byte, to, to_byte, before_markers)
          m->bytepos += nbytes;
          m->charpos += nchars;
        }
-
-      marker = m->chain;
     }
 
   /* Adjusting only markers whose insertion-type is t may result in
-     disordered overlays in the slot `overlays_before'.  */
+     - disordered start and end in overlays, and 
+     - disordered overlays in the slot `overlays_before' of current_buffer.  */
   if (adjusted)
-    fix_overlays_before (current_buffer, from, to);
+    {
+      fix_start_end_in_overlays(from, to);
+      fix_overlays_before (current_buffer, from, to);
+    }
 }
 
 /* Adjust point for an insertion of NBYTES bytes, which are NCHARS characters.
@@ -470,15 +483,13 @@ adjust_markers_for_replace (from, from_byte, old_chars, old_bytes,
                            new_chars, new_bytes)
      int from, from_byte, old_chars, old_bytes, new_chars, new_bytes;
 {
-  Lisp_Object marker = BUF_MARKERS (current_buffer);
+  register struct Lisp_Marker *m;
   int prev_to_byte = from_byte + old_bytes;
   int diff_chars = new_chars - old_chars;
   int diff_bytes = new_bytes - old_bytes;
 
-  while (!NILP (marker))
+  for (m = BUF_MARKERS (current_buffer); m; m = m->next)
     {
-      register struct Lisp_Marker *m = XMARKER (marker);
-
       if (m->bytepos >= prev_to_byte)
        {
          m->charpos += diff_chars;
@@ -489,8 +500,6 @@ adjust_markers_for_replace (from, from_byte, old_chars, old_bytes,
          m->charpos = from;
          m->bytepos = from_byte;
        }
-
-      marker = m->chain;
     }
 
   CHECK_MARKERS ();
@@ -500,10 +509,9 @@ adjust_markers_for_replace (from, from_byte, old_chars, old_bytes,
 /* Make the gap NBYTES_ADDED bytes longer.  */
 
 void
-make_gap (nbytes_added)
+make_gap_larger (nbytes_added)
      int nbytes_added;
 {
-  unsigned char *result;
   Lisp_Object tem;
   int real_gap_loc;
   int real_gap_loc_byte;
@@ -514,26 +522,16 @@ make_gap (nbytes_added)
 
   /* Don't allow a buffer size that won't fit in an int
      even if it will fit in a Lisp integer.
-     That won't work because so many places use `int'.  */
-     
-  if (Z_BYTE - BEG_BYTE + GAP_SIZE + nbytes_added
-      >= ((unsigned) 1 << (min (BITS_PER_INT, VALBITS) - 1)))
-    error ("Buffer exceeds maximum size");
+     That won't work because so many places use `int'.
 
-  BLOCK_INPUT;
-  /* We allocate extra 1-byte `\0' at the tail for anchoring a search.  */
-  result = BUFFER_REALLOC (BEG_ADDR, (Z_BYTE - BEG_BYTE
-                                     + GAP_SIZE + nbytes_added + 1));
+     Make sure we don't introduce overflows in the calculation.  */
 
-  if (result == 0)
-    {
-      UNBLOCK_INPUT;
-      memory_full ();
-    }
+  if (Z_BYTE - BEG_BYTE + GAP_SIZE
+      >= (((EMACS_INT) 1 << (min (VALBITS, BITS_PER_INT) - 1)) - 1
+         - nbytes_added))
+    error ("Buffer exceeds maximum size");
 
-  /* We can't unblock until the new address is properly stored.  */
-  BEG_ADDR = result;
-  UNBLOCK_INPUT;
+  enlarge_buffer_text (current_buffer, nbytes_added);
 
   /* Prevent quitting in move_gap.  */
   tem = Vinhibit_quit;
@@ -562,6 +560,78 @@ make_gap (nbytes_added)
 
   Vinhibit_quit = tem;
 }
+
+
+/* Make the gap NBYTES_REMOVED bytes shorter.  */
+
+void
+make_gap_smaller (nbytes_removed)
+     int nbytes_removed;
+{
+  Lisp_Object tem;
+  int real_gap_loc;
+  int real_gap_loc_byte;
+  int real_Z;
+  int real_Z_byte;
+  int real_beg_unchanged;
+  int new_gap_size;
+
+  /* Make sure the gap is at least 20 bytes.  */
+  if (GAP_SIZE - nbytes_removed < 20)
+    nbytes_removed = GAP_SIZE - 20;
+
+  /* Prevent quitting in move_gap.  */
+  tem = Vinhibit_quit;
+  Vinhibit_quit = Qt;
+
+  real_gap_loc = GPT;
+  real_gap_loc_byte = GPT_BYTE;
+  new_gap_size = GAP_SIZE - nbytes_removed;
+  real_Z = Z;
+  real_Z_byte = Z_BYTE;
+  real_beg_unchanged = BEG_UNCHANGED;
+
+  /* Pretend that the last unwanted part of the gap is the entire gap,
+     and that the first desired part of the gap is part of the buffer
+     text.  */
+  bzero (GPT_ADDR, new_gap_size);
+  GPT += new_gap_size;
+  GPT_BYTE += new_gap_size;
+  Z += new_gap_size;
+  Z_BYTE += new_gap_size;
+  GAP_SIZE = nbytes_removed;
+
+  /* Move the unwanted pretend gap to the end of the buffer.  This
+     adjusts the markers properly too.  */
+  gap_right (Z, Z_BYTE);
+
+  enlarge_buffer_text (current_buffer, -nbytes_removed);
+
+  /* Now restore the desired gap.  */
+  GAP_SIZE = new_gap_size;
+  GPT = real_gap_loc;
+  GPT_BYTE = real_gap_loc_byte;
+  Z = real_Z;
+  Z_BYTE = real_Z_byte;
+  BEG_UNCHANGED = real_beg_unchanged;
+
+  /* Put an anchor.  */
+  *(Z_ADDR) = 0;
+
+  Vinhibit_quit = tem;
+}
+
+void
+make_gap (nbytes_added)
+     int nbytes_added;
+{
+  if (nbytes_added >= 0)
+    make_gap_larger (nbytes_added);
+#if defined USE_MMAP_FOR_BUFFERS || defined REL_ALLOC || defined DOUG_LEA_MALLOC
+  else
+    make_gap_smaller (-nbytes_added);
+#endif
+}
 \f
 /* Copy NBYTES bytes of text from FROM_ADDR to TO_ADDR.
    FROM_MULTIBYTE says whether the incoming text is multibyte.
@@ -573,7 +643,7 @@ make_gap (nbytes_added)
 int
 copy_text (from_addr, to_addr, nbytes,
           from_multibyte, to_multibyte)
-     unsigned char *from_addr;
+     const unsigned char *from_addr;
      unsigned char *to_addr;
      int nbytes;
      int from_multibyte, to_multibyte;
@@ -589,22 +659,11 @@ copy_text (from_addr, to_addr, nbytes,
       int bytes_left = nbytes;
       Lisp_Object tbl = Qnil;
 
-      /* We set the variable tbl to the reverse table of
-         Vnonascii_translation_table in advance.  */
-      if (CHAR_TABLE_P (Vnonascii_translation_table))
-       {
-         tbl = Fchar_table_extra_slot (Vnonascii_translation_table,
-                                       make_number (0));
-         if (!CHAR_TABLE_P (tbl))
-           tbl = Qnil;
-       }
-
-      /* Convert multibyte to single byte.  */
       while (bytes_left > 0)
        {
          int thislen, c;
          c = STRING_CHAR_AND_LENGTH (from_addr, bytes_left, thislen);
-         if (!SINGLE_BYTE_CHAR_P (c))
+         if (!ASCII_CHAR_P (c))
            c = multibyte_char_to_unibyte (c, tbl);
          *to_addr++ = c;
          from_addr += thislen;
@@ -642,7 +701,7 @@ copy_text (from_addr, to_addr, nbytes,
 
 int
 count_size_as_multibyte (ptr, nbytes)
-     unsigned char *ptr;
+     const unsigned char *ptr;
      int nbytes;
 {
   int i;
@@ -674,7 +733,7 @@ count_size_as_multibyte (ptr, nbytes)
 
 void
 insert (string, nbytes)
-     register unsigned char *string;
+     register const unsigned char *string;
      register int nbytes;
 {
   if (nbytes > 0)
@@ -690,7 +749,7 @@ insert (string, nbytes)
 
 void
 insert_and_inherit (string, nbytes)
-     register unsigned char *string;
+     register const unsigned char *string;
      register int nbytes;
 {
   if (nbytes > 0)
@@ -726,7 +785,7 @@ insert_char (c)
 
 void
 insert_string (s)
-     char *s;
+     const char *s;
 {
   insert (s, strlen (s));
 }
@@ -738,7 +797,7 @@ insert_string (s)
 
 void
 insert_before_markers (string, nbytes)
-     unsigned char *string;
+     const unsigned char *string;
      register int nbytes;
 {
   if (nbytes > 0)
@@ -755,7 +814,7 @@ insert_before_markers (string, nbytes)
 
 void
 insert_before_markers_and_inherit (string, nbytes)
-     unsigned char *string;
+     const unsigned char *string;
      register int nbytes;
 {
   if (nbytes > 0)
@@ -772,7 +831,7 @@ insert_before_markers_and_inherit (string, nbytes)
 
 void
 insert_1 (string, nbytes, inherit, prepare, before_markers)
-     register unsigned char *string;
+     register const unsigned char *string;
      register int nbytes;
      int inherit, prepare, before_markers;
 {
@@ -790,12 +849,12 @@ insert_1 (string, nbytes, inherit, prepare, before_markers)
 
 int
 count_combining_before (string, length, pos, pos_byte)
-     unsigned char *string;
+     const unsigned char *string;
      int length;
      int pos, pos_byte;
 {
   int len, combining_bytes;
-  unsigned char *p;
+  const unsigned char *p;
 
   if (NILP (current_buffer->enable_multibyte_characters))
     return 0;
@@ -837,7 +896,7 @@ count_combining_before (string, length, pos, pos_byte)
 
 int
 count_combining_after (string, length, pos, pos_byte)
-     unsigned char *string;
+     const unsigned char *string;
      int length;
      int pos, pos_byte;
 {
@@ -903,10 +962,13 @@ count_combining_after (string, length, pos, pos_byte)
 
 void
 insert_1_both (string, nchars, nbytes, inherit, prepare, before_markers)
-     register unsigned char *string;
+     register const unsigned char *string;
      register int nchars, nbytes;
      int inherit, prepare, before_markers;
 {
+  if (nchars == 0)
+    return;
+
   if (NILP (current_buffer->enable_multibyte_characters))
     nchars = nbytes;
 
@@ -947,6 +1009,10 @@ insert_1_both (string, nchars, nbytes, inherit, prepare, before_markers)
   if (GPT_BYTE < GPT)
     abort ();
 
+  /* The insert may have been in the unchanged region, so check again. */
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
+
   adjust_overlays_for_insert (PT, nchars);
   adjust_markers_for_insert (PT, PT_BYTE,
                             PT + nchars, PT_BYTE + nbytes,
@@ -1023,7 +1089,7 @@ insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
     outgoing_nbytes = nchars;
   else if (! STRING_MULTIBYTE (string))
     outgoing_nbytes
-      = count_size_as_multibyte (&XSTRING (string)->data[pos_byte],
+      = count_size_as_multibyte (SDATA (string) + pos_byte,
                                 nbytes);
 
   GCPRO1 (string);
@@ -1040,7 +1106,7 @@ insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
 
   /* Copy the string text into the buffer, perhaps converting
      between single-byte and multibyte.  */
-  copy_text (XSTRING (string)->data + pos_byte, GPT_ADDR, nbytes,
+  copy_text (SDATA (string) + pos_byte, GPT_ADDR, nbytes,
             STRING_MULTIBYTE (string),
             ! NILP (current_buffer->enable_multibyte_characters));
 
@@ -1070,6 +1136,10 @@ insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
   if (GPT_BYTE < GPT)
     abort ();
 
+  /* The insert may have been in the unchanged region, so check again. */
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
+
   adjust_overlays_for_insert (PT, nchars);
   adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
                             PT_BYTE + outgoing_nbytes,
@@ -1077,16 +1147,60 @@ insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
 
   offset_intervals (current_buffer, PT, nchars);
 
-  intervals = XSTRING (string)->intervals;
+  intervals = STRING_INTERVALS (string);
   /* Get the intervals for the part of the string we are inserting.  */
-  if (nbytes < STRING_BYTES (XSTRING (string)))
+  if (nbytes < SBYTES (string))
     intervals = copy_intervals (intervals, pos, nchars);
-                              
+
   /* Insert those intervals.  */
   graft_intervals_into_buffer (intervals, PT, nchars,
                               current_buffer, inherit);
 
   adjust_point (nchars, outgoing_nbytes);
+
+  CHECK_MARKERS ();
+}
+\f
+/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
+   starting at GPT_ADDR.  */
+
+void
+insert_from_gap (nchars, nbytes)
+     register int nchars, nbytes;
+{
+  if (NILP (current_buffer->enable_multibyte_characters))
+    nchars = nbytes;
+
+  record_insert (GPT, nchars);
+  MODIFF++;
+
+  GAP_SIZE -= nbytes;
+  GPT += nchars;
+  ZV += nchars;
+  Z += nchars;
+  GPT_BYTE += nbytes;
+  ZV_BYTE += nbytes;
+  Z_BYTE += nbytes;
+  if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
+
+  if (GPT_BYTE < GPT)
+    abort ();
+
+  adjust_overlays_for_insert (GPT - nchars, nchars);
+  adjust_markers_for_insert (GPT - nchars, GPT_BYTE - nbytes,
+                            GPT, GPT_BYTE, 0);
+
+  if (BUF_INTERVALS (current_buffer) != 0)
+    {
+      offset_intervals (current_buffer, GPT - nchars, nchars);
+      graft_intervals_into_buffer (NULL_INTERVAL, GPT - nchars, nchars,
+                                  current_buffer, 0);
+    }
+
+  if (GPT - nchars < PT)
+    adjust_point (nchars, nbytes);
+
+  CHECK_MARKERS ();
 }
 \f
 /* Insert text from BUF, NCHARS characters starting at CHARPOS, into the
@@ -1147,13 +1261,13 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
 
       if (chunk < incoming_nbytes)
        outgoing_after_gap
-         = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, 
+         = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf,
                                                       from_byte + chunk),
                                     incoming_nbytes - chunk);
 
       outgoing_nbytes = outgoing_before_gap + outgoing_after_gap;
     }
-  
+
   /* Make sure point-max won't overflow after this insertion.  */
   XSETINT (temp, outgoing_nbytes + Z);
   if (outgoing_nbytes + Z != XINT (temp))
@@ -1217,6 +1331,10 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
   if (GPT_BYTE < GPT)
     abort ();
 
+  /* The insert may have been in the unchanged region, so check again. */
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
+
   adjust_overlays_for_insert (PT, nchars);
   adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
                             PT_BYTE + outgoing_nbytes,
@@ -1233,7 +1351,7 @@ insert_from_buffer_1 (buf, from, nchars, inherit)
        from += nchars;
       intervals = copy_intervals (intervals, from, nchars);
     }
-                              
+
   /* Insert those intervals.  */
   graft_intervals_into_buffer (intervals, PT, nchars, current_buffer, inherit);
 
@@ -1262,8 +1380,8 @@ adjust_after_replace (from, from_byte, prev_text, len, len_byte)
 
   if (STRINGP (prev_text))
     {
-      nchars_del = XSTRING (prev_text)->size;
-      nbytes_del = STRING_BYTES (XSTRING (prev_text));
+      nchars_del = SCHARS (prev_text);
+      nbytes_del = SBYTES (prev_text);
     }
 
   /* Update various buffer positions for the new text.  */
@@ -1310,6 +1428,56 @@ adjust_after_replace (from, from_byte, prev_text, len, len_byte)
   MODIFF++;
 }
 
+/* Like adjust_after_replace, but doesn't require PREV_TEXT.
+   This is for use when undo is not enabled in the current buffer.  */
+
+void
+adjust_after_replace_noundo (from, from_byte, nchars_del, nbytes_del, len, len_byte)
+     int from, from_byte, nchars_del, nbytes_del, len, len_byte;
+{
+#ifdef BYTE_COMBINING_DEBUG
+  if (count_combining_before (GPT_ADDR, len_byte, from, from_byte)
+      || count_combining_after (GPT_ADDR, len_byte, from, from_byte))
+    abort ();
+#endif
+
+  /* Update various buffer positions for the new text.  */
+  GAP_SIZE -= len_byte;
+  ZV += len; Z+= len;
+  ZV_BYTE += len_byte; Z_BYTE += len_byte;
+  GPT += len; GPT_BYTE += len_byte;
+  if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
+
+  if (nchars_del > 0)
+    adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
+                               len, len_byte);
+  else
+    adjust_markers_for_insert (from, from_byte,
+                              from + len, from_byte + len_byte, 0);
+
+  if (len > nchars_del)
+    adjust_overlays_for_insert (from, len - nchars_del);
+  else if (len < nchars_del)
+    adjust_overlays_for_delete (from, nchars_del - len);
+  if (BUF_INTERVALS (current_buffer) != 0)
+    {
+      offset_intervals (current_buffer, from, len - nchars_del);
+    }
+
+  if (from < PT)
+    adjust_point (len - nchars_del, len_byte - nbytes_del);
+
+  /* As byte combining will decrease Z, we must check this again. */
+  if (Z - GPT < END_UNCHANGED)
+    END_UNCHANGED = Z - GPT;
+
+  CHECK_MARKERS ();
+
+  if (len == 0)
+    evaporate_overlays (from);
+  MODIFF++;
+}
+
 /* Record undo information, adjust markers and position keepers for an
    insertion of a text from FROM (FROM_BYTE) to TO (TO_BYTE).  The
    text already exists in the current buffer but character length (TO
@@ -1347,8 +1515,8 @@ replace_range (from, to, new, prepare, inherit, markers)
      Lisp_Object new;
      int from, to, prepare, inherit, markers;
 {
-  int inschars = XSTRING (new)->size;
-  int insbytes = STRING_BYTES (XSTRING (new));
+  int inschars = SCHARS (new);
+  int insbytes = SBYTES (new);
   int from_byte, to_byte;
   int nbytes_del, nchars_del;
   register Lisp_Object temp;
@@ -1360,6 +1528,7 @@ replace_range (from, to, new, prepare, inherit, markers)
   CHECK_MARKERS ();
 
   GCPRO1 (new);
+  deletion = Qnil;
 
   if (prepare)
     {
@@ -1392,7 +1561,7 @@ replace_range (from, to, new, prepare, inherit, markers)
     outgoing_insbytes = inschars;
   else if (! STRING_MULTIBYTE (new))
     outgoing_insbytes
-      = count_size_as_multibyte (XSTRING (new)->data, insbytes);
+      = count_size_as_multibyte (SDATA (new), insbytes);
 
   /* Make sure point-max won't overflow after this insertion.  */
   XSETINT (temp, Z_BYTE - nbytes_del + insbytes);
@@ -1413,13 +1582,6 @@ replace_range (from, to, new, prepare, inherit, markers)
   if (! EQ (current_buffer->undo_list, Qt))
     deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
 
-  if (markers)
-    /* Relocate all markers pointing into the new, larger gap
-       to point at the end of the text before the gap.
-       Do this before recording the deletion,
-       so that undo handles this after reinserting the text.  */
-    adjust_markers_for_delete (from, from_byte, to, to_byte);
-
   GAP_SIZE += nbytes_del;
   ZV -= nchars_del;
   Z -= nchars_del;
@@ -1427,7 +1589,7 @@ replace_range (from, to, new, prepare, inherit, markers)
   Z_BYTE -= nbytes_del;
   GPT = from;
   GPT_BYTE = from_byte;
-  *(GPT_ADDR) = 0;             /* Put an anchor.  */
+  if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
 
   if (GPT_BYTE < GPT)
     abort ();
@@ -1442,7 +1604,7 @@ replace_range (from, to, new, prepare, inherit, markers)
 
   /* Copy the string text into the buffer, perhaps converting
      between single-byte and multibyte.  */
-  copy_text (XSTRING (new)->data, GPT_ADDR, insbytes,
+  copy_text (SDATA (new), GPT_ADDR, insbytes,
             STRING_MULTIBYTE (new),
             ! NILP (current_buffer->enable_multibyte_characters));
 
@@ -1479,16 +1641,17 @@ replace_range (from, to, new, prepare, inherit, markers)
      adjusting the markers that bound the overlays.  */
   adjust_overlays_for_delete (from, nchars_del);
   adjust_overlays_for_insert (from, inschars);
+
+  /* Adjust markers for the deletion and the insertion.  */
   if (markers)
-    adjust_markers_for_insert (from, from_byte,
-                              from + inschars, from_byte + outgoing_insbytes,
-                              0);
+    adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
+                               inschars, outgoing_insbytes);
 
   offset_intervals (current_buffer, from, inschars - nchars_del);
 
   /* Get the intervals for the part of the string we are inserting--
      not including the combined-before bytes.  */
-  intervals = XSTRING (new)->intervals;
+  intervals = STRING_INTERVALS (new);
   /* Insert those intervals.  */
   graft_intervals_into_buffer (intervals, from, inschars,
                               current_buffer, inherit);
@@ -1546,7 +1709,7 @@ del_range_1 (from, to, prepare, ret_string)
     {
       int range_length = to - from;
       prepare_to_modify_buffer (from, to, &from);
-      to = from + range_length;
+      to = min (ZV, from + range_length);
     }
 
   from_byte = CHAR_TO_BYTE (from);
@@ -1589,7 +1752,12 @@ del_range_byte (from_byte, to_byte, prepare)
 
       if (old_from != from)
        from_byte = CHAR_TO_BYTE (from);
-      if (old_to == Z - to)
+      if (to > ZV)
+       {
+         to = ZV;
+         to_byte = ZV_BYTE;
+       }
+      else if (old_to == Z - to)
        to_byte = CHAR_TO_BYTE (to);
     }
 
@@ -1628,7 +1796,12 @@ del_range_both (from, from_byte, to, to_byte, prepare)
 
       if (old_from != from)
        from_byte = CHAR_TO_BYTE (from);
-      if (old_to == Z - to)
+      if (to > ZV)
+       {
+         to = ZV;
+         to_byte = ZV_BYTE;
+       }
+      else if (old_to == Z - to)
        to_byte = CHAR_TO_BYTE (to);
     }
 
@@ -1699,7 +1872,7 @@ del_range_2 (from, from_byte, to, to_byte, ret_string)
   Z -= nchars_del;
   GPT = from;
   GPT_BYTE = from_byte;
-  *(GPT_ADDR) = 0;             /* Put an anchor.  */
+  if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor.  */
 
   if (GPT_BYTE < GPT)
     abort ();
@@ -1779,7 +1952,7 @@ prepare_to_modify_buffer (start, end, preserve_ptr)
          GCPRO1 (preserve_marker);
          verify_interval_modification (current_buffer, start, end);
          *preserve_ptr = marker_position (preserve_marker);
-         unchain_marker (preserve_marker);
+         unchain_marker (XMARKER (preserve_marker));
          UNGCPRO;
        }
       else
@@ -1827,7 +2000,7 @@ prepare_to_modify_buffer (start, end, preserve_ptr)
   if (! NILP (preserve_marker))                                        \
     {                                                          \
       *preserve_ptr = marker_position (preserve_marker);       \
-      unchain_marker (preserve_marker);                                \
+      unchain_marker (XMARKER (preserve_marker));              \
     }
 
 #define PRESERVE_START_END                     \
@@ -1878,16 +2051,6 @@ signal_before_change (start_int, end_int, preserve_ptr)
       call1 (Vrun_hooks, Qfirst_change_hook);
     }
 
-  /* Run the before-change-function if any.
-     We don't bother "binding" this variable to nil
-     because it is obsolete anyway and new code should not use it.  */
-  if (!NILP (Vbefore_change_function))
-    {
-      PRESERVE_VALUE;
-      PRESERVE_START_END;
-      call2 (Vbefore_change_function, FETCH_START, FETCH_END);
-    }
-
   /* Now run the before-change-functions if any.  */
   if (!NILP (Vbefore_change_functions))
     {
@@ -1895,6 +2058,8 @@ signal_before_change (start_int, end_int, preserve_ptr)
       Lisp_Object before_change_functions;
       Lisp_Object after_change_functions;
       struct gcpro gcpro1, gcpro2;
+      struct buffer *old = current_buffer;
+      struct buffer *new;
 
       PRESERVE_VALUE;
       PRESERVE_START_END;
@@ -1914,14 +2079,25 @@ signal_before_change (start_int, end_int, preserve_ptr)
       args[2] = FETCH_END;
       run_hook_list_with_args (before_change_functions, 3, args);
 
-      /* "Unbind" the variables we "bound" to nil.  */
-      Vbefore_change_functions = before_change_functions;
-      Vafter_change_functions = after_change_functions;
+      /* "Unbind" the variables we "bound" to nil.  Beware a
+        buffer-local hook which changes the buffer when run (e.g. W3).  */
+      if (old != current_buffer)
+       {
+         new = current_buffer;
+         set_buffer_internal (old);
+         Vbefore_change_functions = before_change_functions;
+         Vafter_change_functions = after_change_functions;
+         set_buffer_internal (new);
+       }
+      else
+       {
+         Vbefore_change_functions = before_change_functions;
+         Vafter_change_functions = after_change_functions;
+       }
       UNGCPRO;
     }
 
-  if (!NILP (current_buffer->overlays_before)
-      || !NILP (current_buffer->overlays_after))
+  if (current_buffer->overlays_before || current_buffer->overlays_after)
     {
       PRESERVE_VALUE;
       report_overlay_modification (FETCH_START, FETCH_END, 0,
@@ -1954,9 +2130,9 @@ signal_after_change (charpos, lendel, lenins)
      and there are no before-change functions,
      just record the args that we were going to use.  */
   if (! NILP (Vcombine_after_change_calls)
-      && NILP (Vbefore_change_function) && NILP (Vbefore_change_functions)
-      && NILP (current_buffer->overlays_before)
-      && NILP (current_buffer->overlays_after))
+      && NILP (Vbefore_change_functions)
+      && !current_buffer->overlays_before
+      && !current_buffer->overlays_after)
     {
       Lisp_Object elt;
 
@@ -1974,22 +2150,16 @@ signal_after_change (charpos, lendel, lenins)
       return;
     }
 
-  if (!NILP (combine_after_change_list)) 
+  if (!NILP (combine_after_change_list))
     Fcombine_after_change_execute ();
 
-  /* Run the after-change-function if any.
-     We don't bother "binding" this variable to nil
-     because it is obsolete anyway and new code should not use it.  */
-  if (!NILP (Vafter_change_function))
-    call3 (Vafter_change_function,
-          make_number (charpos), make_number (charpos + lenins),
-          make_number (lendel));
-
   if (!NILP (Vafter_change_functions))
     {
       Lisp_Object args[4];
       Lisp_Object before_change_functions;
       Lisp_Object after_change_functions;
+      struct buffer *old = current_buffer;
+      struct buffer *new;
       struct gcpro gcpro1, gcpro2;
 
       /* "Bind" before-change-functions and after-change-functions
@@ -2009,14 +2179,25 @@ signal_after_change (charpos, lendel, lenins)
       run_hook_list_with_args (after_change_functions,
                               4, args);
 
-      /* "Unbind" the variables we "bound" to nil.  */
-      Vbefore_change_functions = before_change_functions;
-      Vafter_change_functions = after_change_functions;
+      /* "Unbind" the variables we "bound" to nil.  Beware a
+        buffer-local hook which changes the buffer when run (e.g. W3).  */
+      if (old != current_buffer)
+       {
+         new = current_buffer;
+         set_buffer_internal (old);
+         Vbefore_change_functions = before_change_functions;
+         Vafter_change_functions = after_change_functions;
+         set_buffer_internal (new);
+       }
+      else
+       {
+         Vbefore_change_functions = before_change_functions;
+         Vafter_change_functions = after_change_functions;
+       }
       UNGCPRO;
     }
 
-  if (!NILP (current_buffer->overlays_before)
-      || !NILP (current_buffer->overlays_after))
+  if (current_buffer->overlays_before || current_buffer->overlays_after)
     report_overlay_modification (make_number (charpos),
                                 make_number (charpos + lenins),
                                 1,
@@ -2040,11 +2221,11 @@ Fcombine_after_change_execute_1 (val)
 }
 
 DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
-  Scombine_after_change_execute, 0, 0, 0,
-  "This function is for use internally in `combine-after-change-calls'.")
-  ()
+       Scombine_after_change_execute, 0, 0, 0,
+       doc: /* This function is for use internally in `combine-after-change-calls'.  */)
+     ()
 {
-  int count = specpdl_ptr - specpdl;
+  int count = SPECPDL_INDEX ();
   int beg, end, change;
   int begpos, endpos;
   Lisp_Object tail;
@@ -2099,7 +2280,7 @@ DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
      that was changed.  */
   begpos = BEG + beg;
   endpos = Z - end;
-  
+
   /* We are about to handle these, so discard them.  */
   combine_after_change_list = Qnil;
 
@@ -2121,17 +2302,22 @@ syms_of_insdel ()
   combine_after_change_buffer = Qnil;
 
   DEFVAR_BOOL ("check-markers-debug-flag", &check_markers_debug_flag,
-    "Non-nil means enable debugging checks for invalid marker positions.");
+              doc: /* Non-nil means enable debugging checks for invalid marker positions.  */);
   check_markers_debug_flag = 0;
   DEFVAR_LISP ("combine-after-change-calls", &Vcombine_after_change_calls,
-    "Used internally by the `combine-after-change-calls' macro.");
+              doc: /* Used internally by the `combine-after-change-calls' macro.  */);
   Vcombine_after_change_calls = Qnil;
 
   DEFVAR_BOOL ("inhibit-modification-hooks", &inhibit_modification_hooks,
-    "Non-nil means don't run any of the hooks that respond to buffer changes.\n\
-This affects `before-change-functions' and `after-change-functions',\n\
-as well as hooks attached to text properties and overlays.");
+              doc: /* Non-nil means don't run any of the hooks that respond to buffer changes.
+This affects `before-change-functions' and `after-change-functions',
+as well as hooks attached to text properties and overlays.  */);
   inhibit_modification_hooks = 0;
+  Qinhibit_modification_hooks = intern ("inhibit-modification-hooks");
+  staticpro (&Qinhibit_modification_hooks);
 
   defsubr (&Scombine_after_change_execute);
 }
+
+/* arch-tag: 9b34b886-47d7-465e-a234-299af411b23d
+   (do not change this comment) */