From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Michael Albinus Newsgroups: gmane.emacs.devel Subject: filenotify.el (2) Date: Thu, 27 Jun 2013 14:18:28 +0200 Message-ID: <87r4fn7nmz.fsf@gmx.de> References: <87d2ra9z4x.fsf@gmx.de> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1372335535 30067 80.91.229.3 (27 Jun 2013 12:18:55 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 27 Jun 2013 12:18:55 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Jun 27 14:18:56 2013 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1UsBA3-0001of-EN for ged-emacs-devel@m.gmane.org; Thu, 27 Jun 2013 14:18:51 +0200 Original-Received: from localhost ([::1]:42917 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UsBA3-0005zx-0X for ged-emacs-devel@m.gmane.org; Thu, 27 Jun 2013 08:18:51 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:37736) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UsB9v-0005zl-OI for emacs-devel@gnu.org; Thu, 27 Jun 2013 08:18:48 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UsB9r-0008Uz-V2 for emacs-devel@gnu.org; Thu, 27 Jun 2013 08:18:43 -0400 Original-Received: from mout.gmx.net ([212.227.17.22]:49185) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UsB9r-0008Un-C5 for emacs-devel@gnu.org; Thu, 27 Jun 2013 08:18:39 -0400 Original-Received: from mailout-de.gmx.net ([10.1.76.4]) by mrigmx.server.lan (mrigmx002) with ESMTP (Nemesis) id 0MMqOH-1Uu6pG1WJD-008ZqT for ; Thu, 27 Jun 2013 14:18:37 +0200 Original-Received: (qmail invoked by alias); 27 Jun 2013 12:18:32 -0000 Original-Received: from p4FE64F97.dip0.t-ipconnect.de (EHLO detlef.gmx.de) [79.230.79.151] by mail.gmx.net (mp004) with SMTP; 27 Jun 2013 14:18:32 +0200 X-Authenticated: #3708877 X-Provags-ID: V01U2FsdGVkX18U4ThWVwQUsQPGOI3l9kqSl7caLXSIAhAR8J/aGB 3dSKHDg8+knRMj In-Reply-To: <87d2ra9z4x.fsf@gmx.de> (Michael Albinus's message of "Tue, 25 Jun 2013 14:02:38 +0200") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3.50 (gnu/linux) X-Y-GMX-Trusted: 0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 212.227.17.22 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:161134 Archived-At: --=-=-= Content-Type: text/plain Hi, Enclosed is the updated version of the patch. I have incorporated the comments, and I have also modified inotify.c and gfilenotify.c in order to use `file-notify-error'. This shall also be changed in w32notify.c; since I cannot test the build on Windows I haven't done it yet. I have also started to write test/automated/file-notify-tests.el, but this is far from being presentable yet. Best regards, Michael. --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename=filenotify.diff Content-Transfer-Encoding: quoted-printable =3D=3D=3D modified file 'lisp/ChangeLog' *** lisp/ChangeLog 2013-06-27 09:26:54 +0000 --- lisp/ChangeLog 2013-06-27 12:03:14 +0000 *************** *** 1,3 **** --- 1,15 ---- + 2013-06-27 Michael Albinus +=20 + * filenotify.el: New package. +=20 + * 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. +=20 + * subr.el (file-notify-handle-event): Move function to filenotify.el. +=20 2013-06-27 Dmitry Gutov =20=20 * emacs-lisp/package-x.el (package-upload-buffer-internal): Adapt *************** *** 153,164 **** =20=20 2013-06-25 R=C3=BCdiger Sonderfeld =20=20 ! * lisp/textmodes/bibtex.el (bibtex-generate-url-list): Add support for DOI URLs. =20=20 2013-06-25 R=C3=BCdiger Sonderfeld =20=20 ! * lisp/textmodes/bibtex.el (bibtex-mode, bibtex-set-dialect): Update imenu-support when dialect changes. =20=20 2013-06-25 Leo Liu --- 165,176 ---- =20=20 2013-06-25 R=C3=BCdiger Sonderfeld =20=20 ! * textmodes/bibtex.el (bibtex-generate-url-list): Add support for DOI URLs. =20=20 2013-06-25 R=C3=BCdiger Sonderfeld =20=20 ! * textmodes/bibtex.el (bibtex-mode, bibtex-set-dialect): Update imenu-support when dialect changes. =20=20 2013-06-25 Leo Liu =3D=3D=3D modified file 'lisp/autorevert.el' *** lisp/autorevert.el 2013-06-05 19:57:10 +0000 --- lisp/autorevert.el 2013-06-27 09:00:44 +0000 *************** *** 103,108 **** --- 103,109 ---- =20=20 (eval-when-compile (require 'cl-lib)) (require 'timer) + (require 'filenotify) =20=20 ;; Custom Group: ;; *************** *** 270,290 **** :type 'boolean :version "24.4") =20=20 ! (defconst auto-revert-notify-enabled ! (or (featurep 'gfilenotify) (featurep 'inotify) (featurep 'w32notify)) ! "Non-nil when Emacs has been compiled with file notification support.") !=20 ! (defcustom auto-revert-use-notify auto-revert-notify-enabled "If non-nil Auto Revert Mode uses file notification functions. This requires Emacs being compiled with file notification ! support (see `auto-revert-notify-enabled'). You should set this ! variable through Custom." :group 'auto-revert :type 'boolean :set (lambda (variable value) ! (set-default variable (and auto-revert-notify-enabled value)) (unless (symbol-value variable) ! (when auto-revert-notify-enabled (dolist (buf (buffer-list)) (with-current-buffer buf (when (symbol-value 'auto-revert-notify-watch-descriptor) --- 271,287 ---- :type 'boolean :version "24.4") =20=20 ! (defcustom auto-revert-use-notify (and file-notify-support t) "If non-nil Auto Revert Mode uses file notification functions. This requires Emacs being compiled with file notification ! support (see `file-notify-support'). You should set this variable ! through Custom." :group 'auto-revert :type 'boolean :set (lambda (variable value) ! (set-default variable (and file-notify-support value)) (unless (symbol-value variable) ! (when file-notify-support (dolist (buf (buffer-list)) (with-current-buffer buf (when (symbol-value 'auto-revert-notify-watch-descriptor) *************** *** 502,513 **** (puthash key value auto-revert-notify-watch-descriptor-hash-list) (remhash key auto-revert-notify-watch-descriptor-hash-list) (ignore-errors ! (funcall ! (cond ! ((fboundp 'gfile-rm-watch) 'gfile-rm-watch) ! ((fboundp 'inotify-rm-watch) 'inotify-rm-watch) ! ((fboundp 'w32notify-rm-watch) 'w32notify-rm-watch)) ! auto-revert-notify-watch-descriptor))))) auto-revert-notify-watch-descriptor-hash-list) (remove-hook 'kill-buffer-hook 'auto-revert-notify-rm-watch)) (setq auto-revert-notify-watch-descriptor nil --- 499,505 ---- (puthash key value auto-revert-notify-watch-descriptor-hash-list) (remhash key auto-revert-notify-watch-descriptor-hash-list) (ignore-errors ! (file-notify-rm-watch auto-revert-notify-watch-descriptor))))) auto-revert-notify-watch-descriptor-hash-list) (remove-hook 'kill-buffer-hook 'auto-revert-notify-rm-watch)) (setq auto-revert-notify-watch-descriptor nil *************** *** 522,621 **** =20=20 (when (and buffer-file-name auto-revert-use-notify (not auto-revert-notify-watch-descriptor)) ! (let ((func ! (cond ! ((fboundp 'gfile-add-watch) 'gfile-add-watch) ! ((fboundp 'inotify-add-watch) 'inotify-add-watch) ! ((fboundp 'w32notify-add-watch) 'w32notify-add-watch))) ! (aspect ! (cond ! ((fboundp 'gfile-add-watch) '(watch-mounts)) ! ;; `attrib' is needed for file modification time. ! ((fboundp 'inotify-add-watch) '(attrib create modify moved-to)) ! ((fboundp 'w32notify-add-watch) '(size last-write-time)))) ! (file (if (or (fboundp 'gfile-add-watch) (fboundp 'inotify-add-watch)) ! (directory-file-name (expand-file-name default-directory)) ! (buffer-file-name)))) ! (setq auto-revert-notify-watch-descriptor ! (ignore-errors ! (funcall func file aspect 'auto-revert-notify-handler))) ! (if auto-revert-notify-watch-descriptor ! (progn ! (puthash ! auto-revert-notify-watch-descriptor ! (cons (current-buffer) ! (gethash auto-revert-notify-watch-descriptor ! auto-revert-notify-watch-descriptor-hash-list)) ! auto-revert-notify-watch-descriptor-hash-list) ! (add-hook (make-local-variable 'kill-buffer-hook) ! 'auto-revert-notify-rm-watch)) ! ;; Fallback to file checks. ! (set (make-local-variable 'auto-revert-use-notify) nil))))) !=20 ! (defun auto-revert-notify-event-p (event) ! "Check that event is a file notification event." ! (and (listp event) ! (cond ((featurep 'gfilenotify) ! (and (>=3D (length event) 3) (stringp (nth 2 event)))) ! ((featurep 'inotify) ! (=3D (length event) 4)) ! ((featurep 'w32notify) ! (and (=3D (length event) 3) (stringp (nth 2 event))))))) !=20 ! (defun auto-revert-notify-event-descriptor (event) ! "Return watch descriptor of file notification event, or nil." ! (and (auto-revert-notify-event-p event) (car event))) !=20 ! (defun auto-revert-notify-event-action (event) ! "Return action of file notification event, or nil." ! (and (auto-revert-notify-event-p event) (nth 1 event))) !=20 ! (defun auto-revert-notify-event-file-name (event) ! "Return file name of file notification event, or nil." ! (and (auto-revert-notify-event-p event) ! (cond ((featurep 'gfilenotify) (nth 2 event)) ! ((featurep 'inotify) (nth 3 event)) ! ((featurep 'w32notify) (nth 2 event))))) =20=20 (defun auto-revert-notify-handler (event) "Handle an EVENT returned from file notification." ! (when (auto-revert-notify-event-p event) ! (let* ((descriptor (auto-revert-notify-event-descriptor event)) ! (action (auto-revert-notify-event-action event)) ! (file (auto-revert-notify-event-file-name event)) (buffers (gethash descriptor auto-revert-notify-watch-descriptor-hash-list))) ! (ignore-errors ! ;; Check, that event is meant for us. ! ;; TODO: Filter events which stop watching, like `move' or `removed'. ! (cl-assert descriptor) ! (cond ! ((featurep 'gfilenotify) ! (cl-assert (memq action '(attribute-changed changed created deleted ! ;; FIXME: I keep getting this action,= so I ! ;; added it here, but I have no idea = what ! ;; I'm doing. --Stef ! changes-done-hint)) ! t)) ! ((featurep 'inotify) ! (cl-assert (or (memq 'attrib action) ! (memq 'create action) ! (memq 'modify action) ! (memq 'moved-to action)))) ! ((featurep 'w32notify) (cl-assert (eq 'modified action)))) ! ;; Since we watch a directory, a file name must be returned. ! (cl-assert (stringp file)) ! (dolist (buffer buffers) ! (when (buffer-live-p buffer) ! (with-current-buffer buffer ! (when (and (stringp buffer-file-name) ! (string-equal ! (file-name-nondirectory file) ! (file-name-nondirectory buffer-file-name))) ! ;; Mark buffer modified. ! (setq auto-revert-notify-modified-p t) ! ;; No need to check other buffers. ! (cl-return))))))))) =20=20 (defun auto-revert-active-p () "Check if auto-revert is active (in current buffer or globally)." --- 514,571 ---- =20=20 (when (and buffer-file-name auto-revert-use-notify (not auto-revert-notify-watch-descriptor)) ! (setq auto-revert-notify-watch-descriptor ! (ignore-errors ! (file-notify-add-watch ! (directory-file-name (expand-file-name default-directory)) ! '(change attribute-change) 'auto-revert-notify-handler))) ! (if auto-revert-notify-watch-descriptor ! (progn ! (puthash ! auto-revert-notify-watch-descriptor ! (cons (current-buffer) ! (gethash auto-revert-notify-watch-descriptor ! auto-revert-notify-watch-descriptor-hash-list)) ! auto-revert-notify-watch-descriptor-hash-list) ! (add-hook (make-local-variable 'kill-buffer-hook) ! 'auto-revert-notify-rm-watch)) ! ;; Fallback to file checks. ! (set (make-local-variable 'auto-revert-use-notify) nil)))) =20=20 (defun auto-revert-notify-handler (event) "Handle an EVENT returned from file notification." ! (ignore-errors ! (let* ((descriptor (car event)) ! (action (nth 1 event)) ! (file (nth 2 event)) ! (file1 (nth 3 event)) ;; Target of `renamed'. (buffers (gethash descriptor auto-revert-notify-watch-descriptor-hash-list))) ! ;; Check, that event is meant for us. ! (cl-assert descriptor) ! ;; We do not handle `deleted', because nothing has to be refreshed. ! (cl-assert (memq action '(attribute-changed changed created renamed= )) t) ! ;; Since we watch a directory, a file name must be returned. ! (cl-assert (stringp file)) ! (when (eq action 'renamed) (cl-assert (stringp file1))) ! ;; Loop over all buffers, in order to find the intended one. ! (dolist (buffer buffers) ! (when (buffer-live-p buffer) ! (with-current-buffer buffer ! (when (and (stringp buffer-file-name) ! (or ! (and (memq action '(attribute-changed changed created)) ! (string-equal ! (file-name-nondirectory file) ! (file-name-nondirectory buffer-file-name))) ! (and (eq action 'renamed) ! (string-equal ! (file-name-nondirectory file1) ! (file-name-nondirectory buffer-file-name))))) ! ;; Mark buffer modified. ! (setq auto-revert-notify-modified-p t) ! ;; No need to check other buffers. ! (cl-return)))))))) =20=20 (defun auto-revert-active-p () "Check if auto-revert is active (in current buffer or globally)." =3D=3D=3D added file 'lisp/filenotify.el' *** lisp/filenotify.el 1970-01-01 00:00:00 +0000 --- lisp/filenotify.el 2013-06-27 06:49:24 +0000 *************** *** 0 **** --- 1,292 ---- + ;;; filenotify.el --- watch files for changes on disk +=20 + ;; Copyright (C) 2013 Free Software Foundation, Inc. +=20 + ;; Author: Michael Albinus +=20 + ;; This file is part of GNU Emacs. +=20 + ;; GNU Emacs is free software: you can redistribute it and/or modify + ;; it under the terms of the GNU General Public License as published by + ;; the Free Software Foundation, either version 3 of the License, or + ;; (at your option) any later version. +=20 + ;; GNU Emacs is distributed in the hope that it will be useful, + ;; but WITHOUT ANY WARRANTY; without even the implied warranty of + ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + ;; GNU General Public License for more details. +=20 + ;; You should have received a copy of the GNU General Public License + ;; along with GNU Emacs. If not, see . +=20 + ;;; Commentary +=20 + ;; This package is an abstraction layer from the different low-level + ;; file notification packages `gfilenotify', `inotify' and + ;; `w32notify'. +=20 + ;;; Code: +=20 + ;;;###autoload + (defconst file-notify-support + (cond + ((featurep 'gfilenotify) 'gfilenotify) + ((featurep 'inotify) 'inotify) + ((featurep 'w32notify) 'w32notify)) + "Non-nil when Emacs has been compiled with file notification support. + The value is the name of the low-level file notification package + to be used for local file systems. Remote file notifications + could use another implementation.") +=20 + (defvar file-notify-descriptors (make-hash-table :test 'equal) + "Hash table for registered file notification descriptors. + A key in this hash table is the descriptor as returned from a + `gfilenotify', `inotify' or `w32notify'. The value in the hash + table is the cons cell (FILE . CALLBACK).") +=20 + ;; This function is used by `gfilenotify', `inotify' and `w32notify' even= ts. + ;;;###autoload + (defun file-notify-handle-event (event) + "Handle file system monitoring event. + If EVENT is a filewatch event, call its callback. + Otherwise, signal a `file-notify-error'." + (interactive "e") + (if (and (eq (car event) 'file-notify) + (>=3D (length event) 3)) + (funcall (nth 2 event) (nth 1 event)) + (signal 'file-notify-error + (cons "Not a valid file-notify event" event)))) +=20 + (defvar file-notify--pending-event nil + "Pending file notification event for a future `renamed' action. + This is a list (DESCRIPTOR ACTION COOKIE FILE), or nil. ACTION + is either `moved-from' or `renamed-from'.") +=20 + (defun file-notify--event-file-name (event) + "Return file name of file notification event, or nil." + (cond ((eq file-notify-support 'gfilenotify) (nth 2 event)) + ((eq file-notify-support 'inotify) (nth 3 event)) + ((eq file-notify-support 'w32notify) (nth 2 event)))) +=20 + ;; Only `gfilenotify' could return two file names. + (defun file-notify--event-file1-name (event) + "Return second file name of file notification event, or nil. + This is available in case a file has been moved." + (and (eq file-notify-support 'gfilenotify) (nth 3 event))) +=20 + ;; Cookies are offered by `inotify' only. + (defun file-notify--event-cookie (event) + "Return cookie of file notification event, or nil. + This is available in case a file has been moved." + (and (eq file-notify-support 'inotify) (nth 2 event))) +=20 + ;; The callback function used to map between specific flags of the + ;; respective file notifications, and the ones we return. + (defun file-notify-callback (event) + "Handle an EVENT returned from file notification. + EVENT is the same one as in `file-notify-handle-event' except the + car of that event, which is the symbol `file-notify'." + (let ((descriptor (car event)) + (actions (nth 1 event)) + (file (file-notify--event-file-name event)) + file1 cookie callback tmp) + ;; Check, that event is meant for us. + (unless (setq callback (cdr (gethash descriptor file-notify-descripto= rs))) + (signal 'file-notify-error (list "Not a valid descriptor" descripto= r))) +=20 + ;; Make actions a list. + (unless (consp actions) (setq actions (cons actions nil))) +=20 + ;; Loop over actions. In fact, more than one action happens only + ;; for `inotify'. + (dolist (action actions) +=20 + ;; Send pending event, if it doesn't match. + (when (and file-notify--pending-event + ;; The cookie doesn't match. + (not (eq (nth 2 file-notify--pending-event) + (file-notify--event-cookie event))) + (or + ;; inotify. + (and (eq (nth 1 file-notify--pending-event) 'moved-from) + (not (eq action 'moved-to))) + ;; w32notify. + (and (eq (nth 1 file-notify--pending-event) 'renamed-from) + (not (eq action 'renamed-to))))) + (funcall callback + (list (nth 0 file-notify--pending-event) + 'deleted (nth 3 file-notify--pending-event))) + (setq file-notify--pending-event nil)) +=20 + ;; Map action. We ignore all events which cannot be mapped. + (setq action + (cond + ;; gfilenotify. + ((memq action '(attribute-changed changed created deleted)) action) + ((eq action 'moved) + (setq file1 (file-notify--event-file1-name event)) + 'renamed) +=20 + ;; inotify. + ((eq action 'attrib) 'attribute-changed) + ((eq action 'create) 'created) + ((eq action 'modify) 'changed) + ((memq action '(delete 'delete-self move-self)) 'deleted) + ;; Make the event pending. + ((eq action 'moved-from) + (setq file-notify--pending-event + (list descriptor action + (file-notify--event-cookie event) file)) + nil) + ;; Look for pending event. + ((eq action 'moved-to) + (if (null file-notify--pending-event) + 'created + (setq file1 file + file (nth 3 file-notify--pending-event) + file-notify--pending-event nil) + 'renamed)) +=20 + ;; w32notify. + ((eq action 'added) 'created) + ((eq action 'modified) 'changed) + ((eq action 'removed) 'deleted) + ;; Make the event pending. + ((eq 'renamed-from action) + (setq file-notify--pending-event + (list descriptor action nil file)) + nil) + ;; Look for pending event. + ((eq 'renamed-to action) + (if (null file-notify--pending-event) + 'created + (setq file1 file + file (nth 3 file-notify--pending-event) + file-notify--pending-event nil) + 'renamed)))) +=20 + ;; Apply callback. + (when action + (if file1 + (funcall callback (list descriptor action file file1)) + (funcall callback (list descriptor action file))))))) +=20 + (defun file-notify-add-watch (file flags callback) + "Add a watch for filesystem events pertaining to FILE. + This arranges for filesystem events pertaining to FILE to be reported + to Emacs. Use `file-notify-rm-watch' to cancel the watch. +=20 + The returned value is a descriptor for the added watch. If the + file cannot be watched for some reason, this function signals a + `file-error' error. +=20 + FLAGS is a list of conditions to set what will be watched for. It can + include the following symbols: +=20 + `change' -- watch for file changes + `attribute-change' -- watch for file attributes changes, like + permissions or modification time +=20 + If FILE is a directory, 'change' watches for file creation or + deletion in that directory. +=20 + When any event happens, Emacs will call the CALLBACK function passing + it a single argument EVENT, which is of the form +=20 + (DESCRIPTOR ACTION FILE [FILE1]) +=20 + DESCRIPTOR is the same object as the one returned by this function. + ACTION is the description of the event. It could be any one of the + following: +=20 + `created' -- FILE was created + `deleted' -- FILE was deleted + `changed' -- FILE has changed + `renamed' -- FILE has been renamed to FILE1 + `attribute-changed' -- a FILE attribute was changed +=20 + FILE is the name of the file whose event is being reported." + ;; Check arguments. + (unless (stringp file) + (signal 'wrong-type-argument (list file))) + (setq file (expand-file-name file)) + (unless (and (consp flags) + (null (delq 'change (delq 'attribute-change (copy-tree flags))))) + (signal 'wrong-type-argument (list flags))) + (unless (functionp callback) + (signal 'wrong-type-argument (list callback))) +=20 + (let ((handler (find-file-name-handler file 'file-notify-add-watch)) + func fl desc) +=20 + ;; Check, whether this has been registered already. + (maphash + (lambda (key value) + (when (equal (cons file callback) value) (setq desc key))) + file-notify-descriptors) +=20 + (unless desc + (if handler + ;; A file name handler could exist even if there is no local + ;; file notification support. + (setq desc + (funcall handler 'file-notify-add-watch file flags callback)) +=20 + ;; Check, whether Emacs has been compiled with file + ;; notification support. + (unless file-notify-support + (signal 'file-notify-error + '("No file notification package available"))) +=20 + ;; Determine local function to be called. + (setq func (cond + ((eq file-notify-support 'gfilenotify) 'gfile-add-watch) + ((eq file-notify-support 'inotify) 'inotify-add-watch) + ((eq file-notify-support 'w32notify) 'w32notify-add-watch))) +=20 + ;; Determine respective flags. + (if (eq file-notify-support 'gfilenotify) + (setq fl '(watch-mounts send-moved)) + (when (memq 'change flags) + (setq + fl + (cond + ((eq file-notify-support 'inotify) '(create modify move delete)) + ((eq file-notify-support 'w32notify) '(size last-write-time))))) + (when (memq 'attribute-change flags) + (add-to-list + 'fl + (cond + ((eq file-notify-support 'inotify) 'attrib) + ((eq file-notify-support 'w32notify) 'attributes))))) +=20 + ;; Call low-level function. + (setq desc (funcall func file fl 'file-notify-callback)))) +=20 + ;; Return descriptor. + (puthash desc (cons file callback) file-notify-descriptors) + desc)) +=20 + (defun file-notify-rm-watch (descriptor) + "Remove an existing watch specified by its DESCRIPTOR. + DESCRIPTOR should be an object returned by `file-notify-add-watch'." + (let ((file (car (gethash descriptor file-notify-descriptors))) + handler) +=20 + (when (stringp file) + (setq handler (find-file-name-handler file 'file-notify-rm-watch)) + (if handler + (funcall handler 'file-notify-rm-watch descriptor) + (funcall + (cond + ((eq file-notify-support 'gfilenotify) 'gfile-rm-watch) + ((eq file-notify-support 'inotify) 'inotify-rm-watch) + ((eq file-notify-support 'w32notify) 'w32notify-rm-watch)) + descriptor))) +=20 + (remhash descriptor file-notify-descriptors))) +=20 + ;; The end: + (provide 'filenotify) +=20 + ;;; filenotify.el ends here =3D=3D=3D modified file 'lisp/subr.el' *** lisp/subr.el 2013-06-20 14:15:42 +0000 --- lisp/subr.el 2013-06-24 07:49:32 +0000 *************** *** 4494,4513 **** nil ,@(cdr (cdr spec))))) =20=20 - ;;;; Support for watching filesystem events. -=20 - (defun file-notify-handle-event (event) - "Handle file system monitoring event. - If EVENT is a filewatch event, call its callback. - Otherwise, signal a `filewatch-error'." - (interactive "e") - (if (and (eq (car event) 'file-notify) - (>=3D (length event) 3)) - (funcall (nth 2 event) (nth 1 event)) - (signal 'filewatch-error - (cons "Not a valid file-notify event" event)))) -=20 - ;;;; Comparing version strings. =20=20 (defconst version-separator "." --- 4494,4499 ---- =3D=3D=3D modified file 'src/ChangeLog' *** src/ChangeLog 2013-06-24 00:31:31 +0000 --- src/ChangeLog 2013-06-27 11:56:19 +0000 *************** *** 1,3 **** --- 1,13 ---- + 2013-06-27 Michael Albinus +=20 + * fileio.c (Qfile_notify_error): New error symbol. +=20 + * gfilenotify.c (Fgfile_add_watch, Fgfile_rm_watch): + * inotify.c (inotify_callback, symbol_to_inotifymask) + (Finotify_add_watch, Finotify_rm_watch): Use it. +=20 + * lisp.h (Qfile_notify_error): Declare. +=20 2013-06-23 Paul Eggert =20=20 A more-conservative workaround for Cygwin SIGCHLD issues (Bug#14569). =3D=3D=3D modified file 'src/fileio.c' *** src/fileio.c 2013-06-18 07:42:37 +0000 --- src/fileio.c 2013-06-26 07:56:12 +0000 *************** *** 148,154 **** #ifdef WINDOWSNT #endif =20=20 ! Lisp_Object Qfile_error; static Lisp_Object Qfile_already_exists, Qfile_date_error; static Lisp_Object Qexcl; Lisp_Object Qfile_name_history; --- 148,154 ---- #ifdef WINDOWSNT #endif =20=20 ! Lisp_Object Qfile_error, Qfile_notify_error; static Lisp_Object Qfile_already_exists, Qfile_date_error; static Lisp_Object Qexcl; Lisp_Object Qfile_name_history; *************** *** 5887,5892 **** --- 5887,5893 ---- DEFSYM (Qfile_error, "file-error"); DEFSYM (Qfile_already_exists, "file-already-exists"); DEFSYM (Qfile_date_error, "file-date-error"); + DEFSYM (Qfile_notify_error, "file-notify-error"); DEFSYM (Qexcl, "excl"); =20=20 DEFVAR_LISP ("file-name-coding-system", Vfile_name_coding_system, *************** *** 5925,5930 **** --- 5926,5936 ---- Fput (Qfile_date_error, Qerror_message, build_pure_c_string ("Cannot set file date")); =20=20 + Fput (Qfile_notify_error, Qerror_conditions, + Fpurecopy (list3 (Qfile_notify_error, Qfile_error, Qerror))); + Fput (Qfile_notify_error, Qerror_message, + build_pure_c_string ("File notification error")); +=20 DEFVAR_LISP ("file-name-handler-alist", Vfile_name_handler_alist, doc: /* Alist of elements (REGEXP . HANDLER) for file names handl= ed specially. If a file name matches REGEXP, all I/O on that file is done by calling =3D=3D=3D modified file 'src/gfilenotify.c' *** src/gfilenotify.c 2013-06-06 07:04:35 +0000 --- src/gfilenotify.c 2013-06-27 11:47:42 +0000 *************** *** 132,146 **** to Emacs. Use `gfile-rm-watch' to cancel the watch. =20=20 Value is a descriptor for the added watch. If the file cannot be ! watched for some reason, this function signals a `file-error' error. =20=20 FLAGS is a list of conditions to set what will be watched for. It can include the following symbols: =20=20 'watch-mounts' -- watch for mount events 'send-moved' -- pair 'deleted' and 'created' events caused by file ! renames (moves) and send a single 'event-moved' ! event instead =20=20 When any event happens, Emacs will call the CALLBACK function passing it a single argument EVENT, which is of the form --- 132,145 ---- to Emacs. Use `gfile-rm-watch' to cancel the watch. =20=20 Value is a descriptor for the added watch. If the file cannot be ! watched for some reason, this function signals a `file-notify-error' erro= r. =20=20 FLAGS is a list of conditions to set what will be watched for. It can include the following symbols: =20=20 'watch-mounts' -- watch for mount events 'send-moved' -- pair 'deleted' and 'created' events caused by file ! renames and send a single 'renamed' event instead =20=20 When any event happens, Emacs will call the CALLBACK function passing it a single argument EVENT, which is of the form *************** *** 193,199 **** /* Enable watch. */ monitor =3D g_file_monitor (gfile, gflags, NULL, NULL); if (! monitor) ! xsignal2 (Qfile_error, build_string ("Cannot watch file"), file); =20=20 /* On all known glib platforms, converting MONITOR directly to a Lisp_Object value results is a Lisp integer, which is safe. This --- 192,198 ---- /* Enable watch. */ monitor =3D g_file_monitor (gfile, gflags, NULL, NULL); if (! monitor) ! xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), fil= e); =20=20 /* On all known glib platforms, converting MONITOR directly to a Lisp_Object value results is a Lisp integer, which is safe. This *************** *** 202,208 **** if (! INTEGERP (watch_descriptor)) { g_object_unref (monitor); ! xsignal2 (Qfile_error, build_string ("Unsupported file watcher"), f= ile); } =20=20 g_signal_connect (monitor, "changed", --- 201,208 ---- if (! INTEGERP (watch_descriptor)) { g_object_unref (monitor); ! xsignal2 (Qfile_notify_error, build_string ("Unsupported file watch= er"), ! file); } =20=20 g_signal_connect (monitor, "changed", *************** *** 226,239 **** Lisp_Object watch_object =3D assq_no_quit (watch_descriptor, watch_list= ); =20=20 if (! CONSP (watch_object)) ! xsignal2 (Qfile_error, build_string ("Not a watch descriptor"), watch_descriptor); =20=20 eassert (INTEGERP (watch_descriptor)); int_monitor =3D XLI (watch_descriptor); monitor =3D (GFileMonitor *) int_monitor; if (!g_file_monitor_cancel (monitor)) ! xsignal2 (Qfile_error, build_string ("Could not rm watch"), watch_descriptor); =20=20 /* Remove watch descriptor from watch list. */ --- 226,239 ---- Lisp_Object watch_object =3D assq_no_quit (watch_descriptor, watch_list= ); =20=20 if (! CONSP (watch_object)) ! xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"), watch_descriptor); =20=20 eassert (INTEGERP (watch_descriptor)); int_monitor =3D XLI (watch_descriptor); monitor =3D (GFileMonitor *) int_monitor; if (!g_file_monitor_cancel (monitor)) ! xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"), watch_descriptor); =20=20 /* Remove watch descriptor from watch list. */ =3D=3D=3D modified file 'src/inotify.c' *** src/inotify.c 2013-01-02 16:30:50 +0000 --- src/inotify.c 2013-06-27 08:42:14 +0000 *************** *** 158,172 **** =20=20 to_read =3D 0; if (ioctl (fd, FIONREAD, &to_read) =3D=3D -1) ! report_file_error ("Error while trying to retrieve file system events= ", ! Qnil); buffer =3D xmalloc (to_read); n =3D read (fd, buffer, to_read); if (n < 0) { xfree (buffer); ! report_file_error ("Error while trying to read file system events", ! Qnil); } =20=20 EVENT_INIT (event); --- 158,174 ---- =20=20 to_read =3D 0; if (ioctl (fd, FIONREAD, &to_read) =3D=3D -1) ! xsignal1 ! (Qfile_notify_error, ! build_string ("Error while trying to retrieve file system events")= ); buffer =3D xmalloc (to_read); n =3D read (fd, buffer, to_read); if (n < 0) { xfree (buffer); ! xsignal1 ! (Qfile_notify_error, ! build_string ("Error while trying to read file system events")); } =20=20 EVENT_INIT (event); *************** *** 242,248 **** else if (EQ (symb, Qt) || EQ (symb, Qall_events)) return IN_ALL_EVENTS; else ! signal_error ("Unknown aspect", symb); } =20=20 static uint32_t --- 244,250 ---- else if (EQ (symb, Qt) || EQ (symb, Qall_events)) return IN_ALL_EVENTS; else ! xsignal2 (Qfile_notify_error, build_string ("Unknown aspect"), symb= ); } =20=20 static uint32_t *************** *** 335,342 **** if (inotifyfd =3D=3D -1) { inotifyfd =3D uninitialized; ! report_file_error ("File watching feature (inotify) is not avai= lable", ! Qnil); } watch_list =3D Qnil; add_read_fd (inotifyfd, &inotify_callback, NULL); --- 337,345 ---- if (inotifyfd =3D=3D -1) { inotifyfd =3D uninitialized; ! xsignal1 ! (Qfile_notify_error, ! build_string ("File watching feature (inotify) is not available")); } watch_list =3D Qnil; add_read_fd (inotifyfd, &inotify_callback, NULL); *************** *** 346,352 **** encoded_file_name =3D ENCODE_FILE (file_name); watchdesc =3D inotify_add_watch (inotifyfd, SSDATA (encoded_file_name),= mask); if (watchdesc =3D=3D -1) ! report_file_error ("Could not add watch for file", Fcons (file_name, = Qnil)); =20=20 watch_descriptor =3D make_watch_descriptor (watchdesc); =20=20 --- 349,356 ---- encoded_file_name =3D ENCODE_FILE (file_name); watchdesc =3D inotify_add_watch (inotifyfd, SSDATA (encoded_file_name),= mask); if (watchdesc =3D=3D -1) ! xsignal2 (Qfile_notify_error, ! build_string ("Could not add watch for file"), file_name); =20=20 watch_descriptor =3D make_watch_descriptor (watchdesc); =20=20 *************** *** 375,382 **** int wd =3D XINT (watch_descriptor); =20=20 if (inotify_rm_watch (inotifyfd, wd) =3D=3D -1) ! report_file_error ("Could not rm watch", Fcons (watch_descriptor, ! Qnil)); =20=20 /* Remove watch descriptor from watch list. */ watch_object =3D Fassoc (watch_descriptor, watch_list); --- 379,386 ---- int wd =3D XINT (watch_descriptor); =20=20 if (inotify_rm_watch (inotifyfd, wd) =3D=3D -1) ! xsignal2 (Qfile_notify_error, ! build_string ("Could not rm watch"), watch_descriptor); =20=20 /* Remove watch descriptor from watch list. */ watch_object =3D Fassoc (watch_descriptor, watch_list); =3D=3D=3D modified file 'src/lisp.h' *** src/lisp.h 2013-06-21 20:11:44 +0000 --- src/lisp.h 2013-06-26 07:41:17 +0000 *************** *** 3799,3804 **** --- 3799,3805 ---- /* Defined in fileio.c. */ =20=20 extern Lisp_Object Qfile_error; + extern Lisp_Object Qfile_notify_error; extern Lisp_Object Qfile_exists_p; extern Lisp_Object Qfile_directory_p; extern Lisp_Object Qinsert_file_contents; --=-=-=--