diff options
| author | Michael Albinus | 2013-07-04 11:39:36 +0200 |
|---|---|---|
| committer | Michael Albinus | 2013-07-04 11:39:36 +0200 |
| commit | 864c58ca5f32d564d79707b862cfba0b9cf7107e (patch) | |
| tree | 257ade009531572963b7c987a12f4b05212b924c | |
| parent | 86dfb7a8155ba4705f6bdc8e9be3a38388ad207e (diff) | |
| download | emacs-864c58ca5f32d564d79707b862cfba0b9cf7107e.tar.gz emacs-864c58ca5f32d564d79707b862cfba0b9cf7107e.zip | |
* filenotify.el: New package.
* autorevert.el (top): Require filenotify.el.
(auto-revert-notify-enabled): Remove. Use `file-notify-support'
instead.
(auto-revert-notify-rm-watch, auto-revert-notify-add-watch)
(auto-revert-notify-handler): Use `file-notify-*' functions.
* subr.el (file-notify-handle-event): Move function to filenotify.el.
* net/tramp.el (tramp-file-name-for-operation): Handle
`file-notify-add-watch' and `file-notify-rm-watch'.
* net/tramp-sh.el (tramp-sh-file-name-handler-alist): Add handler
for `file-notify-add-watch' and `file-notify-rm-watch'.
(tramp-process-sentinel): Improve trace.
(tramp-sh-handle-file-notify-add-watch)
(tramp-sh-file-notify-process-filter)
(tramp-sh-handle-file-notify-rm-watch)
(tramp-get-remote-inotifywait): New defuns.
| -rw-r--r-- | lisp/ChangeLog | 27 | ||||
| -rw-r--r-- | lisp/autorevert.el | 158 | ||||
| -rw-r--r-- | lisp/filenotify.el | 324 | ||||
| -rw-r--r-- | lisp/net/tramp-sh.el | 68 | ||||
| -rw-r--r-- | lisp/net/tramp.el | 6 | ||||
| -rw-r--r-- | lisp/subr.el | 14 |
6 files changed, 474 insertions, 123 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 8a54c5ac370..7921f77ca05 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,26 @@ | |||
| 1 | 2013-07-04 Michael Albinus <michael.albinus@gmx.de> | ||
| 2 | |||
| 3 | * filenotify.el: New package. | ||
| 4 | |||
| 5 | * autorevert.el (top): Require filenotify.el. | ||
| 6 | (auto-revert-notify-enabled): Remove. Use `file-notify-support' | ||
| 7 | instead. | ||
| 8 | (auto-revert-notify-rm-watch, auto-revert-notify-add-watch) | ||
| 9 | (auto-revert-notify-handler): Use `file-notify-*' functions. | ||
| 10 | |||
| 11 | * subr.el (file-notify-handle-event): Move function to filenotify.el. | ||
| 12 | |||
| 13 | * net/tramp.el (tramp-file-name-for-operation): Handle | ||
| 14 | `file-notify-add-watch' and `file-notify-rm-watch'. | ||
| 15 | |||
| 16 | * net/tramp-sh.el (tramp-sh-file-name-handler-alist): Add handler | ||
| 17 | for `file-notify-add-watch' and `file-notify-rm-watch'. | ||
| 18 | (tramp-process-sentinel): Improve trace. | ||
| 19 | (tramp-sh-handle-file-notify-add-watch) | ||
| 20 | (tramp-sh-file-notify-process-filter) | ||
| 21 | (tramp-sh-handle-file-notify-rm-watch) | ||
| 22 | (tramp-get-remote-inotifywait): New defuns. | ||
| 23 | |||
| 1 | 2013-07-03 Juri Linkov <juri@jurta.org> | 24 | 2013-07-03 Juri Linkov <juri@jurta.org> |
| 2 | 25 | ||
| 3 | * buff-menu.el (Buffer-menu-multi-occur): Add args and move the | 26 | * buff-menu.el (Buffer-menu-multi-occur): Add args and move the |
| @@ -299,12 +322,12 @@ | |||
| 299 | 322 | ||
| 300 | 2013-06-25 Rüdiger Sonderfeld <ruediger@c-plusplus.de> | 323 | 2013-06-25 Rüdiger Sonderfeld <ruediger@c-plusplus.de> |
| 301 | 324 | ||
| 302 | * lisp/textmodes/bibtex.el (bibtex-generate-url-list): Add support | 325 | * textmodes/bibtex.el (bibtex-generate-url-list): Add support |
| 303 | for DOI URLs. | 326 | for DOI URLs. |
| 304 | 327 | ||
| 305 | 2013-06-25 Rüdiger Sonderfeld <ruediger@c-plusplus.de> | 328 | 2013-06-25 Rüdiger Sonderfeld <ruediger@c-plusplus.de> |
| 306 | 329 | ||
| 307 | * lisp/textmodes/bibtex.el (bibtex-mode, bibtex-set-dialect): | 330 | * textmodes/bibtex.el (bibtex-mode, bibtex-set-dialect): |
| 308 | Update imenu-support when dialect changes. | 331 | Update imenu-support when dialect changes. |
| 309 | 332 | ||
| 310 | 2013-06-25 Leo Liu <sdl.web@gmail.com> | 333 | 2013-06-25 Leo Liu <sdl.web@gmail.com> |
diff --git a/lisp/autorevert.el b/lisp/autorevert.el index 4a6d4cb4cc0..00e88fc4a3d 100644 --- a/lisp/autorevert.el +++ b/lisp/autorevert.el | |||
| @@ -103,6 +103,7 @@ | |||
| 103 | 103 | ||
| 104 | (eval-when-compile (require 'cl-lib)) | 104 | (eval-when-compile (require 'cl-lib)) |
| 105 | (require 'timer) | 105 | (require 'timer) |
| 106 | (require 'filenotify) | ||
| 106 | 107 | ||
| 107 | ;; Custom Group: | 108 | ;; Custom Group: |
| 108 | ;; | 109 | ;; |
| @@ -270,21 +271,17 @@ This variable becomes buffer local when set in any fashion.") | |||
| 270 | :type 'boolean | 271 | :type 'boolean |
| 271 | :version "24.4") | 272 | :version "24.4") |
| 272 | 273 | ||
| 273 | (defconst auto-revert-notify-enabled | 274 | (defcustom auto-revert-use-notify (and file-notify-support t) |
| 274 | (or (featurep 'gfilenotify) (featurep 'inotify) (featurep 'w32notify)) | ||
| 275 | "Non-nil when Emacs has been compiled with file notification support.") | ||
| 276 | |||
| 277 | (defcustom auto-revert-use-notify auto-revert-notify-enabled | ||
| 278 | "If non-nil Auto Revert Mode uses file notification functions. | 275 | "If non-nil Auto Revert Mode uses file notification functions. |
| 279 | This requires Emacs being compiled with file notification | 276 | This requires Emacs being compiled with file notification |
| 280 | support (see `auto-revert-notify-enabled'). You should set this | 277 | support (see `file-notify-support'). You should set this variable |
| 281 | variable through Custom." | 278 | through Custom." |
| 282 | :group 'auto-revert | 279 | :group 'auto-revert |
| 283 | :type 'boolean | 280 | :type 'boolean |
| 284 | :set (lambda (variable value) | 281 | :set (lambda (variable value) |
| 285 | (set-default variable (and auto-revert-notify-enabled value)) | 282 | (set-default variable (and file-notify-support value)) |
| 286 | (unless (symbol-value variable) | 283 | (unless (symbol-value variable) |
| 287 | (when auto-revert-notify-enabled | 284 | (when file-notify-support |
| 288 | (dolist (buf (buffer-list)) | 285 | (dolist (buf (buffer-list)) |
| 289 | (with-current-buffer buf | 286 | (with-current-buffer buf |
| 290 | (when (symbol-value 'auto-revert-notify-watch-descriptor) | 287 | (when (symbol-value 'auto-revert-notify-watch-descriptor) |
| @@ -502,12 +499,7 @@ will use an up-to-date value of `auto-revert-interval'" | |||
| 502 | (puthash key value auto-revert-notify-watch-descriptor-hash-list) | 499 | (puthash key value auto-revert-notify-watch-descriptor-hash-list) |
| 503 | (remhash key auto-revert-notify-watch-descriptor-hash-list) | 500 | (remhash key auto-revert-notify-watch-descriptor-hash-list) |
| 504 | (ignore-errors | 501 | (ignore-errors |
| 505 | (funcall | 502 | (file-notify-rm-watch auto-revert-notify-watch-descriptor))))) |
| 506 | (cond | ||
| 507 | ((fboundp 'gfile-rm-watch) 'gfile-rm-watch) | ||
| 508 | ((fboundp 'inotify-rm-watch) 'inotify-rm-watch) | ||
| 509 | ((fboundp 'w32notify-rm-watch) 'w32notify-rm-watch)) | ||
| 510 | auto-revert-notify-watch-descriptor))))) | ||
| 511 | auto-revert-notify-watch-descriptor-hash-list) | 503 | auto-revert-notify-watch-descriptor-hash-list) |
| 512 | (remove-hook 'kill-buffer-hook 'auto-revert-notify-rm-watch)) | 504 | (remove-hook 'kill-buffer-hook 'auto-revert-notify-rm-watch)) |
| 513 | (setq auto-revert-notify-watch-descriptor nil | 505 | (setq auto-revert-notify-watch-descriptor nil |
| @@ -522,100 +514,58 @@ will use an up-to-date value of `auto-revert-interval'" | |||
| 522 | 514 | ||
| 523 | (when (and buffer-file-name auto-revert-use-notify | 515 | (when (and buffer-file-name auto-revert-use-notify |
| 524 | (not auto-revert-notify-watch-descriptor)) | 516 | (not auto-revert-notify-watch-descriptor)) |
| 525 | (let ((func | 517 | (setq auto-revert-notify-watch-descriptor |
| 526 | (cond | 518 | (ignore-errors |
| 527 | ((fboundp 'gfile-add-watch) 'gfile-add-watch) | 519 | (file-notify-add-watch |
| 528 | ((fboundp 'inotify-add-watch) 'inotify-add-watch) | 520 | (expand-file-name buffer-file-name default-directory) |
| 529 | ((fboundp 'w32notify-add-watch) 'w32notify-add-watch))) | 521 | '(change attribute-change) 'auto-revert-notify-handler))) |
| 530 | (aspect | 522 | (if auto-revert-notify-watch-descriptor |
| 531 | (cond | 523 | (progn |
| 532 | ((fboundp 'gfile-add-watch) '(watch-mounts)) | 524 | (puthash |
| 533 | ;; `attrib' is needed for file modification time. | 525 | auto-revert-notify-watch-descriptor |
| 534 | ((fboundp 'inotify-add-watch) '(attrib create modify moved-to)) | 526 | (cons (current-buffer) |
| 535 | ((fboundp 'w32notify-add-watch) '(size last-write-time)))) | 527 | (gethash auto-revert-notify-watch-descriptor |
| 536 | (file (if (or (fboundp 'gfile-add-watch) (fboundp 'inotify-add-watch)) | 528 | auto-revert-notify-watch-descriptor-hash-list)) |
| 537 | (directory-file-name (expand-file-name default-directory)) | 529 | auto-revert-notify-watch-descriptor-hash-list) |
| 538 | (buffer-file-name)))) | 530 | (add-hook (make-local-variable 'kill-buffer-hook) |
| 539 | (setq auto-revert-notify-watch-descriptor | 531 | 'auto-revert-notify-rm-watch)) |
| 540 | (ignore-errors | 532 | ;; Fallback to file checks. |
| 541 | (funcall func file aspect 'auto-revert-notify-handler))) | 533 | (set (make-local-variable 'auto-revert-use-notify) nil)))) |
| 542 | (if auto-revert-notify-watch-descriptor | ||
| 543 | (progn | ||
| 544 | (puthash | ||
| 545 | auto-revert-notify-watch-descriptor | ||
| 546 | (cons (current-buffer) | ||
| 547 | (gethash auto-revert-notify-watch-descriptor | ||
| 548 | auto-revert-notify-watch-descriptor-hash-list)) | ||
| 549 | auto-revert-notify-watch-descriptor-hash-list) | ||
| 550 | (add-hook (make-local-variable 'kill-buffer-hook) | ||
| 551 | 'auto-revert-notify-rm-watch)) | ||
| 552 | ;; Fallback to file checks. | ||
| 553 | (set (make-local-variable 'auto-revert-use-notify) nil))))) | ||
| 554 | |||
| 555 | (defun auto-revert-notify-event-p (event) | ||
| 556 | "Check that event is a file notification event." | ||
| 557 | (and (listp event) | ||
| 558 | (cond ((featurep 'gfilenotify) | ||
| 559 | (and (>= (length event) 3) (stringp (nth 2 event)))) | ||
| 560 | ((featurep 'inotify) | ||
| 561 | (= (length event) 4)) | ||
| 562 | ((featurep 'w32notify) | ||
| 563 | (and (= (length event) 3) (stringp (nth 2 event))))))) | ||
| 564 | |||
| 565 | (defun auto-revert-notify-event-descriptor (event) | ||
| 566 | "Return watch descriptor of file notification event, or nil." | ||
| 567 | (and (auto-revert-notify-event-p event) (car event))) | ||
| 568 | |||
| 569 | (defun auto-revert-notify-event-action (event) | ||
| 570 | "Return action of file notification event, or nil." | ||
| 571 | (and (auto-revert-notify-event-p event) (nth 1 event))) | ||
| 572 | |||
| 573 | (defun auto-revert-notify-event-file-name (event) | ||
| 574 | "Return file name of file notification event, or nil." | ||
| 575 | (and (auto-revert-notify-event-p event) | ||
| 576 | (cond ((featurep 'gfilenotify) (nth 2 event)) | ||
| 577 | ((featurep 'inotify) (nth 3 event)) | ||
| 578 | ((featurep 'w32notify) (nth 2 event))))) | ||
| 579 | 534 | ||
| 580 | (defun auto-revert-notify-handler (event) | 535 | (defun auto-revert-notify-handler (event) |
| 581 | "Handle an EVENT returned from file notification." | 536 | "Handle an EVENT returned from file notification." |
| 582 | (when (auto-revert-notify-event-p event) | 537 | (ignore-errors |
| 583 | (let* ((descriptor (auto-revert-notify-event-descriptor event)) | 538 | (let* ((descriptor (car event)) |
| 584 | (action (auto-revert-notify-event-action event)) | 539 | (action (nth 1 event)) |
| 585 | (file (auto-revert-notify-event-file-name event)) | 540 | (file (nth 2 event)) |
| 541 | (file1 (nth 3 event)) ;; Target of `renamed'. | ||
| 586 | (buffers (gethash descriptor | 542 | (buffers (gethash descriptor |
| 587 | auto-revert-notify-watch-descriptor-hash-list))) | 543 | auto-revert-notify-watch-descriptor-hash-list))) |
| 588 | (ignore-errors | 544 | ;; Check, that event is meant for us. |
| 589 | ;; Check, that event is meant for us. | 545 | (cl-assert descriptor) |
| 590 | ;; TODO: Filter events which stop watching, like `move' or `removed'. | 546 | ;; We do not handle `deleted', because nothing has to be refreshed. |
| 591 | (cl-assert descriptor) | 547 | (cl-assert (memq action '(attribute-changed changed created renamed)) t) |
| 592 | (cond | 548 | ;; Since we watch a directory, a file name must be returned. |
| 593 | ((featurep 'gfilenotify) | 549 | (cl-assert (stringp file)) |
| 594 | (cl-assert (memq action '(attribute-changed changed created deleted | 550 | (when (eq action 'renamed) (cl-assert (stringp file1))) |
| 595 | ;; FIXME: I keep getting this action, so I | 551 | ;; Loop over all buffers, in order to find the intended one. |
| 596 | ;; added it here, but I have no idea what | 552 | (dolist (buffer buffers) |
| 597 | ;; I'm doing. --Stef | 553 | (when (buffer-live-p buffer) |
| 598 | changes-done-hint)) | 554 | (with-current-buffer buffer |
| 599 | t)) | 555 | (when (and (stringp buffer-file-name) |
| 600 | ((featurep 'inotify) | 556 | (or |
| 601 | (cl-assert (or (memq 'attrib action) | 557 | (and (memq action '(attribute-changed changed created)) |
| 602 | (memq 'create action) | 558 | (string-equal |
| 603 | (memq 'modify action) | 559 | (file-name-nondirectory file) |
| 604 | (memq 'moved-to action)))) | 560 | (file-name-nondirectory buffer-file-name))) |
| 605 | ((featurep 'w32notify) (cl-assert (eq 'modified action)))) | 561 | (and (eq action 'renamed) |
| 606 | ;; Since we watch a directory, a file name must be returned. | 562 | (string-equal |
| 607 | (cl-assert (stringp file)) | 563 | (file-name-nondirectory file1) |
| 608 | (dolist (buffer buffers) | 564 | (file-name-nondirectory buffer-file-name))))) |
| 609 | (when (buffer-live-p buffer) | 565 | ;; Mark buffer modified. |
| 610 | (with-current-buffer buffer | 566 | (setq auto-revert-notify-modified-p t) |
| 611 | (when (and (stringp buffer-file-name) | 567 | ;; No need to check other buffers. |
| 612 | (string-equal | 568 | (cl-return)))))))) |
| 613 | (file-name-nondirectory file) | ||
| 614 | (file-name-nondirectory buffer-file-name))) | ||
| 615 | ;; Mark buffer modified. | ||
| 616 | (setq auto-revert-notify-modified-p t) | ||
| 617 | ;; No need to check other buffers. | ||
| 618 | (cl-return))))))))) | ||
| 619 | 569 | ||
| 620 | (defun auto-revert-active-p () | 570 | (defun auto-revert-active-p () |
| 621 | "Check if auto-revert is active (in current buffer or globally)." | 571 | "Check if auto-revert is active (in current buffer or globally)." |
diff --git a/lisp/filenotify.el b/lisp/filenotify.el new file mode 100644 index 00000000000..e170db2dd5f --- /dev/null +++ b/lisp/filenotify.el | |||
| @@ -0,0 +1,324 @@ | |||
| 1 | ;;; filenotify.el --- watch files for changes on disk | ||
| 2 | |||
| 3 | ;; Copyright (C) 2013 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Michael Albinus <michael.albinus@gmx.de> | ||
| 6 | |||
| 7 | ;; This file is part of GNU Emacs. | ||
| 8 | |||
| 9 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 10 | ;; it under the terms of the GNU General Public License as published by | ||
| 11 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 12 | ;; (at your option) any later version. | ||
| 13 | |||
| 14 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | ;; GNU General Public License for more details. | ||
| 18 | |||
| 19 | ;; You should have received a copy of the GNU General Public License | ||
| 20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | |||
| 22 | ;;; Commentary | ||
| 23 | |||
| 24 | ;; This package is an abstraction layer from the different low-level | ||
| 25 | ;; file notification packages `gfilenotify', `inotify' and | ||
| 26 | ;; `w32notify'. | ||
| 27 | |||
| 28 | ;;; Code: | ||
| 29 | |||
| 30 | ;;;###autoload | ||
| 31 | (defconst file-notify-support | ||
| 32 | (cond | ||
| 33 | ((featurep 'gfilenotify) 'gfilenotify) | ||
| 34 | ((featurep 'inotify) 'inotify) | ||
| 35 | ((featurep 'w32notify) 'w32notify)) | ||
| 36 | "Non-nil when Emacs has been compiled with file notification support. | ||
| 37 | The value is the name of the low-level file notification package | ||
| 38 | to be used for local file systems. Remote file notifications | ||
| 39 | could use another implementation.") | ||
| 40 | |||
| 41 | (defvar file-notify-descriptors (make-hash-table :test 'equal) | ||
| 42 | "Hash table for registered file notification descriptors. | ||
| 43 | A key in this hash table is the descriptor as returned from | ||
| 44 | `gfilenotify', `inotify', `w32notify' or a file name handler. | ||
| 45 | The value in the hash table is the cons cell (DIR FILE CALLBACK).") | ||
| 46 | |||
| 47 | ;; This function is used by `gfilenotify', `inotify' and `w32notify' events. | ||
| 48 | ;;;###autoload | ||
| 49 | (defun file-notify-handle-event (event) | ||
| 50 | "Handle file system monitoring event. | ||
| 51 | If EVENT is a filewatch event, call its callback. | ||
| 52 | Otherwise, signal a `file-notify-error'." | ||
| 53 | (interactive "e") | ||
| 54 | (if (and (eq (car event) 'file-notify) | ||
| 55 | (>= (length event) 3)) | ||
| 56 | (funcall (nth 2 event) (nth 1 event)) | ||
| 57 | (signal 'file-notify-error | ||
| 58 | (cons "Not a valid file-notify event" event)))) | ||
| 59 | |||
| 60 | (defvar file-notify--pending-events nil | ||
| 61 | "List of pending file notification events for a future `renamed' action. | ||
| 62 | The entries are a list (DESCRIPTOR ACTION FILE COOKIE). ACTION | ||
| 63 | is either `moved-from' or `renamed-from'.") | ||
| 64 | |||
| 65 | (defun file-notify--event-file-name (event) | ||
| 66 | "Return file name of file notification event, or nil." | ||
| 67 | (expand-file-name | ||
| 68 | (or (and (stringp (nth 2 event)) (nth 2 event)) "") | ||
| 69 | (car (gethash (car event) file-notify-descriptors)))) | ||
| 70 | |||
| 71 | ;; Only `gfilenotify' could return two file names. | ||
| 72 | (defun file-notify--event-file1-name (event) | ||
| 73 | "Return second file name of file notification event, or nil. | ||
| 74 | This is available in case a file has been moved." | ||
| 75 | (and (stringp (nth 3 event)) | ||
| 76 | (expand-file-name | ||
| 77 | (nth 3 event) (car (gethash (car event) file-notify-descriptors))))) | ||
| 78 | |||
| 79 | ;; Cookies are offered by `inotify' only. | ||
| 80 | (defun file-notify--event-cookie (event) | ||
| 81 | "Return cookie of file notification event, or nil. | ||
| 82 | This is available in case a file has been moved." | ||
| 83 | (nth 3 event)) | ||
| 84 | |||
| 85 | ;; The callback function used to map between specific flags of the | ||
| 86 | ;; respective file notifications, and the ones we return. | ||
| 87 | (defun file-notify-callback (event) | ||
| 88 | "Handle an EVENT returned from file notification. | ||
| 89 | EVENT is the same one as in `file-notify-handle-event' except the | ||
| 90 | car of that event, which is the symbol `file-notify'." | ||
| 91 | (let* ((desc (car event)) | ||
| 92 | (registered (gethash desc file-notify-descriptors)) | ||
| 93 | (pending-event (assoc desc file-notify--pending-events)) | ||
| 94 | (actions (nth 1 event)) | ||
| 95 | (file (file-notify--event-file-name event)) | ||
| 96 | file1 cookie callback) | ||
| 97 | |||
| 98 | ;; Make actions a list. | ||
| 99 | (unless (consp actions) (setq actions (cons actions nil))) | ||
| 100 | |||
| 101 | ;; Check, that event is meant for us. | ||
| 102 | (unless (setq callback (nth 2 registered)) | ||
| 103 | (setq actions nil)) | ||
| 104 | |||
| 105 | ;; Loop over actions. In fact, more than one action happens only | ||
| 106 | ;; for `inotify'. | ||
| 107 | (dolist (action actions) | ||
| 108 | |||
| 109 | ;; Send pending event, if it doesn't match. | ||
| 110 | (when (and pending-event | ||
| 111 | ;; The cookie doesn't match. | ||
| 112 | (not (eq (file-notify--event-cookie pending-event) | ||
| 113 | (file-notify--event-cookie event))) | ||
| 114 | (or | ||
| 115 | ;; inotify. | ||
| 116 | (and (eq (nth 1 pending-event) 'moved-from) | ||
| 117 | (not (eq action 'moved-to))) | ||
| 118 | ;; w32notify. | ||
| 119 | (and (eq (nth 1 pending-event) 'renamed-from) | ||
| 120 | (not (eq action 'renamed-to))))) | ||
| 121 | (funcall callback | ||
| 122 | (list desc 'deleted | ||
| 123 | (file-notify--event-file-name pending-event))) | ||
| 124 | (setq file-notify--pending-events | ||
| 125 | (delete pending-event file-notify--pending-events))) | ||
| 126 | |||
| 127 | ;; Map action. We ignore all events which cannot be mapped. | ||
| 128 | (setq action | ||
| 129 | (cond | ||
| 130 | ;; gfilenotify. | ||
| 131 | ((memq action '(attribute-changed changed created deleted)) action) | ||
| 132 | ((eq action 'moved) | ||
| 133 | (setq file1 (file-notify--event-file1-name event)) | ||
| 134 | 'renamed) | ||
| 135 | |||
| 136 | ;; inotify. | ||
| 137 | ((eq action 'attrib) 'attribute-changed) | ||
| 138 | ((eq action 'create) 'created) | ||
| 139 | ((eq action 'modify) 'changed) | ||
| 140 | ((memq action '(delete 'delete-self move-self)) 'deleted) | ||
| 141 | ;; Make the event pending. | ||
| 142 | ((eq action 'moved-from) | ||
| 143 | (add-to-list 'file-notify--pending-events | ||
| 144 | (list desc action file | ||
| 145 | (file-notify--event-cookie event))) | ||
| 146 | nil) | ||
| 147 | ;; Look for pending event. | ||
| 148 | ((eq action 'moved-to) | ||
| 149 | (if (null pending-event) | ||
| 150 | 'created | ||
| 151 | (setq file1 file | ||
| 152 | file (file-notify--event-file-name pending-event) | ||
| 153 | file-notify--pending-events | ||
| 154 | (delete pending-event file-notify--pending-events)) | ||
| 155 | 'renamed)) | ||
| 156 | |||
| 157 | ;; w32notify. | ||
| 158 | ((eq action 'added) 'created) | ||
| 159 | ((eq action 'modified) 'changed) | ||
| 160 | ((eq action 'removed) 'deleted) | ||
| 161 | ;; Make the event pending. | ||
| 162 | ((eq 'renamed-from action) | ||
| 163 | (add-to-list 'file-notify--pending-events | ||
| 164 | (list desc action file | ||
| 165 | (file-notify--event-cookie event))) | ||
| 166 | nil) | ||
| 167 | ;; Look for pending event. | ||
| 168 | ((eq 'renamed-to action) | ||
| 169 | (if (null pending-event) | ||
| 170 | 'created | ||
| 171 | (setq file1 file | ||
| 172 | file (file-notify--event-file-name pending-event) | ||
| 173 | file-notify--pending-events | ||
| 174 | (delete pending-event file-notify--pending-events)) | ||
| 175 | 'renamed)))) | ||
| 176 | |||
| 177 | ;; Apply callback. | ||
| 178 | (when (and action | ||
| 179 | (or | ||
| 180 | ;; If there is no relative file name for that watch, | ||
| 181 | ;; we watch the whole directory. | ||
| 182 | (null (nth 1 registered)) | ||
| 183 | ;; File matches. | ||
| 184 | (string-equal | ||
| 185 | (nth 1 registered) (file-name-nondirectory file)) | ||
| 186 | ;; File1 matches. | ||
| 187 | (and (stringp file1) | ||
| 188 | (string-equal | ||
| 189 | (nth 1 registered) (file-name-nondirectory file1))))) | ||
| 190 | (if file1 | ||
| 191 | (funcall callback (list desc action file file1)) | ||
| 192 | (funcall callback (list desc action file))))))) | ||
| 193 | |||
| 194 | (defun file-notify-add-watch (file flags callback) | ||
| 195 | "Add a watch for filesystem events pertaining to FILE. | ||
| 196 | This arranges for filesystem events pertaining to FILE to be reported | ||
| 197 | to Emacs. Use `file-notify-rm-watch' to cancel the watch. | ||
| 198 | |||
| 199 | The returned value is a descriptor for the added watch. If the | ||
| 200 | file cannot be watched for some reason, this function signals a | ||
| 201 | `file-notify-error' error. | ||
| 202 | |||
| 203 | FLAGS is a list of conditions to set what will be watched for. It can | ||
| 204 | include the following symbols: | ||
| 205 | |||
| 206 | `change' -- watch for file changes | ||
| 207 | `attribute-change' -- watch for file attributes changes, like | ||
| 208 | permissions or modification time | ||
| 209 | |||
| 210 | If FILE is a directory, 'change' watches for file creation or | ||
| 211 | deletion in that directory. | ||
| 212 | |||
| 213 | When any event happens, Emacs will call the CALLBACK function passing | ||
| 214 | it a single argument EVENT, which is of the form | ||
| 215 | |||
| 216 | (DESCRIPTOR ACTION FILE [FILE1]) | ||
| 217 | |||
| 218 | DESCRIPTOR is the same object as the one returned by this function. | ||
| 219 | ACTION is the description of the event. It could be any one of the | ||
| 220 | following: | ||
| 221 | |||
| 222 | `created' -- FILE was created | ||
| 223 | `deleted' -- FILE was deleted | ||
| 224 | `changed' -- FILE has changed | ||
| 225 | `renamed' -- FILE has been renamed to FILE1 | ||
| 226 | `attribute-changed' -- a FILE attribute was changed | ||
| 227 | |||
| 228 | FILE is the name of the file whose event is being reported." | ||
| 229 | ;; Check arguments. | ||
| 230 | (unless (stringp file) | ||
| 231 | (signal 'wrong-type-argument (list file))) | ||
| 232 | (setq file (expand-file-name file)) | ||
| 233 | (unless (and (consp flags) | ||
| 234 | (null (delq 'change (delq 'attribute-change (copy-tree flags))))) | ||
| 235 | (signal 'wrong-type-argument (list flags))) | ||
| 236 | (unless (functionp callback) | ||
| 237 | (signal 'wrong-type-argument (list callback))) | ||
| 238 | |||
| 239 | (let* ((handler (find-file-name-handler file 'file-notify-add-watch)) | ||
| 240 | (dir (directory-file-name | ||
| 241 | (if (or (and (not handler) (eq file-notify-support 'w32notify)) | ||
| 242 | (file-directory-p file)) | ||
| 243 | file | ||
| 244 | (file-name-directory file)))) | ||
| 245 | desc func l-flags) | ||
| 246 | |||
| 247 | ;; Check, whether this has been registered already. | ||
| 248 | ; (maphash | ||
| 249 | ; (lambda (key value) | ||
| 250 | ; (when (equal (cons file callback) value) (setq desc key))) | ||
| 251 | ; file-notify-descriptors) | ||
| 252 | |||
| 253 | (unless desc | ||
| 254 | (if handler | ||
| 255 | ;; A file name handler could exist even if there is no local | ||
| 256 | ;; file notification support. | ||
| 257 | (setq desc (funcall | ||
| 258 | handler 'file-notify-add-watch dir flags callback)) | ||
| 259 | |||
| 260 | ;; Check, whether Emacs has been compiled with file | ||
| 261 | ;; notification support. | ||
| 262 | (unless file-notify-support | ||
| 263 | (signal 'file-notify-error | ||
| 264 | '("No file notification package available"))) | ||
| 265 | |||
| 266 | ;; Determine low-level function to be called. | ||
| 267 | (setq func (cond | ||
| 268 | ((eq file-notify-support 'gfilenotify) 'gfile-add-watch) | ||
| 269 | ((eq file-notify-support 'inotify) 'inotify-add-watch) | ||
| 270 | ((eq file-notify-support 'w32notify) 'w32notify-add-watch))) | ||
| 271 | |||
| 272 | ;; Determine respective flags. | ||
| 273 | (if (eq file-notify-support 'gfilenotify) | ||
| 274 | (setq l-flags '(watch-mounts send-moved)) | ||
| 275 | (when (memq 'change flags) | ||
| 276 | (setq | ||
| 277 | l-flags | ||
| 278 | (cond | ||
| 279 | ((eq file-notify-support 'inotify) '(create modify move delete)) | ||
| 280 | ((eq file-notify-support 'w32notify) | ||
| 281 | '(file-name directory-name size last-write-time))))) | ||
| 282 | (when (memq 'attribute-change flags) | ||
| 283 | (add-to-list | ||
| 284 | 'l-flags | ||
| 285 | (cond | ||
| 286 | ((eq file-notify-support 'inotify) 'attrib) | ||
| 287 | ((eq file-notify-support 'w32notify) 'attributes))))) | ||
| 288 | |||
| 289 | ;; Call low-level function. | ||
| 290 | (setq desc (funcall func dir l-flags 'file-notify-callback)))) | ||
| 291 | |||
| 292 | ;; Return descriptor. | ||
| 293 | (puthash desc | ||
| 294 | (list (directory-file-name | ||
| 295 | (if (file-directory-p dir) dir (file-name-directory dir))) | ||
| 296 | (unless (file-directory-p file) | ||
| 297 | (file-name-nondirectory file)) | ||
| 298 | callback) | ||
| 299 | file-notify-descriptors) | ||
| 300 | desc)) | ||
| 301 | |||
| 302 | (defun file-notify-rm-watch (descriptor) | ||
| 303 | "Remove an existing watch specified by its DESCRIPTOR. | ||
| 304 | DESCRIPTOR should be an object returned by `file-notify-add-watch'." | ||
| 305 | (let ((file (car (gethash descriptor file-notify-descriptors))) | ||
| 306 | handler) | ||
| 307 | |||
| 308 | (when (stringp file) | ||
| 309 | (setq handler (find-file-name-handler file 'file-notify-rm-watch)) | ||
| 310 | (if handler | ||
| 311 | (funcall handler 'file-notify-rm-watch descriptor) | ||
| 312 | (funcall | ||
| 313 | (cond | ||
| 314 | ((eq file-notify-support 'gfilenotify) 'gfile-rm-watch) | ||
| 315 | ((eq file-notify-support 'inotify) 'inotify-rm-watch) | ||
| 316 | ((eq file-notify-support 'w32notify) 'w32notify-rm-watch)) | ||
| 317 | descriptor))) | ||
| 318 | |||
| 319 | (remhash descriptor file-notify-descriptors))) | ||
| 320 | |||
| 321 | ;; The end: | ||
| 322 | (provide 'filenotify) | ||
| 323 | |||
| 324 | ;;; filenotify.el ends here | ||
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 387084a807b..f402e2b2774 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el | |||
| @@ -862,7 +862,9 @@ of command line.") | |||
| 862 | (set-file-selinux-context . tramp-sh-handle-set-file-selinux-context) | 862 | (set-file-selinux-context . tramp-sh-handle-set-file-selinux-context) |
| 863 | (file-acl . tramp-sh-handle-file-acl) | 863 | (file-acl . tramp-sh-handle-file-acl) |
| 864 | (set-file-acl . tramp-sh-handle-set-file-acl) | 864 | (set-file-acl . tramp-sh-handle-set-file-acl) |
| 865 | (vc-registered . tramp-sh-handle-vc-registered)) | 865 | (vc-registered . tramp-sh-handle-vc-registered) |
| 866 | (file-notify-add-watch . tramp-sh-handle-file-notify-add-watch) | ||
| 867 | (file-notify-rm-watch . tramp-sh-handle-file-notify-rm-watch)) | ||
| 866 | "Alist of handler functions. | 868 | "Alist of handler functions. |
| 867 | Operations not mentioned here will be handled by the normal Emacs functions.") | 869 | Operations not mentioned here will be handled by the normal Emacs functions.") |
| 868 | 870 | ||
| @@ -2669,7 +2671,7 @@ the result will be a local, non-Tramp, filename." | |||
| 2669 | (unless (memq (process-status proc) '(run open)) | 2671 | (unless (memq (process-status proc) '(run open)) |
| 2670 | (let ((vec (tramp-get-connection-property proc "vector" nil))) | 2672 | (let ((vec (tramp-get-connection-property proc "vector" nil))) |
| 2671 | (when vec | 2673 | (when vec |
| 2672 | (tramp-message vec 5 "Sentinel called: `%s' `%s'" proc event) | 2674 | (tramp-message vec 5 "Sentinel called: `%S' `%s'" proc event) |
| 2673 | (tramp-flush-connection-property proc) | 2675 | (tramp-flush-connection-property proc) |
| 2674 | (tramp-flush-directory-property vec ""))))) | 2676 | (tramp-flush-directory-property vec ""))))) |
| 2675 | 2677 | ||
| @@ -3376,6 +3378,63 @@ Fall back to normal file name handler if no Tramp handler exists." | |||
| 3376 | ;; Default file name handlers, we don't care. | 3378 | ;; Default file name handlers, we don't care. |
| 3377 | (t (tramp-run-real-handler operation args))))))) | 3379 | (t (tramp-run-real-handler operation args))))))) |
| 3378 | 3380 | ||
| 3381 | ;; We use inotify for implementation. It is more likely to exist than glib. | ||
| 3382 | (defun tramp-sh-handle-file-notify-add-watch (file-name flags callback) | ||
| 3383 | "Like `file-notify-add-watch' for Tramp files." | ||
| 3384 | (setq file-name (expand-file-name file-name)) | ||
| 3385 | (with-parsed-tramp-file-name file-name nil | ||
| 3386 | (let* ((default-directory (file-name-directory file-name)) | ||
| 3387 | (command (tramp-get-remote-inotifywait v)) | ||
| 3388 | (events | ||
| 3389 | (cond | ||
| 3390 | ((and (memq 'change flags) (memq 'attribute-change flags)) | ||
| 3391 | "create,modify,move,delete,attrib") | ||
| 3392 | ((memq 'change flags) "create,modify,move,delete") | ||
| 3393 | ((memq 'attribute-change flags) "attrib"))) | ||
| 3394 | (p (and command | ||
| 3395 | (start-file-process | ||
| 3396 | "inotifywait" (generate-new-buffer " *inotifywait*") | ||
| 3397 | command "-mq" "-e" events localname)))) | ||
| 3398 | ;; Return the process object as watch-descriptor. | ||
| 3399 | (if (not (processp p)) | ||
| 3400 | (tramp-error | ||
| 3401 | v 'file-notify-error "`inotifywait' not found on remote host") | ||
| 3402 | (tramp-compat-set-process-query-on-exit-flag p nil) | ||
| 3403 | (set-process-filter p 'tramp-sh-file-notify-process-filter) | ||
| 3404 | p)))) | ||
| 3405 | |||
| 3406 | (defun tramp-sh-file-notify-process-filter (proc string) | ||
| 3407 | "Read output from \"inotifywait\" and add corresponding file-notify events." | ||
| 3408 | (tramp-message proc 6 (format "%S\n%s" proc string)) | ||
| 3409 | (dolist (line (split-string string "[\n\r]+" 'omit-nulls)) | ||
| 3410 | ;; Check, whether there is a problem. | ||
| 3411 | (unless | ||
| 3412 | (string-match | ||
| 3413 | "^[^[:blank:]]+[[:blank:]]+\\([^[:blank:]]+\\)+\\([[:blank:]]+\\([^[:blank:]]+\\)\\)?[[:blank:]]*$" line) | ||
| 3414 | (tramp-error proc 'file-notify-error "%s" line)) | ||
| 3415 | |||
| 3416 | ;; Usually, we would add an Emacs event now. Unfortunately, | ||
| 3417 | ;; `unread-command-events' does not accept several events at once. | ||
| 3418 | ;; Therefore, we apply the callback directly. | ||
| 3419 | (let* ((object | ||
| 3420 | (list | ||
| 3421 | proc | ||
| 3422 | (mapcar | ||
| 3423 | (lambda (x) | ||
| 3424 | (intern-soft (replace-regexp-in-string "_" "-" (downcase x)))) | ||
| 3425 | (split-string (match-string 1 line) "," 'omit-nulls)) | ||
| 3426 | (match-string 3 line)))) | ||
| 3427 | (tramp-compat-funcall 'file-notify-callback object)))) | ||
| 3428 | |||
| 3429 | (defvar file-notify-descriptors) | ||
| 3430 | (defun tramp-sh-handle-file-notify-rm-watch (proc) | ||
| 3431 | "Like `file-notify-rm-watch' for Tramp files." | ||
| 3432 | ;; The descriptor must be a process object. | ||
| 3433 | (unless (and (processp proc) (gethash proc file-notify-descriptors)) | ||
| 3434 | (tramp-error proc 'file-notify-error "Not a valid descriptor %S" proc)) | ||
| 3435 | (tramp-message proc 6 (format "Kill %S" proc)) | ||
| 3436 | (kill-process proc)) | ||
| 3437 | |||
| 3379 | ;;; Internal Functions: | 3438 | ;;; Internal Functions: |
| 3380 | 3439 | ||
| 3381 | (defun tramp-maybe-send-script (vec script name) | 3440 | (defun tramp-maybe-send-script (vec script name) |
| @@ -4864,6 +4923,11 @@ Return ATTR." | |||
| 4864 | (tramp-message vec 5 "Finding a suitable `trash' command") | 4923 | (tramp-message vec 5 "Finding a suitable `trash' command") |
| 4865 | (tramp-find-executable vec "trash" (tramp-get-remote-path vec)))) | 4924 | (tramp-find-executable vec "trash" (tramp-get-remote-path vec)))) |
| 4866 | 4925 | ||
| 4926 | (defun tramp-get-remote-inotifywait (vec) | ||
| 4927 | (with-tramp-connection-property vec "inotifywait" | ||
| 4928 | (tramp-message vec 5 "Finding a suitable `inotifywait' command") | ||
| 4929 | (tramp-find-executable vec "inotifywait" (tramp-get-remote-path vec) t t))) | ||
| 4930 | |||
| 4867 | (defun tramp-get-remote-id (vec) | 4931 | (defun tramp-get-remote-id (vec) |
| 4868 | (with-tramp-connection-property vec "id" | 4932 | (with-tramp-connection-property vec "id" |
| 4869 | (tramp-message vec 5 "Finding POSIX `id' command") | 4933 | (tramp-message vec 5 "Finding POSIX `id' command") |
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 4ec3a4b7829..8b19a7ca5d3 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el | |||
| @@ -1964,7 +1964,7 @@ ARGS are the arguments OPERATION has been called with." | |||
| 1964 | ;; Emacs 22+ only. | 1964 | ;; Emacs 22+ only. |
| 1965 | 'set-file-times | 1965 | 'set-file-times |
| 1966 | ;; Emacs 24+ only. | 1966 | ;; Emacs 24+ only. |
| 1967 | 'file-acl 'file-selinux-context | 1967 | 'file-acl 'file-notify-add-watch 'file-selinux-context |
| 1968 | 'set-file-acl 'set-file-selinux-context | 1968 | 'set-file-acl 'set-file-selinux-context |
| 1969 | ;; XEmacs only. | 1969 | ;; XEmacs only. |
| 1970 | 'abbreviate-file-name 'create-file-buffer | 1970 | 'abbreviate-file-name 'create-file-buffer |
| @@ -2018,6 +2018,10 @@ ARGS are the arguments OPERATION has been called with." | |||
| 2018 | ;; XEmacs only. | 2018 | ;; XEmacs only. |
| 2019 | 'dired-print-file 'dired-shell-call-process)) | 2019 | 'dired-print-file 'dired-shell-call-process)) |
| 2020 | default-directory) | 2020 | default-directory) |
| 2021 | ;; PROC. | ||
| 2022 | ((eq operation 'file-notify-rm-watch) | ||
| 2023 | (with-current-buffer (process-buffer (nth 0 args)) | ||
| 2024 | default-directory)) | ||
| 2021 | ;; Unknown file primitive. | 2025 | ;; Unknown file primitive. |
| 2022 | (t (error "unknown file I/O primitive: %s" operation)))) | 2026 | (t (error "unknown file I/O primitive: %s" operation)))) |
| 2023 | 2027 | ||
diff --git a/lisp/subr.el b/lisp/subr.el index 55cdcb45f50..f8262eb7f6d 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -4496,20 +4496,6 @@ convenience wrapper around `make-progress-reporter' and friends. | |||
| 4496 | nil ,@(cdr (cdr spec))))) | 4496 | nil ,@(cdr (cdr spec))))) |
| 4497 | 4497 | ||
| 4498 | 4498 | ||
| 4499 | ;;;; Support for watching filesystem events. | ||
| 4500 | |||
| 4501 | (defun file-notify-handle-event (event) | ||
| 4502 | "Handle file system monitoring event. | ||
| 4503 | If EVENT is a filewatch event, call its callback. | ||
| 4504 | Otherwise, signal a `filewatch-error'." | ||
| 4505 | (interactive "e") | ||
| 4506 | (if (and (eq (car event) 'file-notify) | ||
| 4507 | (>= (length event) 3)) | ||
| 4508 | (funcall (nth 2 event) (nth 1 event)) | ||
| 4509 | (signal 'filewatch-error | ||
| 4510 | (cons "Not a valid file-notify event" event)))) | ||
| 4511 | |||
| 4512 | |||
| 4513 | ;;;; Comparing version strings. | 4499 | ;;;; Comparing version strings. |
| 4514 | 4500 | ||
| 4515 | (defconst version-separator "." | 4501 | (defconst version-separator "." |