Add code from mcheck.c of glibc-1.09.1.
[bpt/emacs.git] / src / textprop.c
CommitLineData
d418ef42 1/* Interface code for dealing with text properties.
ecfb39ee 2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000 Free Software Foundation, Inc.
d418ef42
JA
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
71dfa9f4 8the Free Software Foundation; either version 2, or (at your option)
d418ef42
JA
9any later version.
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
17along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
d418ef42 20
18160b98 21#include <config.h>
d418ef42
JA
22#include "lisp.h"
23#include "intervals.h"
24#include "buffer.h"
f5957179 25#include "window.h"
59a486ab
RS
26
27#ifndef NULL
28#define NULL (void *)0
29#endif
318d2fa8
RS
30
31/* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
33
34#define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
d418ef42
JA
35\f
36
37/* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
40
9c79dd1b
JA
41 set_properties needs to deal with the interval property cache.
42
d418ef42 43 It is assumed that for any interval plist, a property appears
d4b530ad 44 only once on the list. Although some code i.e., remove_properties,
d418ef42 45 handles the more general case, the uniqueness of properties is
eb8c3be9 46 necessary for the system to remain consistent. This requirement
cdf3e5a2 47 is enforced by the subrs installing properties onto the intervals. */
d418ef42
JA
48
49\f
cdf3e5a2 50/* Types of hooks. */
d418ef42
JA
51Lisp_Object Qmouse_left;
52Lisp_Object Qmouse_entered;
53Lisp_Object Qpoint_left;
54Lisp_Object Qpoint_entered;
dc70cea7
RS
55Lisp_Object Qcategory;
56Lisp_Object Qlocal_map;
d418ef42 57
cdf3e5a2 58/* Visual properties text (including strings) may have. */
d418ef42 59Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
69bb837e 60Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
19e1c426
RS
61
62/* Sticky properties */
63Lisp_Object Qfront_sticky, Qrear_nonsticky;
d7b4e137
JB
64
65/* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
66 the o1's cdr. Otherwise, return zero. This is handy for
67 traversing plists. */
70949dac 68#define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
d7b4e137 69
688a5a0f 70Lisp_Object Vinhibit_point_motion_hooks;
ad1b2f20 71Lisp_Object Vdefault_text_properties;
abc2f676 72Lisp_Object Vtext_property_default_nonsticky;
688a5a0f 73
318d2fa8
RS
74/* verify_interval_modification saves insertion hooks here
75 to be run later by report_interval_modification. */
76Lisp_Object interval_insert_behind_hooks;
77Lisp_Object interval_insert_in_front_hooks;
d418ef42 78\f
ac876a79
JA
79/* Extract the interval at the position pointed to by BEGIN from
80 OBJECT, a string or buffer. Additionally, check that the positions
81 pointed to by BEGIN and END are within the bounds of OBJECT, and
82 reverse them if *BEGIN is greater than *END. The objects pointed
83 to by BEGIN and END may be integers or markers; if the latter, they
84 are coerced to integers.
d418ef42 85
d4b530ad
RS
86 When OBJECT is a string, we increment *BEGIN and *END
87 to make them origin-one.
88
d418ef42
JA
89 Note that buffer points don't correspond to interval indices.
90 For example, point-max is 1 greater than the index of the last
91 character. This difference is handled in the caller, which uses
92 the validated points to determine a length, and operates on that.
93 Exceptions are Ftext_properties_at, Fnext_property_change, and
94 Fprevious_property_change which call this function with BEGIN == END.
95 Handle this case specially.
96
97 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
ac876a79
JA
98 create an interval tree for OBJECT if one doesn't exist, provided
99 the object actually contains text. In the current design, if there
d4b530ad 100 is no text, there can be no text properties. */
d418ef42
JA
101
102#define soft 0
103#define hard 1
104
9dd7eec6 105INTERVAL
d418ef42
JA
106validate_interval_range (object, begin, end, force)
107 Lisp_Object object, *begin, *end;
108 int force;
109{
110 register INTERVAL i;
d4b530ad
RS
111 int searchpos;
112
d418ef42
JA
113 CHECK_STRING_OR_BUFFER (object, 0);
114 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
115 CHECK_NUMBER_COERCE_MARKER (*end, 0);
116
117 /* If we are asked for a point, but from a subr which operates
cdf3e5a2 118 on a range, then return nothing. */
64a49ca7 119 if (EQ (*begin, *end) && begin != end)
d418ef42
JA
120 return NULL_INTERVAL;
121
122 if (XINT (*begin) > XINT (*end))
123 {
d4b530ad
RS
124 Lisp_Object n;
125 n = *begin;
d418ef42 126 *begin = *end;
d4b530ad 127 *end = n;
d418ef42
JA
128 }
129
5d2fa46f 130 if (BUFFERP (object))
d418ef42
JA
131 {
132 register struct buffer *b = XBUFFER (object);
133
d418ef42
JA
134 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
135 && XINT (*end) <= BUF_ZV (b)))
136 args_out_of_range (*begin, *end);
866bf246 137 i = BUF_INTERVALS (b);
d418ef42 138
cdf3e5a2 139 /* If there's no text, there are no properties. */
d4b530ad
RS
140 if (BUF_BEGV (b) == BUF_ZV (b))
141 return NULL_INTERVAL;
142
143 searchpos = XINT (*begin);
d418ef42
JA
144 }
145 else
146 {
147 register struct Lisp_String *s = XSTRING (object);
148
d4b530ad 149 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
d418ef42
JA
150 && XINT (*end) <= s->size))
151 args_out_of_range (*begin, *end);
ad077db0 152 XSETFASTINT (*begin, XFASTINT (*begin));
b1e94638 153 if (begin != end)
ad077db0 154 XSETFASTINT (*end, XFASTINT (*end));
d418ef42 155 i = s->intervals;
d4b530ad
RS
156
157 if (s->size == 0)
158 return NULL_INTERVAL;
159
160 searchpos = XINT (*begin);
d418ef42
JA
161 }
162
163 if (NULL_INTERVAL_P (i))
164 return (force ? create_root_interval (object) : i);
165
d4b530ad 166 return find_interval (i, searchpos);
d418ef42
JA
167}
168
169/* Validate LIST as a property list. If LIST is not a list, then
170 make one consisting of (LIST nil). Otherwise, verify that LIST
cdf3e5a2 171 is even numbered and thus suitable as a plist. */
d418ef42
JA
172
173static Lisp_Object
174validate_plist (list)
4d780c76 175 Lisp_Object list;
d418ef42
JA
176{
177 if (NILP (list))
178 return Qnil;
179
180 if (CONSP (list))
181 {
182 register int i;
183 register Lisp_Object tail;
184 for (i = 0, tail = list; !NILP (tail); i++)
b1e94638
JB
185 {
186 tail = Fcdr (tail);
187 QUIT;
188 }
d418ef42
JA
189 if (i & 1)
190 error ("Odd length text property list");
191 return list;
192 }
193
194 return Fcons (list, Fcons (Qnil, Qnil));
195}
196
d418ef42 197/* Return nonzero if interval I has all the properties,
cdf3e5a2 198 with the same values, of list PLIST. */
d418ef42
JA
199
200static int
201interval_has_all_properties (plist, i)
202 Lisp_Object plist;
203 INTERVAL i;
204{
695f302f 205 register Lisp_Object tail1, tail2, sym1;
d418ef42
JA
206 register int found;
207
cdf3e5a2 208 /* Go through each element of PLIST. */
d418ef42
JA
209 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
210 {
211 sym1 = Fcar (tail1);
212 found = 0;
213
214 /* Go through I's plist, looking for sym1 */
215 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
216 if (EQ (sym1, Fcar (tail2)))
217 {
218 /* Found the same property on both lists. If the
cdf3e5a2 219 values are unequal, return zero. */
734c51b2 220 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
d418ef42
JA
221 return 0;
222
cdf3e5a2 223 /* Property has same value on both lists; go to next one. */
d418ef42
JA
224 found = 1;
225 break;
226 }
227
228 if (! found)
229 return 0;
230 }
231
232 return 1;
233}
234
235/* Return nonzero if the plist of interval I has any of the
cdf3e5a2 236 properties of PLIST, regardless of their values. */
d418ef42
JA
237
238static INLINE int
239interval_has_some_properties (plist, i)
240 Lisp_Object plist;
241 INTERVAL i;
242{
243 register Lisp_Object tail1, tail2, sym;
244
cdf3e5a2 245 /* Go through each element of PLIST. */
d418ef42
JA
246 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
247 {
248 sym = Fcar (tail1);
249
250 /* Go through i's plist, looking for tail1 */
251 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
252 if (EQ (sym, Fcar (tail2)))
253 return 1;
254 }
255
256 return 0;
257}
d4b530ad 258\f
d7b4e137
JB
259/* Changing the plists of individual intervals. */
260
261/* Return the value of PROP in property-list PLIST, or Qunbound if it
262 has none. */
64a49ca7 263static Lisp_Object
d7b4e137 264property_value (plist, prop)
33ca3504 265 Lisp_Object plist, prop;
d7b4e137
JB
266{
267 Lisp_Object value;
268
269 while (PLIST_ELT_P (plist, value))
70949dac
KR
270 if (EQ (XCAR (plist), prop))
271 return XCAR (value);
d7b4e137 272 else
70949dac 273 plist = XCDR (value);
d7b4e137
JB
274
275 return Qunbound;
276}
277
d4b530ad
RS
278/* Set the properties of INTERVAL to PROPERTIES,
279 and record undo info for the previous values.
280 OBJECT is the string or buffer that INTERVAL belongs to. */
281
282static void
283set_properties (properties, interval, object)
284 Lisp_Object properties, object;
285 INTERVAL interval;
286{
d7b4e137 287 Lisp_Object sym, value;
d4b530ad 288
d7b4e137 289 if (BUFFERP (object))
d4b530ad 290 {
d7b4e137
JB
291 /* For each property in the old plist which is missing from PROPERTIES,
292 or has a different value in PROPERTIES, make an undo record. */
293 for (sym = interval->plist;
294 PLIST_ELT_P (sym, value);
70949dac
KR
295 sym = XCDR (value))
296 if (! EQ (property_value (properties, XCAR (sym)),
297 XCAR (value)))
f7a9275a 298 {
f7a9275a 299 record_property_change (interval->position, LENGTH (interval),
70949dac 300 XCAR (sym), XCAR (value),
f7a9275a
RS
301 object);
302 }
d7b4e137
JB
303
304 /* For each new property that has no value at all in the old plist,
305 make an undo record binding it to nil, so it will be removed. */
306 for (sym = properties;
307 PLIST_ELT_P (sym, value);
70949dac
KR
308 sym = XCDR (value))
309 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
f7a9275a 310 {
f7a9275a 311 record_property_change (interval->position, LENGTH (interval),
70949dac 312 XCAR (sym), Qnil,
f7a9275a
RS
313 object);
314 }
d4b530ad
RS
315 }
316
317 /* Store new properties. */
318 interval->plist = Fcopy_sequence (properties);
319}
d418ef42
JA
320
321/* Add the properties of PLIST to the interval I, or set
322 the value of I's property to the value of the property on PLIST
323 if they are different.
324
d4b530ad
RS
325 OBJECT should be the string or buffer the interval is in.
326
d418ef42
JA
327 Return nonzero if this changes I (i.e., if any members of PLIST
328 are actually added to I's plist) */
329
d4b530ad
RS
330static int
331add_properties (plist, i, object)
d418ef42
JA
332 Lisp_Object plist;
333 INTERVAL i;
d4b530ad 334 Lisp_Object object;
d418ef42 335{
c98da214 336 Lisp_Object tail1, tail2, sym1, val1;
d418ef42
JA
337 register int changed = 0;
338 register int found;
c98da214
RS
339 struct gcpro gcpro1, gcpro2, gcpro3;
340
341 tail1 = plist;
342 sym1 = Qnil;
343 val1 = Qnil;
344 /* No need to protect OBJECT, because we can GC only in the case
345 where it is a buffer, and live buffers are always protected.
346 I and its plist are also protected, via OBJECT. */
347 GCPRO3 (tail1, sym1, val1);
d418ef42 348
cdf3e5a2 349 /* Go through each element of PLIST. */
d418ef42
JA
350 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
351 {
352 sym1 = Fcar (tail1);
353 val1 = Fcar (Fcdr (tail1));
354 found = 0;
355
356 /* Go through I's plist, looking for sym1 */
357 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
358 if (EQ (sym1, Fcar (tail2)))
359 {
c98da214
RS
360 /* No need to gcpro, because tail2 protects this
361 and it must be a cons cell (we get an error otherwise). */
3814ccf5 362 register Lisp_Object this_cdr;
d418ef42 363
3814ccf5 364 this_cdr = Fcdr (tail2);
cdf3e5a2 365 /* Found the property. Now check its value. */
d418ef42
JA
366 found = 1;
367
368 /* The properties have the same value on both lists.
cdf3e5a2 369 Continue to the next property. */
734c51b2 370 if (EQ (val1, Fcar (this_cdr)))
d418ef42
JA
371 break;
372
d4b530ad 373 /* Record this change in the buffer, for undo purposes. */
5d2fa46f 374 if (BUFFERP (object))
d4b530ad 375 {
f7a9275a
RS
376 record_property_change (i->position, LENGTH (i),
377 sym1, Fcar (this_cdr), object);
d4b530ad
RS
378 }
379
d418ef42
JA
380 /* I's property has a different value -- change it */
381 Fsetcar (this_cdr, val1);
382 changed++;
383 break;
384 }
385
386 if (! found)
387 {
d4b530ad 388 /* Record this change in the buffer, for undo purposes. */
5d2fa46f 389 if (BUFFERP (object))
d4b530ad 390 {
f7a9275a
RS
391 record_property_change (i->position, LENGTH (i),
392 sym1, Qnil, object);
d4b530ad 393 }
d418ef42
JA
394 i->plist = Fcons (sym1, Fcons (val1, i->plist));
395 changed++;
396 }
397 }
398
c98da214
RS
399 UNGCPRO;
400
d418ef42
JA
401 return changed;
402}
403
404/* For any members of PLIST which are properties of I, remove them
d4b530ad
RS
405 from I's plist.
406 OBJECT is the string or buffer containing I. */
d418ef42 407
d4b530ad
RS
408static int
409remove_properties (plist, i, object)
d418ef42
JA
410 Lisp_Object plist;
411 INTERVAL i;
d4b530ad 412 Lisp_Object object;
d418ef42 413{
3814ccf5 414 register Lisp_Object tail1, tail2, sym, current_plist;
d418ef42
JA
415 register int changed = 0;
416
3814ccf5 417 current_plist = i->plist;
cdf3e5a2 418 /* Go through each element of plist. */
d418ef42
JA
419 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
420 {
421 sym = Fcar (tail1);
422
423 /* First, remove the symbol if its at the head of the list */
424 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
425 {
5d2fa46f 426 if (BUFFERP (object))
d4b530ad 427 {
f7a9275a
RS
428 record_property_change (i->position, LENGTH (i),
429 sym, Fcar (Fcdr (current_plist)),
430 object);
d4b530ad
RS
431 }
432
d418ef42
JA
433 current_plist = Fcdr (Fcdr (current_plist));
434 changed++;
435 }
436
437 /* Go through i's plist, looking for sym */
438 tail2 = current_plist;
439 while (! NILP (tail2))
440 {
3814ccf5
KH
441 register Lisp_Object this;
442 this = Fcdr (Fcdr (tail2));
d418ef42
JA
443 if (EQ (sym, Fcar (this)))
444 {
5d2fa46f 445 if (BUFFERP (object))
d4b530ad 446 {
f7a9275a
RS
447 record_property_change (i->position, LENGTH (i),
448 sym, Fcar (Fcdr (this)), object);
d4b530ad
RS
449 }
450
d418ef42
JA
451 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
452 changed++;
453 }
454 tail2 = this;
455 }
456 }
457
458 if (changed)
459 i->plist = current_plist;
460 return changed;
461}
462
d4b530ad 463#if 0
d418ef42 464/* Remove all properties from interval I. Return non-zero
cdf3e5a2 465 if this changes the interval. */
d418ef42
JA
466
467static INLINE int
468erase_properties (i)
469 INTERVAL i;
470{
471 if (NILP (i->plist))
472 return 0;
473
474 i->plist = Qnil;
475 return 1;
476}
d4b530ad 477#endif
d418ef42 478\f
ad077db0 479/* Returns the interval of POSITION in OBJECT.
cdf3e5a2
RS
480 POSITION is BEG-based. */
481
482INTERVAL
483interval_of (position, object)
484 int position;
485 Lisp_Object object;
486{
487 register INTERVAL i;
488 int beg, end;
489
490 if (NILP (object))
491 XSETBUFFER (object, current_buffer);
d0cb872a
KH
492 else if (EQ (object, Qt))
493 return NULL_INTERVAL;
cdf3e5a2
RS
494
495 CHECK_STRING_OR_BUFFER (object, 0);
496
497 if (BUFFERP (object))
498 {
499 register struct buffer *b = XBUFFER (object);
500
501 beg = BUF_BEGV (b);
502 end = BUF_ZV (b);
503 i = BUF_INTERVALS (b);
504 }
505 else
506 {
507 register struct Lisp_String *s = XSTRING (object);
508
ad077db0
KH
509 beg = 0;
510 end = s->size;
cdf3e5a2
RS
511 i = s->intervals;
512 }
513
514 if (!(beg <= position && position <= end))
c7ef8e24 515 args_out_of_range (make_number (position), make_number (position));
cdf3e5a2
RS
516 if (beg == end || NULL_INTERVAL_P (i))
517 return NULL_INTERVAL;
518
519 return find_interval (i, position);
520}
521\f
d418ef42
JA
522DEFUN ("text-properties-at", Ftext_properties_at,
523 Stext_properties_at, 1, 2, 0,
96f90544
RS
524 "Return the list of properties of the character at POSITION in OBJECT.\n\
525OBJECT is the string or buffer to look for the properties in;\n\
526nil means the current buffer.\n\
d4b530ad 527If POSITION is at the end of OBJECT, the value is nil.")
1f5e848a
EN
528 (position, object)
529 Lisp_Object position, object;
d418ef42
JA
530{
531 register INTERVAL i;
d418ef42
JA
532
533 if (NILP (object))
c8a4fc3d 534 XSETBUFFER (object, current_buffer);
d418ef42 535
1f5e848a 536 i = validate_interval_range (object, &position, &position, soft);
d418ef42
JA
537 if (NULL_INTERVAL_P (i))
538 return Qnil;
1f5e848a 539 /* If POSITION is at the end of the interval,
d4b530ad
RS
540 it means it's the end of OBJECT.
541 There are no properties at the very end,
542 since no character follows. */
1f5e848a 543 if (XINT (position) == LENGTH (i) + i->position)
d4b530ad 544 return Qnil;
d418ef42
JA
545
546 return i->plist;
547}
548
5fbe2a44 549DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
1f5e848a 550 "Return the value of POSITION's property PROP, in OBJECT.\n\
d4b530ad
RS
551OBJECT is optional and defaults to the current buffer.\n\
552If POSITION is at the end of OBJECT, the value is nil.")
1f5e848a
EN
553 (position, prop, object)
554 Lisp_Object position, object;
46bb7c2b 555 Lisp_Object prop;
5fbe2a44 556{
1f5e848a 557 return textget (Ftext_properties_at (position, object), prop);
5fbe2a44
RS
558}
559
8d41abc4
MB
560/* Return the value of POSITION's property PROP, in OBJECT.
561 OBJECT is optional and defaults to the current buffer.
562 If OVERLAY is non-0, then in the case that the returned property is from
563 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
564 returned in *OVERLAY.
565 If POSITION is at the end of OBJECT, the value is nil.
566 If OBJECT is a buffer, then overlay properties are considered as well as
567 text properties.
568 If OBJECT is a window, then that window's buffer is used, but
569 window-specific overlays are considered only if they are associated
570 with OBJECT. */
571Lisp_Object
572get_char_property_and_overlay (position, prop, object, overlay)
1f5e848a 573 Lisp_Object position, object;
f5957179 574 register Lisp_Object prop;
8d41abc4 575 Lisp_Object *overlay;
f5957179
KH
576{
577 struct window *w = 0;
578
1f5e848a 579 CHECK_NUMBER_COERCE_MARKER (position, 0);
f5957179
KH
580
581 if (NILP (object))
c8a4fc3d 582 XSETBUFFER (object, current_buffer);
f5957179
KH
583
584 if (WINDOWP (object))
585 {
586 w = XWINDOW (object);
64a49ca7 587 object = w->buffer;
f5957179
KH
588 }
589 if (BUFFERP (object))
590 {
1f5e848a 591 int posn = XINT (position);
f5957179
KH
592 int noverlays;
593 Lisp_Object *overlay_vec, tem;
594 int next_overlay;
595 int len;
cbc55f55
RS
596 struct buffer *obuf = current_buffer;
597
598 set_buffer_temp (XBUFFER (object));
f5957179
KH
599
600 /* First try with room for 40 overlays. */
601 len = 40;
602 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
603
59a486ab 604 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
ecfb39ee 605 &next_overlay, NULL, 0);
f5957179
KH
606
607 /* If there are more than 40,
608 make enough space for all, and try again. */
609 if (noverlays > len)
610 {
611 len = noverlays;
612 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
59a486ab 613 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
ecfb39ee 614 &next_overlay, NULL, 0);
f5957179
KH
615 }
616 noverlays = sort_overlays (overlay_vec, noverlays, w);
617
cbc55f55
RS
618 set_buffer_temp (obuf);
619
f5957179
KH
620 /* Now check the overlays in order of decreasing priority. */
621 while (--noverlays >= 0)
622 {
623 tem = Foverlay_get (overlay_vec[noverlays], prop);
624 if (!NILP (tem))
8d41abc4
MB
625 {
626 if (overlay)
627 /* Return the overlay we got the property from. */
628 *overlay = overlay_vec[noverlays];
629 return tem;
630 }
f5957179
KH
631 }
632 }
8d41abc4
MB
633
634 if (overlay)
635 /* Indicate that the return value is not from an overlay. */
636 *overlay = Qnil;
637
f5957179
KH
638 /* Not a buffer, or no appropriate overlay, so fall through to the
639 simpler case. */
8d41abc4
MB
640 return Fget_text_property (position, prop, object);
641}
642
643DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
644 "Return the value of POSITION's property PROP, in OBJECT.\n\
645OBJECT is optional and defaults to the current buffer.\n\
646If POSITION is at the end of OBJECT, the value is nil.\n\
647If OBJECT is a buffer, then overlay properties are considered as well as\n\
648text properties.\n\
649If OBJECT is a window, then that window's buffer is used, but window-specific\n\
650overlays are considered only if they are associated with OBJECT.")
651 (position, prop, object)
652 Lisp_Object position, object;
653 register Lisp_Object prop;
654{
655 return get_char_property_and_overlay (position, prop, object, 0);
f5957179 656}
fcab51aa
RS
657\f
658DEFUN ("next-char-property-change", Fnext_char_property_change,
659 Snext_char_property_change, 1, 2, 0,
660 "Return the position of next text property or overlay change.\n\
661This scans characters forward from POSITION in OBJECT till it finds\n\
662a change in some text property, or the beginning or end of an overlay,\n\
663and returns the position of that.\n\
664If none is found, the function returns (point-max).\n\
665\n\
666If the optional third argument LIMIT is non-nil, don't search\n\
667past position LIMIT; return LIMIT if nothing is found before LIMIT.")
668 (position, limit)
669 Lisp_Object position, limit;
670{
671 Lisp_Object temp;
672
673 temp = Fnext_overlay_change (position);
674 if (! NILP (limit))
675 {
676 CHECK_NUMBER (limit, 2);
677 if (XINT (limit) < XINT (temp))
678 temp = limit;
679 }
680 return Fnext_property_change (position, Qnil, temp);
681}
682
683DEFUN ("previous-char-property-change", Fprevious_char_property_change,
684 Sprevious_char_property_change, 1, 2, 0,
685 "Return the position of previous text property or overlay change.\n\
686Scans characters backward from POSITION in OBJECT till it finds\n\
687a change in some text property, or the beginning or end of an overlay,\n\
688and returns the position of that.\n\
689If none is found, the function returns (point-max).\n\
690\n\
691If the optional third argument LIMIT is non-nil, don't search\n\
692past position LIMIT; return LIMIT if nothing is found before LIMIT.")
693 (position, limit)
694 Lisp_Object position, limit;
695{
696 Lisp_Object temp;
f5957179 697
fcab51aa
RS
698 temp = Fprevious_overlay_change (position);
699 if (! NILP (limit))
700 {
701 CHECK_NUMBER (limit, 2);
702 if (XINT (limit) > XINT (temp))
703 temp = limit;
704 }
705 return Fprevious_property_change (position, Qnil, temp);
706}
0b0737d1
GM
707
708
b7e047fb
MB
709DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
710 Snext_single_char_property_change, 2, 4, 0,
711 "Return the position of next text property or overlay change for a specific property.\n\
712Scans characters forward from POSITION till it finds\n\
713a change in the PROP property, then returns the position of the change.\n\
714The optional third argument OBJECT is the string or buffer to scan.\n\
715The property values are compared with `eq'.\n\
716Return nil if the property is constant all the way to the end of OBJECT.\n\
717If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
718If the optional fourth argument LIMIT is non-nil, don't search\n\
719past position LIMIT; return LIMIT if nothing is found before LIMIT.")
720 (position, prop, object, limit)
721 Lisp_Object prop, position, object, limit;
0b0737d1
GM
722{
723 if (STRINGP (object))
724 {
b7e047fb
MB
725 position = Fnext_single_property_change (position, prop, object, limit);
726 if (NILP (position))
0b0737d1
GM
727 {
728 if (NILP (limit))
b7e047fb 729 position = make_number (XSTRING (object)->size);
0b0737d1 730 else
b7e047fb 731 position = limit;
0b0737d1
GM
732 }
733 }
734 else
735 {
736 Lisp_Object initial_value, value;
0b0737d1
GM
737 int count = specpdl_ptr - specpdl;
738
b7e047fb 739 if (! NILP (object))
0b0737d1
GM
740 CHECK_BUFFER (object, 0);
741
742 if (BUFFERP (object) && current_buffer != XBUFFER (object))
743 {
744 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
745 Fset_buffer (object);
746 }
747
b7e047fb 748 initial_value = Fget_char_property (position, prop, object);
0b0737d1 749
b7e047fb
MB
750 if (NILP (limit))
751 XSETFASTINT (limit, BUF_ZV (current_buffer));
752 else
753 CHECK_NUMBER_COERCE_MARKER (limit, 0);
754
755 for (;;)
0b0737d1 756 {
b7e047fb
MB
757 position = Fnext_char_property_change (position, limit);
758 if (XFASTINT (position) >= XFASTINT (limit)) {
759 position = limit;
760 break;
761 }
762
763 value = Fget_char_property (position, prop, object);
0b0737d1
GM
764 if (!EQ (value, initial_value))
765 break;
766 }
767
768 unbind_to (count, Qnil);
769 }
770
b7e047fb 771 return position;
0b0737d1
GM
772}
773
b7e047fb
MB
774DEFUN ("previous-single-char-property-change",
775 Fprevious_single_char_property_change,
776 Sprevious_single_char_property_change, 2, 4, 0,
777 "Return the position of previous text property or overlay change for a specific property.\n\
778Scans characters backward from POSITION till it finds\n\
779a change in the PROP property, then returns the position of the change.\n\
780The optional third argument OBJECT is the string or buffer to scan.\n\
781The property values are compared with `eq'.\n\
782Return nil if the property is constant all the way to the start of OBJECT.\n\
783If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
784If the optional fourth argument LIMIT is non-nil, don't search\n\
785back past position LIMIT; return LIMIT if nothing is found before LIMIT.")
786 (position, prop, object, limit)
787 Lisp_Object prop, position, object, limit;
788{
789 if (STRINGP (object))
790 {
791 position = Fprevious_single_property_change (position, prop, object, limit);
792 if (NILP (position))
793 {
794 if (NILP (limit))
795 position = make_number (XSTRING (object)->size);
796 else
797 position = limit;
798 }
799 }
800 else
801 {
b7e047fb
MB
802 int count = specpdl_ptr - specpdl;
803
804 if (! NILP (object))
805 CHECK_BUFFER (object, 0);
806
807 if (BUFFERP (object) && current_buffer != XBUFFER (object))
808 {
809 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
810 Fset_buffer (object);
811 }
812
813 if (NILP (limit))
814 XSETFASTINT (limit, BUF_BEGV (current_buffer));
815 else
816 CHECK_NUMBER_COERCE_MARKER (limit, 0);
817
ce6b02e0
MB
818 if (XFASTINT (position) <= XFASTINT (limit))
819 position = limit;
820 else
b7e047fb 821 {
ce6b02e0 822 Lisp_Object initial_value =
d4225c08
KR
823 Fget_char_property (make_number (XFASTINT (position) - 1),
824 prop, object);
ce6b02e0
MB
825
826 for (;;)
827 {
828 position = Fprevious_char_property_change (position, limit);
0b0737d1 829
ce6b02e0
MB
830 if (XFASTINT (position) <= XFASTINT (limit))
831 {
832 position = limit;
833 break;
834 }
835 else
836 {
837 Lisp_Object value =
d4225c08
KR
838 Fget_char_property (make_number (XFASTINT (position) - 1),
839 prop, object);
ce6b02e0
MB
840
841 if (!EQ (value, initial_value))
842 break;
843 }
844 }
b7e047fb
MB
845 }
846
847 unbind_to (count, Qnil);
848 }
849
850 return position;
851}
fcab51aa 852\f
d418ef42 853DEFUN ("next-property-change", Fnext_property_change,
111b637d 854 Snext_property_change, 1, 3, 0,
5fbe2a44 855 "Return the position of next property change.\n\
1f5e848a 856Scans characters forward from POSITION in OBJECT till it finds\n\
5fbe2a44
RS
857a change in some text property, then returns the position of the change.\n\
858The optional second argument OBJECT is the string or buffer to scan.\n\
859Return nil if the property is constant all the way to the end of OBJECT.\n\
1f5e848a 860If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
111b637d
RS
861If the optional third argument LIMIT is non-nil, don't search\n\
862past position LIMIT; return LIMIT if nothing is found before LIMIT.")
1f5e848a
EN
863 (position, object, limit)
864 Lisp_Object position, object, limit;
d418ef42
JA
865{
866 register INTERVAL i, next;
867
5fbe2a44 868 if (NILP (object))
c8a4fc3d 869 XSETBUFFER (object, current_buffer);
5fbe2a44 870
041aa96f 871 if (! NILP (limit) && ! EQ (limit, Qt))
1387d54e
KH
872 CHECK_NUMBER_COERCE_MARKER (limit, 0);
873
1f5e848a 874 i = validate_interval_range (object, &position, &position, soft);
d418ef42 875
041aa96f
RS
876 /* If LIMIT is t, return start of next interval--don't
877 bother checking further intervals. */
878 if (EQ (limit, Qt))
879 {
44214c1b
RS
880 if (NULL_INTERVAL_P (i))
881 next = i;
882 else
883 next = next_interval (i);
884
c7b6dfa6 885 if (NULL_INTERVAL_P (next))
1f5e848a
EN
886 XSETFASTINT (position, (STRINGP (object)
887 ? XSTRING (object)->size
888 : BUF_ZV (XBUFFER (object))));
c7b6dfa6 889 else
ad077db0 890 XSETFASTINT (position, next->position);
1f5e848a 891 return position;
041aa96f
RS
892 }
893
44214c1b
RS
894 if (NULL_INTERVAL_P (i))
895 return limit;
896
897 next = next_interval (i);
898
111b637d 899 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
ad077db0 900 && (NILP (limit) || next->position < XFASTINT (limit)))
d418ef42
JA
901 next = next_interval (next);
902
903 if (NULL_INTERVAL_P (next))
111b637d 904 return limit;
ad077db0 905 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
111b637d 906 return limit;
d418ef42 907
ad077db0 908 XSETFASTINT (position, next->position);
1f5e848a 909 return position;
19e1c426
RS
910}
911
912/* Return 1 if there's a change in some property between BEG and END. */
913
914int
915property_change_between_p (beg, end)
916 int beg, end;
917{
918 register INTERVAL i, next;
919 Lisp_Object object, pos;
920
c8a4fc3d 921 XSETBUFFER (object, current_buffer);
e9c4fbcd 922 XSETFASTINT (pos, beg);
19e1c426
RS
923
924 i = validate_interval_range (object, &pos, &pos, soft);
925 if (NULL_INTERVAL_P (i))
926 return 0;
927
928 next = next_interval (i);
929 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
930 {
931 next = next_interval (next);
e050ef74
RS
932 if (NULL_INTERVAL_P (next))
933 return 0;
ad077db0 934 if (next->position >= end)
19e1c426
RS
935 return 0;
936 }
937
938 if (NULL_INTERVAL_P (next))
939 return 0;
940
941 return 1;
d418ef42
JA
942}
943
9c79dd1b 944DEFUN ("next-single-property-change", Fnext_single_property_change,
111b637d 945 Snext_single_property_change, 2, 4, 0,
5fbe2a44 946 "Return the position of next property change for a specific property.\n\
1f5e848a 947Scans characters forward from POSITION till it finds\n\
5fbe2a44
RS
948a change in the PROP property, then returns the position of the change.\n\
949The optional third argument OBJECT is the string or buffer to scan.\n\
da625a3c 950The property values are compared with `eq'.\n\
5fbe2a44 951Return nil if the property is constant all the way to the end of OBJECT.\n\
1f5e848a 952If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
111b637d 953If the optional fourth argument LIMIT is non-nil, don't search\n\
5abb9556 954past position LIMIT; return LIMIT if nothing is found before LIMIT.")
1f5e848a
EN
955 (position, prop, object, limit)
956 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
957{
958 register INTERVAL i, next;
959 register Lisp_Object here_val;
960
5fbe2a44 961 if (NILP (object))
c8a4fc3d 962 XSETBUFFER (object, current_buffer);
5fbe2a44 963
1387d54e
KH
964 if (!NILP (limit))
965 CHECK_NUMBER_COERCE_MARKER (limit, 0);
966
1f5e848a 967 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 968 if (NULL_INTERVAL_P (i))
111b637d 969 return limit;
9c79dd1b 970
6a0486dd 971 here_val = textget (i->plist, prop);
9c79dd1b 972 next = next_interval (i);
6a0486dd 973 while (! NULL_INTERVAL_P (next)
111b637d 974 && EQ (here_val, textget (next->plist, prop))
ad077db0 975 && (NILP (limit) || next->position < XFASTINT (limit)))
9c79dd1b
JA
976 next = next_interval (next);
977
978 if (NULL_INTERVAL_P (next))
111b637d 979 return limit;
ad077db0 980 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
111b637d 981 return limit;
9c79dd1b 982
ad077db0 983 return make_number (next->position);
9c79dd1b
JA
984}
985
d418ef42 986DEFUN ("previous-property-change", Fprevious_property_change,
111b637d 987 Sprevious_property_change, 1, 3, 0,
5fbe2a44 988 "Return the position of previous property change.\n\
1f5e848a 989Scans characters backwards from POSITION in OBJECT till it finds\n\
5fbe2a44
RS
990a change in some text property, then returns the position of the change.\n\
991The optional second argument OBJECT is the string or buffer to scan.\n\
992Return nil if the property is constant all the way to the start of OBJECT.\n\
1f5e848a 993If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
111b637d 994If the optional third argument LIMIT is non-nil, don't search\n\
5abb9556 995back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1f5e848a
EN
996 (position, object, limit)
997 Lisp_Object position, object, limit;
d418ef42
JA
998{
999 register INTERVAL i, previous;
1000
5fbe2a44 1001 if (NILP (object))
c8a4fc3d 1002 XSETBUFFER (object, current_buffer);
5fbe2a44 1003
1387d54e
KH
1004 if (!NILP (limit))
1005 CHECK_NUMBER_COERCE_MARKER (limit, 0);
1006
1f5e848a 1007 i = validate_interval_range (object, &position, &position, soft);
d418ef42 1008 if (NULL_INTERVAL_P (i))
111b637d 1009 return limit;
d418ef42 1010
53b7feec 1011 /* Start with the interval containing the char before point. */
1f5e848a 1012 if (i->position == XFASTINT (position))
53b7feec
RS
1013 i = previous_interval (i);
1014
d418ef42 1015 previous = previous_interval (i);
111b637d
RS
1016 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1017 && (NILP (limit)
ad077db0 1018 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
d418ef42
JA
1019 previous = previous_interval (previous);
1020 if (NULL_INTERVAL_P (previous))
111b637d
RS
1021 return limit;
1022 if (!NILP (limit)
ad077db0 1023 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
111b637d 1024 return limit;
d418ef42 1025
ad077db0 1026 return make_number (previous->position + LENGTH (previous));
d418ef42
JA
1027}
1028
9c79dd1b 1029DEFUN ("previous-single-property-change", Fprevious_single_property_change,
111b637d 1030 Sprevious_single_property_change, 2, 4, 0,
5fbe2a44 1031 "Return the position of previous property change for a specific property.\n\
1f5e848a 1032Scans characters backward from POSITION till it finds\n\
5fbe2a44
RS
1033a change in the PROP property, then returns the position of the change.\n\
1034The optional third argument OBJECT is the string or buffer to scan.\n\
93fda178 1035The property values are compared with `eq'.\n\
5fbe2a44 1036Return nil if the property is constant all the way to the start of OBJECT.\n\
1f5e848a 1037If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
111b637d 1038If the optional fourth argument LIMIT is non-nil, don't search\n\
5abb9556 1039back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1f5e848a
EN
1040 (position, prop, object, limit)
1041 Lisp_Object position, prop, object, limit;
9c79dd1b
JA
1042{
1043 register INTERVAL i, previous;
1044 register Lisp_Object here_val;
1045
5fbe2a44 1046 if (NILP (object))
c8a4fc3d 1047 XSETBUFFER (object, current_buffer);
5fbe2a44 1048
1387d54e
KH
1049 if (!NILP (limit))
1050 CHECK_NUMBER_COERCE_MARKER (limit, 0);
1051
1f5e848a 1052 i = validate_interval_range (object, &position, &position, soft);
9c79dd1b 1053
53b7feec 1054 /* Start with the interval containing the char before point. */
1f5e848a 1055 if (! NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
53b7feec
RS
1056 i = previous_interval (i);
1057
6873cfa3
KH
1058 if (NULL_INTERVAL_P (i))
1059 return limit;
1060
6a0486dd 1061 here_val = textget (i->plist, prop);
9c79dd1b
JA
1062 previous = previous_interval (i);
1063 while (! NULL_INTERVAL_P (previous)
111b637d
RS
1064 && EQ (here_val, textget (previous->plist, prop))
1065 && (NILP (limit)
ad077db0 1066 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
9c79dd1b
JA
1067 previous = previous_interval (previous);
1068 if (NULL_INTERVAL_P (previous))
111b637d
RS
1069 return limit;
1070 if (!NILP (limit)
ad077db0 1071 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
111b637d 1072 return limit;
9c79dd1b 1073
ad077db0 1074 return make_number (previous->position + LENGTH (previous));
9c79dd1b 1075}
fcab51aa 1076\f
c98da214
RS
1077/* Callers note, this can GC when OBJECT is a buffer (or nil). */
1078
d418ef42 1079DEFUN ("add-text-properties", Fadd_text_properties,
5fbe2a44
RS
1080 Sadd_text_properties, 3, 4, 0,
1081 "Add properties to the text from START to END.\n\
1f5e848a 1082The third argument PROPERTIES is a property list\n\
5fbe2a44
RS
1083specifying the property values to add.\n\
1084The optional fourth argument, OBJECT,\n\
1085is the string or buffer containing the text.\n\
1086Return t if any property value actually changed, nil otherwise.")
1087 (start, end, properties, object)
1088 Lisp_Object start, end, properties, object;
d418ef42
JA
1089{
1090 register INTERVAL i, unchanged;
caa31568 1091 register int s, len, modified = 0;
c98da214 1092 struct gcpro gcpro1;
d418ef42
JA
1093
1094 properties = validate_plist (properties);
1095 if (NILP (properties))
1096 return Qnil;
1097
5fbe2a44 1098 if (NILP (object))
c8a4fc3d 1099 XSETBUFFER (object, current_buffer);
5fbe2a44 1100
d418ef42
JA
1101 i = validate_interval_range (object, &start, &end, hard);
1102 if (NULL_INTERVAL_P (i))
1103 return Qnil;
1104
1105 s = XINT (start);
1106 len = XINT (end) - s;
1107
c98da214
RS
1108 /* No need to protect OBJECT, because we GC only if it's a buffer,
1109 and live buffers are always protected. */
1110 GCPRO1 (properties);
1111
d418ef42 1112 /* If we're not starting on an interval boundary, we have to
cdf3e5a2 1113 split this interval. */
d418ef42
JA
1114 if (i->position != s)
1115 {
1116 /* If this interval already has the properties, we can
cdf3e5a2 1117 skip it. */
d418ef42
JA
1118 if (interval_has_all_properties (properties, i))
1119 {
1120 int got = (LENGTH (i) - (s - i->position));
1121 if (got >= len)
64db1307 1122 RETURN_UNGCPRO (Qnil);
d418ef42 1123 len -= got;
05d5b93e 1124 i = next_interval (i);
d418ef42
JA
1125 }
1126 else
1127 {
1128 unchanged = i;
ad9c1940 1129 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 1130 copy_properties (unchanged, i);
d418ef42
JA
1131 }
1132 }
1133
2a631db1
RS
1134 if (BUFFERP (object))
1135 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1136
daa5e28f 1137 /* We are at the beginning of interval I, with LEN chars to scan. */
caa31568 1138 for (;;)
d418ef42 1139 {
d4b530ad
RS
1140 if (i == 0)
1141 abort ();
1142
d418ef42
JA
1143 if (LENGTH (i) >= len)
1144 {
c98da214
RS
1145 /* We can UNGCPRO safely here, because there will be just
1146 one more chance to gc, in the next call to add_properties,
1147 and after that we will not need PROPERTIES or OBJECT again. */
1148 UNGCPRO;
1149
d418ef42 1150 if (interval_has_all_properties (properties, i))
26c76ace 1151 {
2a631db1
RS
1152 if (BUFFERP (object))
1153 signal_after_change (XINT (start), XINT (end) - XINT (start),
1154 XINT (end) - XINT (start));
26c76ace
RS
1155
1156 return modified ? Qt : Qnil;
1157 }
d418ef42
JA
1158
1159 if (LENGTH (i) == len)
1160 {
d4b530ad 1161 add_properties (properties, i, object);
2a631db1
RS
1162 if (BUFFERP (object))
1163 signal_after_change (XINT (start), XINT (end) - XINT (start),
1164 XINT (end) - XINT (start));
d418ef42
JA
1165 return Qt;
1166 }
1167
1168 /* i doesn't have the properties, and goes past the change limit */
1169 unchanged = i;
ad9c1940 1170 i = split_interval_left (unchanged, len);
d418ef42 1171 copy_properties (unchanged, i);
d4b530ad 1172 add_properties (properties, i, object);
2a631db1
RS
1173 if (BUFFERP (object))
1174 signal_after_change (XINT (start), XINT (end) - XINT (start),
1175 XINT (end) - XINT (start));
d418ef42
JA
1176 return Qt;
1177 }
1178
1179 len -= LENGTH (i);
d4b530ad 1180 modified += add_properties (properties, i, object);
d418ef42
JA
1181 i = next_interval (i);
1182 }
1183}
1184
c98da214
RS
1185/* Callers note, this can GC when OBJECT is a buffer (or nil). */
1186
d4b530ad
RS
1187DEFUN ("put-text-property", Fput_text_property,
1188 Sput_text_property, 4, 5, 0,
1189 "Set one property of the text from START to END.\n\
1f5e848a 1190The third and fourth arguments PROPERTY and VALUE\n\
d4b530ad
RS
1191specify the property to add.\n\
1192The optional fifth argument, OBJECT,\n\
1193is the string or buffer containing the text.")
1f5e848a
EN
1194 (start, end, property, value, object)
1195 Lisp_Object start, end, property, value, object;
d4b530ad
RS
1196{
1197 Fadd_text_properties (start, end,
1f5e848a 1198 Fcons (property, Fcons (value, Qnil)),
d4b530ad
RS
1199 object);
1200 return Qnil;
1201}
1202
d418ef42 1203DEFUN ("set-text-properties", Fset_text_properties,
5fbe2a44
RS
1204 Sset_text_properties, 3, 4, 0,
1205 "Completely replace properties of text from START to END.\n\
1f5e848a 1206The third argument PROPERTIES is the new property list.\n\
5fbe2a44
RS
1207The optional fourth argument, OBJECT,\n\
1208is the string or buffer containing the text.")
1f5e848a
EN
1209 (start, end, properties, object)
1210 Lisp_Object start, end, properties, object;
0087ade6
GM
1211{
1212 return set_text_properties (start, end, properties, object, Qt);
1213}
1214
1215
1216/* Replace properties of text from START to END with new list of
1217 properties PROPERTIES. OBJECT is the buffer or string containing
1218 the text. OBJECT nil means use the current buffer.
1219 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1220 is non-nil if properties were replaced; it is nil if there weren't
1221 any properties to replace. */
1222
1223Lisp_Object
1224set_text_properties (start, end, properties, object, signal_after_change_p)
1225 Lisp_Object start, end, properties, object, signal_after_change_p;
d418ef42
JA
1226{
1227 register INTERVAL i, unchanged;
9c79dd1b 1228 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42 1229 register int s, len;
33d7d0df
RS
1230 Lisp_Object ostart, oend;
1231
1232 ostart = start;
1233 oend = end;
d418ef42 1234
1f5e848a 1235 properties = validate_plist (properties);
d418ef42 1236
5fbe2a44 1237 if (NILP (object))
c8a4fc3d 1238 XSETBUFFER (object, current_buffer);
5fbe2a44 1239
919fa9cb
RS
1240 /* If we want no properties for a whole string,
1241 get rid of its intervals. */
1f5e848a 1242 if (NILP (properties) && STRINGP (object)
919fa9cb
RS
1243 && XFASTINT (start) == 0
1244 && XFASTINT (end) == XSTRING (object)->size)
1245 {
26c76ace
RS
1246 if (! XSTRING (object)->intervals)
1247 return Qt;
1248
919fa9cb
RS
1249 XSTRING (object)->intervals = 0;
1250 return Qt;
1251 }
1252
facc570e 1253 i = validate_interval_range (object, &start, &end, soft);
919fa9cb 1254
d418ef42 1255 if (NULL_INTERVAL_P (i))
facc570e 1256 {
1f5e848a
EN
1257 /* If buffer has no properties, and we want none, return now. */
1258 if (NILP (properties))
facc570e
RS
1259 return Qnil;
1260
33d7d0df
RS
1261 /* Restore the original START and END values
1262 because validate_interval_range increments them for strings. */
1263 start = ostart;
1264 end = oend;
1265
facc570e
RS
1266 i = validate_interval_range (object, &start, &end, hard);
1267 /* This can return if start == end. */
1268 if (NULL_INTERVAL_P (i))
1269 return Qnil;
1270 }
d418ef42
JA
1271
1272 s = XINT (start);
1273 len = XINT (end) - s;
1274
2a631db1
RS
1275 if (BUFFERP (object))
1276 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1277
d418ef42
JA
1278 if (i->position != s)
1279 {
1280 unchanged = i;
ad9c1940 1281 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 1282
d418ef42
JA
1283 if (LENGTH (i) > len)
1284 {
9c79dd1b 1285 copy_properties (unchanged, i);
ad9c1940 1286 i = split_interval_left (i, len);
1f5e848a 1287 set_properties (properties, i, object);
0087ade6 1288 if (BUFFERP (object) && !NILP (signal_after_change_p))
2a631db1
RS
1289 signal_after_change (XINT (start), XINT (end) - XINT (start),
1290 XINT (end) - XINT (start));
26c76ace 1291
d418ef42
JA
1292 return Qt;
1293 }
1294
1f5e848a 1295 set_properties (properties, i, object);
daa5e28f 1296
9c79dd1b 1297 if (LENGTH (i) == len)
26c76ace 1298 {
0087ade6 1299 if (BUFFERP (object) && !NILP (signal_after_change_p))
2a631db1
RS
1300 signal_after_change (XINT (start), XINT (end) - XINT (start),
1301 XINT (end) - XINT (start));
26c76ace
RS
1302
1303 return Qt;
1304 }
9c79dd1b
JA
1305
1306 prev_changed = i;
d418ef42
JA
1307 len -= LENGTH (i);
1308 i = next_interval (i);
1309 }
1310
cd7d971d 1311 /* We are starting at the beginning of an interval, I */
7855e674 1312 while (len > 0)
d418ef42 1313 {
d4b530ad
RS
1314 if (i == 0)
1315 abort ();
1316
d418ef42
JA
1317 if (LENGTH (i) >= len)
1318 {
cd7d971d 1319 if (LENGTH (i) > len)
ad9c1940 1320 i = split_interval_left (i, len);
d418ef42 1321
6f232881
RS
1322 /* We have to call set_properties even if we are going to
1323 merge the intervals, so as to make the undo records
1324 and cause redisplay to happen. */
1f5e848a 1325 set_properties (properties, i, object);
6f232881 1326 if (!NULL_INTERVAL_P (prev_changed))
9c79dd1b 1327 merge_interval_left (i);
0087ade6 1328 if (BUFFERP (object) && !NILP (signal_after_change_p))
2a631db1
RS
1329 signal_after_change (XINT (start), XINT (end) - XINT (start),
1330 XINT (end) - XINT (start));
d418ef42
JA
1331 return Qt;
1332 }
1333
1334 len -= LENGTH (i);
6f232881
RS
1335
1336 /* We have to call set_properties even if we are going to
1337 merge the intervals, so as to make the undo records
1338 and cause redisplay to happen. */
1f5e848a 1339 set_properties (properties, i, object);
9c79dd1b 1340 if (NULL_INTERVAL_P (prev_changed))
6f232881 1341 prev_changed = i;
9c79dd1b
JA
1342 else
1343 prev_changed = i = merge_interval_left (i);
1344
d418ef42
JA
1345 i = next_interval (i);
1346 }
1347
0087ade6 1348 if (BUFFERP (object) && !NILP (signal_after_change_p))
2a631db1
RS
1349 signal_after_change (XINT (start), XINT (end) - XINT (start),
1350 XINT (end) - XINT (start));
d418ef42
JA
1351 return Qt;
1352}
1353
1354DEFUN ("remove-text-properties", Fremove_text_properties,
5fbe2a44
RS
1355 Sremove_text_properties, 3, 4, 0,
1356 "Remove some properties from text from START to END.\n\
1f5e848a 1357The third argument PROPERTIES is a property list\n\
5fbe2a44 1358whose property names specify the properties to remove.\n\
1f5e848a 1359\(The values stored in PROPERTIES are ignored.)\n\
5fbe2a44
RS
1360The optional fourth argument, OBJECT,\n\
1361is the string or buffer containing the text.\n\
1362Return t if any property was actually removed, nil otherwise.")
1f5e848a
EN
1363 (start, end, properties, object)
1364 Lisp_Object start, end, properties, object;
d418ef42
JA
1365{
1366 register INTERVAL i, unchanged;
caa31568 1367 register int s, len, modified = 0;
d418ef42 1368
5fbe2a44 1369 if (NILP (object))
c8a4fc3d 1370 XSETBUFFER (object, current_buffer);
5fbe2a44 1371
d418ef42
JA
1372 i = validate_interval_range (object, &start, &end, soft);
1373 if (NULL_INTERVAL_P (i))
1374 return Qnil;
1375
1376 s = XINT (start);
1377 len = XINT (end) - s;
9c79dd1b 1378
d418ef42
JA
1379 if (i->position != s)
1380 {
1381 /* No properties on this first interval -- return if
cdf3e5a2 1382 it covers the entire region. */
1f5e848a 1383 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1384 {
1385 int got = (LENGTH (i) - (s - i->position));
1386 if (got >= len)
1387 return Qnil;
1388 len -= got;
05d5b93e 1389 i = next_interval (i);
d418ef42 1390 }
daa5e28f
RS
1391 /* Split away the beginning of this interval; what we don't
1392 want to modify. */
d418ef42
JA
1393 else
1394 {
1395 unchanged = i;
ad9c1940 1396 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 1397 copy_properties (unchanged, i);
d418ef42
JA
1398 }
1399 }
1400
2a631db1
RS
1401 if (BUFFERP (object))
1402 modify_region (XBUFFER (object), XINT (start), XINT (end));
26c76ace 1403
d418ef42 1404 /* We are at the beginning of an interval, with len to scan */
caa31568 1405 for (;;)
d418ef42 1406 {
d4b530ad
RS
1407 if (i == 0)
1408 abort ();
1409
d418ef42
JA
1410 if (LENGTH (i) >= len)
1411 {
1f5e848a 1412 if (! interval_has_some_properties (properties, i))
d418ef42
JA
1413 return modified ? Qt : Qnil;
1414
1415 if (LENGTH (i) == len)
1416 {
1f5e848a 1417 remove_properties (properties, i, object);
2a631db1
RS
1418 if (BUFFERP (object))
1419 signal_after_change (XINT (start), XINT (end) - XINT (start),
1420 XINT (end) - XINT (start));
d418ef42
JA
1421 return Qt;
1422 }
1423
1424 /* i has the properties, and goes past the change limit */
daa5e28f 1425 unchanged = i;
ad9c1940 1426 i = split_interval_left (i, len);
d418ef42 1427 copy_properties (unchanged, i);
1f5e848a 1428 remove_properties (properties, i, object);
2a631db1
RS
1429 if (BUFFERP (object))
1430 signal_after_change (XINT (start), XINT (end) - XINT (start),
1431 XINT (end) - XINT (start));
d418ef42
JA
1432 return Qt;
1433 }
1434
1435 len -= LENGTH (i);
1f5e848a 1436 modified += remove_properties (properties, i, object);
d418ef42
JA
1437 i = next_interval (i);
1438 }
1439}
fcab51aa 1440\f
ad9c1940
JB
1441DEFUN ("text-property-any", Ftext_property_any,
1442 Stext_property_any, 4, 5, 0,
1f5e848a
EN
1443 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1444If so, return the position of the first character whose property PROPERTY\n\
1445is `eq' to VALUE. Otherwise return nil.\n\
ad9c1940
JB
1446The optional fifth argument, OBJECT, is the string or buffer\n\
1447containing the text.")
1f5e848a
EN
1448 (start, end, property, value, object)
1449 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1450{
1451 register INTERVAL i;
1452 register int e, pos;
1453
1454 if (NILP (object))
c8a4fc3d 1455 XSETBUFFER (object, current_buffer);
ad9c1940 1456 i = validate_interval_range (object, &start, &end, soft);
2084fddb
KH
1457 if (NULL_INTERVAL_P (i))
1458 return (!NILP (value) || EQ (start, end) ? Qnil : start);
ad9c1940
JB
1459 e = XINT (end);
1460
1461 while (! NULL_INTERVAL_P (i))
1462 {
1463 if (i->position >= e)
1464 break;
1f5e848a 1465 if (EQ (textget (i->plist, property), value))
ad9c1940
JB
1466 {
1467 pos = i->position;
1468 if (pos < XINT (start))
1469 pos = XINT (start);
ad077db0 1470 return make_number (pos);
ad9c1940
JB
1471 }
1472 i = next_interval (i);
1473 }
1474 return Qnil;
1475}
1476
1477DEFUN ("text-property-not-all", Ftext_property_not_all,
1478 Stext_property_not_all, 4, 5, 0,
1f5e848a
EN
1479 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1480If so, return the position of the first character whose property PROPERTY\n\
1481is not `eq' to VALUE. Otherwise, return nil.\n\
ad9c1940
JB
1482The optional fifth argument, OBJECT, is the string or buffer\n\
1483containing the text.")
1f5e848a
EN
1484 (start, end, property, value, object)
1485 Lisp_Object start, end, property, value, object;
ad9c1940
JB
1486{
1487 register INTERVAL i;
1488 register int s, e;
1489
1490 if (NILP (object))
c8a4fc3d 1491 XSETBUFFER (object, current_buffer);
ad9c1940
JB
1492 i = validate_interval_range (object, &start, &end, soft);
1493 if (NULL_INTERVAL_P (i))
916a3119 1494 return (NILP (value) || EQ (start, end)) ? Qnil : start;
ad9c1940
JB
1495 s = XINT (start);
1496 e = XINT (end);
1497
1498 while (! NULL_INTERVAL_P (i))
1499 {
1500 if (i->position >= e)
1501 break;
1f5e848a 1502 if (! EQ (textget (i->plist, property), value))
ad9c1940
JB
1503 {
1504 if (i->position > s)
1505 s = i->position;
ad077db0 1506 return make_number (s);
ad9c1940
JB
1507 }
1508 i = next_interval (i);
1509 }
1510 return Qnil;
1511}
fcab51aa 1512\f
15e4954b
JB
1513/* I don't think this is the right interface to export; how often do you
1514 want to do something like this, other than when you're copying objects
1515 around?
1516
1517 I think it would be better to have a pair of functions, one which
1518 returns the text properties of a region as a list of ranges and
1519 plists, and another which applies such a list to another object. */
1520
c98da214
RS
1521/* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1522 SRC and DEST may each refer to strings or buffers.
1523 Optional sixth argument PROP causes only that property to be copied.
1524 Properties are copied to DEST as if by `add-text-properties'.
1525 Return t if any property value actually changed, nil otherwise. */
1526
1527/* Note this can GC when DEST is a buffer. */
ad077db0 1528
15e4954b
JB
1529Lisp_Object
1530copy_text_properties (start, end, src, pos, dest, prop)
1531 Lisp_Object start, end, src, pos, dest, prop;
1532{
1533 INTERVAL i;
1534 Lisp_Object res;
1535 Lisp_Object stuff;
1536 Lisp_Object plist;
1537 int s, e, e2, p, len, modified = 0;
c98da214 1538 struct gcpro gcpro1, gcpro2;
15e4954b
JB
1539
1540 i = validate_interval_range (src, &start, &end, soft);
1541 if (NULL_INTERVAL_P (i))
1542 return Qnil;
1543
1544 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1545 {
1546 Lisp_Object dest_start, dest_end;
1547
1548 dest_start = pos;
e9c4fbcd 1549 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
15e4954b
JB
1550 /* Apply this to a copy of pos; it will try to increment its arguments,
1551 which we don't want. */
1552 validate_interval_range (dest, &dest_start, &dest_end, soft);
1553 }
1554
1555 s = XINT (start);
1556 e = XINT (end);
1557 p = XINT (pos);
1558
1559 stuff = Qnil;
1560
1561 while (s < e)
1562 {
1563 e2 = i->position + LENGTH (i);
1564 if (e2 > e)
1565 e2 = e;
1566 len = e2 - s;
1567
1568 plist = i->plist;
1569 if (! NILP (prop))
1570 while (! NILP (plist))
1571 {
1572 if (EQ (Fcar (plist), prop))
1573 {
1574 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1575 break;
1576 }
1577 plist = Fcdr (Fcdr (plist));
1578 }
1579 if (! NILP (plist))
1580 {
1581 /* Must defer modifications to the interval tree in case src
cdf3e5a2 1582 and dest refer to the same string or buffer. */
15e4954b
JB
1583 stuff = Fcons (Fcons (make_number (p),
1584 Fcons (make_number (p + len),
1585 Fcons (plist, Qnil))),
1586 stuff);
1587 }
1588
1589 i = next_interval (i);
1590 if (NULL_INTERVAL_P (i))
1591 break;
1592
1593 p += len;
1594 s = i->position;
1595 }
1596
c98da214
RS
1597 GCPRO2 (stuff, dest);
1598
15e4954b
JB
1599 while (! NILP (stuff))
1600 {
1601 res = Fcar (stuff);
1602 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1603 Fcar (Fcdr (Fcdr (res))), dest);
1604 if (! NILP (res))
1605 modified++;
1606 stuff = Fcdr (stuff);
1607 }
1608
c98da214
RS
1609 UNGCPRO;
1610
15e4954b
JB
1611 return modified ? Qt : Qnil;
1612}
9dd7eec6
GM
1613
1614
1615/* Return a list representing the text properties of OBJECT between
1616 START and END. if PROP is non-nil, report only on that property.
1617 Each result list element has the form (S E PLIST), where S and E
1618 are positions in OBJECT and PLIST is a property list containing the
1619 text properties of OBJECT between S and E. Value is nil if OBJECT
1620 doesn't contain text properties between START and END. */
1621
1622Lisp_Object
1623text_property_list (object, start, end, prop)
1624 Lisp_Object object, start, end, prop;
1625{
1626 struct interval *i;
1627 Lisp_Object result;
9dd7eec6
GM
1628
1629 result = Qnil;
1630
1631 i = validate_interval_range (object, &start, &end, soft);
1632 if (!NULL_INTERVAL_P (i))
1633 {
1634 int s = XINT (start);
1635 int e = XINT (end);
1636
1637 while (s < e)
1638 {
1639 int interval_end, len;
1640 Lisp_Object plist;
1641
1642 interval_end = i->position + LENGTH (i);
1643 if (interval_end > e)
1644 interval_end = e;
1645 len = interval_end - s;
1646
1647 plist = i->plist;
1648
1649 if (!NILP (prop))
1650 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1651 if (EQ (Fcar (plist), prop))
1652 {
1653 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1654 break;
1655 }
1656
1657 if (!NILP (plist))
1658 result = Fcons (Fcons (make_number (s),
1659 Fcons (make_number (s + len),
1660 Fcons (plist, Qnil))),
1661 result);
1662
1663 i = next_interval (i);
1664 if (NULL_INTERVAL_P (i))
1665 break;
1666 s = i->position;
1667 }
1668 }
1669
1670 return result;
1671}
1672
1673
1674/* Add text properties to OBJECT from LIST. LIST is a list of triples
1675 (START END PLIST), where START and END are positions and PLIST is a
1676 property list containing the text properties to add. Adjust START
1677 and END positions by DELTA before adding properties. Value is
1678 non-zero if OBJECT was modified. */
1679
1680int
1681add_text_properties_from_list (object, list, delta)
1682 Lisp_Object object, list, delta;
1683{
1684 struct gcpro gcpro1, gcpro2;
1685 int modified_p = 0;
1686
1687 GCPRO2 (list, object);
1688
1689 for (; CONSP (list); list = XCDR (list))
1690 {
1691 Lisp_Object item, start, end, plist, tem;
1692
1693 item = XCAR (list);
1694 start = make_number (XINT (XCAR (item)) + XINT (delta));
1695 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1696 plist = XCAR (XCDR (XCDR (item)));
1697
1698 tem = Fadd_text_properties (start, end, plist, object);
1699 if (!NILP (tem))
1700 modified_p = 1;
1701 }
1702
1703 UNGCPRO;
1704 return modified_p;
1705}
1706
1707
1708
1709/* Modify end-points of ranges in LIST destructively. LIST is a list
1710 as returned from text_property_list. Change end-points equal to
1711 OLD_END to NEW_END. */
1712
1713void
1714extend_property_ranges (list, old_end, new_end)
1715 Lisp_Object list, old_end, new_end;
1716{
1717 for (; CONSP (list); list = XCDR (list))
1718 {
1719 Lisp_Object item, end;
1720
1721 item = XCAR (list);
1722 end = XCAR (XCDR (item));
1723
1724 if (EQ (end, old_end))
70949dac 1725 XCAR (XCDR (item)) = new_end;
9dd7eec6
GM
1726 }
1727}
1728
1729
318d2fa8
RS
1730\f
1731/* Call the modification hook functions in LIST, each with START and END. */
1732
1733static void
1734call_mod_hooks (list, start, end)
1735 Lisp_Object list, start, end;
1736{
1737 struct gcpro gcpro1;
1738 GCPRO1 (list);
1739 while (!NILP (list))
1740 {
1741 call2 (Fcar (list), start, end);
1742 list = Fcdr (list);
1743 }
1744 UNGCPRO;
1745}
1746
96f90544
RS
1747/* Check for read-only intervals between character positions START ... END,
1748 in BUF, and signal an error if we find one.
1749
1750 Then check for any modification hooks in the range.
1751 Create a list of all these hooks in lexicographic order,
1752 eliminating consecutive extra copies of the same hook. Then call
1753 those hooks in order, with START and END - 1 as arguments. */
15e4954b 1754
318d2fa8
RS
1755void
1756verify_interval_modification (buf, start, end)
1757 struct buffer *buf;
1758 int start, end;
1759{
1760 register INTERVAL intervals = BUF_INTERVALS (buf);
695f302f 1761 register INTERVAL i;
318d2fa8
RS
1762 Lisp_Object hooks;
1763 register Lisp_Object prev_mod_hooks;
1764 Lisp_Object mod_hooks;
1765 struct gcpro gcpro1;
1766
1767 hooks = Qnil;
1768 prev_mod_hooks = Qnil;
1769 mod_hooks = Qnil;
1770
1771 interval_insert_behind_hooks = Qnil;
1772 interval_insert_in_front_hooks = Qnil;
1773
1774 if (NULL_INTERVAL_P (intervals))
1775 return;
1776
1777 if (start > end)
1778 {
1779 int temp = start;
1780 start = end;
1781 end = temp;
1782 }
1783
1784 /* For an insert operation, check the two chars around the position. */
1785 if (start == end)
1786 {
1787 INTERVAL prev;
1788 Lisp_Object before, after;
1789
1790 /* Set I to the interval containing the char after START,
1791 and PREV to the interval containing the char before START.
1792 Either one may be null. They may be equal. */
1793 i = find_interval (intervals, start);
1794
1795 if (start == BUF_BEGV (buf))
1796 prev = 0;
1797 else if (i->position == start)
1798 prev = previous_interval (i);
1799 else if (i->position < start)
1800 prev = i;
1801 if (start == BUF_ZV (buf))
1802 i = 0;
1803
1804 /* If Vinhibit_read_only is set and is not a list, we can
1805 skip the read_only checks. */
1806 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
1807 {
1808 /* If I and PREV differ we need to check for the read-only
cdf3e5a2 1809 property together with its stickiness. If either I or
318d2fa8
RS
1810 PREV are 0, this check is all we need.
1811 We have to take special care, since read-only may be
1812 indirectly defined via the category property. */
1813 if (i != prev)
1814 {
1815 if (! NULL_INTERVAL_P (i))
1816 {
1817 after = textget (i->plist, Qread_only);
1818
1819 /* If interval I is read-only and read-only is
1820 front-sticky, inhibit insertion.
1821 Check for read-only as well as category. */
1822 if (! NILP (after)
1823 && NILP (Fmemq (after, Vinhibit_read_only)))
1824 {
1825 Lisp_Object tem;
1826
1827 tem = textget (i->plist, Qfront_sticky);
1828 if (TMEM (Qread_only, tem)
1829 || (NILP (Fplist_get (i->plist, Qread_only))
1830 && TMEM (Qcategory, tem)))
e28c666e 1831 Fsignal (Qtext_read_only, Qnil);
318d2fa8
RS
1832 }
1833 }
1834
1835 if (! NULL_INTERVAL_P (prev))
1836 {
1837 before = textget (prev->plist, Qread_only);
1838
1839 /* If interval PREV is read-only and read-only isn't
1840 rear-nonsticky, inhibit insertion.
1841 Check for read-only as well as category. */
1842 if (! NILP (before)
1843 && NILP (Fmemq (before, Vinhibit_read_only)))
1844 {
1845 Lisp_Object tem;
1846
1847 tem = textget (prev->plist, Qrear_nonsticky);
1848 if (! TMEM (Qread_only, tem)
1849 && (! NILP (Fplist_get (prev->plist,Qread_only))
1850 || ! TMEM (Qcategory, tem)))
e28c666e 1851 Fsignal (Qtext_read_only, Qnil);
318d2fa8
RS
1852 }
1853 }
1854 }
1855 else if (! NULL_INTERVAL_P (i))
1856 {
1857 after = textget (i->plist, Qread_only);
1858
1859 /* If interval I is read-only and read-only is
1860 front-sticky, inhibit insertion.
1861 Check for read-only as well as category. */
1862 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
1863 {
1864 Lisp_Object tem;
1865
1866 tem = textget (i->plist, Qfront_sticky);
1867 if (TMEM (Qread_only, tem)
1868 || (NILP (Fplist_get (i->plist, Qread_only))
1869 && TMEM (Qcategory, tem)))
e28c666e 1870 Fsignal (Qtext_read_only, Qnil);
318d2fa8
RS
1871
1872 tem = textget (prev->plist, Qrear_nonsticky);
1873 if (! TMEM (Qread_only, tem)
1874 && (! NILP (Fplist_get (prev->plist, Qread_only))
1875 || ! TMEM (Qcategory, tem)))
e28c666e 1876 Fsignal (Qtext_read_only, Qnil);
318d2fa8
RS
1877 }
1878 }
1879 }
1880
1881 /* Run both insert hooks (just once if they're the same). */
1882 if (!NULL_INTERVAL_P (prev))
1883 interval_insert_behind_hooks
1884 = textget (prev->plist, Qinsert_behind_hooks);
1885 if (!NULL_INTERVAL_P (i))
1886 interval_insert_in_front_hooks
1887 = textget (i->plist, Qinsert_in_front_hooks);
1888 }
1889 else
1890 {
1891 /* Loop over intervals on or next to START...END,
1892 collecting their hooks. */
1893
1894 i = find_interval (intervals, start);
1895 do
1896 {
1897 if (! INTERVAL_WRITABLE_P (i))
e28c666e 1898 Fsignal (Qtext_read_only, Qnil);
318d2fa8
RS
1899
1900 mod_hooks = textget (i->plist, Qmodification_hooks);
1901 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1902 {
1903 hooks = Fcons (mod_hooks, hooks);
1904 prev_mod_hooks = mod_hooks;
1905 }
1906
1907 i = next_interval (i);
1908 }
1909 /* Keep going thru the interval containing the char before END. */
1910 while (! NULL_INTERVAL_P (i) && i->position < end);
1911
1912 GCPRO1 (hooks);
1913 hooks = Fnreverse (hooks);
1914 while (! EQ (hooks, Qnil))
1915 {
1916 call_mod_hooks (Fcar (hooks), make_number (start),
1917 make_number (end));
1918 hooks = Fcdr (hooks);
1919 }
1920 UNGCPRO;
1921 }
1922}
1923
96f90544 1924/* Run the interval hooks for an insertion on character range START ... END.
318d2fa8
RS
1925 verify_interval_modification chose which hooks to run;
1926 this function is called after the insertion happens
1927 so it can indicate the range of inserted text. */
1928
1929void
1930report_interval_modification (start, end)
1931 Lisp_Object start, end;
1932{
1933 if (! NILP (interval_insert_behind_hooks))
2e34157c 1934 call_mod_hooks (interval_insert_behind_hooks, start, end);
318d2fa8
RS
1935 if (! NILP (interval_insert_in_front_hooks)
1936 && ! EQ (interval_insert_in_front_hooks,
1937 interval_insert_behind_hooks))
2e34157c 1938 call_mod_hooks (interval_insert_in_front_hooks, start, end);
318d2fa8
RS
1939}
1940\f
d418ef42
JA
1941void
1942syms_of_textprop ()
1943{
ad1b2f20 1944 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
c7dd82a3 1945 "Property-list used as default values.\n\
ad1b2f20
BG
1946The value of a property in this list is seen as the value for every\n\
1947character that does not have its own value for that property.");
1948 Vdefault_text_properties = Qnil;
c7dd82a3 1949
688a5a0f 1950 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
33d7d0df
RS
1951 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1952This also inhibits the use of the `intangible' text property.");
688a5a0f 1953 Vinhibit_point_motion_hooks = Qnil;
318d2fa8 1954
abc2f676
KH
1955 DEFVAR_LISP ("text-property-default-nonsticky",
1956 &Vtext_property_default_nonsticky,
1957 "Alist of properties vs the corresponding non-stickinesses.\n\
1958Each element has the form (PROPERTY . NONSTICKINESS).\n\
1959\n\
1960If a character in a buffer has PROPERTY, new text inserted adjacent to\n\
1961the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,\n\
1962inherits it if NONSTICKINESS is nil. The front-sticky and\n\
1963rear-nonsticky properties of the character overrides NONSTICKINESS.");
1964 Vtext_property_default_nonsticky = Qnil;
1965
318d2fa8
RS
1966 staticpro (&interval_insert_behind_hooks);
1967 staticpro (&interval_insert_in_front_hooks);
1968 interval_insert_behind_hooks = Qnil;
1969 interval_insert_in_front_hooks = Qnil;
1970
688a5a0f 1971
d418ef42
JA
1972 /* Common attributes one might give text */
1973
1974 staticpro (&Qforeground);
1975 Qforeground = intern ("foreground");
1976 staticpro (&Qbackground);
1977 Qbackground = intern ("background");
1978 staticpro (&Qfont);
1979 Qfont = intern ("font");
1980 staticpro (&Qstipple);
1981 Qstipple = intern ("stipple");
1982 staticpro (&Qunderline);
1983 Qunderline = intern ("underline");
1984 staticpro (&Qread_only);
1985 Qread_only = intern ("read-only");
1986 staticpro (&Qinvisible);
1987 Qinvisible = intern ("invisible");
46b4e741
KH
1988 staticpro (&Qintangible);
1989 Qintangible = intern ("intangible");
dc70cea7
RS
1990 staticpro (&Qcategory);
1991 Qcategory = intern ("category");
1992 staticpro (&Qlocal_map);
1993 Qlocal_map = intern ("local-map");
19e1c426
RS
1994 staticpro (&Qfront_sticky);
1995 Qfront_sticky = intern ("front-sticky");
1996 staticpro (&Qrear_nonsticky);
1997 Qrear_nonsticky = intern ("rear-nonsticky");
69bb837e
RS
1998 staticpro (&Qmouse_face);
1999 Qmouse_face = intern ("mouse-face");
d418ef42
JA
2000
2001 /* Properties that text might use to specify certain actions */
2002
2003 staticpro (&Qmouse_left);
2004 Qmouse_left = intern ("mouse-left");
2005 staticpro (&Qmouse_entered);
2006 Qmouse_entered = intern ("mouse-entered");
2007 staticpro (&Qpoint_left);
2008 Qpoint_left = intern ("point-left");
2009 staticpro (&Qpoint_entered);
2010 Qpoint_entered = intern ("point-entered");
d418ef42
JA
2011
2012 defsubr (&Stext_properties_at);
5fbe2a44 2013 defsubr (&Sget_text_property);
eb769fd7 2014 defsubr (&Sget_char_property);
fcab51aa
RS
2015 defsubr (&Snext_char_property_change);
2016 defsubr (&Sprevious_char_property_change);
b7e047fb
MB
2017 defsubr (&Snext_single_char_property_change);
2018 defsubr (&Sprevious_single_char_property_change);
d418ef42 2019 defsubr (&Snext_property_change);
9c79dd1b 2020 defsubr (&Snext_single_property_change);
d418ef42 2021 defsubr (&Sprevious_property_change);
9c79dd1b 2022 defsubr (&Sprevious_single_property_change);
d418ef42 2023 defsubr (&Sadd_text_properties);
d4b530ad 2024 defsubr (&Sput_text_property);
d418ef42
JA
2025 defsubr (&Sset_text_properties);
2026 defsubr (&Sremove_text_properties);
ad9c1940
JB
2027 defsubr (&Stext_property_any);
2028 defsubr (&Stext_property_not_all);
5fbe2a44 2029/* defsubr (&Serase_text_properties); */
15e4954b 2030/* defsubr (&Scopy_text_properties); */
d418ef42 2031}
25013c26 2032