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