Simplify redefinition of 'abort' (Bug#12316).
[bpt/emacs.git] / src / insdel.c
CommitLineData
b45433b3 1/* Buffer insertion/deletion and gap motion for GNU Emacs.
acaf905b 2 Copyright (C) 1985-1986, 1993-1995, 1997-2012
8cabe764 3 Free Software Foundation, Inc.
b45433b3
JB
4
5This file is part of GNU Emacs.
6
9ec0b715 7GNU Emacs is free software: you can redistribute it and/or modify
b45433b3 8it under the terms of the GNU General Public License as published by
9ec0b715
GM
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
b45433b3
JB
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
9ec0b715 18along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
b45433b3
JB
19
20
18160b98 21#include <config.h>
d7306fe6 22#include <setjmp.h>
06d6db33
PE
23
24#include <intprops.h>
25
b45433b3 26#include "lisp.h"
679194a6 27#include "intervals.h"
811f7370 28#include "character.h"
e5560ff7 29#include "buffer.h"
b45433b3 30#include "window.h"
d014bf88 31#include "blockinput.h"
dfcf069d 32#include "region-cache.h"
b45433b3 33
a08d4ba7
PE
34static void insert_from_string_1 (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t,
35 ptrdiff_t, bool, bool);
36static void insert_from_buffer_1 (struct buffer *, ptrdiff_t, ptrdiff_t, bool);
37static void gap_left (ptrdiff_t, ptrdiff_t, bool);
38static void gap_right (ptrdiff_t, ptrdiff_t);
395ec62e 39
fb2e7d14
RS
40/* List of elements of the form (BEG-UNCHANGED END-UNCHANGED CHANGE-AMOUNT)
41 describing changes which happened while combine_after_change_calls
a08d4ba7 42 was non-nil. We use this to decide how to call them
fb2e7d14
RS
43 once the deferral ends.
44
45 In each element.
46 BEG-UNCHANGED is the number of chars before the changed range.
47 END-UNCHANGED is the number of chars after the changed range,
48 and CHANGE-AMOUNT is the number of characters inserted by the change
49 (negative for a deletion). */
77382fcc 50static Lisp_Object combine_after_change_list;
fb2e7d14
RS
51
52/* Buffer which combine_after_change_list is about. */
77382fcc 53static Lisp_Object combine_after_change_buffer;
27ea0af2
GM
54
55Lisp_Object Qinhibit_modification_hooks;
4889fc82 56
d311d28c 57static void signal_before_change (ptrdiff_t, ptrdiff_t, ptrdiff_t *);
e2688e4a
DA
58
59/* Also used in marker.c to enable expensive marker checks. */
60
61#ifdef MARKER_DEBUG
60ea6052 62
85876d07 63static void
971de7fb 64check_markers (void)
60ea6052 65{
a08d4ba7
PE
66 struct Lisp_Marker *tail;
67 bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters));
60ea6052 68
dab0b04d 69 for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
60ea6052 70 {
dab0b04d 71 if (tail->buffer->text != current_buffer->text)
1088b922 72 emacs_abort ();
dab0b04d 73 if (tail->charpos > Z)
1088b922 74 emacs_abort ();
dab0b04d 75 if (tail->bytepos > Z_BYTE)
1088b922 76 emacs_abort ();
dab0b04d 77 if (multibyte && ! CHAR_HEAD_P (FETCH_BYTE (tail->bytepos)))
1088b922 78 emacs_abort ();
60ea6052
RS
79 }
80}
e2688e4a
DA
81
82#else /* not MARKER_DEBUG */
83
84#define check_markers() do { } while (0)
85
86#endif /* MARKER_DEBUG */
87
3be11131 88/* Move gap to position CHARPOS.
b45433b3
JB
89 Note that this can quit! */
90
c660b094 91void
d311d28c 92move_gap (ptrdiff_t charpos)
b45433b3 93{
3be11131 94 move_gap_both (charpos, charpos_to_bytepos (charpos));
b45433b3
JB
95}
96
3be11131
RS
97/* Move gap to byte position BYTEPOS, which is also char position CHARPOS.
98 Note that this can quit! */
99
100void
d311d28c 101move_gap_both (ptrdiff_t charpos, ptrdiff_t bytepos)
3be11131
RS
102{
103 if (bytepos < GPT_BYTE)
104 gap_left (charpos, bytepos, 0);
105 else if (bytepos > GPT_BYTE)
106 gap_right (charpos, bytepos);
107}
108
109/* Move the gap to a position less than the current GPT.
110 BYTEPOS describes the new position as a byte position,
111 and CHARPOS is the corresponding char position.
a08d4ba7 112 If NEWGAP, then don't update beg_unchanged and end_unchanged. */
b45433b3 113
2bcaed71 114static void
a08d4ba7 115gap_left (ptrdiff_t charpos, ptrdiff_t bytepos, bool newgap)
b45433b3 116{
a08d4ba7
PE
117 unsigned char *to, *from;
118 ptrdiff_t i;
d311d28c 119 ptrdiff_t new_s1;
b45433b3 120
b45433b3 121 if (!newgap)
a50df55b 122 BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
b45433b3 123
3be11131 124 i = GPT_BYTE;
b45433b3
JB
125 to = GAP_END_ADDR;
126 from = GPT_ADDR;
3be11131 127 new_s1 = GPT_BYTE;
b45433b3
JB
128
129 /* Now copy the characters. To move the gap down,
130 copy characters up. */
131
132 while (1)
133 {
134 /* I gets number of characters left to copy. */
3be11131 135 i = new_s1 - bytepos;
b45433b3
JB
136 if (i == 0)
137 break;
138 /* If a quit is requested, stop copying now.
3be11131 139 Change BYTEPOS to be where we have actually moved the gap to. */
b45433b3
JB
140 if (QUITP)
141 {
3be11131
RS
142 bytepos = new_s1;
143 charpos = BYTE_TO_CHAR (bytepos);
b45433b3
JB
144 break;
145 }
146 /* Move at most 32000 chars before checking again for a quit. */
147 if (i > 32000)
148 i = 32000;
72af86bd
AS
149 new_s1 -= i;
150 from -= i, to -= i;
151 memmove (to, from, i);
b45433b3
JB
152 }
153
f868cd8a
JB
154 /* Adjust buffer data structure, to put the gap at BYTEPOS.
155 BYTEPOS is where the loop above stopped, which may be what
156 was specified or may be where a quit was detected. */
3be11131
RS
157 GPT_BYTE = bytepos;
158 GPT = charpos;
e2688e4a 159 eassert (charpos <= bytepos);
469ff680 160 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
b45433b3
JB
161 QUIT;
162}
163
2b34df4e 164/* Move the gap to a position greater than the current GPT.
3be11131
RS
165 BYTEPOS describes the new position as a byte position,
166 and CHARPOS is the corresponding char position. */
167
2bcaed71 168static void
d311d28c 169gap_right (ptrdiff_t charpos, ptrdiff_t bytepos)
b45433b3
JB
170{
171 register unsigned char *to, *from;
d311d28c
PE
172 register ptrdiff_t i;
173 ptrdiff_t new_s1;
b45433b3 174
a50df55b 175 BUF_COMPUTE_UNCHANGED (current_buffer, charpos, GPT);
b45433b3 176
3be11131 177 i = GPT_BYTE;
b45433b3
JB
178 from = GAP_END_ADDR;
179 to = GPT_ADDR;
3be11131 180 new_s1 = GPT_BYTE;
b45433b3
JB
181
182 /* Now copy the characters. To move the gap up,
183 copy characters down. */
184
185 while (1)
186 {
187 /* I gets number of characters left to copy. */
3be11131 188 i = bytepos - new_s1;
b45433b3
JB
189 if (i == 0)
190 break;
191 /* If a quit is requested, stop copying now.
3be11131 192 Change BYTEPOS to be where we have actually moved the gap to. */
b45433b3
JB
193 if (QUITP)
194 {
3be11131
RS
195 bytepos = new_s1;
196 charpos = BYTE_TO_CHAR (bytepos);
b45433b3
JB
197 break;
198 }
199 /* Move at most 32000 chars before checking again for a quit. */
200 if (i > 32000)
201 i = 32000;
72af86bd
AS
202 new_s1 += i;
203 memmove (to, from, i);
204 from += i, to += i;
b45433b3
JB
205 }
206
3be11131
RS
207 GPT = charpos;
208 GPT_BYTE = bytepos;
e2688e4a 209 eassert (charpos <= bytepos);
469ff680 210 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
b45433b3
JB
211 QUIT;
212}
2b083808 213\f
3be11131
RS
214/* Adjust all markers for a deletion
215 whose range in bytes is FROM_BYTE to TO_BYTE.
216 The range in charpos is FROM to TO.
217
218 This function assumes that the gap is adjacent to
219 or inside of the range being deleted. */
beecb55b 220
809f3d51 221void
d311d28c
PE
222adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
223 ptrdiff_t to, ptrdiff_t to_byte)
3be11131
RS
224{
225 Lisp_Object marker;
226 register struct Lisp_Marker *m;
d311d28c 227 register ptrdiff_t charpos;
3be11131 228
dab0b04d 229 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
3be11131 230 {
3be11131 231 charpos = m->charpos;
e2688e4a 232 eassert (charpos <= Z);
3be11131
RS
233
234 /* If the marker is after the deletion,
80f6e77c 235 relocate by number of chars / bytes deleted. */
3be11131 236 if (charpos > to)
80f6e77c
RS
237 {
238 m->charpos -= to - from;
239 m->bytepos -= to_byte - from_byte;
240 }
80f6e77c 241 /* Here's the case where a marker is inside text being deleted. */
3be11131
RS
242 else if (charpos > from)
243 {
00de2987 244 if (! m->insertion_type)
dab0b04d 245 { /* Normal markers will end up at the beginning of the
00de2987 246 re-inserted text after undoing a deletion, and must be
177c0ea7 247 adjusted to move them to the correct place. */
dab0b04d 248 XSETMISC (marker, m);
28259cac 249 record_marker_adjustment (marker, from - charpos);
dab0b04d 250 }
00de2987 251 else if (charpos < to)
dab0b04d 252 { /* Before-insertion markers will automatically move forward
00de2987
MB
253 upon re-inserting the deleted text, so we have to arrange
254 for them to move backward to the correct position. */
dab0b04d 255 XSETMISC (marker, m);
28259cac 256 record_marker_adjustment (marker, to - charpos);
dab0b04d 257 }
3be11131 258 m->charpos = from;
80f6e77c 259 m->bytepos = from_byte;
3be11131 260 }
00de2987
MB
261 /* Here's the case where a before-insertion marker is immediately
262 before the deleted region. */
263 else if (charpos == from && m->insertion_type)
264 {
265 /* Undoing the change uses normal insertion, which will
266 incorrectly make MARKER move forward, so we arrange for it
267 to then move backward to the correct place at the beginning
268 of the deleted region. */
dab0b04d 269 XSETMISC (marker, m);
00de2987
MB
270 record_marker_adjustment (marker, to - from);
271 }
3be11131
RS
272 }
273}
23b20a70 274
2b083808 275\f
432f78d2
RS
276/* Adjust markers for an insertion that stretches from FROM / FROM_BYTE
277 to TO / TO_BYTE. We have to relocate the charpos of every marker
278 that points after the insertion (but not their bytepos).
3be11131 279
3be11131
RS
280 When a marker points at the insertion point,
281 we advance it if either its insertion-type is t
282 or BEFORE_MARKERS is true. */
283
284static void
d311d28c 285adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte,
a08d4ba7 286 ptrdiff_t to, ptrdiff_t to_byte, bool before_markers)
beecb55b 287{
dab0b04d 288 struct Lisp_Marker *m;
a08d4ba7 289 bool adjusted = 0;
d311d28c
PE
290 ptrdiff_t nchars = to - from;
291 ptrdiff_t nbytes = to_byte - from_byte;
beecb55b 292
dab0b04d 293 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
beecb55b 294 {
86a56ef3
SM
295 eassert (m->bytepos >= m->charpos
296 && m->bytepos - m->charpos <= Z_BYTE - Z);
1f90a790 297
432f78d2 298 if (m->bytepos == from_byte)
469ff680 299 {
432f78d2
RS
300 if (m->insertion_type || before_markers)
301 {
b16afa45
KH
302 m->bytepos = to_byte;
303 m->charpos = to;
432f78d2
RS
304 if (m->insertion_type)
305 adjusted = 1;
306 }
ce97a2d7 307 }
80f6e77c
RS
308 else if (m->bytepos > from_byte)
309 {
310 m->bytepos += nbytes;
b16afa45 311 m->charpos += nchars;
80f6e77c 312 }
beecb55b 313 }
3be11131
RS
314
315 /* Adjusting only markers whose insertion-type is t may result in
409f2919 316 - disordered start and end in overlays, and
6b61353c 317 - disordered overlays in the slot `overlays_before' of current_buffer. */
469ff680 318 if (adjusted)
6b61353c 319 {
5e617bc2 320 fix_start_end_in_overlays (from, to);
6b61353c
KH
321 fix_overlays_before (current_buffer, from, to);
322 }
beecb55b
RS
323}
324
3be11131
RS
325/* Adjust point for an insertion of NBYTES bytes, which are NCHARS characters.
326
327 This is used only when the value of point changes due to an insert
328 or delete; it does not represent a conceptual change in point as a
329 marker. In particular, point is not crossing any interval
330 boundaries, so there's no need to use the usual SET_PT macro. In
331 fact it would be incorrect to do so, because either the old or the
332 new value of point is out of sync with the current set of
333 intervals. */
334
a27a38d8 335static void
d311d28c 336adjust_point (ptrdiff_t nchars, ptrdiff_t nbytes)
a27a38d8 337{
cffc6f3b 338 SET_BUF_PT_BOTH (current_buffer, PT + nchars, PT_BYTE + nbytes);
3be11131 339 /* In a single-byte buffer, the two positions must be equal. */
86a56ef3 340 eassert (PT_BYTE >= PT && PT_BYTE - PT <= ZV_BYTE - ZV);
a27a38d8 341}
b45433b3 342\f
652838b5
KH
343/* Adjust markers for a replacement of a text at FROM (FROM_BYTE) of
344 length OLD_CHARS (OLD_BYTES) to a new text of length NEW_CHARS
adee4f91
KH
345 (NEW_BYTES). It is assumed that OLD_CHARS > 0, i.e., this is not
346 an insertion. */
652838b5
KH
347
348static void
d311d28c
PE
349adjust_markers_for_replace (ptrdiff_t from, ptrdiff_t from_byte,
350 ptrdiff_t old_chars, ptrdiff_t old_bytes,
351 ptrdiff_t new_chars, ptrdiff_t new_bytes)
652838b5 352{
dab0b04d 353 register struct Lisp_Marker *m;
d311d28c
PE
354 ptrdiff_t prev_to_byte = from_byte + old_bytes;
355 ptrdiff_t diff_chars = new_chars - old_chars;
356 ptrdiff_t diff_bytes = new_bytes - old_bytes;
652838b5 357
dab0b04d 358 for (m = BUF_MARKERS (current_buffer); m; m = m->next)
652838b5 359 {
adee4f91 360 if (m->bytepos >= prev_to_byte)
652838b5 361 {
adee4f91
KH
362 m->charpos += diff_chars;
363 m->bytepos += diff_bytes;
652838b5 364 }
adee4f91 365 else if (m->bytepos > from_byte)
652838b5 366 {
652838b5 367 m->charpos = from;
b16afa45 368 m->bytepos = from_byte;
652838b5 369 }
652838b5 370 }
3019692c 371
e2688e4a 372 check_markers ();
652838b5
KH
373}
374
375\f
99561444
PE
376void
377buffer_overflow (void)
378{
379 error ("Maximum buffer size exceeded");
380}
381
3be11131 382/* Make the gap NBYTES_ADDED bytes longer. */
b45433b3 383
85876d07 384static void
d311d28c 385make_gap_larger (ptrdiff_t nbytes_added)
b45433b3 386{
b45433b3 387 Lisp_Object tem;
d311d28c
PE
388 ptrdiff_t real_gap_loc;
389 ptrdiff_t real_gap_loc_byte;
390 ptrdiff_t old_gap_size;
391 ptrdiff_t current_size = Z_BYTE - BEG_BYTE + GAP_SIZE;
1c8e352f 392 enum { enough_for_a_while = 2000 };
b45433b3 393
1c8e352f
PE
394 if (BUF_BYTES_MAX - current_size < nbytes_added)
395 buffer_overflow ();
b45433b3 396
1c8e352f
PE
397 /* If we have to get more space, get enough to last a while;
398 but do not exceed the maximum buffer size. */
399 nbytes_added = min (nbytes_added + enough_for_a_while,
400 BUF_BYTES_MAX - current_size);
94056516 401
71a7bfa7 402 enlarge_buffer_text (current_buffer, nbytes_added);
b45433b3
JB
403
404 /* Prevent quitting in move_gap. */
405 tem = Vinhibit_quit;
406 Vinhibit_quit = Qt;
407
408 real_gap_loc = GPT;
3be11131 409 real_gap_loc_byte = GPT_BYTE;
b45433b3
JB
410 old_gap_size = GAP_SIZE;
411
412 /* Call the newly allocated space a gap at the end of the whole space. */
413 GPT = Z + GAP_SIZE;
7d92db58 414 GPT_BYTE = Z_BYTE + GAP_SIZE;
3be11131 415 GAP_SIZE = nbytes_added;
b45433b3
JB
416
417 /* Move the new gap down to be consecutive with the end of the old one.
418 This adjusts the markers properly too. */
3be11131 419 gap_left (real_gap_loc + old_gap_size, real_gap_loc_byte + old_gap_size, 1);
b45433b3
JB
420
421 /* Now combine the two into one large gap. */
422 GAP_SIZE += old_gap_size;
423 GPT = real_gap_loc;
3be11131 424 GPT_BYTE = real_gap_loc_byte;
b45433b3 425
469ff680
KH
426 /* Put an anchor. */
427 *(Z_ADDR) = 0;
428
b45433b3
JB
429 Vinhibit_quit = tem;
430}
8af5b8e7 431
b58c5c4a 432#if defined USE_MMAP_FOR_BUFFERS || defined REL_ALLOC || defined DOUG_LEA_MALLOC
8af5b8e7 433
7273faa1 434/* Make the gap NBYTES_REMOVED bytes shorter. */
8af5b8e7 435
85876d07 436static void
d311d28c 437make_gap_smaller (ptrdiff_t nbytes_removed)
8af5b8e7
AI
438{
439 Lisp_Object tem;
d311d28c
PE
440 ptrdiff_t real_gap_loc;
441 ptrdiff_t real_gap_loc_byte;
442 ptrdiff_t real_Z;
443 ptrdiff_t real_Z_byte;
444 ptrdiff_t real_beg_unchanged;
445 ptrdiff_t new_gap_size;
8af5b8e7
AI
446
447 /* Make sure the gap is at least 20 bytes. */
448 if (GAP_SIZE - nbytes_removed < 20)
449 nbytes_removed = GAP_SIZE - 20;
450
451 /* Prevent quitting in move_gap. */
452 tem = Vinhibit_quit;
453 Vinhibit_quit = Qt;
454
455 real_gap_loc = GPT;
456 real_gap_loc_byte = GPT_BYTE;
73df3b72 457 new_gap_size = GAP_SIZE - nbytes_removed;
8af5b8e7
AI
458 real_Z = Z;
459 real_Z_byte = Z_BYTE;
73df3b72 460 real_beg_unchanged = BEG_UNCHANGED;
8af5b8e7
AI
461
462 /* Pretend that the last unwanted part of the gap is the entire gap,
463 and that the first desired part of the gap is part of the buffer
464 text. */
72af86bd 465 memset (GPT_ADDR, 0, new_gap_size);
73df3b72
KS
466 GPT += new_gap_size;
467 GPT_BYTE += new_gap_size;
468 Z += new_gap_size;
469 Z_BYTE += new_gap_size;
8af5b8e7
AI
470 GAP_SIZE = nbytes_removed;
471
472 /* Move the unwanted pretend gap to the end of the buffer. This
473 adjusts the markers properly too. */
474 gap_right (Z, Z_BYTE);
475
476 enlarge_buffer_text (current_buffer, -nbytes_removed);
477
478 /* Now restore the desired gap. */
73df3b72 479 GAP_SIZE = new_gap_size;
8af5b8e7
AI
480 GPT = real_gap_loc;
481 GPT_BYTE = real_gap_loc_byte;
482 Z = real_Z;
483 Z_BYTE = real_Z_byte;
73df3b72 484 BEG_UNCHANGED = real_beg_unchanged;
8af5b8e7
AI
485
486 /* Put an anchor. */
487 *(Z_ADDR) = 0;
488
489 Vinhibit_quit = tem;
490}
491
b58c5c4a
PE
492#endif /* USE_MMAP_FOR_BUFFERS || REL_ALLOC || DOUG_LEA_MALLOC */
493
8af5b8e7 494void
d311d28c 495make_gap (ptrdiff_t nbytes_added)
8af5b8e7
AI
496{
497 if (nbytes_added >= 0)
498 make_gap_larger (nbytes_added);
d77fbc16 499#if defined USE_MMAP_FOR_BUFFERS || defined REL_ALLOC || defined DOUG_LEA_MALLOC
8af5b8e7
AI
500 else
501 make_gap_smaller (-nbytes_added);
502#endif
503}
b45433b3 504\f
2b083808
RS
505/* Copy NBYTES bytes of text from FROM_ADDR to TO_ADDR.
506 FROM_MULTIBYTE says whether the incoming text is multibyte.
507 TO_MULTIBYTE says whether to store the text as multibyte.
508 If FROM_MULTIBYTE != TO_MULTIBYTE, we convert.
509
510 Return the number of bytes stored at TO_ADDR. */
511
d311d28c 512ptrdiff_t
ae19ba7c 513copy_text (const unsigned char *from_addr, unsigned char *to_addr,
a08d4ba7 514 ptrdiff_t nbytes, bool from_multibyte, bool to_multibyte)
2b083808
RS
515{
516 if (from_multibyte == to_multibyte)
517 {
72af86bd 518 memcpy (to_addr, from_addr, nbytes);
2b083808
RS
519 return nbytes;
520 }
521 else if (from_multibyte)
522 {
d311d28c
PE
523 ptrdiff_t nchars = 0;
524 ptrdiff_t bytes_left = nbytes;
f44cbcd8 525
2b083808
RS
526 while (bytes_left > 0)
527 {
b16afa45 528 int thislen, c;
62a6e103 529 c = STRING_CHAR_AND_LENGTH (from_addr, thislen);
1ede3eb6
KH
530 if (! ASCII_CHAR_P (c))
531 c &= 0xFF;
f44cbcd8 532 *to_addr++ = c;
2b083808 533 from_addr += thislen;
7e79b8e0 534 bytes_left -= thislen;
2b083808
RS
535 nchars++;
536 }
537 return nchars;
538 }
539 else
540 {
541 unsigned char *initial_to_addr = to_addr;
542
543 /* Convert single-byte to multibyte. */
544 while (nbytes > 0)
545 {
546 int c = *from_addr++;
2b083808 547
4c0354d7 548 if (!ASCII_CHAR_P (c))
2b083808 549 {
4c0354d7 550 c = BYTE8_TO_CHAR (c);
0ef71121 551 to_addr += CHAR_STRING (c, to_addr);
2b083808
RS
552 nbytes--;
553 }
554 else
555 /* Special case for speed. */
556 *to_addr++ = c, nbytes--;
557 }
558 return to_addr - initial_to_addr;
559 }
560}
2b083808 561\f
b45433b3 562/* Insert a string of specified length before point.
2b083808
RS
563 This function judges multibyteness based on
564 enable_multibyte_characters in the current buffer;
565 it never converts between single-byte and multibyte.
566
ef29f213
KH
567 DO NOT use this for the contents of a Lisp string or a Lisp buffer!
568 prepare_to_modify_buffer could relocate the text. */
b45433b3 569
c660b094 570void
d311d28c 571insert (const char *string, ptrdiff_t nbytes)
b45433b3 572{
3be11131 573 if (nbytes > 0)
395ec62e 574 {
d311d28c 575 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
576 insert_1_both (string, len, nbytes, 0, 1, 0);
577 opoint = PT - len;
578 signal_after_change (opoint, 0, len);
0ef71121 579 update_compositions (opoint, PT, CHECK_BORDER);
cd11ef31
RS
580 }
581}
582
2b083808
RS
583/* Likewise, but inherit text properties from neighboring characters. */
584
c660b094 585void
d311d28c 586insert_and_inherit (const char *string, ptrdiff_t nbytes)
cd11ef31 587{
3be11131 588 if (nbytes > 0)
cd11ef31 589 {
d311d28c 590 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
591 insert_1_both (string, len, nbytes, 1, 1, 0);
592 opoint = PT - len;
593 signal_after_change (opoint, 0, len);
0ef71121 594 update_compositions (opoint, PT, CHECK_BORDER);
395ec62e
KH
595 }
596}
b45433b3 597
2b083808 598/* Insert the character C before point. Do not inherit text properties. */
3be11131 599
c660b094 600void
ae19ba7c 601insert_char (int c)
3be11131 602{
0ef71121 603 unsigned char str[MAX_MULTIBYTE_LENGTH];
2b083808
RS
604 int len;
605
4b4deea2 606 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
0ef71121 607 len = CHAR_STRING (c, str);
2b083808
RS
608 else
609 {
610 len = 1;
0ef71121 611 str[0] = c;
2b083808 612 }
3be11131 613
b68864e5 614 insert ((char *) str, len);
3be11131
RS
615}
616
2b083808 617/* Insert the null-terminated string S before point. */
3be11131
RS
618
619void
ae19ba7c 620insert_string (const char *s)
3be11131
RS
621{
622 insert (s, strlen (s));
623}
624
625/* Like `insert' except that all markers pointing at the place where
626 the insertion happens are adjusted to point after it.
627 Don't use this function to insert part of a Lisp string,
628 since gc could happen and relocate it. */
629
630void
d311d28c 631insert_before_markers (const char *string, ptrdiff_t nbytes)
3be11131
RS
632{
633 if (nbytes > 0)
634 {
d311d28c 635 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
636 insert_1_both (string, len, nbytes, 0, 1, 1);
637 opoint = PT - len;
638 signal_after_change (opoint, 0, len);
0ef71121 639 update_compositions (opoint, PT, CHECK_BORDER);
3be11131
RS
640 }
641}
642
2b083808
RS
643/* Likewise, but inherit text properties from neighboring characters. */
644
3be11131 645void
b68864e5 646insert_before_markers_and_inherit (const char *string,
d311d28c 647 ptrdiff_t nbytes)
3be11131
RS
648{
649 if (nbytes > 0)
650 {
d311d28c 651 ptrdiff_t len = chars_in_text ((unsigned char *) string, nbytes), opoint;
bab3eee1
EZ
652 insert_1_both (string, len, nbytes, 1, 1, 1);
653 opoint = PT - len;
654 signal_after_change (opoint, 0, len);
0ef71121 655 update_compositions (opoint, PT, CHECK_BORDER);
3be11131
RS
656 }
657}
1f90a790 658
3be11131
RS
659/* Subroutine used by the insert functions above. */
660
661void
d311d28c 662insert_1 (const char *string, ptrdiff_t nbytes,
a08d4ba7 663 bool inherit, bool prepare, bool before_markers)
395ec62e 664{
b68864e5
PE
665 insert_1_both (string, chars_in_text ((unsigned char *) string, nbytes),
666 nbytes, inherit, prepare, before_markers);
432f78d2 667}
b16afa45 668
1f90a790 669\f
b16afa45
KH
670#ifdef BYTE_COMBINING_DEBUG
671
432f78d2
RS
672/* See if the bytes before POS/POS_BYTE combine with bytes
673 at the start of STRING to form a single character.
ce97a2d7 674 If so, return the number of bytes at the start of STRING
432f78d2 675 which combine in this way. Otherwise, return 0. */
b45433b3 676
432f78d2 677int
d311d28c
PE
678count_combining_before (const unsigned char *string, ptrdiff_t length,
679 ptrdiff_t pos, ptrdiff_t pos_byte)
432f78d2 680{
cae184f2 681 int len, combining_bytes;
641a26d2 682 const unsigned char *p;
b45433b3 683
432f78d2
RS
684 if (NILP (current_buffer->enable_multibyte_characters))
685 return 0;
cae184f2
KH
686
687 /* At first, we can exclude the following cases:
688 (1) STRING[0] can't be a following byte of multibyte sequence.
689 (2) POS is the start of the current buffer.
690 (3) A character before POS is not a multibyte character. */
691 if (length == 0 || CHAR_HEAD_P (*string)) /* case (1) */
432f78d2 692 return 0;
cae184f2 693 if (pos_byte == BEG_BYTE) /* case (2) */
432f78d2 694 return 0;
cae184f2
KH
695 len = 1;
696 p = BYTE_POS_ADDR (pos_byte - 1);
697 while (! CHAR_HEAD_P (*p)) p--, len++;
409f2919 698 if (! LEADING_CODE_P (*p)) /* case (3) */
432f78d2 699 return 0;
cae184f2 700
cae184f2
KH
701 combining_bytes = BYTES_BY_CHAR_HEAD (*p) - len;
702 if (combining_bytes <= 0)
703 /* The character preceding POS is, complete and no room for
704 combining bytes (combining_bytes == 0), or an independent 8-bit
705 character (combining_bytes < 0). */
432f78d2 706 return 0;
ce97a2d7 707
cae184f2
KH
708 /* We have a combination situation. Count the bytes at STRING that
709 may combine. */
710 p = string + 1;
ce97a2d7
RS
711 while (!CHAR_HEAD_P (*p) && p < string + length)
712 p++;
713
cae184f2 714 return (combining_bytes < p - string ? combining_bytes : p - string);
432f78d2 715}
b45433b3 716
432f78d2
RS
717/* See if the bytes after POS/POS_BYTE combine with bytes
718 at the end of STRING to form a single character.
719 If so, return the number of bytes after POS/POS_BYTE
720 which combine in this way. Otherwise, return 0. */
679194a6 721
432f78d2 722int
ae19ba7c 723count_combining_after (const unsigned char *string,
d311d28c 724 ptrdiff_t length, ptrdiff_t pos, ptrdiff_t pos_byte)
432f78d2 725{
d311d28c
PE
726 ptrdiff_t opos_byte = pos_byte;
727 ptrdiff_t i;
728 ptrdiff_t bytes;
cae184f2 729 unsigned char *bufp;
3be11131 730
432f78d2
RS
731 if (NILP (current_buffer->enable_multibyte_characters))
732 return 0;
cae184f2
KH
733
734 /* At first, we can exclude the following cases:
735 (1) The last byte of STRING is an ASCII.
736 (2) POS is the last of the current buffer.
737 (3) A character at POS can't be a following byte of multibyte
738 character. */
739 if (length > 0 && ASCII_BYTE_P (string[length - 1])) /* case (1) */
740 return 0;
741 if (pos_byte == Z_BYTE) /* case (2) */
432f78d2 742 return 0;
cae184f2
KH
743 bufp = BYTE_POS_ADDR (pos_byte);
744 if (CHAR_HEAD_P (*bufp)) /* case (3) */
745 return 0;
746
432f78d2 747 i = length - 1;
b57a7b0b 748 while (i >= 0 && ! CHAR_HEAD_P (string[i]))
432f78d2
RS
749 {
750 i--;
751 }
b57a7b0b
KH
752 if (i < 0)
753 {
cae184f2
KH
754 /* All characters in STRING are not character head. We must
755 check also preceding bytes at POS. We are sure that the gap
756 is at POS. */
757 unsigned char *p = BEG_ADDR;
b57a7b0b 758 i = pos_byte - 2;
cae184f2 759 while (i >= 0 && ! CHAR_HEAD_P (p[i]))
b57a7b0b 760 i--;
409f2919 761 if (i < 0 || !LEADING_CODE_P (p[i]))
b57a7b0b 762 return 0;
0ef71121 763
cae184f2
KH
764 bytes = BYTES_BY_CHAR_HEAD (p[i]);
765 return (bytes <= pos_byte - 1 - i + length
766 ? 0
767 : bytes - (pos_byte - 1 - i + length));
b57a7b0b 768 }
409f2919 769 if (!LEADING_CODE_P (string[i]))
432f78d2
RS
770 return 0;
771
cae184f2
KH
772 bytes = BYTES_BY_CHAR_HEAD (string[i]) - (length - i);
773 bufp++, pos_byte++;
774 while (!CHAR_HEAD_P (*bufp)) bufp++, pos_byte++;
cd11ef31 775
cae184f2 776 return (bytes <= pos_byte - opos_byte ? bytes : pos_byte - opos_byte);
b45433b3 777}
2b083808 778
b16afa45 779#endif
9f3ede3c 780
1f90a790 781\f
2b083808
RS
782/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
783 starting at STRING. INHERIT, PREPARE and BEFORE_MARKERS
784 are the same as in insert_1. */
785
786void
b68864e5 787insert_1_both (const char *string,
d311d28c 788 ptrdiff_t nchars, ptrdiff_t nbytes,
a08d4ba7 789 bool inherit, bool prepare, bool before_markers)
2b083808 790{
da80a8d5
GM
791 if (nchars == 0)
792 return;
177c0ea7 793
4b4deea2 794 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
57404188
KH
795 nchars = nbytes;
796
35d63725
RS
797 if (prepare)
798 /* Do this before moving and increasing the gap,
799 because the before-change hooks might move the gap
800 or make it smaller. */
801 prepare_to_modify_buffer (PT, PT, NULL);
802
2b083808
RS
803 if (PT != GPT)
804 move_gap_both (PT, PT_BYTE);
805 if (GAP_SIZE < nbytes)
806 make_gap (nbytes - GAP_SIZE);
807
b16afa45
KH
808#ifdef BYTE_COMBINING_DEBUG
809 if (count_combining_before (string, nbytes, PT, PT_BYTE)
810 || count_combining_after (string, nbytes, PT, PT_BYTE))
1088b922 811 emacs_abort ();
b16afa45 812#endif
432f78d2
RS
813
814 /* Record deletion of the surrounding text that combines with
815 the insertion. This, together with recording the insertion,
b16afa45
KH
816 will add up to the right stuff in the undo list. */
817 record_insert (PT, nchars);
2b083808 818 MODIFF++;
3e145152 819 CHARS_MODIFF = MODIFF;
2b083808 820
72af86bd 821 memcpy (GPT_ADDR, string, nbytes);
2b083808 822
2b083808 823 GAP_SIZE -= nbytes;
1f90a790
RS
824 GPT += nchars;
825 ZV += nchars;
826 Z += nchars;
2b083808
RS
827 GPT_BYTE += nbytes;
828 ZV_BYTE += nbytes;
829 Z_BYTE += nbytes;
830 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1f90a790 831
e2688e4a 832 eassert (GPT <= GPT_BYTE);
1f90a790 833
9dde4e0c
KS
834 /* The insert may have been in the unchanged region, so check again. */
835 if (Z - GPT < END_UNCHANGED)
836 END_UNCHANGED = Z - GPT;
837
1f90a790 838 adjust_overlays_for_insert (PT, nchars);
432f78d2 839 adjust_markers_for_insert (PT, PT_BYTE,
1f90a790 840 PT + nchars, PT_BYTE + nbytes,
2b083808 841 before_markers);
ce97a2d7 842
0c94c8d6 843 if (buffer_intervals (current_buffer))
1f90a790
RS
844 offset_intervals (current_buffer, PT, nchars);
845
0c94c8d6 846 if (!inherit && buffer_intervals (current_buffer))
f2cab2ea
GM
847 set_text_properties (make_number (PT), make_number (PT + nchars),
848 Qnil, Qnil, Qnil);
ce97a2d7 849
b16afa45 850 adjust_point (nchars, nbytes);
828f1ed3 851
e2688e4a 852 check_markers ();
2b083808 853}
3be11131 854\f
679194a6 855/* Insert the part of the text of STRING, a Lisp object assumed to be
2b083808
RS
856 of type string, consisting of the LENGTH characters (LENGTH_BYTE bytes)
857 starting at position POS / POS_BYTE. If the text of STRING has properties,
858 copy them into the buffer.
679194a6
JA
859
860 It does not work to use `insert' for this, because a GC could happen
72af86bd 861 before we copy the stuff into the buffer, and relocate the string
7e1ea612 862 without insert noticing. */
679194a6 863
c660b094 864void
d311d28c 865insert_from_string (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
a08d4ba7 866 ptrdiff_t length, ptrdiff_t length_byte, bool inherit)
395ec62e 867{
d311d28c 868 ptrdiff_t opoint = PT;
d8bf4256
RS
869
870 if (SCHARS (string) == 0)
871 return;
872
62b82678
RS
873 insert_from_string_1 (string, pos, pos_byte, length, length_byte,
874 inherit, 0);
875 signal_after_change (opoint, 0, PT - opoint);
0ef71121 876 update_compositions (opoint, PT, CHECK_BORDER);
395ec62e
KH
877}
878
2b083808
RS
879/* Like `insert_from_string' except that all markers pointing
880 at the place where the insertion happens are adjusted to point after it. */
3be11131
RS
881
882void
ae19ba7c 883insert_from_string_before_markers (Lisp_Object string,
d311d28c
PE
884 ptrdiff_t pos, ptrdiff_t pos_byte,
885 ptrdiff_t length, ptrdiff_t length_byte,
a08d4ba7 886 bool inherit)
3be11131 887{
d311d28c 888 ptrdiff_t opoint = PT;
d8bf4256
RS
889
890 if (SCHARS (string) == 0)
891 return;
892
62b82678
RS
893 insert_from_string_1 (string, pos, pos_byte, length, length_byte,
894 inherit, 1);
895 signal_after_change (opoint, 0, PT - opoint);
0ef71121 896 update_compositions (opoint, PT, CHECK_BORDER);
3be11131
RS
897}
898
899/* Subroutine of the insertion functions above. */
900
901static void
d311d28c
PE
902insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
903 ptrdiff_t nchars, ptrdiff_t nbytes,
a08d4ba7 904 bool inherit, bool before_markers)
b45433b3 905{
b45433b3 906 struct gcpro gcpro1;
d311d28c 907 ptrdiff_t outgoing_nbytes = nbytes;
ce97a2d7 908 INTERVAL intervals;
2b083808
RS
909
910 /* Make OUTGOING_NBYTES describe the text
911 as it will be inserted in this buffer. */
912
4b4deea2 913 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
2b083808 914 outgoing_nbytes = nchars;
2a1d8be0 915 else if (! STRING_MULTIBYTE (string))
2b083808 916 outgoing_nbytes
f7e233a8 917 = count_size_as_multibyte (SDATA (string) + pos_byte,
2b083808 918 nbytes);
b45433b3 919
b45433b3 920 GCPRO1 (string);
35d63725
RS
921 /* Do this before moving and increasing the gap,
922 because the before-change hooks might move the gap
923 or make it smaller. */
d206af14 924 prepare_to_modify_buffer (PT, PT, NULL);
b45433b3 925
2bcaed71 926 if (PT != GPT)
3be11131 927 move_gap_both (PT, PT_BYTE);
534b9840 928 if (GAP_SIZE < outgoing_nbytes)
2b083808 929 make_gap (outgoing_nbytes - GAP_SIZE);
b45433b3
JB
930 UNGCPRO;
931
2b083808
RS
932 /* Copy the string text into the buffer, perhaps converting
933 between single-byte and multibyte. */
d5db4077 934 copy_text (SDATA (string) + pos_byte, GPT_ADDR, nbytes,
2a1d8be0 935 STRING_MULTIBYTE (string),
4b4deea2 936 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
b45433b3 937
b16afa45 938#ifdef BYTE_COMBINING_DEBUG
432f78d2
RS
939 /* We have copied text into the gap, but we have not altered
940 PT or PT_BYTE yet. So we can pass PT and PT_BYTE
941 to these functions and get the same results as we would
942 have got earlier on. Meanwhile, PT_ADDR does point to
943 the text that has been stored by copy_text. */
b16afa45
KH
944 if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
945 || count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
1088b922 946 emacs_abort ();
b16afa45 947#endif
432f78d2 948
b16afa45 949 record_insert (PT, nchars);
432f78d2 950 MODIFF++;
3e145152 951 CHARS_MODIFF = MODIFF;
432f78d2 952
7792090e 953 GAP_SIZE -= outgoing_nbytes;
1f90a790
RS
954 GPT += nchars;
955 ZV += nchars;
956 Z += nchars;
2b083808
RS
957 GPT_BYTE += outgoing_nbytes;
958 ZV_BYTE += outgoing_nbytes;
959 Z_BYTE += outgoing_nbytes;
469ff680 960 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
3be11131 961
e2688e4a 962 eassert (GPT <= GPT_BYTE);
679194a6 963
9dde4e0c
KS
964 /* The insert may have been in the unchanged region, so check again. */
965 if (Z - GPT < END_UNCHANGED)
966 END_UNCHANGED = Z - GPT;
967
1f90a790
RS
968 adjust_overlays_for_insert (PT, nchars);
969 adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
970 PT_BYTE + outgoing_nbytes,
1f90a790 971 before_markers);
ce97a2d7 972
1f90a790
RS
973 offset_intervals (current_buffer, PT, nchars);
974
0c94c8d6 975 intervals = string_intervals (string);
b16afa45 976 /* Get the intervals for the part of the string we are inserting. */
d5db4077 977 if (nbytes < SBYTES (string))
1f90a790 978 intervals = copy_intervals (intervals, pos, nchars);
177c0ea7 979
ce97a2d7 980 /* Insert those intervals. */
1f90a790 981 graft_intervals_into_buffer (intervals, PT, nchars,
9391e591 982 current_buffer, inherit);
ce97a2d7 983
b16afa45 984 adjust_point (nchars, outgoing_nbytes);
8f924df7 985
e2688e4a 986 check_markers ();
8f924df7
KH
987}
988\f
989/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
990 starting at GPT_ADDR. */
991
992void
d311d28c 993insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes)
8f924df7 994{
4b4deea2 995 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
8f924df7
KH
996 nchars = nbytes;
997
998 record_insert (GPT, nchars);
999 MODIFF++;
1000
1001 GAP_SIZE -= nbytes;
1002 GPT += nchars;
1003 ZV += nchars;
1004 Z += nchars;
1005 GPT_BYTE += nbytes;
1006 ZV_BYTE += nbytes;
1007 Z_BYTE += nbytes;
1008 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1009
e2688e4a 1010 eassert (GPT <= GPT_BYTE);
8f924df7 1011
c669988b
KH
1012 adjust_overlays_for_insert (GPT - nchars, nchars);
1013 adjust_markers_for_insert (GPT - nchars, GPT_BYTE - nbytes,
1014 GPT, GPT_BYTE, 0);
8f924df7 1015
0c94c8d6 1016 if (buffer_intervals (current_buffer))
c669988b
KH
1017 {
1018 offset_intervals (current_buffer, GPT - nchars, nchars);
77c7bcb1 1019 graft_intervals_into_buffer (NULL, GPT - nchars, nchars,
c669988b
KH
1020 current_buffer, 0);
1021 }
8f924df7
KH
1022
1023 if (GPT - nchars < PT)
1024 adjust_point (nchars, nbytes);
1025
e2688e4a 1026 check_markers ();
b45433b3 1027}
3be11131
RS
1028\f
1029/* Insert text from BUF, NCHARS characters starting at CHARPOS, into the
ef29f213
KH
1030 current buffer. If the text in BUF has properties, they are absorbed
1031 into the current buffer.
1032
1033 It does not work to use `insert' for this, because a malloc could happen
72af86bd 1034 and relocate BUF's text before the copy happens. */
ef29f213
KH
1035
1036void
ae19ba7c 1037insert_from_buffer (struct buffer *buf,
a08d4ba7 1038 ptrdiff_t charpos, ptrdiff_t nchars, bool inherit)
ef29f213 1039{
d311d28c 1040 ptrdiff_t opoint = PT;
3be11131 1041
62b82678
RS
1042 insert_from_buffer_1 (buf, charpos, nchars, inherit);
1043 signal_after_change (opoint, 0, PT - opoint);
0ef71121 1044 update_compositions (opoint, PT, CHECK_BORDER);
ef29f213
KH
1045}
1046
1047static void
ae19ba7c 1048insert_from_buffer_1 (struct buffer *buf,
a08d4ba7 1049 ptrdiff_t from, ptrdiff_t nchars, bool inherit)
ef29f213 1050{
d311d28c
PE
1051 ptrdiff_t chunk, chunk_expanded;
1052 ptrdiff_t from_byte = buf_charpos_to_bytepos (buf, from);
1053 ptrdiff_t to_byte = buf_charpos_to_bytepos (buf, from + nchars);
1054 ptrdiff_t incoming_nbytes = to_byte - from_byte;
1055 ptrdiff_t outgoing_nbytes = incoming_nbytes;
ce97a2d7 1056 INTERVAL intervals;
2b083808
RS
1057
1058 /* Make OUTGOING_NBYTES describe the text
1059 as it will be inserted in this buffer. */
1060
4b4deea2 1061 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
2b083808 1062 outgoing_nbytes = nchars;
4b4deea2 1063 else if (NILP (BVAR (buf, enable_multibyte_characters)))
4468c4f1 1064 {
d311d28c
PE
1065 ptrdiff_t outgoing_before_gap = 0;
1066 ptrdiff_t outgoing_after_gap = 0;
ef29f213 1067
4468c4f1
KH
1068 if (from < BUF_GPT (buf))
1069 {
1070 chunk = BUF_GPT_BYTE (buf) - from_byte;
1071 if (chunk > incoming_nbytes)
1072 chunk = incoming_nbytes;
1073 outgoing_before_gap
1074 = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf, from_byte),
1075 chunk);
1076 }
1077 else
1078 chunk = 0;
1079
1080 if (chunk < incoming_nbytes)
1081 outgoing_after_gap
177c0ea7 1082 = count_size_as_multibyte (BUF_BYTE_ADDRESS (buf,
4468c4f1
KH
1083 from_byte + chunk),
1084 incoming_nbytes - chunk);
1085
1086 outgoing_nbytes = outgoing_before_gap + outgoing_after_gap;
1087 }
177c0ea7 1088
35d63725
RS
1089 /* Do this before moving and increasing the gap,
1090 because the before-change hooks might move the gap
1091 or make it smaller. */
d206af14 1092 prepare_to_modify_buffer (PT, PT, NULL);
ef29f213
KH
1093
1094 if (PT != GPT)
3be11131 1095 move_gap_both (PT, PT_BYTE);
2b083808
RS
1096 if (GAP_SIZE < outgoing_nbytes)
1097 make_gap (outgoing_nbytes - GAP_SIZE);
ef29f213 1098
3be11131 1099 if (from < BUF_GPT (buf))
ef29f213 1100 {
3be11131 1101 chunk = BUF_GPT_BYTE (buf) - from_byte;
2b083808
RS
1102 if (chunk > incoming_nbytes)
1103 chunk = incoming_nbytes;
4468c4f1
KH
1104 /* Record number of output bytes, so we know where
1105 to put the output from the second copy_text. */
1106 chunk_expanded
1107 = copy_text (BUF_BYTE_ADDRESS (buf, from_byte),
1108 GPT_ADDR, chunk,
4b4deea2
TT
1109 ! NILP (BVAR (buf, enable_multibyte_characters)),
1110 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
ef29f213
KH
1111 }
1112 else
4468c4f1
KH
1113 chunk_expanded = chunk = 0;
1114
2b083808
RS
1115 if (chunk < incoming_nbytes)
1116 copy_text (BUF_BYTE_ADDRESS (buf, from_byte + chunk),
4468c4f1 1117 GPT_ADDR + chunk_expanded, incoming_nbytes - chunk,
4b4deea2
TT
1118 ! NILP (BVAR (buf, enable_multibyte_characters)),
1119 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
ef29f213 1120
b16afa45 1121#ifdef BYTE_COMBINING_DEBUG
432f78d2
RS
1122 /* We have copied text into the gap, but we have not altered
1123 PT or PT_BYTE yet. So we can pass PT and PT_BYTE
1124 to these functions and get the same results as we would
1f90a790 1125 have got earlier on. Meanwhile, GPT_ADDR does point to
432f78d2 1126 the text that has been stored by copy_text. */
b16afa45
KH
1127 if (count_combining_before (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE)
1128 || count_combining_after (GPT_ADDR, outgoing_nbytes, PT, PT_BYTE))
1088b922 1129 emacs_abort ();
b16afa45 1130#endif
432f78d2 1131
b16afa45 1132 record_insert (PT, nchars);
432f78d2 1133 MODIFF++;
3e145152 1134 CHARS_MODIFF = MODIFF;
432f78d2 1135
2b083808 1136 GAP_SIZE -= outgoing_nbytes;
1f90a790
RS
1137 GPT += nchars;
1138 ZV += nchars;
1139 Z += nchars;
2b083808
RS
1140 GPT_BYTE += outgoing_nbytes;
1141 ZV_BYTE += outgoing_nbytes;
1142 Z_BYTE += outgoing_nbytes;
469ff680 1143 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1f90a790 1144
e2688e4a 1145 eassert (GPT <= GPT_BYTE);
1f90a790 1146
9dde4e0c
KS
1147 /* The insert may have been in the unchanged region, so check again. */
1148 if (Z - GPT < END_UNCHANGED)
1149 END_UNCHANGED = Z - GPT;
1150
1f90a790
RS
1151 adjust_overlays_for_insert (PT, nchars);
1152 adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
432f78d2 1153 PT_BYTE + outgoing_nbytes,
b16afa45 1154 0);
ce97a2d7 1155
0c94c8d6 1156 if (buffer_intervals (current_buffer))
1f90a790 1157 offset_intervals (current_buffer, PT, nchars);
ce97a2d7 1158
b16afa45 1159 /* Get the intervals for the part of the string we are inserting. */
0c94c8d6 1160 intervals = buffer_intervals (buf);
f1a6b216 1161 if (nchars < BUF_Z (buf) - BUF_BEG (buf))
06d8227a
GM
1162 {
1163 if (buf == current_buffer && PT <= from)
1164 from += nchars;
1165 intervals = copy_intervals (intervals, from, nchars);
1166 }
177c0ea7 1167
ce97a2d7 1168 /* Insert those intervals. */
1f90a790 1169 graft_intervals_into_buffer (intervals, PT, nchars, current_buffer, inherit);
ce97a2d7 1170
b16afa45 1171 adjust_point (nchars, outgoing_nbytes);
b45433b3
JB
1172}
1173\f
652838b5
KH
1174/* Record undo information and adjust markers and position keepers for
1175 a replacement of a text PREV_TEXT at FROM to a new text of LEN
1176 chars (LEN_BYTE bytes) which resides in the gap just after
1177 GPT_ADDR.
1178
1179 PREV_TEXT nil means the new text was just inserted. */
2d9eea44 1180
77382fcc 1181static void
d311d28c
PE
1182adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte,
1183 Lisp_Object prev_text, ptrdiff_t len, ptrdiff_t len_byte)
1e9c7b7d 1184{
d311d28c 1185 ptrdiff_t nchars_del = 0, nbytes_del = 0;
61415a25 1186
b16afa45
KH
1187#ifdef BYTE_COMBINING_DEBUG
1188 if (count_combining_before (GPT_ADDR, len_byte, from, from_byte)
1189 || count_combining_after (GPT_ADDR, len_byte, from, from_byte))
1088b922 1190 emacs_abort ();
b16afa45
KH
1191#endif
1192
828f1ed3
KH
1193 if (STRINGP (prev_text))
1194 {
d5db4077
KR
1195 nchars_del = SCHARS (prev_text);
1196 nbytes_del = SBYTES (prev_text);
828f1ed3
KH
1197 }
1198
61415a25
KH
1199 /* Update various buffer positions for the new text. */
1200 GAP_SIZE -= len_byte;
1201 ZV += len; Z+= len;
1202 ZV_BYTE += len_byte; Z_BYTE += len_byte;
1203 GPT += len; GPT_BYTE += len_byte;
1204 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1205
adee4f91
KH
1206 if (nchars_del > 0)
1207 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1208 len, len_byte);
1209 else
1210 adjust_markers_for_insert (from, from_byte,
1211 from + len, from_byte + len_byte, 0);
b16afa45 1212
4b4deea2 1213 if (! EQ (BVAR (current_buffer, undo_list), Qt))
828f1ed3 1214 {
828f1ed3 1215 if (nchars_del > 0)
b16afa45
KH
1216 record_delete (from, prev_text);
1217 record_insert (from, len);
828f1ed3 1218 }
652838b5
KH
1219
1220 if (len > nchars_del)
1221 adjust_overlays_for_insert (from, len - nchars_del);
1222 else if (len < nchars_del)
1223 adjust_overlays_for_delete (from, nchars_del - len);
8707c1e5 1224
0c94c8d6 1225 if (buffer_intervals (current_buffer))
8707c1e5 1226 offset_intervals (current_buffer, from, len - nchars_del);
61415a25 1227
b16afa45
KH
1228 if (from < PT)
1229 adjust_point (len - nchars_del, len_byte - nbytes_del);
61415a25 1230
9f3ede3c 1231 /* As byte combining will decrease Z, we must check this again. */
a50df55b
GM
1232 if (Z - GPT < END_UNCHANGED)
1233 END_UNCHANGED = Z - GPT;
9f3ede3c 1234
e2688e4a 1235 check_markers ();
60ea6052 1236
1e9c7b7d 1237 if (len == 0)
b58e3ca1
RS
1238 evaporate_overlays (from);
1239 MODIFF++;
3e145152 1240 CHARS_MODIFF = MODIFF;
b58e3ca1
RS
1241}
1242
652838b5
KH
1243/* Record undo information, adjust markers and position keepers for an
1244 insertion of a text from FROM (FROM_BYTE) to TO (TO_BYTE). The
1245 text already exists in the current buffer but character length (TO
1246 - FROM) may be incorrect, the correct length is NEWLEN. */
1247
1248void
d311d28c
PE
1249adjust_after_insert (ptrdiff_t from, ptrdiff_t from_byte,
1250 ptrdiff_t to, ptrdiff_t to_byte, ptrdiff_t newlen)
652838b5 1251{
d311d28c 1252 ptrdiff_t len = to - from, len_byte = to_byte - from_byte;
652838b5
KH
1253
1254 if (GPT != to)
1255 move_gap_both (to, to_byte);
1256 GAP_SIZE += len_byte;
1257 GPT -= len; GPT_BYTE -= len_byte;
1258 ZV -= len; ZV_BYTE -= len_byte;
1259 Z -= len; Z_BYTE -= len_byte;
1260 adjust_after_replace (from, from_byte, Qnil, newlen, len_byte);
1261}
085db7de 1262\f
3be11131 1263/* Replace the text from character positions FROM to TO with NEW,
a08d4ba7 1264 If PREPARE, call prepare_to_modify_buffer.
c5ca4d3a
RS
1265 If INHERIT, the newly inserted text should inherit text properties
1266 from the surrounding non-deleted text. */
1267
1268/* Note that this does not yet handle markers quite right.
1269 Also it needs to record a single undo-entry that does a replacement
1270 rather than a separate delete and insert.
60aa777a
RS
1271 That way, undo will also handle markers properly.
1272
1273 But if MARKERS is 0, don't relocate markers. */
c5ca4d3a
RS
1274
1275void
d311d28c 1276replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
a08d4ba7 1277 bool prepare, bool inherit, bool markers)
c5ca4d3a 1278{
d311d28c
PE
1279 ptrdiff_t inschars = SCHARS (new);
1280 ptrdiff_t insbytes = SBYTES (new);
1281 ptrdiff_t from_byte, to_byte;
1282 ptrdiff_t nbytes_del, nchars_del;
c5ca4d3a 1283 struct gcpro gcpro1;
ce97a2d7 1284 INTERVAL intervals;
d311d28c 1285 ptrdiff_t outgoing_insbytes = insbytes;
23b20a70 1286 Lisp_Object deletion;
c5ca4d3a 1287
e2688e4a 1288 check_markers ();
60ea6052 1289
c5ca4d3a 1290 GCPRO1 (new);
6bbd7a29 1291 deletion = Qnil;
c5ca4d3a
RS
1292
1293 if (prepare)
1294 {
d311d28c 1295 ptrdiff_t range_length = to - from;
c5ca4d3a
RS
1296 prepare_to_modify_buffer (from, to, &from);
1297 to = from + range_length;
1298 }
1299
3be11131
RS
1300 UNGCPRO;
1301
b50a28de 1302 /* Make args be valid. */
c5ca4d3a
RS
1303 if (from < BEGV)
1304 from = BEGV;
1305 if (to > ZV)
1306 to = ZV;
1307
3be11131
RS
1308 from_byte = CHAR_TO_BYTE (from);
1309 to_byte = CHAR_TO_BYTE (to);
c5ca4d3a 1310
3be11131
RS
1311 nchars_del = to - from;
1312 nbytes_del = to_byte - from_byte;
1313
1314 if (nbytes_del <= 0 && insbytes == 0)
1315 return;
c5ca4d3a 1316
1f90a790
RS
1317 /* Make OUTGOING_INSBYTES describe the text
1318 as it will be inserted in this buffer. */
1319
4b4deea2 1320 if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
1f90a790 1321 outgoing_insbytes = inschars;
2a1d8be0 1322 else if (! STRING_MULTIBYTE (new))
1f90a790 1323 outgoing_insbytes
d5db4077 1324 = count_size_as_multibyte (SDATA (new), insbytes);
1f90a790 1325
c5ca4d3a
RS
1326 GCPRO1 (new);
1327
1328 /* Make sure the gap is somewhere in or next to what we are deleting. */
1329 if (from > GPT)
3be11131 1330 gap_right (from, from_byte);
c5ca4d3a 1331 if (to < GPT)
3be11131 1332 gap_left (to, to_byte, 0);
c5ca4d3a 1333
039af53a
KH
1334 /* Even if we don't record for undo, we must keep the original text
1335 because we may have to recover it because of inappropriate byte
1336 combining. */
4b4deea2 1337 if (! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1338 deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
c5ca4d3a 1339
3be11131
RS
1340 GAP_SIZE += nbytes_del;
1341 ZV -= nchars_del;
1342 Z -= nchars_del;
1343 ZV_BYTE -= nbytes_del;
1344 Z_BYTE -= nbytes_del;
c5ca4d3a 1345 GPT = from;
3be11131 1346 GPT_BYTE = from_byte;
017e09ac 1347 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
c5ca4d3a 1348
e2688e4a 1349 eassert (GPT <= GPT_BYTE);
3be11131 1350
a50df55b
GM
1351 if (GPT - BEG < BEG_UNCHANGED)
1352 BEG_UNCHANGED = GPT - BEG;
1353 if (Z - GPT < END_UNCHANGED)
1354 END_UNCHANGED = Z - GPT;
c5ca4d3a 1355
599a9e4f
PE
1356 if (GAP_SIZE < outgoing_insbytes)
1357 make_gap (outgoing_insbytes - GAP_SIZE);
c5ca4d3a 1358
1f90a790
RS
1359 /* Copy the string text into the buffer, perhaps converting
1360 between single-byte and multibyte. */
d5db4077 1361 copy_text (SDATA (new), GPT_ADDR, insbytes,
2a1d8be0 1362 STRING_MULTIBYTE (new),
4b4deea2 1363 ! NILP (BVAR (current_buffer, enable_multibyte_characters)));
1f90a790 1364
b16afa45 1365#ifdef BYTE_COMBINING_DEBUG
255c7dae
RS
1366 /* We have copied text into the gap, but we have not marked
1367 it as part of the buffer. So we can use the old FROM and FROM_BYTE
1368 here, for both the previous text and the following text.
1369 Meanwhile, GPT_ADDR does point to
432f78d2 1370 the text that has been stored by copy_text. */
b16afa45
KH
1371 if (count_combining_before (GPT_ADDR, outgoing_insbytes, from, from_byte)
1372 || count_combining_after (GPT_ADDR, outgoing_insbytes, from, from_byte))
1088b922 1373 emacs_abort ();
b16afa45 1374#endif
432f78d2 1375
4b4deea2 1376 if (! EQ (BVAR (current_buffer, undo_list), Qt))
23b20a70 1377 {
bec1aca2
RS
1378 /* Record the insertion first, so that when we undo,
1379 the deletion will be undone first. Thus, undo
1380 will insert before deleting, and thus will keep
1381 the markers before and after this text separate. */
1382 record_insert (from + SCHARS (deletion), inschars);
b16afa45 1383 record_delete (from, deletion);
23b20a70 1384 }
c5ca4d3a 1385
1f90a790
RS
1386 GAP_SIZE -= outgoing_insbytes;
1387 GPT += inschars;
1388 ZV += inschars;
1389 Z += inschars;
1390 GPT_BYTE += outgoing_insbytes;
1391 ZV_BYTE += outgoing_insbytes;
1392 Z_BYTE += outgoing_insbytes;
c5ca4d3a
RS
1393 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1394
e2688e4a 1395 eassert (GPT <= GPT_BYTE);
3be11131 1396
c5ca4d3a
RS
1397 /* Adjust the overlay center as needed. This must be done after
1398 adjusting the markers that bound the overlays. */
3be11131 1399 adjust_overlays_for_delete (from, nchars_del);
1f90a790 1400 adjust_overlays_for_insert (from, inschars);
048a0fbb
RS
1401
1402 /* Adjust markers for the deletion and the insertion. */
60aa777a 1403 if (markers)
048a0fbb
RS
1404 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1405 inschars, outgoing_insbytes);
c5ca4d3a 1406
255c7dae 1407 offset_intervals (current_buffer, from, inschars - nchars_del);
ce97a2d7
RS
1408
1409 /* Get the intervals for the part of the string we are inserting--
1410 not including the combined-before bytes. */
0c94c8d6 1411 intervals = string_intervals (new);
ce97a2d7 1412 /* Insert those intervals. */
1f90a790 1413 graft_intervals_into_buffer (intervals, from, inschars,
ce97a2d7 1414 current_buffer, inherit);
c5ca4d3a 1415
1f90a790
RS
1416 /* Relocate point as if it were a marker. */
1417 if (from < PT)
8bedbe9d 1418 adjust_point ((from + inschars - (PT < to ? PT : to)),
1f90a790 1419 (from_byte + outgoing_insbytes
8bedbe9d 1420 - (PT_BYTE < to_byte ? PT_BYTE : to_byte)));
c5ca4d3a 1421
1f90a790
RS
1422 if (outgoing_insbytes == 0)
1423 evaporate_overlays (from);
93b882e8 1424
e2688e4a 1425 check_markers ();
60ea6052 1426
c5ca4d3a 1427 MODIFF++;
3e145152 1428 CHARS_MODIFF = MODIFF;
c5ca4d3a
RS
1429 UNGCPRO;
1430
255c7dae 1431 signal_after_change (from, nchars_del, GPT - from);
0ef71121 1432 update_compositions (from, GPT, CHECK_BORDER);
c5ca4d3a
RS
1433}
1434\f
085db7de
RS
1435/* Replace the text from character positions FROM to TO with
1436 the text in INS of length INSCHARS.
1437 Keep the text properties that applied to the old characters
1438 (extending them to all the new chars if there are more new chars).
1439
1440 Note that this does not yet handle markers quite right.
1441
a08d4ba7 1442 If MARKERS, relocate markers.
085db7de
RS
1443
1444 Unlike most functions at this level, never call
1445 prepare_to_modify_buffer and never call signal_after_change. */
1446
1447void
d311d28c
PE
1448replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
1449 ptrdiff_t to, ptrdiff_t to_byte,
1450 const char *ins, ptrdiff_t inschars, ptrdiff_t insbytes,
a08d4ba7 1451 bool markers)
085db7de 1452{
d311d28c 1453 ptrdiff_t nbytes_del, nchars_del;
085db7de 1454
e2688e4a 1455 check_markers ();
085db7de
RS
1456
1457 nchars_del = to - from;
1458 nbytes_del = to_byte - from_byte;
1459
1460 if (nbytes_del <= 0 && insbytes == 0)
1461 return;
1462
085db7de
RS
1463 /* Make sure the gap is somewhere in or next to what we are deleting. */
1464 if (from > GPT)
1465 gap_right (from, from_byte);
1466 if (to < GPT)
1467 gap_left (to, to_byte, 0);
1468
1469 GAP_SIZE += nbytes_del;
1470 ZV -= nchars_del;
1471 Z -= nchars_del;
1472 ZV_BYTE -= nbytes_del;
1473 Z_BYTE -= nbytes_del;
1474 GPT = from;
1475 GPT_BYTE = from_byte;
1476 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1477
e2688e4a 1478 eassert (GPT <= GPT_BYTE);
085db7de
RS
1479
1480 if (GPT - BEG < BEG_UNCHANGED)
1481 BEG_UNCHANGED = GPT - BEG;
1482 if (Z - GPT < END_UNCHANGED)
1483 END_UNCHANGED = Z - GPT;
1484
1485 if (GAP_SIZE < insbytes)
1486 make_gap (insbytes - GAP_SIZE);
1487
1488 /* Copy the replacement text into the buffer. */
72af86bd 1489 memcpy (GPT_ADDR, ins, insbytes);
085db7de
RS
1490
1491#ifdef BYTE_COMBINING_DEBUG
1492 /* We have copied text into the gap, but we have not marked
1493 it as part of the buffer. So we can use the old FROM and FROM_BYTE
1494 here, for both the previous text and the following text.
1495 Meanwhile, GPT_ADDR does point to
1496 the text that has been stored by copy_text. */
1497 if (count_combining_before (GPT_ADDR, insbytes, from, from_byte)
1498 || count_combining_after (GPT_ADDR, insbytes, from, from_byte))
1088b922 1499 emacs_abort ();
085db7de
RS
1500#endif
1501
1502 GAP_SIZE -= insbytes;
1503 GPT += inschars;
1504 ZV += inschars;
1505 Z += inschars;
1506 GPT_BYTE += insbytes;
1507 ZV_BYTE += insbytes;
1508 Z_BYTE += insbytes;
1509 if (GAP_SIZE > 0) *(GPT_ADDR) = 0; /* Put an anchor. */
1510
e2688e4a 1511 eassert (GPT <= GPT_BYTE);
085db7de
RS
1512
1513 /* Adjust the overlay center as needed. This must be done after
1514 adjusting the markers that bound the overlays. */
1515 if (nchars_del != inschars)
1516 {
1517 adjust_overlays_for_insert (from, inschars);
1518 adjust_overlays_for_delete (from + inschars, nchars_del);
1519 }
1520
1521 /* Adjust markers for the deletion and the insertion. */
1522 if (markers
7077904e 1523 && ! (nchars_del == 1 && inschars == 1 && nbytes_del == insbytes))
085db7de
RS
1524 adjust_markers_for_replace (from, from_byte, nchars_del, nbytes_del,
1525 inschars, insbytes);
1526
1527 offset_intervals (current_buffer, from, inschars - nchars_del);
1528
1529 /* Relocate point as if it were a marker. */
7077904e
KH
1530 if (from < PT && (nchars_del != inschars || nbytes_del != insbytes))
1531 {
1532 if (PT < to)
1533 /* PT was within the deleted text. Move it to FROM. */
1534 adjust_point (from - PT, from_byte - PT_BYTE);
1535 else
1536 adjust_point (inschars - nchars_del, insbytes - nbytes_del);
1537 }
085db7de
RS
1538
1539 if (insbytes == 0)
1540 evaporate_overlays (from);
1541
e2688e4a 1542 check_markers ();
085db7de
RS
1543
1544 MODIFF++;
3e145152 1545 CHARS_MODIFF = MODIFF;
085db7de
RS
1546}
1547\f
b45433b3 1548/* Delete characters in current buffer
3be11131
RS
1549 from FROM up to (but not including) TO.
1550 If TO comes before FROM, we delete nothing. */
b45433b3 1551
c660b094 1552void
d311d28c 1553del_range (ptrdiff_t from, ptrdiff_t to)
47c64747 1554{
7dae4502 1555 del_range_1 (from, to, 1, 0);
47c64747
RS
1556}
1557
7dae4502
SM
1558/* Like del_range; PREPARE says whether to call prepare_to_modify_buffer.
1559 RET_STRING says to return the deleted text. */
47c64747 1560
7dae4502 1561Lisp_Object
a08d4ba7 1562del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
b45433b3 1563{
d311d28c 1564 ptrdiff_t from_byte, to_byte;
7dae4502
SM
1565 Lisp_Object deletion;
1566 struct gcpro gcpro1;
3be11131
RS
1567
1568 /* Make args be valid */
1569 if (from < BEGV)
1570 from = BEGV;
1571 if (to > ZV)
1572 to = ZV;
1573
1574 if (to <= from)
7dae4502 1575 return Qnil;
3be11131
RS
1576
1577 if (prepare)
1578 {
d311d28c 1579 ptrdiff_t range_length = to - from;
3be11131 1580 prepare_to_modify_buffer (from, to, &from);
0a411995 1581 to = min (ZV, from + range_length);
3be11131
RS
1582 }
1583
1584 from_byte = CHAR_TO_BYTE (from);
1585 to_byte = CHAR_TO_BYTE (to);
1586
7dae4502 1587 deletion = del_range_2 (from, from_byte, to, to_byte, ret_string);
5e617bc2 1588 GCPRO1 (deletion);
9c36993a 1589 signal_after_change (from, to - from, 0);
233cc02d 1590 update_compositions (from, from, CHECK_HEAD);
7dae4502
SM
1591 UNGCPRO;
1592 return deletion;
3be11131
RS
1593}
1594
1595/* Like del_range_1 but args are byte positions, not char positions. */
1596
1597void
a08d4ba7 1598del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte, bool prepare)
3be11131 1599{
d311d28c 1600 ptrdiff_t from, to;
3be11131
RS
1601
1602 /* Make args be valid */
1603 if (from_byte < BEGV_BYTE)
1604 from_byte = BEGV_BYTE;
1605 if (to_byte > ZV_BYTE)
1606 to_byte = ZV_BYTE;
1607
1608 if (to_byte <= from_byte)
1609 return;
1610
1611 from = BYTE_TO_CHAR (from_byte);
1612 to = BYTE_TO_CHAR (to_byte);
b45433b3 1613
d206af14
RS
1614 if (prepare)
1615 {
d311d28c
PE
1616 ptrdiff_t old_from = from, old_to = Z - to;
1617 ptrdiff_t range_length = to - from;
d206af14
RS
1618 prepare_to_modify_buffer (from, to, &from);
1619 to = from + range_length;
3be11131
RS
1620
1621 if (old_from != from)
1622 from_byte = CHAR_TO_BYTE (from);
0a411995
GM
1623 if (to > ZV)
1624 {
1625 to = ZV;
1626 to_byte = ZV_BYTE;
1627 }
1628 else if (old_to == Z - to)
3be11131 1629 to_byte = CHAR_TO_BYTE (to);
d206af14
RS
1630 }
1631
7dae4502 1632 del_range_2 (from, from_byte, to, to_byte, 0);
9c36993a 1633 signal_after_change (from, to - from, 0);
0ef71121 1634 update_compositions (from, from, CHECK_HEAD);
3be11131
RS
1635}
1636
1637/* Like del_range_1, but positions are specified both as charpos
1638 and bytepos. */
1639
1640void
d311d28c 1641del_range_both (ptrdiff_t from, ptrdiff_t from_byte,
a08d4ba7 1642 ptrdiff_t to, ptrdiff_t to_byte, bool prepare)
3be11131 1643{
b45433b3 1644 /* Make args be valid */
3be11131
RS
1645 if (from_byte < BEGV_BYTE)
1646 from_byte = BEGV_BYTE;
1647 if (to_byte > ZV_BYTE)
1648 to_byte = ZV_BYTE;
1649
1650 if (to_byte <= from_byte)
1651 return;
1652
b45433b3
JB
1653 if (from < BEGV)
1654 from = BEGV;
1655 if (to > ZV)
1656 to = ZV;
1657
3be11131
RS
1658 if (prepare)
1659 {
d311d28c
PE
1660 ptrdiff_t old_from = from, old_to = Z - to;
1661 ptrdiff_t range_length = to - from;
3be11131
RS
1662 prepare_to_modify_buffer (from, to, &from);
1663 to = from + range_length;
1664
1665 if (old_from != from)
1666 from_byte = CHAR_TO_BYTE (from);
0a411995
GM
1667 if (to > ZV)
1668 {
1669 to = ZV;
1670 to_byte = ZV_BYTE;
1671 }
1672 else if (old_to == Z - to)
3be11131
RS
1673 to_byte = CHAR_TO_BYTE (to);
1674 }
1675
7dae4502 1676 del_range_2 (from, from_byte, to, to_byte, 0);
9c36993a 1677 signal_after_change (from, to - from, 0);
0ef71121 1678 update_compositions (from, from, CHECK_HEAD);
3be11131
RS
1679}
1680
1681/* Delete a range of text, specified both as character positions
1682 and byte positions. FROM and TO are character positions,
7dae4502 1683 while FROM_BYTE and TO_BYTE are byte positions.
a08d4ba7 1684 If RET_STRING, the deleted area is returned as a string. */
3be11131 1685
7dae4502 1686Lisp_Object
d311d28c 1687del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
a08d4ba7 1688 ptrdiff_t to, ptrdiff_t to_byte, bool ret_string)
3be11131 1689{
a08d4ba7 1690 ptrdiff_t nbytes_del, nchars_del;
628cea90 1691 Lisp_Object deletion;
3be11131 1692
e2688e4a 1693 check_markers ();
60ea6052 1694
3be11131
RS
1695 nchars_del = to - from;
1696 nbytes_del = to_byte - from_byte;
b45433b3
JB
1697
1698 /* Make sure the gap is somewhere in or next to what we are deleting. */
1699 if (from > GPT)
3be11131 1700 gap_right (from, from_byte);
b45433b3 1701 if (to < GPT)
3be11131 1702 gap_left (to, to_byte, 0);
b45433b3 1703
b16afa45
KH
1704#ifdef BYTE_COMBINING_DEBUG
1705 if (count_combining_before (BUF_BYTE_ADDRESS (current_buffer, to_byte),
1706 Z_BYTE - to_byte, from, from_byte))
1088b922 1707 emacs_abort ();
b16afa45 1708#endif
628cea90 1709
4b4deea2 1710 if (ret_string || ! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1711 deletion = make_buffer_string_both (from, from_byte, to, to_byte, 1);
7dae4502
SM
1712 else
1713 deletion = Qnil;
1714
8948d317
RS
1715 /* Relocate all markers pointing into the new, larger gap
1716 to point at the end of the text before the gap.
3be11131
RS
1717 Do this before recording the deletion,
1718 so that undo handles this after reinserting the text. */
1719 adjust_markers_for_delete (from, from_byte, to, to_byte);
b16afa45 1720
4b4deea2 1721 if (! EQ (BVAR (current_buffer, undo_list), Qt))
b16afa45 1722 record_delete (from, deletion);
be09561e 1723 MODIFF++;
3e145152 1724 CHARS_MODIFF = MODIFF;
be09561e 1725
b45433b3 1726 /* Relocate point as if it were a marker. */
2bcaed71 1727 if (from < PT)
3be11131
RS
1728 adjust_point (from - (PT < to ? PT : to),
1729 from_byte - (PT_BYTE < to_byte ? PT_BYTE : to_byte));
b45433b3 1730
3be11131 1731 offset_intervals (current_buffer, from, - nchars_del);
16032db6 1732
adde4858 1733 /* Adjust the overlay center as needed. This must be done after
a7f38d28 1734 adjusting the markers that bound the overlays. */
e3a87305 1735 adjust_overlays_for_delete (from, nchars_del);
adde4858 1736
3be11131
RS
1737 GAP_SIZE += nbytes_del;
1738 ZV_BYTE -= nbytes_del;
1739 Z_BYTE -= nbytes_del;
1740 ZV -= nchars_del;
1741 Z -= nchars_del;
b45433b3 1742 GPT = from;
3be11131 1743 GPT_BYTE = from_byte;
b3b58c01
AS
1744 if (GAP_SIZE > 0 && !current_buffer->text->inhibit_shrinking)
1745 /* Put an anchor, unless called from decode_coding_object which
1746 needs to access the previous gap contents. */
1747 *(GPT_ADDR) = 0;
e3a87305 1748
e2688e4a 1749 eassert (GPT <= GPT_BYTE);
3be11131 1750
a50df55b
GM
1751 if (GPT - BEG < BEG_UNCHANGED)
1752 BEG_UNCHANGED = GPT - BEG;
1753 if (Z - GPT < END_UNCHANGED)
1754 END_UNCHANGED = Z - GPT;
b45433b3 1755
e2688e4a 1756 check_markers ();
60ea6052 1757
d386034e 1758 evaporate_overlays (from);
7dae4502
SM
1759
1760 return deletion;
b45433b3
JB
1761}
1762\f
3be11131
RS
1763/* Call this if you're about to change the region of BUFFER from
1764 character positions START to END. This checks the read-only
1765 properties of the region, calls the necessary modification hooks,
1766 and warns the next redisplay that it should pay attention to that
3e145152
CY
1767 area.
1768
a08d4ba7 1769 If PRESERVE_CHARS_MODIFF, do not update CHARS_MODIFF.
3e145152 1770 Otherwise set CHARS_MODIFF to the new value of MODIFF. */
3be11131 1771
c660b094 1772void
d311d28c 1773modify_region (struct buffer *buffer, ptrdiff_t start, ptrdiff_t end,
a08d4ba7 1774 bool preserve_chars_modiff)
b45433b3 1775{
04a759c8
JB
1776 struct buffer *old_buffer = current_buffer;
1777
a3d794a1 1778 set_buffer_internal (buffer);
04a759c8 1779
d206af14 1780 prepare_to_modify_buffer (start, end, NULL);
b45433b3 1781
a50df55b 1782 BUF_COMPUTE_UNCHANGED (buffer, start - 1, end);
83010cd6 1783
9fbf87cd 1784 if (MODIFF <= SAVE_MODIFF)
83010cd6 1785 record_first_change ();
b45433b3 1786 MODIFF++;
3e145152
CY
1787 if (! preserve_chars_modiff)
1788 CHARS_MODIFF = MODIFF;
04a759c8 1789
39eb03f1 1790 bset_point_before_scroll (buffer, Qnil);
069cdc4f 1791
a3d794a1 1792 set_buffer_internal (old_buffer);
b45433b3 1793}
d206af14 1794\f
3be11131
RS
1795/* Check that it is okay to modify the buffer between START and END,
1796 which are char positions.
1797
679194a6
JA
1798 Run the before-change-function, if any. If intervals are in use,
1799 verify that the text to be modified is not read-only, and call
d206af14
RS
1800 any modification properties the text may have.
1801
1802 If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
1803 by holding its value temporarily in a marker. */
b45433b3 1804
c660b094 1805void
d311d28c
PE
1806prepare_to_modify_buffer (ptrdiff_t start, ptrdiff_t end,
1807 ptrdiff_t *preserve_ptr)
b45433b3 1808{
234fb773
CY
1809 struct buffer *base_buffer;
1810
4b4deea2 1811 if (!NILP (BVAR (current_buffer, read_only)))
b45433b3
JB
1812 Fbarf_if_buffer_read_only ();
1813
2e9f55fd
GM
1814 /* Let redisplay consider other windows than selected_window
1815 if modifying another buffer. */
d3d50620 1816 if (XBUFFER (XWINDOW (selected_window)->buffer) != current_buffer)
2e9f55fd
GM
1817 ++windows_or_buffers_changed;
1818
0c94c8d6 1819 if (buffer_intervals (current_buffer))
d206af14
RS
1820 {
1821 if (preserve_ptr)
1822 {
1823 Lisp_Object preserve_marker;
1824 struct gcpro gcpro1;
1825 preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil);
1826 GCPRO1 (preserve_marker);
1827 verify_interval_modification (current_buffer, start, end);
1828 *preserve_ptr = marker_position (preserve_marker);
dab0b04d 1829 unchain_marker (XMARKER (preserve_marker));
d206af14
RS
1830 UNGCPRO;
1831 }
1832 else
1833 verify_interval_modification (current_buffer, start, end);
1834 }
b45433b3 1835
234fb773
CY
1836 /* For indirect buffers, use the base buffer to check clashes. */
1837 if (current_buffer->base_buffer != 0)
1838 base_buffer = current_buffer->base_buffer;
1839 else
1840 base_buffer = current_buffer;
1841
b45433b3 1842#ifdef CLASH_DETECTION
4b4deea2 1843 if (!NILP (BVAR (base_buffer, file_truename))
ab6c5c0c 1844 /* Make binding buffer-file-name to nil effective. */
4b4deea2 1845 && !NILP (BVAR (base_buffer, filename))
9fbf87cd 1846 && SAVE_MODIFF >= MODIFF)
4b4deea2 1847 lock_file (BVAR (base_buffer, file_truename));
b45433b3
JB
1848#else
1849 /* At least warn if this file has changed on disk since it was visited. */
4b4deea2 1850 if (!NILP (BVAR (base_buffer, filename))
9fbf87cd 1851 && SAVE_MODIFF >= MODIFF
d427b66a 1852 && NILP (Fverify_visited_file_modtime (Fcurrent_buffer ()))
4b4deea2 1853 && !NILP (Ffile_exists_p (BVAR (base_buffer, filename))))
b45433b3 1854 call1 (intern ("ask-user-about-supersession-threat"),
4b4deea2 1855 BVAR (base_buffer,filename));
b45433b3
JB
1856#endif /* not CLASH_DETECTION */
1857
9852377f 1858 /* If `select-active-regions' is non-nil, save the region text. */
4b4deea2 1859 if (!NILP (BVAR (current_buffer, mark_active))
8b78d5e3 1860 && !inhibit_modification_hooks
4b4deea2 1861 && XMARKER (BVAR (current_buffer, mark))->buffer
7c23dd44
CY
1862 && NILP (Vsaved_region_selection)
1863 && (EQ (Vselect_active_regions, Qonly)
1864 ? EQ (CAR_SAFE (Vtransient_mark_mode), Qonly)
1865 : (!NILP (Vselect_active_regions)
1866 && !NILP (Vtransient_mark_mode))))
9852377f 1867 {
d311d28c
PE
1868 ptrdiff_t b = XMARKER (BVAR (current_buffer, mark))->charpos;
1869 ptrdiff_t e = PT;
746812d9
CY
1870 if (b < e)
1871 Vsaved_region_selection = make_buffer_string (b, e, 0);
1872 else if (b > e)
1873 Vsaved_region_selection = make_buffer_string (e, b, 0);
9852377f
CY
1874 }
1875
d206af14 1876 signal_before_change (start, end, preserve_ptr);
2f545eea 1877
56e1065e
JB
1878 if (current_buffer->newline_cache)
1879 invalidate_region_cache (current_buffer,
1880 current_buffer->newline_cache,
1881 start - BEG, Z - end);
1882 if (current_buffer->width_run_cache)
1883 invalidate_region_cache (current_buffer,
1884 current_buffer->width_run_cache,
1885 start - BEG, Z - end);
1886
2f545eea 1887 Vdeactivate_mark = Qt;
b45433b3
JB
1888}
1889\f
d206af14
RS
1890/* These macros work with an argument named `preserve_ptr'
1891 and a local variable named `preserve_marker'. */
1892
1893#define PRESERVE_VALUE \
1894 if (preserve_ptr && NILP (preserve_marker)) \
1895 preserve_marker = Fcopy_marker (make_number (*preserve_ptr), Qnil)
1896
1897#define RESTORE_VALUE \
1898 if (! NILP (preserve_marker)) \
1899 { \
1900 *preserve_ptr = marker_position (preserve_marker); \
dab0b04d 1901 unchain_marker (XMARKER (preserve_marker)); \
d206af14
RS
1902 }
1903
b86e0aaf
RS
1904#define PRESERVE_START_END \
1905 if (NILP (start_marker)) \
1906 start_marker = Fcopy_marker (start, Qnil); \
1907 if (NILP (end_marker)) \
1908 end_marker = Fcopy_marker (end, Qnil);
1909
1910#define FETCH_START \
1911 (! NILP (start_marker) ? Fmarker_position (start_marker) : start)
1912
1913#define FETCH_END \
1914 (! NILP (end_marker) ? Fmarker_position (end_marker) : end)
1915
4a181359 1916/* Set a variable to nil if an error occurred.
e6d3f090
SM
1917 Don't change the variable if there was no error.
1918 VAL is a cons-cell (VARIABLE . NO-ERROR-FLAG).
1919 VARIABLE is the variable to maybe set to nil.
1920 NO-ERROR-FLAG is nil if there was an error,
1921 anything else meaning no error (so this function does nothing). */
85876d07 1922static Lisp_Object
971de7fb 1923reset_var_on_error (Lisp_Object val)
4a181359
SM
1924{
1925 if (NILP (XCDR (val)))
1926 Fset (XCAR (val), Qnil);
1927 return Qnil;
1928}
1929
eb8c3be9 1930/* Signal a change to the buffer immediately before it happens.
d206af14
RS
1931 START_INT and END_INT are the bounds of the text to be changed.
1932
1933 If PRESERVE_PTR is nonzero, we relocate *PRESERVE_PTR
1934 by holding its value temporarily in a marker. */
b45433b3 1935
4889fc82 1936static void
d311d28c
PE
1937signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
1938 ptrdiff_t *preserve_ptr)
b45433b3 1939{
fb4ee5cd 1940 Lisp_Object start, end;
b86e0aaf 1941 Lisp_Object start_marker, end_marker;
d206af14 1942 Lisp_Object preserve_marker;
b86e0aaf 1943 struct gcpro gcpro1, gcpro2, gcpro3;
d311d28c 1944 ptrdiff_t count = SPECPDL_INDEX ();
fb4ee5cd 1945
fd16a4c6
KH
1946 if (inhibit_modification_hooks)
1947 return;
1948
fb4ee5cd
RS
1949 start = make_number (start_int);
1950 end = make_number (end_int);
d206af14 1951 preserve_marker = Qnil;
b86e0aaf
RS
1952 start_marker = Qnil;
1953 end_marker = Qnil;
1954 GCPRO3 (preserve_marker, start_marker, end_marker);
fb4ee5cd 1955
4a181359
SM
1956 specbind (Qinhibit_modification_hooks, Qt);
1957
dee091a3
JD
1958 /* If buffer is unmodified, run a special hook for that case. The
1959 check for Vfirst_change_hook is just a minor optimization. */
9fbf87cd 1960 if (SAVE_MODIFF >= MODIFF
dee091a3 1961 && !NILP (Vfirst_change_hook))
d206af14
RS
1962 {
1963 PRESERVE_VALUE;
b86e0aaf 1964 PRESERVE_START_END;
dee091a3 1965 Frun_hooks (1, &Qfirst_change_hook);
d206af14 1966 }
dbc4e1c1 1967
3d1e2d9c 1968 /* Now run the before-change-functions if any. */
e45fb8bf
RS
1969 if (!NILP (Vbefore_change_functions))
1970 {
3d1e2d9c 1971 Lisp_Object args[3];
4a181359 1972 Lisp_Object rvoe_arg = Fcons (Qbefore_change_functions, Qnil);
3d1e2d9c 1973
d206af14 1974 PRESERVE_VALUE;
b86e0aaf 1975 PRESERVE_START_END;
d206af14 1976
4a181359
SM
1977 /* Mark before-change-functions to be reset to nil in case of error. */
1978 record_unwind_protect (reset_var_on_error, rvoe_arg);
3d1e2d9c
RS
1979
1980 /* Actually run the hook functions. */
1981 args[0] = Qbefore_change_functions;
b86e0aaf
RS
1982 args[1] = FETCH_START;
1983 args[2] = FETCH_END;
4a181359 1984 Frun_hook_with_args (3, args);
3d1e2d9c 1985
4a181359
SM
1986 /* There was no error: unarm the reset_on_error. */
1987 XSETCDR (rvoe_arg, Qt);
e45fb8bf 1988 }
d07c0804 1989
4cb3e6b3 1990 if (buffer_has_overlays ())
d206af14
RS
1991 {
1992 PRESERVE_VALUE;
b86e0aaf
RS
1993 report_overlay_modification (FETCH_START, FETCH_END, 0,
1994 FETCH_START, FETCH_END, Qnil);
d206af14
RS
1995 }
1996
b86e0aaf
RS
1997 if (! NILP (start_marker))
1998 free_marker (start_marker);
1999 if (! NILP (end_marker))
2000 free_marker (end_marker);
d206af14
RS
2001 RESTORE_VALUE;
2002 UNGCPRO;
4a181359
SM
2003
2004 unbind_to (count, Qnil);
b45433b3
JB
2005}
2006
eb8c3be9 2007/* Signal a change immediately after it happens.
3be11131 2008 CHARPOS is the character position of the start of the changed text.
b45433b3
JB
2009 LENDEL is the number of characters of the text before the change.
2010 (Not the whole buffer; just the part that was changed.)
8b09e5d0
RS
2011 LENINS is the number of characters in that part of the text
2012 after the change. */
b45433b3 2013
c660b094 2014void
d311d28c 2015signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
b45433b3 2016{
d311d28c 2017 ptrdiff_t count = SPECPDL_INDEX ();
fd16a4c6
KH
2018 if (inhibit_modification_hooks)
2019 return;
2020
fb2e7d14
RS
2021 /* If we are deferring calls to the after-change functions
2022 and there are no before-change functions,
2023 just record the args that we were going to use. */
2024 if (! NILP (Vcombine_after_change_calls)
1675d086 2025 && NILP (Vbefore_change_functions)
4cb3e6b3 2026 && !buffer_has_overlays ())
fb2e7d14
RS
2027 {
2028 Lisp_Object elt;
2029
2030 if (!NILP (combine_after_change_list)
2031 && current_buffer != XBUFFER (combine_after_change_buffer))
2032 Fcombine_after_change_execute ();
2033
3be11131
RS
2034 elt = Fcons (make_number (charpos - BEG),
2035 Fcons (make_number (Z - (charpos - lendel + lenins)),
fb2e7d14
RS
2036 Fcons (make_number (lenins - lendel), Qnil)));
2037 combine_after_change_list
2038 = Fcons (elt, combine_after_change_list);
2039 combine_after_change_buffer = Fcurrent_buffer ();
2040
2041 return;
2042 }
2043
177c0ea7 2044 if (!NILP (combine_after_change_list))
fb2e7d14
RS
2045 Fcombine_after_change_execute ();
2046
4a181359
SM
2047 specbind (Qinhibit_modification_hooks, Qt);
2048
e45fb8bf
RS
2049 if (!NILP (Vafter_change_functions))
2050 {
3d1e2d9c 2051 Lisp_Object args[4];
4a181359
SM
2052 Lisp_Object rvoe_arg = Fcons (Qafter_change_functions, Qnil);
2053
2054 /* Mark after-change-functions to be reset to nil in case of error. */
2055 record_unwind_protect (reset_var_on_error, rvoe_arg);
3d1e2d9c
RS
2056
2057 /* Actually run the hook functions. */
2058 args[0] = Qafter_change_functions;
3be11131
RS
2059 XSETFASTINT (args[1], charpos);
2060 XSETFASTINT (args[2], charpos + lenins);
3d1e2d9c 2061 XSETFASTINT (args[3], lendel);
4a181359 2062 Frun_hook_with_args (4, args);
3d1e2d9c 2063
4a181359
SM
2064 /* There was no error: unarm the reset_on_error. */
2065 XSETCDR (rvoe_arg, Qt);
e45fb8bf 2066 }
d07c0804 2067
4cb3e6b3 2068 if (buffer_has_overlays ())
3be11131
RS
2069 report_overlay_modification (make_number (charpos),
2070 make_number (charpos + lenins),
d07c0804 2071 1,
3be11131
RS
2072 make_number (charpos),
2073 make_number (charpos + lenins),
d07c0804 2074 make_number (lendel));
c5ca0786
RS
2075
2076 /* After an insertion, call the text properties
2077 insert-behind-hooks or insert-in-front-hooks. */
2078 if (lendel == 0)
d6b81c0f
AS
2079 report_interval_modification (make_number (charpos),
2080 make_number (charpos + lenins));
4a181359
SM
2081
2082 unbind_to (count, Qnil);
b45433b3 2083}
fb2e7d14 2084
85876d07 2085static Lisp_Object
971de7fb 2086Fcombine_after_change_execute_1 (Lisp_Object val)
fb2e7d14
RS
2087{
2088 Vcombine_after_change_calls = val;
2089 return val;
2090}
2091
2092DEFUN ("combine-after-change-execute", Fcombine_after_change_execute,
335c5470
PJ
2093 Scombine_after_change_execute, 0, 0, 0,
2094 doc: /* This function is for use internally in `combine-after-change-calls'. */)
5842a27b 2095 (void)
fb2e7d14 2096{
d311d28c
PE
2097 ptrdiff_t count = SPECPDL_INDEX ();
2098 ptrdiff_t beg, end, change;
2099 ptrdiff_t begpos, endpos;
fb2e7d14
RS
2100 Lisp_Object tail;
2101
101e446f
KH
2102 if (NILP (combine_after_change_list))
2103 return Qnil;
2104
c47a9ed1
CY
2105 /* It is rare for combine_after_change_buffer to be invalid, but
2106 possible. It can happen when combine-after-change-calls is
2107 non-nil, and insertion calls a file handler (e.g. through
2108 lock_file) which scribbles into a temp file -- cyd */
2109 if (!BUFFERP (combine_after_change_buffer)
4b4deea2 2110 || NILP (BVAR (XBUFFER (combine_after_change_buffer), name)))
c47a9ed1
CY
2111 {
2112 combine_after_change_list = Qnil;
2113 return Qnil;
2114 }
2115
66322887 2116 record_unwind_current_buffer ();
fb2e7d14
RS
2117
2118 Fset_buffer (combine_after_change_buffer);
2119
2120 /* # chars unchanged at beginning of buffer. */
2121 beg = Z - BEG;
2122 /* # chars unchanged at end of buffer. */
2123 end = beg;
2124 /* Total amount of insertion (negative for deletion). */
2125 change = 0;
2126
2127 /* Scan the various individual changes,
2128 accumulating the range info in BEG, END and CHANGE. */
2129 for (tail = combine_after_change_list; CONSP (tail);
03699b14 2130 tail = XCDR (tail))
fb2e7d14 2131 {
e688a080 2132 Lisp_Object elt;
d311d28c 2133 ptrdiff_t thisbeg, thisend, thischange;
fb2e7d14
RS
2134
2135 /* Extract the info from the next element. */
03699b14 2136 elt = XCAR (tail);
fb2e7d14
RS
2137 if (! CONSP (elt))
2138 continue;
03699b14 2139 thisbeg = XINT (XCAR (elt));
fb2e7d14 2140
03699b14 2141 elt = XCDR (elt);
fb2e7d14
RS
2142 if (! CONSP (elt))
2143 continue;
03699b14 2144 thisend = XINT (XCAR (elt));
fb2e7d14 2145
03699b14 2146 elt = XCDR (elt);
fb2e7d14
RS
2147 if (! CONSP (elt))
2148 continue;
03699b14 2149 thischange = XINT (XCAR (elt));
fb2e7d14
RS
2150
2151 /* Merge this range into the accumulated range. */
2152 change += thischange;
2153 if (thisbeg < beg)
2154 beg = thisbeg;
2155 if (thisend < end)
2156 end = thisend;
2157 }
2158
2159 /* Get the current start and end positions of the range
2160 that was changed. */
2161 begpos = BEG + beg;
2162 endpos = Z - end;
177c0ea7 2163
fb2e7d14
RS
2164 /* We are about to handle these, so discard them. */
2165 combine_after_change_list = Qnil;
2166
2167 /* Now run the after-change functions for real.
2168 Turn off the flag that defers them. */
2169 record_unwind_protect (Fcombine_after_change_execute_1,
2170 Vcombine_after_change_calls);
2171 signal_after_change (begpos, endpos - begpos - change, endpos - begpos);
0ef71121 2172 update_compositions (begpos, endpos, CHECK_ALL);
fb2e7d14 2173
101e446f 2174 return unbind_to (count, Qnil);
fb2e7d14
RS
2175}
2176\f
dfcf069d 2177void
971de7fb 2178syms_of_insdel (void)
fb2e7d14
RS
2179{
2180 staticpro (&combine_after_change_list);
bf0bf758 2181 staticpro (&combine_after_change_buffer);
fb2e7d14 2182 combine_after_change_list = Qnil;
101e446f 2183 combine_after_change_buffer = Qnil;
fb2e7d14 2184
29208e82 2185 DEFVAR_LISP ("combine-after-change-calls", Vcombine_after_change_calls,
335c5470 2186 doc: /* Used internally by the `combine-after-change-calls' macro. */);
fb2e7d14
RS
2187 Vcombine_after_change_calls = Qnil;
2188
29208e82 2189 DEFVAR_BOOL ("inhibit-modification-hooks", inhibit_modification_hooks,
335c5470
PJ
2190 doc: /* Non-nil means don't run any of the hooks that respond to buffer changes.
2191This affects `before-change-functions' and `after-change-functions',
2192as well as hooks attached to text properties and overlays. */);
7baf49cf 2193 inhibit_modification_hooks = 0;
cd3520a4 2194 DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks");
7baf49cf 2195
fb2e7d14
RS
2196 defsubr (&Scombine_after_change_execute);
2197}