diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 48 | ||||
| -rw-r--r-- | src/filelock.c | 141 | ||||
| -rw-r--r-- | src/intervals.h | 2 | ||||
| -rw-r--r-- | src/lisp.h | 2 | ||||
| -rw-r--r-- | src/textprop.c | 51 |
5 files changed, 158 insertions, 86 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 1b8b3c56004..adff00f7189 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,51 @@ | |||
| 1 | 2013-03-02 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | The lock for FILE is now .#FILE or .#-FILE (Bug#13807). | ||
| 4 | The old approach, which fell back on DIR/.#FILE.0 through | ||
| 5 | DIR/.#FILE.9, had race conditions that could not be easily fixed. | ||
| 6 | If DIR/.#FILE is a non-symlink file, Emacs now does not create a | ||
| 7 | lock file for DIR/FILE; that is, DIR/FILE is no longer partly | ||
| 8 | protected by a lock if DIR/.#FILE is a non-symlink file ("partly" | ||
| 9 | because the locking mechanism was never reliable in that case). | ||
| 10 | This patch fixes this and other bugs discovered by a code | ||
| 11 | inspection that was prompted by | ||
| 12 | <http://lists.gnu.org/archive/html/emacs-devel/2013-02/msg00531.html>. | ||
| 13 | Also, this patch switches to .#-FILE (not .#FILE) on MS-Windows, | ||
| 14 | to avoid interoperability problems between the MS-Windows and | ||
| 15 | non-MS-Windows implementations. MS-Windows and non-MS-Windows | ||
| 16 | instances of Emacs now ignore each others' locks. | ||
| 17 | * filelock.c (defined_WINDOWSNT): New constant. | ||
| 18 | (MAKE_LOCK_NAME, fill_in_lock_file_name): | ||
| 19 | Don't create DIR/.#FILE.0 through DIR/.#FILE.9. Instead, create | ||
| 20 | DIR/.#FILE symlinks on non-MS-Windows hosts, and DIR/.#-FILE | ||
| 21 | regular files on MS-Windows hosts. | ||
| 22 | (MAKE_LOCK_NAME, unlock_file, Ffile_locked_p): | ||
| 23 | Use SAFE_ALLOCA to avoid problems with long file names. | ||
| 24 | (MAX_LFINFO): Now a local constant, not a global macro. | ||
| 25 | (IS_LOCK_FILE): Remove. | ||
| 26 | (lock_file_1): Don't inspect errno if symlink call succeeds; | ||
| 27 | that's not portable. | ||
| 28 | (lock_file): Document that this function can return if lock | ||
| 29 | creation fails. | ||
| 30 | (lock_file): Don't access freed storage. | ||
| 31 | |||
| 32 | 2013-03-02 Andreas Schwab <schwab@linux-m68k.org> | ||
| 33 | |||
| 34 | * lisp.h (XPNTR) [!USE_LSB_TAG]: Remove extra paren. (Bug#13734) | ||
| 35 | |||
| 36 | 2013-03-02 Paul Eggert <eggert@cs.ucla.edu> | ||
| 37 | |||
| 38 | * textprop.c: Use bool for booleans. | ||
| 39 | (validate_interval_range, Fadd_text_properties) | ||
| 40 | (Fremove_text_properties): Prefer bool to int when either works. | ||
| 41 | |||
| 42 | 2013-03-02 Eli Zaretskii <eliz@gnu.org> | ||
| 43 | |||
| 44 | * textprop.c (Fadd_text_properties, Fremove_text_properties): If | ||
| 45 | the interval tree changes as a side effect of calling | ||
| 46 | modify_region, re-do processing starting from the call to | ||
| 47 | validate_interval_range. (Bug#13743) | ||
| 48 | |||
| 1 | 2013-02-28 Eli Zaretskii <eliz@gnu.org> | 49 | 2013-02-28 Eli Zaretskii <eliz@gnu.org> |
| 2 | 50 | ||
| 3 | * w32.c (sys_open): Don't reset the flags for FD in fd_info[]. | 51 | * w32.c (sys_open): Don't reset the flags for FD in fd_info[]. |
diff --git a/src/filelock.c b/src/filelock.c index 78cd60a12e1..14b9d4aaca5 100644 --- a/src/filelock.c +++ b/src/filelock.c | |||
| @@ -64,7 +64,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 64 | #define WTMP_FILE "/var/log/wtmp" | 64 | #define WTMP_FILE "/var/log/wtmp" |
| 65 | #endif | 65 | #endif |
| 66 | 66 | ||
| 67 | /* The strategy: to lock a file FN, create a symlink .#FN in FN's | 67 | /* On non-MS-Windows systems, use a symbolic link to represent a lock. |
| 68 | The strategy: to lock a file FN, create a symlink .#FN in FN's | ||
| 68 | directory, with link data `user@host.pid'. This avoids a single | 69 | directory, with link data `user@host.pid'. This avoids a single |
| 69 | mount (== failure) point for lock files. | 70 | mount (== failure) point for lock files. |
| 70 | 71 | ||
| @@ -97,7 +98,12 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 97 | has contributed this implementation for Emacs), and was designed by | 98 | has contributed this implementation for Emacs), and was designed by |
| 98 | Ethan Jacobson, Kimbo Mundy, and others. | 99 | Ethan Jacobson, Kimbo Mundy, and others. |
| 99 | 100 | ||
| 100 | --karl@cs.umb.edu/karl@hq.ileaf.com. */ | 101 | --karl@cs.umb.edu/karl@hq.ileaf.com. |
| 102 | |||
| 103 | On MS-Windows, symbolic links do not work well, so instead of a | ||
| 104 | symlink .#FN -> 'user@host.pid', the lock is a regular file .#-FN | ||
| 105 | with contents 'user@host.pid'. MS-Windows and non-MS-Windows | ||
| 106 | versions of Emacs ignore each other's locks. */ | ||
| 101 | 107 | ||
| 102 | 108 | ||
| 103 | /* Return the time of the last system boot. */ | 109 | /* Return the time of the last system boot. */ |
| @@ -291,55 +297,31 @@ typedef struct | |||
| 291 | /* Free the two dynamically-allocated pieces in PTR. */ | 297 | /* Free the two dynamically-allocated pieces in PTR. */ |
| 292 | #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) | 298 | #define FREE_LOCK_INFO(i) do { xfree ((i).user); xfree ((i).host); } while (0) |
| 293 | 299 | ||
| 294 | |||
| 295 | /* Write the name of the lock file for FNAME into LOCKNAME. Length | ||
| 296 | will be that of FN plus two more for the leading `.#' plus 1 for | ||
| 297 | the trailing period plus one for the digit after it plus one for | ||
| 298 | the null. */ | ||
| 299 | #define MAKE_LOCK_NAME(LOCKNAME, FNAME) \ | ||
| 300 | (LOCKNAME = alloca (SBYTES (FNAME) + 2 + 1 + 1 + 1), \ | ||
| 301 | fill_in_lock_file_name (LOCKNAME, (FNAME))) | ||
| 302 | |||
| 303 | #ifdef WINDOWSNT | 300 | #ifdef WINDOWSNT |
| 304 | /* 256 chars for user, 1024 chars for host, 10 digits for each of 2 int's. */ | 301 | enum { defined_WINDOWSNT = 1 }; |
| 305 | #define MAX_LFINFO (256 + 1024 + 10 + 10 + 2) | ||
| 306 | /* min size: .@PID */ | ||
| 307 | #define IS_LOCK_FILE(ST) (MAX_LFINFO >= (ST).st_size && (ST).st_size >= 3) | ||
| 308 | #else | 302 | #else |
| 309 | #define IS_LOCK_FILE(ST) S_ISLNK ((ST).st_mode) | 303 | enum { defined_WINDOWSNT = 0 }; |
| 310 | #endif | 304 | #endif |
| 311 | 305 | ||
| 306 | /* Write the name of the lock file for FNAME into LOCKNAME. Length | ||
| 307 | will be that of FNAME plus two more for the leading ".#", | ||
| 308 | plus one for "-" if MS-Windows, plus one for the null. */ | ||
| 309 | #define MAKE_LOCK_NAME(lockname, fname) \ | ||
| 310 | (lockname = SAFE_ALLOCA (SBYTES (fname) + 2 + defined_WINDOWSNT + 1), \ | ||
| 311 | fill_in_lock_file_name (lockname, fname)) | ||
| 312 | |||
| 312 | static void | 313 | static void |
| 313 | fill_in_lock_file_name (register char *lockfile, register Lisp_Object fn) | 314 | fill_in_lock_file_name (char *lockfile, Lisp_Object fn) |
| 314 | { | 315 | { |
| 315 | ptrdiff_t length = SBYTES (fn); | 316 | char *last_slash = memrchr (SSDATA (fn), '/', SBYTES (fn)); |
| 316 | register char *p; | 317 | char *base = last_slash + 1; |
| 317 | struct stat st; | 318 | ptrdiff_t dirlen = base - SSDATA (fn); |
| 318 | int count = 0; | 319 | memcpy (lockfile, SSDATA (fn), dirlen); |
| 319 | 320 | lockfile[dirlen] = '.'; | |
| 320 | strcpy (lockfile, SSDATA (fn)); | 321 | lockfile[dirlen + 1] = '#'; |
| 321 | 322 | if (defined_WINDOWSNT) | |
| 322 | /* Shift the nondirectory part of the file name (including the null) | 323 | lockfile[dirlen + 2] = '-'; |
| 323 | right two characters. Here is one of the places where we'd have to | 324 | strcpy (lockfile + dirlen + 2 + defined_WINDOWSNT, base); |
| 324 | do something to support 14-character-max file names. */ | ||
| 325 | for (p = lockfile + length; p != lockfile && *p != '/'; p--) | ||
| 326 | p[2] = *p; | ||
| 327 | |||
| 328 | /* Insert the `.#'. */ | ||
| 329 | p[1] = '.'; | ||
| 330 | p[2] = '#'; | ||
| 331 | |||
| 332 | p = lockfile + length + 2; | ||
| 333 | |||
| 334 | while (lstat (lockfile, &st) == 0 && !IS_LOCK_FILE (st)) | ||
| 335 | { | ||
| 336 | if (count > 9) | ||
| 337 | { | ||
| 338 | *p = '\0'; | ||
| 339 | return; | ||
| 340 | } | ||
| 341 | sprintf (p, ".%d", count++); | ||
| 342 | } | ||
| 343 | } | 325 | } |
| 344 | 326 | ||
| 345 | static int | 327 | static int |
| @@ -356,8 +338,8 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) | |||
| 356 | { | 338 | { |
| 357 | /* Deny everybody else any kind of access to the file until we are | 339 | /* Deny everybody else any kind of access to the file until we are |
| 358 | done writing it and close the handle. This makes the entire | 340 | done writing it and close the handle. This makes the entire |
| 359 | open/write/close operation atomic, as far as other processes | 341 | open/write/close operation atomic, as far as other WINDOWSNT |
| 360 | are concerned. */ | 342 | processes are concerned. */ |
| 361 | int fd = _sopen (lfname, | 343 | int fd = _sopen (lfname, |
| 362 | _O_WRONLY | _O_BINARY | _O_CREAT | _O_EXCL | _O_NOINHERIT, | 344 | _O_WRONLY | _O_BINARY | _O_CREAT | _O_EXCL | _O_NOINHERIT, |
| 363 | _SH_DENYRW, S_IREAD | S_IWRITE); | 345 | _SH_DENYRW, S_IREAD | S_IWRITE); |
| @@ -380,7 +362,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) | |||
| 380 | } | 362 | } |
| 381 | #else | 363 | #else |
| 382 | err = symlink (lock_info_str, lfname); | 364 | err = symlink (lock_info_str, lfname); |
| 383 | if (errno == EEXIST && force) | 365 | if (err != 0 && errno == EEXIST && force) |
| 384 | { | 366 | { |
| 385 | unlink (lfname); | 367 | unlink (lfname); |
| 386 | err = symlink (lock_info_str, lfname); | 368 | err = symlink (lock_info_str, lfname); |
| @@ -440,6 +422,8 @@ read_lock_data (char *lfname) | |||
| 440 | #else | 422 | #else |
| 441 | int fd = emacs_open (lfname, O_RDONLY | O_BINARY, S_IREAD); | 423 | int fd = emacs_open (lfname, O_RDONLY | O_BINARY, S_IREAD); |
| 442 | ssize_t nbytes; | 424 | ssize_t nbytes; |
| 425 | /* 256 chars for user, 1024 chars for host, 10 digits for each of 2 int's. */ | ||
| 426 | enum { MAX_LFINFO = 256 + 1024 + 10 + 10 + 2 }; | ||
| 443 | char lfinfo[MAX_LFINFO + 1]; | 427 | char lfinfo[MAX_LFINFO + 1]; |
| 444 | 428 | ||
| 445 | if (fd < 0) | 429 | if (fd < 0) |
| @@ -601,6 +585,7 @@ lock_if_free (lock_info_type *clasher, register char *lfname) | |||
| 601 | decided to go ahead without locking. | 585 | decided to go ahead without locking. |
| 602 | 586 | ||
| 603 | When this returns, either the lock is locked for us, | 587 | When this returns, either the lock is locked for us, |
| 588 | or lock creation failed, | ||
| 604 | or the user has said to go ahead without locking. | 589 | or the user has said to go ahead without locking. |
| 605 | 590 | ||
| 606 | If the file is locked by someone else, this calls | 591 | If the file is locked by someone else, this calls |
| @@ -612,11 +597,9 @@ lock_if_free (lock_info_type *clasher, register char *lfname) | |||
| 612 | void | 597 | void |
| 613 | lock_file (Lisp_Object fn) | 598 | lock_file (Lisp_Object fn) |
| 614 | { | 599 | { |
| 615 | register Lisp_Object attack, orig_fn, encoded_fn; | 600 | Lisp_Object orig_fn, encoded_fn; |
| 616 | register char *lfname, *locker; | 601 | char *lfname; |
| 617 | ptrdiff_t locker_size; | ||
| 618 | lock_info_type lock_info; | 602 | lock_info_type lock_info; |
| 619 | printmax_t pid; | ||
| 620 | struct gcpro gcpro1; | 603 | struct gcpro gcpro1; |
| 621 | USE_SAFE_ALLOCA; | 604 | USE_SAFE_ALLOCA; |
| 622 | 605 | ||
| @@ -657,38 +640,36 @@ lock_file (Lisp_Object fn) | |||
| 657 | call1 (intern ("ask-user-about-supersession-threat"), fn); | 640 | call1 (intern ("ask-user-about-supersession-threat"), fn); |
| 658 | 641 | ||
| 659 | } | 642 | } |
| 660 | UNGCPRO; | ||
| 661 | |||
| 662 | /* Try to lock the lock. */ | ||
| 663 | if (lock_if_free (&lock_info, lfname) <= 0) | ||
| 664 | /* Return now if we have locked it, or if lock creation failed */ | ||
| 665 | return; | ||
| 666 | 643 | ||
| 667 | /* Else consider breaking the lock */ | 644 | /* Try to lock the lock. */ |
| 668 | locker_size = (strlen (lock_info.user) + strlen (lock_info.host) | 645 | if (0 < lock_if_free (&lock_info, lfname)) |
| 669 | + INT_STRLEN_BOUND (printmax_t) | ||
| 670 | + sizeof "@ (pid )"); | ||
| 671 | locker = SAFE_ALLOCA (locker_size); | ||
| 672 | pid = lock_info.pid; | ||
| 673 | esprintf (locker, "%s@%s (pid %"pMd")", | ||
| 674 | lock_info.user, lock_info.host, pid); | ||
| 675 | FREE_LOCK_INFO (lock_info); | ||
| 676 | |||
| 677 | attack = call2 (intern ("ask-user-about-lock"), fn, build_string (locker)); | ||
| 678 | SAFE_FREE (); | ||
| 679 | if (!NILP (attack)) | ||
| 680 | /* User says take the lock */ | ||
| 681 | { | 646 | { |
| 682 | lock_file_1 (lfname, 1); | 647 | /* Someone else has the lock. Consider breaking it. */ |
| 683 | return; | 648 | ptrdiff_t locker_size = (strlen (lock_info.user) + strlen (lock_info.host) |
| 649 | + INT_STRLEN_BOUND (printmax_t) | ||
| 650 | + sizeof "@ (pid )"); | ||
| 651 | char *locker = SAFE_ALLOCA (locker_size); | ||
| 652 | printmax_t pid = lock_info.pid; | ||
| 653 | Lisp_Object attack; | ||
| 654 | esprintf (locker, "%s@%s (pid %"pMd")", | ||
| 655 | lock_info.user, lock_info.host, pid); | ||
| 656 | FREE_LOCK_INFO (lock_info); | ||
| 657 | |||
| 658 | attack = call2 (intern ("ask-user-about-lock"), fn, build_string (locker)); | ||
| 659 | /* Take the lock if the user said so. */ | ||
| 660 | if (!NILP (attack)) | ||
| 661 | lock_file_1 (lfname, 1); | ||
| 684 | } | 662 | } |
| 685 | /* User says ignore the lock */ | 663 | |
| 664 | UNGCPRO; | ||
| 665 | SAFE_FREE (); | ||
| 686 | } | 666 | } |
| 687 | 667 | ||
| 688 | void | 668 | void |
| 689 | unlock_file (register Lisp_Object fn) | 669 | unlock_file (Lisp_Object fn) |
| 690 | { | 670 | { |
| 691 | register char *lfname; | 671 | char *lfname; |
| 672 | USE_SAFE_ALLOCA; | ||
| 692 | 673 | ||
| 693 | fn = Fexpand_file_name (fn, Qnil); | 674 | fn = Fexpand_file_name (fn, Qnil); |
| 694 | fn = ENCODE_FILE (fn); | 675 | fn = ENCODE_FILE (fn); |
| @@ -697,6 +678,8 @@ unlock_file (register Lisp_Object fn) | |||
| 697 | 678 | ||
| 698 | if (current_lock_owner (0, lfname) == 2) | 679 | if (current_lock_owner (0, lfname) == 2) |
| 699 | unlink (lfname); | 680 | unlink (lfname); |
| 681 | |||
| 682 | SAFE_FREE (); | ||
| 700 | } | 683 | } |
| 701 | 684 | ||
| 702 | void | 685 | void |
| @@ -762,9 +745,10 @@ t if it is locked by you, else a string saying which user has locked it. */) | |||
| 762 | (Lisp_Object filename) | 745 | (Lisp_Object filename) |
| 763 | { | 746 | { |
| 764 | Lisp_Object ret; | 747 | Lisp_Object ret; |
| 765 | register char *lfname; | 748 | char *lfname; |
| 766 | int owner; | 749 | int owner; |
| 767 | lock_info_type locker; | 750 | lock_info_type locker; |
| 751 | USE_SAFE_ALLOCA; | ||
| 768 | 752 | ||
| 769 | filename = Fexpand_file_name (filename, Qnil); | 753 | filename = Fexpand_file_name (filename, Qnil); |
| 770 | 754 | ||
| @@ -781,6 +765,7 @@ t if it is locked by you, else a string saying which user has locked it. */) | |||
| 781 | if (owner > 0) | 765 | if (owner > 0) |
| 782 | FREE_LOCK_INFO (locker); | 766 | FREE_LOCK_INFO (locker); |
| 783 | 767 | ||
| 768 | SAFE_FREE (); | ||
| 784 | return ret; | 769 | return ret; |
| 785 | } | 770 | } |
| 786 | 771 | ||
diff --git a/src/intervals.h b/src/intervals.h index d6191225b1f..a38e83cf10e 100644 --- a/src/intervals.h +++ b/src/intervals.h | |||
| @@ -259,7 +259,7 @@ extern Lisp_Object get_local_map (ptrdiff_t, struct buffer *, Lisp_Object); | |||
| 259 | extern INTERVAL update_interval (INTERVAL, ptrdiff_t); | 259 | extern INTERVAL update_interval (INTERVAL, ptrdiff_t); |
| 260 | extern void set_intervals_multibyte (bool); | 260 | extern void set_intervals_multibyte (bool); |
| 261 | extern INTERVAL validate_interval_range (Lisp_Object, Lisp_Object *, | 261 | extern INTERVAL validate_interval_range (Lisp_Object, Lisp_Object *, |
| 262 | Lisp_Object *, int); | 262 | Lisp_Object *, bool); |
| 263 | extern INTERVAL interval_of (ptrdiff_t, Lisp_Object); | 263 | extern INTERVAL interval_of (ptrdiff_t, Lisp_Object); |
| 264 | 264 | ||
| 265 | /* Defined in xdisp.c. */ | 265 | /* Defined in xdisp.c. */ |
diff --git a/src/lisp.h b/src/lisp.h index 3d018b2b45e..f978a3b2fe0 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -512,7 +512,7 @@ static EMACS_INT const VALMASK | |||
| 512 | 512 | ||
| 513 | /* DATA_SEG_BITS forces extra bits to be or'd in with any pointers | 513 | /* DATA_SEG_BITS forces extra bits to be or'd in with any pointers |
| 514 | which were stored in a Lisp_Object. */ | 514 | which were stored in a Lisp_Object. */ |
| 515 | #define XPNTR(a) ((uintptr_t) ((XLI (a) & VALMASK)) | DATA_SEG_BITS)) | 515 | #define XPNTR(a) ((uintptr_t) ((XLI (a) & VALMASK) | DATA_SEG_BITS)) |
| 516 | 516 | ||
| 517 | #endif /* not USE_LSB_TAG */ | 517 | #endif /* not USE_LSB_TAG */ |
| 518 | 518 | ||
diff --git a/src/textprop.c b/src/textprop.c index 9499b53301f..18e893b3ef2 100644 --- a/src/textprop.c +++ b/src/textprop.c | |||
| @@ -125,9 +125,10 @@ modify_region (Lisp_Object buffer, Lisp_Object start, Lisp_Object end) | |||
| 125 | #define hard 1 | 125 | #define hard 1 |
| 126 | 126 | ||
| 127 | INTERVAL | 127 | INTERVAL |
| 128 | validate_interval_range (Lisp_Object object, Lisp_Object *begin, Lisp_Object *end, int force) | 128 | validate_interval_range (Lisp_Object object, Lisp_Object *begin, |
| 129 | Lisp_Object *end, bool force) | ||
| 129 | { | 130 | { |
| 130 | register INTERVAL i; | 131 | INTERVAL i; |
| 131 | ptrdiff_t searchpos; | 132 | ptrdiff_t searchpos; |
| 132 | 133 | ||
| 133 | CHECK_STRING_OR_BUFFER (object); | 134 | CHECK_STRING_OR_BUFFER (object); |
| @@ -1131,6 +1132,7 @@ Return t if any property value actually changed, nil otherwise. */) | |||
| 1131 | ptrdiff_t s, len; | 1132 | ptrdiff_t s, len; |
| 1132 | bool modified = 0; | 1133 | bool modified = 0; |
| 1133 | struct gcpro gcpro1; | 1134 | struct gcpro gcpro1; |
| 1135 | bool first_time = 1; | ||
| 1134 | 1136 | ||
| 1135 | properties = validate_plist (properties); | 1137 | properties = validate_plist (properties); |
| 1136 | if (NILP (properties)) | 1138 | if (NILP (properties)) |
| @@ -1139,6 +1141,7 @@ Return t if any property value actually changed, nil otherwise. */) | |||
| 1139 | if (NILP (object)) | 1141 | if (NILP (object)) |
| 1140 | XSETBUFFER (object, current_buffer); | 1142 | XSETBUFFER (object, current_buffer); |
| 1141 | 1143 | ||
| 1144 | retry: | ||
| 1142 | i = validate_interval_range (object, &start, &end, hard); | 1145 | i = validate_interval_range (object, &start, &end, hard); |
| 1143 | if (!i) | 1146 | if (!i) |
| 1144 | return Qnil; | 1147 | return Qnil; |
| @@ -1174,8 +1177,25 @@ Return t if any property value actually changed, nil otherwise. */) | |||
| 1174 | copy_properties (unchanged, i); | 1177 | copy_properties (unchanged, i); |
| 1175 | } | 1178 | } |
| 1176 | 1179 | ||
| 1177 | if (BUFFERP (object)) | 1180 | if (BUFFERP (object) && first_time) |
| 1178 | modify_region (object, start, end); | 1181 | { |
| 1182 | ptrdiff_t prev_total_length = TOTAL_LENGTH (i); | ||
| 1183 | ptrdiff_t prev_pos = i->position; | ||
| 1184 | |||
| 1185 | modify_region (object, start, end); | ||
| 1186 | /* If someone called us recursively as a side effect of | ||
| 1187 | modify_region, and changed the intervals behind our back | ||
| 1188 | (could happen if lock_file, called by prepare_to_modify_buffer, | ||
| 1189 | triggers redisplay, and that calls add-text-properties again | ||
| 1190 | in the same buffer), we cannot continue with I, because its | ||
| 1191 | data changed. So we restart the interval analysis anew. */ | ||
| 1192 | if (TOTAL_LENGTH (i) != prev_total_length | ||
| 1193 | || i->position != prev_pos) | ||
| 1194 | { | ||
| 1195 | first_time = 0; | ||
| 1196 | goto retry; | ||
| 1197 | } | ||
| 1198 | } | ||
| 1179 | 1199 | ||
| 1180 | /* We are at the beginning of interval I, with LEN chars to scan. */ | 1200 | /* We are at the beginning of interval I, with LEN chars to scan. */ |
| 1181 | for (;;) | 1201 | for (;;) |
| @@ -1427,10 +1447,12 @@ Use `set-text-properties' if you want to remove all text properties. */) | |||
| 1427 | INTERVAL i, unchanged; | 1447 | INTERVAL i, unchanged; |
| 1428 | ptrdiff_t s, len; | 1448 | ptrdiff_t s, len; |
| 1429 | bool modified = 0; | 1449 | bool modified = 0; |
| 1450 | bool first_time = 1; | ||
| 1430 | 1451 | ||
| 1431 | if (NILP (object)) | 1452 | if (NILP (object)) |
| 1432 | XSETBUFFER (object, current_buffer); | 1453 | XSETBUFFER (object, current_buffer); |
| 1433 | 1454 | ||
| 1455 | retry: | ||
| 1434 | i = validate_interval_range (object, &start, &end, soft); | 1456 | i = validate_interval_range (object, &start, &end, soft); |
| 1435 | if (!i) | 1457 | if (!i) |
| 1436 | return Qnil; | 1458 | return Qnil; |
| @@ -1462,8 +1484,25 @@ Use `set-text-properties' if you want to remove all text properties. */) | |||
| 1462 | copy_properties (unchanged, i); | 1484 | copy_properties (unchanged, i); |
| 1463 | } | 1485 | } |
| 1464 | 1486 | ||
| 1465 | if (BUFFERP (object)) | 1487 | if (BUFFERP (object) && first_time) |
| 1466 | modify_region (object, start, end); | 1488 | { |
| 1489 | ptrdiff_t prev_total_length = TOTAL_LENGTH (i); | ||
| 1490 | ptrdiff_t prev_pos = i->position; | ||
| 1491 | |||
| 1492 | modify_region (object, start, end); | ||
| 1493 | /* If someone called us recursively as a side effect of | ||
| 1494 | modify_region, and changed the intervals behind our back | ||
| 1495 | (could happen if lock_file, called by prepare_to_modify_buffer, | ||
| 1496 | triggers redisplay, and that calls add-text-properties again | ||
| 1497 | in the same buffer), we cannot continue with I, because its | ||
| 1498 | data changed. So we restart the interval analysis anew. */ | ||
| 1499 | if (TOTAL_LENGTH (i) != prev_total_length | ||
| 1500 | || i->position != prev_pos) | ||
| 1501 | { | ||
| 1502 | first_time = 0; | ||
| 1503 | goto retry; | ||
| 1504 | } | ||
| 1505 | } | ||
| 1467 | 1506 | ||
| 1468 | /* We are at the beginning of an interval, with len to scan */ | 1507 | /* We are at the beginning of an interval, with len to scan */ |
| 1469 | for (;;) | 1508 | for (;;) |