rmail-output-read-file-name fix for bug#12214
[bpt/emacs.git] / src / intervals.c
CommitLineData
a50699fd 1/* Code for doing intervals.
acaf905b 2 Copyright (C) 1993-1995, 1997-1998, 2001-2012 Free Software Foundation, Inc.
a50699fd
JA
3
4This file is part of GNU Emacs.
5
9ec0b715 6GNU Emacs is free software: you can redistribute it and/or modify
a50699fd 7it under the terms of the GNU General Public License as published by
9ec0b715
GM
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
a50699fd
JA
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
9ec0b715 17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
a50699fd
JA
18
19
20/* NOTES:
21
22 Have to ensure that we can't put symbol nil on a plist, or some
23 functions may work incorrectly.
24
25 An idea: Have the owner of the tree keep count of splits and/or
26 insertion lengths (in intervals), and balance after every N.
27
28 Need to call *_left_hook when buffer is killed.
29
30 Scan for zero-length, or 0-length to see notes about handling
31 zero length interval-markers.
32
33 There are comments around about freeing intervals. It might be
34 faster to explicitly free them (put them on the free list) than
35 to GC them.
36
37*/
38
39
18160b98 40#include <config.h>
7961135c
DA
41
42#define INTERVALS_INLINE EXTERN_INLINE
43
d7306fe6 44#include <setjmp.h>
37aa2f85 45#include <intprops.h>
a50699fd
JA
46#include "lisp.h"
47#include "intervals.h"
e5560ff7 48#include "character.h"
a50699fd 49#include "buffer.h"
328c0f1f 50#include "puresize.h"
f54a8c1a 51#include "keyboard.h"
8feddab4 52#include "keymap.h"
a50699fd 53
45d82bdc
KH
54/* Test for membership, allowing for t (actually any non-cons) to mean the
55 universal set. */
56
57#define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
58
93ea6e8f
PE
59static Lisp_Object merge_properties_sticky (Lisp_Object, Lisp_Object);
60static INTERVAL merge_interval_right (INTERVAL);
f57e2426 61static INTERVAL reproduce_tree (INTERVAL, INTERVAL);
a50699fd 62\f
7ce503fd 63/* Utility functions for intervals. */
a50699fd
JA
64
65
7ce503fd 66/* Create the root interval of some object, a buffer or string. */
a50699fd
JA
67
68INTERVAL
971de7fb 69create_root_interval (Lisp_Object parent)
a50699fd 70{
328c0f1f
RS
71 INTERVAL new;
72
73 CHECK_IMPURE (parent);
74
75 new = make_interval ();
a50699fd 76
b629dd47 77 if (BUFFERP (parent))
a50699fd 78 {
2bc7a79b
JB
79 new->total_length = (BUF_Z (XBUFFER (parent))
80 - BUF_BEG (XBUFFER (parent)));
9c08a8d4 81 eassert (0 <= TOTAL_LENGTH (new));
8707c1e5 82 buffer_set_intervals (XBUFFER (parent), new);
4867a283 83 new->position = BEG;
a50699fd 84 }
b629dd47 85 else if (STRINGP (parent))
a50699fd 86 {
d5db4077 87 new->total_length = SCHARS (parent);
9c08a8d4 88 eassert (0 <= TOTAL_LENGTH (new));
ad8c997f 89 string_set_intervals (parent, new);
944d4e4b 90 new->position = 0;
a50699fd
JA
91 }
92
6a3d20cc 93 interval_set_object (new, parent);
a50699fd
JA
94
95 return new;
96}
97
98/* Make the interval TARGET have exactly the properties of SOURCE */
99
100void
971de7fb 101copy_properties (register INTERVAL source, register INTERVAL target)
a50699fd
JA
102{
103 if (DEFAULT_INTERVAL_P (source) && DEFAULT_INTERVAL_P (target))
104 return;
105
106 COPY_INTERVAL_CACHE (source, target);
6a3d20cc 107 interval_set_plist (target, Fcopy_sequence (source->plist));
a50699fd
JA
108}
109
110/* Merge the properties of interval SOURCE into the properties
323a7ad4
RS
111 of interval TARGET. That is to say, each property in SOURCE
112 is added to TARGET if TARGET has no such property as yet. */
a50699fd
JA
113
114static void
971de7fb 115merge_properties (register INTERVAL source, register INTERVAL target)
a50699fd
JA
116{
117 register Lisp_Object o, sym, val;
118
119 if (DEFAULT_INTERVAL_P (source) && DEFAULT_INTERVAL_P (target))
120 return;
121
122 MERGE_INTERVAL_CACHE (source, target);
123
124 o = source->plist;
4867a283 125 while (CONSP (o))
a50699fd 126 {
4867a283 127 sym = XCAR (o);
4028a938
KS
128 o = XCDR (o);
129 CHECK_CONS (o);
130
131 val = target->plist;
132 while (CONSP (val) && !EQ (XCAR (val), sym))
133 {
134 val = XCDR (val);
135 if (!CONSP (val))
136 break;
137 val = XCDR (val);
138 }
a50699fd
JA
139
140 if (NILP (val))
141 {
4867a283 142 val = XCAR (o);
6a3d20cc 143 interval_set_plist (target, Fcons (sym, Fcons (val, target->plist)));
a50699fd 144 }
4028a938 145 o = XCDR (o);
a50699fd
JA
146 }
147}
148
149/* Return 1 if the two intervals have the same properties,
7ce503fd 150 0 otherwise. */
a50699fd
JA
151
152int
971de7fb 153intervals_equal (INTERVAL i0, INTERVAL i1)
a50699fd 154{
4028a938
KS
155 register Lisp_Object i0_cdr, i0_sym;
156 register Lisp_Object i1_cdr, i1_val;
a50699fd
JA
157
158 if (DEFAULT_INTERVAL_P (i0) && DEFAULT_INTERVAL_P (i1))
159 return 1;
160
323a7ad4
RS
161 if (DEFAULT_INTERVAL_P (i0) || DEFAULT_INTERVAL_P (i1))
162 return 0;
163
a50699fd 164 i0_cdr = i0->plist;
4028a938
KS
165 i1_cdr = i1->plist;
166 while (CONSP (i0_cdr) && CONSP (i1_cdr))
a50699fd 167 {
4867a283 168 i0_sym = XCAR (i0_cdr);
4028a938
KS
169 i0_cdr = XCDR (i0_cdr);
170 if (!CONSP (i0_cdr))
171 return 0; /* abort (); */
172 i1_val = i1->plist;
173 while (CONSP (i1_val) && !EQ (XCAR (i1_val), i0_sym))
174 {
175 i1_val = XCDR (i1_val);
176 if (!CONSP (i1_val))
177 return 0; /* abort (); */
178 i1_val = XCDR (i1_val);
179 }
a50699fd 180
7ce503fd 181 /* i0 has something i1 doesn't. */
a50699fd
JA
182 if (EQ (i1_val, Qnil))
183 return 0;
184
7ce503fd 185 /* i0 and i1 both have sym, but it has different values in each. */
4028a938
KS
186 if (!CONSP (i1_val)
187 || (i1_val = XCDR (i1_val), !CONSP (i1_val))
188 || !EQ (XCAR (i1_val), XCAR (i0_cdr)))
a50699fd
JA
189 return 0;
190
4867a283 191 i0_cdr = XCDR (i0_cdr);
a50699fd 192
4028a938
KS
193 i1_cdr = XCDR (i1_cdr);
194 if (!CONSP (i1_cdr))
195 return 0; /* abort (); */
196 i1_cdr = XCDR (i1_cdr);
197 }
a50699fd 198
4028a938
KS
199 /* Lengths of the two plists were equal. */
200 return (NILP (i0_cdr) && NILP (i1_cdr));
a50699fd
JA
201}
202\f
a50699fd 203
19d4e9a7
SM
204/* Traverse an interval tree TREE, performing FUNCTION on each node.
205 No guarantee is made about the order of traversal.
206 Pass FUNCTION two args: an interval, and ARG. */
207
208void
971de7fb 209traverse_intervals_noorder (INTERVAL tree, void (*function) (INTERVAL, Lisp_Object), Lisp_Object arg)
19d4e9a7
SM
210{
211 /* Minimize stack usage. */
77c7bcb1 212 while (tree)
19d4e9a7
SM
213 {
214 (*function) (tree, arg);
77c7bcb1 215 if (!tree->right)
19d4e9a7
SM
216 tree = tree->left;
217 else
218 {
219 traverse_intervals_noorder (tree->left, function, arg);
220 tree = tree->right;
221 }
222 }
223}
224
a50699fd 225/* Traverse an interval tree TREE, performing FUNCTION on each node.
4a93c905 226 Pass FUNCTION two args: an interval, and ARG. */
a50699fd
JA
227
228void
d311d28c 229traverse_intervals (INTERVAL tree, ptrdiff_t position,
e79123aa 230 void (*function) (INTERVAL, Lisp_Object), Lisp_Object arg)
a50699fd 231{
77c7bcb1 232 while (tree)
19d4e9a7 233 {
42005513 234 traverse_intervals (tree->left, position, function, arg);
19d4e9a7
SM
235 position += LEFT_TOTAL_LENGTH (tree);
236 tree->position = position;
237 (*function) (tree, arg);
42005513 238 position += LENGTH (tree); tree = tree->right;
19d4e9a7 239 }
a50699fd
JA
240}
241\f
242#if 0
e39adcda
GM
243
244static int icount;
245static int idepth;
246static int zero_length;
247
7ce503fd 248/* These functions are temporary, for debugging purposes only. */
a50699fd
JA
249
250INTERVAL search_interval, found_interval;
251
252void
1dae0f0a 253check_for_interval (INTERVAL i)
a50699fd
JA
254{
255 if (i == search_interval)
256 {
257 found_interval = i;
258 icount++;
259 }
260}
261
262INTERVAL
1dae0f0a 263search_for_interval (INTERVAL i, INTERVAL tree)
a50699fd
JA
264{
265 icount = 0;
266 search_interval = i;
77c7bcb1 267 found_interval = NULL;
19d4e9a7 268 traverse_intervals_noorder (tree, &check_for_interval, Qnil);
a50699fd
JA
269 return found_interval;
270}
271
272static void
1dae0f0a 273inc_interval_count (INTERVAL i)
a50699fd
JA
274{
275 icount++;
276 if (LENGTH (i) == 0)
277 zero_length++;
278 if (depth > idepth)
279 idepth = depth;
280}
281
282int
1dae0f0a 283count_intervals (INTERVAL i)
a50699fd
JA
284{
285 icount = 0;
286 idepth = 0;
287 zero_length = 0;
19d4e9a7 288 traverse_intervals_noorder (i, &inc_interval_count, Qnil);
a50699fd
JA
289
290 return icount;
291}
292
293static INTERVAL
1dae0f0a 294root_interval (INTERVAL interval)
a50699fd
JA
295{
296 register INTERVAL i = interval;
297
298 while (! ROOT_INTERVAL_P (i))
439d5cb4 299 i = INTERVAL_PARENT (i);
a50699fd
JA
300
301 return i;
302}
303#endif
304\f
305/* Assuming that a left child exists, perform the following operation:
306
307 A B
308 / \ / \
309 B => A
310 / \ / \
311 c c
312*/
313
55d4c1b2 314static inline INTERVAL
971de7fb 315rotate_right (INTERVAL interval)
a50699fd
JA
316{
317 INTERVAL i;
318 INTERVAL B = interval->left;
d311d28c 319 ptrdiff_t old_total = interval->total_length;
a50699fd 320
7ce503fd 321 /* Deal with any Parent of A; make it point to B. */
a50699fd 322 if (! ROOT_INTERVAL_P (interval))
e39adcda
GM
323 {
324 if (AM_LEFT_CHILD (interval))
6a3d20cc 325 interval_set_left (INTERVAL_PARENT (interval), B);
e39adcda 326 else
6a3d20cc 327 interval_set_right (INTERVAL_PARENT (interval), B);
e39adcda 328 }
6a3d20cc 329 interval_copy_parent (B, interval);
a50699fd 330
4314dea4
RS
331 /* Make B the parent of A */
332 i = B->right;
6a3d20cc
DA
333 interval_set_right (B, interval);
334 interval_set_parent (interval, B);
a50699fd 335
4314dea4 336 /* Make A point to c */
6a3d20cc 337 interval_set_left (interval, i);
77c7bcb1 338 if (i)
6a3d20cc 339 interval_set_parent (i, interval);
4314dea4 340
550bd63a 341 /* A's total length is decreased by the length of B and its left child. */
4314dea4 342 interval->total_length -= B->total_length - LEFT_TOTAL_LENGTH (interval);
9c08a8d4 343 eassert (0 <= TOTAL_LENGTH (interval));
4314dea4
RS
344
345 /* B must have the same total length of A. */
346 B->total_length = old_total;
9c08a8d4 347 eassert (0 <= TOTAL_LENGTH (B));
a50699fd
JA
348
349 return B;
350}
4314dea4 351
a50699fd
JA
352/* Assuming that a right child exists, perform the following operation:
353
7d0393cf
JB
354 A B
355 / \ / \
a50699fd 356 B => A
7d0393cf 357 / \ / \
a50699fd
JA
358 c c
359*/
360
55d4c1b2 361static inline INTERVAL
971de7fb 362rotate_left (INTERVAL interval)
a50699fd
JA
363{
364 INTERVAL i;
365 INTERVAL B = interval->right;
d311d28c 366 ptrdiff_t old_total = interval->total_length;
a50699fd 367
4314dea4 368 /* Deal with any parent of A; make it point to B. */
a50699fd 369 if (! ROOT_INTERVAL_P (interval))
e39adcda
GM
370 {
371 if (AM_LEFT_CHILD (interval))
6a3d20cc 372 interval_set_left (INTERVAL_PARENT (interval), B);
e39adcda 373 else
6a3d20cc 374 interval_set_right (INTERVAL_PARENT (interval), B);
e39adcda 375 }
6a3d20cc 376 interval_copy_parent (B, interval);
a50699fd
JA
377
378 /* Make B the parent of A */
4314dea4 379 i = B->left;
6a3d20cc
DA
380 interval_set_left (B, interval);
381 interval_set_parent (interval, B);
a50699fd
JA
382
383 /* Make A point to c */
6a3d20cc 384 interval_set_right (interval, i);
77c7bcb1 385 if (i)
6a3d20cc 386 interval_set_parent (i, interval);
4314dea4 387
550bd63a 388 /* A's total length is decreased by the length of B and its right child. */
4314dea4 389 interval->total_length -= B->total_length - RIGHT_TOTAL_LENGTH (interval);
9c08a8d4 390 eassert (0 <= TOTAL_LENGTH (interval));
4314dea4
RS
391
392 /* B must have the same total length of A. */
393 B->total_length = old_total;
9c08a8d4 394 eassert (0 <= TOTAL_LENGTH (B));
a50699fd
JA
395
396 return B;
397}
398\f
4314dea4
RS
399/* Balance an interval tree with the assumption that the subtrees
400 themselves are already balanced. */
401
402static INTERVAL
971de7fb 403balance_an_interval (INTERVAL i)
4314dea4 404{
d311d28c 405 register ptrdiff_t old_diff, new_diff;
4314dea4
RS
406
407 while (1)
408 {
409 old_diff = LEFT_TOTAL_LENGTH (i) - RIGHT_TOTAL_LENGTH (i);
410 if (old_diff > 0)
411 {
727fec2d 412 /* Since the left child is longer, there must be one. */
4314dea4
RS
413 new_diff = i->total_length - i->left->total_length
414 + RIGHT_TOTAL_LENGTH (i->left) - LEFT_TOTAL_LENGTH (i->left);
1ea40aa2 415 if (eabs (new_diff) >= old_diff)
4314dea4
RS
416 break;
417 i = rotate_right (i);
418 balance_an_interval (i->right);
419 }
420 else if (old_diff < 0)
421 {
727fec2d 422 /* Since the right child is longer, there must be one. */
4314dea4
RS
423 new_diff = i->total_length - i->right->total_length
424 + LEFT_TOTAL_LENGTH (i->right) - RIGHT_TOTAL_LENGTH (i->right);
1ea40aa2 425 if (eabs (new_diff) >= -old_diff)
4314dea4
RS
426 break;
427 i = rotate_left (i);
428 balance_an_interval (i->left);
429 }
430 else
431 break;
432 }
433 return i;
434}
435
436/* Balance INTERVAL, potentially stuffing it back into its parent
437 Lisp Object. */
438
55d4c1b2 439static inline INTERVAL
971de7fb 440balance_possible_root_interval (register INTERVAL interval)
4314dea4
RS
441{
442 Lisp_Object parent;
439d5cb4 443 int have_parent = 0;
4314dea4 444
439d5cb4 445 if (!INTERVAL_HAS_OBJECT (interval) && !INTERVAL_HAS_PARENT (interval))
4314dea4
RS
446 return interval;
447
439d5cb4
KR
448 if (INTERVAL_HAS_OBJECT (interval))
449 {
450 have_parent = 1;
451 GET_INTERVAL_OBJECT (parent, interval);
452 }
4314dea4
RS
453 interval = balance_an_interval (interval);
454
439d5cb4
KR
455 if (have_parent)
456 {
457 if (BUFFERP (parent))
8707c1e5 458 buffer_set_intervals (XBUFFER (parent), interval);
439d5cb4 459 else if (STRINGP (parent))
ad8c997f 460 string_set_intervals (parent, interval);
439d5cb4 461 }
4314dea4
RS
462
463 return interval;
464}
465
466/* Balance the interval tree TREE. Balancing is by weight
467 (the amount of text). */
468
469static INTERVAL
971de7fb 470balance_intervals_internal (register INTERVAL tree)
4314dea4
RS
471{
472 /* Balance within each side. */
473 if (tree->left)
8f3b9b95 474 balance_intervals_internal (tree->left);
4314dea4 475 if (tree->right)
8f3b9b95 476 balance_intervals_internal (tree->right);
4314dea4
RS
477 return balance_an_interval (tree);
478}
479
480/* Advertised interface to balance intervals. */
481
482INTERVAL
971de7fb 483balance_intervals (INTERVAL tree)
4314dea4 484{
77c7bcb1 485 return tree ? balance_intervals_internal (tree) : NULL;
4314dea4 486}
77c7bcb1 487
8707c1e5
DA
488/* Rebalance text properties of B. */
489
490static void
491buffer_balance_intervals (struct buffer *b)
492{
493 INTERVAL i;
494
495 eassert (b != NULL);
496 i = buffer_get_intervals (b);
497 if (i)
498 buffer_set_intervals (b, balance_an_interval (i));
499}
500
2bc7a79b
JB
501/* Split INTERVAL into two pieces, starting the second piece at
502 character position OFFSET (counting from 0), relative to INTERVAL.
503 INTERVAL becomes the left-hand piece, and the right-hand piece
504 (second, lexicographically) is returned.
90ba40fc
JA
505
506 The size and position fields of the two intervals are set based upon
507 those of the original interval. The property list of the new interval
508 is reset, thus it is up to the caller to do the right thing with the
509 result.
a50699fd
JA
510
511 Note that this does not change the position of INTERVAL; if it is a root,
7ce503fd 512 it is still a root after this operation. */
a50699fd
JA
513
514INTERVAL
d311d28c 515split_interval_right (INTERVAL interval, ptrdiff_t offset)
a50699fd
JA
516{
517 INTERVAL new = make_interval ();
d311d28c
PE
518 ptrdiff_t position = interval->position;
519 ptrdiff_t new_length = LENGTH (interval) - offset;
a50699fd 520
2bc7a79b 521 new->position = position + offset;
6a3d20cc 522 interval_set_parent (new, interval);
a50699fd 523
4314dea4 524 if (NULL_RIGHT_CHILD (interval))
a50699fd 525 {
6a3d20cc 526 interval_set_right (interval, new);
a50699fd 527 new->total_length = new_length;
9c08a8d4 528 eassert (0 <= TOTAL_LENGTH (new));
a50699fd 529 }
cc6e2aaa
RS
530 else
531 {
532 /* Insert the new node between INTERVAL and its right child. */
6a3d20cc
DA
533 interval_set_right (new, interval->right);
534 interval_set_parent (interval->right, new);
535 interval_set_right (interval, new);
cc6e2aaa 536 new->total_length = new_length + new->right->total_length;
9c08a8d4 537 eassert (0 <= TOTAL_LENGTH (new));
cc6e2aaa
RS
538 balance_an_interval (new);
539 }
7d0393cf 540
4314dea4
RS
541 balance_possible_root_interval (interval);
542
a50699fd
JA
543 return new;
544}
545
2bc7a79b
JB
546/* Split INTERVAL into two pieces, starting the second piece at
547 character position OFFSET (counting from 0), relative to INTERVAL.
548 INTERVAL becomes the right-hand piece, and the left-hand piece
549 (first, lexicographically) is returned.
a50699fd 550
90ba40fc
JA
551 The size and position fields of the two intervals are set based upon
552 those of the original interval. The property list of the new interval
553 is reset, thus it is up to the caller to do the right thing with the
554 result.
555
556 Note that this does not change the position of INTERVAL; if it is a root,
7ce503fd 557 it is still a root after this operation. */
a50699fd
JA
558
559INTERVAL
d311d28c 560split_interval_left (INTERVAL interval, ptrdiff_t offset)
a50699fd
JA
561{
562 INTERVAL new = make_interval ();
d311d28c 563 ptrdiff_t new_length = offset;
a50699fd 564
a50699fd 565 new->position = interval->position;
2bc7a79b 566 interval->position = interval->position + offset;
6a3d20cc 567 interval_set_parent (new, interval);
a50699fd
JA
568
569 if (NULL_LEFT_CHILD (interval))
570 {
6a3d20cc 571 interval_set_left (interval, new);
a50699fd 572 new->total_length = new_length;
9c08a8d4 573 eassert (0 <= TOTAL_LENGTH (new));
a50699fd 574 }
cc6e2aaa
RS
575 else
576 {
577 /* Insert the new node between INTERVAL and its left child. */
6a3d20cc
DA
578 interval_set_left (new, interval->left);
579 interval_set_parent (new->left, new);
580 interval_set_left (interval, new);
cc6e2aaa 581 new->total_length = new_length + new->left->total_length;
9c08a8d4 582 eassert (0 <= TOTAL_LENGTH (new));
cc6e2aaa
RS
583 balance_an_interval (new);
584 }
7d0393cf 585
4314dea4 586 balance_possible_root_interval (interval);
a50699fd
JA
587
588 return new;
589}
590\f
944d4e4b
KH
591/* Return the proper position for the first character
592 described by the interval tree SOURCE.
593 This is 1 if the parent is a buffer,
594 0 if the parent is a string or if there is no parent.
595
596 Don't use this function on an interval which is the child
597 of another interval! */
598
fc7bf025 599static int
971de7fb 600interval_start_pos (INTERVAL source)
944d4e4b
KH
601{
602 Lisp_Object parent;
603
77c7bcb1 604 if (!source)
944d4e4b
KH
605 return 0;
606
e0b8c689
KR
607 if (! INTERVAL_HAS_OBJECT (source))
608 return 0;
439d5cb4 609 GET_INTERVAL_OBJECT (parent, source);
944d4e4b
KH
610 if (BUFFERP (parent))
611 return BUF_BEG (XBUFFER (parent));
612 return 0;
613}
614
90ba40fc 615/* Find the interval containing text position POSITION in the text
24e3d3bf 616 represented by the interval tree TREE. POSITION is a buffer
944d4e4b
KH
617 position (starting from 1) or a string index (starting from 0).
618 If POSITION is at the end of the buffer or string,
619 return the interval containing the last character.
a50699fd 620
90ba40fc
JA
621 The `position' field, which is a cache of an interval's position,
622 is updated in the interval found. Other functions (e.g., next_interval)
7ce503fd 623 will update this cache based on the result of find_interval. */
90ba40fc 624
1863bbf8 625INTERVAL
d311d28c 626find_interval (register INTERVAL tree, register ptrdiff_t position)
a50699fd 627{
24e3d3bf
JB
628 /* The distance from the left edge of the subtree at TREE
629 to POSITION. */
d311d28c 630 register ptrdiff_t relative_position;
a50699fd 631
77c7bcb1
DA
632 if (!tree)
633 return NULL;
a50699fd 634
944d4e4b 635 relative_position = position;
439d5cb4
KR
636 if (INTERVAL_HAS_OBJECT (tree))
637 {
638 Lisp_Object parent;
639 GET_INTERVAL_OBJECT (parent, tree);
640 if (BUFFERP (parent))
641 relative_position -= BUF_BEG (XBUFFER (parent));
642 }
944d4e4b 643
cce7fefc 644 eassert (relative_position <= TOTAL_LENGTH (tree));
a50699fd 645
52283633
SM
646 if (!handling_signal)
647 tree = balance_possible_root_interval (tree);
4314dea4 648
a50699fd
JA
649 while (1)
650 {
24e3d3bf 651 if (relative_position < LEFT_TOTAL_LENGTH (tree))
a50699fd
JA
652 {
653 tree = tree->left;
654 }
24e3d3bf
JB
655 else if (! NULL_RIGHT_CHILD (tree)
656 && relative_position >= (TOTAL_LENGTH (tree)
657 - RIGHT_TOTAL_LENGTH (tree)))
a50699fd
JA
658 {
659 relative_position -= (TOTAL_LENGTH (tree)
660 - RIGHT_TOTAL_LENGTH (tree));
661 tree = tree->right;
662 }
663 else
664 {
944d4e4b 665 tree->position
4867a283
SM
666 = (position - relative_position /* left edge of *tree. */
667 + LEFT_TOTAL_LENGTH (tree)); /* left edge of this interval. */
24e3d3bf 668
a50699fd
JA
669 return tree;
670 }
671 }
672}
673\f
674/* Find the succeeding interval (lexicographically) to INTERVAL.
90ba40fc 675 Sets the `position' field based on that of INTERVAL (see
7ce503fd 676 find_interval). */
a50699fd
JA
677
678INTERVAL
971de7fb 679next_interval (register INTERVAL interval)
a50699fd
JA
680{
681 register INTERVAL i = interval;
d311d28c 682 register ptrdiff_t next_position;
a50699fd 683
77c7bcb1
DA
684 if (!i)
685 return NULL;
a50699fd
JA
686 next_position = interval->position + LENGTH (interval);
687
688 if (! NULL_RIGHT_CHILD (i))
689 {
690 i = i->right;
691 while (! NULL_LEFT_CHILD (i))
692 i = i->left;
693
694 i->position = next_position;
695 return i;
696 }
697
698 while (! NULL_PARENT (i))
699 {
700 if (AM_LEFT_CHILD (i))
701 {
439d5cb4 702 i = INTERVAL_PARENT (i);
a50699fd
JA
703 i->position = next_position;
704 return i;
705 }
706
439d5cb4 707 i = INTERVAL_PARENT (i);
a50699fd
JA
708 }
709
77c7bcb1 710 return NULL;
a50699fd
JA
711}
712
713/* Find the preceding interval (lexicographically) to INTERVAL.
90ba40fc 714 Sets the `position' field based on that of INTERVAL (see
7ce503fd 715 find_interval). */
a50699fd
JA
716
717INTERVAL
971de7fb 718previous_interval (register INTERVAL interval)
a50699fd
JA
719{
720 register INTERVAL i;
a50699fd 721
77c7bcb1
DA
722 if (!interval)
723 return NULL;
a50699fd
JA
724
725 if (! NULL_LEFT_CHILD (interval))
726 {
727 i = interval->left;
728 while (! NULL_RIGHT_CHILD (i))
729 i = i->right;
730
731 i->position = interval->position - LENGTH (i);
732 return i;
733 }
734
735 i = interval;
736 while (! NULL_PARENT (i))
737 {
738 if (AM_RIGHT_CHILD (i))
739 {
439d5cb4 740 i = INTERVAL_PARENT (i);
a50699fd
JA
741
742 i->position = interval->position - LENGTH (i);
743 return i;
744 }
439d5cb4 745 i = INTERVAL_PARENT (i);
a50699fd
JA
746 }
747
77c7bcb1 748 return NULL;
a50699fd 749}
25eeac41
RS
750
751/* Find the interval containing POS given some non-NULL INTERVAL
75167cd4 752 in the same tree. Note that we need to update interval->position
52283633
SM
753 if we go down the tree.
754 To speed up the process, we assume that the ->position of
755 I and all its parents is already uptodate. */
25eeac41 756INTERVAL
d311d28c 757update_interval (register INTERVAL i, ptrdiff_t pos)
25eeac41 758{
77c7bcb1
DA
759 if (!i)
760 return NULL;
25eeac41 761
7d0393cf 762 while (1)
25eeac41 763 {
7d0393cf 764 if (pos < i->position)
25eeac41
RS
765 {
766 /* Move left. */
7d0393cf 767 if (pos >= i->position - TOTAL_LENGTH (i->left))
75167cd4
RS
768 {
769 i->left->position = i->position - TOTAL_LENGTH (i->left)
770 + LEFT_TOTAL_LENGTH (i->left);
771 i = i->left; /* Move to the left child */
772 }
7d0393cf 773 else if (NULL_PARENT (i))
25eeac41 774 error ("Point before start of properties");
7d0393cf 775 else
439d5cb4 776 i = INTERVAL_PARENT (i);
25eeac41
RS
777 continue;
778 }
779 else if (pos >= INTERVAL_LAST_POS (i))
780 {
781 /* Move right. */
7d0393cf 782 if (pos < INTERVAL_LAST_POS (i) + TOTAL_LENGTH (i->right))
75167cd4 783 {
70cc95c4
SM
784 i->right->position = INTERVAL_LAST_POS (i)
785 + LEFT_TOTAL_LENGTH (i->right);
75167cd4
RS
786 i = i->right; /* Move to the right child */
787 }
7d0393cf 788 else if (NULL_PARENT (i))
7c85f529 789 error ("Point %"pD"d after end of properties", pos);
7d0393cf 790 else
70cc95c4 791 i = INTERVAL_PARENT (i);
25eeac41
RS
792 continue;
793 }
7d0393cf 794 else
25eeac41
RS
795 return i;
796 }
797}
798
90ba40fc
JA
799/* Effect an adjustment corresponding to the addition of LENGTH characters
800 of text. Do this by finding the interval containing POSITION in the
550bd63a 801 interval tree TREE, and then adjusting all of its ancestors by adding
90ba40fc
JA
802 LENGTH to them.
803
804 If POSITION is the first character of an interval, meaning that point
805 is actually between the two intervals, make the new text belong to
806 the interval which is "sticky".
807
1d1d7ba0 808 If both intervals are "sticky", then make them belong to the left-most
90ba40fc 809 interval. Another possibility would be to create a new interval for
7ce503fd 810 this text, and make it have the merged properties of both ends. */
90ba40fc
JA
811
812static INTERVAL
e79123aa 813adjust_intervals_for_insertion (INTERVAL tree,
d311d28c 814 ptrdiff_t position, ptrdiff_t length)
90ba40fc
JA
815{
816 register INTERVAL i;
7ce503fd
RS
817 register INTERVAL temp;
818 int eobp = 0;
944d4e4b 819 Lisp_Object parent;
d311d28c 820 ptrdiff_t offset;
7d0393cf 821
cce7fefc 822 eassert (TOTAL_LENGTH (tree) > 0);
90ba40fc 823
439d5cb4 824 GET_INTERVAL_OBJECT (parent, tree);
944d4e4b
KH
825 offset = (BUFFERP (parent) ? BUF_BEG (XBUFFER (parent)) : 0);
826
24e3d3bf
JB
827 /* If inserting at point-max of a buffer, that position will be out
828 of range. Remember that buffer positions are 1-based. */
944d4e4b
KH
829 if (position >= TOTAL_LENGTH (tree) + offset)
830 {
831 position = TOTAL_LENGTH (tree) + offset;
832 eobp = 1;
833 }
90ba40fc
JA
834
835 i = find_interval (tree, position);
7ce503fd 836
2313b945
RS
837 /* If in middle of an interval which is not sticky either way,
838 we must not just give its properties to the insertion.
f56b42ac
KH
839 So split this interval at the insertion point.
840
841 Originally, the if condition here was this:
842 (! (position == i->position || eobp)
843 && END_NONSTICKY_P (i)
844 && FRONT_NONSTICKY_P (i))
845 But, these macros are now unreliable because of introduction of
846 Vtext_property_default_nonsticky. So, we always check properties
847 one by one if POSITION is in middle of an interval. */
848 if (! (position == i->position || eobp))
2313b945 849 {
ca41a64d
RS
850 Lisp_Object tail;
851 Lisp_Object front, rear;
852
f56b42ac
KH
853 tail = i->plist;
854
855 /* Properties font-sticky and rear-nonsticky override
856 Vtext_property_default_nonsticky. So, if they are t, we can
857 skip one by one checking of properties. */
858 rear = textget (i->plist, Qrear_nonsticky);
859 if (! CONSP (rear) && ! NILP (rear))
860 {
861 /* All properties are nonsticky. We split the interval. */
862 goto check_done;
863 }
ca41a64d 864 front = textget (i->plist, Qfront_sticky);
f56b42ac
KH
865 if (! CONSP (front) && ! NILP (front))
866 {
867 /* All properties are sticky. We don't split the interval. */
868 tail = Qnil;
869 goto check_done;
870 }
ca41a64d 871
f56b42ac
KH
872 /* Does any actual property pose an actual problem? We break
873 the loop if we find a nonsticky property. */
874 for (; CONSP (tail); tail = Fcdr (XCDR (tail)))
ca41a64d 875 {
f56b42ac 876 Lisp_Object prop, tmp;
03699b14 877 prop = XCAR (tail);
ca41a64d 878
f56b42ac 879 /* Is this particular property front-sticky? */
ca41a64d
RS
880 if (CONSP (front) && ! NILP (Fmemq (prop, front)))
881 continue;
882
f56b42ac
KH
883 /* Is this particular property rear-nonsticky? */
884 if (CONSP (rear) && ! NILP (Fmemq (prop, rear)))
885 break;
886
887 /* Is this particular property recorded as sticky or
888 nonsticky in Vtext_property_default_nonsticky? */
889 tmp = Fassq (prop, Vtext_property_default_nonsticky);
890 if (CONSP (tmp))
891 {
892 if (NILP (tmp))
893 continue;
894 break;
895 }
896
897 /* By default, a text property is rear-sticky, thus we
898 continue the loop. */
ca41a64d
RS
899 }
900
f56b42ac 901 check_done:
ca41a64d
RS
902 /* If any property is a real problem, split the interval. */
903 if (! NILP (tail))
904 {
905 temp = split_interval_right (i, position - i->position);
906 copy_properties (i, temp);
907 i = temp;
908 }
2313b945
RS
909 }
910
90ba40fc 911 /* If we are positioned between intervals, check the stickiness of
7ce503fd
RS
912 both of them. We have to do this too, if we are at BEG or Z. */
913 if (position == i->position || eobp)
90ba40fc 914 {
7ce503fd
RS
915 register INTERVAL prev;
916
917 if (position == BEG)
918 prev = 0;
919 else if (eobp)
920 {
921 prev = i;
922 i = 0;
923 }
924 else
925 prev = previous_interval (i);
90ba40fc 926
7ce503fd
RS
927 /* Even if we are positioned between intervals, we default
928 to the left one if it exists. We extend it now and split
8e6208c5 929 off a part later, if stickiness demands it. */
439d5cb4 930 for (temp = prev ? prev : i; temp; temp = INTERVAL_PARENT_OR_NULL (temp))
4314dea4
RS
931 {
932 temp->total_length += length;
9c08a8d4 933 eassert (0 <= TOTAL_LENGTH (temp));
4314dea4
RS
934 temp = balance_possible_root_interval (temp);
935 }
7d0393cf 936
7ce503fd 937 /* If at least one interval has sticky properties,
f56b42ac
KH
938 we check the stickiness property by property.
939
940 Originally, the if condition here was this:
941 (END_NONSTICKY_P (prev) || FRONT_STICKY_P (i))
942 But, these macros are now unreliable because of introduction
943 of Vtext_property_default_nonsticky. So, we always have to
944 check stickiness of properties one by one. If cache of
945 stickiness is implemented in the future, we may be able to
946 use those macros again. */
947 if (1)
7ce503fd 948 {
dd675b05 949 Lisp_Object pleft, pright;
7ce503fd
RS
950 struct interval newi;
951
0c16dfed 952 RESET_INTERVAL (&newi);
77c7bcb1
DA
953 pleft = prev ? prev->plist : Qnil;
954 pright = i ? i->plist : Qnil;
6a3d20cc 955 interval_set_plist (&newi, merge_properties_sticky (pleft, pright));
7ce503fd 956
ef1900f3 957 if (! prev) /* i.e. position == BEG */
7ce503fd
RS
958 {
959 if (! intervals_equal (i, &newi))
960 {
961 i = split_interval_left (i, length);
6a3d20cc 962 interval_set_plist (i, newi.plist);
7ce503fd
RS
963 }
964 }
965 else if (! intervals_equal (prev, &newi))
966 {
6a3d20cc
DA
967 prev = split_interval_right (prev, position - prev->position);
968 interval_set_plist (prev, newi.plist);
77c7bcb1 969 if (i && intervals_equal (prev, i))
7ce503fd
RS
970 merge_interval_right (prev);
971 }
972
973 /* We will need to update the cache here later. */
974 }
975 else if (! prev && ! NILP (i->plist))
976 {
977 /* Just split off a new interval at the left.
978 Since I wasn't front-sticky, the empty plist is ok. */
979 i = split_interval_left (i, length);
980 }
90ba40fc
JA
981 }
982
7ce503fd
RS
983 /* Otherwise just extend the interval. */
984 else
90ba40fc 985 {
439d5cb4 986 for (temp = i; temp; temp = INTERVAL_PARENT_OR_NULL (temp))
4314dea4
RS
987 {
988 temp->total_length += length;
9c08a8d4 989 eassert (0 <= TOTAL_LENGTH (temp));
4314dea4
RS
990 temp = balance_possible_root_interval (temp);
991 }
90ba40fc 992 }
7d0393cf 993
90ba40fc
JA
994 return tree;
995}
7ce503fd 996
45d82bdc
KH
997/* Any property might be front-sticky on the left, rear-sticky on the left,
998 front-sticky on the right, or rear-sticky on the right; the 16 combinations
999 can be arranged in a matrix with rows denoting the left conditions and
1000 columns denoting the right conditions:
1001 _ __ _
1002_ FR FR FR FR
1003FR__ 0 1 2 3
1004 _FR 4 5 6 7
1005FR 8 9 A B
1006 FR C D E F
1007
1008 left-props = '(front-sticky (p8 p9 pa pb pc pd pe pf)
1009 rear-nonsticky (p4 p5 p6 p7 p8 p9 pa pb)
1010 p0 L p1 L p2 L p3 L p4 L p5 L p6 L p7 L
1011 p8 L p9 L pa L pb L pc L pd L pe L pf L)
1012 right-props = '(front-sticky (p2 p3 p6 p7 pa pb pe pf)
1013 rear-nonsticky (p1 p2 p5 p6 p9 pa pd pe)
1014 p0 R p1 R p2 R p3 R p4 R p5 R p6 R p7 R
1015 p8 R p9 R pa R pb R pc R pd R pe R pf R)
1016
1017 We inherit from whoever has a sticky side facing us. If both sides
1018 do (cases 2, 3, E, and F), then we inherit from whichever side has a
1019 non-nil value for the current property. If both sides do, then we take
1020 from the left.
1021
1022 When we inherit a property, we get its stickiness as well as its value.
1023 So, when we merge the above two lists, we expect to get this:
1024
1025 result = '(front-sticky (p6 p7 pa pb pc pd pe pf)
1026 rear-nonsticky (p6 pa)
1027 p0 L p1 L p2 L p3 L p6 R p7 R
1028 pa R pb R pc L pd L pe L pf L)
1029
1030 The optimizable special cases are:
1031 left rear-nonsticky = nil, right front-sticky = nil (inherit left)
1032 left rear-nonsticky = t, right front-sticky = t (inherit right)
1033 left rear-nonsticky = t, right front-sticky = nil (inherit none)
1034*/
1035
93ea6e8f 1036static Lisp_Object
971de7fb 1037merge_properties_sticky (Lisp_Object pleft, Lisp_Object pright)
7ce503fd 1038{
dd675b05
KH
1039 register Lisp_Object props, front, rear;
1040 Lisp_Object lfront, lrear, rfront, rrear;
4ab19eb3 1041 register Lisp_Object tail1, tail2, sym, lval, rval, cat;
45d82bdc 1042 int use_left, use_right;
4ab19eb3 1043 int lpresent;
7ce503fd 1044
dd675b05
KH
1045 props = Qnil;
1046 front = Qnil;
1047 rear = Qnil;
1048 lfront = textget (pleft, Qfront_sticky);
1049 lrear = textget (pleft, Qrear_nonsticky);
1050 rfront = textget (pright, Qfront_sticky);
1051 rrear = textget (pright, Qrear_nonsticky);
1052
45d82bdc 1053 /* Go through each element of PRIGHT. */
4867a283 1054 for (tail1 = pright; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
7ce503fd 1055 {
f56b42ac
KH
1056 Lisp_Object tmp;
1057
4867a283 1058 sym = XCAR (tail1);
7ce503fd
RS
1059
1060 /* Sticky properties get special treatment. */
1061 if (EQ (sym, Qrear_nonsticky) || EQ (sym, Qfront_sticky))
1062 continue;
45d82bdc 1063
4867a283
SM
1064 rval = Fcar (XCDR (tail1));
1065 for (tail2 = pleft; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
1066 if (EQ (sym, XCAR (tail2)))
45d82bdc 1067 break;
45d82bdc 1068
4ab19eb3
RS
1069 /* Indicate whether the property is explicitly defined on the left.
1070 (We know it is defined explicitly on the right
1071 because otherwise we don't get here.) */
1072 lpresent = ! NILP (tail2);
1073 lval = (NILP (tail2) ? Qnil : Fcar (Fcdr (tail2)));
1074
f56b42ac
KH
1075 /* Even if lrear or rfront say nothing about the stickiness of
1076 SYM, Vtext_property_default_nonsticky may give default
1077 stickiness to SYM. */
1078 tmp = Fassq (sym, Vtext_property_default_nonsticky);
1079 use_left = (lpresent
1080 && ! (TMEM (sym, lrear)
3a62677f 1081 || (CONSP (tmp) && ! NILP (XCDR (tmp)))));
f56b42ac
KH
1082 use_right = (TMEM (sym, rfront)
1083 || (CONSP (tmp) && NILP (XCDR (tmp))));
45d82bdc
KH
1084 if (use_left && use_right)
1085 {
4ab19eb3
RS
1086 if (NILP (lval))
1087 use_left = 0;
1088 else if (NILP (rval))
1089 use_right = 0;
45d82bdc
KH
1090 }
1091 if (use_left)
7ce503fd 1092 {
45d82bdc
KH
1093 /* We build props as (value sym ...) rather than (sym value ...)
1094 because we plan to nreverse it when we're done. */
4ab19eb3 1095 props = Fcons (lval, Fcons (sym, props));
45d82bdc 1096 if (TMEM (sym, lfront))
7ce503fd 1097 front = Fcons (sym, front);
45d82bdc
KH
1098 if (TMEM (sym, lrear))
1099 rear = Fcons (sym, rear);
7ce503fd 1100 }
45d82bdc 1101 else if (use_right)
7ce503fd 1102 {
4ab19eb3 1103 props = Fcons (rval, Fcons (sym, props));
45d82bdc
KH
1104 if (TMEM (sym, rfront))
1105 front = Fcons (sym, front);
1106 if (TMEM (sym, rrear))
1107 rear = Fcons (sym, rear);
7ce503fd
RS
1108 }
1109 }
45d82bdc
KH
1110
1111 /* Now go through each element of PLEFT. */
4867a283 1112 for (tail2 = pleft; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
7ce503fd 1113 {
f56b42ac
KH
1114 Lisp_Object tmp;
1115
4867a283 1116 sym = XCAR (tail2);
7ce503fd
RS
1117
1118 /* Sticky properties get special treatment. */
1119 if (EQ (sym, Qrear_nonsticky) || EQ (sym, Qfront_sticky))
1120 continue;
1121
45d82bdc 1122 /* If sym is in PRIGHT, we've already considered it. */
4867a283
SM
1123 for (tail1 = pright; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
1124 if (EQ (sym, XCAR (tail1)))
7ce503fd 1125 break;
45d82bdc
KH
1126 if (! NILP (tail1))
1127 continue;
1128
4867a283 1129 lval = Fcar (XCDR (tail2));
45d82bdc 1130
f56b42ac
KH
1131 /* Even if lrear or rfront say nothing about the stickiness of
1132 SYM, Vtext_property_default_nonsticky may give default
1133 stickiness to SYM. */
1134 tmp = Fassq (sym, Vtext_property_default_nonsticky);
1135
45d82bdc 1136 /* Since rval is known to be nil in this loop, the test simplifies. */
f56b42ac 1137 if (! (TMEM (sym, lrear) || (CONSP (tmp) && ! NILP (XCDR (tmp)))))
7ce503fd 1138 {
4ab19eb3 1139 props = Fcons (lval, Fcons (sym, props));
45d82bdc
KH
1140 if (TMEM (sym, lfront))
1141 front = Fcons (sym, front);
1142 }
f56b42ac 1143 else if (TMEM (sym, rfront) || (CONSP (tmp) && NILP (XCDR (tmp))))
45d82bdc
KH
1144 {
1145 /* The value is nil, but we still inherit the stickiness
1146 from the right. */
7ce503fd 1147 front = Fcons (sym, front);
45d82bdc 1148 if (TMEM (sym, rrear))
7ce503fd
RS
1149 rear = Fcons (sym, rear);
1150 }
1151 }
550bd63a 1152 props = Fnreverse (props);
7ce503fd 1153 if (! NILP (rear))
550bd63a 1154 props = Fcons (Qrear_nonsticky, Fcons (Fnreverse (rear), props));
4ab19eb3
RS
1155
1156 cat = textget (props, Qcategory);
1157 if (! NILP (front)
7d0393cf 1158 &&
4ab19eb3
RS
1159 /* If we have inherited a front-stick category property that is t,
1160 we don't need to set up a detailed one. */
1161 ! (! NILP (cat) && SYMBOLP (cat)
1162 && EQ (Fget (cat, Qfront_sticky), Qt)))
45d82bdc 1163 props = Fcons (Qfront_sticky, Fcons (Fnreverse (front), props));
7ce503fd 1164 return props;
7ce503fd
RS
1165}
1166
a50699fd 1167\f
7d0393cf 1168/* Delete a node I from its interval tree by merging its subtrees
90ba40fc 1169 into one subtree which is then returned. Caller is responsible for
7ce503fd 1170 storing the resulting subtree into its parent. */
a50699fd
JA
1171
1172static INTERVAL
971de7fb 1173delete_node (register INTERVAL i)
a50699fd
JA
1174{
1175 register INTERVAL migrate, this;
d311d28c 1176 register ptrdiff_t migrate_amt;
a50699fd 1177
77c7bcb1 1178 if (!i->left)
a50699fd 1179 return i->right;
77c7bcb1 1180 if (!i->right)
a50699fd
JA
1181 return i->left;
1182
1183 migrate = i->left;
1184 migrate_amt = i->left->total_length;
1185 this = i->right;
1186 this->total_length += migrate_amt;
77c7bcb1 1187 while (this->left)
a50699fd
JA
1188 {
1189 this = this->left;
1190 this->total_length += migrate_amt;
1191 }
9c08a8d4 1192 eassert (0 <= TOTAL_LENGTH (this));
6a3d20cc
DA
1193 interval_set_left (this, migrate);
1194 interval_set_parent (migrate, this);
a50699fd
JA
1195
1196 return i->right;
1197}
1198
1199/* Delete interval I from its tree by calling `delete_node'
1200 and properly connecting the resultant subtree.
1201
1202 I is presumed to be empty; that is, no adjustments are made
7ce503fd 1203 for the length of I. */
a50699fd 1204
93ea6e8f 1205static void
971de7fb 1206delete_interval (register INTERVAL i)
a50699fd
JA
1207{
1208 register INTERVAL parent;
d311d28c 1209 ptrdiff_t amt = LENGTH (i);
a50699fd 1210
cce7fefc 1211 eassert (amt == 0); /* Only used on zero-length intervals now. */
a50699fd
JA
1212
1213 if (ROOT_INTERVAL_P (i))
1214 {
dd675b05 1215 Lisp_Object owner;
439d5cb4 1216 GET_INTERVAL_OBJECT (owner, i);
a50699fd 1217 parent = delete_node (i);
77c7bcb1 1218 if (parent)
6a3d20cc 1219 interval_set_object (parent, owner);
a50699fd 1220
b629dd47 1221 if (BUFFERP (owner))
8707c1e5 1222 buffer_set_intervals (XBUFFER (owner), parent);
b629dd47 1223 else if (STRINGP (owner))
ad8c997f 1224 string_set_intervals (owner, parent);
a50699fd
JA
1225 else
1226 abort ();
1227
1228 return;
1229 }
1230
439d5cb4 1231 parent = INTERVAL_PARENT (i);
a50699fd
JA
1232 if (AM_LEFT_CHILD (i))
1233 {
6a3d20cc 1234 interval_set_left (parent, delete_node (i));
77c7bcb1 1235 if (parent->left)
6a3d20cc 1236 interval_set_parent (parent->left, parent);
a50699fd
JA
1237 }
1238 else
1239 {
6a3d20cc 1240 interval_set_right (parent, delete_node (i));
77c7bcb1 1241 if (parent->right)
6a3d20cc 1242 interval_set_parent (parent->right, parent);
a50699fd
JA
1243 }
1244}
1245\f
24e3d3bf
JB
1246/* Find the interval in TREE corresponding to the relative position
1247 FROM and delete as much as possible of AMOUNT from that interval.
1248 Return the amount actually deleted, and if the interval was
1249 zeroed-out, delete that interval node from the tree.
1250
1251 Note that FROM is actually origin zero, aka relative to the
1252 leftmost edge of tree. This is appropriate since we call ourselves
1253 recursively on subtrees.
a50699fd 1254
1d1d7ba0 1255 Do this by recursing down TREE to the interval in question, and
7ce503fd 1256 deleting the appropriate amount of text. */
a50699fd 1257
d311d28c
PE
1258static ptrdiff_t
1259interval_deletion_adjustment (register INTERVAL tree, register ptrdiff_t from,
1260 register ptrdiff_t amount)
a50699fd 1261{
d311d28c 1262 register ptrdiff_t relative_position = from;
a50699fd 1263
77c7bcb1 1264 if (!tree)
a50699fd
JA
1265 return 0;
1266
b50a28de 1267 /* Left branch. */
24e3d3bf 1268 if (relative_position < LEFT_TOTAL_LENGTH (tree))
a50699fd 1269 {
d311d28c 1270 ptrdiff_t subtract = interval_deletion_adjustment (tree->left,
c20db43f
PE
1271 relative_position,
1272 amount);
a50699fd 1273 tree->total_length -= subtract;
9c08a8d4 1274 eassert (0 <= TOTAL_LENGTH (tree));
a50699fd
JA
1275 return subtract;
1276 }
b50a28de 1277 /* Right branch. */
24e3d3bf
JB
1278 else if (relative_position >= (TOTAL_LENGTH (tree)
1279 - RIGHT_TOTAL_LENGTH (tree)))
a50699fd 1280 {
d311d28c 1281 ptrdiff_t subtract;
a50699fd
JA
1282
1283 relative_position -= (tree->total_length
1284 - RIGHT_TOTAL_LENGTH (tree));
1285 subtract = interval_deletion_adjustment (tree->right,
1286 relative_position,
1287 amount);
1288 tree->total_length -= subtract;
9c08a8d4 1289 eassert (0 <= TOTAL_LENGTH (tree));
a50699fd
JA
1290 return subtract;
1291 }
7ce503fd 1292 /* Here -- this node. */
a50699fd
JA
1293 else
1294 {
24e3d3bf 1295 /* How much can we delete from this interval? */
d311d28c 1296 ptrdiff_t my_amount = ((tree->total_length
e79123aa
EZ
1297 - RIGHT_TOTAL_LENGTH (tree))
1298 - relative_position);
24e3d3bf
JB
1299
1300 if (amount > my_amount)
1301 amount = my_amount;
1302
1303 tree->total_length -= amount;
9c08a8d4 1304 eassert (0 <= TOTAL_LENGTH (tree));
24e3d3bf
JB
1305 if (LENGTH (tree) == 0)
1306 delete_interval (tree);
7d0393cf 1307
24e3d3bf 1308 return amount;
a50699fd
JA
1309 }
1310
7ce503fd 1311 /* Never reach here. */
a50699fd
JA
1312}
1313
24e3d3bf
JB
1314/* Effect the adjustments necessary to the interval tree of BUFFER to
1315 correspond to the deletion of LENGTH characters from that buffer
1316 text. The deletion is effected at position START (which is a
7ce503fd 1317 buffer position, i.e. origin 1). */
1d1d7ba0 1318
a50699fd 1319static void
e79123aa 1320adjust_intervals_for_deletion (struct buffer *buffer,
d311d28c 1321 ptrdiff_t start, ptrdiff_t length)
a50699fd 1322{
d311d28c 1323 register ptrdiff_t left_to_delete = length;
8707c1e5 1324 register INTERVAL tree = buffer_get_intervals (buffer);
944d4e4b 1325 Lisp_Object parent;
d311d28c 1326 ptrdiff_t offset;
944d4e4b 1327
439d5cb4 1328 GET_INTERVAL_OBJECT (parent, tree);
944d4e4b 1329 offset = (BUFFERP (parent) ? BUF_BEG (XBUFFER (parent)) : 0);
a50699fd 1330
77c7bcb1 1331 if (!tree)
a50699fd
JA
1332 return;
1333
cce7fefc
DA
1334 eassert (start <= offset + TOTAL_LENGTH (tree)
1335 && start + length <= offset + TOTAL_LENGTH (tree));
24e3d3bf 1336
a50699fd
JA
1337 if (length == TOTAL_LENGTH (tree))
1338 {
8707c1e5 1339 buffer_set_intervals (buffer, NULL);
a50699fd
JA
1340 return;
1341 }
1342
1343 if (ONLY_INTERVAL_P (tree))
1344 {
1345 tree->total_length -= length;
9c08a8d4 1346 eassert (0 <= TOTAL_LENGTH (tree));
a50699fd
JA
1347 return;
1348 }
1349
944d4e4b
KH
1350 if (start > offset + TOTAL_LENGTH (tree))
1351 start = offset + TOTAL_LENGTH (tree);
a50699fd
JA
1352 while (left_to_delete > 0)
1353 {
944d4e4b 1354 left_to_delete -= interval_deletion_adjustment (tree, start - offset,
a50699fd 1355 left_to_delete);
8707c1e5 1356 tree = buffer_get_intervals (buffer);
a50699fd
JA
1357 if (left_to_delete == tree->total_length)
1358 {
8707c1e5 1359 buffer_set_intervals (buffer, NULL);
a50699fd
JA
1360 return;
1361 }
1362 }
1363}
1364\f
eb8c3be9 1365/* Make the adjustments necessary to the interval tree of BUFFER to
1d1d7ba0
JA
1366 represent an addition or deletion of LENGTH characters starting
1367 at position START. Addition or deletion is indicated by the sign
56120d6f 1368 of LENGTH. */
a9041e6c 1369
09db192c 1370void
9898bd0e 1371offset_intervals (struct buffer *buffer, ptrdiff_t start, ptrdiff_t length)
a50699fd 1372{
8707c1e5 1373 if (!buffer_get_intervals (buffer) || length == 0)
a50699fd
JA
1374 return;
1375
1376 if (length > 0)
8707c1e5
DA
1377 adjust_intervals_for_insertion (buffer_get_intervals (buffer),
1378 start, length);
a50699fd 1379 else
37aa2f85 1380 {
d311d28c 1381 IF_LINT (if (length < - TYPE_MAXIMUM (ptrdiff_t)) abort ();)
37aa2f85
PE
1382 adjust_intervals_for_deletion (buffer, start, -length);
1383 }
a50699fd 1384}
9c79dd1b
JA
1385\f
1386/* Merge interval I with its lexicographic successor. The resulting
1387 interval is returned, and has the properties of the original
1388 successor. The properties of I are lost. I is removed from the
1389 interval tree.
1390
1391 IMPORTANT:
1392 The caller must verify that this is not the last (rightmost)
7ce503fd 1393 interval. */
9c79dd1b 1394
93ea6e8f 1395static INTERVAL
971de7fb 1396merge_interval_right (register INTERVAL i)
9c79dd1b 1397{
d311d28c 1398 register ptrdiff_t absorb = LENGTH (i);
9c79dd1b
JA
1399 register INTERVAL successor;
1400
7ce503fd 1401 /* Find the succeeding interval. */
9c79dd1b 1402 if (! NULL_RIGHT_CHILD (i)) /* It's below us. Add absorb
7ce503fd 1403 as we descend. */
9c79dd1b
JA
1404 {
1405 successor = i->right;
1406 while (! NULL_LEFT_CHILD (successor))
1407 {
1408 successor->total_length += absorb;
9c08a8d4 1409 eassert (0 <= TOTAL_LENGTH (successor));
9c79dd1b
JA
1410 successor = successor->left;
1411 }
1412
1413 successor->total_length += absorb;
9c08a8d4 1414 eassert (0 <= TOTAL_LENGTH (successor));
9c79dd1b
JA
1415 delete_interval (i);
1416 return successor;
1417 }
1418
60cfd278
AS
1419 /* Zero out this interval. */
1420 i->total_length -= absorb;
9c08a8d4 1421 eassert (0 <= TOTAL_LENGTH (i));
60cfd278 1422
9c79dd1b
JA
1423 successor = i;
1424 while (! NULL_PARENT (successor)) /* It's above us. Subtract as
7ce503fd 1425 we ascend. */
9c79dd1b
JA
1426 {
1427 if (AM_LEFT_CHILD (successor))
1428 {
439d5cb4 1429 successor = INTERVAL_PARENT (successor);
9c79dd1b
JA
1430 delete_interval (i);
1431 return successor;
1432 }
1433
439d5cb4 1434 successor = INTERVAL_PARENT (successor);
9c79dd1b 1435 successor->total_length -= absorb;
9c08a8d4 1436 eassert (0 <= TOTAL_LENGTH (successor));
9c79dd1b
JA
1437 }
1438
1439 /* This must be the rightmost or last interval and cannot
7ce503fd 1440 be merged right. The caller should have known. */
9c79dd1b
JA
1441 abort ();
1442}
1443\f
1444/* Merge interval I with its lexicographic predecessor. The resulting
1445 interval is returned, and has the properties of the original predecessor.
1446 The properties of I are lost. Interval node I is removed from the tree.
1447
1448 IMPORTANT:
7ce503fd 1449 The caller must verify that this is not the first (leftmost) interval. */
9c79dd1b
JA
1450
1451INTERVAL
971de7fb 1452merge_interval_left (register INTERVAL i)
9c79dd1b 1453{
d311d28c 1454 register ptrdiff_t absorb = LENGTH (i);
9c79dd1b
JA
1455 register INTERVAL predecessor;
1456
7ce503fd 1457 /* Find the preceding interval. */
9c79dd1b 1458 if (! NULL_LEFT_CHILD (i)) /* It's below us. Go down,
7ce503fd 1459 adding ABSORB as we go. */
9c79dd1b
JA
1460 {
1461 predecessor = i->left;
1462 while (! NULL_RIGHT_CHILD (predecessor))
1463 {
1464 predecessor->total_length += absorb;
9c08a8d4 1465 eassert (0 <= TOTAL_LENGTH (predecessor));
9c79dd1b
JA
1466 predecessor = predecessor->right;
1467 }
1468
1469 predecessor->total_length += absorb;
9c08a8d4 1470 eassert (0 <= TOTAL_LENGTH (predecessor));
9c79dd1b
JA
1471 delete_interval (i);
1472 return predecessor;
1473 }
1474
60cfd278
AS
1475 /* Zero out this interval. */
1476 i->total_length -= absorb;
9c08a8d4 1477 eassert (0 <= TOTAL_LENGTH (i));
60cfd278 1478
9c79dd1b
JA
1479 predecessor = i;
1480 while (! NULL_PARENT (predecessor)) /* It's above us. Go up,
60cfd278 1481 subtracting ABSORB. */
9c79dd1b
JA
1482 {
1483 if (AM_RIGHT_CHILD (predecessor))
1484 {
439d5cb4 1485 predecessor = INTERVAL_PARENT (predecessor);
9c79dd1b
JA
1486 delete_interval (i);
1487 return predecessor;
1488 }
1489
439d5cb4 1490 predecessor = INTERVAL_PARENT (predecessor);
9c79dd1b 1491 predecessor->total_length -= absorb;
9c08a8d4 1492 eassert (0 <= TOTAL_LENGTH (predecessor));
9c79dd1b 1493 }
a50699fd 1494
9c79dd1b 1495 /* This must be the leftmost or first interval and cannot
7ce503fd 1496 be merged left. The caller should have known. */
9c79dd1b
JA
1497 abort ();
1498}
1499\f
44386687
DA
1500/* Create a copy of SOURCE but with the default value of UP. */
1501
1502static INTERVAL
1503reproduce_interval (INTERVAL source)
1504{
1505 register INTERVAL target = make_interval ();
1506
1507 target->total_length = source->total_length;
1508 target->position = source->position;
1509
1510 copy_properties (source, target);
1511
1512 if (! NULL_LEFT_CHILD (source))
1513 interval_set_left (target, reproduce_tree (source->left, target));
1514 if (! NULL_RIGHT_CHILD (source))
1515 interval_set_right (target, reproduce_tree (source->right, target));
1516
1517 return target;
1518}
1519
1d1d7ba0
JA
1520/* Make an exact copy of interval tree SOURCE which descends from
1521 PARENT. This is done by recursing through SOURCE, copying
1522 the current interval and its properties, and then adjusting
7ce503fd 1523 the pointers of the copy. */
1d1d7ba0 1524
a50699fd 1525static INTERVAL
971de7fb 1526reproduce_tree (INTERVAL source, INTERVAL parent)
a50699fd 1527{
44386687 1528 register INTERVAL target = reproduce_interval (source);
439d5cb4 1529
44386687
DA
1530 interval_set_parent (target, parent);
1531 return target;
439d5cb4
KR
1532}
1533
1534static INTERVAL
971de7fb 1535reproduce_tree_obj (INTERVAL source, Lisp_Object parent)
439d5cb4 1536{
44386687 1537 register INTERVAL target = reproduce_interval (source);
a50699fd 1538
44386687
DA
1539 interval_set_object (target, parent);
1540 return target;
a50699fd 1541}
294efdbe 1542\f
9c79dd1b 1543/* Insert the intervals of SOURCE into BUFFER at POSITION.
0b79989f 1544 LENGTH is the length of the text in SOURCE.
a50699fd 1545
944d4e4b
KH
1546 The `position' field of the SOURCE intervals is assumed to be
1547 consistent with its parent; therefore, SOURCE must be an
1548 interval tree made with copy_interval or must be the whole
1549 tree of a buffer or a string.
1550
2bc7a79b
JB
1551 This is used in insdel.c when inserting Lisp_Strings into the
1552 buffer. The text corresponding to SOURCE is already in the buffer
1553 when this is called. The intervals of new tree are a copy of those
1554 belonging to the string being inserted; intervals are never
1555 shared.
a50699fd 1556
0b79989f
RS
1557 If the inserted text had no intervals associated, and we don't
1558 want to inherit the surrounding text's properties, this function
a50699fd 1559 simply returns -- offset_intervals should handle placing the
90ba40fc 1560 text in the correct interval, depending on the sticky bits.
a50699fd
JA
1561
1562 If the inserted text had properties (intervals), then there are two
1563 cases -- either insertion happened in the middle of some interval,
1564 or between two intervals.
1565
65e8ee52
DA
1566 If the text goes into the middle of an interval, then new intervals
1567 are created in the middle, and new text has the union of its properties
1568 and those of the text into which it was inserted.
a50699fd
JA
1569
1570 If the text goes between two intervals, then if neither interval
90ba40fc
JA
1571 had its appropriate sticky property set (front_sticky, rear_sticky),
1572 the new text has only its properties. If one of the sticky properties
a50699fd 1573 is set, then the new text "sticks" to that region and its properties
eb8c3be9 1574 depend on merging as above. If both the preceding and succeeding
90ba40fc
JA
1575 intervals to the new text are "sticky", then the new text retains
1576 only its properties, as if neither sticky property were set. Perhaps
a50699fd 1577 we should consider merging all three sets of properties onto the new
7ce503fd 1578 text... */
a50699fd
JA
1579
1580void
d311d28c
PE
1581graft_intervals_into_buffer (INTERVAL source, ptrdiff_t position,
1582 ptrdiff_t length, struct buffer *buffer,
e79123aa 1583 int inherit)
a50699fd 1584{
dc6c6455 1585 register INTERVAL under, over, this;
e5d967c9 1586 register INTERVAL tree;
d311d28c 1587 ptrdiff_t over_used;
a50699fd 1588
8707c1e5 1589 tree = buffer_get_intervals (buffer);
e5d967c9 1590
34e096ed
RS
1591 /* If the new text has no properties, then with inheritance it
1592 becomes part of whatever interval it was inserted into.
1593 To prevent inheritance, we must clear out the properties
1594 of the newly inserted text. */
77c7bcb1 1595 if (!source)
0b79989f
RS
1596 {
1597 Lisp_Object buf;
77c7bcb1 1598 if (!inherit && tree && length > 0)
0b79989f 1599 {
55cfc731 1600 XSETBUFFER (buf, buffer);
34e096ed
RS
1601 set_text_properties_1 (make_number (position),
1602 make_number (position + length),
1603 Qnil, buf, 0);
0b79989f 1604 }
8707c1e5
DA
1605 /* Shouldn't be necessary. --Stef */
1606 buffer_balance_intervals (buffer);
0b79989f
RS
1607 return;
1608 }
a50699fd 1609
b50a28de
SM
1610 eassert (length == TOTAL_LENGTH (source));
1611
1612 if ((BUF_Z (buffer) - BUF_BEG (buffer)) == length)
56120d6f 1613 {
8707c1e5 1614 /* The inserted text constitutes the whole buffer, so
7ce503fd 1615 simply copy over the interval structure. */
8707c1e5
DA
1616 Lisp_Object buf;
1617
1618 XSETBUFFER (buf, buffer);
1619 buffer_set_intervals (buffer, reproduce_tree_obj (source, buf));
1620 buffer_get_intervals (buffer)->position = BUF_BEG (buffer);
1621 eassert (buffer_get_intervals (buffer)->up_obj == 1);
1622 return;
1623 }
77c7bcb1 1624 else if (!tree)
8707c1e5
DA
1625 {
1626 /* Create an interval tree in which to place a copy
7ce503fd 1627 of the intervals of the inserted string. */
249a6da9 1628 Lisp_Object buf;
8707c1e5 1629
55cfc731 1630 XSETBUFFER (buf, buffer);
323a7ad4 1631 tree = create_root_interval (buf);
8707c1e5 1632 }
cce7fefc
DA
1633 /* Paranoia -- the text has already been added, so
1634 this buffer should be of non-zero length. */
1635 eassert (TOTAL_LENGTH (tree) > 0);
a50699fd
JA
1636
1637 this = under = find_interval (tree, position);
77c7bcb1 1638 eassert (under);
944d4e4b 1639 over = find_interval (source, interval_start_pos (source));
a50699fd 1640
323a7ad4
RS
1641 /* Here for insertion in the middle of an interval.
1642 Split off an equivalent interval to the right,
1643 then don't bother with it any more. */
a50699fd 1644
323a7ad4 1645 if (position > under->position)
a50699fd
JA
1646 {
1647 INTERVAL end_unchanged
2bc7a79b 1648 = split_interval_left (this, position - under->position);
a50699fd 1649 copy_properties (under, end_unchanged);
323a7ad4 1650 under->position = position;
a50699fd 1651 }
323a7ad4
RS
1652 else
1653 {
f56b42ac
KH
1654 /* This call may have some effect because previous_interval may
1655 update `position' fields of intervals. Thus, don't ignore it
1656 for the moment. Someone please tell me the truth (K.Handa). */
dc6c6455
PE
1657 INTERVAL prev = previous_interval (under);
1658 (void) prev;
f56b42ac
KH
1659#if 0
1660 /* But, this code surely has no effect. And, anyway,
1661 END_NONSTICKY_P is unreliable now. */
7ce503fd 1662 if (prev && !END_NONSTICKY_P (prev))
323a7ad4 1663 prev = 0;
f56b42ac 1664#endif /* 0 */
323a7ad4
RS
1665 }
1666
1667 /* Insertion is now at beginning of UNDER. */
a50699fd 1668
323a7ad4 1669 /* The inserted text "sticks" to the interval `under',
7ce503fd
RS
1670 which means it gets those properties.
1671 The properties of under are the result of
8e6208c5 1672 adjust_intervals_for_insertion, so stickiness has
7ce503fd 1673 already been taken care of. */
7d0393cf 1674
6b61353c
KH
1675 /* OVER is the interval we are copying from next.
1676 OVER_USED says how many characters' worth of OVER
1677 have already been copied into target intervals.
1678 UNDER is the next interval in the target. */
1679 over_used = 0;
77c7bcb1 1680 while (over)
a50699fd 1681 {
6b61353c
KH
1682 /* If UNDER is longer than OVER, split it. */
1683 if (LENGTH (over) - over_used < LENGTH (under))
7ce503fd 1684 {
6b61353c 1685 this = split_interval_left (under, LENGTH (over) - over_used);
7ce503fd
RS
1686 copy_properties (under, this);
1687 }
323a7ad4
RS
1688 else
1689 this = under;
6b61353c
KH
1690
1691 /* THIS is now the interval to copy or merge into.
1692 OVER covers all of it. */
7ea69158 1693 if (inherit)
7ce503fd
RS
1694 merge_properties (over, this);
1695 else
1696 copy_properties (over, this);
6b61353c
KH
1697
1698 /* If THIS and OVER end at the same place,
1699 advance OVER to a new source interval. */
1700 if (LENGTH (this) == LENGTH (over) - over_used)
1701 {
1702 over = next_interval (over);
1703 over_used = 0;
1704 }
1705 else
1706 /* Otherwise just record that more of OVER has been used. */
1707 over_used += LENGTH (this);
1708
1709 /* Always advance to a new target interval. */
1710 under = next_interval (this);
a50699fd
JA
1711 }
1712
8707c1e5 1713 buffer_balance_intervals (buffer);
a50699fd
JA
1714}
1715
5cae0ec6
RS
1716/* Get the value of property PROP from PLIST,
1717 which is the plist of an interval.
7d0393cf 1718 We check for direct properties, for categories with property PROP,
06d92327 1719 and for PROP appearing on the default-text-properties list. */
5cae0ec6
RS
1720
1721Lisp_Object
971de7fb 1722textget (Lisp_Object plist, register Lisp_Object prop)
323a7ad4 1723{
91b53904 1724 return lookup_char_property (plist, prop, 1);
948fe32d
CW
1725}
1726
1727Lisp_Object
971de7fb 1728lookup_char_property (Lisp_Object plist, register Lisp_Object prop, int textprop)
948fe32d
CW
1729{
1730 register Lisp_Object tail, fallback = Qnil;
323a7ad4 1731
91b53904 1732 for (tail = plist; CONSP (tail); tail = Fcdr (XCDR (tail)))
323a7ad4
RS
1733 {
1734 register Lisp_Object tem;
91b53904 1735 tem = XCAR (tail);
323a7ad4 1736 if (EQ (prop, tem))
91b53904 1737 return Fcar (XCDR (tail));
5cae0ec6 1738 if (EQ (tem, Qcategory))
5dd6606e 1739 {
91b53904 1740 tem = Fcar (XCDR (tail));
5dd6606e
RS
1741 if (SYMBOLP (tem))
1742 fallback = Fget (tem, prop);
1743 }
323a7ad4 1744 }
5cae0ec6 1745
70743ff1
BG
1746 if (! NILP (fallback))
1747 return fallback;
948fe32d
CW
1748 /* Check for alternative properties */
1749 tail = Fassq (prop, Vchar_property_alias_alist);
931285e2
LT
1750 if (! NILP (tail))
1751 {
1752 tail = XCDR (tail);
1753 for (; NILP (fallback) && CONSP (tail); tail = XCDR (tail))
1754 fallback = Fplist_get (plist, XCAR (tail));
1755 }
1756
948fe32d
CW
1757 if (textprop && NILP (fallback) && CONSP (Vdefault_text_properties))
1758 fallback = Fplist_get (Vdefault_text_properties, prop);
1759 return fallback;
323a7ad4 1760}
7ce503fd 1761
294efdbe 1762\f
ef1900f3
RS
1763/* Set point in BUFFER "temporarily" to CHARPOS, which corresponds to
1764 byte position BYTEPOS. */
1765
09db192c 1766void
6ba7f443 1767temp_set_point_both (struct buffer *buffer,
d311d28c 1768 ptrdiff_t charpos, ptrdiff_t bytepos)
ef1900f3
RS
1769{
1770 /* In a single-byte buffer, the two positions must be equal. */
cce7fefc
DA
1771 if (BUF_ZV (buffer) == BUF_ZV_BYTE (buffer))
1772 eassert (charpos == bytepos);
ef1900f3 1773
cce7fefc
DA
1774 eassert (charpos <= bytepos);
1775 eassert (charpos <= BUF_ZV (buffer) || BUF_BEGV (buffer) <= charpos);
ef1900f3 1776
cffc6f3b 1777 SET_BUF_PT_BOTH (buffer, charpos, bytepos);
ef1900f3
RS
1778}
1779
f8ab8c1f
EZ
1780/* Set point "temporarily", without checking any text properties. */
1781
09db192c 1782void
d311d28c 1783temp_set_point (struct buffer *buffer, ptrdiff_t charpos)
f8ab8c1f
EZ
1784{
1785 temp_set_point_both (buffer, charpos,
1786 buf_charpos_to_bytepos (buffer, charpos));
1787}
1788
7d0393cf 1789/* Set point in BUFFER to CHARPOS. If the target position is
f65013b0 1790 before an intangible character, move to an ok place. */
a50699fd
JA
1791
1792void
d311d28c 1793set_point (ptrdiff_t charpos)
ef1900f3 1794{
6ba7f443 1795 set_point_both (charpos, buf_charpos_to_bytepos (current_buffer, charpos));
ef1900f3
RS
1796}
1797
f0dcf801
MB
1798/* If there's an invisible character at position POS + TEST_OFFS in the
1799 current buffer, and the invisible property has a `stickiness' such that
1800 inserting a character at position POS would inherit the property it,
1801 return POS + ADJ, otherwise return POS. If TEST_INTANG is non-zero,
53964682 1802 then intangibility is required as well as invisibility.
f0dcf801
MB
1803
1804 TEST_OFFS should be either 0 or -1, and ADJ should be either 1 or -1.
1805
1806 Note that `stickiness' is determined by overlay marker insertion types,
7d0393cf 1807 if the invisible property comes from an overlay. */
f0dcf801 1808
d311d28c
PE
1809static ptrdiff_t
1810adjust_for_invis_intang (ptrdiff_t pos, ptrdiff_t test_offs, ptrdiff_t adj,
e79123aa 1811 int test_intang)
f0dcf801
MB
1812{
1813 Lisp_Object invis_propval, invis_overlay;
1814 Lisp_Object test_pos;
1815
1816 if ((adj < 0 && pos + adj < BEGV) || (adj > 0 && pos + adj > ZV))
1817 /* POS + ADJ would be beyond the buffer bounds, so do no adjustment. */
1818 return pos;
1819
1820 test_pos = make_number (pos + test_offs);
1821
1822 invis_propval
1823 = get_char_property_and_overlay (test_pos, Qinvisible, Qnil,
1824 &invis_overlay);
1825
1826 if ((!test_intang
1827 || ! NILP (Fget_char_property (test_pos, Qintangible, Qnil)))
1828 && TEXT_PROP_MEANS_INVISIBLE (invis_propval)
1829 /* This next test is true if the invisible property has a stickiness
1830 such that an insertion at POS would inherit it. */
1831 && (NILP (invis_overlay)
1832 /* Invisible property is from a text-property. */
4867a283 1833 ? (text_property_stickiness (Qinvisible, make_number (pos), Qnil)
f0dcf801
MB
1834 == (test_offs == 0 ? 1 : -1))
1835 /* Invisible property is from an overlay. */
1836 : (test_offs == 0
1837 ? XMARKER (OVERLAY_START (invis_overlay))->insertion_type == 0
1838 : XMARKER (OVERLAY_END (invis_overlay))->insertion_type == 1)))
1839 pos += adj;
1840
1841 return pos;
1842}
1843
ef1900f3 1844/* Set point in BUFFER to CHARPOS, which corresponds to byte
7d0393cf 1845 position BYTEPOS. If the target position is
ef1900f3
RS
1846 before an intangible character, move to an ok place. */
1847
1848void
d311d28c 1849set_point_both (ptrdiff_t charpos, ptrdiff_t bytepos)
a50699fd 1850{
e39adcda 1851 register INTERVAL to, from, toprev, fromprev;
d311d28c
PE
1852 ptrdiff_t buffer_point;
1853 ptrdiff_t old_position = PT;
594a1605
CY
1854 /* This ensures that we move forward past intangible text when the
1855 initial position is the same as the destination, in the rare
1856 instances where this is important, e.g. in line-move-finish
1857 (simple.el). */
ef1900f3 1858 int backwards = (charpos < old_position ? 1 : 0);
580fae94 1859 int have_overlays;
d311d28c 1860 ptrdiff_t original_position;
a50699fd 1861
4c31be61 1862 BSET (current_buffer, point_before_scroll, Qnil);
b6a0ebc3 1863
6ba7f443 1864 if (charpos == PT)
a50699fd
JA
1865 return;
1866
ef1900f3 1867 /* In a single-byte buffer, the two positions must be equal. */
6ba7f443 1868 eassert (ZV != ZV_BYTE || charpos == bytepos);
ef1900f3 1869
62056764
JB
1870 /* Check this now, before checking if the buffer has any intervals.
1871 That way, we can catch conditions which break this sanity check
1872 whether or not there are intervals in the buffer. */
6ba7f443 1873 eassert (charpos <= ZV && charpos >= BEGV);
62056764 1874
4cb3e6b3 1875 have_overlays = buffer_has_overlays ();
580fae94
RS
1876
1877 /* If we have no text properties and overlays,
1878 then we can do it quickly. */
8707c1e5 1879 if (!buffer_get_intervals (current_buffer) && ! have_overlays)
a50699fd 1880 {
6ba7f443 1881 temp_set_point_both (current_buffer, charpos, bytepos);
a50699fd
JA
1882 return;
1883 }
1884
ef1900f3
RS
1885 /* Set TO to the interval containing the char after CHARPOS,
1886 and TOPREV to the interval containing the char before CHARPOS.
323a7ad4 1887 Either one may be null. They may be equal. */
8707c1e5 1888 to = find_interval (buffer_get_intervals (current_buffer), charpos);
6ba7f443 1889 if (charpos == BEGV)
294efdbe 1890 toprev = 0;
ef1900f3 1891 else if (to && to->position == charpos)
323a7ad4 1892 toprev = previous_interval (to);
323a7ad4
RS
1893 else
1894 toprev = to;
1895
6ba7f443 1896 buffer_point = (PT == ZV ? ZV - 1 : PT);
9c79dd1b 1897
323a7ad4
RS
1898 /* Set FROM to the interval containing the char after PT,
1899 and FROMPREV to the interval containing the char before PT.
1900 Either one may be null. They may be equal. */
7ce503fd 1901 /* We could cache this and save time. */
8707c1e5 1902 from = find_interval (buffer_get_intervals (current_buffer), buffer_point);
6ba7f443 1903 if (buffer_point == BEGV)
294efdbe 1904 fromprev = 0;
6ba7f443 1905 else if (from && from->position == PT)
323a7ad4 1906 fromprev = previous_interval (from);
6ba7f443 1907 else if (buffer_point != PT)
323a7ad4
RS
1908 fromprev = from, from = 0;
1909 else
1910 fromprev = from;
a50699fd 1911
7ce503fd 1912 /* Moving within an interval. */
580fae94
RS
1913 if (to == from && toprev == fromprev && INTERVAL_VISIBLE_P (to)
1914 && ! have_overlays)
a50699fd 1915 {
6ba7f443 1916 temp_set_point_both (current_buffer, charpos, bytepos);
a50699fd
JA
1917 return;
1918 }
1919
ef1900f3 1920 original_position = charpos;
580fae94 1921
5eabb4e7
RS
1922 /* If the new position is between two intangible characters
1923 with the same intangible property value,
1924 move forward or backward until a change in that property. */
580fae94 1925 if (NILP (Vinhibit_point_motion_hooks)
77c7bcb1 1926 && ((to && toprev)
b827a9e3
RS
1927 || have_overlays)
1928 /* Intangibility never stops us from positioning at the beginning
1929 or end of the buffer, so don't bother checking in that case. */
ef1900f3 1930 && charpos != BEGV && charpos != ZV)
a50699fd 1931 {
580fae94 1932 Lisp_Object pos;
f0dcf801 1933 Lisp_Object intangible_propval;
580fae94 1934
d5219de5
RS
1935 if (backwards)
1936 {
0270b877 1937 /* If the preceding character is both intangible and invisible,
f0dcf801
MB
1938 and the invisible property is `rear-sticky', perturb it so
1939 that the search starts one character earlier -- this ensures
1940 that point can never move to the end of an invisible/
1941 intangible/rear-sticky region. */
1942 charpos = adjust_for_invis_intang (charpos, -1, -1, 1);
1d14d232 1943
f0dcf801 1944 XSETINT (pos, charpos);
5eabb4e7
RS
1945
1946 /* If following char is intangible,
1947 skip back over all chars with matching intangible property. */
1d14d232
RS
1948
1949 intangible_propval = Fget_char_property (pos, Qintangible, Qnil);
1950
5eabb4e7 1951 if (! NILP (intangible_propval))
1d14d232 1952 {
6ba7f443 1953 while (XINT (pos) > BEGV
1d14d232
RS
1954 && EQ (Fget_char_property (make_number (XINT (pos) - 1),
1955 Qintangible, Qnil),
1956 intangible_propval))
1957 pos = Fprevious_char_property_change (pos, Qnil);
f0dcf801
MB
1958
1959 /* Set CHARPOS from POS, and if the final intangible character
1960 that we skipped over is also invisible, and the invisible
1961 property is `front-sticky', perturb it to be one character
1962 earlier -- this ensures that point can never move to the
1963 beginning of an invisible/intangible/front-sticky region. */
1964 charpos = adjust_for_invis_intang (XINT (pos), 0, -1, 0);
1d14d232 1965 }
d5219de5 1966 }
0df8950e 1967 else
d5219de5 1968 {
f0dcf801
MB
1969 /* If the following character is both intangible and invisible,
1970 and the invisible property is `front-sticky', perturb it so
1971 that the search starts one character later -- this ensures
1972 that point can never move to the beginning of an
1973 invisible/intangible/front-sticky region. */
1974 charpos = adjust_for_invis_intang (charpos, 0, 1, 1);
1975
1976 XSETINT (pos, charpos);
1977
1d14d232
RS
1978 /* If preceding char is intangible,
1979 skip forward over all chars with matching intangible property. */
1980
ef1900f3 1981 intangible_propval = Fget_char_property (make_number (charpos - 1),
580fae94 1982 Qintangible, Qnil);
5eabb4e7 1983
5eabb4e7 1984 if (! NILP (intangible_propval))
1d14d232 1985 {
6ba7f443 1986 while (XINT (pos) < ZV
1d14d232
RS
1987 && EQ (Fget_char_property (pos, Qintangible, Qnil),
1988 intangible_propval))
1989 pos = Fnext_char_property_change (pos, Qnil);
580fae94 1990
f0dcf801
MB
1991 /* Set CHARPOS from POS, and if the final intangible character
1992 that we skipped over is also invisible, and the invisible
1993 property is `rear-sticky', perturb it to be one character
1994 later -- this ensures that point can never move to the
1995 end of an invisible/intangible/rear-sticky region. */
1996 charpos = adjust_for_invis_intang (XINT (pos), -1, 1, 0);
1d14d232 1997 }
d5219de5 1998 }
580fae94 1999
6ba7f443 2000 bytepos = buf_charpos_to_bytepos (current_buffer, charpos);
580fae94
RS
2001 }
2002
ef1900f3 2003 if (charpos != original_position)
580fae94 2004 {
ef1900f3
RS
2005 /* Set TO to the interval containing the char after CHARPOS,
2006 and TOPREV to the interval containing the char before CHARPOS.
580fae94 2007 Either one may be null. They may be equal. */
8707c1e5 2008 to = find_interval (buffer_get_intervals (current_buffer), charpos);
6ba7f443 2009 if (charpos == BEGV)
580fae94 2010 toprev = 0;
ef1900f3 2011 else if (to && to->position == charpos)
580fae94
RS
2012 toprev = previous_interval (to);
2013 else
2014 toprev = to;
a50699fd 2015 }
323a7ad4 2016
5eabb4e7
RS
2017 /* Here TO is the interval after the stopping point
2018 and TOPREV is the interval before the stopping point.
2019 One or the other may be null. */
2020
6ba7f443 2021 temp_set_point_both (current_buffer, charpos, bytepos);
a50699fd 2022
e0f24100 2023 /* We run point-left and point-entered hooks here, if the
d7e3e52b 2024 two intervals are not equivalent. These hooks take
323a7ad4 2025 (old_point, new_point) as arguments. */
ddd931ff
RS
2026 if (NILP (Vinhibit_point_motion_hooks)
2027 && (! intervals_equal (from, to)
2028 || ! intervals_equal (fromprev, toprev)))
9c79dd1b 2029 {
323a7ad4
RS
2030 Lisp_Object leave_after, leave_before, enter_after, enter_before;
2031
2032 if (fromprev)
4e8f005c 2033 leave_before = textget (fromprev->plist, Qpoint_left);
323a7ad4 2034 else
4e8f005c
CY
2035 leave_before = Qnil;
2036
323a7ad4 2037 if (from)
4e8f005c 2038 leave_after = textget (from->plist, Qpoint_left);
323a7ad4 2039 else
4e8f005c 2040 leave_after = Qnil;
323a7ad4
RS
2041
2042 if (toprev)
4e8f005c 2043 enter_before = textget (toprev->plist, Qpoint_entered);
323a7ad4 2044 else
4e8f005c
CY
2045 enter_before = Qnil;
2046
323a7ad4 2047 if (to)
4e8f005c 2048 enter_after = textget (to->plist, Qpoint_entered);
323a7ad4 2049 else
4e8f005c 2050 enter_after = Qnil;
9c79dd1b 2051
323a7ad4 2052 if (! EQ (leave_before, enter_before) && !NILP (leave_before))
4e8f005c
CY
2053 call2 (leave_before, make_number (old_position),
2054 make_number (charpos));
323a7ad4 2055 if (! EQ (leave_after, enter_after) && !NILP (leave_after))
4e8f005c
CY
2056 call2 (leave_after, make_number (old_position),
2057 make_number (charpos));
9c79dd1b 2058
323a7ad4 2059 if (! EQ (enter_before, leave_before) && !NILP (enter_before))
4e8f005c
CY
2060 call2 (enter_before, make_number (old_position),
2061 make_number (charpos));
323a7ad4 2062 if (! EQ (enter_after, leave_after) && !NILP (enter_after))
4e8f005c
CY
2063 call2 (enter_after, make_number (old_position),
2064 make_number (charpos));
9c79dd1b 2065 }
a50699fd 2066}
294efdbe 2067\f
a7fa233f
RS
2068/* Move point to POSITION, unless POSITION is inside an intangible
2069 segment that reaches all the way to point. */
2070
2071void
d311d28c 2072move_if_not_intangible (ptrdiff_t position)
a7fa233f
RS
2073{
2074 Lisp_Object pos;
2075 Lisp_Object intangible_propval;
2076
2077 XSETINT (pos, position);
2078
2079 if (! NILP (Vinhibit_point_motion_hooks))
2080 /* If intangible is inhibited, always move point to POSITION. */
2081 ;
2e34157c 2082 else if (PT < position && XINT (pos) < ZV)
a7fa233f
RS
2083 {
2084 /* We want to move forward, so check the text before POSITION. */
2085
2086 intangible_propval = Fget_char_property (pos,
2087 Qintangible, Qnil);
2088
2089 /* If following char is intangible,
2090 skip back over all chars with matching intangible property. */
2091 if (! NILP (intangible_propval))
2092 while (XINT (pos) > BEGV
2093 && EQ (Fget_char_property (make_number (XINT (pos) - 1),
2094 Qintangible, Qnil),
2095 intangible_propval))
2096 pos = Fprevious_char_property_change (pos, Qnil);
2097 }
2e34157c 2098 else if (XINT (pos) > BEGV)
a7fa233f
RS
2099 {
2100 /* We want to move backward, so check the text after POSITION. */
2101
2102 intangible_propval = Fget_char_property (make_number (XINT (pos) - 1),
2103 Qintangible, Qnil);
2104
2105 /* If following char is intangible,
887f2a2d 2106 skip forward over all chars with matching intangible property. */
a7fa233f
RS
2107 if (! NILP (intangible_propval))
2108 while (XINT (pos) < ZV
2109 && EQ (Fget_char_property (pos, Qintangible, Qnil),
2110 intangible_propval))
2111 pos = Fnext_char_property_change (pos, Qnil);
2112
2113 }
97d8f112
RS
2114 else if (position < BEGV)
2115 position = BEGV;
2116 else if (position > ZV)
2117 position = ZV;
a7fa233f 2118
7d0393cf 2119 /* If the whole stretch between PT and POSITION isn't intangible,
a7fa233f
RS
2120 try moving to POSITION (which means we actually move farther
2121 if POSITION is inside of intangible text). */
2122
2123 if (XINT (pos) != PT)
2124 SET_PT (position);
2125}
2126\f
f56b42ac
KH
2127/* If text at position POS has property PROP, set *VAL to the property
2128 value, *START and *END to the beginning and end of a region that
2129 has the same property, and return 1. Otherwise return 0.
2130
2131 OBJECT is the string or buffer to look for the property in;
2132 nil means the current buffer. */
2133
2134int
d311d28c
PE
2135get_property_and_range (ptrdiff_t pos, Lisp_Object prop, Lisp_Object *val,
2136 ptrdiff_t *start, ptrdiff_t *end, Lisp_Object object)
f56b42ac
KH
2137{
2138 INTERVAL i, prev, next;
2139
2140 if (NILP (object))
8707c1e5 2141 i = find_interval (buffer_get_intervals (current_buffer), pos);
f56b42ac 2142 else if (BUFFERP (object))
8707c1e5 2143 i = find_interval (buffer_get_intervals (XBUFFER (object)), pos);
f56b42ac 2144 else if (STRINGP (object))
ad8c997f 2145 i = find_interval (string_get_intervals (object), pos);
f56b42ac
KH
2146 else
2147 abort ();
2148
77c7bcb1 2149 if (!i || (i->position + LENGTH (i) <= pos))
f56b42ac
KH
2150 return 0;
2151 *val = textget (i->plist, prop);
2152 if (NILP (*val))
2153 return 0;
2154
2155 next = i; /* remember it in advance */
2156 prev = previous_interval (i);
77c7bcb1 2157 while (prev
f56b42ac
KH
2158 && EQ (*val, textget (prev->plist, prop)))
2159 i = prev, prev = previous_interval (prev);
2160 *start = i->position;
2161
2162 next = next_interval (i);
77c7bcb1 2163 while (next && EQ (*val, textget (next->plist, prop)))
f56b42ac
KH
2164 i = next, next = next_interval (next);
2165 *end = i->position + LENGTH (i);
2166
2167 return 1;
2168}
2169\f
2b4b027f
GM
2170/* Return the proper local keymap TYPE for position POSITION in
2171 BUFFER; TYPE should be one of `keymap' or `local-map'. Use the map
2172 specified by the PROP property, if any. Otherwise, if TYPE is
1b0440ed
RS
2173 `local-map' use BUFFER's local map.
2174
2175 POSITION must be in the accessible part of BUFFER. */
5cae0ec6
RS
2176
2177Lisp_Object
d311d28c 2178get_local_map (register ptrdiff_t position, register struct buffer *buffer,
e79123aa 2179 Lisp_Object type)
5cae0ec6 2180{
f94ecad1 2181 Lisp_Object prop, lispy_position, lispy_buffer;
d311d28c 2182 ptrdiff_t old_begv, old_zv, old_begv_byte, old_zv_byte;
5cae0ec6 2183
7ce503fd 2184 /* Perhaps we should just change `position' to the limit. */
1b0440ed 2185 if (position > BUF_ZV (buffer) || position < BUF_BEGV (buffer))
5cae0ec6
RS
2186 abort ();
2187
0f7a5fda
KH
2188 /* Ignore narrowing, so that a local map continues to be valid even if
2189 the visible region contains no characters and hence no properties. */
2190 old_begv = BUF_BEGV (buffer);
2191 old_zv = BUF_ZV (buffer);
ef1900f3
RS
2192 old_begv_byte = BUF_BEGV_BYTE (buffer);
2193 old_zv_byte = BUF_ZV_BYTE (buffer);
cffc6f3b
CY
2194
2195 SET_BUF_BEGV_BOTH (buffer, BUF_BEG (buffer), BUF_BEG_BYTE (buffer));
2196 SET_BUF_ZV_BOTH (buffer, BUF_Z (buffer), BUF_Z_BYTE (buffer));
0f7a5fda 2197
0f7a5fda
KH
2198 XSETFASTINT (lispy_position, position);
2199 XSETBUFFER (lispy_buffer, buffer);
4867a283
SM
2200 /* First check if the CHAR has any property. This is because when
2201 we click with the mouse, the mouse pointer is really pointing
2202 to the CHAR after POS. */
2b4b027f 2203 prop = Fget_char_property (lispy_position, type, lispy_buffer);
4867a283
SM
2204 /* If not, look at the POS's properties. This is necessary because when
2205 editing a field with a `local-map' property, we want insertion at the end
2206 to obey the `local-map' property. */
2207 if (NILP (prop))
2208 prop = get_pos_property (lispy_position, type, lispy_buffer);
0f7a5fda 2209
cffc6f3b
CY
2210 SET_BUF_BEGV_BOTH (buffer, old_begv, old_begv_byte);
2211 SET_BUF_ZV_BOTH (buffer, old_zv, old_zv_byte);
5cae0ec6
RS
2212
2213 /* Use the local map only if it is valid. */
02067692
SM
2214 prop = get_keymap (prop, 0, 0);
2215 if (CONSP (prop))
5cae0ec6
RS
2216 return prop;
2217
2b4b027f 2218 if (EQ (type, Qkeymap))
6a7dccef
DL
2219 return Qnil;
2220 else
4b4deea2 2221 return BVAR (buffer, keymap);
5cae0ec6
RS
2222}
2223\f
9c79dd1b 2224/* Produce an interval tree reflecting the intervals in
944d4e4b
KH
2225 TREE from START to START + LENGTH.
2226 The new interval tree has no parent and has a starting-position of 0. */
a50699fd 2227
7b1d5b85 2228INTERVAL
d311d28c 2229copy_intervals (INTERVAL tree, ptrdiff_t start, ptrdiff_t length)
a50699fd
JA
2230{
2231 register INTERVAL i, new, t;
d311d28c 2232 register ptrdiff_t got, prevlen;
a50699fd 2233
77c7bcb1
DA
2234 if (!tree || length <= 0)
2235 return NULL;
a50699fd
JA
2236
2237 i = find_interval (tree, start);
77c7bcb1 2238 eassert (i && LENGTH (i) > 0);
a50699fd 2239
7ce503fd 2240 /* If there is only one interval and it's the default, return nil. */
a50699fd
JA
2241 if ((start - i->position + 1 + length) < LENGTH (i)
2242 && DEFAULT_INTERVAL_P (i))
77c7bcb1 2243 return NULL;
a50699fd
JA
2244
2245 new = make_interval ();
944d4e4b 2246 new->position = 0;
a50699fd 2247 got = (LENGTH (i) - (start - i->position));
9c79dd1b 2248 new->total_length = length;
9c08a8d4 2249 eassert (0 <= TOTAL_LENGTH (new));
a50699fd
JA
2250 copy_properties (i, new);
2251
2252 t = new;
95e3e1ef 2253 prevlen = got;
a50699fd
JA
2254 while (got < length)
2255 {
2256 i = next_interval (i);
2bc7a79b 2257 t = split_interval_right (t, prevlen);
a50699fd 2258 copy_properties (i, t);
95e3e1ef
RS
2259 prevlen = LENGTH (i);
2260 got += prevlen;
a50699fd
JA
2261 }
2262
4314dea4 2263 return balance_an_interval (new);
a50699fd
JA
2264}
2265
7ce503fd 2266/* Give STRING the properties of BUFFER from POSITION to LENGTH. */
a50699fd 2267
09db192c 2268void
e79123aa 2269copy_intervals_to_string (Lisp_Object string, struct buffer *buffer,
d311d28c 2270 ptrdiff_t position, ptrdiff_t length)
a50699fd 2271{
8707c1e5 2272 INTERVAL interval_copy = copy_intervals (buffer_get_intervals (buffer),
a50699fd 2273 position, length);
77c7bcb1 2274 if (!interval_copy)
a50699fd
JA
2275 return;
2276
6a3d20cc 2277 interval_set_object (interval_copy, string);
ad8c997f 2278 string_set_intervals (string, interval_copy);
a50699fd 2279}
d8638d30 2280\f
944d4e4b 2281/* Return 1 if strings S1 and S2 have identical properties; 0 otherwise.
d8638d30
RS
2282 Assume they have identical characters. */
2283
2284int
971de7fb 2285compare_string_intervals (Lisp_Object s1, Lisp_Object s2)
d8638d30
RS
2286{
2287 INTERVAL i1, i2;
d311d28c
PE
2288 ptrdiff_t pos = 0;
2289 ptrdiff_t end = SCHARS (s1);
d8638d30 2290
ad8c997f
DA
2291 i1 = find_interval (string_get_intervals (s1), 0);
2292 i2 = find_interval (string_get_intervals (s2), 0);
d8638d30
RS
2293
2294 while (pos < end)
2295 {
2296 /* Determine how far we can go before we reach the end of I1 or I2. */
d311d28c
PE
2297 ptrdiff_t len1 = (i1 != 0 ? INTERVAL_LAST_POS (i1) : end) - pos;
2298 ptrdiff_t len2 = (i2 != 0 ? INTERVAL_LAST_POS (i2) : end) - pos;
2299 ptrdiff_t distance = min (len1, len2);
d8638d30
RS
2300
2301 /* If we ever find a mismatch between the strings,
2302 they differ. */
2303 if (! intervals_equal (i1, i2))
2304 return 0;
2305
2306 /* Advance POS till the end of the shorter interval,
2307 and advance one or both interval pointers for the new position. */
2308 pos += distance;
2309 if (len1 == distance)
2310 i1 = next_interval (i1);
2311 if (len2 == distance)
2312 i2 = next_interval (i2);
2313 }
2314 return 1;
2315}
37f26f3c 2316\f
37f26f3c
RS
2317/* Recursively adjust interval I in the current buffer
2318 for setting enable_multibyte_characters to MULTI_FLAG.
2319 The range of interval I is START ... END in characters,
2320 START_BYTE ... END_BYTE in bytes. */
2321
2322static void
e79123aa 2323set_intervals_multibyte_1 (INTERVAL i, int multi_flag,
d311d28c
PE
2324 ptrdiff_t start, ptrdiff_t start_byte,
2325 ptrdiff_t end, ptrdiff_t end_byte)
37f26f3c 2326{
37f26f3c
RS
2327 /* Fix the length of this interval. */
2328 if (multi_flag)
2329 i->total_length = end - start;
2330 else
2331 i->total_length = end_byte - start_byte;
9c08a8d4 2332 eassert (0 <= TOTAL_LENGTH (i));
727fec2d
RS
2333
2334 if (TOTAL_LENGTH (i) == 0)
2335 {
2336 delete_interval (i);
2337 return;
2338 }
37f26f3c
RS
2339
2340 /* Recursively fix the length of the subintervals. */
2341 if (i->left)
2342 {
d311d28c 2343 ptrdiff_t left_end, left_end_byte;
37f26f3c
RS
2344
2345 if (multi_flag)
2346 {
d311d28c 2347 ptrdiff_t temp;
37f26f3c
RS
2348 left_end_byte = start_byte + LEFT_TOTAL_LENGTH (i);
2349 left_end = BYTE_TO_CHAR (left_end_byte);
727fec2d
RS
2350
2351 temp = CHAR_TO_BYTE (left_end);
2352
2353 /* If LEFT_END_BYTE is in the middle of a character,
f813361d 2354 adjust it and LEFT_END to a char boundary. */
727fec2d
RS
2355 if (left_end_byte > temp)
2356 {
2357 left_end_byte = temp;
2358 }
2359 if (left_end_byte < temp)
2360 {
2361 left_end--;
2362 left_end_byte = CHAR_TO_BYTE (left_end);
2363 }
37f26f3c
RS
2364 }
2365 else
2366 {
2367 left_end = start + LEFT_TOTAL_LENGTH (i);
2368 left_end_byte = CHAR_TO_BYTE (left_end);
2369 }
2370
2371 set_intervals_multibyte_1 (i->left, multi_flag, start, start_byte,
2372 left_end, left_end_byte);
2373 }
2374 if (i->right)
2375 {
d311d28c 2376 ptrdiff_t right_start_byte, right_start;
37f26f3c
RS
2377
2378 if (multi_flag)
2379 {
d311d28c 2380 ptrdiff_t temp;
727fec2d 2381
37f26f3c
RS
2382 right_start_byte = end_byte - RIGHT_TOTAL_LENGTH (i);
2383 right_start = BYTE_TO_CHAR (right_start_byte);
727fec2d
RS
2384
2385 /* If RIGHT_START_BYTE is in the middle of a character,
f813361d 2386 adjust it and RIGHT_START to a char boundary. */
727fec2d
RS
2387 temp = CHAR_TO_BYTE (right_start);
2388
2389 if (right_start_byte < temp)
2390 {
2391 right_start_byte = temp;
2392 }
2393 if (right_start_byte > temp)
2394 {
2395 right_start++;
2396 right_start_byte = CHAR_TO_BYTE (right_start);
2397 }
37f26f3c
RS
2398 }
2399 else
2400 {
2401 right_start = end - RIGHT_TOTAL_LENGTH (i);
2402 right_start_byte = CHAR_TO_BYTE (right_start);
2403 }
2404
2405 set_intervals_multibyte_1 (i->right, multi_flag,
2406 right_start, right_start_byte,
2407 end, end_byte);
2408 }
727fec2d
RS
2409
2410 /* Rounding to char boundaries can theoretically ake this interval
2411 spurious. If so, delete one child, and copy its property list
2412 to this interval. */
2413 if (LEFT_TOTAL_LENGTH (i) + RIGHT_TOTAL_LENGTH (i) >= TOTAL_LENGTH (i))
2414 {
2415 if ((i)->left)
2416 {
6a3d20cc 2417 interval_set_plist (i, i->left->plist);
727fec2d
RS
2418 (i)->left->total_length = 0;
2419 delete_interval ((i)->left);
2420 }
2421 else
2422 {
6a3d20cc 2423 interval_set_plist (i, i->right->plist);
727fec2d
RS
2424 (i)->right->total_length = 0;
2425 delete_interval ((i)->right);
2426 }
2427 }
37f26f3c 2428}
d2f7a802 2429
24cef261
RS
2430/* Update the intervals of the current buffer
2431 to fit the contents as multibyte (if MULTI_FLAG is 1)
2432 or to fit them as non-multibyte (if MULTI_FLAG is 0). */
2433
2434void
971de7fb 2435set_intervals_multibyte (int multi_flag)
24cef261 2436{
8707c1e5
DA
2437 INTERVAL i = buffer_get_intervals (current_buffer);
2438
2439 if (i)
2440 set_intervals_multibyte_1 (i, multi_flag, BEG, BEG_BYTE, Z, Z_BYTE);
24cef261 2441}