#include <config.h>
#include "lisp.h"
#include "intervals.h"
#include "buffer.h"
#include "charset.h"
#include "window.h"
#include "blockinput.h"
#include "region-cache.h"
#ifndef NULL
#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));
static void gap_right P_ ((int, int));
static void adjust_markers_gap_motion P_ ((int, int, int));
static void adjust_markers_for_insert P_ ((int, int, int, int, int));
void adjust_markers_for_delete P_ ((int, int, int, int));
static void adjust_markers_for_replace P_ ((int, int, int, int, int, int));
static void adjust_point P_ ((int, int));
Lisp_Object Fcombine_after_change_execute ();
Lisp_Object Vcombine_after_change_calls;
Lisp_Object combine_after_change_list;
Lisp_Object combine_after_change_buffer;
Lisp_Object Qinhibit_modification_hooks;
static int check_markers_debug_flag;
#define CHECK_MARKERS() \
if (check_markers_debug_flag) \
check_markers (); \
else
void
check_markers ()
{
register Lisp_Object tail;
int multibyte = ! NILP (current_buffer->enable_multibyte_characters);
tail = BUF_MARKERS (current_buffer);
while (! NILP (tail))
{
if (XMARKER (tail)->buffer->text != current_buffer->text)
abort ();
if (XMARKER (tail)->charpos > Z)
abort ();
if (XMARKER (tail)->bytepos > Z_BYTE)
abort ();
if (multibyte && ! CHAR_HEAD_P (FETCH_BYTE (XMARKER (tail)->bytepos)))
abort ();
tail = XMARKER (tail)->chain;
}
}
void
move_gap (charpos)
int charpos;
{
move_gap_both (charpos, charpos_to_bytepos (charpos));
}
void
move_gap_both (charpos, bytepos)
int charpos, bytepos;
{
if (bytepos < GPT_BYTE)
gap_left (charpos, bytepos, 0);
else if (bytepos > GPT_BYTE)
gap_right (charpos, bytepos);
}
static void
gap_left (charpos, bytepos, newgap)
register int charpos, bytepos;
int newgap;
{
register unsigned char *to, *from;
register int i;
int new_s1;
if (!newgap)
BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
i = GPT_BYTE;
to = GAP_END_ADDR;
from = GPT_ADDR;
new_s1 = GPT_BYTE;
while (1)
{
i = new_s1 - bytepos;
if (i == 0)
break;
if (QUITP)
{
bytepos = new_s1;
charpos = BYTE_TO_CHAR (bytepos);
break;
}
if (i > 32000)
i = 32000;
#ifdef GAP_USE_BCOPY
if (i >= 128
&& (BCOPY_UPWARD_SAFE
|| to - from >= 128))
{
if (!BCOPY_UPWARD_SAFE && i > to - from)
i = to - from;
new_s1 -= i;
from -= i, to -= i;
bcopy (from, to, i);
}
else
#endif
{
new_s1 -= i;
while (--i >= 0)
*--to = *--from;
}
}
adjust_markers_gap_motion (bytepos, GPT_BYTE, GAP_SIZE);
GPT_BYTE = bytepos;
GPT = charpos;
if (bytepos < charpos)
abort ();
if (GAP_SIZE > 0) *(GPT_ADDR) = 0;
QUIT;
}
static void
gap_right (charpos, bytepos)
register int charpos, bytepos;
{
register unsigned char *to, *from;
register int i;
int new_s1;
BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
i = GPT_BYTE;
from = GAP_END_ADDR;
to = GPT_ADDR;
new_s1 = GPT_BYTE;
while (1)
{
i = bytepos - new_s1;
if (i == 0)
break;
if (QUITP)
{
bytepos = new_s1;
charpos = BYTE_TO_CHAR (bytepos);
break;
}
if (i > 32000)
i = 32000;
#ifdef GAP_USE_BCOPY
if (i >= 128
&& (BCOPY_DOWNWARD_SAFE
|| from - to >= 128))
{
if (!BCOPY_DOWNWARD_SAFE && i > from - to)
i = from - to;
new_s1 += i;
bcopy (from, to, i);
from += i, to += i;
}
else
#endif
{
new_s1 += i;
while (--i >= 0)
*to++ = *from++;
}
}
adjust_markers_gap_motion (GPT_BYTE + GAP_SIZE, bytepos + GAP_SIZE,
- GAP_SIZE);
GPT = charpos;
GPT_BYTE = bytepos;
if (bytepos < charpos)
abort ();
if (GAP_SIZE > 0) *(GPT_ADDR) = 0;
QUIT;
}
int adjust_markers_test;
static void
adjust_markers_gap_motion (from, to, amount)
register int from, to, amount;
{
#if 0
Lisp_Object marker;
register struct Lisp_Marker *m;
register int mpos;
marker = BUF_MARKERS (current_buffer);
while (!NILP (marker))
{
m = XMARKER (marker);
mpos = m->bytepos;
if (amount > 0)
{
if (mpos > to && mpos < to + amount)
{
if (adjust_markers_test)
abort ();
mpos = to + amount;
}
}
else
{
if (mpos > from + amount && mpos <= from)
{
if (adjust_markers_test)
abort ();
mpos = from + amount;
}
}
if (mpos > from && mpos <= to)
mpos += amount;
m->bufpos = mpos;
marker = m->chain;
}
#endif
}
void
adjust_markers_for_delete (from, from_byte, to, to_byte)
register int from, from_byte, to, to_byte;
{
Lisp_Object marker;
register struct Lisp_Marker *m;
register int charpos;
marker = BUF_MARKERS (current_buffer);
while (!NILP (marker))
{
m = XMARKER (marker);
charpos = m->charpos;
if (charpos > Z)
abort ();
if (charpos > to)
{
m->charpos -= to - from;
m->bytepos -= to_byte - from_byte;
}
else if (charpos > from)
{
if (! m->insertion_type)
record_marker_adjustment (marker, from - charpos);
else if (charpos < to)
record_marker_adjustment (marker, charpos - to);
m->charpos = from;
m->bytepos = from_byte;
}
else if (charpos == from && m->insertion_type)
{
record_marker_adjustment (marker, to - from);
}
marker = m->chain;
}
}
static void
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;
int adjusted = 0;
int nchars = to - from;
int nbytes = to_byte - from_byte;
marker = BUF_MARKERS (current_buffer);
while (!NILP (marker))
{
register struct Lisp_Marker *m = XMARKER (marker);
if (Z == Z_BYTE)
{
if (m->charpos != m->bytepos)
abort ();
}
if (m->bytepos == from_byte)
{
if (m->insertion_type || before_markers)
{
m->bytepos = to_byte;
m->charpos = to;
if (m->insertion_type)
adjusted = 1;
}
}
else if (m->bytepos > from_byte)
{
m->bytepos += nbytes;
m->charpos += nchars;
}
marker = m->chain;
}
if (adjusted)
fix_overlays_before (current_buffer, from, to);
}
static void
adjust_point (nchars, nbytes)
int nchars, nbytes;
{
BUF_PT (current_buffer) += nchars;
BUF_PT_BYTE (current_buffer) += nbytes;
if (ZV == ZV_BYTE
&& PT != PT_BYTE)
abort ();
}
static void
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);
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))
{
register struct Lisp_Marker *m = XMARKER (marker);
if (m->bytepos >= prev_to_byte)
{
m->charpos += diff_chars;
m->bytepos += diff_bytes;
}
else if (m->bytepos > from_byte)
{
m->charpos = from;
m->bytepos = from_byte;
}
marker = m->chain;
}
CHECK_MARKERS ();
}
void
make_gap (nbytes_added)
int nbytes_added;
{
Lisp_Object tem;
int real_gap_loc;
int real_gap_loc_byte;
int old_gap_size;
nbytes_added += 2000;
if (Z_BYTE - BEG_BYTE + GAP_SIZE + nbytes_added
>= ((unsigned) 1 << (min (BITS_PER_INT, VALBITS) - 1)))
error ("Buffer exceeds maximum size");
enlarge_buffer_text (current_buffer, nbytes_added);
tem = Vinhibit_quit;
Vinhibit_quit = Qt;
real_gap_loc = GPT;
real_gap_loc_byte = GPT_BYTE;
old_gap_size = GAP_SIZE;
GPT = Z + GAP_SIZE;
GPT_BYTE = Z_BYTE + GAP_SIZE;
GAP_SIZE = nbytes_added;
gap_left (real_gap_loc + old_gap_size, real_gap_loc_byte + old_gap_size, 1);
GAP_SIZE += old_gap_size;
GPT = real_gap_loc;
GPT_BYTE = real_gap_loc_byte;
*(Z_ADDR) = 0;
Vinhibit_quit = tem;
}
int
copy_text (from_addr, to_addr, nbytes,
from_multibyte, to_multibyte)
unsigned char *from_addr;
unsigned char *to_addr;
int nbytes;
int from_multibyte, to_multibyte;
{
if (from_multibyte == to_multibyte)
{
bcopy (from_addr, to_addr, nbytes);
return nbytes;
}
else if (from_multibyte)
{
int nchars = 0;
int bytes_left = nbytes;
Lisp_Object tbl = Qnil;
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;
}
while (bytes_left > 0)
{
int thislen, c;
c = STRING_CHAR_AND_LENGTH (from_addr, bytes_left, thislen);
if (!SINGLE_BYTE_CHAR_P (c))
c = multibyte_char_to_unibyte (c, tbl);
*to_addr++ = c;
from_addr += thislen;
bytes_left -= thislen;
nchars++;
}
return nchars;
}
else
{
unsigned char *initial_to_addr = to_addr;
while (nbytes > 0)
{
int c = *from_addr++;
if (c >= 0200)
{
c = unibyte_char_to_multibyte (c);
to_addr += CHAR_STRING (c, to_addr);
nbytes--;
}
else
*to_addr++ = c, nbytes--;
}
return to_addr - initial_to_addr;
}
}
int
count_size_as_multibyte (ptr, nbytes)
unsigned char *ptr;
int nbytes;
{
int i;
int outgoing_nbytes = 0;
for (i = 0; i < nbytes; i++)
{
unsigned int c = *ptr++;
if (c < 0200)
outgoing_nbytes++;
else
{
c = unibyte_char_to_multibyte (c);
outgoing_nbytes += CHAR_BYTES (c);
}
}
return outgoing_nbytes;
}
void
insert (string, nbytes)
register unsigned char *string;
register int nbytes;
{
if (nbytes > 0)
{
int opoint = PT;
insert_1 (string, nbytes, 0, 1, 0);
signal_after_change (opoint, 0, PT - opoint);
update_compositions (opoint, PT, CHECK_BORDER);
}
}
void
insert_and_inherit (string, nbytes)
register unsigned char *string;
register int nbytes;
{
if (nbytes > 0)
{
int opoint = PT;
insert_1 (string, nbytes, 1, 1, 0);
signal_after_change (opoint, 0, PT - opoint);
update_compositions (opoint, PT, CHECK_BORDER);
}
}
void
insert_char (c)
int c;
{
unsigned char str[MAX_MULTIBYTE_LENGTH];
int len;
if (! NILP (current_buffer->enable_multibyte_characters))
len = CHAR_STRING (c, str);
else
{
len = 1;
str[0] = c;
}
insert (str, len);
}
void
insert_string (s)
char *s;
{
insert (s, strlen (s));
}
void
insert_before_markers (string, nbytes)
unsigned char *string;
register int nbytes;
{
if (nbytes > 0)
{
int opoint = PT;
insert_1 (string, nbytes, 0, 1, 1);
signal_after_change (opoint, 0, PT - opoint);
update_compositions (opoint, PT, CHECK_BORDER);
}
}
void
insert_before_markers_and_inherit (string, nbytes)
unsigned char *string;
register int nbytes;
{
if (nbytes > 0)
{
int opoint = PT;
insert_1 (string, nbytes, 1, 1, 1);
signal_after_change (opoint, 0, PT - opoint);
update_compositions (opoint, PT, CHECK_BORDER);
}
}
void
insert_1 (string, nbytes, inherit, prepare, before_markers)
register unsigned char *string;
register int nbytes;
int inherit, prepare, before_markers;
{
insert_1_both (string, chars_in_text (string, nbytes), nbytes,
inherit, prepare, before_markers);
}
#ifdef BYTE_COMBINING_DEBUG
int
count_combining_before (string, length, pos, pos_byte)
unsigned char *string;
int length;
int pos, pos_byte;
{
int len, combining_bytes;
unsigned char *p;
if (NILP (current_buffer->enable_multibyte_characters))
return 0;
if (length == 0 || CHAR_HEAD_P (*string))
return 0;
if (pos_byte == BEG_BYTE)
return 0;
len = 1;
p = BYTE_POS_ADDR (pos_byte - 1);
while (! CHAR_HEAD_P (*p)) p--, len++;
if (! BASE_LEADING_CODE_P (*p))
return 0;
combining_bytes = BYTES_BY_CHAR_HEAD (*p) - len;
if (combining_bytes <= 0)
return 0;
p = string + 1;
while (!CHAR_HEAD_P (*p) && p < string + length)
p++;
return (combining_bytes < p - string ? combining_bytes : p - string);
}
int
count_combining_after (string, length, pos, pos_byte)
unsigned char *string;
int length;
int pos, pos_byte;
{
int opos_byte = pos_byte;
int i;
int bytes;
unsigned char *bufp;
if (NILP (current_buffer->enable_multibyte_characters))
return 0;
if (length > 0 && ASCII_BYTE_P (string[length - 1]))
return 0;
if (pos_byte == Z_BYTE)
return 0;
bufp = BYTE_POS_ADDR (pos_byte);
if (CHAR_HEAD_P (*bufp))
return 0;
i = length - 1;
while (i >= 0 && ! CHAR_HEAD_P (string[i]))
{
i--;
}
if (i < 0)
{
unsigned char *p = BEG_ADDR;
i = pos_byte - 2;
while (i >= 0 && ! CHAR_HEAD_P (p[i]))
i--;
if (i < 0 || !BASE_LEADING_CODE_P (p[i]))
return 0;
bytes = BYTES_BY_CHAR_HEAD (p[i]);
return (bytes <= pos_byte - 1 - i + length
? 0
: bytes - (pos_byte - 1 - i + length));
}
if (!BASE_LEADING_CODE_P (string[i]))
return 0;
bytes = BYTES_BY_CHAR_HEAD (string[i]) - (length - i);
bufp++, pos_byte++;
while (!CHAR_HEAD_P (*bufp)) bufp++, pos_byte++;
return (bytes <= pos_byte - opos_byte ? bytes : pos_byte - opos_byte);
}
#endif
void
insert_1_both (string, nchars, nbytes, inherit, prepare, before_markers)
register unsigned char *string;
register int nchars, nbytes;
int inherit, prepare, before_markers;
{
if (NILP (current_buffer->enable_multibyte_characters))
nchars = nbytes;
if (prepare)
prepare_to_modify_buffer (PT, PT, NULL);
if (PT != GPT)
move_gap_both (PT, PT_BYTE);
if (GAP_SIZE < nbytes)
make_gap (nbytes - GAP_SIZE);
#ifdef BYTE_COMBINING_DEBUG
if (count_combining_before (string, nbytes, PT, PT_BYTE)
|| count_combining_after (string, nbytes, PT, PT_BYTE))
abort ();
#endif
record_insert (PT, nchars);
MODIFF++;
bcopy (string, GPT_ADDR, nbytes);
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;
if (GPT_BYTE < GPT)
abort ();
adjust_overlays_for_insert (PT, nchars);
adjust_markers_for_insert (PT, PT_BYTE,
PT + nchars, PT_BYTE + nbytes,
before_markers);
if (BUF_INTERVALS (current_buffer) != 0)
offset_intervals (current_buffer, PT, nchars);
if (!inherit && BUF_INTERVALS (current_buffer) != 0)
set_text_properties (make_number (PT), make_number (PT + nchars),
Qnil, Qnil, Qnil);
adjust_point (nchars, nbytes);
CHECK_MARKERS ();
}
void
insert_from_string (string, pos, pos_byte, length, length_byte, inherit)
Lisp_Object string;
register int pos, pos_byte, length, length_byte;
int inherit;
{
int opoint = PT;
insert_from_string_1 (string, pos, pos_byte, length, length_byte,
inherit, 0);
signal_after_change (opoint, 0, PT - opoint);
update_compositions (opoint, PT, CHECK_BORDER);
}
void
insert_from_string_before_markers (string, pos, pos_byte,
length, length_byte, inherit)
Lisp_Object string;
register int pos, pos_byte, length, length_byte;
int inherit;
{
int opoint = PT;
insert_from_string_1 (string, pos, pos_byte, length, length_byte,
inherit, 1);
signal_after_change (opoint, 0, PT - opoint);
update_compositions (opoint, PT, CHECK_BORDER);
}
static void
insert_from_string_1 (string, pos, pos_byte, nchars, nbytes,
inherit, before_markers)
Lisp_Object string;
register int pos, pos_byte, nchars, nbytes;
int inherit, before_markers;
{
struct gcpro gcpro1;
int outgoing_nbytes = nbytes;
INTERVAL intervals;
if (NILP (current_buffer->enable_multibyte_characters))
outgoing_nbytes = nchars;
else if (! STRING_MULTIBYTE (string))
outgoing_nbytes
= count_size_as_multibyte (&XSTRING (string)->data[pos_byte],
nbytes);
GCPRO1 (string);
prepare_to_modify_buffer (PT, PT, NULL);
if (PT != GPT)
move_gap_both (PT, PT_BYTE);
if (GAP_SIZE < outgoing_nbytes)
make_gap (outgoing_nbytes - GAP_SIZE);
UNGCPRO;
copy_text (XSTRING (string)->data + pos_byte, GPT_ADDR, nbytes,
STRING_MULTIBYTE (string),
! NILP (current_buffer->enable_multibyte_characters));
#ifdef BYTE_COMBINING_DEBUG
if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
|| count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
abort ();
#endif
record_insert (PT, nchars);
MODIFF++;
GAP_SIZE -= outgoing_nbytes;
GPT += nchars;
ZV += nchars;
Z += nchars;
GPT_BYTE += outgoing_nbytes;
ZV_BYTE += outgoing_nbytes;
Z_BYTE += outgoing_nbytes;
if (GAP_SIZE > 0) *(GPT_ADDR) = 0;
if (GPT_BYTE < GPT)
abort ();
adjust_overlays_for_insert (PT, nchars);
adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
PT_BYTE + outgoing_nbytes,
before_markers);
offset_intervals (current_buffer, PT, nchars);
intervals = XSTRING (string)->intervals;
if (nbytes < STRING_BYTES (XSTRING (string)))
intervals = copy_intervals (intervals, pos, nchars);
graft_intervals_into_buffer (intervals, PT, nchars,
current_buffer, inherit);
adjust_point (nchars, outgoing_nbytes);
}
void
insert_from_buffer (buf, charpos, nchars, inherit)
struct buffer *buf;
int charpos, nchars;
int inherit;
{
int opoint = PT;
insert_from_buffer_1 (buf, charpos, nchars, inherit);
signal_after_change (opoint, 0, PT - opoint);
update_compositions (opoint, PT, CHECK_BORDER);
}
static void
insert_from_buffer_1 (buf, from, nchars, inherit)
struct buffer *buf;
int from, nchars;
int inherit;
{
register Lisp_Object temp;
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;
int outgoing_nbytes = incoming_nbytes;
INTERVAL intervals;
if (NILP (current_buffer->enable_multibyte_characters))
outgoing_nbytes = nchars;
else if (NILP (buf->enable_multibyte_characters))
{
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;
}
XSETINT (temp, outgoing_nbytes + Z);
if (outgoing_nbytes + Z != XINT (temp))
error ("Maximum buffer size exceeded");
prepare_to_modify_buffer (PT, PT, NULL);
if (PT != GPT)
move_gap_both (PT, PT_BYTE);
if (GAP_SIZE < outgoing_nbytes)
make_gap (outgoing_nbytes - GAP_SIZE);
if (from < BUF_GPT (buf))
{
chunk = BUF_GPT_BYTE (buf) - from_byte;
if (chunk > incoming_nbytes)
chunk = incoming_nbytes;
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_expanded = chunk = 0;
if (chunk < incoming_nbytes)
copy_text (BUF_BYTE_ADDRESS (buf, from_byte + chunk),
GPT_ADDR + chunk_expanded, incoming_nbytes - chunk,
! NILP (buf->enable_multibyte_characters),
! NILP (current_buffer->enable_multibyte_characters));
#ifdef BYTE_COMBINING_DEBUG
if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
|| count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
abort ();
#endif
record_insert (PT, nchars);
MODIFF++;
GAP_SIZE -= outgoing_nbytes;
GPT += nchars;
ZV += nchars;
Z += nchars;
GPT_BYTE += outgoing_nbytes;
ZV_BYTE += outgoing_nbytes;
Z_BYTE += outgoing_nbytes;
if (GAP_SIZE > 0) *(GPT_ADDR) = 0;
if (GPT_BYTE < GPT)
abort ();
adjust_overlays_for_insert (PT, nchars);
adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
PT_BYTE + outgoing_nbytes,
0);
if (BUF_INTERVALS (current_buffer) != 0)
offset_intervals (current_buffer, PT, nchars);
intervals = BUF_INTERVALS (buf);
if (outgoing_nbytes < BUF_Z_BYTE (buf) - BUF_BEG_BYTE (buf))
{
if (buf == current_buffer && PT <= from)
from += nchars;
intervals = copy_intervals (intervals, from, nchars);
}
graft_intervals_into_buffer (intervals, PT, nchars, current_buffer, inherit);
adjust_point (nchars, outgoing_nbytes);
}
void
adjust_after_replace (from, from_byte, prev_text, len, len_byte)
int from, from_byte, len, len_byte;
Lisp_Object prev_text;
{
int nchars_del = 0, nbytes_del = 0;
#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
if (STRINGP (prev_text))
{
nchars_del = XSTRING (prev_text)->size;
nbytes_del = STRING_BYTES (XSTRING (prev_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;
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 (! EQ (current_buffer->undo_list, Qt))
{
if (nchars_del > 0)
record_delete (from, prev_text);
record_insert (from, len);
}
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);
if (Z - GPT < END_UNCHANGED)
END_UNCHANGED = Z - GPT;
CHECK_MARKERS ();
if (len == 0)
evaporate_overlays (from);
MODIFF++;
}
void
adjust_after_insert (from, from_byte, to, to_byte, newlen)
int from, from_byte, to, to_byte, newlen;
{
int len = to - from, len_byte = to_byte - from_byte;
if (GPT != to)
move_gap_both (to, to_byte);
GAP_SIZE += len_byte;
GPT -= len; GPT_BYTE -= len_byte;
ZV -= len; ZV_BYTE -= len_byte;
Z -= len; Z_BYTE -= len_byte;
adjust_after_replace (from, from_byte, Qnil, newlen, len_byte);
}
void
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 from_byte, to_byte;
int nbytes_del, nchars_del;
register Lisp_Object temp;
struct gcpro gcpro1;
INTERVAL intervals;
int outgoing_insbytes = insbytes;
Lisp_Object deletion;
CHECK_MARKERS ();
GCPRO1 (new);
deletion = Qnil;
if (prepare)
{
int range_length = to - from;
prepare_to_modify_buffer (from, to, &from);
to = from + range_length;
}
UNGCPRO;
if (from < BEGV)
from = BEGV;
if (to > ZV)
to = ZV;
from_byte = CHAR_TO_BYTE (from);
to_byte = CHAR_TO_BYTE (to);
nchars_del = to - from;
nbytes_del = to_byte - from_byte;
if (nbytes_del <= 0 && insbytes == 0)
return;
if (NILP (current_buffer->enable_multibyte_characters))
outgoing_insbytes = inschars;
else if (! STRING_MULTIBYTE (new))
outgoing_insbytes
= count_size_as_multibyte (XSTRING (new)->data, insbytes);
XSETINT (temp, Z_BYTE - nbytes_del + insbytes);
if (Z_BYTE - nbytes_del + insbytes != XINT (temp))
error ("Maximum buffer size exceeded");
GCPRO1 (new);
if (from > GPT)
gap_right (from, from_byte);
if (to < GPT)
gap_left (to, to_byte, 0);
if (! EQ (current_buffer->undo_list, Qt))
deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
if (markers)
adjust_markers_for_delete (from, from_byte, to, to_byte);
GAP_SIZE += nbytes_del;
ZV -= nchars_del;
Z -= nchars_del;
ZV_BYTE -= nbytes_del;
Z_BYTE -= nbytes_del;
GPT = from;
GPT_BYTE = from_byte;
*(GPT_ADDR) = 0;
if (GPT_BYTE < GPT)
abort ();
if (GPT - BEG < BEG_UNCHANGED)
BEG_UNCHANGED = GPT - BEG;
if (Z - GPT < END_UNCHANGED)
END_UNCHANGED = Z - GPT;
if (GAP_SIZE < insbytes)
make_gap (insbytes - GAP_SIZE);
copy_text (XSTRING (new)->data, GPT_ADDR, insbytes,
STRING_MULTIBYTE (new),
! NILP (current_buffer->enable_multibyte_characters));
#ifdef BYTE_COMBINING_DEBUG
if (count_combining_before (GPT_ADDR, outgoing_insbytes, from, from_byte)
|| count_combining_after (GPT_ADDR, outgoing_insbytes, from, from_byte))
abort ();
#endif
if (! EQ (current_buffer->undo_list, Qt))
{
record_delete (from, deletion);
record_insert (from, inschars);
}
GAP_SIZE -= outgoing_insbytes;
GPT += inschars;
ZV += inschars;
Z += inschars;
GPT_BYTE += outgoing_insbytes;
ZV_BYTE += outgoing_insbytes;
Z_BYTE += outgoing_insbytes;
if (GAP_SIZE > 0) *(GPT_ADDR) = 0;
if (GPT_BYTE < GPT)
abort ();
adjust_overlays_for_delete (from, nchars_del);
adjust_overlays_for_insert (from, inschars);
if (markers)
adjust_markers_for_insert (from, from_byte,
from + inschars, from_byte + outgoing_insbytes,
0);
offset_intervals (current_buffer, from, inschars - nchars_del);
intervals = XSTRING (new)->intervals;
graft_intervals_into_buffer (intervals, from, inschars,
current_buffer, inherit);
if (from < PT)
adjust_point ((from + inschars - (PT < to ? PT : to)),
(from_byte + outgoing_insbytes
- (PT_BYTE < to_byte ? PT_BYTE : to_byte)));
if (outgoing_insbytes == 0)
evaporate_overlays (from);
CHECK_MARKERS ();
MODIFF++;
UNGCPRO;
signal_after_change (from, nchars_del, GPT - from);
update_compositions (from, GPT, CHECK_BORDER);
}
void
del_range (from, to)
register int from, to;
{
del_range_1 (from, to, 1, 0);
}
Lisp_Object
del_range_1 (from, to, prepare, ret_string)
int from, to, prepare, ret_string;
{
int from_byte, to_byte;
Lisp_Object deletion;
struct gcpro gcpro1;
if (from < BEGV)
from = BEGV;
if (to > ZV)
to = ZV;
if (to <= from)
return Qnil;
if (prepare)
{
int range_length = to - from;
prepare_to_modify_buffer (from, to, &from);
to = min (ZV, from + range_length);
}
from_byte = CHAR_TO_BYTE (from);
to_byte = CHAR_TO_BYTE (to);
deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
GCPRO1(deletion);
signal_after_change (from, to - from, 0);
update_compositions (from, from, CHECK_HEAD);
UNGCPRO;
return deletion;
}
void
del_range_byte (from_byte, to_byte, prepare)
int from_byte, to_byte, prepare;
{
int from, to;
if (from_byte < BEGV_BYTE)
from_byte = BEGV_BYTE;
if (to_byte > ZV_BYTE)
to_byte = ZV_BYTE;
if (to_byte <= from_byte)
return;
from = BYTE_TO_CHAR (from_byte);
to = BYTE_TO_CHAR (to_byte);
if (prepare)
{
int old_from = from, old_to = Z - to;
int range_length = to - from;
prepare_to_modify_buffer (from, to, &from);
to = from + range_length;
if (old_from != from)
from_byte = CHAR_TO_BYTE (from);
if (to > ZV)
{
to = ZV;
to_byte = ZV_BYTE;
}
else if (old_to == Z - to)
to_byte = CHAR_TO_BYTE (to);
}
del_range_2 (from, from_byte, to, to_byte, 0);
signal_after_change (from, to - from, 0);
update_compositions (from, from, CHECK_HEAD);
}
void
del_range_both (from, from_byte, to, to_byte, prepare)
int from, from_byte, to, to_byte, prepare;
{
if (from_byte < BEGV_BYTE)
from_byte = BEGV_BYTE;
if (to_byte > ZV_BYTE)
to_byte = ZV_BYTE;
if (to_byte <= from_byte)
return;
if (from < BEGV)
from = BEGV;
if (to > ZV)
to = ZV;
if (prepare)
{
int old_from = from, old_to = Z - to;
int range_length = to - from;
prepare_to_modify_buffer (from, to, &from);
to = from + range_length;
if (old_from != from)
from_byte = CHAR_TO_BYTE (from);
if (to > ZV)
{
to = ZV;
to_byte = ZV_BYTE;
}
else if (old_to == Z - to)
to_byte = CHAR_TO_BYTE (to);
}
del_range_2 (from, from_byte, to, to_byte, 0);
signal_after_change (from, to - from, 0);
update_compositions (from, from, CHECK_HEAD);
}
Lisp_Object
del_range_2 (from, from_byte, to, to_byte, ret_string)
int from, from_byte, to, to_byte, ret_string;
{
register int nbytes_del, nchars_del;
Lisp_Object deletion;
CHECK_MARKERS ();
nchars_del = to - from;
nbytes_del = to_byte - from_byte;
if (from > GPT)
gap_right (from, from_byte);
if (to < GPT)
gap_left (to, to_byte, 0);
#ifdef BYTE_COMBINING_DEBUG
if (count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
Z_BYTE - to_byte, from, from_byte))
abort ();
#endif
if (ret_string || ! EQ (current_buffer->undo_list, Qt))
deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
else
deletion = Qnil;
adjust_markers_for_delete (from, from_byte, to, to_byte);
if (! EQ (current_buffer->undo_list, Qt))
record_delete (from, deletion);
MODIFF++;
if (from < PT)
adjust_point (from - (PT < to ? PT : to),
from_byte - (PT_BYTE < to_byte ? PT_BYTE : to_byte));
offset_intervals (current_buffer, from, - nchars_del);
adjust_overlays_for_delete (from, nchars_del);
GAP_SIZE += nbytes_del;
ZV_BYTE -= nbytes_del;
Z_BYTE -= nbytes_del;
ZV -= nchars_del;
Z -= nchars_del;
GPT = from;
GPT_BYTE = from_byte;
*(GPT_ADDR) = 0;
if (GPT_BYTE < GPT)
abort ();
if (GPT - BEG < BEG_UNCHANGED)
BEG_UNCHANGED = GPT - BEG;
if (Z - GPT < END_UNCHANGED)
END_UNCHANGED = Z - GPT;
CHECK_MARKERS ();
evaporate_overlays (from);
return deletion;
}
void
modify_region (buffer, start, end)
struct buffer *buffer;
int start, end;
{
struct buffer *old_buffer = current_buffer;
if (buffer != old_buffer)
set_buffer_internal (buffer);
prepare_to_modify_buffer (start, end, NULL);
BUF_COMPUTE_UNCHANGED (buffer, start - 1, end);
if (MODIFF <= SAVE_MODIFF)
record_first_change ();
MODIFF++;
buffer->point_before_scroll = Qnil;
if (buffer != old_buffer)
set_buffer_internal (old_buffer);
}
void
prepare_to_modify_buffer (start, end, preserve_ptr)
int start, end;
int *preserve_ptr;
{
if (!NILP (current_buffer->read_only))
Fbarf_if_buffer_read_only ();
if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer)
++windows_or_buffers_changed;
if (BUF_INTERVALS (current_buffer) != 0)
{
if (preserve_ptr)
{
Lisp_Object preserve_marker;
struct gcpro gcpro1;
preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil);
GCPRO1 (preserve_marker);
verify_interval_modification (current_buffer, start, end);
*preserve_ptr = marker_position (preserve_marker);
unchain_marker (preserve_marker);
UNGCPRO;
}
else
verify_interval_modification (current_buffer, start, end);
}
#ifdef CLASH_DETECTION
if (!NILP (current_buffer->file_truename)
&& !NILP (current_buffer->filename)
&& SAVE_MODIFF >= MODIFF)
lock_file (current_buffer->file_truename);
#else
if (!NILP (current_buffer->filename)
&& SAVE_MODIFF >= MODIFF
&& NILP (Fverify_visited_file_modtime (Fcurrent_buffer ()))
&& !NILP (Ffile_exists_p (current_buffer->filename)))
call1 (intern ("ask-user-about-supersession-threat"),
current_buffer->filename);
#endif
signal_before_change (start, end, preserve_ptr);
if (current_buffer->newline_cache)
invalidate_region_cache (current_buffer,
current_buffer->newline_cache,
start - BEG, Z - end);
if (current_buffer->width_run_cache)
invalidate_region_cache (current_buffer,
current_buffer->width_run_cache,
start - BEG, Z - end);
Vdeactivate_mark = Qt;
}
#define PRESERVE_VALUE \
if (preserve_ptr && NILP (preserve_marker)) \
preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil)
#define RESTORE_VALUE \
if (! NILP (preserve_marker)) \
{ \
*preserve_ptr = marker_position (preserve_marker); \
unchain_marker (preserve_marker); \
}
#define PRESERVE_START_END \
if (NILP (start_marker)) \
start_marker = Fcopy_marker (start, Qnil); \
if (NILP (end_marker)) \
end_marker = Fcopy_marker (end, Qnil);
#define FETCH_START \
(! NILP (start_marker) ? Fmarker_position (start_marker) : start)
#define FETCH_END \
(! NILP (end_marker) ? Fmarker_position (end_marker) : end)
void
signal_before_change (start_int, end_int, preserve_ptr)
int start_int, end_int;
int *preserve_ptr;
{
Lisp_Object start, end;
Lisp_Object start_marker, end_marker;
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;
start_marker = Qnil;
end_marker = Qnil;
GCPRO3 (preserve_marker, start_marker, end_marker);
if (SAVE_MODIFF >= MODIFF
&& !NILP (Vfirst_change_hook)
&& !NILP (Vrun_hooks))
{
PRESERVE_VALUE;
PRESERVE_START_END;
call1 (Vrun_hooks, Qfirst_change_hook);
}
if (!NILP (Vbefore_change_functions))
{
Lisp_Object args[3];
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;
before_change_functions = Vbefore_change_functions;
after_change_functions = Vafter_change_functions;
Vbefore_change_functions = Qnil;
Vafter_change_functions = Qnil;
GCPRO2 (before_change_functions, after_change_functions);
args[0] = Qbefore_change_functions;
args[1] = FETCH_START;
args[2] = FETCH_END;
run_hook_list_with_args (before_change_functions, 3, args);
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))
{
PRESERVE_VALUE;
report_overlay_modification (FETCH_START, FETCH_END, 0,
FETCH_START, FETCH_END, Qnil);
}
if (! NILP (start_marker))
free_marker (start_marker);
if (! NILP (end_marker))
free_marker (end_marker);
RESTORE_VALUE;
UNGCPRO;
}
void
signal_after_change (charpos, lendel, lenins)
int charpos, lendel, lenins;
{
if (inhibit_modification_hooks)
return;
if (! NILP (Vcombine_after_change_calls)
&& NILP (Vbefore_change_functions)
&& NILP (current_buffer->overlays_before)
&& NILP (current_buffer->overlays_after))
{
Lisp_Object elt;
if (!NILP (combine_after_change_list)
&& current_buffer != XBUFFER (combine_after_change_buffer))
Fcombine_after_change_execute ();
elt = Fcons (make_number (charpos - BEG),
Fcons (make_number (Z - (charpos - lendel + lenins)),
Fcons (make_number (lenins - lendel), Qnil)));
combine_after_change_list
= Fcons (elt, combine_after_change_list);
combine_after_change_buffer = Fcurrent_buffer ();
return;
}
if (!NILP (combine_after_change_list))
Fcombine_after_change_execute ();
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;
before_change_functions = Vbefore_change_functions;
after_change_functions = Vafter_change_functions;
Vbefore_change_functions = Qnil;
Vafter_change_functions = Qnil;
GCPRO2 (before_change_functions, after_change_functions);
args[0] = Qafter_change_functions;
XSETFASTINT (args[1], charpos);
XSETFASTINT (args[2], charpos + lenins);
XSETFASTINT (args[3], lendel);
run_hook_list_with_args (after_change_functions,
4, args);
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))
report_overlay_modification (make_number (charpos),
make_number (charpos + lenins),
1,
make_number (charpos),
make_number (charpos + lenins),
make_number (lendel));
if (lendel == 0)
report_interval_modification (make_number (charpos),
make_number (charpos + lenins));
}
Lisp_Object
Fcombine_after_change_execute_1 (val)
Lisp_Object val;
{
Vcombine_after_change_calls = val;
return 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'.")
()
{
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);
beg = Z - BEG;
end = beg;
change = 0;
for (tail = combine_after_change_list; CONSP (tail);
tail = XCDR (tail))
{
Lisp_Object elt;
int thisbeg, thisend, thischange;
elt = XCAR (tail);
if (! CONSP (elt))
continue;
thisbeg = XINT (XCAR (elt));
elt = XCDR (elt);
if (! CONSP (elt))
continue;
thisend = XINT (XCAR (elt));
elt = XCDR (elt);
if (! CONSP (elt))
continue;
thischange = XINT (XCAR (elt));
change += thischange;
if (thisbeg < beg)
beg = thisbeg;
if (thisend < end)
end = thisend;
}
begpos = BEG + beg;
endpos = Z - end;
combine_after_change_list = Qnil;
record_unwind_protect (Fcombine_after_change_execute_1,
Vcombine_after_change_calls);
signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
update_compositions (begpos, endpos, CHECK_ALL);
return unbind_to (count, Qnil);
}
void
syms_of_insdel ()
{
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.");
check_markers_debug_flag = 0;
DEFVAR_LISP ("combine-after-change-calls", &Vcombine_after_change_calls,
"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.");
inhibit_modification_hooks = 0;
Qinhibit_modification_hooks = intern ("inhibit-modification-hooks");
staticpro (&Qinhibit_modification_hooks);
defsubr (&Scombine_after_change_execute);
}