Support filesystem notification through inotify on GNU/Linux.
configure.ac (inotify): New option. (HAVE_INOTIFY): Test for inotify. src/termhooks.h (enum event_kind) [HAVE_INOTIFY]: Add FILE_NOTIFY_EVENT. src/lisp.h (syms_of_inotify) [HAVE_INOTIFY]: Add prototype. src/keyboard.c (Qfile_inotify) [HAVE_INOTIFY]: New variable. (syms_of_keyboard): DEFSYM it. (kbd_buffer_get_event) [HAVE_INOTIFY]: Generate FILE_NOTIFY_EVENT. (make_lispy_event): Support FILE_NOTIFY_EVENT by generating Qfile_inotify events. (keys_of_keyboard) [HAVE_INOTIFY]: Bind file-inotify events in special-event-map to inotify-handle-event. src/emacs.c (main) [HAVE_INOTIFY]: Call syms_of_inotify. src/Makefile.in (base_obj): Add inotify.o. src/inotify.c: New file. lisp/subr.el (inotify-event-p, inotify-handle-event): New functions. test/automated/inotify-test.el: New test.
This commit is contained in:
parent
265c2fbf11
commit
81606b1050
14 changed files with 613 additions and 1 deletions
|
@ -1,3 +1,8 @@
|
|||
2012-12-10 Rüdiger Sonderfeld <ruediger@c-plusplus.de>
|
||||
|
||||
* configure.ac (inotify): New option.
|
||||
(HAVE_INOTIFY): Test for inotify.
|
||||
|
||||
2012-12-09 Andreas Schwab <schwab@linux-m68k.org>
|
||||
|
||||
* configure.ac: Fix source command in .gdbinit.
|
||||
|
|
13
configure.ac
13
configure.ac
|
@ -185,6 +185,7 @@ OPTION_DEFAULT_ON([gconf],[don't compile with GConf support])
|
|||
OPTION_DEFAULT_ON([gsettings],[don't compile with GSettings support])
|
||||
OPTION_DEFAULT_ON([selinux],[don't compile with SELinux support])
|
||||
OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS support])
|
||||
OPTION_DEFAULT_ON([inotify],[don't compile with inotify (file-watch) support])
|
||||
|
||||
## For the times when you want to build Emacs but don't have
|
||||
## a suitable makeinfo, and can live without the manuals.
|
||||
|
@ -2175,6 +2176,18 @@ fi
|
|||
AC_SUBST(LIBGNUTLS_LIBS)
|
||||
AC_SUBST(LIBGNUTLS_CFLAGS)
|
||||
|
||||
dnl inotify is only available on GNU/Linux.
|
||||
HAVE_INOTIFY=no
|
||||
if test "${with_inotify}" = "yes"; then
|
||||
AC_CHECK_HEADERS(sys/inotify.h)
|
||||
if test "$ac_cv_header_sys_inotify_h" = yes ; then
|
||||
AC_CHECK_FUNCS(inotify_init1 inotify_add_watch inotify_rm_watch, HAVE_INOTIFY=yes)
|
||||
fi
|
||||
fi
|
||||
if test "${HAVE_INOTIFY}" = "yes"; then
|
||||
AC_DEFINE(HAVE_INOTIFY, [1], [Define to 1 to use inotify])
|
||||
fi
|
||||
|
||||
dnl Do not put whitespace before the #include statements below.
|
||||
dnl Older compilers (eg sunos4 cc) choke on it.
|
||||
HAVE_XAW3D=no
|
||||
|
|
5
etc/NEWS
5
etc/NEWS
|
@ -133,6 +133,11 @@ spurious warnings about an unused var.
|
|||
|
||||
* Lisp changes in Emacs 24.4
|
||||
|
||||
** Support for filesystem notifications.
|
||||
Emacs now supports notifications of filesystem changes, such as
|
||||
creation, modification, and deletion of files. This requires the
|
||||
'inotify' API on GNU/Linux systems.
|
||||
|
||||
** Face changes
|
||||
|
||||
*** The `face-spec-set' is now analogous to `setq' for face specs.
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
2012-12-10 Rüdiger Sonderfeld <ruediger@c-plusplus.de>
|
||||
|
||||
* subr.el (inotify-event-p, inotify-handle-event): New functions.
|
||||
|
||||
2012-12-10 Dani Moncayo <dmoncayo@gmail.com>
|
||||
|
||||
* simple.el (just-one-space): Doc fix.
|
||||
|
|
20
lisp/subr.el
20
lisp/subr.el
|
@ -4322,6 +4322,26 @@ convenience wrapper around `make-progress-reporter' and friends.
|
|||
(progress-reporter-done ,temp2)
|
||||
nil ,@(cdr (cdr spec)))))
|
||||
|
||||
|
||||
;;;; Support for watching filesystem events.
|
||||
|
||||
(defun inotify-event-p (event)
|
||||
"Check if EVENT is an inotify event."
|
||||
(and (listp event)
|
||||
(>= (length event) 3)
|
||||
(eq (car event) 'file-inotify)))
|
||||
|
||||
;;;###autoload
|
||||
(defun inotify-handle-event (event)
|
||||
"Handle file system monitoring event.
|
||||
If EVENT is a filewatch event then the callback is called. If EVENT is
|
||||
not a filewatch event then a `filewatch-error' is signaled."
|
||||
(interactive "e")
|
||||
(unless (inotify-event-p event)
|
||||
(signal 'filewatch-error (cons "Not a valid inotify event" event)))
|
||||
|
||||
(funcall (nth 2 event) (nth 1 event)))
|
||||
|
||||
|
||||
;;;; Comparing version strings.
|
||||
|
||||
|
|
|
@ -1,3 +1,24 @@
|
|||
2012-12-10 Rüdiger Sonderfeld <ruediger@c-plusplus.de>
|
||||
|
||||
* termhooks.h (enum event_kind) [HAVE_INOTIFY]: Add
|
||||
FILE_NOTIFY_EVENT.
|
||||
|
||||
* lisp.h (syms_of_inotify) [HAVE_INOTIFY]: Add prototype.
|
||||
|
||||
* keyboard.c (Qfile_inotify) [HAVE_INOTIFY]: New variable.
|
||||
(syms_of_keyboard): DEFSYM it.
|
||||
(kbd_buffer_get_event) [HAVE_INOTIFY]: Generate FILE_NOTIFY_EVENT.
|
||||
(make_lispy_event): Support FILE_NOTIFY_EVENT by generating
|
||||
Qfile_inotify events.
|
||||
(keys_of_keyboard) [HAVE_INOTIFY]: Bind file-inotify events in
|
||||
special-event-map to inotify-handle-event.
|
||||
|
||||
* emacs.c (main) [HAVE_INOTIFY]: Call syms_of_inotify.
|
||||
|
||||
* Makefile.in (base_obj): Add inotify.o.
|
||||
|
||||
* inotify.c: New file.
|
||||
|
||||
2012-12-10 Jan Djärv <jan.h.d@swipnet.se>
|
||||
|
||||
* nsterm.m (fd_handler:): FD_ZERO fds (Bug#13103).
|
||||
|
|
|
@ -344,7 +344,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
|
|||
syntax.o $(UNEXEC_OBJ) bytecode.o \
|
||||
process.o gnutls.o callproc.o \
|
||||
region-cache.o sound.o atimer.o \
|
||||
doprnt.o intervals.o textprop.o composite.o xml.o \
|
||||
doprnt.o intervals.o textprop.o composite.o xml.o inotify.o \
|
||||
profiler.o \
|
||||
$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
|
||||
$(W32_OBJ) $(WINDOW_SYSTEM_OBJ)
|
||||
|
|
|
@ -1442,6 +1442,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
|
|||
syms_of_gnutls ();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
syms_of_inotify ();
|
||||
#endif /* HAVE_INOTIFY */
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
syms_of_dbusbind ();
|
||||
#endif /* HAVE_DBUS */
|
||||
|
|
438
src/inotify.c
Normal file
438
src/inotify.c
Normal file
|
@ -0,0 +1,438 @@
|
|||
/* Inotify support for Emacs
|
||||
|
||||
Copyright (C) 2012
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
|
||||
#include "lisp.h"
|
||||
#include "coding.h"
|
||||
#include "process.h"
|
||||
#include "keyboard.h"
|
||||
#include "character.h"
|
||||
#include "frame.h" /* Required for termhooks.h. */
|
||||
#include "termhooks.h"
|
||||
|
||||
static Lisp_Object Qaccess; /* IN_ACCESS */
|
||||
static Lisp_Object Qattrib; /* IN_ATTRIB */
|
||||
static Lisp_Object Qclose_write; /* IN_CLOSE_WRITE */
|
||||
static Lisp_Object Qclose_nowrite; /* IN_CLOSE_NOWRITE */
|
||||
static Lisp_Object Qcreate; /* IN_CREATE */
|
||||
static Lisp_Object Qdelete; /* IN_DELETE */
|
||||
static Lisp_Object Qdelete_self; /* IN_DELETE_SELF */
|
||||
static Lisp_Object Qmodify; /* IN_MODIFY */
|
||||
static Lisp_Object Qmove_self; /* IN_MOVE_SELF */
|
||||
static Lisp_Object Qmoved_from; /* IN_MOVED_FROM */
|
||||
static Lisp_Object Qmoved_to; /* IN_MOVED_TO */
|
||||
static Lisp_Object Qopen; /* IN_OPEN */
|
||||
|
||||
static Lisp_Object Qall_events; /* IN_ALL_EVENTS */
|
||||
static Lisp_Object Qmove; /* IN_MOVE */
|
||||
static Lisp_Object Qclose; /* IN_CLOSE */
|
||||
|
||||
static Lisp_Object Qdont_follow; /* IN_DONT_FOLLOW */
|
||||
static Lisp_Object Qexcl_unlink; /* IN_EXCL_UNLINK */
|
||||
static Lisp_Object Qmask_add; /* IN_MASK_ADD */
|
||||
static Lisp_Object Qoneshot; /* IN_ONESHOT */
|
||||
static Lisp_Object Qonlydir; /* IN_ONLYDIR */
|
||||
|
||||
static Lisp_Object Qignored; /* IN_IGNORED */
|
||||
static Lisp_Object Qisdir; /* IN_ISDIR */
|
||||
static Lisp_Object Qq_overflow; /* IN_Q_OVERFLOW */
|
||||
static Lisp_Object Qunmount; /* IN_UNMOUNT */
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/* Ignore bits that might be undefined on old GNU/Linux systems. */
|
||||
#ifndef IN_EXCL_UNLINK
|
||||
# define IN_EXCL_UNLINK 0
|
||||
#endif
|
||||
#ifndef IN_DONT_FOLLOW
|
||||
# define IN_DONT_FOLLOW 0
|
||||
#endif
|
||||
#ifndef IN_ONLYDIR
|
||||
# define IN_ONLYDIR 0
|
||||
#endif
|
||||
|
||||
enum { uninitialized = -100 };
|
||||
/* File handle for inotify. */
|
||||
static int inotifyfd = uninitialized;
|
||||
|
||||
/* Assoc list of files being watched.
|
||||
Format:
|
||||
(watch-descriptor . callback)
|
||||
*/
|
||||
static Lisp_Object watch_list;
|
||||
|
||||
static Lisp_Object
|
||||
make_watch_descriptor (int wd)
|
||||
{
|
||||
/* TODO replace this with a Misc Object! */
|
||||
return make_number (wd);
|
||||
}
|
||||
|
||||
static Lisp_Object
|
||||
mask_to_aspects (uint32_t mask) {
|
||||
Lisp_Object aspects = Qnil;
|
||||
if (mask & IN_ACCESS)
|
||||
aspects = Fcons (Qaccess, aspects);
|
||||
if (mask & IN_ATTRIB)
|
||||
aspects = Fcons (Qattrib, aspects);
|
||||
if (mask & IN_CLOSE_WRITE)
|
||||
aspects = Fcons (Qclose_write, aspects);
|
||||
if (mask & IN_CLOSE_NOWRITE)
|
||||
aspects = Fcons (Qclose_nowrite, aspects);
|
||||
if (mask & IN_CREATE)
|
||||
aspects = Fcons (Qcreate, aspects);
|
||||
if (mask & IN_DELETE)
|
||||
aspects = Fcons (Qdelete, aspects);
|
||||
if (mask & IN_DELETE_SELF)
|
||||
aspects = Fcons (Qdelete_self, aspects);
|
||||
if (mask & IN_MODIFY)
|
||||
aspects = Fcons (Qmodify, aspects);
|
||||
if (mask & IN_MOVE_SELF)
|
||||
aspects = Fcons (Qmove_self, aspects);
|
||||
if (mask & IN_MOVED_FROM)
|
||||
aspects = Fcons (Qmoved_from, aspects);
|
||||
if (mask & IN_MOVED_TO)
|
||||
aspects = Fcons (Qmoved_to, aspects);
|
||||
if (mask & IN_OPEN)
|
||||
aspects = Fcons (Qopen, aspects);
|
||||
if (mask & IN_IGNORED)
|
||||
aspects = Fcons (Qignored, aspects);
|
||||
if (mask & IN_ISDIR)
|
||||
aspects = Fcons (Qisdir, aspects);
|
||||
if (mask & IN_Q_OVERFLOW)
|
||||
aspects = Fcons (Qq_overflow, aspects);
|
||||
if (mask & IN_UNMOUNT)
|
||||
aspects = Fcons (Qunmount, aspects);
|
||||
return aspects;
|
||||
}
|
||||
|
||||
static Lisp_Object
|
||||
inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev)
|
||||
{
|
||||
Lisp_Object name = Qnil;
|
||||
if (ev->len > 0)
|
||||
{
|
||||
size_t const len = strlen (ev->name);
|
||||
name = make_unibyte_string (ev->name, min (len, ev->len));
|
||||
name = DECODE_FILE (name);
|
||||
}
|
||||
|
||||
return list2 (list4 (make_watch_descriptor (ev->wd),
|
||||
mask_to_aspects (ev->mask),
|
||||
make_number (ev->cookie),
|
||||
name),
|
||||
XCDR (watch_object));
|
||||
}
|
||||
|
||||
/* This callback is called when the FD is available for read. The inotify
|
||||
events are read from FD and converted into input_events. */
|
||||
static void
|
||||
inotify_callback (int fd, void *_)
|
||||
{
|
||||
struct input_event event;
|
||||
Lisp_Object watch_object;
|
||||
int to_read;
|
||||
char *buffer;
|
||||
ssize_t n;
|
||||
size_t i;
|
||||
|
||||
to_read = 0;
|
||||
if (ioctl (fd, FIONREAD, &to_read) == -1)
|
||||
report_file_error ("Error while trying to retrieve file system events",
|
||||
Qnil);
|
||||
buffer = xmalloc (to_read);
|
||||
n = read (fd, buffer, to_read);
|
||||
if (n < 0)
|
||||
{
|
||||
xfree (buffer);
|
||||
report_file_error ("Error while trying to read file system events",
|
||||
Qnil);
|
||||
}
|
||||
|
||||
EVENT_INIT (event);
|
||||
event.kind = FILE_NOTIFY_EVENT;
|
||||
event.arg = Qnil;
|
||||
|
||||
i = 0;
|
||||
while (i < (size_t)n)
|
||||
{
|
||||
struct inotify_event *ev = (struct inotify_event*)&buffer[i];
|
||||
|
||||
watch_object = Fassoc (make_watch_descriptor (ev->wd), watch_list);
|
||||
if (!NILP (watch_object))
|
||||
{
|
||||
event.arg = inotifyevent_to_event (watch_object, ev);
|
||||
|
||||
/* If event was removed automatically: Drop it from watch list. */
|
||||
if (ev->mask & IN_IGNORED)
|
||||
watch_list = Fdelete (watch_object, watch_list);
|
||||
}
|
||||
|
||||
i += sizeof (*ev) + ev->len;
|
||||
}
|
||||
|
||||
if (!NILP (event.arg))
|
||||
kbd_buffer_store_event (&event);
|
||||
|
||||
xfree (buffer);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
symbol_to_inotifymask (Lisp_Object symb)
|
||||
{
|
||||
if (EQ (symb, Qaccess))
|
||||
return IN_ACCESS;
|
||||
else if (EQ (symb, Qattrib))
|
||||
return IN_ATTRIB;
|
||||
else if (EQ (symb, Qclose_write))
|
||||
return IN_CLOSE_WRITE;
|
||||
else if (EQ (symb, Qclose_nowrite))
|
||||
return IN_CLOSE_NOWRITE;
|
||||
else if (EQ (symb, Qcreate))
|
||||
return IN_CREATE;
|
||||
else if (EQ (symb, Qdelete))
|
||||
return IN_DELETE;
|
||||
else if (EQ (symb, Qdelete_self))
|
||||
return IN_DELETE_SELF;
|
||||
else if (EQ (symb, Qmodify))
|
||||
return IN_MODIFY;
|
||||
else if (EQ (symb, Qmove_self))
|
||||
return IN_MOVE_SELF;
|
||||
else if (EQ (symb, Qmoved_from))
|
||||
return IN_MOVED_FROM;
|
||||
else if (EQ (symb, Qmoved_to))
|
||||
return IN_MOVED_TO;
|
||||
else if (EQ (symb, Qopen))
|
||||
return IN_OPEN;
|
||||
else if (EQ (symb, Qmove))
|
||||
return IN_MOVE;
|
||||
else if (EQ (symb, Qclose))
|
||||
return IN_CLOSE;
|
||||
|
||||
else if (EQ (symb, Qdont_follow))
|
||||
return IN_DONT_FOLLOW;
|
||||
else if (EQ (symb, Qexcl_unlink))
|
||||
return IN_EXCL_UNLINK;
|
||||
else if (EQ (symb, Qmask_add))
|
||||
return IN_MASK_ADD;
|
||||
else if (EQ (symb, Qoneshot))
|
||||
return IN_ONESHOT;
|
||||
else if (EQ (symb, Qonlydir))
|
||||
return IN_ONLYDIR;
|
||||
|
||||
else if (EQ (symb, Qt) || EQ (symb, Qall_events))
|
||||
return IN_ALL_EVENTS;
|
||||
else
|
||||
signal_error ("Unknown aspect", symb);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
aspect_to_inotifymask (Lisp_Object aspect)
|
||||
{
|
||||
if (CONSP (aspect))
|
||||
{
|
||||
Lisp_Object x = aspect;
|
||||
uint32_t mask = 0;
|
||||
while (CONSP (x))
|
||||
{
|
||||
mask |= symbol_to_inotifymask (XCAR (x));
|
||||
x = XCDR (x);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
else
|
||||
return symbol_to_inotifymask (aspect);
|
||||
}
|
||||
|
||||
DEFUN ("inotify-add-watch", Finotify_add_watch, Sinotify_add_watch, 3, 3, 0,
|
||||
doc: /* Add a watch for FILE-NAME to inotify.
|
||||
|
||||
A WATCH-DESCRIPTOR is returned on success. ASPECT might be one of the following
|
||||
symbols or a list of those symbols:
|
||||
|
||||
access
|
||||
attrib
|
||||
close-write
|
||||
close-nowrite
|
||||
create
|
||||
delete
|
||||
delete-self
|
||||
modify
|
||||
move-self
|
||||
moved-from
|
||||
moved-to
|
||||
open
|
||||
|
||||
all-events or t
|
||||
move
|
||||
close
|
||||
|
||||
The following symbols can also be added to a list of aspects
|
||||
|
||||
dont-follow
|
||||
excl-unlink
|
||||
mask-add
|
||||
oneshot
|
||||
onlydir
|
||||
|
||||
Watching a directory is not recursive. CALLBACK gets called in case of an
|
||||
event. It gets passed a single argument EVENT which contains an event structure
|
||||
of the format
|
||||
|
||||
(WATCH-DESCRIPTOR ASPECTS COOKIE NAME)
|
||||
|
||||
WATCH-DESCRIPTOR is the same object that was returned by this function. It can
|
||||
be tested for equality using `equal'. ASPECTS describes the event. It is a
|
||||
list of ASPECT symbols described above and can also contain one of the following
|
||||
symbols
|
||||
|
||||
ignored
|
||||
isdir
|
||||
q-overflow
|
||||
unmount
|
||||
|
||||
COOKIE is an object that can be compared using `equal' to identify two matching
|
||||
renames (moved-from and moved-to).
|
||||
|
||||
If a directory is watched then NAME is the name of file that caused the event.
|
||||
|
||||
See inotify(7) and inotify_add_watch(2) for further information. The inotify fd
|
||||
is managed internally and there is no corresponding inotify_init. Use
|
||||
`inotify-rm-watch' to remove a watch.
|
||||
*/)
|
||||
(Lisp_Object file_name, Lisp_Object aspect, Lisp_Object callback)
|
||||
{
|
||||
uint32_t mask;
|
||||
Lisp_Object watch_object;
|
||||
Lisp_Object decoded_file_name;
|
||||
Lisp_Object watch_descriptor;
|
||||
int watchdesc = -1;
|
||||
|
||||
CHECK_STRING (file_name);
|
||||
|
||||
if (inotifyfd == uninitialized)
|
||||
{
|
||||
inotifyfd = inotify_init1 (IN_NONBLOCK|IN_CLOEXEC);
|
||||
if (inotifyfd == -1)
|
||||
{
|
||||
inotifyfd = uninitialized;
|
||||
report_file_error ("File watching feature (inotify) is not available",
|
||||
Qnil);
|
||||
}
|
||||
watch_list = Qnil;
|
||||
add_read_fd (inotifyfd, &inotify_callback, NULL);
|
||||
}
|
||||
|
||||
mask = aspect_to_inotifymask (aspect);
|
||||
decoded_file_name = ENCODE_FILE (file_name);
|
||||
watchdesc = inotify_add_watch (inotifyfd, SSDATA (decoded_file_name), mask);
|
||||
if (watchdesc == -1)
|
||||
report_file_error ("Could not add watch for file", Fcons (file_name, Qnil));
|
||||
|
||||
watch_descriptor = make_watch_descriptor (watchdesc);
|
||||
|
||||
/* Delete existing watch object. */
|
||||
watch_object = Fassoc (watch_descriptor, watch_list);
|
||||
if (!NILP (watch_object))
|
||||
watch_list = Fdelete (watch_object, watch_list);
|
||||
|
||||
/* Store watch object in watch list. */
|
||||
watch_object = Fcons (watch_descriptor, callback);
|
||||
watch_list = Fcons (watch_object, watch_list);
|
||||
|
||||
return watch_descriptor;
|
||||
}
|
||||
|
||||
DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
|
||||
doc: /* Remove an existing WATCH-DESCRIPTOR.
|
||||
|
||||
WATCH-DESCRIPTOR should be an object returned by `inotify-add-watch'.
|
||||
|
||||
See inotify_rm_watch(2) for more information.
|
||||
*/)
|
||||
(Lisp_Object watch_descriptor)
|
||||
{
|
||||
Lisp_Object watch_object;
|
||||
int wd = XINT (watch_descriptor);
|
||||
|
||||
if (inotify_rm_watch (inotifyfd, wd) == -1)
|
||||
report_file_error ("Could not rm watch", Fcons (watch_descriptor,
|
||||
Qnil));
|
||||
|
||||
/* Remove watch descriptor from watch list. */
|
||||
watch_object = Fassoc (watch_descriptor, watch_list);
|
||||
if (!NILP (watch_object))
|
||||
watch_list = Fdelete (watch_object, watch_list);
|
||||
|
||||
/* Cleanup if no more files are watched. */
|
||||
if (NILP (watch_list))
|
||||
{
|
||||
close (inotifyfd);
|
||||
delete_read_fd (inotifyfd);
|
||||
inotifyfd = uninitialized;
|
||||
}
|
||||
|
||||
return Qt;
|
||||
}
|
||||
|
||||
void
|
||||
syms_of_inotify (void)
|
||||
{
|
||||
DEFSYM (Qaccess, "access");
|
||||
DEFSYM (Qattrib, "attrib");
|
||||
DEFSYM (Qclose_write, "close-write");
|
||||
DEFSYM (Qclose_nowrite, "close-nowrite");
|
||||
DEFSYM (Qcreate, "create");
|
||||
DEFSYM (Qdelete, "delete");
|
||||
DEFSYM (Qdelete_self, "delete-self");
|
||||
DEFSYM (Qmodify, "modify");
|
||||
DEFSYM (Qmove_self, "move-self");
|
||||
DEFSYM (Qmoved_from, "moved-from");
|
||||
DEFSYM (Qmoved_to, "moved-to");
|
||||
DEFSYM (Qopen, "open");
|
||||
|
||||
DEFSYM (Qall_events, "all-events");
|
||||
DEFSYM (Qmove, "move");
|
||||
DEFSYM (Qclose, "close");
|
||||
|
||||
DEFSYM (Qdont_follow, "dont-follow");
|
||||
DEFSYM (Qexcl_unlink, "excl-unlink");
|
||||
DEFSYM (Qmask_add, "mask-add");
|
||||
DEFSYM (Qoneshot, "oneshot");
|
||||
DEFSYM (Qonlydir, "onlydir");
|
||||
|
||||
DEFSYM (Qignored, "ignored");
|
||||
DEFSYM (Qisdir, "isdir");
|
||||
DEFSYM (Qq_overflow, "q-overflow");
|
||||
DEFSYM (Qunmount, "unmount");
|
||||
|
||||
defsubr (&Sinotify_add_watch);
|
||||
defsubr (&Sinotify_rm_watch);
|
||||
|
||||
staticpro (&watch_list);
|
||||
|
||||
Fprovide (intern_c_string ("inotify"), Qnil);
|
||||
}
|
||||
|
||||
#endif /* HAVE_INOTIFY */
|
|
@ -319,6 +319,9 @@ static Lisp_Object Qsave_session;
|
|||
#ifdef HAVE_DBUS
|
||||
static Lisp_Object Qdbus_event;
|
||||
#endif
|
||||
#ifdef HAVE_INOTIFY
|
||||
static Lisp_Object Qfile_inotify;
|
||||
#endif /* HAVE_INOTIFY */
|
||||
static Lisp_Object Qconfig_changed_event;
|
||||
|
||||
/* Lisp_Object Qmouse_movement; - also an event header */
|
||||
|
@ -3960,6 +3963,13 @@ kbd_buffer_get_event (KBOARD **kbp,
|
|||
obj = make_lispy_event (event);
|
||||
kbd_fetch_ptr = event + 1;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_INOTIFY
|
||||
else if (event->kind == FILE_NOTIFY_EVENT)
|
||||
{
|
||||
obj = make_lispy_event (event);
|
||||
kbd_fetch_ptr = event + 1;
|
||||
}
|
||||
#endif
|
||||
else if (event->kind == CONFIG_CHANGED_EVENT)
|
||||
{
|
||||
|
@ -5874,6 +5884,13 @@ make_lispy_event (struct input_event *event)
|
|||
}
|
||||
#endif /* HAVE_DBUS */
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
case FILE_NOTIFY_EVENT:
|
||||
{
|
||||
return Fcons (Qfile_inotify, event->arg);
|
||||
}
|
||||
#endif /* HAVE_INOTIFY */
|
||||
|
||||
case CONFIG_CHANGED_EVENT:
|
||||
return Fcons (Qconfig_changed_event,
|
||||
Fcons (event->arg,
|
||||
|
@ -11337,6 +11354,10 @@ syms_of_keyboard (void)
|
|||
DEFSYM (Qdbus_event, "dbus-event");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
DEFSYM (Qfile_inotify, "file-inotify");
|
||||
#endif /* HAVE_INOTIFY */
|
||||
|
||||
DEFSYM (QCenable, ":enable");
|
||||
DEFSYM (QCvisible, ":visible");
|
||||
DEFSYM (QChelp, ":help");
|
||||
|
@ -12093,6 +12114,13 @@ keys_of_keyboard (void)
|
|||
"dbus-handle-event");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
/* Define a special event which is raised for inotify callback
|
||||
functions. */
|
||||
initial_define_lispy_key (Vspecial_event_map, "file-inotify",
|
||||
"inotify-handle-event");
|
||||
#endif /* HAVE_INOTIFY */
|
||||
|
||||
initial_define_lispy_key (Vspecial_event_map, "config-changed-event",
|
||||
"ignore");
|
||||
#if defined (WINDOWSNT)
|
||||
|
|
|
@ -3516,6 +3516,11 @@ extern void syms_of_fontset (void);
|
|||
extern Lisp_Object Qfont_param;
|
||||
#endif
|
||||
|
||||
/* Defined in inotify.c */
|
||||
#ifdef HAVE_INOTIFY
|
||||
extern void syms_of_inotify (void);
|
||||
#endif
|
||||
|
||||
/* Defined in xfaces.c. */
|
||||
extern Lisp_Object Qdefault, Qtool_bar, Qfringe;
|
||||
extern Lisp_Object Qheader_line, Qscroll_bar, Qcursor;
|
||||
|
|
|
@ -211,6 +211,11 @@ enum event_kind
|
|||
, NS_NONKEY_EVENT
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
/* File or directory was changed. */
|
||||
, FILE_NOTIFY_EVENT
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/* If a struct input_event has a kind which is SELECTION_REQUEST_EVENT
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
2012-12-10 Rüdiger Sonderfeld <ruediger@c-plusplus.de>
|
||||
|
||||
* automated/inotify-test.el: New test.
|
||||
|
||||
2012-12-02 Chong Yidong <cyd@gnu.org>
|
||||
|
||||
* automated/ruby-mode-tests.el
|
||||
|
|
60
test/automated/inotify-test.el
Normal file
60
test/automated/inotify-test.el
Normal file
|
@ -0,0 +1,60 @@
|
|||
;;; inotify-tests.el --- Test suite for inotify. -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Rüdiger Sonderfeld <ruediger@c-plusplus.de>
|
||||
;; Keywords: internal
|
||||
;; Human-Keywords: internal
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; 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.
|
||||
|
||||
;; 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.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
|
||||
(when (featurep 'inotify)
|
||||
|
||||
;; (ert-deftest filewatch-file-watch-aspects-check ()
|
||||
;; "Test whether `file-watch' properly checks the aspects."
|
||||
;; (let ((temp-file (make-temp-file "filewatch-aspects")))
|
||||
;; (should (stringp temp-file))
|
||||
;; (should-error (file-watch temp-file 'wrong nil)
|
||||
;; :type 'error)
|
||||
;; (should-error (file-watch temp-file '(modify t) nil)
|
||||
;; :type 'error)
|
||||
;; (should-error (file-watch temp-file '(modify all-modify) nil)
|
||||
;; :type 'error)
|
||||
;; (should-error (file-watch temp-file '(access wrong modify) nil)
|
||||
;; :type 'error)))
|
||||
|
||||
(ert-deftest inotify-file-watch-simple ()
|
||||
"Test if watching a normal file works."
|
||||
(let ((temp-file (make-temp-file "inotify-simple"))
|
||||
(events 0))
|
||||
(let ((wd
|
||||
(inotify-add-watch temp-file t (lambda (ev)
|
||||
(setq events (1+ events))))))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(with-temp-file temp-file
|
||||
(insert "Foo\n"))
|
||||
(sit-for 5) ;; Hacky. Wait for 5s until events are processed
|
||||
(should (> events 0)))
|
||||
(inotify-rm-watch wd)))))
|
||||
)
|
||||
|
||||
(provide 'inotify-tests)
|
||||
;;; inotify-tests.el ends here.
|
Loading…
Add table
Reference in a new issue