check_markers ()
{
register Lisp_Object tail, prev, next;
+ int multibyte = ! NILP (current_buffer->enable_multibyte_characters);
tail = BUF_MARKERS (current_buffer);
abort ();
if (XMARKER (tail)->bytepos > Z_BYTE)
abort ();
+ if (multibyte && ! CHAR_HEAD_P (FETCH_BYTE (XMARKER (tail)->bytepos)))
+ abort ();
tail = XMARKER (tail)->chain;
}
}
}
-/* Adjust all markers for a byte combining of NBYTES at char position
- FROM and byte position FROM_BYTE. */
-
-static void
-adjust_markers_for_combining (from, from_byte, nbytes)
- register int from, from_byte, nbytes;
-{
- Lisp_Object marker;
- register struct Lisp_Marker *m;
- register int bytepos;
- register int to_byte = from_byte + nbytes;
-
- marker = BUF_MARKERS (current_buffer);
-
- while (!NILP (marker))
- {
- m = XMARKER (marker);
- bytepos = m->bytepos;
-
- if (bytepos >= to_byte)
- {
- record_marker_adjustment (marker, - nbytes);
- m->charpos -= nbytes;
- }
- else if (bytepos > from_byte)
- {
- record_marker_adjustment (marker, from - m->charpos);
- m->charpos = from;
- m->bytepos = to_byte;
- }
- else if (bytepos == from_byte)
- {
- m->bytepos = to_byte;
- }
-
- marker = m->chain;
- }
-}
\f
/* Adjust all markers for calling record_delete for combining bytes.
whose range in bytes is FROM_BYTE to TO_BYTE.
{
if (m->insertion_type || before_markers)
{
- m->bytepos += nbytes + combined_after_bytes;
- m->charpos += nchars + !!combined_after_bytes;
+ m->bytepos = to_byte + combined_after_bytes;
+ m->charpos = to - combined_before_bytes;
/* Point the marker before the combined character,
so that undoing the insertion puts it back where it was. */
if (combined_after_bytes)
but don't leave it pointing in the middle of a character.
Point the marker after the combined character,
so that undoing the insertion puts it back where it was. */
-
- /* Here we depend on the fact that the gap is after
- all of the combining bytes that we are going to skip over. */
- DEC_BOTH (m->charpos, m->bytepos);
- INC_BOTH (m->charpos, m->bytepos);
+ m->bytepos += combined_before_bytes;
+ if (combined_before_bytes == nbytes)
+ /* All new bytes plus combined_after_bytes (if any)
+ are combined. */
+ m->bytepos += combined_after_bytes;
}
}
/* If a marker was pointing into the combining bytes
{
/* Put it after the combining bytes. */
m->bytepos = to_byte + combined_after_bytes;
- m->charpos = to + 1;
+ m->charpos = to - combined_before_bytes;
/* Now move it back before the combined character,
so that undoing the insertion will put it where it was. */
DEC_BOTH (m->charpos, m->bytepos);
else if (m->bytepos > from_byte)
{
m->bytepos += nbytes;
- m->charpos += nchars;
+ m->charpos += nchars - combined_after_bytes - combined_before_bytes;
}
marker = m->chain;
{
Lisp_Object marker = BUF_MARKERS (current_buffer);
int prev_to_byte = from_byte + old_bytes;
- int diff_chars = new_chars - old_chars;
+ int diff_chars
+ = (new_chars - combined_before_bytes) - (old_chars + combined_after_bytes);
int diff_bytes = new_bytes - old_bytes;
while (!NILP (marker))
{
register struct Lisp_Marker *m = XMARKER (marker);
- if (m->bytepos >= prev_to_byte)
+ if (m->bytepos >= prev_to_byte
+ && (old_bytes != 0
+ /* If this is an insertion (replacing 0 chars),
+ reject the case of a marker that is at the
+ insertion point and should stay before the insertion. */
+ || m->bytepos > from_byte || m->insertion_type))
{
if (m->bytepos < prev_to_byte + combined_after_bytes)
{
/* Put it after the combining bytes. */
- m->bytepos = from_byte + new_bytes;
- m->charpos = from + new_chars;
+ m->bytepos = from_byte + new_bytes + combined_after_bytes;
+ m->charpos = from + new_chars - combined_before_bytes;
}
else
{
m->charpos += diff_chars;
m->bytepos += diff_bytes;
}
- if (m->charpos == from + new_chars)
- record_marker_adjustment (marker, - old_chars);
}
- else if (m->bytepos > from_byte)
+ else if (m->bytepos >= from_byte)
{
- record_marker_adjustment (marker, from - m->charpos);
m->charpos = from;
- m->bytepos = from_byte;
+ m->bytepos = from_byte + combined_before_bytes;
+ /* If all new bytes are combined in addition to that there
+ are after combining bytes, we must set byte position of
+ the marker after the after combining bytes. */
+ if (combined_before_bytes == new_bytes)
+ m->bytepos += combined_after_bytes;
}
marker = m->chain;
}
+
+ CHECK_MARKERS ();
}
\f
unsigned char workbuf[4], *str;
int len;
- if ((c >= 0240 || !NILP (Vnonascii_translation_table)) && c < 0400)
+ if (c < 0400
+ && (c >= 0240
+ || (c >= 0200 && !NILP (Vnonascii_translation_table))))
{
c = unibyte_char_to_multibyte (c);
len = CHAR_STRING (c, workbuf, str);
{
unsigned int c = *ptr++;
- if (c < 0240 && NILP (Vnonascii_translation_table))
+ if (c < 0200 || (c < 0240 && NILP (Vnonascii_translation_table)))
outgoing_nbytes++;
else
{
return 0;
if (length == 0 || CHAR_HEAD_P (*string))
return 0;
- if (pos == BEGV)
+ if (pos == BEG)
return 0;
c = FETCH_BYTE (pos_byte - 1);
if (ASCII_BYTE_P (c))
if (NILP (current_buffer->enable_multibyte_characters))
return 0;
- if (length == 0 || ASCII_BYTE_P (string[length - 1]))
+ if (length > 0 && ASCII_BYTE_P (string[length - 1]))
return 0;
i = length - 1;
while (i >= 0 && ! CHAR_HEAD_P (string[i]))
else if (!BASE_LEADING_CODE_P (string[i]))
return 0;
- if (pos == ZV)
+ if (pos == Z)
return 0;
c = FETCH_BYTE (pos_byte);
if (CHAR_HEAD_P (c))
return 0;
- while (pos_byte < ZV_BYTE)
+ while (pos_byte < Z_BYTE)
{
c = FETCH_BYTE (pos_byte);
if (CHAR_HEAD_P (c))
/* Combine NBYTES stray trailing-codes, which were formerly separate
characters, with the preceding character. These bytes
are located after position POS / POS_BYTE, and the preceding character
- is located just before that position. */
+ is located just before that position.
+
+ This function does not adjust markers for byte combining. That
+ should be done in advance by the functions
+ adjust_markers_for_insert or adjust_markers_for_replace. */
static void
combine_bytes (pos, pos_byte, nbytes)
int pos, pos_byte, nbytes;
{
- /* Adjust all markers. */
- adjust_markers_for_combining (pos, pos_byte, nbytes);
-
adjust_overlays_for_delete (pos, nbytes);
ADJUST_CHAR_POS (BUF_PT (current_buffer), BUF_PT_BYTE (current_buffer));
if (BUF_INTERVALS (current_buffer) != 0)
/* Only defined if Emacs is compiled with USE_TEXT_PROPERTIES. */
offset_intervals (current_buffer, pos, - nbytes);
+}
- CHECK_MARKERS ();
+void
+byte_combining_error ()
+{
+ error ("Byte combining across boundary of accessible buffer text inhibitted");
}
+
+/* If we are going to combine bytes at POS which is at a narrowed
+ region boundary, signal an error. */
+#define CHECK_BYTE_COMBINING_FOR_INSERT(pos) \
+ do { \
+ if (combined_before_bytes && pos == BEGV \
+ || combined_after_bytes && pos == ZV) \
+ byte_combining_error (); \
+ } while (0)
+
\f
/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
starting at STRING. INHERIT, PREPARE and BEFORE_MARKERS
= count_combining_before (string, nbytes, PT, PT_BYTE);
combined_after_bytes
= count_combining_after (string, nbytes, PT, PT_BYTE);
+ CHECK_BYTE_COMBINING_FOR_INSERT (PT);
/* Record deletion of the surrounding text that combines with
the insertion. This, together with recording the insertion,
if (combined_before_bytes)
combine_bytes (pos, pos_byte, combined_before_bytes);
}
+
+ CHECK_MARKERS ();
}
\f
/* Insert the part of the text of STRING, a Lisp object assumed to be
= count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE);
combined_after_bytes
= count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE);
+ {
+ unsigned char save = *(GPT_ADDR);
+ *(GPT_ADDR) = 0;
+ CHECK_BYTE_COMBINING_FOR_INSERT (PT);
+ *(GPT_ADDR) = save;
+ }
/* Record deletion of the surrounding text that combines with
the insertion. This, together with recording the insertion,
int inherit;
{
register Lisp_Object temp;
- int chunk;
+ int chunk, chunk_expanded;
int from_byte = buf_charpos_to_bytepos (buf, from);
int to_byte = buf_charpos_to_bytepos (buf, from + nchars);
int incoming_nbytes = to_byte - from_byte;
if (NILP (current_buffer->enable_multibyte_characters))
outgoing_nbytes = nchars;
else if (NILP (buf->enable_multibyte_characters))
- outgoing_nbytes
- = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, from_byte),
- incoming_nbytes);
+ {
+ int outgoing_before_gap = 0;
+ int outgoing_after_gap = 0;
+
+ if (from < BUF_GPT (buf))
+ {
+ chunk = BUF_GPT_BYTE (buf) - from_byte;
+ if (chunk > incoming_nbytes)
+ chunk = incoming_nbytes;
+ outgoing_before_gap
+ = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, from_byte),
+ chunk);
+ }
+ else
+ chunk = 0;
+
+ if (chunk < incoming_nbytes)
+ outgoing_after_gap
+ = 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))
chunk = BUF_GPT_BYTE (buf) - from_byte;
if (chunk > incoming_nbytes)
chunk = incoming_nbytes;
- copy_text (BUF_BYTE_ADDRESS (buf, from_byte),
- GPT_ADDR, chunk,
- ! NILP (buf->enable_multibyte_characters),
- ! NILP (current_buffer->enable_multibyte_characters));
+ /* Record number of output bytes, so we know where
+ to put the output from the second copy_text. */
+ chunk_expanded
+ = copy_text (BUF_BYTE_ADDRESS (buf, from_byte),
+ GPT_ADDR, chunk,
+ ! NILP (buf->enable_multibyte_characters),
+ ! NILP (current_buffer->enable_multibyte_characters));
}
else
- chunk = 0;
+ chunk_expanded = chunk = 0;
+
if (chunk < incoming_nbytes)
copy_text (BUF_BYTE_ADDRESS (buf, from_byte + chunk),
- GPT_ADDR + chunk, incoming_nbytes - chunk,
+ GPT_ADDR + chunk_expanded, incoming_nbytes - chunk,
! NILP (buf->enable_multibyte_characters),
! NILP (current_buffer->enable_multibyte_characters));
combined_before_bytes
= count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE);
combined_after_bytes
- = count_combining_after (GPT_ADDR, outgoing_nbytes,
- PT, PT_BYTE);
+ = count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE);
+ {
+ unsigned char save = *(GPT_ADDR);
+ *(GPT_ADDR) = 0;
+ CHECK_BYTE_COMBINING_FOR_INSERT (PT);
+ *(GPT_ADDR) = save;
+ }
/* Record deletion of the surrounding text that combines with
the insertion. This, together with recording the insertion,
= count_combining_before (GPT_ADDR, len_byte, from, from_byte);
int combined_after_bytes
= count_combining_after (GPT_ADDR, len_byte, from, from_byte);
+ /* This flag tells if we combine some bytes with a character before
+ FROM. This happens even if combined_before_bytes is zero. */
+ int combine_before = (combined_before_bytes
+ || (len == 0 && combined_after_bytes));
+
int nchars_del = 0, nbytes_del = 0;
+ if (STRINGP (prev_text))
+ {
+ nchars_del = XSTRING (prev_text)->size;
+ nbytes_del = STRING_BYTES (XSTRING (prev_text));
+ }
+
+ if (combine_before && from == BEGV
+ || combined_after_bytes && from == ZV)
+ {
+ /* We can't combine bytes nor signal an error here. So, let's
+ pretend that the new text is just a single space. */
+ len = len_byte = 1;
+ combined_before_bytes = combined_after_bytes = 0;
+ *(GPT_ADDR) = ' ';
+ }
+
if (combined_after_bytes)
{
Lisp_Object deletion;
from_byte + combined_after_bytes);
if (! EQ (current_buffer->undo_list, Qt))
- record_delete (from + len, deletion);
+ record_delete (from + nchars_del, deletion);
}
- if (combined_before_bytes)
+ if (combined_before_bytes
+ || len_byte == 0 && combined_after_bytes > 0)
{
Lisp_Object deletion;
deletion = Qnil;
GPT += len; GPT_BYTE += len_byte;
if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
+ /* The gap should be at character boundary. */
if (combined_after_bytes)
move_gap_both (GPT + combined_after_bytes,
GPT_BYTE + combined_after_bytes);
- if (STRINGP (prev_text))
- {
- nchars_del = XSTRING (prev_text)->size;
- nbytes_del = STRING_BYTES (XSTRING (prev_text));
- }
adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
len, len_byte,
combined_before_bytes, combined_after_bytes);
- if (STRINGP (prev_text))
- record_delete (from - !!combined_before_bytes, prev_text);
- record_insert (from - !!combined_before_bytes,
- len - combined_before_bytes + !!combined_before_bytes);
+ if (! EQ (current_buffer->undo_list, Qt))
+ {
+ if (nchars_del > 0)
+ record_delete (from - combine_before, prev_text);
+ if (combine_before)
+ record_insert (from - 1, len - combined_before_bytes + 1);
+ else
+ record_insert (from, len);
+ }
if (len > nchars_del)
adjust_overlays_for_insert (from, len - nchars_del);
if (combined_after_bytes)
{
- if (combined_before_bytes)
+ if (combined_before_bytes == len_byte)
+ /* This is the case that all new bytes are combined. */
combined_before_bytes += combined_after_bytes;
else
combine_bytes (from + len, from_byte + len_byte,
combined_after_bytes);
}
-
if (combined_before_bytes)
combine_bytes (from, from_byte, combined_before_bytes);
}
+ /* 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)
if (to < GPT)
gap_left (to, to_byte, 0);
- deletion = Qnil;
-
- if (! EQ (current_buffer->undo_list, Qt))
- deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
+ /* Even if we don't record for undo, we must keep the original text
+ because we may have to recover it because of inappropriate byte
+ combining. */
+ deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
if (markers)
/* Relocate all markers pointing into the new, larger gap
combined_after_bytes
= count_combining_after (GPT_ADDR, outgoing_insbytes, from, from_byte);
+ if (combined_before_bytes && from == BEGV
+ || combined_after_bytes && from == ZV)
+ {
+ /* Bytes are being combined across the region boundary. We
+ should avoid it. We recover the original contents before
+ signaling an error. */
+ bcopy (XSTRING (deletion)->data, GPT_ADDR, nbytes_del);
+ GAP_SIZE -= nbytes_del;
+ ZV += nchars_del;
+ Z += nchars_del;
+ ZV_BYTE += nbytes_del;
+ Z_BYTE += nbytes_del;
+ GPT = from + nchars_del;
+ GPT_BYTE = from_byte + nbytes_del;
+ *(GPT_ADDR) = 0; /* Put an anchor. */
+ if (markers)
+ adjust_markers_for_insert (from, from_byte, to, to_byte, 0, 0, 0);
+ UNGCPRO;
+ byte_combining_error ();
+ GCPRO1 (new);
+ }
+
/* Record deletion of the surrounding text that combines with
the insertion. This, together with recording the insertion,
will add up to the right stuff in the undo list.
if (! EQ (current_buffer->undo_list, Qt))
deletion = make_buffer_string_both (from, from_byte,
from + combined_after_bytes,
- from_byte + combined_after_bytes, 1);
+ from_byte + combined_after_bytes,
+ 1);
adjust_markers_for_record_delete (from, from_byte,
from + combined_after_bytes,
from_byte + combined_after_bytes);
if (! EQ (current_buffer->undo_list, Qt))
- record_delete (from + inschars, deletion);
+ record_delete (from + nchars_del, deletion);
}
if (combined_before_bytes)
if (combined_after_bytes)
{
- if (combined_before_bytes)
+ if (combined_before_bytes == outgoing_insbytes)
+ /* This is the case that all new bytes are combined. */
combined_before_bytes += combined_after_bytes;
else
combine_bytes (from + inschars, from_byte + outgoing_insbytes,
if (combined_before_bytes)
combine_bytes (from, from_byte, combined_before_bytes);
+ /* As byte combining will decrease Z, we must check this again. */
+ if (Z - GPT < end_unchanged)
+ end_unchanged = Z - GPT;
+
if (outgoing_insbytes == 0)
evaporate_overlays (from);
to_byte = CHAR_TO_BYTE (to);
del_range_2 (from, from_byte, to, to_byte);
+ signal_after_change (from, to - from, 0);
}
/* Like del_range_1 but args are byte positions, not char positions. */
}
del_range_2 (from, from_byte, to, to_byte);
+ signal_after_change (from, to - from, 0);
}
/* Like del_range_1, but positions are specified both as charpos
}
del_range_2 (from, from_byte, to, to_byte);
+ signal_after_change (from, to - from, 0);
}
/* Delete a range of text, specified both as character positions
combined_after_bytes
= count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
- ZV_BYTE - to_byte, from, from_byte);
+ Z_BYTE - to_byte, from, from_byte);
if (combined_after_bytes)
{
+ if (from == BEGV || to == ZV)
+ byte_combining_error ();
from_byte_1 = from_byte;
DEC_POS (from_byte_1);
}
if (combined_after_bytes)
{
+ /* Adjust markers for byte combining. As we have already
+ adjuted markers without concerning byte combining, here we
+ must concern only byte combining. */
+ adjust_markers_for_replace (from, from_byte, 0, 0, 0, 0,
+ 0, combined_after_bytes);
combine_bytes (from, from_byte, combined_after_bytes);
record_insert (GPT - 1, 1);
+
+ if (Z - GPT < end_unchanged)
+ end_unchanged = Z - GPT;
}
CHECK_MARKERS ();
evaporate_overlays (from);
- signal_after_change (from, nchars_del, 0);
}
\f
/* Call this if you're about to change the region of BUFFER from
Lisp_Object preserve_marker;
struct gcpro gcpro1, gcpro2, gcpro3;
+ if (inhibit_modification_hooks)
+ return;
+
start = make_number (start_int);
end = make_number (end_int);
preserve_marker = Qnil;
signal_after_change (charpos, lendel, lenins)
int charpos, lendel, lenins;
{
+ if (inhibit_modification_hooks)
+ return;
+
/* If we are deferring calls to the after-change functions
and there are no before-change functions,
just record the args that we were going to use. */
"This function is for use internally in `combine-after-change-calls'.")
()
{
- register Lisp_Object val;
int count = specpdl_ptr - specpdl;
int beg, end, change;
int begpos, endpos;
Lisp_Object tail;
+ if (NILP (combine_after_change_list))
+ return Qnil;
+
record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
Fset_buffer (combine_after_change_buffer);
Vcombine_after_change_calls);
signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
- return unbind_to (count, val);
+ return unbind_to (count, Qnil);
}
\f
void
{
staticpro (&combine_after_change_list);
combine_after_change_list = Qnil;
+ 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.");