From 81606b10501169a5671061b8461bbc32dcec8705 Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=BCdiger=20Sonderfeld?= Date: Mon, 10 Dec 2012 06:17:21 -0500 Subject: [PATCH] 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. --- ChangeLog | 5 + configure.ac | 13 + etc/NEWS | 5 + lisp/ChangeLog | 4 + lisp/subr.el | 20 ++ src/ChangeLog | 21 ++ src/Makefile.in | 2 +- src/emacs.c | 4 + src/inotify.c | 438 +++++++++++++++++++++++++++++++++ src/keyboard.c | 28 +++ src/lisp.h | 5 + src/termhooks.h | 5 + test/ChangeLog | 4 + test/automated/inotify-test.el | 60 +++++ 14 files changed, 613 insertions(+), 1 deletion(-) create mode 100644 src/inotify.c create mode 100644 test/automated/inotify-test.el diff --git a/ChangeLog b/ChangeLog index eb572b26b16..4bdbee1ad0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2012-12-10 Rüdiger Sonderfeld + + * configure.ac (inotify): New option. + (HAVE_INOTIFY): Test for inotify. + 2012-12-09 Andreas Schwab * configure.ac: Fix source command in .gdbinit. diff --git a/configure.ac b/configure.ac index 3303f80c3e3..abdf515144b 100644 --- a/configure.ac +++ b/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 diff --git a/etc/NEWS b/etc/NEWS index c7622b09b6b..f04dcea2bde 100644 --- a/etc/NEWS +++ b/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. diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 787bfb7563b..1e65aa3a089 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,7 @@ +2012-12-10 Rüdiger Sonderfeld + + * subr.el (inotify-event-p, inotify-handle-event): New functions. + 2012-12-10 Dani Moncayo * simple.el (just-one-space): Doc fix. diff --git a/lisp/subr.el b/lisp/subr.el index e08277370a9..4f2e8f22bfe 100644 --- a/lisp/subr.el +++ b/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. diff --git a/src/ChangeLog b/src/ChangeLog index e7fc8179e07..e234b342ac7 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,24 @@ +2012-12-10 Rüdiger Sonderfeld + + * 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 * nsterm.m (fd_handler:): FD_ZERO fds (Bug#13103). diff --git a/src/Makefile.in b/src/Makefile.in index d034ad04796..eb7c7720ea5 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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) diff --git a/src/emacs.c b/src/emacs.c index fbaf0355000..c5d27a28852 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -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 */ diff --git a/src/inotify.c b/src/inotify.c new file mode 100644 index 00000000000..4db95a069fe --- /dev/null +++ b/src/inotify.c @@ -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 . */ + +#include + +#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 +#include + +/* 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 */ diff --git a/src/keyboard.c b/src/keyboard.c index fc155c5a5f7..4c484efe0c0 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -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) diff --git a/src/lisp.h b/src/lisp.h index 91fc3dfa1c6..5acb37f08d1 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -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; diff --git a/src/termhooks.h b/src/termhooks.h index b35c927fc53..da65f9d6b74 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -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 diff --git a/test/ChangeLog b/test/ChangeLog index 21f0f29b73b..7633d974f57 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,7 @@ +2012-12-10 Rüdiger Sonderfeld + + * automated/inotify-test.el: New test. + 2012-12-02 Chong Yidong * automated/ruby-mode-tests.el diff --git a/test/automated/inotify-test.el b/test/automated/inotify-test.el new file mode 100644 index 00000000000..edda7ef0418 --- /dev/null +++ b/test/automated/inotify-test.el @@ -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 +;; 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 . + +;;; 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. -- 2.39.5